summaryrefslogtreecommitdiffstats
path: root/src/fundamental
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/fundamental/bootspec-fundamental.c62
-rw-r--r--src/fundamental/bootspec-fundamental.h17
-rw-r--r--src/fundamental/confidential-virt-fundamental.h72
-rw-r--r--src/fundamental/efivars-fundamental.c37
-rw-r--r--src/fundamental/efivars-fundamental.h50
-rw-r--r--src/fundamental/logarithm.h59
-rw-r--r--src/fundamental/macro-fundamental.h515
-rw-r--r--src/fundamental/memory-util-fundamental.h108
-rw-r--r--src/fundamental/meson.build11
-rw-r--r--src/fundamental/sbat.h14
-rw-r--r--src/fundamental/sha256.c285
-rw-r--r--src/fundamental/sha256.h39
-rw-r--r--src/fundamental/string-util-fundamental.c228
-rw-r--r--src/fundamental/string-util-fundamental.h121
-rw-r--r--src/fundamental/tpm2-pcr.h51
-rw-r--r--src/fundamental/uki.c23
-rw-r--r--src/fundamental/uki.h28
-rw-r--r--src/fundamental/unaligned-fundamental.h40
18 files changed, 1760 insertions, 0 deletions
diff --git a/src/fundamental/bootspec-fundamental.c b/src/fundamental/bootspec-fundamental.c
new file mode 100644
index 0000000..b2841e3
--- /dev/null
+++ b/src/fundamental/bootspec-fundamental.c
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "bootspec-fundamental.h"
+
+bool bootspec_pick_name_version_sort_key(
+ const sd_char *os_pretty_name,
+ const sd_char *os_image_id,
+ const sd_char *os_name,
+ const sd_char *os_id,
+ const sd_char *os_image_version,
+ const sd_char *os_version,
+ const sd_char *os_version_id,
+ const sd_char *os_build_id,
+ const sd_char **ret_name,
+ const sd_char **ret_version,
+ const sd_char **ret_sort_key) {
+
+ const sd_char *good_name, *good_version, *good_sort_key;
+
+ /* Find the best human readable title, version string and sort key for a boot entry (using the
+ * os-release(5) fields). Precise is preferred over vague, and human readable over machine
+ * readable. Thus:
+ *
+ * 1. First priority gets the PRETTY_NAME field, which is the primary string intended for display,
+ * and should already contain both a nice description and a version indication (if that concept
+ * applies).
+ *
+ * 2. Otherwise we go for IMAGE_ID and IMAGE_VERSION (thus we show details about the image,
+ * i.e. specific combination of packages and configuration), if that concept applies.
+ *
+ * 3. Otherwise we go for NAME and VERSION (i.e. human readable OS name and version)
+ *
+ * 4. Otherwise we go for ID and VERSION_ID (i.e. machine readable OS name and version)
+ *
+ * 5. Finally, for the version we'll use BUILD_ID (i.e. a machine readable version that identifies
+ * the original OS build used during installation)
+ *
+ * Note that the display logic will show only the name by default, except if that isn't unique in
+ * which case the version is shown too.
+ *
+ * Note that name/version determined here are used only for display purposes. Boot entry preference
+ * sorting (i.e. algorithmic ordering of boot entries) is done based on the order of the sort key (if
+ * defined) or entry "id" string (i.e. entry file name) otherwise. */
+
+ good_name = os_pretty_name ?: (os_image_id ?: (os_name ?: os_id));
+ good_version = os_image_version ?: (os_version ?: (os_version_id ?: os_build_id));
+ good_sort_key = os_image_id ?: os_id;
+
+ if (!good_name)
+ return false;
+
+ if (ret_name)
+ *ret_name = good_name;
+
+ if (ret_version)
+ *ret_version = good_version;
+
+ if (ret_sort_key)
+ *ret_sort_key = good_sort_key;
+
+ return true;
+}
diff --git a/src/fundamental/bootspec-fundamental.h b/src/fundamental/bootspec-fundamental.h
new file mode 100644
index 0000000..19b489c
--- /dev/null
+++ b/src/fundamental/bootspec-fundamental.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "string-util-fundamental.h"
+
+bool bootspec_pick_name_version_sort_key(
+ const sd_char *os_pretty_name,
+ const sd_char *os_image_id,
+ const sd_char *os_name,
+ const sd_char *os_id,
+ const sd_char *os_image_version,
+ const sd_char *os_version,
+ const sd_char *os_version_id,
+ const sd_char *os_build_id,
+ const sd_char **ret_name,
+ const sd_char **ret_version,
+ const sd_char **ret_sort_key);
diff --git a/src/fundamental/confidential-virt-fundamental.h b/src/fundamental/confidential-virt-fundamental.h
new file mode 100644
index 0000000..986923e
--- /dev/null
+++ b/src/fundamental/confidential-virt-fundamental.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdint.h>
+
+/* Keep CVM detection logic in this file at feature parity with
+ * that in src/efi/boot/vmm.c */
+
+#define CPUID_PROCESSOR_INFO_AND_FEATURE_BITS UINT32_C(0x1)
+
+/*
+ * AMD64 Architecture Programmer’s Manual Volume 3:
+ * General-Purpose and System Instructions.
+ * Chapter: E4.1 - Maximum Extended Function Number and Vendor String
+ * https://www.amd.com/system/files/TechDocs/24594.pdf
+ */
+#define CPUID_GET_HIGHEST_FUNCTION UINT32_C(0x80000000)
+
+/*
+ * AMD64 Architecture Programmer’s Manual Volume 3:
+ * General-Purpose and System Instructions.
+ * Chapter: E4.17 - Encrypted Memory Capabilities
+ * https://www.amd.com/system/files/TechDocs/24594.pdf
+ */
+#define CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES UINT32_C(0x8000001f)
+
+/*
+ * AMD64 Architecture Programmer’s Manual Volume 3:
+ * General-Purpose and System Instructions.
+ * Chapter: 15.34.10 - SEV_STATUS MSR
+ * https://www.amd.com/system/files/TechDocs/24593.pdf
+ */
+#define MSR_AMD64_SEV UINT32_C(0xc0010131)
+
+/*
+ * Intel® TDX Module v1.5 Base Architecture Specification
+ * Chapter: 11.2
+ * https://www.intel.com/content/www/us/en/content-details/733575/intel-tdx-module-v1-5-base-architecture-specification.html
+ */
+
+#define CPUID_INTEL_TDX_ENUMERATION UINT32_C(0x21)
+
+/* Requirements for Implementing the Microsoft Hypervisor Interface
+ * https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs
+ */
+#define CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS UINT32_C(0x40000000)
+
+#define CPUID_HYPERV_FEATURES UINT32_C(0x40000003)
+
+#define CPUID_HYPERV_ISOLATION_CONFIG UINT32_C(0x4000000C)
+
+#define CPUID_HYPERV_MIN UINT32_C(0x40000005)
+#define CPUID_HYPERV_MAX UINT32_C(0x4000ffff)
+
+#define CPUID_SIG_AMD "AuthenticAMD"
+#define CPUID_SIG_INTEL "GenuineIntel"
+#define CPUID_SIG_INTEL_TDX "IntelTDX "
+#define CPUID_SIG_HYPERV "Microsoft Hv"
+
+/* ecx bit 31: set => hyperpvisor, unset => bare metal */
+#define CPUID_FEATURE_HYPERVISOR (UINT32_C(1) << 31)
+
+/* Linux include/asm-generic/hyperv-tlfs.h */
+#define CPUID_HYPERV_CPU_MANAGEMENT (UINT32_C(1) << 12) /* root partition */
+#define CPUID_HYPERV_ISOLATION (UINT32_C(1) << 22) /* confidential VM partition */
+
+#define CPUID_HYPERV_ISOLATION_TYPE_MASK UINT32_C(0xf)
+#define CPUID_HYPERV_ISOLATION_TYPE_SNP 2
+
+#define EAX_SEV (UINT32_C(1) << 1)
+#define MSR_SEV (UINT64_C(1) << 0)
+#define MSR_SEV_ES (UINT64_C(1) << 1)
+#define MSR_SEV_SNP (UINT64_C(1) << 2)
diff --git a/src/fundamental/efivars-fundamental.c b/src/fundamental/efivars-fundamental.c
new file mode 100644
index 0000000..2ec3bfb
--- /dev/null
+++ b/src/fundamental/efivars-fundamental.c
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "efivars-fundamental.h"
+
+static const sd_char * const table[_SECURE_BOOT_MAX] = {
+ [SECURE_BOOT_UNSUPPORTED] = STR_C("unsupported"),
+ [SECURE_BOOT_DISABLED] = STR_C("disabled"),
+ [SECURE_BOOT_UNKNOWN] = STR_C("unknown"),
+ [SECURE_BOOT_AUDIT] = STR_C("audit"),
+ [SECURE_BOOT_DEPLOYED] = STR_C("deployed"),
+ [SECURE_BOOT_SETUP] = STR_C("setup"),
+ [SECURE_BOOT_USER] = STR_C("user"),
+};
+
+const sd_char *secure_boot_mode_to_string(SecureBootMode m) {
+ return (m >= 0 && m < _SECURE_BOOT_MAX) ? table[m] : NULL;
+}
+
+SecureBootMode decode_secure_boot_mode(bool secure, bool audit, bool deployed, bool setup) {
+ /* See figure 32-4 Secure Boot Modes from UEFI Specification 2.9 */
+ if (secure && deployed && !audit && !setup)
+ return SECURE_BOOT_DEPLOYED;
+ if (secure && !deployed && !audit && !setup)
+ return SECURE_BOOT_USER;
+ if (!secure && !deployed && audit && setup)
+ return SECURE_BOOT_AUDIT;
+ if (!secure && !deployed && !audit && setup)
+ return SECURE_BOOT_SETUP;
+
+ /* Some firmware allows disabling secure boot while not being in
+ * setup mode unless the PK is cleared. */
+ if (!secure && !deployed && !audit && !setup)
+ return SECURE_BOOT_DISABLED;
+
+ /* Well, this should not happen. */
+ return SECURE_BOOT_UNKNOWN;
+}
diff --git a/src/fundamental/efivars-fundamental.h b/src/fundamental/efivars-fundamental.h
new file mode 100644
index 0000000..2d25d22
--- /dev/null
+++ b/src/fundamental/efivars-fundamental.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#ifdef SD_BOOT
+# define EINVAL 22
+#else
+# include <errno.h>
+#endif
+#include "string-util-fundamental.h"
+
+/* Features of the loader, i.e. systemd-boot */
+#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT (UINT64_C(1) << 0)
+#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (UINT64_C(1) << 1)
+#define EFI_LOADER_FEATURE_ENTRY_DEFAULT (UINT64_C(1) << 2)
+#define EFI_LOADER_FEATURE_ENTRY_ONESHOT (UINT64_C(1) << 3)
+#define EFI_LOADER_FEATURE_BOOT_COUNTING (UINT64_C(1) << 4)
+#define EFI_LOADER_FEATURE_XBOOTLDR (UINT64_C(1) << 5)
+#define EFI_LOADER_FEATURE_RANDOM_SEED (UINT64_C(1) << 6)
+#define EFI_LOADER_FEATURE_LOAD_DRIVER (UINT64_C(1) << 7)
+#define EFI_LOADER_FEATURE_SORT_KEY (UINT64_C(1) << 8)
+#define EFI_LOADER_FEATURE_SAVED_ENTRY (UINT64_C(1) << 9)
+#define EFI_LOADER_FEATURE_DEVICETREE (UINT64_C(1) << 10)
+#define EFI_LOADER_FEATURE_SECUREBOOT_ENROLL (UINT64_C(1) << 11)
+#define EFI_LOADER_FEATURE_RETAIN_SHIM (UINT64_C(1) << 12)
+#define EFI_LOADER_FEATURE_MENU_DISABLE (UINT64_C(1) << 13)
+
+/* Features of the stub, i.e. systemd-stub */
+#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0)
+#define EFI_STUB_FEATURE_PICK_UP_CREDENTIALS (UINT64_C(1) << 1)
+#define EFI_STUB_FEATURE_PICK_UP_SYSEXTS (UINT64_C(1) << 2)
+#define EFI_STUB_FEATURE_THREE_PCRS (UINT64_C(1) << 3)
+#define EFI_STUB_FEATURE_RANDOM_SEED (UINT64_C(1) << 4)
+#define EFI_STUB_FEATURE_CMDLINE_ADDONS (UINT64_C(1) << 5)
+#define EFI_STUB_FEATURE_CMDLINE_SMBIOS (UINT64_C(1) << 6)
+#define EFI_STUB_FEATURE_DEVICETREE_ADDONS (UINT64_C(1) << 7)
+
+typedef enum SecureBootMode {
+ SECURE_BOOT_UNSUPPORTED,
+ SECURE_BOOT_DISABLED,
+ SECURE_BOOT_UNKNOWN,
+ SECURE_BOOT_AUDIT,
+ SECURE_BOOT_DEPLOYED,
+ SECURE_BOOT_SETUP,
+ SECURE_BOOT_USER,
+ _SECURE_BOOT_MAX,
+ _SECURE_BOOT_INVALID = -EINVAL,
+} SecureBootMode;
+
+const sd_char *secure_boot_mode_to_string(SecureBootMode m);
+SecureBootMode decode_secure_boot_mode(bool secure, bool audit, bool deployed, bool setup);
diff --git a/src/fundamental/logarithm.h b/src/fundamental/logarithm.h
new file mode 100644
index 0000000..0b03bbd
--- /dev/null
+++ b/src/fundamental/logarithm.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdint.h>
+
+/* Note: log2(0) == log2(1) == 0 here and below. */
+
+#define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0)
+#define NONCONST_LOG2ULL(x) ({ \
+ unsigned long long _x = (x); \
+ _x > 1 ? (unsigned) __builtin_clzll(_x) ^ 63U : 0; \
+ })
+#define LOG2ULL(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2ULL(x), NONCONST_LOG2ULL(x))
+
+static inline unsigned log2u64(uint64_t x) {
+#if __SIZEOF_LONG_LONG__ == 8
+ return LOG2ULL(x);
+#else
+# error "Wut?"
+#endif
+}
+
+static inline unsigned u32ctz(uint32_t n) {
+#if __SIZEOF_INT__ == 4
+ return n != 0 ? __builtin_ctz(n) : 32;
+#else
+# error "Wut?"
+#endif
+}
+
+#define popcount(n) \
+ _Generic((n), \
+ unsigned char: __builtin_popcount(n), \
+ unsigned short: __builtin_popcount(n), \
+ unsigned: __builtin_popcount(n), \
+ unsigned long: __builtin_popcountl(n), \
+ unsigned long long: __builtin_popcountll(n))
+
+#define CONST_LOG2U(x) ((x) > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1 : 0)
+#define NONCONST_LOG2U(x) ({ \
+ unsigned _x = (x); \
+ _x > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(_x) - 1 : 0; \
+ })
+#define LOG2U(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2U(x), NONCONST_LOG2U(x))
+
+static inline unsigned log2i(int x) {
+ return LOG2U(x);
+}
+
+static inline unsigned log2u(unsigned x) {
+ return LOG2U(x);
+}
+
+static inline unsigned log2u_round_up(unsigned x) {
+ if (x <= 1)
+ return 0;
+
+ return log2u(x - 1) + 1;
+}
diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h
new file mode 100644
index 0000000..797330d
--- /dev/null
+++ b/src/fundamental/macro-fundamental.h
@@ -0,0 +1,515 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#if !SD_BOOT
+# include <assert.h>
+#endif
+
+#include <limits.h>
+#include <stdalign.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* Temporarily disable some warnings */
+#define DISABLE_WARNING_DEPRECATED_DECLARATIONS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+
+#define DISABLE_WARNING_FORMAT_NONLITERAL \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"")
+
+#define DISABLE_WARNING_MISSING_PROTOTYPES \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
+
+#define DISABLE_WARNING_NONNULL \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wnonnull\"")
+
+#define DISABLE_WARNING_SHADOW \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wshadow\"")
+
+#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
+
+#define DISABLE_WARNING_TYPE_LIMITS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
+
+#define DISABLE_WARNING_ADDRESS \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Waddress\"")
+
+#define REENABLE_WARNING \
+ _Pragma("GCC diagnostic pop")
+
+#define _align_(x) __attribute__((__aligned__(x)))
+#define _alignas_(x) __attribute__((__aligned__(alignof(x))))
+#define _alignptr_ __attribute__((__aligned__(sizeof(void *))))
+#define _cleanup_(x) __attribute__((__cleanup__(x)))
+#define _const_ __attribute__((__const__))
+#define _deprecated_ __attribute__((__deprecated__))
+#define _destructor_ __attribute__((__destructor__))
+#define _hidden_ __attribute__((__visibility__("hidden")))
+#define _likely_(x) (__builtin_expect(!!(x), 1))
+#define _malloc_ __attribute__((__malloc__))
+#define _noinline_ __attribute__((noinline))
+#define _noreturn_ _Noreturn
+#define _packed_ __attribute__((__packed__))
+#define _printf_(a, b) __attribute__((__format__(printf, a, b)))
+#define _public_ __attribute__((__visibility__("default")))
+#define _pure_ __attribute__((__pure__))
+#define _retain_ __attribute__((__retain__))
+#define _returns_nonnull_ __attribute__((__returns_nonnull__))
+#define _section_(x) __attribute__((__section__(x)))
+#define _sentinel_ __attribute__((__sentinel__))
+#define _unlikely_(x) (__builtin_expect(!!(x), 0))
+#define _unused_ __attribute__((__unused__))
+#define _used_ __attribute__((__used__))
+#define _warn_unused_result_ __attribute__((__warn_unused_result__))
+#define _weak_ __attribute__((__weak__))
+#define _weakref_(x) __attribute__((__weakref__(#x)))
+
+#ifdef __clang__
+# define _alloc_(...)
+#else
+# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
+#endif
+
+#if __GNUC__ >= 7 || (defined(__clang__) && __clang_major__ >= 10)
+# define _fallthrough_ __attribute__((__fallthrough__))
+#else
+# define _fallthrough_
+#endif
+
+#define XSTRINGIFY(x) #x
+#define STRINGIFY(x) XSTRINGIFY(x)
+
+#ifndef __COVERITY__
+# define VOID_0 ((void)0)
+#else
+# define VOID_0 ((void*)0)
+#endif
+
+#define ELEMENTSOF(x) \
+ (__builtin_choose_expr( \
+ !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
+ sizeof(x)/sizeof((x)[0]), \
+ VOID_0))
+
+#define XCONCATENATE(x, y) x ## y
+#define CONCATENATE(x, y) XCONCATENATE(x, y)
+
+#if SD_BOOT
+ _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
+
+ #ifdef NDEBUG
+ #define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
+ #define assert_not_reached() __builtin_unreachable()
+ #else
+ #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
+ #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__)
+ #endif
+ #define static_assert _Static_assert
+ #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
+#endif
+
+/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
+#define ASSERT_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert)
+#define ASSERT_SE_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert_se)
+#define _ASSERT_PTR(expr, var, check) \
+ ({ \
+ typeof(expr) var = (expr); \
+ check(var); \
+ var; \
+ })
+
+#define ASSERT_NONNEG(expr) \
+ ({ \
+ typeof(expr) _expr_ = (expr), _zero = 0; \
+ assert(_expr_ >= _zero); \
+ _expr_; \
+ })
+
+#define ASSERT_SE_NONNEG(expr) \
+ ({ \
+ typeof(expr) _expr_ = (expr), _zero = 0; \
+ assert_se(_expr_ >= _zero); \
+ _expr_; \
+ })
+
+#define assert_cc(expr) static_assert(expr, #expr)
+
+#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
+#define UNIQ __COUNTER__
+
+/* Note that this works differently from pthread_once(): this macro does
+ * not synchronize code execution, i.e. code that is run conditionalized
+ * on this macro will run concurrently to all other code conditionalized
+ * the same way, there's no ordering or completion enforced. */
+#define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
+#define __ONCE(o) \
+ ({ \
+ static bool (o) = false; \
+ __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
+ })
+
+#undef MAX
+#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
+#define __MAX(aq, a, bq, b) \
+ ({ \
+ const typeof(a) UNIQ_T(A, aq) = (a); \
+ const typeof(b) UNIQ_T(B, bq) = (b); \
+ UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
+ })
+
+#define IS_UNSIGNED_INTEGER_TYPE(type) \
+ (__builtin_types_compatible_p(typeof(type), unsigned char) || \
+ __builtin_types_compatible_p(typeof(type), unsigned short) || \
+ __builtin_types_compatible_p(typeof(type), unsigned) || \
+ __builtin_types_compatible_p(typeof(type), unsigned long) || \
+ __builtin_types_compatible_p(typeof(type), unsigned long long))
+
+#define IS_SIGNED_INTEGER_TYPE(type) \
+ (__builtin_types_compatible_p(typeof(type), signed char) || \
+ __builtin_types_compatible_p(typeof(type), signed short) || \
+ __builtin_types_compatible_p(typeof(type), signed) || \
+ __builtin_types_compatible_p(typeof(type), signed long) || \
+ __builtin_types_compatible_p(typeof(type), signed long long))
+
+/* Evaluates to (void) if _A or _B are not constant or of different types (being integers of different sizes
+ * is also OK as long as the signedness matches) */
+#define CONST_MAX(_A, _B) \
+ (__builtin_choose_expr( \
+ __builtin_constant_p(_A) && \
+ __builtin_constant_p(_B) && \
+ (__builtin_types_compatible_p(typeof(_A), typeof(_B)) || \
+ (IS_UNSIGNED_INTEGER_TYPE(_A) && IS_UNSIGNED_INTEGER_TYPE(_B)) || \
+ (IS_SIGNED_INTEGER_TYPE(_A) && IS_SIGNED_INTEGER_TYPE(_B))), \
+ ((_A) > (_B)) ? (_A) : (_B), \
+ VOID_0))
+
+/* takes two types and returns the size of the larger one */
+#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
+
+#define MAX3(x, y, z) \
+ ({ \
+ const typeof(x) _c = MAX(x, y); \
+ MAX(_c, z); \
+ })
+
+#define MAX4(x, y, z, a) \
+ ({ \
+ const typeof(x) _d = MAX3(x, y, z); \
+ MAX(_d, a); \
+ })
+
+#undef MIN
+#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b))
+#define __MIN(aq, a, bq, b) \
+ ({ \
+ const typeof(a) UNIQ_T(A, aq) = (a); \
+ const typeof(b) UNIQ_T(B, bq) = (b); \
+ UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
+ })
+
+/* evaluates to (void) if _A or _B are not constant or of different types */
+#define CONST_MIN(_A, _B) \
+ (__builtin_choose_expr( \
+ __builtin_constant_p(_A) && \
+ __builtin_constant_p(_B) && \
+ __builtin_types_compatible_p(typeof(_A), typeof(_B)), \
+ ((_A) < (_B)) ? (_A) : (_B), \
+ VOID_0))
+
+#define MIN3(x, y, z) \
+ ({ \
+ const typeof(x) _c = MIN(x, y); \
+ MIN(_c, z); \
+ })
+
+/* Returns true if the passed integer is a positive power of two */
+#define CONST_ISPOWEROF2(x) \
+ ((x) > 0 && ((x) & ((x) - 1)) == 0)
+
+#define ISPOWEROF2(x) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(x), \
+ CONST_ISPOWEROF2(x), \
+ ({ \
+ const typeof(x) _x = (x); \
+ CONST_ISPOWEROF2(_x); \
+ }))
+
+#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
+#define __LESS_BY(aq, a, bq, b) \
+ ({ \
+ const typeof(a) UNIQ_T(A, aq) = (a); \
+ const typeof(b) UNIQ_T(B, bq) = (b); \
+ UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \
+ })
+
+#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b))
+#define __CMP(aq, a, bq, b) \
+ ({ \
+ const typeof(a) UNIQ_T(A, aq) = (a); \
+ const typeof(b) UNIQ_T(B, bq) = (b); \
+ UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \
+ UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \
+ })
+
+#undef CLAMP
+#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high))
+#define __CLAMP(xq, x, lowq, low, highq, high) \
+ ({ \
+ const typeof(x) UNIQ_T(X, xq) = (x); \
+ const typeof(low) UNIQ_T(LOW, lowq) = (low); \
+ const typeof(high) UNIQ_T(HIGH, highq) = (high); \
+ UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \
+ UNIQ_T(HIGH, highq) : \
+ UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \
+ UNIQ_T(LOW, lowq) : \
+ UNIQ_T(X, xq); \
+ })
+
+/* [(x + y - 1) / y] suffers from an integer overflow, even though the
+ * computation should be possible in the given type. Therefore, we use
+ * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
+ * quotient and the remainder, so both should be equally fast. */
+#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y))
+#define __DIV_ROUND_UP(xq, x, yq, y) \
+ ({ \
+ const typeof(x) UNIQ_T(X, xq) = (x); \
+ const typeof(y) UNIQ_T(Y, yq) = (y); \
+ (UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \
+ })
+
+/* Rounds up x to the next multiple of y. Resolves to typeof(x) -1 in case of overflow */
+#define __ROUND_UP(q, x, y) \
+ ({ \
+ const typeof(y) UNIQ_T(A, q) = (y); \
+ const typeof(x) UNIQ_T(B, q) = DIV_ROUND_UP((x), UNIQ_T(A, q)); \
+ typeof(x) UNIQ_T(C, q); \
+ __builtin_mul_overflow(UNIQ_T(B, q), UNIQ_T(A, q), &UNIQ_T(C, q)) ? (typeof(x)) -1 : UNIQ_T(C, q); \
+ })
+#define ROUND_UP(x, y) __ROUND_UP(UNIQ, (x), (y))
+
+#define CASE_F_1(X) case X:
+#define CASE_F_2(X, ...) case X: CASE_F_1( __VA_ARGS__)
+#define CASE_F_3(X, ...) case X: CASE_F_2( __VA_ARGS__)
+#define CASE_F_4(X, ...) case X: CASE_F_3( __VA_ARGS__)
+#define CASE_F_5(X, ...) case X: CASE_F_4( __VA_ARGS__)
+#define CASE_F_6(X, ...) case X: CASE_F_5( __VA_ARGS__)
+#define CASE_F_7(X, ...) case X: CASE_F_6( __VA_ARGS__)
+#define CASE_F_8(X, ...) case X: CASE_F_7( __VA_ARGS__)
+#define CASE_F_9(X, ...) case X: CASE_F_8( __VA_ARGS__)
+#define CASE_F_10(X, ...) case X: CASE_F_9( __VA_ARGS__)
+#define CASE_F_11(X, ...) case X: CASE_F_10( __VA_ARGS__)
+#define CASE_F_12(X, ...) case X: CASE_F_11( __VA_ARGS__)
+#define CASE_F_13(X, ...) case X: CASE_F_12( __VA_ARGS__)
+#define CASE_F_14(X, ...) case X: CASE_F_13( __VA_ARGS__)
+#define CASE_F_15(X, ...) case X: CASE_F_14( __VA_ARGS__)
+#define CASE_F_16(X, ...) case X: CASE_F_15( __VA_ARGS__)
+#define CASE_F_17(X, ...) case X: CASE_F_16( __VA_ARGS__)
+#define CASE_F_18(X, ...) case X: CASE_F_17( __VA_ARGS__)
+#define CASE_F_19(X, ...) case X: CASE_F_18( __VA_ARGS__)
+#define CASE_F_20(X, ...) case X: CASE_F_19( __VA_ARGS__)
+
+#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
+#define FOR_EACH_MAKE_CASE(...) \
+ GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \
+ CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \
+ (__VA_ARGS__)
+
+#define IN_SET(x, first, ...) \
+ ({ \
+ bool _found = false; \
+ /* If the build breaks in the line below, you need to extend the case macros. We use typeof(+x) \
+ * here to widen the type of x if it is a bit-field as this would otherwise be illegal. */ \
+ static const typeof(+x) __assert_in_set[] _unused_ = { first, __VA_ARGS__ }; \
+ assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
+ switch (x) { \
+ FOR_EACH_MAKE_CASE(first, __VA_ARGS__) \
+ _found = true; \
+ break; \
+ default: \
+ break; \
+ } \
+ _found; \
+ })
+
+/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time
+ * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
+#define TAKE_GENERIC(var, type, nullvalue) \
+ ({ \
+ type *_pvar_ = &(var); \
+ type _var_ = *_pvar_; \
+ type _nullvalue_ = nullvalue; \
+ *_pvar_ = _nullvalue_; \
+ _var_; \
+ })
+#define TAKE_PTR_TYPE(ptr, type) TAKE_GENERIC(ptr, type, NULL)
+#define TAKE_PTR(ptr) TAKE_PTR_TYPE(ptr, typeof(ptr))
+#define TAKE_STRUCT_TYPE(s, type) TAKE_GENERIC(s, type, {})
+#define TAKE_STRUCT(s) TAKE_STRUCT_TYPE(s, typeof(s))
+
+/*
+ * STRLEN - return the length of a string literal, minus the trailing NUL byte.
+ * Contrary to strlen(), this is a constant expression.
+ * @x: a string literal.
+ */
+#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
+
+#define mfree(memory) \
+ ({ \
+ free(memory); \
+ (typeof(memory)) NULL; \
+ })
+
+static inline size_t ALIGN_TO(size_t l, size_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ if (l > SIZE_MAX - (ali - 1))
+ return SIZE_MAX; /* indicate overflow */
+
+ return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline uint64_t ALIGN_TO_U64(uint64_t l, uint64_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ if (l > UINT64_MAX - (ali - 1))
+ return UINT64_MAX; /* indicate overflow */
+
+ return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline size_t ALIGN_DOWN(size_t l, size_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & ~(ali - 1);
+}
+
+static inline uint64_t ALIGN_DOWN_U64(uint64_t l, uint64_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & ~(ali - 1);
+}
+
+static inline size_t ALIGN_OFFSET(size_t l, size_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & (ali - 1);
+}
+
+static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
+ assert(ISPOWEROF2(ali));
+
+ return l & (ali - 1);
+}
+
+#define ALIGN2(l) ALIGN_TO(l, 2)
+#define ALIGN4(l) ALIGN_TO(l, 4)
+#define ALIGN8(l) ALIGN_TO(l, 8)
+#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
+#define ALIGN(l) ALIGN_TO(l, sizeof(void*))
+#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
+
+/* Checks if the specified pointer is aligned as appropriate for the specific type */
+#define IS_ALIGNED16(p) (((uintptr_t) p) % alignof(uint16_t) == 0)
+#define IS_ALIGNED32(p) (((uintptr_t) p) % alignof(uint32_t) == 0)
+#define IS_ALIGNED64(p) (((uintptr_t) p) % alignof(uint64_t) == 0)
+
+/* Same as ALIGN_TO but callable in constant contexts. */
+#define CONST_ALIGN_TO(l, ali) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(l) && \
+ __builtin_constant_p(ali) && \
+ CONST_ISPOWEROF2(ali) && \
+ (l <= SIZE_MAX - (ali - 1)), /* overflow? */ \
+ ((l) + (ali) - 1) & ~((ali) - 1), \
+ VOID_0)
+
+/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable
+ * alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a
+ * warning or if you want to assert that the cast gives a pointer of suitable alignment. */
+#define CAST_ALIGN_PTR(t, p) \
+ ({ \
+ const void *_p = (p); \
+ assert(((uintptr_t) _p) % alignof(t) == 0); \
+ (t *) _p; \
+ })
+
+#define UPDATE_FLAG(orig, flag, b) \
+ ((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
+#define SET_FLAG(v, flag, b) \
+ (v) = UPDATE_FLAG(v, flag, b)
+#define FLAGS_SET(v, flags) \
+ ((~(v) & (flags)) == 0)
+
+/* A wrapper for 'func' to return void.
+ * Only useful when a void-returning function is required by some API. */
+#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \
+ static inline void name(type *p) { \
+ func(p); \
+ }
+
+/* When func() returns the void value (NULL, -1, …) of the appropriate type */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
+ static inline void func##p(type *p) { \
+ if (*p) \
+ *p = func(*p); \
+ }
+
+/* When func() doesn't return the appropriate type, set variable to empty afterwards.
+ * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \
+ static inline void func##p(type *p) { \
+ if (*p != (empty)) { \
+ DISABLE_WARNING_ADDRESS; \
+ assert(func); \
+ REENABLE_WARNING; \
+ func(*p); \
+ *p = (empty); \
+ } \
+ }
+
+/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty) \
+ static inline void func##p(type *p) { \
+ if (*p != (empty)) { \
+ func(*p); \
+ *p = (empty); \
+ } \
+ }
+
+/* Declare a flexible array usable in a union.
+ * This is essentially a work-around for a pointless constraint in C99
+ * and might go away in some future version of the standard.
+ *
+ * See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=3080ea5553cc909b000d1f1d964a9041962f2c5b
+ */
+#define DECLARE_FLEX_ARRAY(type, name) \
+ struct { \
+ dummy_t __empty__ ## name; \
+ type name[]; \
+ }
+
+/* Declares an ELF read-only string section that does not occupy memory at runtime. */
+#define DECLARE_NOALLOC_SECTION(name, text) \
+ asm(".pushsection " name ",\"S\"\n\t" \
+ ".ascii " STRINGIFY(text) "\n\t" \
+ ".zero 1\n\t" \
+ ".popsection\n")
+
+#ifdef SBAT_DISTRO
+ #define DECLARE_SBAT(text) DECLARE_NOALLOC_SECTION(".sbat", text)
+#else
+ #define DECLARE_SBAT(text)
+#endif
diff --git a/src/fundamental/memory-util-fundamental.h b/src/fundamental/memory-util-fundamental.h
new file mode 100644
index 0000000..6870f54
--- /dev/null
+++ b/src/fundamental/memory-util-fundamental.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stddef.h>
+
+#if SD_BOOT
+# include "efi-string.h"
+#else
+# include <string.h>
+#endif
+
+#include "macro-fundamental.h"
+
+#define memzero(x, l) \
+ ({ \
+ size_t _l_ = (l); \
+ _l_ > 0 ? memset((x), 0, _l_) : (x); \
+ })
+
+#if !SD_BOOT && HAVE_EXPLICIT_BZERO
+static inline void *explicit_bzero_safe(void *p, size_t l) {
+ if (p && l > 0)
+ explicit_bzero(p, l);
+
+ return p;
+}
+#else
+static inline void *explicit_bzero_safe(void *p, size_t l) {
+ if (p && l > 0) {
+ memset(p, 0, l);
+ __asm__ __volatile__("" : : "r"(p) : "memory");
+ }
+ return p;
+}
+#endif
+
+struct VarEraser {
+ /* NB: This is a pointer to memory to erase in case of CLEANUP_ERASE(). Pointer to pointer to memory
+ * to erase in case of CLEANUP_ERASE_PTR() */
+ void *p;
+ size_t size;
+};
+
+static inline void erase_var(struct VarEraser *e) {
+ explicit_bzero_safe(e->p, e->size);
+}
+
+/* Mark var to be erased when leaving scope. */
+#define CLEANUP_ERASE(var) \
+ _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \
+ .p = &(var), \
+ .size = sizeof(var), \
+ }
+
+static inline void erase_varp(struct VarEraser *e) {
+
+ /* Very similar to erase_var(), but assumes `p` is a pointer to a pointer whose memory shall be destructed. */
+ if (!e->p)
+ return;
+
+ explicit_bzero_safe(*(void**) e->p, e->size);
+}
+
+/* Mark pointer so that memory pointed to is erased when leaving scope. Note: this takes a pointer to the
+ * specified pointer, instead of just a copy of it. This is to allow callers to invalidate the pointer after
+ * use, if they like, disabling our automatic erasure (for example because they succeeded with whatever they
+ * wanted to do and now intend to return the allocated buffer to their caller without it being erased). */
+#define CLEANUP_ERASE_PTR(ptr, sz) \
+ _cleanup_(erase_varp) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \
+ .p = (ptr), \
+ .size = (sz), \
+ }
+
+typedef void (*free_array_func_t)(void *p, size_t n);
+
+/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
+typedef struct ArrayCleanup {
+ void **parray;
+ size_t *pn;
+ free_array_func_t pfunc;
+} ArrayCleanup;
+
+static inline void array_cleanup(const ArrayCleanup *c) {
+ assert(c);
+
+ assert(!c->parray == !c->pn);
+
+ if (!c->parray)
+ return;
+
+ if (*c->parray) {
+ assert(c->pfunc);
+ c->pfunc(*c->parray, *c->pn);
+ *c->parray = NULL;
+ }
+
+ *c->pn = 0;
+}
+
+#define CLEANUP_ARRAY(array, n, func) \
+ _cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
+ .parray = (void**) &(array), \
+ .pn = &(n), \
+ .pfunc = (free_array_func_t) ({ \
+ void (*_f)(typeof(array[0]) *a, size_t b) = func; \
+ _f; \
+ }), \
+ }
diff --git a/src/fundamental/meson.build b/src/fundamental/meson.build
new file mode 100644
index 0000000..b7ca6cf
--- /dev/null
+++ b/src/fundamental/meson.build
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+fundamental_include = include_directories('.')
+
+fundamental_sources = files(
+ 'bootspec-fundamental.c',
+ 'efivars-fundamental.c',
+ 'sha256.c',
+ 'string-util-fundamental.c',
+ 'uki.c',
+)
diff --git a/src/fundamental/sbat.h b/src/fundamental/sbat.h
new file mode 100644
index 0000000..9288e05
--- /dev/null
+++ b/src/fundamental/sbat.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#ifdef SBAT_DISTRO
+# include "version.h"
+# define SBAT_MAGIC "sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md\n"
+# define SBAT_BOOT_SECTION_TEXT \
+ SBAT_MAGIC \
+ SBAT_PROJECT "-boot" ",1,The systemd Developers," SBAT_PROJECT "," PROJECT_VERSION "," PROJECT_URL "\n" \
+ SBAT_PROJECT "-boot" "." SBAT_DISTRO "," STRINGIFY(SBAT_DISTRO_GENERATION) "," SBAT_DISTRO_SUMMARY "," SBAT_DISTRO_PKGNAME "," SBAT_DISTRO_VERSION "," SBAT_DISTRO_URL "\n"
+# define SBAT_STUB_SECTION_TEXT \
+ SBAT_MAGIC \
+ SBAT_PROJECT "-stub" ",1,The systemd Developers," SBAT_PROJECT "," PROJECT_VERSION "," PROJECT_URL "\n" \
+ SBAT_PROJECT "-stub" "." SBAT_DISTRO "," STRINGIFY(SBAT_DISTRO_GENERATION) "," SBAT_DISTRO_SUMMARY "," SBAT_DISTRO_PKGNAME "," SBAT_DISTRO_VERSION "," SBAT_DISTRO_URL "\n"
+#endif
diff --git a/src/fundamental/sha256.c b/src/fundamental/sha256.c
new file mode 100644
index 0000000..4389e9e
--- /dev/null
+++ b/src/fundamental/sha256.c
@@ -0,0 +1,285 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/* Stolen from glibc and converted to our style. In glibc it comes with the following copyright blurb: */
+
+/* Functions to compute SHA256 message digest of files or memory blocks.
+ according to the definition of SHA256 in FIPS 180-2.
+ Copyright (C) 2007-2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdbool.h>
+#if SD_BOOT
+# include "efi-string.h"
+#else
+# include <string.h>
+#endif
+
+#include "macro-fundamental.h"
+#include "sha256.h"
+#include "unaligned-fundamental.h"
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define SWAP(n) \
+ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+# define SWAP64(n) \
+ (((n) << 56) \
+ | (((n) & 0xff00) << 40) \
+ | (((n) & 0xff0000) << 24) \
+ | (((n) & 0xff000000) << 8) \
+ | (((n) >> 8) & 0xff000000) \
+ | (((n) >> 24) & 0xff0000) \
+ | (((n) >> 40) & 0xff00) \
+ | ((n) >> 56))
+#else
+# define SWAP(n) (n)
+# define SWAP64(n) (n)
+#endif
+
+/* This array contains the bytes used to pad the buffer to the next
+ 64-byte boundary. (FIPS 180-2:5.1.1) */
+static const uint8_t fillbuf[64] = {
+ 0x80, 0 /* , 0, 0, ... */
+};
+
+/* Constants for SHA256 from FIPS 180-2:4.2.2. */
+static const uint32_t K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+static void sha256_process_block(const void *, size_t, struct sha256_ctx *);
+
+/* Initialize structure containing state of computation.
+ (FIPS 180-2:5.3.2) */
+void sha256_init_ctx(struct sha256_ctx *ctx) {
+ assert(ctx);
+
+ ctx->H[0] = 0x6a09e667;
+ ctx->H[1] = 0xbb67ae85;
+ ctx->H[2] = 0x3c6ef372;
+ ctx->H[3] = 0xa54ff53a;
+ ctx->H[4] = 0x510e527f;
+ ctx->H[5] = 0x9b05688c;
+ ctx->H[6] = 0x1f83d9ab;
+ ctx->H[7] = 0x5be0cd19;
+
+ ctx->total64 = 0;
+ ctx->buflen = 0;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+ prolog according to the standard and write the result to RESBUF. */
+uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]) {
+ /* Take yet unprocessed bytes into account. */
+ uint32_t bytes = ctx->buflen;
+ size_t pad;
+
+ assert(ctx);
+ assert(resbuf);
+
+ /* Now count remaining bytes. */
+ ctx->total64 += bytes;
+
+ pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+ memcpy(&ctx->buffer[bytes], fillbuf, pad);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ ctx->buffer32[(bytes + pad + 4) / 4] = SWAP(ctx->total[TOTAL64_low] << 3);
+ ctx->buffer32[(bytes + pad) / 4] = SWAP((ctx->total[TOTAL64_high] << 3)
+ | (ctx->total[TOTAL64_low] >> 29));
+
+ /* Process last bytes. */
+ sha256_process_block(ctx->buffer, bytes + pad + 8, ctx);
+
+ /* Put result from CTX in first 32 bytes following RESBUF. */
+ for (size_t i = 0; i < 8; ++i)
+ unaligned_write_ne32(resbuf + i * sizeof(uint32_t), SWAP(ctx->H[i]));
+ return resbuf;
+}
+
+void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx) {
+ assert(buffer);
+ assert(ctx);
+
+ /* When we already have some bits in our internal buffer concatenate
+ both inputs first. */
+
+ if (ctx->buflen != 0) {
+ size_t left_over = ctx->buflen;
+ size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+ memcpy(&ctx->buffer[left_over], buffer, add);
+ ctx->buflen += add;
+
+ if (ctx->buflen > 64) {
+ sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx);
+
+ ctx->buflen &= 63;
+ /* The regions in the following copy operation cannot overlap. */
+ memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+ ctx->buflen);
+ }
+
+ buffer = (const char *) buffer + add;
+ len -= add;
+ }
+
+ /* Process available complete blocks. */
+ if (len >= 64) {
+ if (IS_ALIGNED32(buffer)) {
+ sha256_process_block(buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ } else
+ while (len > 64) {
+ memcpy(ctx->buffer, buffer, 64);
+ sha256_process_block(ctx->buffer, 64, ctx);
+ buffer = (const char *) buffer + 64;
+ len -= 64;
+ }
+ }
+
+ /* Move remaining bytes into internal buffer. */
+ if (len > 0) {
+ size_t left_over = ctx->buflen;
+
+ memcpy(&ctx->buffer[left_over], buffer, len);
+ left_over += len;
+ if (left_over >= 64) {
+ sha256_process_block(ctx->buffer, 64, ctx);
+ left_over -= 64;
+ memcpy(ctx->buffer, &ctx->buffer[64], left_over);
+ }
+ ctx->buflen = left_over;
+ }
+}
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+ It is assumed that LEN % 64 == 0. */
+static void sha256_process_block(const void *buffer, size_t len, struct sha256_ctx *ctx) {
+ const uint32_t *words = ASSERT_PTR(buffer);
+ size_t nwords = len / sizeof(uint32_t);
+
+ assert(ctx);
+
+ uint32_t a = ctx->H[0];
+ uint32_t b = ctx->H[1];
+ uint32_t c = ctx->H[2];
+ uint32_t d = ctx->H[3];
+ uint32_t e = ctx->H[4];
+ uint32_t f = ctx->H[5];
+ uint32_t g = ctx->H[6];
+ uint32_t h = ctx->H[7];
+
+ /* First increment the byte count. FIPS 180-2 specifies the possible
+ length of the file up to 2^64 bits. Here we only compute the
+ number of bytes. */
+ ctx->total64 += len;
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ while (nwords > 0) {
+ uint32_t W[64];
+ uint32_t a_save = a;
+ uint32_t b_save = b;
+ uint32_t c_save = c;
+ uint32_t d_save = d;
+ uint32_t e_save = e;
+ uint32_t f_save = f;
+ uint32_t g_save = g;
+ uint32_t h_save = h;
+
+ /* Operators defined in FIPS 180-2:4.1.2. */
+#define Ch(x, y, z) ((x & y) ^ (~x & z))
+#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define S0(x) (CYCLIC (x, 2) ^ CYCLIC (x, 13) ^ CYCLIC (x, 22))
+#define S1(x) (CYCLIC (x, 6) ^ CYCLIC (x, 11) ^ CYCLIC (x, 25))
+#define R0(x) (CYCLIC (x, 7) ^ CYCLIC (x, 18) ^ (x >> 3))
+#define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10))
+
+ /* It is unfortunate that C does not provide an operator for
+ cyclic rotation. Hope the C compiler is smart enough. */
+#define CYCLIC(w, s) ((w >> s) | (w << (32 - s)))
+
+ /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2. */
+ for (size_t t = 0; t < 16; ++t) {
+ W[t] = SWAP (*words);
+ ++words;
+ }
+ for (size_t t = 16; t < 64; ++t)
+ W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16];
+
+ /* The actual computation according to FIPS 180-2:6.2.2 step 3. */
+ for (size_t t = 0; t < 64; ++t) {
+ uint32_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t];
+ uint32_t T2 = S0 (a) + Maj (a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ /* Add the starting values of the context according to FIPS 180-2:6.2.2
+ step 4. */
+ a += a_save;
+ b += b_save;
+ c += c_save;
+ d += d_save;
+ e += e_save;
+ f += f_save;
+ g += g_save;
+ h += h_save;
+
+ /* Prepare for the next round. */
+ nwords -= 16;
+ }
+
+ /* Put checksum in context given as argument. */
+ ctx->H[0] = a;
+ ctx->H[1] = b;
+ ctx->H[2] = c;
+ ctx->H[3] = d;
+ ctx->H[4] = e;
+ ctx->H[5] = f;
+ ctx->H[6] = g;
+ ctx->H[7] = h;
+}
+
+uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]) {
+ struct sha256_ctx ctx;
+ sha256_init_ctx(&ctx);
+ sha256_process_bytes(buffer, sz, &ctx);
+ return sha256_finish_ctx(&ctx, result);
+}
diff --git a/src/fundamental/sha256.h b/src/fundamental/sha256.h
new file mode 100644
index 0000000..dbb08e3
--- /dev/null
+++ b/src/fundamental/sha256.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define SHA256_DIGEST_SIZE 32
+
+struct sha256_ctx {
+ uint32_t H[8];
+
+ union {
+ uint64_t total64;
+#define TOTAL64_low (1 - (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+#define TOTAL64_high (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ uint32_t total[2];
+ };
+
+ uint32_t buflen;
+
+ union {
+ uint8_t buffer[128]; /* NB: always correctly aligned for UINT32. */
+ uint32_t buffer32[32];
+ uint64_t buffer64[16];
+ };
+};
+
+void sha256_init_ctx(struct sha256_ctx *ctx);
+uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]);
+void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx);
+
+static inline void sha256_process_bytes_and_size(const void *buffer, size_t len, struct sha256_ctx *ctx) {
+ sha256_process_bytes(&len, sizeof(len), ctx);
+ sha256_process_bytes(buffer, len, ctx);
+}
+
+uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]);
+
+#define SHA256_DIRECT(buffer, sz) sha256_direct(buffer, sz, (uint8_t[SHA256_DIGEST_SIZE]) {})
diff --git a/src/fundamental/string-util-fundamental.c b/src/fundamental/string-util-fundamental.c
new file mode 100644
index 0000000..a5bafc6
--- /dev/null
+++ b/src/fundamental/string-util-fundamental.c
@@ -0,0 +1,228 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#if !SD_BOOT
+# include <ctype.h>
+#endif
+
+#include "macro-fundamental.h"
+#include "string-util-fundamental.h"
+
+sd_char *startswith(const sd_char *s, const sd_char *prefix) {
+ size_t l;
+
+ assert(s);
+ assert(prefix);
+
+ l = strlen(prefix);
+ if (!strneq(s, prefix, l))
+ return NULL;
+
+ return (sd_char*) s + l;
+}
+
+sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
+ size_t l;
+
+ assert(s);
+ assert(prefix);
+
+ l = strlen(prefix);
+ if (!strncaseeq(s, prefix, l))
+ return NULL;
+
+ return (sd_char*) s + l;
+}
+
+sd_char* endswith(const sd_char *s, const sd_char *postfix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return (sd_char*) s + sl;
+
+ if (sl < pl)
+ return NULL;
+
+ if (strcmp(s + sl - pl, postfix) != 0)
+ return NULL;
+
+ return (sd_char*) s + sl - pl;
+}
+
+sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return (sd_char*) s + sl;
+
+ if (sl < pl)
+ return NULL;
+
+ if (strcasecmp(s + sl - pl, postfix) != 0)
+ return NULL;
+
+ return (sd_char*) s + sl - pl;
+}
+
+static bool is_valid_version_char(sd_char a) {
+ return ascii_isdigit(a) || ascii_isalpha(a) || IN_SET(a, '~', '-', '^', '.');
+}
+
+int strverscmp_improved(const sd_char *a, const sd_char *b) {
+ /* This function is similar to strverscmp(3), but it treats '-' and '.' as separators.
+ *
+ * The logic is based on rpm's rpmvercmp(), but unlike rpmvercmp(), it distiguishes e.g.
+ * '123a' and '123.a', with '123a' being newer.
+ *
+ * It allows direct comparison of strings which contain both a version and a release; e.g.
+ * '247.2-3.1.fc33.x86_64' or '5.11.0-0.rc5.20210128git76c057c84d28.137.fc34'.
+ *
+ * The input string is split into segments. Each segment is numeric or alphabetic, and may be
+ * prefixed with the following:
+ * '~' : used for pre-releases, a segment prefixed with this is the oldest,
+ * '-' : used for the separator between version and release,
+ * '^' : used for patched releases, a segment with this is newer than one with '-'.
+ * '.' : used for point releases.
+ * Note that no prefix segment is the newest. All non-supported characters are dropped, and
+ * handled as a separator of segments, e.g., '123_a' is equivalent to '123a'.
+ *
+ * By using this, version strings can be sorted like following:
+ * (older) 122.1
+ * ^ 123~rc1-1
+ * | 123
+ * | 123-a
+ * | 123-a.1
+ * | 123-1
+ * | 123-1.1
+ * | 123^post1
+ * | 123.a-1
+ * | 123.1-1
+ * v 123a-1
+ * (newer) 124-1
+ */
+
+ a = strempty(a);
+ b = strempty(b);
+
+ for (;;) {
+ const sd_char *aa, *bb;
+ int r;
+
+ /* Drop leading invalid characters. */
+ while (*a != '\0' && !is_valid_version_char(*a))
+ a++;
+ while (*b != '\0' && !is_valid_version_char(*b))
+ b++;
+
+ /* Handle '~'. Used for pre-releases, e.g. 123~rc1, or 4.5~alpha1 */
+ if (*a == '~' || *b == '~') {
+ /* The string prefixed with '~' is older. */
+ r = CMP(*a != '~', *b != '~');
+ if (r != 0)
+ return r;
+
+ /* Now both strings are prefixed with '~'. Compare remaining strings. */
+ a++;
+ b++;
+ }
+
+ /* If at least one string reaches the end, then longer is newer.
+ * Note that except for '~' prefixed segments, a string which has more segments is newer.
+ * So, this check must be after the '~' check. */
+ if (*a == '\0' || *b == '\0')
+ return CMP(*a, *b);
+
+ /* Handle '-', which separates version and release, e.g 123.4-3.1.fc33.x86_64 */
+ if (*a == '-' || *b == '-') {
+ /* The string prefixed with '-' is older (e.g., 123-9 vs 123.1-1) */
+ r = CMP(*a != '-', *b != '-');
+ if (r != 0)
+ return r;
+
+ a++;
+ b++;
+ }
+
+ /* Handle '^'. Used for patched release. */
+ if (*a == '^' || *b == '^') {
+ r = CMP(*a != '^', *b != '^');
+ if (r != 0)
+ return r;
+
+ a++;
+ b++;
+ }
+
+ /* Handle '.'. Used for point releases. */
+ if (*a == '.' || *b == '.') {
+ r = CMP(*a != '.', *b != '.');
+ if (r != 0)
+ return r;
+
+ a++;
+ b++;
+ }
+
+ if (ascii_isdigit(*a) || ascii_isdigit(*b)) {
+ /* Find the leading numeric segments. One may be an empty string. So,
+ * numeric segments are always newer than alpha segments. */
+ for (aa = a; ascii_isdigit(*aa); aa++)
+ ;
+ for (bb = b; ascii_isdigit(*bb); bb++)
+ ;
+
+ /* Check if one of the strings was empty, but the other not. */
+ r = CMP(a != aa, b != bb);
+ if (r != 0)
+ return r;
+
+ /* Skip leading '0', to make 00123 equivalent to 123. */
+ while (*a == '0')
+ a++;
+ while (*b == '0')
+ b++;
+
+ /* To compare numeric segments without parsing their values, first compare the
+ * lengths of the segments. Eg. 12345 vs 123, longer is newer. */
+ r = CMP(aa - a, bb - b);
+ if (r != 0)
+ return r;
+
+ /* Then, compare them as strings. */
+ r = CMP(strncmp(a, b, aa - a), 0);
+ if (r != 0)
+ return r;
+ } else {
+ /* Find the leading non-numeric segments. */
+ for (aa = a; ascii_isalpha(*aa); aa++)
+ ;
+ for (bb = b; ascii_isalpha(*bb); bb++)
+ ;
+
+ /* Note that the segments are usually not NUL-terminated. */
+ r = CMP(strncmp(a, b, MIN(aa - a, bb - b)), 0);
+ if (r != 0)
+ return r;
+
+ /* Longer is newer, e.g. abc vs abcde. */
+ r = CMP(aa - a, bb - b);
+ if (r != 0)
+ return r;
+ }
+
+ /* The current segments are equivalent. Let's move to the next one. */
+ a = aa;
+ b = bb;
+ }
+}
diff --git a/src/fundamental/string-util-fundamental.h b/src/fundamental/string-util-fundamental.h
new file mode 100644
index 0000000..b537b2e
--- /dev/null
+++ b/src/fundamental/string-util-fundamental.h
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#if SD_BOOT
+# include "efi.h"
+# include "efi-string.h"
+#else
+# include <string.h>
+#endif
+
+#include "macro-fundamental.h"
+
+#if SD_BOOT
+# define strlen strlen16
+# define strcmp strcmp16
+# define strncmp strncmp16
+# define strcasecmp strcasecmp16
+# define strncasecmp strncasecmp16
+# define STR_C(str) (L ## str)
+typedef char16_t sd_char;
+#else
+# define STR_C(str) (str)
+typedef char sd_char;
+#endif
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
+#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
+
+static inline int strcmp_ptr(const sd_char *a, const sd_char *b) {
+ if (a && b)
+ return strcmp(a, b);
+
+ return CMP(a, b);
+}
+
+static inline int strcasecmp_ptr(const sd_char *a, const sd_char *b) {
+ if (a && b)
+ return strcasecmp(a, b);
+
+ return CMP(a, b);
+}
+
+static inline bool streq_ptr(const sd_char *a, const sd_char *b) {
+ return strcmp_ptr(a, b) == 0;
+}
+
+static inline bool strcaseeq_ptr(const sd_char *a, const sd_char *b) {
+ return strcasecmp_ptr(a, b) == 0;
+}
+
+static inline size_t strlen_ptr(const sd_char *s) {
+ if (!s)
+ return 0;
+
+ return strlen(s);
+}
+
+sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
+sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
+sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_;
+sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_;
+
+static inline bool isempty(const sd_char *a) {
+ return !a || a[0] == '\0';
+}
+
+static inline const sd_char *strempty(const sd_char *s) {
+ return s ?: STR_C("");
+}
+
+static inline const sd_char *yes_no(bool b) {
+ return b ? STR_C("yes") : STR_C("no");
+}
+
+static inline const sd_char *on_off(bool b) {
+ return b ? STR_C("on") : STR_C("off");
+}
+
+static inline const sd_char* comparison_operator(int result) {
+ return result < 0 ? STR_C("<") : result > 0 ? STR_C(">") : STR_C("==");
+}
+
+int strverscmp_improved(const sd_char *a, const sd_char *b);
+
+/* Like startswith(), but operates on arbitrary memory blocks */
+static inline void *memory_startswith(const void *p, size_t sz, const sd_char *token) {
+ assert(token);
+
+ size_t n = strlen(token) * sizeof(sd_char);
+ if (sz < n)
+ return NULL;
+
+ assert(p);
+
+ if (memcmp(p, token, n) != 0)
+ return NULL;
+
+ return (uint8_t*) p + n;
+}
+
+#define _STRV_FOREACH(s, l, i) \
+ for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++)
+
+#define STRV_FOREACH(s, l) \
+ _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ))
+
+static inline bool ascii_isdigit(sd_char a) {
+ /* A pure ASCII, locale independent version of isdigit() */
+ return a >= '0' && a <= '9';
+}
+
+static inline bool ascii_ishex(sd_char a) {
+ return ascii_isdigit(a) || (a >= 'a' && a <= 'f') || (a >= 'A' && a <= 'F');
+}
+
+static inline bool ascii_isalpha(sd_char a) {
+ /* A pure ASCII, locale independent version of isalpha() */
+ return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
+}
diff --git a/src/fundamental/tpm2-pcr.h b/src/fundamental/tpm2-pcr.h
new file mode 100644
index 0000000..d0d5b74
--- /dev/null
+++ b/src/fundamental/tpm2-pcr.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro-fundamental.h"
+
+/* The various TPM PCRs we measure into from sd-stub and sd-boot. */
+
+enum {
+ /* The following names for PCRs 0…7 are based on the names in the "TCG PC Client Specific Platform
+ * Firmware Profile Specification"
+ * (https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/) */
+ TPM2_PCR_PLATFORM_CODE = 0,
+ TPM2_PCR_PLATFORM_CONFIG = 1,
+ TPM2_PCR_EXTERNAL_CODE = 2,
+ TPM2_PCR_EXTERNAL_CONFIG = 3,
+ TPM2_PCR_BOOT_LOADER_CODE = 4,
+ TPM2_PCR_BOOT_LOADER_CONFIG = 5,
+ TPM2_PCR_HOST_PLATFORM = 6,
+ TPM2_PCR_SECURE_BOOT_POLICY = 7,
+
+ /* The following names for PCRs 9…15 are based on the "Linux TPM PCR Registry"
+ (https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/) */
+ TPM2_PCR_KERNEL_INITRD = 9,
+ TPM2_PCR_IMA = 10,
+
+ /* systemd: This TPM PCR is where we extend the sd-stub "payloads" into, before using them. i.e. the kernel
+ * ELF image, embedded initrd, and so on. In contrast to PCR 4 (which also contains this data, given
+ * the whole surrounding PE image is measured into it) this should be reasonably pre-calculatable,
+ * because it *only* consists of static data from the kernel PE image. */
+ TPM2_PCR_KERNEL_BOOT = 11,
+
+ /* systemd: This TPM PCR is where sd-stub extends the kernel command line and any passed credentials into. */
+ TPM2_PCR_KERNEL_CONFIG = 12,
+
+ /* systemd: This TPM PCR is where we extend the initrd sysext images into which we pass to the booted kernel */
+ TPM2_PCR_SYSEXTS = 13,
+ TPM2_PCR_SHIM_POLICY = 14,
+
+ /* systemd: This TPM PCR is where we measure the root fs volume key (and maybe /var/'s) if it is split off */
+ TPM2_PCR_SYSTEM_IDENTITY = 15,
+
+ /* As per "TCG PC Client Specific Platform Firmware Profile Specification" again, see above */
+ TPM2_PCR_DEBUG = 16,
+ TPM2_PCR_APPLICATION_SUPPORT = 23,
+};
+
+/* The tag used for EV_EVENT_TAG event log records covering the boot loader config */
+#define LOADER_CONF_EVENT_TAG_ID UINT32_C(0xf5bc582a)
+
+/* The tag used for EV_EVENT_TAG event log records covering Devicetree blobs */
+#define DEVICETREE_ADDON_EVENT_TAG_ID UINT32_C(0x6c46f751)
diff --git a/src/fundamental/uki.c b/src/fundamental/uki.c
new file mode 100644
index 0000000..b1fa044
--- /dev/null
+++ b/src/fundamental/uki.c
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stddef.h>
+
+#include "uki.h"
+
+const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = {
+ /* These section names must fit in 8ch (excluding any trailing NUL) as per PE spec for executables:
+ * https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers
+ * (Note that PE *object* files may have longer section names (via indirection in the string table) but
+ * this is not allowed for PE *executables*, which UKIs are.) */
+ [UNIFIED_SECTION_LINUX] = ".linux",
+ [UNIFIED_SECTION_OSREL] = ".osrel",
+ [UNIFIED_SECTION_CMDLINE] = ".cmdline",
+ [UNIFIED_SECTION_INITRD] = ".initrd",
+ [UNIFIED_SECTION_SPLASH] = ".splash",
+ [UNIFIED_SECTION_DTB] = ".dtb",
+ [UNIFIED_SECTION_UNAME] = ".uname",
+ [UNIFIED_SECTION_SBAT] = ".sbat",
+ [UNIFIED_SECTION_PCRSIG] = ".pcrsig",
+ [UNIFIED_SECTION_PCRPKEY] = ".pcrpkey",
+ NULL,
+};
diff --git a/src/fundamental/uki.h b/src/fundamental/uki.h
new file mode 100644
index 0000000..ffa960f
--- /dev/null
+++ b/src/fundamental/uki.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro-fundamental.h"
+
+/* List of PE sections that have special meaning for us in unified kernels. This is the canonical order in
+ * which we measure the sections into TPM PCR 11. PLEASE DO NOT REORDER! */
+typedef enum UnifiedSection {
+ UNIFIED_SECTION_LINUX,
+ UNIFIED_SECTION_OSREL,
+ UNIFIED_SECTION_CMDLINE,
+ UNIFIED_SECTION_INITRD,
+ UNIFIED_SECTION_SPLASH,
+ UNIFIED_SECTION_DTB,
+ UNIFIED_SECTION_UNAME,
+ UNIFIED_SECTION_SBAT,
+ UNIFIED_SECTION_PCRSIG,
+ UNIFIED_SECTION_PCRPKEY,
+ _UNIFIED_SECTION_MAX,
+} UnifiedSection;
+
+extern const char* const unified_sections[_UNIFIED_SECTION_MAX + 1];
+
+static inline bool unified_section_measure(UnifiedSection section) {
+ /* Don't include the PCR signature in the PCR measurements, since they sign the expected result of
+ * the measurement, and hence shouldn't be input to it. */
+ return section >= 0 && section < _UNIFIED_SECTION_MAX && section != UNIFIED_SECTION_PCRSIG;
+}
diff --git a/src/fundamental/unaligned-fundamental.h b/src/fundamental/unaligned-fundamental.h
new file mode 100644
index 0000000..a4c810a
--- /dev/null
+++ b/src/fundamental/unaligned-fundamental.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdint.h>
+
+static inline uint16_t unaligned_read_ne16(const void *_u) {
+ const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
+
+ return u->x;
+}
+
+static inline uint32_t unaligned_read_ne32(const void *_u) {
+ const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
+
+ return u->x;
+}
+
+static inline uint64_t unaligned_read_ne64(const void *_u) {
+ const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
+
+ return u->x;
+}
+
+static inline void unaligned_write_ne16(void *_u, uint16_t a) {
+ struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
+
+ u->x = a;
+}
+
+static inline void unaligned_write_ne32(void *_u, uint32_t a) {
+ struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
+
+ u->x = a;
+}
+
+static inline void unaligned_write_ne64(void *_u, uint64_t a) {
+ struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
+
+ u->x = a;
+}