1 /* 2 * Copyright 2016 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 13 14 class EfiDevice : public Node 15 { 16 public: 17 EfiDevice(EFI_BLOCK_IO *blockIo, EFI_DEVICE_PATH *devicePath); 18 virtual ~EfiDevice(); 19 20 virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, 21 size_t bufferSize); 22 virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, 23 size_t bufferSize) { return B_UNSUPPORTED; } 24 virtual off_t Size() const { return fSize; } 25 26 uint32 BlockSize() const { return fBlockSize; } 27 private: 28 EFI_BLOCK_IO* fBlockIo; 29 EFI_DEVICE_PATH* fDevicePath; 30 31 uint32 fBlockSize; 32 uint64 fSize; 33 }; 34 35 36 EfiDevice::EfiDevice(EFI_BLOCK_IO *blockIo, EFI_DEVICE_PATH *devicePath) 37 : 38 fBlockIo(blockIo), 39 fDevicePath(devicePath) 40 { 41 fBlockSize = fBlockIo->Media->BlockSize; 42 fSize = (fBlockIo->Media->LastBlock + 1) * fBlockSize; 43 } 44 45 46 EfiDevice::~EfiDevice() 47 { 48 } 49 50 51 ssize_t 52 EfiDevice::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) 53 { 54 uint32 offset = pos % fBlockSize; 55 pos /= fBlockSize; 56 57 uint32 numBlocks = (offset + bufferSize + fBlockSize) / fBlockSize; 58 char readBuffer[numBlocks * fBlockSize]; 59 60 EFI_STATUS status = fBlockIo->ReadBlocks(fBlockIo, fBlockIo->Media->MediaId, 61 pos, sizeof(readBuffer), readBuffer); 62 63 if (status != EFI_SUCCESS) 64 return B_ERROR; 65 66 memcpy(buffer, readBuffer + offset, bufferSize); 67 68 return bufferSize; 69 } 70 71 72 static EFI_DEVICE_PATH* 73 find_device_path(EFI_DEVICE_PATH *devicePath, uint16 type, uint16 subType) 74 { 75 EFI_DEVICE_PATH *node = devicePath; 76 while (!IsDevicePathEnd(node)) { 77 if (DevicePathType(node) == type 78 && (subType == 0xFFFF || DevicePathSubType(node) == subType)) 79 return node; 80 81 node = NextDevicePathNode(node); 82 } 83 84 return NULL; 85 } 86 87 88 static status_t 89 add_boot_devices(NodeList *devicesList) 90 { 91 EFI_GUID blockIoGuid = BLOCK_IO_PROTOCOL; 92 EFI_GUID devicePathGuid = DEVICE_PATH_PROTOCOL; 93 EFI_BLOCK_IO *blockIo; 94 EFI_DEVICE_PATH *devicePath, *node, *targetDevicePath = NULL; 95 EFI_HANDLE *handles = NULL; 96 EFI_STATUS status; 97 UINTN size = 0; 98 99 status = kBootServices->LocateHandle(ByProtocol, &blockIoGuid, 0, &size, 0); 100 if (status != EFI_BUFFER_TOO_SMALL) 101 return B_ENTRY_NOT_FOUND; 102 103 handles = (EFI_HANDLE*)malloc(size); 104 status = kBootServices->LocateHandle(ByProtocol, &blockIoGuid, 0, &size, 105 handles); 106 if (status != EFI_SUCCESS) { 107 if (handles != NULL) 108 free(handles); 109 return B_ENTRY_NOT_FOUND; 110 } 111 112 for (int n = (size / sizeof(EFI_HANDLE)) - 1; n >= 0; --n) { 113 status = kBootServices->HandleProtocol(handles[n], &devicePathGuid, 114 (void**)&devicePath); 115 if (status != EFI_SUCCESS) 116 continue; 117 118 node = devicePath; 119 while (!IsDevicePathEnd(NextDevicePathNode(node))) 120 node = NextDevicePathNode(node); 121 122 if (DevicePathType(node) == MEDIA_DEVICE_PATH 123 && DevicePathSubType(node) == MEDIA_CDROM_DP) { 124 targetDevicePath = find_device_path(devicePath, 125 MESSAGING_DEVICE_PATH, 0xFFFF); 126 continue; 127 } 128 129 if (DevicePathType(node) != MESSAGING_DEVICE_PATH) 130 continue; 131 132 status = kBootServices->HandleProtocol(handles[n], &blockIoGuid, 133 (void**)&blockIo); 134 if (status != EFI_SUCCESS || !blockIo->Media->MediaPresent) 135 continue; 136 137 EfiDevice *device = new(std::nothrow)EfiDevice(blockIo, devicePath); 138 if (device == NULL) 139 continue; 140 141 if (targetDevicePath != NULL 142 && memcmp(targetDevicePath, node, DevicePathNodeLength(node)) == 0) 143 devicesList->InsertBefore(devicesList->Head(), device); 144 else 145 devicesList->Insert(device); 146 147 targetDevicePath = NULL; 148 } 149 150 return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND; 151 } 152 153 154 static off_t 155 get_next_check_sum_offset(int32 index, off_t maxSize) 156 { 157 if (index < 2) 158 return index * 512; 159 160 if (index < 4) 161 return (maxSize >> 10) + index * 2048; 162 163 //return ((system_time() + index) % (maxSize >> 9)) * 512; 164 return 42 * 512; 165 } 166 167 168 static uint32 169 compute_check_sum(Node *device, off_t offset) 170 { 171 char buffer[512]; 172 ssize_t bytesRead = device->ReadAt(NULL, offset, buffer, sizeof(buffer)); 173 if (bytesRead < B_OK) 174 return 0; 175 176 if (bytesRead < (ssize_t)sizeof(buffer)) 177 memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead); 178 179 uint32 *array = (uint32*)buffer; 180 uint32 sum = 0; 181 182 for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) 183 sum += array[i]; 184 185 return sum; 186 } 187 188 189 status_t 190 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) 191 { 192 // TODO: get GUID of partition to boot, and support for SATA/ATA devices 193 return add_boot_devices(devicesList); 194 } 195 196 197 status_t 198 platform_add_block_devices(struct stage2_args *args, NodeList *devicesList) 199 { 200 // add_boot_devices will add all available devices, so nothing to do here 201 return B_OK; 202 } 203 204 205 status_t 206 platform_get_boot_partition(struct stage2_args *args, Node *bootDevice, 207 NodeList *partitions, boot::Partition **_partition) 208 { 209 NodeIterator it = partitions->GetIterator(); 210 while (it.HasNext()) { 211 boot::Partition *partition = (boot::Partition*)it.Next(); 212 // we're only looking for CDs atm, so just return first found 213 *_partition = partition; 214 return B_OK; 215 } 216 217 return B_ERROR; 218 } 219 220 221 status_t 222 platform_register_boot_device(Node *device) 223 { 224 disk_identifier identifier; 225 226 identifier.bus_type = UNKNOWN_BUS; 227 identifier.device_type = UNKNOWN_DEVICE; 228 identifier.device.unknown.size = device->Size(); 229 230 for (uint32 i = 0; i < NUM_DISK_CHECK_SUMS; ++i) { 231 off_t offset = get_next_check_sum_offset(i, device->Size()); 232 identifier.device.unknown.check_sums[i].offset = offset; 233 identifier.device.unknown.check_sums[i].sum = compute_check_sum(device, offset); 234 } 235 236 gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_CD); 237 gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, true); 238 gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE, 239 &identifier, sizeof(disk_identifier)); 240 241 return B_OK; 242 } 243