From 45a24eb7e991eba9c2aff6e981617a3634666ef5 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Tue, 18 Jan 2022 12:24:26 +0100 Subject: [PATCH 1/3] boot: Search for the partition node directly Some firmware creates partition device paths without a messaging device path node, making the search for the xboot loader partition fail. Fixes: #17756 --- src/boot/efi/xbootldr.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/boot/efi/xbootldr.c b/src/boot/efi/xbootldr.c index 4972877d209..26b77c93448 100644 --- a/src/boot/efi/xbootldr.c +++ b/src/boot/efi/xbootldr.c @@ -12,20 +12,17 @@ union GptHeaderBuffer { uint8_t space[CONST_ALIGN_TO(sizeof(EFI_PARTITION_TABLE_HEADER), 512)]; }; -static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) { - EFI_DEVICE_PATH *parent; - UINTN len; - +static EFI_DEVICE_PATH *path_chop(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) { assert(path); assert(node); - len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path; - parent = (EFI_DEVICE_PATH*) xallocate_pool(len + sizeof(EFI_DEVICE_PATH)); + UINTN len = (UINT8 *) node - (UINT8 *) path; + EFI_DEVICE_PATH *chopped = xallocate_pool(len + END_DEVICE_PATH_LENGTH); - CopyMem(parent, path, len); - CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH)); + CopyMem(chopped, path, len); + SetDevicePathEndNode((EFI_DEVICE_PATH *) ((UINT8 *) chopped + len)); - return parent; + return chopped; } static BOOLEAN verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_expected) { @@ -183,13 +180,14 @@ static EFI_STATUS find_device( EFI_BLOCK_IO *block_io; EFI_DEVICE_PATH *p; - /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media - * devices */ - if (DevicePathType(node) != MESSAGING_DEVICE_PATH) + if (DevicePathType(node) != MEDIA_DEVICE_PATH) continue; - /* Determine the device path one level up */ - disk_path = p = path_parent(partition_path, node); + if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) + continue; + + /* Chop off the partition part, leaving us with the path to the disk itself. */ + disk_path = p = path_chop(partition_path, node); if (!disk_path) continue; From d81edd5ad7fb856cf4f96cbd6d84d8f0ed3054f8 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Tue, 18 Jan 2022 13:01:39 +0100 Subject: [PATCH 2/3] boot: Simplify looking for the xboot hard drive The device path should not contain multiple hard drive nodes in it, so looking at them all should not be needed. If some crazy firmware/driver were to make nested GPT drives available like that, we should be only looking at the last partition and its containing GPT drive anyway. --- src/boot/efi/xbootldr.c | 122 ++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/src/boot/efi/xbootldr.c b/src/boot/efi/xbootldr.c index 26b77c93448..bee1d3ea4ae 100644 --- a/src/boot/efi/xbootldr.c +++ b/src/boot/efi/xbootldr.c @@ -160,7 +160,6 @@ static EFI_STATUS find_device( UINT64 *ret_part_size, EFI_GUID *ret_part_uuid) { - EFI_DEVICE_PATH *partition_path; EFI_STATUS err; assert(device); @@ -170,78 +169,81 @@ static EFI_STATUS find_device( assert(ret_part_size); assert(ret_part_uuid); - partition_path = DevicePathFromHandle(device); + EFI_DEVICE_PATH *partition_path = DevicePathFromHandle(device); if (!partition_path) return EFI_NOT_FOUND; + /* Find the (last) partition node itself. */ + EFI_DEVICE_PATH *part_node = NULL; for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { - _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL; - EFI_HANDLE disk_handle; - EFI_BLOCK_IO *block_io; - EFI_DEVICE_PATH *p; - if (DevicePathType(node) != MEDIA_DEVICE_PATH) continue; if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) continue; - /* Chop off the partition part, leaving us with the path to the disk itself. */ - disk_path = p = path_chop(partition_path, node); - if (!disk_path) + part_node = node; + } + + if (!part_node) + return EFI_NOT_FOUND; + + /* Chop off the partition part, leaving us with the full path to the disk itself. */ + _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL; + EFI_DEVICE_PATH *p = disk_path = path_chop(partition_path, part_node); + + EFI_HANDLE disk_handle; + EFI_BLOCK_IO *block_io; + err = BS->LocateDevicePath(&BlockIoProtocol, &p, &disk_handle); + if (EFI_ERROR(err)) + return err; + + err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io); + if (EFI_ERROR(err)) + return err; + + /* Filter out some block devices early. (We only care about block devices that aren't + * partitions themselves — we look for GPT partition tables to parse after all —, and only + * those which contain a medium and have at least 2 blocks.) */ + if (block_io->Media->LogicalPartition || + !block_io->Media->MediaPresent || + block_io->Media->LastBlock <= 1) + return EFI_NOT_FOUND; + + /* Try several copies of the GPT header, in case one is corrupted */ + EFI_LBA backup_lba = 0; + for (UINTN nr = 0; nr < 3; nr++) { + EFI_LBA lba; + + /* Read the first copy at LBA 1 and then try the backup GPT header pointed + * to by the first header if that one was corrupted. As a last resort, + * try the very last LBA of this block device. */ + if (nr == 0) + lba = 1; + else if (nr == 1 && backup_lba != 0) + lba = backup_lba; + else if (nr == 2 && backup_lba != block_io->Media->LastBlock) + lba = block_io->Media->LastBlock; + else continue; - err = BS->LocateDevicePath(&BlockIoProtocol, &p, &disk_handle); - if (EFI_ERROR(err)) - continue; - - err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io); - if (EFI_ERROR(err)) - continue; - - /* Filter out some block devices early. (We only care about block devices that aren't - * partitions themselves — we look for GPT partition tables to parse after all —, and only - * those which contain a medium and have at least 2 blocks.) */ - if (block_io->Media->LogicalPartition || - !block_io->Media->MediaPresent || - block_io->Media->LastBlock <= 1) - continue; - - /* Try several copies of the GPT header, in case one is corrupted */ - EFI_LBA backup_lba = 0; - for (UINTN nr = 0; nr < 3; nr++) { - EFI_LBA lba; - - /* Read the first copy at LBA 1 and then try the backup GPT header pointed - * to by the first header if that one was corrupted. As a last resort, - * try the very last LBA of this block device. */ - if (nr == 0) - lba = 1; - else if (nr == 1 && backup_lba != 0) - lba = backup_lba; - else if (nr == 2 && backup_lba != block_io->Media->LastBlock) - lba = block_io->Media->LastBlock; - else - continue; - - err = try_gpt( - block_io, lba, - nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */ - ret_part_number, - ret_part_start, - ret_part_size, - ret_part_uuid); - if (!EFI_ERROR(err)) { - *ret_device_path = DuplicateDevicePath(partition_path); - if (!*ret_device_path) - return EFI_OUT_OF_RESOURCES; - return EFI_SUCCESS; - } - - /* GPT was valid but no XBOOT loader partition found. */ - if (err == EFI_NOT_FOUND) - break; + err = try_gpt( + block_io, lba, + nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */ + ret_part_number, + ret_part_start, + ret_part_size, + ret_part_uuid); + if (!EFI_ERROR(err)) { + *ret_device_path = DuplicateDevicePath(partition_path); + if (!*ret_device_path) + return EFI_OUT_OF_RESOURCES; + return EFI_SUCCESS; } + + /* GPT was valid but no XBOOT loader partition found. */ + if (err == EFI_NOT_FOUND) + break; } /* No xbootloader partition found */ From 9e814b7715aa67c60a8cc4e76446130e803cf951 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Tue, 18 Jan 2022 13:35:53 +0100 Subject: [PATCH 3/3] boot: Pass around HARDDRIVE_DEVICE_PATH --- src/boot/efi/xbootldr.c | 82 ++++++++++++----------------------------- 1 file changed, 23 insertions(+), 59 deletions(-) diff --git a/src/boot/efi/xbootldr.c b/src/boot/efi/xbootldr.c index bee1d3ea4ae..49d5707e02f 100644 --- a/src/boot/efi/xbootldr.c +++ b/src/boot/efi/xbootldr.c @@ -72,10 +72,7 @@ static EFI_STATUS try_gpt( EFI_BLOCK_IO *block_io, EFI_LBA lba, EFI_LBA *ret_backup_lba, /* May be changed even on error! */ - UINT32 *ret_part_number, - UINT64 *ret_part_start, - UINT64 *ret_part_size, - EFI_GUID *ret_part_uuid) { + HARDDRIVE_DEVICE_PATH *ret_hd) { _cleanup_freepool_ EFI_PARTITION_ENTRY *entries = NULL; union GptHeaderBuffer gpt; @@ -84,10 +81,7 @@ static EFI_STATUS try_gpt( UINTN size; assert(block_io); - assert(ret_part_number); - assert(ret_part_start); - assert(ret_part_size); - assert(ret_part_uuid); + assert(ret_hd); /* Read the GPT header */ err = block_io->ReadBlocks( @@ -139,10 +133,12 @@ static EFI_STATUS try_gpt( if (end < start) /* Bogus? */ continue; - *ret_part_number = i + 1; - *ret_part_start = start; - *ret_part_size = end - start + 1; - CopyMem(ret_part_uuid, &entry->UniquePartitionGUID, sizeof(*ret_part_uuid)); + ret_hd->PartitionNumber = i + 1; + ret_hd->PartitionStart = start; + ret_hd->PartitionSize = end - start + 1; + ret_hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + ret_hd->SignatureType = SIGNATURE_TYPE_GUID; + CopyMem(ret_hd->Signature, &entry->UniquePartitionGUID, sizeof(ret_hd->Signature)); return EFI_SUCCESS; } @@ -152,22 +148,11 @@ static EFI_STATUS try_gpt( return EFI_NOT_FOUND; } -static EFI_STATUS find_device( - EFI_HANDLE *device, - EFI_DEVICE_PATH **ret_device_path, - UINT32 *ret_part_number, - UINT64 *ret_part_start, - UINT64 *ret_part_size, - EFI_GUID *ret_part_uuid) { - +static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_path) { EFI_STATUS err; assert(device); assert(ret_device_path); - assert(ret_part_number); - assert(ret_part_start); - assert(ret_part_size); - assert(ret_part_uuid); EFI_DEVICE_PATH *partition_path = DevicePathFromHandle(device); if (!partition_path) @@ -212,6 +197,7 @@ static EFI_STATUS find_device( /* Try several copies of the GPT header, in case one is corrupted */ EFI_LBA backup_lba = 0; + HARDDRIVE_DEVICE_PATH hd = *((HARDDRIVE_DEVICE_PATH *) part_node); for (UINTN nr = 0; nr < 3; nr++) { EFI_LBA lba; @@ -230,20 +216,20 @@ static EFI_STATUS find_device( err = try_gpt( block_io, lba, nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */ - ret_part_number, - ret_part_start, - ret_part_size, - ret_part_uuid); - if (!EFI_ERROR(err)) { - *ret_device_path = DuplicateDevicePath(partition_path); - if (!*ret_device_path) - return EFI_OUT_OF_RESOURCES; - return EFI_SUCCESS; + &hd); + if (EFI_ERROR(err)) { + /* GPT was valid but no XBOOT loader partition found. */ + if (err == EFI_NOT_FOUND) + break; + /* Bad GPT, try next one. */ + continue; } - /* GPT was valid but no XBOOT loader partition found. */ - if (err == EFI_NOT_FOUND) - break; + /* Patch in the data we found */ + EFI_DEVICE_PATH *xboot_path = ASSERT_PTR(DuplicateDevicePath(partition_path)); + CopyMem((UINT8 *) xboot_path + ((UINT8 *) part_node - (UINT8 *) partition_path), &hd, sizeof(hd)); + *ret_device_path = xboot_path; + return EFI_SUCCESS; } /* No xbootloader partition found */ @@ -252,40 +238,18 @@ static EFI_STATUS find_device( EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir) { _cleanup_freepool_ EFI_DEVICE_PATH *partition_path = NULL; - UINT32 part_number = UINT32_MAX; - UINT64 part_start = UINT64_MAX, part_size = UINT64_MAX; EFI_HANDLE new_device; EFI_FILE *root_dir; - EFI_GUID part_uuid; EFI_STATUS err; assert(device); assert(ret_device); assert(ret_root_dir); - err = find_device(device, &partition_path, &part_number, &part_start, &part_size, &part_uuid); + err = find_device(device, &partition_path); if (EFI_ERROR(err)) return err; - /* Patch in the data we found */ - for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { - HARDDRIVE_DEVICE_PATH *hd; - - if (DevicePathType(node) != MEDIA_DEVICE_PATH) - continue; - - if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) - continue; - - hd = (HARDDRIVE_DEVICE_PATH*) node; - hd->PartitionNumber = part_number; - hd->PartitionStart = part_start; - hd->PartitionSize = part_size; - CopyMem(hd->Signature, &part_uuid, sizeof(hd->Signature)); - hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; - hd->SignatureType = SIGNATURE_TYPE_GUID; - } - EFI_DEVICE_PATH *dp = partition_path; err = BS->LocateDevicePath(&BlockIoProtocol, &dp, &new_device); if (EFI_ERROR(err))