xref: /haiku/src/system/boot/platform/efi/devices.cpp (revision 8c78892580f132d10e624aef96f835df8d94bf19)
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 //#define TRACE_DEVICES
16 #ifdef TRACE_DEVICES
17 #   define TRACE(x...) dprintf("efi/devices: " x)
18 #else
19 #   define TRACE(x...) ;
20 #endif
21 
22 
23 static efi_guid BlockIoGUID = EFI_BLOCK_IO_PROTOCOL_GUID;
24 
25 
26 class EfiDevice : public Node
27 {
28 	public:
29 		EfiDevice(efi_block_io_protocol *blockIo);
30 		virtual ~EfiDevice();
31 
32 		virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer,
33 			size_t bufferSize);
34 		virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer,
35 			size_t bufferSize) { return B_UNSUPPORTED; }
36 		virtual off_t Size() const {
37 			return (fBlockIo->Media->LastBlock + 1) * BlockSize(); }
38 
39 		uint32 BlockSize() const { return fBlockIo->Media->BlockSize; }
40 		bool ReadOnly() const { return fBlockIo->Media->ReadOnly; }
41 	private:
42 		efi_block_io_protocol*		fBlockIo;
43 };
44 
45 
46 EfiDevice::EfiDevice(efi_block_io_protocol *blockIo)
47 	:
48 	fBlockIo(blockIo)
49 {
50 }
51 
52 
53 EfiDevice::~EfiDevice()
54 {
55 }
56 
57 
58 ssize_t
59 EfiDevice::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
60 {
61 	TRACE("%s called. pos: %" B_PRIdOFF ", %p, %" B_PRIuSIZE "\n", __func__,
62 		pos, buffer, bufferSize);
63 
64 	off_t offset = pos % BlockSize();
65 	pos /= BlockSize();
66 
67 	uint32 numBlocks = (offset + bufferSize + BlockSize()) / BlockSize();
68 	char readBuffer[numBlocks * BlockSize()];
69 
70 	if (fBlockIo->ReadBlocks(fBlockIo, fBlockIo->Media->MediaId,
71 		pos, sizeof(readBuffer), readBuffer) != EFI_SUCCESS)
72 		return B_ERROR;
73 
74 	memcpy(buffer, readBuffer + offset, bufferSize);
75 
76 	return bufferSize;
77 }
78 
79 
80 static off_t
81 get_next_check_sum_offset(int32 index, off_t maxSize)
82 {
83 	TRACE("%s: called\n", __func__);
84 
85 	if (index < 2)
86 		return index * 512;
87 
88 	if (index < 4)
89 		return (maxSize >> 10) + index * 2048;
90 
91 	return ((system_time() + index) % (maxSize >> 9)) * 512;
92 }
93 
94 
95 static uint32
96 compute_check_sum(Node *device, off_t offset)
97 {
98 	TRACE("%s: called\n", __func__);
99 
100 	char buffer[512];
101 	ssize_t bytesRead = device->ReadAt(NULL, offset, buffer, sizeof(buffer));
102 	if (bytesRead < B_OK)
103 		return 0;
104 
105 	if (bytesRead < (ssize_t)sizeof(buffer))
106 		memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead);
107 
108 	uint32 *array = (uint32*)buffer;
109 	uint32 sum = 0;
110 
111 	for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++)
112 		sum += array[i];
113 
114 	return sum;
115 }
116 
117 
118 status_t
119 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
120 {
121 	TRACE("%s: called\n", __func__);
122 
123 	efi_block_io_protocol *blockIo;
124 	size_t memSize = 0;
125 
126 	// Read to zero sized buffer to get memory needed for handles
127 	if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 0)
128 			!= EFI_BUFFER_TOO_SMALL)
129 		panic("Cannot read size of block device handles!");
130 
131 	uint32 noOfHandles = memSize / sizeof(efi_handle);
132 
133 	efi_handle handles[noOfHandles];
134 	if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize,
135 			handles) != EFI_SUCCESS)
136 		panic("Failed to locate block devices!");
137 
138 	// All block devices has one for the disk and one per partition
139 	// There is a special case for a device with one fixed partition
140 	// But we probably do not care about booting on that kind of device
141 	// So find all disk block devices and let Haiku do partition scan
142 	for (uint32 n = 0; n < noOfHandles; n++) {
143 		if (kBootServices->HandleProtocol(handles[n], &BlockIoGUID,
144 				(void**)&blockIo) != EFI_SUCCESS)
145 			panic("Cannot get block device handle!");
146 
147 		TRACE("%s: %p: present: %s, logical: %s, removeable: %s, "
148 			"blocksize: %" B_PRIuSIZE ", lastblock: %" B_PRIu64 "\n",
149 			__func__, blockIo,
150 			blockIo->Media->MediaPresent ? "true" : "false",
151 			blockIo->Media->LogicalPartition ? "true" : "false",
152 			blockIo->Media->RemovableMedia ? "true" : "false",
153 			blockIo->Media->BlockSize, blockIo->Media->LastBlock);
154 
155 		if (!blockIo->Media->MediaPresent || blockIo->Media->LogicalPartition)
156 			continue;
157 
158 		// The qemu flash device with a 256K block sizes sometime show up
159 		// in edk2. If flash is unconfigured, bad things happen on arm.
160 		// edk2 bug: https://bugzilla.tianocore.org/show_bug.cgi?id=2856
161 		// We're not ready for flash devices in efi, so skip anything odd.
162 		if (blockIo->Media->BlockSize > 8192)
163 			continue;
164 
165 		EfiDevice *device = new(std::nothrow)EfiDevice(blockIo);
166 		if (device == NULL)
167 			panic("Can't allocate memory for block devices!");
168 		devicesList->Insert(device);
169 	}
170 	return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND;
171 }
172 
173 status_t
174 platform_add_block_devices(struct stage2_args *args, NodeList *devicesList)
175 {
176 	TRACE("%s: called\n", __func__);
177 
178 	//TODO: Currently we add all in platform_add_boot_device
179 	return B_ENTRY_NOT_FOUND;
180 }
181 
182 status_t
183 platform_get_boot_partition(struct stage2_args *args, Node *bootDevice,
184 		NodeList *partitions, boot::Partition **_partition)
185 {
186 	TRACE("%s: called\n", __func__);
187 	*_partition = (boot::Partition*)partitions->GetIterator().Next();
188 	return *_partition != NULL ? B_OK : B_ENTRY_NOT_FOUND;
189 }
190 
191 
192 status_t
193 platform_register_boot_device(Node *device)
194 {
195 	TRACE("%s: called\n", __func__);
196 
197 	EfiDevice *efiDevice = (EfiDevice *)device;
198 	disk_identifier identifier;
199 
200 	identifier.bus_type = UNKNOWN_BUS;
201 	identifier.device_type = UNKNOWN_DEVICE;
202 	identifier.device.unknown.size = device->Size();
203 
204 	for (uint32 i = 0; i < NUM_DISK_CHECK_SUMS; ++i) {
205 		off_t offset = get_next_check_sum_offset(i, device->Size());
206 		identifier.device.unknown.check_sums[i].offset = offset;
207 		identifier.device.unknown.check_sums[i].sum = compute_check_sum(device,
208 			offset);
209 	}
210 
211 	gBootVolume.SetInt32(BOOT_METHOD, efiDevice->ReadOnly() ? BOOT_METHOD_CD:
212 		BOOT_METHOD_HARD_DISK);
213 	gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, efiDevice->ReadOnly());
214 	gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
215 		&identifier, sizeof(disk_identifier));
216 
217 	return B_OK;
218 }
219 
220 
221 void
222 platform_cleanup_devices()
223 {
224 }
225