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