mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-24 21:34:08 +03:00
sd-boot: Move xbootldr code into its own file
This commit is contained in:
parent
6e7bb7d3ee
commit
bcbc3e81d3
@ -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);
|
||||
|
@ -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 = '''
|
||||
|
212
src/boot/efi/xbootldr.c
Normal file
212
src/boot/efi/xbootldr.c
Normal file
@ -0,0 +1,212 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <efi.h>
|
||||
#include <efigpt.h>
|
||||
#include <efilib.h>
|
||||
|
||||
#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;
|
||||
}
|
8
src/boot/efi/xbootldr.h
Normal file
8
src/boot/efi/xbootldr.h
Normal file
@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <efi.h>
|
||||
|
||||
#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);
|
Loading…
Reference in New Issue
Block a user