From: Ani Sinha Date: Sun, 5 Jan 2025 03:32:31 +0000 (+0530) Subject: hwids: add a new uefi firmware type of device entry X-Git-Tag: v258-rc1~1660^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5d0ac3528bd1b11194e39b1b1f8ecb243cd08566;p=thirdparty%2Fsystemd.git hwids: add a new uefi firmware type of device entry This change adds a new uefi firmware type device entry for the .hwids section. It also adds necessary changes for ukify.py. --- diff --git a/man/ukify_hwid.json.example b/man/ukify_hwid.json.example index 83921b5d936..b016fbb76ea 100644 --- a/man/ukify_hwid.json.example +++ b/man/ukify_hwid.json.example @@ -1,4 +1,5 @@ { + "type": "devicetree", "name": "Example Laptop 16 Gen 7", "compatible": "example,laptop-16-g7", "hwids": [ diff --git a/src/boot/chid.c b/src/boot/chid.c index 38fbc61d7cc..6005b0e8654 100644 --- a/src/boot/chid.c +++ b/src/boot/chid.c @@ -23,8 +23,11 @@ /* Validate the descriptor macros a bit that they match our expectations */ assert_cc(DEVICE_DESCRIPTOR_DEVICETREE == UINT32_C(0x1000001C)); +assert_cc(DEVICE_DESCRIPTOR_UEFI_FW == UINT32_C(0x2000001C)); assert_cc(DEVICE_SIZE_FROM_DESCRIPTOR(DEVICE_DESCRIPTOR_DEVICETREE) == sizeof(Device)); assert_cc(DEVICE_TYPE_FROM_DESCRIPTOR(DEVICE_DESCRIPTOR_DEVICETREE) == DEVICE_TYPE_DEVICETREE); +assert_cc(DEVICE_SIZE_FROM_DESCRIPTOR(DEVICE_DESCRIPTOR_UEFI_FW) == sizeof(Device)); +assert_cc(DEVICE_TYPE_FROM_DESCRIPTOR(DEVICE_DESCRIPTOR_UEFI_FW) == DEVICE_TYPE_UEFI_FW); /** * smbios_to_hashable_string() - Convert ascii smbios string to stripped char16_t. @@ -114,7 +117,8 @@ EFI_STATUS chid_match(const void *hwid_buffer, size_t hwid_length, const Device if (devices[n_devices].descriptor == DEVICE_DESCRIPTOR_EOL) break; - if (devices[n_devices].descriptor != DEVICE_DESCRIPTOR_DEVICETREE) + if (!IN_SET(DEVICE_TYPE_FROM_DESCRIPTOR(devices[n_devices].descriptor), + DEVICE_TYPE_UEFI_FW, DEVICE_TYPE_DEVICETREE)) return EFI_UNSUPPORTED; n_devices++; } diff --git a/src/boot/chid.h b/src/boot/chid.h index 4cc1650a443..c31f08d53b5 100644 --- a/src/boot/chid.h +++ b/src/boot/chid.h @@ -11,11 +11,13 @@ enum { DEVICE_TYPE_DEVICETREE = 0x1, /* A devicetree blob */ + DEVICE_TYPE_UEFI_FW = 0x2, /* A firmware blob */ /* Maybe later additional types for: * - CoCo Bring-Your-Own-Firmware * - ACPI DSDT Overrides * - … */ + _DEVICE_TYPE_MAX, }; #define DEVICE_SIZE_FROM_DESCRIPTOR(u) ((uint32_t) (u) & UINT32_C(0x0FFFFFFF)) @@ -23,6 +25,7 @@ enum { #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_UEFI_FW DEVICE_MAKE_DESCRIPTOR(DEVICE_TYPE_UEFI_FW, sizeof(Device)) #define DEVICE_DESCRIPTOR_EOL UINT32_C(0) typedef struct Device { @@ -36,6 +39,13 @@ typedef struct Device { 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; + struct { + /* Offsets are relative to the beginning of the .hwids PE section. + * They are nul-terminated strings when present or 0 if not present */ + uint32_t name_offset; /* name or identifier for the firmware blob */ + uint32_t fwid_offset; /* identifier to match a specific uefi firmware blob */ + } uefi_fw; + /* fields for other descriptor types… */ }; } _packed_ Device; @@ -45,20 +55,47 @@ 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(offsetof(Device, uefi_fw.name_offset) == 20); +assert_cc(offsetof(Device, uefi_fw.fwid_offset) == 24); assert_cc(sizeof(Device) == 28); static inline const char* device_get_name(const void *base, const Device *device) { - if (device->descriptor != DEVICE_DESCRIPTOR_DEVICETREE) + size_t off = 0; + switch (DEVICE_TYPE_FROM_DESCRIPTOR(device->descriptor)) { + case DEVICE_TYPE_DEVICETREE: + off = device->devicetree.name_offset; + break; + case DEVICE_TYPE_UEFI_FW: + off = device->uefi_fw.name_offset; + break; + default: return NULL; - - return device->devicetree.name_offset == 0 ? NULL : (const char *) ((const uint8_t *) base + device->devicetree.name_offset); + } + return off == 0 ? NULL : (const char *) ((const uint8_t *) base + off); } static inline const char* device_get_compatible(const void *base, const Device *device) { - if (device->descriptor != DEVICE_DESCRIPTOR_DEVICETREE) + size_t off = 0; + switch (DEVICE_TYPE_FROM_DESCRIPTOR(device->descriptor)) { + case DEVICE_TYPE_DEVICETREE: + off = device->devicetree.compatible_offset; + break; + default: return NULL; + } + return off == 0 ? NULL : (const char *) ((const uint8_t *) base + off); +} - return device->devicetree.compatible_offset == 0 ? NULL : (const char *) ((const uint8_t *) base + device->devicetree.compatible_offset); +static inline const char* device_get_fwid(const void *base, const Device *device) { + size_t off = 0; + switch (DEVICE_TYPE_FROM_DESCRIPTOR(device->descriptor)) { + case DEVICE_TYPE_UEFI_FW: + off = device->uefi_fw.fwid_offset; + break; + default: + return NULL; + } + return off == 0 ? NULL : (const char *) ((const uint8_t *) base + off); } EFI_STATUS chid_match(const void *chids_buffer, size_t chids_length, const Device **ret_device); diff --git a/src/boot/hwids/device1.json b/src/boot/hwids/device1.json index 030afc8ef59..6649a76491e 100644 --- a/src/boot/hwids/device1.json +++ b/src/boot/hwids/device1.json @@ -1,4 +1,5 @@ { + "type": "devicetree", "name": "Device 1", "compatible": "test,device-1", "hwids": [ diff --git a/src/boot/hwids/device2.json b/src/boot/hwids/device2.json index dc9b8e85213..c2d84556792 100644 --- a/src/boot/hwids/device2.json +++ b/src/boot/hwids/device2.json @@ -1,4 +1,5 @@ { + "type": "devicetree", "name": "Device 2", "compatible": "test,device-2", "hwids": [ diff --git a/src/boot/hwids/device3.json b/src/boot/hwids/device3.json index d139462c16f..b47ac6d4472 100644 --- a/src/boot/hwids/device3.json +++ b/src/boot/hwids/device3.json @@ -1,4 +1,5 @@ { + "type": "devicetree", "name": "Device 3", "compatible": "test,device-3", "hwids": [ diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index 22195b86bdf..cfd9e25e888 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -1014,27 +1014,27 @@ def merge_sbat(input_pe: list[Path], input_text: list[str]) -> str: ) -# Keep in sync with Device (DEVICE_TYPE_DEVICETREE) from src/boot/chid.h +# Keep in sync with Device from src/boot/chid.h # uint32_t descriptor, EFI_GUID chid, uint32_t name_offset, uint32_t compatible_offset DEVICE_STRUCT_SIZE = 4 + 16 + 4 + 4 NULL_DEVICE = b'\0' * DEVICE_STRUCT_SIZE DEVICE_TYPE_DEVICETREE = 1 +DEVICE_TYPE_UEFI_FW = 2 def device_make_descriptor(device_type: int, size: int) -> int: return (size) | (device_type << 28) -DEVICETREE_DESCRIPTOR = device_make_descriptor(DEVICE_TYPE_DEVICETREE, DEVICE_STRUCT_SIZE) - - -def pack_device(offsets: dict[str, int], name: str, compatible: str, chids: set[uuid.UUID]) -> bytes: +def pack_device( + offsets: dict[str, int], devtype: int, name: str, compatible_or_fwid: str, chids: set[uuid.UUID] +) -> bytes: data = b'' - + descriptor = device_make_descriptor(devtype, DEVICE_STRUCT_SIZE) for chid in sorted(chids): - data += struct.pack(' tuple[bytes, dict[str, int]]: def parse_hwid_dir(path: Path) -> bytes: hwid_files = path.rglob('*.json') + devstr_to_type: dict[str, int] = { + 'devicetree': DEVICE_TYPE_DEVICETREE, + 'uefi-fw': DEVICE_TYPE_UEFI_FW, + } + + # all attributes in the mandatory attributes list must be present + mandatory_attribute = ['type', 'name', 'hwids'] + + # at least one of the following attributes must be present + one_of = ['compatible', 'fwid'] + + one_of_key_to_devtype: dict[str, int] = { + 'compatible': DEVICE_TYPE_DEVICETREE, + 'fwid': DEVICE_TYPE_UEFI_FW, + } strings: set[str] = set() - devices: collections.defaultdict[tuple[str, str], set[uuid.UUID]] = collections.defaultdict(set) + devices: collections.defaultdict[tuple[int, str, str], set[uuid.UUID]] = collections.defaultdict(set) for hwid_file in hwid_files: data = json.loads(hwid_file.read_text(encoding='UTF-8')) - for k in ['name', 'compatible', 'hwids']: + for k in mandatory_attribute: if k not in data: raise ValueError(f'hwid description file "{hwid_file}" does not contain "{k}"') - strings |= {data['name'], data['compatible']} + if not any(key in data for key in one_of): + required_keys = ','.join(one_of) + raise ValueError(f'hwid description file "{hwid_file}" must contain one of {required_keys}') + + # (devtype, name, compatible/fwid) pair uniquely identifies the device + devtype = devstr_to_type[data['type']] - # (name, compatible) pair uniquely identifies the device - devices[(data['name'], data['compatible'])] |= {uuid.UUID(u) for u in data['hwids']} + for k in one_of: + if k in data: + if one_of_key_to_devtype[k] != devtype: + raise ValueError( + f'wrong attribute "{k}" for hwid description file "{hwid_file}", ' + 'device type: "%s"' % devtype + ) + strings |= {data['name'], data[k]} + devices[(devtype, data['name'], data[k])] |= {uuid.UUID(u) for u in data['hwids']} total_device_structs = 1 for dev, uuids in devices.items(): @@ -1076,8 +1103,8 @@ def parse_hwid_dir(path: Path) -> bytes: strings_blob, offsets = pack_strings(strings, total_device_structs * DEVICE_STRUCT_SIZE) devices_blob = b'' - for (name, compatible), uuids in devices.items(): - devices_blob += pack_device(offsets, name, compatible, uuids) + for (devtype, name, compatible_or_fwid), uuids in devices.items(): + devices_blob += pack_device(offsets, devtype, name, compatible_or_fwid, uuids) devices_blob += NULL_DEVICE