1 /* 2 * Copyright 2016-2017 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <string.h> 8 9 #include <boot/partitions.h> 10 #include <boot/platform.h> 11 #include <boot/stage2.h> 12 #include <boot/stdio.h> 13 #include <util/list.h> 14 15 #include "Header.h" 16 17 #include "efi_platform.h" 18 #include <efi/protocol/block-io.h> 19 #include <efi/protocol/loaded-image.h> 20 #include <efi/protocol/device-path.h> 21 22 #include "gpt.h" 23 #include "gpt_known_guids.h" 24 25 26 #define DevicePathNodeLength(a) (((a)->Length[0]) | ((a)->Length[1] << 8)) 27 #define NextDevicePathNode(a) (efi_device_path_protocol*) \ 28 (((uint8_t*)(a)) + DevicePathNodeLength(a)) 29 30 31 struct device_handle { 32 list_link link; 33 efi_device_path_protocol* device_path; 34 efi_handle handle; 35 }; 36 37 38 static struct list sMessagingDevices; 39 static struct list sMediaDevices; 40 41 static efi_guid BlockIoGUID = EFI_BLOCK_IO_PROTOCOL_GUID; 42 static efi_guid LoadedImageGUID = EFI_LOADED_IMAGE_PROTOCOL_GUID; 43 static efi_guid DevicePathGUID = EFI_DEVICE_PATH_PROTOCOL_GUID; 44 45 46 static bool 47 device_path_end(efi_device_path_protocol* path) 48 { 49 if (path == NULL) { 50 dprintf("ERROR: Unexpected end of device protocol path!\n"); 51 return false; 52 } 53 54 return (path->Type == DEVICE_PATH_END 55 && path->SubType == DEVICE_PATH_ENTIRE_END); 56 } 57 58 59 static size_t 60 device_path_length(efi_device_path_protocol* path) 61 { 62 efi_device_path_protocol *node = path; 63 size_t length = 0; 64 while (!device_path_end(node)) { 65 length += DevicePathNodeLength(node); 66 node = NextDevicePathNode(node); 67 } 68 69 // node now points to the device path end node; add its length as well 70 return length + DevicePathNodeLength(node); 71 } 72 73 74 // If matchSubPath is true, then the second device path can be a sub-path 75 // of the first device path 76 static bool 77 compare_device_paths(efi_device_path_protocol* first, 78 efi_device_path_protocol* second, bool matchSubPath = false) 79 { 80 efi_device_path_protocol *firstNode = first; 81 efi_device_path_protocol *secondNode = second; 82 while (!device_path_end(firstNode) && !device_path_end(secondNode)) { 83 size_t firstLength = DevicePathNodeLength(firstNode); 84 size_t secondLength = DevicePathNodeLength(secondNode); 85 if (firstLength != secondLength 86 || memcmp(firstNode, secondNode, firstLength) != 0) { 87 return false; 88 } 89 firstNode = NextDevicePathNode(firstNode); 90 secondNode = NextDevicePathNode(secondNode); 91 } 92 93 if (matchSubPath) 94 return device_path_end(secondNode); 95 96 return device_path_end(firstNode) && device_path_end(secondNode); 97 } 98 99 100 static bool 101 add_device_path(struct list *list, efi_device_path_protocol* path, 102 efi_handle handle) 103 { 104 device_handle *node = NULL; 105 while ((node = (device_handle*)list_get_next_item(list, node)) != NULL) { 106 if (compare_device_paths(node->device_path, path)) 107 return false; 108 } 109 110 size_t length = device_path_length(path); 111 node = (device_handle*)malloc(sizeof(struct device_handle)); 112 node->device_path = (efi_device_path_protocol*)malloc(length); 113 node->handle = handle; 114 memcpy(node->device_path, path, length); 115 116 list_add_item(list, node); 117 118 return true; 119 } 120 121 122 class EfiDevice : public Node 123 { 124 public: 125 EfiDevice(efi_block_io_protocol *blockIo, 126 efi_device_path_protocol *devicePath); 127 virtual ~EfiDevice(); 128 129 virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, 130 size_t bufferSize); 131 virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, 132 size_t bufferSize) { return B_UNSUPPORTED; } 133 virtual off_t Size() const { 134 return (fBlockIo->Media->LastBlock + 1) * BlockSize(); } 135 136 uint32 BlockSize() const { return fBlockIo->Media->BlockSize; } 137 bool ReadOnly() const { return fBlockIo->Media->ReadOnly; } 138 int32 BootMethod() const { 139 if (fDevicePath->Type == DEVICE_PATH_MEDIA) { 140 if (fDevicePath->SubType == MEDIA_CDROM_DP) 141 return BOOT_METHOD_CD; 142 if (fDevicePath->SubType == MEDIA_HARDDRIVE_DP) 143 return BOOT_METHOD_HARD_DISK; 144 } 145 146 return BOOT_METHOD_DEFAULT; 147 } 148 149 efi_device_path_protocol* DevicePath() { return fDevicePath; } 150 151 private: 152 efi_block_io_protocol* fBlockIo; 153 efi_device_path_protocol* fDevicePath; 154 }; 155 156 157 EfiDevice::EfiDevice(efi_block_io_protocol *blockIo, 158 efi_device_path_protocol *devicePath) 159 : 160 fBlockIo(blockIo), 161 fDevicePath(devicePath) 162 { 163 } 164 165 166 EfiDevice::~EfiDevice() 167 { 168 } 169 170 171 ssize_t 172 EfiDevice::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) 173 { 174 uint32 offset = pos % BlockSize(); 175 pos /= BlockSize(); 176 177 uint32 numBlocks = (offset + bufferSize + BlockSize()) / BlockSize(); 178 char readBuffer[numBlocks * BlockSize()]; 179 180 if (fBlockIo->ReadBlocks(fBlockIo, fBlockIo->Media->MediaId, 181 pos, sizeof(readBuffer), readBuffer) != EFI_SUCCESS) 182 return B_ERROR; 183 184 memcpy(buffer, readBuffer + offset, bufferSize); 185 186 return bufferSize; 187 } 188 189 190 static status_t 191 build_device_handles() 192 { 193 efi_guid blockIoGuid = EFI_BLOCK_IO_PROTOCOL_GUID; 194 efi_guid devicePathGuid = EFI_DEVICE_PATH_PROTOCOL_GUID; 195 196 efi_device_path_protocol *devicePath, *node; 197 efi_handle *handles = NULL; 198 efi_status status; 199 size_t size = 0; 200 201 status = kBootServices->LocateHandle(ByProtocol, &blockIoGuid, 0, &size, 0); 202 if (status != EFI_BUFFER_TOO_SMALL) 203 return B_ENTRY_NOT_FOUND; 204 205 handles = (efi_handle*)malloc(size); 206 status = kBootServices->LocateHandle(ByProtocol, &blockIoGuid, 0, &size, 207 handles); 208 if (status != EFI_SUCCESS) { 209 free(handles); 210 return B_ENTRY_NOT_FOUND; 211 } 212 213 for (size_t n = 0; n < (size / sizeof(efi_handle)); n++) { 214 status = kBootServices->HandleProtocol(handles[n], &devicePathGuid, 215 (void**)&devicePath); 216 if (status != EFI_SUCCESS) 217 continue; 218 219 node = devicePath; 220 while (!device_path_end(NextDevicePathNode(node))) 221 node = NextDevicePathNode(node); 222 223 if (node->Type == DEVICE_PATH_MEDIA) 224 add_device_path(&sMediaDevices, devicePath, handles[n]); 225 else if (node->Type == DEVICE_PATH_MESSAGING) 226 add_device_path(&sMessagingDevices, devicePath, handles[n]); 227 } 228 229 return B_OK; 230 } 231 232 233 static off_t 234 get_next_check_sum_offset(int32 index, off_t maxSize) 235 { 236 if (index < 2) 237 return index * 512; 238 239 if (index < 4) 240 return (maxSize >> 10) + index * 2048; 241 242 return ((system_time() + index) % (maxSize >> 9)) * 512; 243 } 244 245 246 static uint32 247 compute_check_sum(Node *device, off_t offset) 248 { 249 char buffer[512]; 250 ssize_t bytesRead = device->ReadAt(NULL, offset, buffer, sizeof(buffer)); 251 if (bytesRead < B_OK) 252 return 0; 253 254 if (bytesRead < (ssize_t)sizeof(buffer)) 255 memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead); 256 257 uint32 *array = (uint32*)buffer; 258 uint32 sum = 0; 259 260 for (uint32 i = 0; 261 i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) { 262 sum += array[i]; 263 } 264 265 return sum; 266 } 267 268 269 static device_handle* 270 get_messaging_device_for_media_device(device_handle *media_device) 271 { 272 device_handle *device = NULL; 273 while ((device = (device_handle*)list_get_next_item(&sMessagingDevices, 274 device)) != NULL) { 275 if (compare_device_paths(media_device->device_path, 276 device->device_path, true)) 277 return device; 278 } 279 280 return NULL; 281 } 282 283 284 static bool 285 get_boot_uuid(void) 286 { 287 return false; 288 } 289 290 291 static status_t 292 add_boot_device(NodeList *devicesList) 293 { 294 return B_ENTRY_NOT_FOUND; 295 } 296 297 298 static status_t 299 add_boot_device_for_image(NodeList *devicesList) 300 { 301 efi_loaded_image_protocol *loadedImage; 302 if (kBootServices->HandleProtocol(kImage, &LoadedImageGUID, 303 (void**)&loadedImage) != EFI_SUCCESS) 304 return B_ERROR; 305 306 efi_device_path_protocol *devicePath, *node; 307 if (kBootServices->HandleProtocol(loadedImage->DeviceHandle, 308 &DevicePathGUID, (void**)&devicePath) != EFI_SUCCESS) 309 return B_ERROR; 310 311 for (node = devicePath; node->Type != DEVICE_PATH_MESSAGING; 312 node = NextDevicePathNode(node)) { 313 if (device_path_end(node)) 314 return B_ERROR; 315 } 316 317 size_t length = device_path_length(devicePath); 318 efi_device_path_protocol *savedDevicePath 319 = (efi_device_path_protocol*)malloc(length); 320 memcpy(savedDevicePath, devicePath, length); 321 322 efi_handle handle; 323 if (kBootServices->LocateDevicePath(&BlockIoGUID, &devicePath, &handle) 324 != EFI_SUCCESS) 325 return B_ERROR; 326 327 if (!device_path_end(devicePath)) 328 return B_ERROR; 329 330 efi_block_io_protocol *blockIo; 331 if (kBootServices->HandleProtocol(handle, &BlockIoGUID, (void**)&blockIo) 332 != EFI_SUCCESS) 333 return B_ERROR; 334 335 if (!blockIo->Media->MediaPresent) 336 return B_ERROR; 337 338 EfiDevice *device = new(std::nothrow)EfiDevice(blockIo, savedDevicePath); 339 if (device == NULL) 340 return B_ERROR; 341 342 add_device_path(&sMessagingDevices, savedDevicePath, handle); 343 devicesList->Insert(device); 344 345 return B_OK; 346 } 347 348 349 static status_t 350 add_cd_devices(NodeList *devicesList) 351 { 352 device_handle *handle = NULL; 353 while ((handle = (device_handle*)list_get_next_item(&sMediaDevices, handle)) 354 != NULL) { 355 efi_device_path_protocol *node = handle->device_path; 356 while (!device_path_end(NextDevicePathNode(node))) 357 node = NextDevicePathNode(node); 358 359 if (node->Type != DEVICE_PATH_MEDIA) 360 continue; 361 362 if (node->SubType != MEDIA_CDROM_DP) 363 continue; 364 365 device_handle *messaging_device 366 = get_messaging_device_for_media_device(handle); 367 if (messaging_device == NULL) 368 continue; 369 370 efi_block_io_protocol *blockIo; 371 efi_guid blockIoGuid = EFI_BLOCK_IO_PROTOCOL_GUID; 372 efi_status status = kBootServices->HandleProtocol(messaging_device->handle, 373 &blockIoGuid, (void**)&blockIo); 374 if (status != EFI_SUCCESS) 375 continue; 376 377 if (!blockIo->Media->MediaPresent) 378 continue; 379 380 EfiDevice *device = new(std::nothrow)EfiDevice(blockIo, 381 handle->device_path); 382 383 if (device == NULL) 384 continue; 385 386 devicesList->Insert(device); 387 } 388 389 return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND; 390 } 391 392 393 static status_t 394 add_remaining_devices(NodeList *devicesList) 395 { 396 device_handle *node = NULL; 397 while ((node = (device_handle*)list_get_next_item(&sMessagingDevices, node)) 398 != NULL) { 399 NodeIterator it = devicesList->GetIterator(); 400 bool found = false; 401 while (it.HasNext()) { 402 EfiDevice *device = (EfiDevice*)it.Next(); 403 // device->DevicePath() is a Media Device Path instance 404 if (compare_device_paths(device->DevicePath(), 405 node->device_path, true)) { 406 found = true; 407 break; 408 } 409 } 410 411 if (!found) { 412 efi_block_io_protocol *blockIo; 413 efi_guid blockIoGuid = EFI_BLOCK_IO_PROTOCOL_GUID; 414 efi_status status = kBootServices->HandleProtocol(node->handle, 415 &blockIoGuid, (void**)&blockIo); 416 if (status != EFI_SUCCESS) 417 continue; 418 419 if (!blockIo->Media->MediaPresent) 420 continue; 421 422 EfiDevice *device = new(std::nothrow)EfiDevice(blockIo, 423 node->device_path); 424 425 if (device == NULL) 426 continue; 427 428 devicesList->Insert(device); 429 } 430 } 431 432 return B_OK; 433 } 434 435 436 static bool 437 device_contains_partition(EfiDevice *device, boot::Partition *partition) 438 { 439 EFI::Header *header = (EFI::Header*)partition->content_cookie; 440 if (header != NULL && header->InitCheck() == B_OK) { 441 // check if device is GPT, and contains partition entry 442 uint32 blockSize = device->BlockSize(); 443 gpt_table_header *deviceHeader = 444 (gpt_table_header*)malloc(blockSize); 445 ssize_t bytesRead = device->ReadAt(NULL, blockSize, deviceHeader, 446 blockSize); 447 if (bytesRead != blockSize) 448 return false; 449 450 if (memcmp(deviceHeader, &header->TableHeader(), 451 sizeof(gpt_table_header)) != 0) 452 return false; 453 454 // partition->cookie == int partition entry index 455 uint32 index = (uint32)(addr_t)partition->cookie; 456 uint32 size = sizeof(gpt_partition_entry) * (index + 1); 457 gpt_partition_entry *entries = (gpt_partition_entry*)malloc(size); 458 bytesRead = device->ReadAt(NULL, 459 deviceHeader->entries_block * blockSize, entries, size); 460 if (bytesRead != size) 461 return false; 462 463 if (memcmp(&entries[index], &header->EntryAt(index), 464 sizeof(gpt_partition_entry)) != 0) 465 return false; 466 467 for (size_t i = 0; i < sizeof(kTypeMap) / sizeof(struct type_map); ++i) 468 if (strcmp(kTypeMap[i].type, BFS_NAME) == 0) 469 if (kTypeMap[i].guid == header->EntryAt(index).partition_type) 470 return true; 471 472 // Our partition has an EFI header, but we couldn't find one, so bail 473 return false; 474 } 475 476 if ((partition->offset + partition->size) <= device->Size()) 477 return true; 478 479 return false; 480 } 481 482 483 status_t 484 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) 485 { 486 // This is the first entry point, so init the lists here 487 list_init(&sMessagingDevices); 488 list_init(&sMediaDevices); 489 490 build_device_handles(); 491 492 if (get_boot_uuid()) { 493 // If we have the UUID, add the boot device containing that partition 494 return add_boot_device(devicesList); 495 } else { 496 // If we don't have a UUID, add all CD devices with media, and the 497 // device that haiku_loader.efi is located on 498 add_boot_device_for_image(devicesList); 499 // We do this first, so that booting from CD is the fallback 500 add_cd_devices(devicesList); 501 if (devicesList->Count() > 0) 502 return B_OK; 503 } 504 505 // Otherwise, we don't know what the boot device is; defer to 506 // platform_add_block_devices() to add the rest 507 return B_ENTRY_NOT_FOUND; 508 } 509 510 511 status_t 512 platform_add_block_devices(struct stage2_args *args, NodeList *devicesList) 513 { 514 return add_remaining_devices(devicesList); 515 } 516 517 518 status_t 519 platform_get_boot_partitions(struct stage2_args *args, Node *bootDevice, 520 NodeList *partitions, NodeList *bootPartitions) 521 { 522 NodeIterator iterator = partitions->GetIterator(); 523 boot::Partition *partition = NULL; 524 while ((partition = (boot::Partition*)iterator.Next()) != NULL) { 525 if (device_contains_partition((EfiDevice*)bootDevice, partition)) { 526 iterator.Remove(); 527 bootPartitions->Insert(partition); 528 } 529 } 530 531 return bootPartitions->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND; 532 } 533 534 535 status_t 536 platform_register_boot_device(Node *device) 537 { 538 EfiDevice *efiDevice = (EfiDevice *)device; 539 disk_identifier identifier; 540 541 // TODO: Setup using device path 542 identifier.bus_type = UNKNOWN_BUS; 543 identifier.device_type = UNKNOWN_DEVICE; 544 identifier.device.unknown.size = device->Size(); 545 546 for (uint32 i = 0; i < NUM_DISK_CHECK_SUMS; ++i) { 547 off_t offset = get_next_check_sum_offset(i, device->Size()); 548 identifier.device.unknown.check_sums[i].offset = offset; 549 identifier.device.unknown.check_sums[i].sum 550 = compute_check_sum(device, offset); 551 } 552 553 gBootVolume.SetInt32(BOOT_METHOD, efiDevice->BootMethod()); 554 gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, efiDevice->ReadOnly()); 555 gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE, 556 &identifier, sizeof(disk_identifier)); 557 558 return B_OK; 559 } 560 561 562 void 563 platform_cleanup_devices() 564 { 565 } 566