xref: /haiku/src/system/boot/platform/efi/devices.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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