mirror of
https://github.com/systemd/systemd.git
synced 2025-03-13 00:58:27 +03:00
commit
682195a00a
@ -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)
|
||||
|
@ -77,7 +77,7 @@
|
||||
<option>--osrel=</option>, <option>--cmdline=</option>, <option>--initrd=</option>,
|
||||
<option>--ucode=</option>, <option>--splash=</option>, <option>--dtb=</option>,
|
||||
<option>--uname=</option>, <option>--sbat=</option>, <option>--pcrpkey=</option>,
|
||||
<option>--profile=</option>, see below. Only <option>--linux=</option> is mandatory. (Alternatively,
|
||||
<option>--profile=</option>, <option>--dtbauto=</option>, <option>--hwids=</option>, see below. Only <option>--linux=</option> is mandatory. (Alternatively,
|
||||
specify <option>--current</option> to use the current values of PCR register 11 instead.)</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v252"/>
|
||||
@ -125,6 +125,8 @@
|
||||
<term><option>--sbat=<replaceable>PATH</replaceable></option></term>
|
||||
<term><option>--pcrpkey=<replaceable>PATH</replaceable></option></term>
|
||||
<term><option>--profile=<replaceable>PATH</replaceable></option></term>
|
||||
<term><option>--dtbauto=<replaceable>PATH</replaceable></option></term>
|
||||
<term><option>--hwids=<replaceable>PATH</replaceable></option></term>
|
||||
|
||||
<listitem><para>When used with the <command>calculate</command> or <command>sign</command> verb,
|
||||
configures the files to read the unified kernel image components from. Each option corresponds with
|
||||
@ -134,7 +136,7 @@
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v252"/>
|
||||
|
||||
<para id="v257">With the exception of <option>--profile=</option>, which has been added in version
|
||||
<para id="v257">With the exception of <option>--profile=</option>, <option>--dtbauto=</option> and <option>--hwids=</option>, which have been added in version
|
||||
257.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
@ -79,6 +79,20 @@
|
||||
|
||||
<listitem><para>A <literal>.dtb</literal> section with a compiled binary DeviceTree.</para></listitem>
|
||||
|
||||
<listitem><para>Zero or more <literal>.dtbauto</literal> sections. Stub will always try to find first matching one.
|
||||
Matching process extracts first <varname>compatible</varname> string from <literal>.dtbauto</literal>
|
||||
section and compares it with the first Devicetree's <varname>compatible</varname> string supplied by
|
||||
the firmware in configuration tables. If firmware does not provide Devicetree, matching with
|
||||
<varname>.hwids</varname> section will be used instead. Stub will use SMBIOS data to calculate hardware
|
||||
IDs of the machine (as per <ulink url="https://learn.microsoft.com/en-us/windows-hardware/drivers/install/specifying-hardware-ids-for-a-computer">specification</ulink>),
|
||||
then it will proceed to trying to find any of them in <literal>.hwids</literal> section and will use first
|
||||
matching entry's <varname>compatible</varname> as a search key among the <literal>.dtbauto</literal>
|
||||
entries, in a similar fashion as the use of <varname>compatible</varname> string read from the firmware
|
||||
provided Devicetree was described before. First matching <literal>.dtbauto</literal> section will be
|
||||
loaded and will override <varname>.dtb</varname> if present.</para></listitem>
|
||||
|
||||
<listitem><para>A <literal>.hwids</literal> section with hardware IDs of the machines to match Devicetrees (refer to <literal>.dtbauto</literal> section description).</para></listitem>
|
||||
|
||||
<listitem><para>A <literal>.uname</literal> section with the kernel version information, i.e. the
|
||||
output of <command>uname -r</command> for the kernel included in the <literal>.linux</literal>
|
||||
section.</para></listitem>
|
||||
|
129
src/boot/efi/chid.c
Normal file
129
src/boot/efi/chid.c
Normal file
@ -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;
|
||||
}
|
23
src/boot/efi/chid.h
Normal file
23
src/boot/efi/chid.h
Normal file
@ -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);
|
@ -106,6 +106,129 @@ EFI_STATUS devicetree_install(struct devicetree_state *state, EFI_FILE *root_dir
|
||||
MAKE_GUID_PTR(EFI_DTB_TABLE), PHYSICAL_ADDRESS_TO_POINTER(state->addr));
|
||||
}
|
||||
|
||||
static const char* devicetree_get_compatible(const void *dtb) {
|
||||
if ((uintptr_t) dtb % alignof(FdtHeader) != 0)
|
||||
return NULL;
|
||||
|
||||
const FdtHeader *dt_header = ASSERT_PTR(dtb);
|
||||
|
||||
if (be32toh(dt_header->magic) != UINT32_C(0xd00dfeed))
|
||||
return NULL;
|
||||
|
||||
uint32_t dt_size = be32toh(dt_header->total_size);
|
||||
uint32_t struct_off = be32toh(dt_header->off_dt_struct);
|
||||
uint32_t struct_size = be32toh(dt_header->size_dt_struct);
|
||||
uint32_t strings_off = be32toh(dt_header->off_dt_strings);
|
||||
uint32_t strings_size = be32toh(dt_header->size_dt_strings);
|
||||
uint32_t end;
|
||||
|
||||
if (PTR_TO_SIZE(dtb) > SIZE_MAX - dt_size)
|
||||
return NULL;
|
||||
|
||||
if (!ADD_SAFE(&end, strings_off, strings_size) || end > dt_size)
|
||||
return NULL;
|
||||
const char *strings_block = (const char *) ((const uint8_t *) dt_header + strings_off);
|
||||
|
||||
if (struct_off % sizeof(uint32_t) != 0)
|
||||
return NULL;
|
||||
if (struct_size % sizeof(uint32_t) != 0 ||
|
||||
!ADD_SAFE(&end, struct_off, struct_size) ||
|
||||
end > strings_off)
|
||||
return NULL;
|
||||
const uint32_t *cursor = (const uint32_t *) ((const uint8_t *) dt_header + struct_off);
|
||||
|
||||
size_t size_words = struct_size / sizeof(uint32_t);
|
||||
size_t len, name_off, len_words, s;
|
||||
|
||||
for (size_t i = 0; i < end; i++) {
|
||||
switch (be32toh(cursor[i])) {
|
||||
case FDT_BEGIN_NODE:
|
||||
if (i >= size_words || cursor[++i] != 0)
|
||||
return NULL;
|
||||
break;
|
||||
case FDT_NOP:
|
||||
break;
|
||||
case FDT_PROP:
|
||||
/* At least 3 words should present: len, name_off, c (nul-terminated string always has non-zero length) */
|
||||
if (i + 3 >= size_words || cursor[++i] != 0)
|
||||
return NULL;
|
||||
len = be32toh(cursor[++i]);
|
||||
name_off = be32toh(cursor[++i]);
|
||||
len_words = DIV_ROUND_UP(len, sizeof(uint32_t));
|
||||
|
||||
if (ADD_SAFE(&s, name_off, STRLEN("compatible")) &&
|
||||
s < strings_size && streq8(strings_block + name_off, "compatible")) {
|
||||
const char *c = (const char *) &cursor[++i];
|
||||
if (len == 0 || i + len_words > size_words || c[len - 1] != '\0')
|
||||
c = NULL;
|
||||
|
||||
return c;
|
||||
}
|
||||
i += len_words;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool firmware_devicetree_exists(void) {
|
||||
return !!find_configuration_table(MAKE_GUID_PTR(EFI_DTB_TABLE));
|
||||
}
|
||||
|
||||
/* This function checks if the firmware provided Devicetree
|
||||
* and a UKI provided Devicetree contain the same first entry
|
||||
* on their respective "compatible" fields (which usually defines
|
||||
* the actual device model). More specifically, given the FW/UKI
|
||||
* "compatible" property pair:
|
||||
*
|
||||
* compatible = "string1", "string2";
|
||||
* compatible = "string1", "string3";
|
||||
*
|
||||
* the function reports a match, while for
|
||||
*
|
||||
* compatible = "string1", "string3";
|
||||
* compatible = "string2", "string1";
|
||||
*
|
||||
* it reports a mismatch.
|
||||
*
|
||||
* Other entries might refer to SoC and therefore can't be used for matching
|
||||
*/
|
||||
EFI_STATUS devicetree_match(const void *uki_dtb, size_t uki_dtb_length) {
|
||||
const void *fw_dtb = find_configuration_table(MAKE_GUID_PTR(EFI_DTB_TABLE));
|
||||
if (!fw_dtb)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
const char *fw_compat = devicetree_get_compatible(fw_dtb);
|
||||
if (!fw_compat)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
return devicetree_match_by_compatible(uki_dtb, uki_dtb_length, fw_compat);
|
||||
}
|
||||
|
||||
EFI_STATUS devicetree_match_by_compatible(const void *uki_dtb, size_t uki_dtb_length, const char *compat) {
|
||||
if ((uintptr_t) uki_dtb % alignof(FdtHeader) != 0)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
const FdtHeader *dt_header = ASSERT_PTR(uki_dtb);
|
||||
|
||||
if (uki_dtb_length < sizeof(FdtHeader) ||
|
||||
uki_dtb_length < be32toh(dt_header->total_size))
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
if (!compat)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
const char *dt_compat = devicetree_get_compatible(uki_dtb);
|
||||
if (!dt_compat)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
/* Only matches the first compatible string from each DT */
|
||||
return streq8(dt_compat, compat) ? EFI_SUCCESS : EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
EFI_STATUS devicetree_install_from_memory(
|
||||
struct devicetree_state *state, const void *dtb_buffer, size_t dtb_length) {
|
||||
|
||||
|
@ -9,6 +9,30 @@ struct devicetree_state {
|
||||
void *orig;
|
||||
};
|
||||
|
||||
enum {
|
||||
FDT_BEGIN_NODE = 1,
|
||||
FDT_END_NODE = 2,
|
||||
FDT_PROP = 3,
|
||||
FDT_NOP = 4,
|
||||
FDT_END = 9,
|
||||
};
|
||||
|
||||
typedef struct FdtHeader {
|
||||
uint32_t magic;
|
||||
uint32_t total_size;
|
||||
uint32_t off_dt_struct;
|
||||
uint32_t off_dt_strings;
|
||||
uint32_t off_mem_rsv_map;
|
||||
uint32_t version;
|
||||
uint32_t last_comp_version;
|
||||
uint32_t boot_cpuid_phys;
|
||||
uint32_t size_dt_strings;
|
||||
uint32_t size_dt_struct;
|
||||
} FdtHeader;
|
||||
|
||||
bool firmware_devicetree_exists(void);
|
||||
EFI_STATUS devicetree_match(const void *uki_dtb, size_t uki_dtb_length);
|
||||
EFI_STATUS devicetree_match_by_compatible(const void *uki_dtb, size_t uki_dtb_length, const char *compat);
|
||||
EFI_STATUS devicetree_install(struct devicetree_state *state, EFI_FILE *root_dir, char16_t *name);
|
||||
EFI_STATUS devicetree_install_from_memory(
|
||||
struct devicetree_state *state, const void *dtb_buffer, size_t dtb_length);
|
||||
|
@ -254,6 +254,7 @@ endif
|
||||
############################################################
|
||||
|
||||
libefi_sources = files(
|
||||
'chid.c',
|
||||
'console.c',
|
||||
'device-path-util.c',
|
||||
'devicetree.c',
|
||||
|
@ -1,5 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "chid.h"
|
||||
#include "devicetree.h"
|
||||
#include "pe.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -162,11 +164,46 @@ static bool pe_section_name_equal(const char *a, const char *b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void pe_locate_sections(
|
||||
static bool pe_use_this_dtb(
|
||||
const void *dtb,
|
||||
size_t dtb_size,
|
||||
const void *base,
|
||||
const Device *device,
|
||||
size_t section_nb) {
|
||||
|
||||
assert(dtb);
|
||||
|
||||
EFI_STATUS err;
|
||||
|
||||
err = devicetree_match(dtb, dtb_size);
|
||||
if (err == EFI_SUCCESS)
|
||||
return true;
|
||||
if (err != EFI_UNSUPPORTED)
|
||||
return false;
|
||||
|
||||
/* There's nothing to match against if firmware does not provide DTB and there is no .hwids section */
|
||||
if (!device || !base)
|
||||
return false;
|
||||
|
||||
const char *compatible = device_get_compatible(base, device);
|
||||
if (!compatible)
|
||||
return false;
|
||||
|
||||
err = devicetree_match_by_compatible(dtb, dtb_size, compatible);
|
||||
if (err == EFI_SUCCESS)
|
||||
return true;
|
||||
if (err == EFI_INVALID_PARAMETER)
|
||||
log_error_status(err, "Found bad DT blob in PE section %zu", section_nb);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pe_locate_sections_internal(
|
||||
const PeSectionHeader section_table[],
|
||||
size_t n_section_table,
|
||||
const char *const section_names[],
|
||||
size_t validate_base,
|
||||
const void *device_table,
|
||||
const Device *device,
|
||||
PeSectionVector sections[]) {
|
||||
|
||||
assert(section_table || n_section_table == 0);
|
||||
@ -206,6 +243,20 @@ static void pe_locate_sections(
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Special handling for .dtbauto sections compared to plain .dtb */
|
||||
if (pe_section_name_equal(section_names[i], ".dtbauto")) {
|
||||
/* .dtbauto sections require validate_base for matching */
|
||||
if (!validate_base)
|
||||
break;
|
||||
if (!pe_use_this_dtb(
|
||||
(const uint8_t *) SIZE_TO_PTR(validate_base) + j->VirtualAddress,
|
||||
j->VirtualSize,
|
||||
device_table,
|
||||
device,
|
||||
i))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* At this time, the sizes and offsets have been validated. Store them away */
|
||||
sections[i] = (PeSectionVector) {
|
||||
.memory_size = j->VirtualSize,
|
||||
@ -224,6 +275,73 @@ static void pe_locate_sections(
|
||||
}
|
||||
}
|
||||
|
||||
static bool looking_for_dbauto(const char *const section_names[]) {
|
||||
assert(section_names);
|
||||
|
||||
for (size_t i = 0; section_names[i]; i++)
|
||||
if (pe_section_name_equal(section_names[i], ".dtbauto"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pe_locate_sections(
|
||||
const PeSectionHeader section_table[],
|
||||
size_t n_section_table,
|
||||
const char *const section_names[],
|
||||
size_t validate_base,
|
||||
PeSectionVector sections[]) {
|
||||
|
||||
if (!looking_for_dbauto(section_names))
|
||||
return pe_locate_sections_internal(
|
||||
section_table,
|
||||
n_section_table,
|
||||
section_names,
|
||||
validate_base,
|
||||
/* device_base */ NULL,
|
||||
/* device */ NULL,
|
||||
sections);
|
||||
|
||||
/* It doesn't make sense not to provide validate_base here */
|
||||
assert(validate_base != 0);
|
||||
|
||||
const void *hwids = NULL;
|
||||
const Device *device = NULL;
|
||||
|
||||
if (!firmware_devicetree_exists()) {
|
||||
/* Find HWIDs table and search for the current device */
|
||||
PeSectionVector hwids_section = {};
|
||||
|
||||
pe_locate_sections_internal(
|
||||
section_table,
|
||||
n_section_table,
|
||||
(const char *const[]) { ".hwids", NULL },
|
||||
validate_base,
|
||||
/* device_table */ NULL,
|
||||
/* device */ NULL,
|
||||
&hwids_section);
|
||||
|
||||
if (hwids_section.memory_offset != 0) {
|
||||
hwids = (const uint8_t *) SIZE_TO_PTR(validate_base) + hwids_section.memory_offset;
|
||||
|
||||
EFI_STATUS err = chid_match(hwids, hwids_section.memory_size, &device);
|
||||
if (err != EFI_SUCCESS) {
|
||||
log_error_status(err, "HWID matching failed, no DT blob will be selected: %m");
|
||||
hwids = NULL;
|
||||
}
|
||||
} else
|
||||
log_info("HWIDs section is missing, no DT blob will be selected");
|
||||
}
|
||||
|
||||
return pe_locate_sections_internal(
|
||||
section_table,
|
||||
n_section_table,
|
||||
section_names,
|
||||
validate_base,
|
||||
hwids,
|
||||
device,
|
||||
sections);
|
||||
}
|
||||
|
||||
static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const PeFileHeader *pe) {
|
||||
/* The kernel may provide alternative PE entry points for different PE architectures. This allows
|
||||
* booting a 64-bit kernel on 32-bit EFI that is otherwise running on a 64-bit CPU. The locations of any
|
||||
|
@ -614,12 +614,13 @@ static EFI_STATUS load_addons(
|
||||
if (err != EFI_SUCCESS ||
|
||||
(!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE) &&
|
||||
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB) &&
|
||||
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTBAUTO) &&
|
||||
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD) &&
|
||||
!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE))) {
|
||||
if (err == EFI_SUCCESS)
|
||||
err = EFI_NOT_FOUND;
|
||||
log_error_status(err,
|
||||
"Unable to locate embedded .cmdline/.dtb/.initrd/.ucode sections in %ls, ignoring: %m",
|
||||
"Unable to locate embedded .cmdline/.dtb/.dtbauto/.initrd/.ucode sections in %ls, ignoring: %m",
|
||||
items[i]);
|
||||
continue;
|
||||
}
|
||||
@ -647,7 +648,21 @@ static EFI_STATUS load_addons(
|
||||
*cmdline = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16);
|
||||
}
|
||||
|
||||
if (devicetree_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) {
|
||||
// FIXME: do we want to do something else here?
|
||||
// This should behave exactly as .dtb/.dtbauto in the main UKI
|
||||
if (devicetree_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTBAUTO)) {
|
||||
*devicetree_addons = xrealloc(*devicetree_addons,
|
||||
*n_devicetree_addons * sizeof(NamedAddon),
|
||||
(*n_devicetree_addons + 1) * sizeof(NamedAddon));
|
||||
|
||||
(*devicetree_addons)[(*n_devicetree_addons)++] = (NamedAddon) {
|
||||
.blob = {
|
||||
.iov_base = xmemdup((const uint8_t*) loaded_addon->ImageBase + sections[UNIFIED_SECTION_DTBAUTO].memory_offset, sections[UNIFIED_SECTION_DTBAUTO].memory_size),
|
||||
.iov_len = sections[UNIFIED_SECTION_DTBAUTO].memory_size,
|
||||
},
|
||||
.filename = xstrdup16(items[i]),
|
||||
};
|
||||
} else if (devicetree_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) {
|
||||
*devicetree_addons = xrealloc(*devicetree_addons,
|
||||
*n_devicetree_addons * sizeof(NamedAddon),
|
||||
(*n_devicetree_addons + 1) * sizeof(NamedAddon));
|
||||
@ -968,13 +983,20 @@ static void install_embedded_devicetree(
|
||||
assert(sections);
|
||||
assert(dt_state);
|
||||
|
||||
if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB))
|
||||
UnifiedSection section = _UNIFIED_SECTION_MAX;
|
||||
|
||||
/* Use automatically selected DT if available, otherwise go for "normal" one */
|
||||
if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTBAUTO))
|
||||
section = UNIFIED_SECTION_DTBAUTO;
|
||||
else if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB))
|
||||
section = UNIFIED_SECTION_DTB;
|
||||
else
|
||||
return;
|
||||
|
||||
err = devicetree_install_from_memory(
|
||||
dt_state,
|
||||
(const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_DTB].memory_offset,
|
||||
sections[UNIFIED_SECTION_DTB].memory_size);
|
||||
(const uint8_t*) loaded_image->ImageBase + sections[section].memory_offset,
|
||||
sections[section].memory_size);
|
||||
if (err != EFI_SUCCESS)
|
||||
log_error_status(err, "Error loading embedded devicetree, ignoring: %m");
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ static inline void* xmemdup(const void *p, size_t l) {
|
||||
}
|
||||
|
||||
#define xnew(type, n) ((type *) xmalloc_multiply((n), sizeof(type)))
|
||||
#define xnew0(type, n) ((type *) xcalloc_multiply((n), sizeof(type)))
|
||||
|
||||
bool free_and_xstrdup16(char16_t **p, const char16_t *s);
|
||||
|
||||
|
@ -103,6 +103,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --sbat=PATH Path to SBAT file %7$s .sbat\n"
|
||||
" --pcrpkey=PATH Path to public key for PCR signatures %7$s .pcrpkey\n"
|
||||
" --profile=PATH Path to profile file %7$s .profile\n"
|
||||
" --hwids=PATH Path to HWIDs file %7$s .hwids\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@ -146,8 +147,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_SBAT,
|
||||
_ARG_PCRSIG, /* the .pcrsig section is not input for signing, hence not actually an argument here */
|
||||
ARG_PCRPKEY,
|
||||
ARG_PROFILE,
|
||||
ARG_HWIDS,
|
||||
_ARG_SECTION_LAST,
|
||||
ARG_PROFILE = _ARG_SECTION_LAST,
|
||||
ARG_DTBAUTO = _ARG_SECTION_LAST,
|
||||
ARG_BANK,
|
||||
ARG_PRIVATE_KEY,
|
||||
ARG_PRIVATE_KEY_SOURCE,
|
||||
@ -170,10 +173,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "ucode", required_argument, NULL, ARG_UCODE },
|
||||
{ "splash", required_argument, NULL, ARG_SPLASH },
|
||||
{ "dtb", required_argument, NULL, ARG_DTB },
|
||||
{ "dtbauto", required_argument, NULL, ARG_DTBAUTO },
|
||||
{ "uname", required_argument, NULL, ARG_UNAME },
|
||||
{ "sbat", required_argument, NULL, ARG_SBAT },
|
||||
{ "pcrpkey", required_argument, NULL, ARG_PCRPKEY },
|
||||
{ "profile", required_argument, NULL, ARG_PROFILE },
|
||||
{ "hwids", required_argument, NULL, ARG_HWIDS },
|
||||
{ "current", no_argument, NULL, 'c' },
|
||||
{ "bank", required_argument, NULL, ARG_BANK },
|
||||
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
|
||||
|
120
src/fundamental/chid-fundamental.c
Normal file
120
src/fundamental/chid-fundamental.c
Normal file
@ -0,0 +1,120 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#if SD_BOOT
|
||||
# include "efi-string.h"
|
||||
# include "util.h"
|
||||
#else
|
||||
# include <byteswap.h>
|
||||
# include <string.h>
|
||||
# include <uchar.h>
|
||||
# include <utf8.h>
|
||||
#define strsize16(str) ((char16_strlen(str) + 1) * sizeof(char16_t))
|
||||
#endif
|
||||
|
||||
#include "chid-fundamental.h"
|
||||
#include "macro-fundamental.h"
|
||||
#include "memory-util-fundamental.h"
|
||||
#include "sha1-fundamental.h"
|
||||
|
||||
static void get_chid(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], uint32_t mask, EFI_GUID *ret_chid) {
|
||||
assert(mask != 0);
|
||||
assert(ret_chid);
|
||||
const EFI_GUID namespace = { UINT32_C(0x12d8ff70), UINT16_C(0x7f4c), UINT16_C(0x7d4c), {} }; /* Swapped to BE */
|
||||
|
||||
struct sha1_ctx ctx = {};
|
||||
sha1_init_ctx(&ctx);
|
||||
|
||||
sha1_process_bytes(&namespace, sizeof(namespace), &ctx);
|
||||
|
||||
for (unsigned i = 0; i < _CHID_SMBIOS_FIELDS_MAX; i++)
|
||||
if ((mask >> i) & 1) {
|
||||
if (i > 0)
|
||||
sha1_process_bytes(L"&", 2, &ctx);
|
||||
sha1_process_bytes(smbios_fields[i], strsize16(smbios_fields[i]), &ctx);
|
||||
}
|
||||
|
||||
uint8_t hash[SHA1_DIGEST_SIZE];
|
||||
sha1_finish_ctx(&ctx, hash);
|
||||
|
||||
assert_cc(sizeof(hash) >= sizeof(*ret_chid));
|
||||
memcpy(ret_chid, hash, sizeof(*ret_chid));
|
||||
|
||||
/* Convert the resulting CHID back to little-endian: */
|
||||
ret_chid->Data1 = bswap_32(ret_chid->Data1);
|
||||
ret_chid->Data2 = bswap_16(ret_chid->Data2);
|
||||
ret_chid->Data3 = bswap_16(ret_chid->Data3);
|
||||
|
||||
/* set specific bits according to RFC4122 Section 4.1.3 */
|
||||
ret_chid->Data3 = (ret_chid->Data3 & 0x0fff) | (5 << 12);
|
||||
ret_chid->Data4[0] = (ret_chid->Data4[0] & UINT8_C(0x3f)) | UINT8_C(0x80);
|
||||
}
|
||||
|
||||
static const uint32_t chid_smbios_table[CHID_TYPES_MAX] = {
|
||||
[3] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_SKU) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_PRODUCT),
|
||||
|
||||
[4] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_SKU),
|
||||
|
||||
[5] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME),
|
||||
|
||||
[6] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_SKU) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_PRODUCT),
|
||||
|
||||
[7] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_SKU),
|
||||
|
||||
[8] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_PRODUCT),
|
||||
|
||||
[9] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME),
|
||||
|
||||
[10] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_PRODUCT),
|
||||
|
||||
[11] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY),
|
||||
|
||||
[13] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_PRODUCT),
|
||||
};
|
||||
|
||||
void chid_calculate(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], EFI_GUID ret_chids[static CHID_TYPES_MAX]) {
|
||||
assert(smbios_fields);
|
||||
assert(ret_chids);
|
||||
for (size_t i = 0; i < _CHID_SMBIOS_FIELDS_MAX; i++)
|
||||
if (chid_smbios_table[i] != 0)
|
||||
get_chid(smbios_fields, chid_smbios_table[i], &ret_chids[i]);
|
||||
else
|
||||
memzero(&ret_chids[i], sizeof(EFI_GUID));
|
||||
}
|
21
src/fundamental/chid-fundamental.h
Normal file
21
src/fundamental/chid-fundamental.h
Normal file
@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "efi-fundamental.h"
|
||||
#include "string-util-fundamental.h"
|
||||
|
||||
#define CHID_TYPES_MAX 15
|
||||
|
||||
typedef enum ChidSmbiosFields {
|
||||
CHID_SMBIOS_MANUFACTURER,
|
||||
CHID_SMBIOS_FAMILY,
|
||||
CHID_SMBIOS_PRODUCT_NAME,
|
||||
CHID_SMBIOS_PRODUCT_SKU,
|
||||
CHID_SMBIOS_BASEBOARD_MANUFACTURER,
|
||||
CHID_SMBIOS_BASEBOARD_PRODUCT,
|
||||
_CHID_SMBIOS_FIELDS_MAX,
|
||||
} ChidSmbiosFields;
|
||||
|
||||
/* CHID (also called HWID by fwupd) is described at https://github.com/fwupd/fwupd/blob/main/docs/hwids.md */
|
||||
void chid_calculate(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], EFI_GUID ret_chids[static CHID_TYPES_MAX]);
|
@ -4,6 +4,7 @@ fundamental_include = include_directories('.')
|
||||
|
||||
fundamental_sources = files(
|
||||
'bootspec-fundamental.c',
|
||||
'chid-fundamental.c',
|
||||
'efivars-fundamental.c',
|
||||
'iovec-util-fundamental.h',
|
||||
'sha1-fundamental.c',
|
||||
|
@ -21,5 +21,7 @@ const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = {
|
||||
[UNIFIED_SECTION_PCRSIG] = ".pcrsig",
|
||||
[UNIFIED_SECTION_PCRPKEY] = ".pcrpkey",
|
||||
[UNIFIED_SECTION_PROFILE] = ".profile",
|
||||
[UNIFIED_SECTION_DTBAUTO] = ".dtbauto",
|
||||
[UNIFIED_SECTION_HWIDS] = ".hwids",
|
||||
NULL,
|
||||
};
|
||||
|
@ -18,6 +18,8 @@ typedef enum UnifiedSection {
|
||||
UNIFIED_SECTION_PCRSIG,
|
||||
UNIFIED_SECTION_PCRPKEY,
|
||||
UNIFIED_SECTION_PROFILE,
|
||||
UNIFIED_SECTION_DTBAUTO,
|
||||
UNIFIED_SECTION_HWIDS,
|
||||
_UNIFIED_SECTION_MAX,
|
||||
} UnifiedSection;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user