]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
hwids: add a new uefi firmware type of device entry
authorAni Sinha <anisinha@redhat.com>
Sun, 5 Jan 2025 03:32:31 +0000 (09:02 +0530)
committerAni Sinha <anisinha@redhat.com>
Sun, 5 Jan 2025 04:10:05 +0000 (09:40 +0530)
This change adds a new uefi firmware type device entry for the .hwids section.
It also adds necessary changes for ukify.py.

man/ukify_hwid.json.example
src/boot/chid.c
src/boot/chid.h
src/boot/hwids/device1.json
src/boot/hwids/device2.json
src/boot/hwids/device3.json
src/ukify/ukify.py

index 83921b5d93635fc1636422e7f0fd3ddcf1bd18f3..b016fbb76eab933f5c5866a16a133c74d27908aa 100644 (file)
@@ -1,4 +1,5 @@
 {
+    "type": "devicetree",
     "name": "Example Laptop 16 Gen 7",
     "compatible": "example,laptop-16-g7",
     "hwids": [
index 38fbc61d7cce7d089079c82b082af112ab8ecaa6..6005b0e8654092a5c8bcd990f97b2c269afffee0 100644 (file)
 
 /* 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++;
         }
index 4cc1650a443eaf696399a2b81dc6cd0842ba896e..c31f08d53b5b6a6162d21fa46f804c18d099bd77 100644 (file)
 
 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);
index 030afc8ef599634241e311d482023165bb340dbb..6649a76491eae7992cb23195285ebe1981f4d565 100644 (file)
@@ -1,4 +1,5 @@
 {
+    "type": "devicetree",
     "name": "Device 1",
     "compatible": "test,device-1",
     "hwids": [
index dc9b8e852138f3bd6747b9bcab64bb77b47f681b..c2d8455679287addcae7f0e4b9cb6705205ee2e4 100644 (file)
@@ -1,4 +1,5 @@
 {
+    "type": "devicetree",
     "name": "Device 2",
     "compatible": "test,device-2",
     "hwids": [
index d139462c16fb14e0c478a4b519e6f8a71fd6f8b1..b47ac6d447263fa7c4ff593241a71936ddaeaf08 100644 (file)
@@ -1,4 +1,5 @@
 {
+    "type": "devicetree",
     "name": "Device 3",
     "compatible": "test,device-3",
     "hwids": [
index 22195b86bdf1e7453c7456d7368b366128c35703..cfd9e25e8885bcb9fc869465ca1c3faea044d460 100755 (executable)
@@ -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('<I', DEVICETREE_DESCRIPTOR)
+        data += struct.pack('<I', descriptor)
         data += chid.bytes_le
-        data += struct.pack('<II', offsets[name], offsets[compatible])
+        data += struct.pack('<II', offsets[name], offsets[compatible_or_fwid])
 
     assert len(data) == DEVICE_STRUCT_SIZE * len(chids)
     return data
@@ -1053,21 +1053,48 @@ def pack_strings(strings: set[str], base: int) -> 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