1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "part-discovery.h"
10 union GptHeaderBuffer
{
11 EFI_PARTITION_TABLE_HEADER gpt_header
;
12 uint8_t space
[CONST_ALIGN_TO(sizeof(EFI_PARTITION_TABLE_HEADER
), 512)];
15 static EFI_DEVICE_PATH
*path_replace_hd(
16 const EFI_DEVICE_PATH
*path
,
17 const EFI_DEVICE_PATH
*node
,
18 const HARDDRIVE_DEVICE_PATH
*new_node
) {
20 /* Create a new device path as a copy of path, while chopping off the remainder starting at the given
21 * node. If new_node is provided, it is appended at the end of the new path. */
26 size_t len
= (uint8_t *) node
- (uint8_t *) path
, new_node_len
= 0;
28 new_node_len
= DevicePathNodeLength(&new_node
->Header
);
30 EFI_DEVICE_PATH
*ret
= xmalloc(len
+ new_node_len
+ END_DEVICE_PATH_LENGTH
);
31 EFI_DEVICE_PATH
*end
= mempcpy(ret
, path
, len
);
34 end
= mempcpy(end
, new_node
, new_node_len
);
36 SetDevicePathEndNode(end
);
40 static bool verify_gpt(union GptHeaderBuffer
*gpt_header_buffer
, EFI_LBA lba_expected
) {
41 EFI_PARTITION_TABLE_HEADER
*h
;
42 uint32_t crc32
, crc32_saved
;
45 assert(gpt_header_buffer
);
47 h
= &gpt_header_buffer
->gpt_header
;
49 /* Some superficial validation of the GPT header */
50 if (memcmp(&h
->Header
.Signature
, "EFI PART", sizeof(h
->Header
.Signature
)) != 0)
53 if (h
->Header
.HeaderSize
< 92 || h
->Header
.HeaderSize
> 512)
56 if (h
->Header
.Revision
!= 0x00010000U
)
59 /* Calculate CRC check */
60 crc32_saved
= h
->Header
.CRC32
;
62 err
= BS
->CalculateCrc32(gpt_header_buffer
, h
->Header
.HeaderSize
, &crc32
);
63 h
->Header
.CRC32
= crc32_saved
;
64 if (err
!= EFI_SUCCESS
|| crc32
!= crc32_saved
)
67 if (h
->MyLBA
!= lba_expected
)
70 if ((h
->SizeOfPartitionEntry
% sizeof(EFI_PARTITION_ENTRY
)) != 0)
73 if (h
->NumberOfPartitionEntries
<= 0 || h
->NumberOfPartitionEntries
> 1024)
77 if (h
->SizeOfPartitionEntry
> UINTN_MAX
/ h
->NumberOfPartitionEntries
)
83 static EFI_STATUS
try_gpt(
85 EFI_BLOCK_IO_PROTOCOL
*block_io
,
87 EFI_LBA
*ret_backup_lba
, /* May be changed even on error! */
88 HARDDRIVE_DEVICE_PATH
*ret_hd
) {
90 _cleanup_free_ EFI_PARTITION_ENTRY
*entries
= NULL
;
91 union GptHeaderBuffer gpt
;
99 /* Read the GPT header */
100 err
= block_io
->ReadBlocks(
102 block_io
->Media
->MediaId
,
105 if (err
!= EFI_SUCCESS
)
108 /* Indicate the location of backup LBA even if the rest of the header is corrupt. */
110 *ret_backup_lba
= gpt
.gpt_header
.AlternateLBA
;
112 if (!verify_gpt(&gpt
, lba
))
113 return EFI_NOT_FOUND
;
115 /* Now load the GPT entry table */
116 size
= ALIGN_TO((UINTN
) gpt
.gpt_header
.SizeOfPartitionEntry
* (UINTN
) gpt
.gpt_header
.NumberOfPartitionEntries
, 512);
117 entries
= xmalloc(size
);
119 err
= block_io
->ReadBlocks(
121 block_io
->Media
->MediaId
,
122 gpt
.gpt_header
.PartitionEntryLBA
,
124 if (err
!= EFI_SUCCESS
)
127 /* Calculate CRC of entries array, too */
128 err
= BS
->CalculateCrc32(entries
, size
, &crc32
);
129 if (err
!= EFI_SUCCESS
|| crc32
!= gpt
.gpt_header
.PartitionEntryArrayCRC32
)
130 return EFI_CRC_ERROR
;
132 /* Now we can finally look for xbootloader partitions. */
133 for (UINTN i
= 0; i
< gpt
.gpt_header
.NumberOfPartitionEntries
; i
++) {
134 EFI_PARTITION_ENTRY
*entry
=
135 (EFI_PARTITION_ENTRY
*) ((uint8_t *) entries
+ gpt
.gpt_header
.SizeOfPartitionEntry
* i
);
137 if (memcmp(&entry
->PartitionTypeGUID
, type
, sizeof(entry
->PartitionTypeGUID
)) != 0)
140 if (entry
->EndingLBA
< entry
->StartingLBA
) /* Bogus? */
143 *ret_hd
= (HARDDRIVE_DEVICE_PATH
) {
145 .Type
= MEDIA_DEVICE_PATH
,
146 .SubType
= MEDIA_HARDDRIVE_DP
,
148 .PartitionNumber
= i
+ 1,
149 .PartitionStart
= entry
->StartingLBA
,
150 .PartitionSize
= entry
->EndingLBA
- entry
->StartingLBA
+ 1,
151 .MBRType
= MBR_TYPE_EFI_PARTITION_TABLE_HEADER
,
152 .SignatureType
= SIGNATURE_TYPE_GUID
,
154 memcpy(ret_hd
->Signature
, &entry
->UniquePartitionGUID
, sizeof(ret_hd
->Signature
));
156 /* HARDDRIVE_DEVICE_PATH has padding, which at least OVMF does not like. */
157 SetDevicePathNodeLength(
159 offsetof(HARDDRIVE_DEVICE_PATH
, SignatureType
) + sizeof(ret_hd
->SignatureType
));
164 /* This GPT was fully valid, but we didn't find what we are looking for. This
165 * means there's no reason to check the second copy of the GPT header */
166 return EFI_NOT_FOUND
;
169 static EFI_STATUS
find_device(const EFI_GUID
*type
, EFI_HANDLE
*device
, EFI_DEVICE_PATH
**ret_device_path
) {
173 assert(ret_device_path
);
175 EFI_DEVICE_PATH
*partition_path
;
176 err
= BS
->HandleProtocol(device
, &DevicePathProtocol
, (void **) &partition_path
);
177 if (err
!= EFI_SUCCESS
)
180 /* Find the (last) partition node itself. */
181 EFI_DEVICE_PATH
*part_node
= NULL
;
182 for (EFI_DEVICE_PATH
*node
= partition_path
; !IsDevicePathEnd(node
); node
= NextDevicePathNode(node
)) {
183 if (DevicePathType(node
) != MEDIA_DEVICE_PATH
)
186 if (DevicePathSubType(node
) != MEDIA_HARDDRIVE_DP
)
193 return EFI_NOT_FOUND
;
195 /* Chop off the partition part, leaving us with the full path to the disk itself. */
196 _cleanup_free_ EFI_DEVICE_PATH
*disk_path
= NULL
;
197 EFI_DEVICE_PATH
*p
= disk_path
= path_replace_hd(partition_path
, part_node
, NULL
);
199 EFI_HANDLE disk_handle
;
200 EFI_BLOCK_IO_PROTOCOL
*block_io
;
201 err
= BS
->LocateDevicePath(&BlockIoProtocol
, &p
, &disk_handle
);
202 if (err
!= EFI_SUCCESS
)
205 err
= BS
->HandleProtocol(disk_handle
, &BlockIoProtocol
, (void **)&block_io
);
206 if (err
!= EFI_SUCCESS
)
209 /* Filter out some block devices early. (We only care about block devices that aren't
210 * partitions themselves — we look for GPT partition tables to parse after all —, and only
211 * those which contain a medium and have at least 2 blocks.) */
212 if (block_io
->Media
->LogicalPartition
||
213 !block_io
->Media
->MediaPresent
||
214 block_io
->Media
->LastBlock
<= 1)
215 return EFI_NOT_FOUND
;
217 /* Try several copies of the GPT header, in case one is corrupted */
218 EFI_LBA backup_lba
= 0;
219 for (UINTN nr
= 0; nr
< 3; nr
++) {
222 /* Read the first copy at LBA 1 and then try the backup GPT header pointed
223 * to by the first header if that one was corrupted. As a last resort,
224 * try the very last LBA of this block device. */
227 else if (nr
== 1 && backup_lba
!= 0)
229 else if (nr
== 2 && backup_lba
!= block_io
->Media
->LastBlock
)
230 lba
= block_io
->Media
->LastBlock
;
234 HARDDRIVE_DEVICE_PATH hd
;
235 err
= try_gpt(type
, block_io
, lba
,
236 nr
== 0 ? &backup_lba
: NULL
, /* Only get backup LBA location from first GPT header. */
238 if (err
!= EFI_SUCCESS
) {
239 /* GPT was valid but no XBOOT loader partition found. */
240 if (err
== EFI_NOT_FOUND
)
242 /* Bad GPT, try next one. */
246 /* Patch in the data we found */
247 *ret_device_path
= path_replace_hd(partition_path
, part_node
, &hd
);
251 /* No xbootloader partition found */
252 return EFI_NOT_FOUND
;
255 EFI_STATUS
partition_open(const EFI_GUID
*type
, EFI_HANDLE
*device
, EFI_HANDLE
*ret_device
,
256 EFI_FILE
**ret_root_dir
) {
257 _cleanup_free_ EFI_DEVICE_PATH
*partition_path
= NULL
;
258 EFI_HANDLE new_device
;
264 assert(ret_root_dir
);
266 err
= find_device(type
, device
, &partition_path
);
267 if (err
!= EFI_SUCCESS
)
270 EFI_DEVICE_PATH
*dp
= partition_path
;
271 err
= BS
->LocateDevicePath(&BlockIoProtocol
, &dp
, &new_device
);
272 if (err
!= EFI_SUCCESS
)
275 err
= open_volume(new_device
, &root_dir
);
276 if (err
!= EFI_SUCCESS
)
280 *ret_device
= new_device
;
281 *ret_root_dir
= root_dir
;