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 "Header.h" 12 13 #include "efi_platform.h" 14 #include <efi/protocol/block-io.h> 15 16 #include "gpt.h" 17 #include "gpt_known_guids.h" 18 19 20 //#define TRACE_DEVICES 21 #ifdef TRACE_DEVICES 22 # define TRACE(x...) dprintf("efi/devices: " x) 23 #else 24 # define TRACE(x...) ; 25 #endif 26 27 28 static efi_guid BlockIoGUID = EFI_BLOCK_IO_PROTOCOL_GUID; 29 30 31 class EfiDevice : public Node 32 { 33 public: 34 EfiDevice(efi_block_io_protocol *blockIo); 35 virtual ~EfiDevice(); 36 37 virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, 38 size_t bufferSize); 39 virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, 40 size_t bufferSize) { return B_UNSUPPORTED; } 41 virtual off_t Size() const { 42 return (fBlockIo->Media->LastBlock + 1) * BlockSize(); } 43 44 uint32 BlockSize() const { return fBlockIo->Media->BlockSize; } 45 private: 46 efi_block_io_protocol* fBlockIo; 47 }; 48 49 50 EfiDevice::EfiDevice(efi_block_io_protocol *blockIo) 51 : 52 fBlockIo(blockIo) 53 { 54 } 55 56 57 EfiDevice::~EfiDevice() 58 { 59 } 60 61 62 ssize_t 63 EfiDevice::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) 64 { 65 TRACE("%s called. pos: %" B_PRIdOFF ", %p, %" B_PRIuSIZE "\n", __func__, 66 pos, buffer, bufferSize); 67 68 off_t offset = pos % BlockSize(); 69 pos /= BlockSize(); 70 71 uint32 numBlocks = (offset + bufferSize + BlockSize() - 1) / BlockSize(); 72 73 // TODO: We really should implement memalign and align all requests to 74 // fBlockIo->Media->IoAlign. This static alignment is large enough though 75 // to catch most required alignments. 76 char readBuffer[numBlocks * BlockSize()] 77 __attribute__((aligned(2048))); 78 79 if (fBlockIo->ReadBlocks(fBlockIo, fBlockIo->Media->MediaId, 80 pos, sizeof(readBuffer), readBuffer) != EFI_SUCCESS) { 81 dprintf("%s: blockIo error reading from device!\n", __func__); 82 return B_ERROR; 83 } 84 85 memcpy(buffer, readBuffer + offset, bufferSize); 86 87 return bufferSize; 88 } 89 90 91 static off_t 92 get_next_check_sum_offset(int32 index, off_t maxSize) 93 { 94 TRACE("%s: called\n", __func__); 95 96 if (index < 2) 97 return index * 512; 98 99 if (index < 4) 100 return (maxSize >> 10) + index * 2048; 101 102 return ((system_time() + index) % (maxSize >> 9)) * 512; 103 } 104 105 106 static uint32 107 compute_check_sum(Node *device, off_t offset) 108 { 109 TRACE("%s: called\n", __func__); 110 111 char buffer[512]; 112 ssize_t bytesRead = device->ReadAt(NULL, offset, buffer, sizeof(buffer)); 113 if (bytesRead < B_OK) 114 return 0; 115 116 if (bytesRead < (ssize_t)sizeof(buffer)) 117 memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead); 118 119 uint32 *array = (uint32*)buffer; 120 uint32 sum = 0; 121 122 for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) 123 sum += array[i]; 124 125 return sum; 126 } 127 128 129 static bool 130 device_contains_partition(EfiDevice *device, boot::Partition *partition) 131 { 132 EFI::Header *header = (EFI::Header*)partition->content_cookie; 133 if (header != NULL && header->InitCheck() == B_OK) { 134 // check if device is GPT, and contains partition entry 135 uint32 blockSize = device->BlockSize(); 136 gpt_table_header *deviceHeader = 137 (gpt_table_header*)malloc(blockSize); 138 ssize_t bytesRead = device->ReadAt(NULL, blockSize, deviceHeader, 139 blockSize); 140 if (bytesRead != (ssize_t)blockSize) 141 return false; 142 143 if (memcmp(deviceHeader, &header->TableHeader(), 144 sizeof(gpt_table_header)) != 0) 145 return false; 146 147 // partition->cookie == int partition entry index 148 uint32 index = (uint32)(addr_t)partition->cookie; 149 uint32 size = sizeof(gpt_partition_entry) * (index + 1); 150 gpt_partition_entry *entries = (gpt_partition_entry*)malloc(size); 151 bytesRead = device->ReadAt(NULL, 152 deviceHeader->entries_block * blockSize, entries, size); 153 if (bytesRead != (ssize_t)size) 154 return false; 155 156 if (memcmp(&entries[index], &header->EntryAt(index), 157 sizeof(gpt_partition_entry)) != 0) 158 return false; 159 160 for (size_t i = 0; i < sizeof(kTypeMap) / sizeof(struct type_map); ++i) 161 if (strcmp(kTypeMap[i].type, BFS_NAME) == 0) 162 if (kTypeMap[i].guid == header->EntryAt(index).partition_type) 163 return true; 164 165 // Our partition has an EFI header, but we couldn't find one, so bail 166 return false; 167 } 168 169 if ((partition->offset + partition->size) <= device->Size()) 170 return true; 171 172 return false; 173 } 174 175 176 status_t 177 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) 178 { 179 TRACE("%s: called\n", __func__); 180 181 efi_block_io_protocol *blockIo; 182 size_t memSize = 0; 183 184 // Read to zero sized buffer to get memory needed for handles 185 if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 0) 186 != EFI_BUFFER_TOO_SMALL) 187 panic("Cannot read size of block device handles!"); 188 189 uint32 noOfHandles = memSize / sizeof(efi_handle); 190 191 efi_handle handles[noOfHandles]; 192 if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 193 handles) != EFI_SUCCESS) 194 panic("Failed to locate block devices!"); 195 196 // All block devices has one for the disk and one per partition 197 // There is a special case for a device with one fixed partition 198 // But we probably do not care about booting on that kind of device 199 // So find all disk block devices and let Haiku do partition scan 200 for (uint32 n = 0; n < noOfHandles; n++) { 201 if (kBootServices->HandleProtocol(handles[n], &BlockIoGUID, 202 (void**)&blockIo) != EFI_SUCCESS) 203 panic("Cannot get block device handle!"); 204 205 TRACE("%s: %p: present: %s, logical: %s, removeable: %s, " 206 "blocksize: %" PRIu32 ", lastblock: %" PRIu64 "\n", 207 __func__, blockIo, 208 blockIo->Media->MediaPresent ? "true" : "false", 209 blockIo->Media->LogicalPartition ? "true" : "false", 210 blockIo->Media->RemovableMedia ? "true" : "false", 211 blockIo->Media->BlockSize, blockIo->Media->LastBlock); 212 213 if (!blockIo->Media->MediaPresent || blockIo->Media->LogicalPartition) 214 continue; 215 216 // The qemu flash device with a 256K block sizes sometime show up 217 // in edk2. If flash is unconfigured, bad things happen on arm. 218 // edk2 bug: https://bugzilla.tianocore.org/show_bug.cgi?id=2856 219 // We're not ready for flash devices in efi, so skip anything odd. 220 if (blockIo->Media->BlockSize > 8192) 221 continue; 222 223 EfiDevice *device = new(std::nothrow)EfiDevice(blockIo); 224 if (device == NULL) 225 panic("Can't allocate memory for block devices!"); 226 devicesList->Insert(device); 227 } 228 return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND; 229 } 230 231 232 status_t 233 platform_add_block_devices(struct stage2_args *args, NodeList *devicesList) 234 { 235 TRACE("%s: called\n", __func__); 236 237 //TODO: Currently we add all in platform_add_boot_device 238 return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND; 239 } 240 241 242 status_t 243 platform_get_boot_partitions(struct stage2_args *args, Node *bootDevice, 244 NodeList *partitions, NodeList *bootPartitions) 245 { 246 NodeIterator iterator = partitions->GetIterator(); 247 boot::Partition *partition = NULL; 248 while ((partition = (boot::Partition*)iterator.Next()) != NULL) { 249 if (device_contains_partition((EfiDevice*)bootDevice, partition)) { 250 bootPartitions->Insert(partition); 251 } 252 } 253 254 return bootPartitions->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND; 255 } 256 257 258 status_t 259 platform_register_boot_device(Node *device) 260 { 261 TRACE("%s: called\n", __func__); 262 263 disk_identifier identifier; 264 265 identifier.bus_type = UNKNOWN_BUS; 266 identifier.device_type = UNKNOWN_DEVICE; 267 identifier.device.unknown.size = device->Size(); 268 269 for (uint32 i = 0; i < NUM_DISK_CHECK_SUMS; ++i) { 270 off_t offset = get_next_check_sum_offset(i, device->Size()); 271 identifier.device.unknown.check_sums[i].offset = offset; 272 identifier.device.unknown.check_sums[i].sum = compute_check_sum(device, 273 offset); 274 } 275 276 // ...HARD_DISK, as we pick partition and have checksum (no need to use _CD) 277 gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_HARD_DISK); 278 gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE, 279 &identifier, sizeof(disk_identifier)); 280 281 return B_OK; 282 } 283 284 285 void 286 platform_cleanup_devices() 287 { 288 } 289