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