1 /* 2 * Copyright 2016-2020 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <boot/partitions.h> 8 #include <boot/platform.h> 9 #include <boot/stage2.h> 10 11 #include "efi_platform.h" 12 #include <efi/protocol/block-io.h> 13 14 15 //#define TRACE_DEVICES 16 #ifdef TRACE_DEVICES 17 # define TRACE(x...) dprintf("efi/devices: " x) 18 #else 19 # define TRACE(x...) ; 20 #endif 21 22 23 static efi_guid BlockIoGUID = EFI_BLOCK_IO_PROTOCOL_GUID; 24 25 26 class EfiDevice : public Node 27 { 28 public: 29 EfiDevice(efi_block_io_protocol *blockIo); 30 virtual ~EfiDevice(); 31 32 virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, 33 size_t bufferSize); 34 virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, 35 size_t bufferSize) { return B_UNSUPPORTED; } 36 virtual off_t Size() const { 37 return (fBlockIo->Media->LastBlock + 1) * BlockSize(); } 38 39 uint32 BlockSize() const { return fBlockIo->Media->BlockSize; } 40 bool ReadOnly() const { return fBlockIo->Media->ReadOnly; } 41 private: 42 efi_block_io_protocol* fBlockIo; 43 }; 44 45 46 EfiDevice::EfiDevice(efi_block_io_protocol *blockIo) 47 : 48 fBlockIo(blockIo) 49 { 50 } 51 52 53 EfiDevice::~EfiDevice() 54 { 55 } 56 57 58 ssize_t 59 EfiDevice::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) 60 { 61 TRACE("%s called. pos: %" B_PRIdOFF ", %p, %" B_PRIuSIZE "\n", __func__, 62 pos, buffer, bufferSize); 63 64 off_t offset = pos % BlockSize(); 65 pos /= BlockSize(); 66 67 uint32 numBlocks = (offset + bufferSize + BlockSize()) / BlockSize(); 68 char readBuffer[numBlocks * BlockSize()]; 69 70 if (fBlockIo->ReadBlocks(fBlockIo, fBlockIo->Media->MediaId, 71 pos, sizeof(readBuffer), readBuffer) != EFI_SUCCESS) 72 return B_ERROR; 73 74 memcpy(buffer, readBuffer + offset, bufferSize); 75 76 return bufferSize; 77 } 78 79 80 static off_t 81 get_next_check_sum_offset(int32 index, off_t maxSize) 82 { 83 TRACE("%s: called\n", __func__); 84 85 if (index < 2) 86 return index * 512; 87 88 if (index < 4) 89 return (maxSize >> 10) + index * 2048; 90 91 return ((system_time() + index) % (maxSize >> 9)) * 512; 92 } 93 94 95 static uint32 96 compute_check_sum(Node *device, off_t offset) 97 { 98 TRACE("%s: called\n", __func__); 99 100 char buffer[512]; 101 ssize_t bytesRead = device->ReadAt(NULL, offset, buffer, sizeof(buffer)); 102 if (bytesRead < B_OK) 103 return 0; 104 105 if (bytesRead < (ssize_t)sizeof(buffer)) 106 memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead); 107 108 uint32 *array = (uint32*)buffer; 109 uint32 sum = 0; 110 111 for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) 112 sum += array[i]; 113 114 return sum; 115 } 116 117 118 status_t 119 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) 120 { 121 TRACE("%s: called\n", __func__); 122 123 efi_block_io_protocol *blockIo; 124 size_t memSize = 0; 125 126 // Read to zero sized buffer to get memory needed for handles 127 if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 0) 128 != EFI_BUFFER_TOO_SMALL) 129 panic("Cannot read size of block device handles!"); 130 131 uint32 noOfHandles = memSize / sizeof(efi_handle); 132 133 efi_handle handles[noOfHandles]; 134 if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 135 handles) != EFI_SUCCESS) 136 panic("Failed to locate block devices!"); 137 138 // All block devices has one for the disk and one per partition 139 // There is a special case for a device with one fixed partition 140 // But we probably do not care about booting on that kind of device 141 // So find all disk block devices and let Haiku do partition scan 142 for (uint32 n = 0; n < noOfHandles; n++) { 143 if (kBootServices->HandleProtocol(handles[n], &BlockIoGUID, 144 (void**)&blockIo) != EFI_SUCCESS) 145 panic("Cannot get block device handle!"); 146 147 TRACE("%s: %p: present: %s, logical: %s, removeable: %s, " 148 "blocksize: %" B_PRIuSIZE ", lastblock: %" B_PRIu64 "\n", 149 __func__, blockIo, 150 blockIo->Media->MediaPresent ? "true" : "false", 151 blockIo->Media->LogicalPartition ? "true" : "false", 152 blockIo->Media->RemovableMedia ? "true" : "false", 153 blockIo->Media->BlockSize, blockIo->Media->LastBlock); 154 155 if (!blockIo->Media->MediaPresent || blockIo->Media->LogicalPartition) 156 continue; 157 158 // The qemu flash device with a 256K block sizes sometime show up 159 // in edk2. If flash is unconfigured, bad things happen on arm. 160 // edk2 bug: https://bugzilla.tianocore.org/show_bug.cgi?id=2856 161 // We're not ready for flash devices in efi, so skip anything odd. 162 if (blockIo->Media->BlockSize > 8192) 163 continue; 164 165 EfiDevice *device = new(std::nothrow)EfiDevice(blockIo); 166 if (device == NULL) 167 panic("Can't allocate memory for block devices!"); 168 devicesList->Insert(device); 169 } 170 return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND; 171 } 172 173 status_t 174 platform_add_block_devices(struct stage2_args *args, NodeList *devicesList) 175 { 176 TRACE("%s: called\n", __func__); 177 178 //TODO: Currently we add all in platform_add_boot_device 179 return B_ENTRY_NOT_FOUND; 180 } 181 182 status_t 183 platform_get_boot_partition(struct stage2_args *args, Node *bootDevice, 184 NodeList *partitions, boot::Partition **_partition) 185 { 186 TRACE("%s: called\n", __func__); 187 *_partition = (boot::Partition*)partitions->GetIterator().Next(); 188 return *_partition != NULL ? B_OK : B_ENTRY_NOT_FOUND; 189 } 190 191 192 status_t 193 platform_register_boot_device(Node *device) 194 { 195 TRACE("%s: called\n", __func__); 196 197 EfiDevice *efiDevice = (EfiDevice *)device; 198 disk_identifier identifier; 199 200 identifier.bus_type = UNKNOWN_BUS; 201 identifier.device_type = UNKNOWN_DEVICE; 202 identifier.device.unknown.size = device->Size(); 203 204 for (uint32 i = 0; i < NUM_DISK_CHECK_SUMS; ++i) { 205 off_t offset = get_next_check_sum_offset(i, device->Size()); 206 identifier.device.unknown.check_sums[i].offset = offset; 207 identifier.device.unknown.check_sums[i].sum = compute_check_sum(device, 208 offset); 209 } 210 211 gBootVolume.SetInt32(BOOT_METHOD, efiDevice->ReadOnly() ? BOOT_METHOD_CD: 212 BOOT_METHOD_HARD_DISK); 213 gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, efiDevice->ReadOnly()); 214 gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE, 215 &identifier, sizeof(disk_identifier)); 216 217 return B_OK; 218 } 219 220 221 void 222 platform_cleanup_devices() 223 { 224 } 225