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