1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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_parent(EFI_DEVICE_PATH
*path
, EFI_DEVICE_PATH
*node
) {
16 EFI_DEVICE_PATH
*parent
;
22 len
= (UINT8
*) NextDevicePathNode(node
) - (UINT8
*) path
;
23 parent
= (EFI_DEVICE_PATH
*) xallocate_pool(len
+ sizeof(EFI_DEVICE_PATH
));
25 CopyMem(parent
, path
, len
);
26 CopyMem((UINT8
*) parent
+ len
, EndDevicePath
, sizeof(EFI_DEVICE_PATH
));
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
;
36 assert(gpt_header_buffer
);
38 h
= &gpt_header_buffer
->gpt_header
;
40 /* Some superficial validation of the GPT header */
41 if(CompareMem(&h
->Header
.Signature
, "EFI PART", sizeof(h
->Header
.Signature
) != 0))
44 if (h
->Header
.HeaderSize
< 92 || h
->Header
.HeaderSize
> 512)
47 if (h
->Header
.Revision
!= 0x00010000U
)
50 /* Calculate CRC check */
51 crc32_saved
= h
->Header
.CRC32
;
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
)
58 if (h
->MyLBA
!= lba_expected
)
61 if (h
->SizeOfPartitionEntry
< sizeof(EFI_PARTITION_ENTRY
))
64 if (h
->NumberOfPartitionEntries
<= 0 || h
->NumberOfPartitionEntries
> 1024)
68 if (h
->SizeOfPartitionEntry
> UINTN_MAX
/ h
->NumberOfPartitionEntries
)
74 static EFI_STATUS
try_gpt(
75 EFI_BLOCK_IO
*block_io
,
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
) {
83 _cleanup_freepool_ EFI_PARTITION_ENTRY
*entries
= NULL
;
84 union GptHeaderBuffer gpt
;
90 assert(ret_part_number
);
91 assert(ret_part_start
);
92 assert(ret_part_size
);
93 assert(ret_part_uuid
);
95 /* Read the GPT header */
96 err
= block_io
->ReadBlocks(
98 block_io
->Media
->MediaId
,
104 /* Indicate the location of backup LBA even if the rest of the header is corrupt. */
106 *ret_backup_lba
= gpt
.gpt_header
.AlternateLBA
;
108 if (!verify_gpt(&gpt
, lba
))
109 return EFI_NOT_FOUND
;
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
);
115 err
= block_io
->ReadBlocks(
117 block_io
->Media
->MediaId
,
118 gpt
.gpt_header
.PartitionEntryLBA
,
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
;
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
;
133 entry
= (EFI_PARTITION_ENTRY
*) ((UINT8
*) entries
+ gpt
.gpt_header
.SizeOfPartitionEntry
* i
);
135 if (CompareMem(&entry
->PartitionTypeGUID
, XBOOTLDR_GUID
, sizeof(entry
->PartitionTypeGUID
)) != 0)
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
));
142 if (end
< start
) /* Bogus? */
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
));
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
;
158 static EFI_STATUS
find_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
) {
166 EFI_DEVICE_PATH
*partition_path
;
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
);
176 partition_path
= DevicePathFromHandle(device
);
178 return EFI_NOT_FOUND
;
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
;
186 /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media
188 if (DevicePathType(node
) != MESSAGING_DEVICE_PATH
)
191 /* Determine the device path one level up */
192 disk_path
= p
= path_parent(partition_path
, node
);
196 err
= BS
->LocateDevicePath(&BlockIoProtocol
, &p
, &disk_handle
);
200 err
= BS
->HandleProtocol(disk_handle
, &BlockIoProtocol
, (void **)&block_io
);
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)
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
++) {
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. */
222 else if (nr
== 1 && backup_lba
!= 0)
224 else if (nr
== 2 && backup_lba
!= block_io
->Media
->LastBlock
)
225 lba
= block_io
->Media
->LastBlock
;
231 nr
== 0 ? &backup_lba
: NULL
, /* Only get backup LBA location from first GPT header. */
236 if (!EFI_ERROR(err
)) {
237 *ret_device_path
= DuplicateDevicePath(partition_path
);
238 if (!*ret_device_path
)
239 return EFI_OUT_OF_RESOURCES
;
243 /* GPT was valid but no XBOOT loader partition found. */
244 if (err
== EFI_NOT_FOUND
)
249 /* No xbootloader partition found */
250 return EFI_NOT_FOUND
;
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
;
264 assert(ret_root_dir
);
266 err
= find_device(device
, &partition_path
, &part_number
, &part_start
, &part_size
, &part_uuid
);
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
;
274 if (DevicePathType(node
) != MEDIA_DEVICE_PATH
)
277 if (DevicePathSubType(node
) != MEDIA_HARDDRIVE_DP
)
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
;
289 EFI_DEVICE_PATH
*dp
= partition_path
;
290 err
= BS
->LocateDevicePath(&BlockIoProtocol
, &dp
, &new_device
);
294 root_dir
= LibOpenRoot(new_device
);
296 return EFI_NOT_FOUND
;
298 *ret_device
= new_device
;
299 *ret_root_dir
= root_dir
;