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))
#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 {
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;
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);
)
-# 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
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():
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