]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: Add HWID calculation from SMBIOS strings and matching against a built-in list
authoranonymix007 <48598263+anonymix007@users.noreply.github.com>
Sat, 31 Aug 2024 18:49:10 +0000 (21:49 +0300)
committeranonymix007 <48598263+anonymix007@users.noreply.github.com>
Tue, 5 Nov 2024 19:29:58 +0000 (22:29 +0300)
LICENSES/README.md
src/boot/efi/chid.c [new file with mode: 0644]
src/boot/efi/chid.h [new file with mode: 0644]
src/boot/efi/meson.build

index 29ae23c727722da2f59d6baf9bcfd210e74f2f43..f8668be82022b21ddd3c216023bb6bb85e7c9548 100644 (file)
@@ -69,6 +69,9 @@ The following exceptions apply:
  * the following sources are under **Public Domain** (LicenseRef-alg-sha1-public-domain):
    - src/fundamental/sha1-fundamental.c
    - src/fundamental/sha1-fundamental.h
+ * the following files are licensed under **BSD-3-Clause** license:
+   - src/boot/efi/chid.c
+   - src/boot/efi/chid.h
  * Heebo fonts under docs/fonts/ are licensed under the **SIL Open Font License 1.1**,
  * any files under test/ without an explicit license we assume non-copyrightable
    (eg: computer-generated fuzzer data)
diff --git a/src/boot/efi/chid.c b/src/boot/efi/chid.c
new file mode 100644 (file)
index 0000000..50d840a
--- /dev/null
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+
+/*
+ * Based on Nikita Travkin's dtbloader implementation.
+ * Copyright (c) 2024 Nikita Travkin <nikita@trvn.ru>
+ *
+ * https://github.com/TravMurav/dtbloader/blob/main/src/chid.c
+ */
+
+/*
+ * Based on Linaro dtbloader implementation.
+ * Copyright (c) 2019, Linaro. All rights reserved.
+ *
+ * https://github.com/aarch64-laptops/edk2/blob/dtbloader-app/EmbeddedPkg/Application/ConfigTableLoader/CHID.c
+ */
+
+#include "chid.h"
+#include "chid-fundamental.h"
+#include "efi.h"
+#include "sha1-fundamental.h"
+#include "smbios.h"
+#include "util.h"
+
+/**
+ * smbios_to_hashable_string() - Convert ascii smbios string to stripped char16_t.
+ */
+static char16_t *smbios_to_hashable_string(const char *str) {
+        if (!str)
+                /* User of this function is expected to free the result. */
+                return xnew0(char16_t, 1);
+
+        /*
+         * We need to strip leading and trailing spaces, leading zeroes.
+         * See fwupd/libfwupdplugin/fu-hwids-smbios.c
+         */
+        while (*str == ' ')
+                str++;
+
+        while (*str == '0')
+                str++;
+
+        size_t len = strlen8(str);
+
+        while (len > 0 && str[len - 1] == ' ')
+                len--;
+
+        return xstrn8_to_16(str, len);
+}
+
+/* This has to be in a struct due to _cleanup_ in populate_board_chids */
+typedef struct SmbiosInfo {
+        const char16_t *smbios_fields[_CHID_SMBIOS_FIELDS_MAX];
+} SmbiosInfo;
+
+static void smbios_info_populate(SmbiosInfo *ret_info) {
+        static RawSmbiosInfo raw = {};
+        static bool raw_info_populated = false;
+
+        if (!raw_info_populated) {
+                smbios_raw_info_populate(&raw);
+                raw_info_populated = true;
+        }
+
+        ret_info->smbios_fields[CHID_SMBIOS_MANUFACTURER] = smbios_to_hashable_string(raw.manufacturer);
+        ret_info->smbios_fields[CHID_SMBIOS_PRODUCT_NAME] = smbios_to_hashable_string(raw.product_name);
+        ret_info->smbios_fields[CHID_SMBIOS_PRODUCT_SKU] = smbios_to_hashable_string(raw.product_sku);
+        ret_info->smbios_fields[CHID_SMBIOS_FAMILY] = smbios_to_hashable_string(raw.family);
+        ret_info->smbios_fields[CHID_SMBIOS_BASEBOARD_PRODUCT] = smbios_to_hashable_string(raw.baseboard_product);
+        ret_info->smbios_fields[CHID_SMBIOS_BASEBOARD_MANUFACTURER] = smbios_to_hashable_string(raw.baseboard_manufacturer);
+}
+
+static void smbios_info_done(SmbiosInfo *info) {
+        FOREACH_ELEMENT(i, info->smbios_fields)
+                free(i);
+}
+
+static EFI_STATUS populate_board_chids(EFI_GUID ret_chids[static CHID_TYPES_MAX]) {
+        _cleanup_(smbios_info_done) SmbiosInfo info = {};
+
+        if (!ret_chids)
+                return EFI_INVALID_PARAMETER;
+
+        smbios_info_populate(&info);
+        chid_calculate(info.smbios_fields, ret_chids);
+
+        return EFI_SUCCESS;
+}
+
+EFI_STATUS chid_match(const void *hwid_buffer, size_t hwid_length, const Device **ret_device) {
+        EFI_STATUS status;
+
+        if ((uintptr_t) hwid_buffer % alignof(Device) != 0)
+                return EFI_INVALID_PARAMETER;
+
+        const Device *devices = ASSERT_PTR(hwid_buffer);
+
+        EFI_GUID chids[CHID_TYPES_MAX] = {};
+        static const size_t priority[] = { 3, 6, 8, 10, 4, 5, 7, 9, 11 }; /* From most to least specific. */
+
+        status = populate_board_chids(chids);
+        if (EFI_STATUS_IS_ERROR(status))
+                return log_error_status(status, "Failed to populate board CHIDs: %m");
+
+        size_t n_devices = 0;
+
+        /* Count devices and check validity */
+        for (; (n_devices + 1) * sizeof(*devices) < hwid_length;) {
+                if (devices[n_devices].struct_size == 0)
+                        break;
+                if (devices[n_devices].struct_size != sizeof(*devices))
+                        return EFI_UNSUPPORTED;
+                n_devices++;
+        }
+
+        if (n_devices == 0)
+                return EFI_NOT_FOUND;
+
+        FOREACH_ELEMENT(i, priority)
+                FOREACH_ARRAY(dev, devices, n_devices) {
+                        /* Can't take a pointer to a packed struct member, so copy to a local variable */
+                        EFI_GUID chid = dev->chid;
+                        if (efi_guid_equal(&chids[*i], &chid)) {
+                                *ret_device = dev;
+                                return EFI_SUCCESS;
+                        }
+                }
+
+        return EFI_NOT_FOUND;
+}
diff --git a/src/boot/efi/chid.h b/src/boot/efi/chid.h
new file mode 100644 (file)
index 0000000..ea6e2d3
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+#pragma once
+
+#include "efi.h"
+
+#include "chid-fundamental.h"
+
+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 */
+        EFI_GUID chid;
+} _packed_ Device;
+
+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);
+}
+
+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);
+}
+
+EFI_STATUS chid_match(const void *chids_buffer, size_t chids_length, const Device **ret_device);
index 0109793b7a96e8f77c2f2597d0118c246a7dd5ae..29c5455dbd19bbcc2c472ce6b4d8d27f0bb540ca 100644 (file)
@@ -254,6 +254,7 @@ endif
 ############################################################
 
 libefi_sources = files(
+        'chid.c',
         'console.c',
         'device-path-util.c',
         'devicetree.c',