From be6e599935cd5206d830e6337881031308e890e2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Nov 2024 23:02:55 +0100 Subject: [PATCH] boot: make .hwids PE section more flexible to cover more than DT one day The proposal in https://github.com/systemd/systemd/pull/35091 suggests that there are going to be more resources sooner or later that shall be embeddable in a UKI, but are specific to some machine. The .hwids logic as it is implemented right now is conceptually flexible enough to cover that too (as long as the system has SMBIOS and thus CHIDs). Hence, let's prepare the ground for a future (that might possibly never come, but let's keep the door open) where the section can be reused for this purpose. The patch is really dumb ultimately. it just changes the initial field in the "Device" struct to carry not just the size of it (as before) but also a type indicator, that is for now fixed to 1, indicating DT blobs. This breaks compatibility, hence this should get merged before we do the v257 release, so that this is done properly before the first release with .hwids. --- src/boot/chid.c | 10 ++++++++-- src/boot/chid.h | 53 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/boot/chid.c b/src/boot/chid.c index 18760fd598..cd82958dcf 100644 --- a/src/boot/chid.c +++ b/src/boot/chid.c @@ -21,6 +21,11 @@ #include "smbios.h" #include "util.h" +/* Validate the descriptor macros a bit that they match our expectations */ +assert_cc(DEVICE_DESCRIPTOR_DEVICETREE == UINT32_C(0x1000001C)); +assert_cc(DEVICE_SIZE_FROM_DESCRIPTOR(DEVICE_DESCRIPTOR_DEVICETREE) == sizeof(Device)); +assert_cc(DEVICE_TYPE_FROM_DESCRIPTOR(DEVICE_DESCRIPTOR_DEVICETREE) == DEVICE_TYPE_DEVICETREE); + /** * smbios_to_hashable_string() - Convert ascii smbios string to stripped char16_t. */ @@ -105,9 +110,10 @@ EFI_STATUS chid_match(const void *hwid_buffer, size_t hwid_length, const Device /* Count devices and check validity */ for (; (n_devices + 1) * sizeof(*devices) < hwid_length;) { - if (devices[n_devices].struct_size == 0) + + if (devices[n_devices].descriptor == DEVICE_DESCRIPTOR_EOL) break; - if (devices[n_devices].struct_size != sizeof(*devices)) + if (devices[n_devices].descriptor != DEVICE_DESCRIPTOR_DEVICETREE) return EFI_UNSUPPORTED; n_devices++; } diff --git a/src/boot/chid.h b/src/boot/chid.h index ea6e2d348f..4cc1650a44 100644 --- a/src/boot/chid.h +++ b/src/boot/chid.h @@ -2,22 +2,63 @@ #pragma once #include "efi.h" - #include "chid-fundamental.h" +/* A .hwids PE section consists of a series of 'Device' structures. A 'Device' structure binds a CHID to some + * resource, for now only Devicetree blobs. Designed to be extensible to other types of resources, should the + * need arise. The series of 'Device' structures is followed by some space for strings that can be referenced + * by offset by the Device structures. */ + +enum { + DEVICE_TYPE_DEVICETREE = 0x1, /* A devicetree blob */ + + /* Maybe later additional types for: + * - CoCo Bring-Your-Own-Firmware + * - ACPI DSDT Overrides + * - … */ +}; + +#define DEVICE_SIZE_FROM_DESCRIPTOR(u) ((uint32_t) (u) & UINT32_C(0x0FFFFFFF)) +#define DEVICE_TYPE_FROM_DESCRIPTOR(u) ((uint32_t) (u) >> 28) +#define DEVICE_MAKE_DESCRIPTOR(type, size) (((uint32_t) (size) | ((uint32_t) type << 28))) + +#define DEVICE_DESCRIPTOR_DEVICETREE DEVICE_MAKE_DESCRIPTOR(DEVICE_TYPE_DEVICETREE, sizeof(Device)) +#define DEVICE_DESCRIPTOR_EOL UINT32_C(0) + typedef struct Device { - uint32_t struct_size; /* = sizeof(struct Device), or 0 for EOL */ - uint32_t name_offset; /* nul-terminated string or 0 if not present */ - uint32_t compatible_offset; /* nul-terminated string or 0 if not present */ + uint32_t descriptor; /* The highest four bit encode the type of entry, the other 28 bit encode the + * size of the structure. Use the macros above to generate or take apart this + * field. */ EFI_GUID chid; + union { + struct { + /* These offsets are relative to the beginning of the .hwids PE section. */ + uint32_t name_offset; /* nul-terminated string or 0 if not present */ + uint32_t compatible_offset; /* nul-terminated string or 0 if not present */ + } devicetree; + /* fields for other descriptor types… */ + }; } _packed_ Device; +/* Validate some offset, since the structure is API and src/ukify/ukify.py encodes them directly */ +assert_cc(offsetof(Device, descriptor) == 0); +assert_cc(offsetof(Device, chid) == 4); +assert_cc(offsetof(Device, devicetree.name_offset) == 20); +assert_cc(offsetof(Device, devicetree.compatible_offset) == 24); +assert_cc(sizeof(Device) == 28); + static inline const char* device_get_name(const void *base, const Device *device) { - return device->name_offset == 0 ? NULL : (const char *) ((const uint8_t *) base + device->name_offset); + if (device->descriptor != DEVICE_DESCRIPTOR_DEVICETREE) + return NULL; + + return device->devicetree.name_offset == 0 ? NULL : (const char *) ((const uint8_t *) base + device->devicetree.name_offset); } static inline const char* device_get_compatible(const void *base, const Device *device) { - return device->compatible_offset == 0 ? NULL : (const char *) ((const uint8_t *) base + device->compatible_offset); + if (device->descriptor != DEVICE_DESCRIPTOR_DEVICETREE) + return NULL; + + return device->devicetree.compatible_offset == 0 ? NULL : (const char *) ((const uint8_t *) base + device->devicetree.compatible_offset); } EFI_STATUS chid_match(const void *chids_buffer, size_t chids_length, const Device **ret_device); -- 2.25.1