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