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