From: Jan Janssen Date: Mon, 27 Sep 2021 08:02:50 +0000 (+0200) Subject: sd-boot: Move xbootldr code into its own file X-Git-Tag: v250-rc1~526 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bcbc3e81d3b876ee2add5f1f4bef0a026089afbf;p=thirdparty%2Fsystemd.git sd-boot: Move xbootldr code into its own file --- diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 30e9516dd7b..e073217f31a 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -17,6 +17,7 @@ #include "secure-boot.h" #include "shim.h" #include "util.h" +#include "xbootldr.h" #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL @@ -2098,208 +2099,19 @@ static VOID config_entry_add_linux( } } -#define XBOOTLDR_GUID \ - &(const EFI_GUID) { 0xbc13c2ff, 0x59e6, 0x4262, { 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 } } - -static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) { - EFI_DEVICE_PATH *parent; - UINTN len; - - assert(path); - assert(node); - - len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path; - parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH)); - CopyMem(parent, path, len); - CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH)); - - return parent; -} - static VOID config_load_xbootldr( Config *config, EFI_HANDLE *device) { - EFI_DEVICE_PATH *partition_path, *disk_path, *copy; - UINT32 found_partition_number = UINT32_MAX; - UINT64 found_partition_start = UINT64_MAX; - UINT64 found_partition_size = UINT64_MAX; - UINT8 found_partition_signature[16] = {}; EFI_HANDLE new_device; EFI_FILE *root_dir; - EFI_STATUS r; + EFI_STATUS err; assert(config); assert(device); - partition_path = DevicePathFromHandle(device); - if (!partition_path) - return; - - for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { - EFI_HANDLE disk_handle; - 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) - continue; - - /* Determine the device path one level up */ - disk_path = path_parent(partition_path, node); - p = disk_path; - r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &p, &disk_handle); - if (EFI_ERROR(r)) - continue; - - r = uefi_call_wrapper(BS->HandleProtocol, 3, disk_handle, &BlockIoProtocol, (VOID **)&block_io); - if (EFI_ERROR(r)) - 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 both copies of the GPT header, in case one is corrupted */ - for (UINTN nr = 0; nr < 2; nr++) { - _cleanup_freepool_ EFI_PARTITION_ENTRY* entries = NULL; - union { - EFI_PARTITION_TABLE_HEADER gpt_header; - uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512]; - } gpt_header_buffer; - EFI_PARTITION_TABLE_HEADER *h = &gpt_header_buffer.gpt_header; - UINT64 where; - UINTN sz; - UINT32 crc32, crc32_saved; - - if (nr == 0) - /* Read the first copy at LBA 1 */ - where = 1; - else - /* Read the second copy at the very last LBA of this block device */ - where = block_io->Media->LastBlock; - - /* Read the GPT header */ - r = uefi_call_wrapper(block_io->ReadBlocks, 5, - block_io, - block_io->Media->MediaId, - where, - sizeof(gpt_header_buffer), &gpt_header_buffer); - if (EFI_ERROR(r)) - continue; - - /* Some superficial validation of the GPT header */ - if(CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature) != 0)) - continue; - - if (h->Header.HeaderSize < 92 || - h->Header.HeaderSize > 512) - continue; - - if (h->Header.Revision != 0x00010000U) - continue; - - /* Calculate CRC check */ - crc32_saved = h->Header.CRC32; - h->Header.CRC32 = 0; - r = BS->CalculateCrc32(&gpt_header_buffer, h->Header.HeaderSize, &crc32); - h->Header.CRC32 = crc32_saved; - if (EFI_ERROR(r) || crc32 != crc32_saved) - continue; - - if (h->MyLBA != where) - continue; - - if (h->SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY)) - continue; - - if (h->NumberOfPartitionEntries <= 0 || - h->NumberOfPartitionEntries > 1024) - continue; - - if (h->SizeOfPartitionEntry > UINTN_MAX / h->NumberOfPartitionEntries) /* overflow check */ - continue; - - /* Now load the GPT entry table */ - sz = ALIGN_TO((UINTN) h->SizeOfPartitionEntry * (UINTN) h->NumberOfPartitionEntries, 512); - entries = AllocatePool(sz); - - r = uefi_call_wrapper(block_io->ReadBlocks, 5, - block_io, - block_io->Media->MediaId, - h->PartitionEntryLBA, - sz, entries); - if (EFI_ERROR(r)) - continue; - - /* Calculate CRC of entries array, too */ - r = BS->CalculateCrc32(&entries, sz, &crc32); - if (EFI_ERROR(r) || crc32 != h->PartitionEntryArrayCRC32) - continue; - - for (UINTN i = 0; i < h->NumberOfPartitionEntries; i++) { - EFI_PARTITION_ENTRY *entry; - - entry = (EFI_PARTITION_ENTRY*) ((UINT8*) entries + h->SizeOfPartitionEntry * i); - - if (CompareMem(&entry->PartitionTypeGUID, XBOOTLDR_GUID, 16) == 0) { - UINT64 end; - - /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */ - CopyMem(&found_partition_start, &entry->StartingLBA, sizeof(found_partition_start)); - CopyMem(&end, &entry->EndingLBA, sizeof(end)); - - if (end < found_partition_start) /* Bogus? */ - continue; - - found_partition_size = end - found_partition_start + 1; - CopyMem(found_partition_signature, &entry->UniquePartitionGUID, sizeof(found_partition_signature)); - - found_partition_number = i + 1; - goto found; - } - } - - break; /* This GPT was fully valid, but we didn't find what we are looking for. This - * means there's no reason to check the second copy of the GPT header */ - } - } - - return; /* Not found */ - -found: - copy = DuplicateDevicePath(partition_path); - - /* Patch in the data we found */ - for (EFI_DEVICE_PATH *node = copy; !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 = found_partition_number; - hd->PartitionStart = found_partition_start; - hd->PartitionSize = found_partition_size; - CopyMem(hd->Signature, found_partition_signature, sizeof(hd->Signature)); - hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; - hd->SignatureType = SIGNATURE_TYPE_GUID; - } - - r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, ©, &new_device); - if (EFI_ERROR(r)) - return; - - root_dir = LibOpenRoot(new_device); - if (!root_dir) + err = xbootldr_open(device, &new_device, &root_dir); + if (EFI_ERROR(err)) return; config_entry_add_linux(config, new_device, root_dir); diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index f1374a964a7..cf3d3e60798 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -15,6 +15,7 @@ efi_headers = files(''' shim.h splash.h util.h + xbootldr.h '''.split()) common_sources = ''' @@ -34,6 +35,7 @@ systemd_boot_sources = ''' drivers.c random-seed.c shim.c + xbootldr.c '''.split() stub_sources = ''' diff --git a/src/boot/efi/xbootldr.c b/src/boot/efi/xbootldr.c new file mode 100644 index 00000000000..5ae2053496e --- /dev/null +++ b/src/boot/efi/xbootldr.c @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include + +#include "util.h" +#include "xbootldr.h" + +static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) { + EFI_DEVICE_PATH *parent; + UINTN len; + + assert(path); + assert(node); + + len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path; + parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH)); + CopyMem(parent, path, len); + CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH)); + + return parent; +} + +EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir) { + EFI_DEVICE_PATH *partition_path, *disk_path, *copy; + UINT32 found_partition_number = UINT32_MAX; + UINT64 found_partition_start = UINT64_MAX; + UINT64 found_partition_size = UINT64_MAX; + UINT8 found_partition_signature[16] = {}; + EFI_HANDLE new_device; + EFI_FILE *root_dir; + EFI_STATUS r; + + assert(device); + assert(ret_device); + assert(ret_root_dir); + + partition_path = DevicePathFromHandle(device); + if (!partition_path) + return EFI_NOT_FOUND; + + for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { + EFI_HANDLE disk_handle; + 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) + continue; + + /* Determine the device path one level up */ + disk_path = path_parent(partition_path, node); + p = disk_path; + r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &p, &disk_handle); + if (EFI_ERROR(r)) + continue; + + r = uefi_call_wrapper(BS->HandleProtocol, 3, disk_handle, &BlockIoProtocol, (VOID **)&block_io); + if (EFI_ERROR(r)) + 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 both copies of the GPT header, in case one is corrupted */ + for (UINTN nr = 0; nr < 2; nr++) { + _cleanup_freepool_ EFI_PARTITION_ENTRY* entries = NULL; + union { + EFI_PARTITION_TABLE_HEADER gpt_header; + uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512]; + } gpt_header_buffer; + EFI_PARTITION_TABLE_HEADER *h = &gpt_header_buffer.gpt_header; + UINT64 where; + UINTN sz; + UINT32 crc32, crc32_saved; + + if (nr == 0) + /* Read the first copy at LBA 1 */ + where = 1; + else + /* Read the second copy at the very last LBA of this block device */ + where = block_io->Media->LastBlock; + + /* Read the GPT header */ + r = uefi_call_wrapper(block_io->ReadBlocks, 5, + block_io, + block_io->Media->MediaId, + where, + sizeof(gpt_header_buffer), &gpt_header_buffer); + if (EFI_ERROR(r)) + continue; + + /* Some superficial validation of the GPT header */ + if(CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature) != 0)) + continue; + + if (h->Header.HeaderSize < 92 || + h->Header.HeaderSize > 512) + continue; + + if (h->Header.Revision != 0x00010000U) + continue; + + /* Calculate CRC check */ + crc32_saved = h->Header.CRC32; + h->Header.CRC32 = 0; + r = BS->CalculateCrc32(&gpt_header_buffer, h->Header.HeaderSize, &crc32); + h->Header.CRC32 = crc32_saved; + if (EFI_ERROR(r) || crc32 != crc32_saved) + continue; + + if (h->MyLBA != where) + continue; + + if (h->SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY)) + continue; + + if (h->NumberOfPartitionEntries <= 0 || + h->NumberOfPartitionEntries > 1024) + continue; + + if (h->SizeOfPartitionEntry > UINTN_MAX / h->NumberOfPartitionEntries) /* overflow check */ + continue; + + /* Now load the GPT entry table */ + sz = ALIGN_TO((UINTN) h->SizeOfPartitionEntry * (UINTN) h->NumberOfPartitionEntries, 512); + entries = AllocatePool(sz); + + r = uefi_call_wrapper(block_io->ReadBlocks, 5, + block_io, + block_io->Media->MediaId, + h->PartitionEntryLBA, + sz, entries); + if (EFI_ERROR(r)) + continue; + + /* Calculate CRC of entries array, too */ + r = BS->CalculateCrc32(&entries, sz, &crc32); + if (EFI_ERROR(r) || crc32 != h->PartitionEntryArrayCRC32) + continue; + + for (UINTN i = 0; i < h->NumberOfPartitionEntries; i++) { + EFI_PARTITION_ENTRY *entry; + + entry = (EFI_PARTITION_ENTRY*) ((UINT8*) entries + h->SizeOfPartitionEntry * i); + + if (CompareMem(&entry->PartitionTypeGUID, XBOOTLDR_GUID, 16) == 0) { + UINT64 end; + + /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */ + CopyMem(&found_partition_start, &entry->StartingLBA, sizeof(found_partition_start)); + CopyMem(&end, &entry->EndingLBA, sizeof(end)); + + if (end < found_partition_start) /* Bogus? */ + continue; + + found_partition_size = end - found_partition_start + 1; + CopyMem(found_partition_signature, &entry->UniquePartitionGUID, sizeof(found_partition_signature)); + + found_partition_number = i + 1; + goto found; + } + } + + break; /* This GPT was fully valid, but we didn't find what we are looking for. This + * means there's no reason to check the second copy of the GPT header */ + } + } + + return EFI_NOT_FOUND; + +found: + copy = DuplicateDevicePath(partition_path); + + /* Patch in the data we found */ + for (EFI_DEVICE_PATH *node = copy; !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 = found_partition_number; + hd->PartitionStart = found_partition_start; + hd->PartitionSize = found_partition_size; + CopyMem(hd->Signature, found_partition_signature, sizeof(hd->Signature)); + hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + hd->SignatureType = SIGNATURE_TYPE_GUID; + } + + r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, ©, &new_device); + if (EFI_ERROR(r)) + return r; + + root_dir = LibOpenRoot(new_device); + if (!root_dir) + return EFI_DEVICE_ERROR; + + *ret_device = new_device; + *ret_root_dir = root_dir; + return EFI_SUCCESS; +} diff --git a/src/boot/efi/xbootldr.h b/src/boot/efi/xbootldr.h new file mode 100644 index 00000000000..4cb370af29c --- /dev/null +++ b/src/boot/efi/xbootldr.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#define XBOOTLDR_GUID \ + &(const EFI_GUID) { 0xbc13c2ff, 0x59e6, 0x4262, { 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 } } + +EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir);