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 static efi_guid BlockIoGUID = EFI_BLOCK_IO_PROTOCOL_GUID; 16 17 18 class EfiDevice : public Node 19 { 20 public: 21 EfiDevice(efi_block_io_protocol *blockIo); 22 virtual ~EfiDevice(); 23 24 virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, 25 size_t bufferSize); 26 virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, 27 size_t bufferSize) { return B_UNSUPPORTED; } 28 virtual off_t Size() const { 29 return (fBlockIo->Media->LastBlock + 1) * BlockSize(); } 30 31 uint32 BlockSize() const { return fBlockIo->Media->BlockSize; } 32 bool ReadOnly() const { return fBlockIo->Media->ReadOnly; } 33 private: 34 efi_block_io_protocol* fBlockIo; 35 }; 36 37 38 EfiDevice::EfiDevice(efi_block_io_protocol *blockIo) 39 : 40 fBlockIo(blockIo) 41 { 42 } 43 44 45 EfiDevice::~EfiDevice() 46 { 47 } 48 49 50 ssize_t 51 EfiDevice::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) 52 { 53 off_t offset = pos % BlockSize(); 54 pos /= BlockSize(); 55 56 uint32 numBlocks = (offset + bufferSize + BlockSize()) / BlockSize(); 57 char readBuffer[numBlocks * BlockSize()]; 58 59 if (fBlockIo->ReadBlocks(fBlockIo, fBlockIo->Media->MediaId, 60 pos, sizeof(readBuffer), readBuffer) != EFI_SUCCESS) 61 return B_ERROR; 62 63 memcpy(buffer, readBuffer + offset, bufferSize); 64 65 return bufferSize; 66 } 67 68 69 static off_t 70 get_next_check_sum_offset(int32 index, off_t maxSize) 71 { 72 if (index < 2) 73 return index * 512; 74 75 if (index < 4) 76 return (maxSize >> 10) + index * 2048; 77 78 return ((system_time() + index) % (maxSize >> 9)) * 512; 79 } 80 81 82 static uint32 83 compute_check_sum(Node *device, off_t offset) 84 { 85 char buffer[512]; 86 ssize_t bytesRead = device->ReadAt(NULL, offset, buffer, sizeof(buffer)); 87 if (bytesRead < B_OK) 88 return 0; 89 90 if (bytesRead < (ssize_t)sizeof(buffer)) 91 memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead); 92 93 uint32 *array = (uint32*)buffer; 94 uint32 sum = 0; 95 96 for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) 97 sum += array[i]; 98 99 return sum; 100 } 101 102 103 status_t 104 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) 105 { 106 efi_block_io_protocol *blockIo; 107 size_t memSize = 0; 108 109 // Read to zero sized buffer to get memory needed for handles 110 if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 0) 111 != EFI_BUFFER_TOO_SMALL) 112 panic("Cannot read size of block device handles!"); 113 114 uint32 noOfHandles = memSize / sizeof(efi_handle); 115 116 efi_handle handles[noOfHandles]; 117 if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 118 handles) != EFI_SUCCESS) 119 panic("Failed to locate block devices!"); 120 121 // All block devices has one for the disk and one per partition 122 // There is a special case for a device with one fixed partition 123 // But we probably do not care about booting on that kind of device 124 // So find all disk block devices and let Haiku do partition scan 125 for (uint32 n = 0; n < noOfHandles; n++) { 126 if (kBootServices->HandleProtocol(handles[n], &BlockIoGUID, 127 (void**)&blockIo) != EFI_SUCCESS) 128 panic("Cannot get block device handle!"); 129 130 if (!blockIo->Media->MediaPresent || blockIo->Media->LogicalPartition) 131 continue; 132 133 EfiDevice *device = new(std::nothrow)EfiDevice(blockIo); 134 if (device == NULL) 135 panic("Can't allocate memory for block devices!"); 136 devicesList->Insert(device); 137 } 138 return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND; 139 } 140 141 status_t 142 platform_add_block_devices(struct stage2_args *args, NodeList *devicesList) 143 { 144 //TODO: Currently we add all in platform_add_boot_device 145 return B_ENTRY_NOT_FOUND; 146 } 147 148 status_t 149 platform_get_boot_partition(struct stage2_args *args, Node *bootDevice, 150 NodeList *partitions, boot::Partition **_partition) 151 { 152 *_partition = (boot::Partition*)partitions->GetIterator().Next(); 153 return *_partition != NULL ? B_OK : B_ENTRY_NOT_FOUND; 154 } 155 156 157 status_t 158 platform_register_boot_device(Node *device) 159 { 160 EfiDevice *efiDevice = (EfiDevice *)device; 161 disk_identifier identifier; 162 163 identifier.bus_type = UNKNOWN_BUS; 164 identifier.device_type = UNKNOWN_DEVICE; 165 identifier.device.unknown.size = device->Size(); 166 167 for (uint32 i = 0; i < NUM_DISK_CHECK_SUMS; ++i) { 168 off_t offset = get_next_check_sum_offset(i, device->Size()); 169 identifier.device.unknown.check_sums[i].offset = offset; 170 identifier.device.unknown.check_sums[i].sum = compute_check_sum(device, 171 offset); 172 } 173 174 gBootVolume.SetInt32(BOOT_METHOD, efiDevice->ReadOnly() ? BOOT_METHOD_CD: 175 BOOT_METHOD_HARD_DISK); 176 gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, efiDevice->ReadOnly()); 177 gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE, 178 &identifier, sizeof(disk_identifier)); 179 180 return B_OK; 181 } 182 183 184 void 185 platform_cleanup_devices() 186 { 187 } 188