xref: /haiku/src/system/boot/platform/efi/devices.cpp (revision 5f4f984a94d150153bcb00a2ed780d0437859543)
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_partitions(struct stage2_args *args, Node *bootDevice,
486 		NodeList *partitions, NodeList *bootPartitions)
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 			bootPartitions->Insert(partition);
493 	}
494 
495 	return bootPartitions->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND;
496 }
497 
498 
499 status_t
500 platform_register_boot_device(Node *device)
501 {
502 	EfiDevice *efiDevice = (EfiDevice *)device;
503 	disk_identifier identifier;
504 
505 	// TODO: Setup using device path
506 	identifier.bus_type = UNKNOWN_BUS;
507 	identifier.device_type = UNKNOWN_DEVICE;
508 	identifier.device.unknown.size = device->Size();
509 
510 	for (uint32 i = 0; i < NUM_DISK_CHECK_SUMS; ++i) {
511 		off_t offset = get_next_check_sum_offset(i, device->Size());
512 		identifier.device.unknown.check_sums[i].offset = offset;
513 		identifier.device.unknown.check_sums[i].sum = compute_check_sum(device, offset);
514 	}
515 
516 	gBootVolume.SetInt32(BOOT_METHOD, efiDevice->BootMethod());
517 	gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, efiDevice->ReadOnly());
518 	gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
519 		&identifier, sizeof(disk_identifier));
520 
521 	return B_OK;
522 }
523