]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/xbootldr.c
test: add shutdown test
[thirdparty/systemd.git] / src / boot / efi / xbootldr.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <efi.h>
4 #include <efigpt.h>
5 #include <efilib.h>
6
7 #include "util.h"
8 #include "xbootldr.h"
9
10 union GptHeaderBuffer {
11 EFI_PARTITION_TABLE_HEADER gpt_header;
12 uint8_t space[CONST_ALIGN_TO(sizeof(EFI_PARTITION_TABLE_HEADER), 512)];
13 };
14
15 static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
16 EFI_DEVICE_PATH *parent;
17 UINTN len;
18
19 assert(path);
20 assert(node);
21
22 len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
23 parent = (EFI_DEVICE_PATH*) xallocate_pool(len + sizeof(EFI_DEVICE_PATH));
24
25 CopyMem(parent, path, len);
26 CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH));
27
28 return parent;
29 }
30
31 static BOOLEAN verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_expected) {
32 EFI_PARTITION_TABLE_HEADER *h;
33 UINT32 crc32, crc32_saved;
34 EFI_STATUS err;
35
36 assert(gpt_header_buffer);
37
38 h = &gpt_header_buffer->gpt_header;
39
40 /* Some superficial validation of the GPT header */
41 if(CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature) != 0))
42 return FALSE;
43
44 if (h->Header.HeaderSize < 92 || h->Header.HeaderSize > 512)
45 return FALSE;
46
47 if (h->Header.Revision != 0x00010000U)
48 return FALSE;
49
50 /* Calculate CRC check */
51 crc32_saved = h->Header.CRC32;
52 h->Header.CRC32 = 0;
53 err = BS->CalculateCrc32(gpt_header_buffer, h->Header.HeaderSize, &crc32);
54 h->Header.CRC32 = crc32_saved;
55 if (EFI_ERROR(err) || crc32 != crc32_saved)
56 return FALSE;
57
58 if (h->MyLBA != lba_expected)
59 return FALSE;
60
61 if (h->SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY))
62 return FALSE;
63
64 if (h->NumberOfPartitionEntries <= 0 || h->NumberOfPartitionEntries > 1024)
65 return FALSE;
66
67 /* overflow check */
68 if (h->SizeOfPartitionEntry > UINTN_MAX / h->NumberOfPartitionEntries)
69 return FALSE;
70
71 return TRUE;
72 }
73
74 static EFI_STATUS try_gpt(
75 EFI_BLOCK_IO *block_io,
76 EFI_LBA lba,
77 EFI_LBA *ret_backup_lba, /* May be changed even on error! */
78 UINT32 *ret_part_number,
79 UINT64 *ret_part_start,
80 UINT64 *ret_part_size,
81 EFI_GUID *ret_part_uuid) {
82
83 _cleanup_freepool_ EFI_PARTITION_ENTRY *entries = NULL;
84 union GptHeaderBuffer gpt;
85 EFI_STATUS err;
86 UINT32 crc32;
87 UINTN size;
88
89 assert(block_io);
90 assert(ret_part_number);
91 assert(ret_part_start);
92 assert(ret_part_size);
93 assert(ret_part_uuid);
94
95 /* Read the GPT header */
96 err = block_io->ReadBlocks(
97 block_io,
98 block_io->Media->MediaId,
99 lba,
100 sizeof(gpt), &gpt);
101 if (EFI_ERROR(err))
102 return err;
103
104 /* Indicate the location of backup LBA even if the rest of the header is corrupt. */
105 if (ret_backup_lba)
106 *ret_backup_lba = gpt.gpt_header.AlternateLBA;
107
108 if (!verify_gpt(&gpt, lba))
109 return EFI_NOT_FOUND;
110
111 /* Now load the GPT entry table */
112 size = ALIGN_TO((UINTN) gpt.gpt_header.SizeOfPartitionEntry * (UINTN) gpt.gpt_header.NumberOfPartitionEntries, 512);
113 entries = xallocate_pool(size);
114
115 err = block_io->ReadBlocks(
116 block_io,
117 block_io->Media->MediaId,
118 gpt.gpt_header.PartitionEntryLBA,
119 size, entries);
120 if (EFI_ERROR(err))
121 return err;
122
123 /* Calculate CRC of entries array, too */
124 err = BS->CalculateCrc32(entries, size, &crc32);
125 if (EFI_ERROR(err) || crc32 != gpt.gpt_header.PartitionEntryArrayCRC32)
126 return EFI_CRC_ERROR;
127
128 /* Now we can finally look for xbootloader partitions. */
129 for (UINTN i = 0; i < gpt.gpt_header.NumberOfPartitionEntries; i++) {
130 EFI_PARTITION_ENTRY *entry;
131 EFI_LBA start, end;
132
133 entry = (EFI_PARTITION_ENTRY*) ((UINT8*) entries + gpt.gpt_header.SizeOfPartitionEntry * i);
134
135 if (CompareMem(&entry->PartitionTypeGUID, XBOOTLDR_GUID, sizeof(entry->PartitionTypeGUID)) != 0)
136 continue;
137
138 /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */
139 CopyMem(&start, &entry->StartingLBA, sizeof(start));
140 CopyMem(&end, &entry->EndingLBA, sizeof(end));
141
142 if (end < start) /* Bogus? */
143 continue;
144
145 *ret_part_number = i + 1;
146 *ret_part_start = start;
147 *ret_part_size = end - start + 1;
148 CopyMem(ret_part_uuid, &entry->UniquePartitionGUID, sizeof(*ret_part_uuid));
149
150 return EFI_SUCCESS;
151 }
152
153 /* This GPT was fully valid, but we didn't find what we are looking for. This
154 * means there's no reason to check the second copy of the GPT header */
155 return EFI_NOT_FOUND;
156 }
157
158 static EFI_STATUS find_device(
159 EFI_HANDLE *device,
160 EFI_DEVICE_PATH **ret_device_path,
161 UINT32 *ret_part_number,
162 UINT64 *ret_part_start,
163 UINT64 *ret_part_size,
164 EFI_GUID *ret_part_uuid) {
165
166 EFI_DEVICE_PATH *partition_path;
167 EFI_STATUS err;
168
169 assert(device);
170 assert(ret_device_path);
171 assert(ret_part_number);
172 assert(ret_part_start);
173 assert(ret_part_size);
174 assert(ret_part_uuid);
175
176 partition_path = DevicePathFromHandle(device);
177 if (!partition_path)
178 return EFI_NOT_FOUND;
179
180 for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
181 _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL;
182 EFI_HANDLE disk_handle;
183 EFI_BLOCK_IO *block_io;
184 EFI_DEVICE_PATH *p;
185
186 /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media
187 * devices */
188 if (DevicePathType(node) != MESSAGING_DEVICE_PATH)
189 continue;
190
191 /* Determine the device path one level up */
192 disk_path = p = path_parent(partition_path, node);
193 if (!disk_path)
194 continue;
195
196 err = BS->LocateDevicePath(&BlockIoProtocol, &p, &disk_handle);
197 if (EFI_ERROR(err))
198 continue;
199
200 err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io);
201 if (EFI_ERROR(err))
202 continue;
203
204 /* Filter out some block devices early. (We only care about block devices that aren't
205 * partitions themselves — we look for GPT partition tables to parse after all —, and only
206 * those which contain a medium and have at least 2 blocks.) */
207 if (block_io->Media->LogicalPartition ||
208 !block_io->Media->MediaPresent ||
209 block_io->Media->LastBlock <= 1)
210 continue;
211
212 /* Try several copies of the GPT header, in case one is corrupted */
213 EFI_LBA backup_lba = 0;
214 for (UINTN nr = 0; nr < 3; nr++) {
215 EFI_LBA lba;
216
217 /* Read the first copy at LBA 1 and then try the backup GPT header pointed
218 * to by the first header if that one was corrupted. As a last resort,
219 * try the very last LBA of this block device. */
220 if (nr == 0)
221 lba = 1;
222 else if (nr == 1 && backup_lba != 0)
223 lba = backup_lba;
224 else if (nr == 2 && backup_lba != block_io->Media->LastBlock)
225 lba = block_io->Media->LastBlock;
226 else
227 continue;
228
229 err = try_gpt(
230 block_io, lba,
231 nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */
232 ret_part_number,
233 ret_part_start,
234 ret_part_size,
235 ret_part_uuid);
236 if (!EFI_ERROR(err)) {
237 *ret_device_path = DuplicateDevicePath(partition_path);
238 if (!*ret_device_path)
239 return EFI_OUT_OF_RESOURCES;
240 return EFI_SUCCESS;
241 }
242
243 /* GPT was valid but no XBOOT loader partition found. */
244 if (err == EFI_NOT_FOUND)
245 break;
246 }
247 }
248
249 /* No xbootloader partition found */
250 return EFI_NOT_FOUND;
251 }
252
253 EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir) {
254 _cleanup_freepool_ EFI_DEVICE_PATH *partition_path = NULL;
255 UINT32 part_number = UINT32_MAX;
256 UINT64 part_start = UINT64_MAX, part_size = UINT64_MAX;
257 EFI_HANDLE new_device;
258 EFI_FILE *root_dir;
259 EFI_GUID part_uuid;
260 EFI_STATUS err;
261
262 assert(device);
263 assert(ret_device);
264 assert(ret_root_dir);
265
266 err = find_device(device, &partition_path, &part_number, &part_start, &part_size, &part_uuid);
267 if (EFI_ERROR(err))
268 return err;
269
270 /* Patch in the data we found */
271 for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
272 HARDDRIVE_DEVICE_PATH *hd;
273
274 if (DevicePathType(node) != MEDIA_DEVICE_PATH)
275 continue;
276
277 if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)
278 continue;
279
280 hd = (HARDDRIVE_DEVICE_PATH*) node;
281 hd->PartitionNumber = part_number;
282 hd->PartitionStart = part_start;
283 hd->PartitionSize = part_size;
284 CopyMem(hd->Signature, &part_uuid, sizeof(hd->Signature));
285 hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
286 hd->SignatureType = SIGNATURE_TYPE_GUID;
287 }
288
289 EFI_DEVICE_PATH *dp = partition_path;
290 err = BS->LocateDevicePath(&BlockIoProtocol, &dp, &new_device);
291 if (EFI_ERROR(err))
292 return err;
293
294 root_dir = LibOpenRoot(new_device);
295 if (!root_dir)
296 return EFI_NOT_FOUND;
297
298 *ret_device = new_device;
299 *ret_root_dir = root_dir;
300 return EFI_SUCCESS;
301 }