]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: make .hwids PE section more flexible to cover more than DT one day
authorLennart Poettering <lennart@poettering.net>
Thu, 14 Nov 2024 22:02:55 +0000 (23:02 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 15 Nov 2024 15:40:43 +0000 (15:40 +0000)
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
src/boot/chid.h

index 18760fd598a9727bf7787e27ceee7a2e1c01c1e5..cd82958dcfe1dca1da77317b9091d0407ac1b088 100644 (file)
 #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++;
         }
index ea6e2d348fa392a69eacc0b5427f64da12d7a3b5..4cc1650a443eaf696399a2b81dc6cd0842ba896e 100644 (file)
@@ -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);