Skip to content

feat(hii): read the HII database from the EFI configuration table - #27

Merged
xaionaro merged 1 commit into
linuxboot:mainfrom
rybackhung:feat/hii-efi-config-table
Nov 10, 2026
Merged

xaionaro merged 1 commit into
linuxboot:mainfrom
rybackhung:feat/hii-efi-config-table

Conversation

@rybackhung

Copy link
Copy Markdown
Contributor

extract_db() read the HII database only from the HIID ACPI table or through the OCP HiiDB efivar. The standard way is to follow the EFI System Table: HiiDatabaseDxe installs the exported database in the configuration table under gEfiHiiDatabaseProtocolGuid (PcdHiiOsRuntimeSupport, TRUE by default in edk2).

Try that first. /sys/firmware/efi/config_table holds the address of the EFI_CONFIGURATION_TABLE array. Scan it for the gEfiHiiDatabaseProtocolGuid entry, at most 256 entries as the count is not exposed, and read the database it points to from /dev/mem. The table does not record the size, so walk the package lists by their PackageLength up to the zero padding that HiiDatabaseDxe leaves after them. When a step fails, print the reason to stderr and fall back to the HIID table and then the OCP variable as before.

Not every kernel creates config_table (it is absent on the aarch64 hosts tried). There the step fails before /dev/mem is opened and the next source is tried.

Document the order in the README.

Tested: cargo test --lib hii::extract; uefisettings hii get on an x86 OVMF guest read the database from the configuration table.

extract_db() read the HII database only from the HIID ACPI table or
through the OCP HiiDB efivar. The standard way is to follow the EFI
System Table: HiiDatabaseDxe installs the exported database in the
configuration table under gEfiHiiDatabaseProtocolGuid
(PcdHiiOsRuntimeSupport, TRUE by default in edk2).

Try that first. /sys/firmware/efi/config_table holds the address of
the EFI_CONFIGURATION_TABLE array. Scan it for the
gEfiHiiDatabaseProtocolGuid entry, at most 256 entries as the count
is not exposed, and read the database it points to from /dev/mem.
The table does not record the size, so walk the package lists by
their PackageLength up to the zero padding that HiiDatabaseDxe
leaves after them. When a step fails, print the reason to stderr and
fall back to the HIID table and then the OCP variable as before.

Not every kernel creates config_table (it is absent on the aarch64
hosts tried). There the step fails before /dev/mem is opened and the
next source is tried.

Document the order in the README.

Tested: cargo test --lib hii::extract; uefisettings hii get on an
x86 OVMF guest read the database from the configuration table.
@xaionaro
xaionaro merged commit 3af9ff3 into linuxboot:main Nov 10, 2026
2 of 3 checks passed
@xaionaro

Copy link
Copy Markdown
Member

Thank you!

xaionaro added a commit that referenced this pull request Nov 11, 2026
Firmware may export its HII database at ExitBootServices() into a
runtime buffer that it lists in the EFI Configuration Table under
EFI_HII_DATABASE_PROTOCOL_GUID (UEFI 2.11 sections 33.2.11.1, 4.6.5,
34.8.1). Upstream e719af3 ("feat(hii): read the HII database from the
EFI configuration table", PR #27) reads that buffer in extract_db: it
takes the entries' address from /sys/firmware/efi/config_table and
reads the entries and the buffer from /dev/mem. This change replaces
that reader with one built on the System Table reader:

- one reader of the entries, at the firmware's width (e719af3 read
  32-bit firmware's 20-byte entries 24 bytes apart): read_entries;
- RuntimeMemory bounds every read: System Table, entries, export;
- checked arithmetic on every firmware value.

The buffer holds what ExportPackageLists() writes (section 34.8.6):
package lists back to back, each from its EFI_HII_PACKAGE_LIST_HEADER,
whose PackageLength includes it (section 33.3.1.2). The configuration
table gives only a pointer (section 4.6.1), so the headers give the
extent: the export ends at a header of zeros, which edk2 leaves in the
zeroed buffer that it allocates a quarter larger (edk2-stable202608
MdeModulePkg/Universal/HiiDatabaseDxe/Database.c:3600-3621), or where
its runtime region leaves no room for another header. A PackageLength
shorter than its header, one past the region, a package list whose
packages, walked by their EFI_HII_PACKAGE_HEADER.Length, do not end
with an EFI_HII_PACKAGE_END package, a header alone, exactly at its
PackageLength (section 33.3.1.1), package lists past the 16 MiB cap of
HII database files, and an export without a package list are refused.
Whether the pointer is virtual is left open (sections 4.6.1, 33.2.11.1)
and edk2 converts no configuration table
(MdeModulePkg/Core/RuntimeDxe/Runtime.c:358), so it is taken as
physical first (PointerSpace::Physical).

The methods are named acpi, efivar and systab (HiiMethod). A caller
names one (HiiSource::Method) or takes the automatic order
(HiiSource::Auto, the default): acpi, then efivar, then systab.
Upstream 3af9ff3 tried the configuration table first; the automatic
order tries it last, as the vendor ACPI tables need no /dev/mem, and
the OCP variable keeps the buffer that those machines read before.
Each method tells its source absent apart from other errors
(HiiMethodError):

- absent: no vendor ACPI table file exists; the HiiDB variable does not
  exist (HiiDbVariableAbsent); Linux locates no System Table, as on a
  BIOS boot or an aarch64 boot without UEFI
  (SystemTableError::NotBootedByEfi, NoUefiInFdt, NoSystemTable); the
  System Table lists no export (HiiExportError::NotListed);
- refused: a MemoryAccessDenied (kernel lockdown, the kernel's checks
  of /dev/mem or /proc/kcore) or EACCES/EPERM in any layer of the
  error;
- failed: anything else, such as a source that fails its checks or a
  missing memory map.

The automatic order moves on past absent and failed methods and stops
at a refused one, so that no later method reads around what the kernel
or the administrator refused. NoHiiDatabase lists each method tried,
in order, with its error, and is the only report of a method that
gave no database: upstream's messages of the configuration table's
failure go. At debug level each method's error and the method that
gave the database are logged, and at warn level each method that
failed before the one that gave it, which the CLI prints with
RUST_LOG=warn. How the acpi and efivar methods read is unchanged.

identify looks for the database by the same methods, in the same order:
an ACPI table file or the HiiDB variable exists, or the System Table
lists the export. The systab method answers no where its source is
absent, or where the build reads no firmware memory map, and fails
where it is refused or fails. HiiBackend gains extract_db_from,
read_hiidb_from and set_from, which read through HiiSourceOptions (the
methods and the memory of the efivar method), and
identify_machine_using, which identify_machine calls with the
automatic order. SystemTableError gains RegionEndsBefore.

EFI_HII_DATABASE_PROTOCOL_GUID is the one constant of that GUID:
upstream's HII_DATABASE_GUID goes, as do its tests, whose cases the
tests of read_sysfs_hex, into_table, read_export and read_hii_export
cover. README.md says in one line that the automatic order reads the
configuration table last, and clippy.toml that DevMem is again the
only caller of read_physical_memory_at.

Test Plan:
  RUSTFLAGS='-D warnings' cargo test -j1 --locked --lib -- \
      extract:: system_table:: source::
    against stubs of the selector, the classification and the export
    reader, on an earlier tree, whose 53 tests of this filter lack 11
    of this commit's 64: 16 failed, 37 passed; all pass in the run
    below
  RUSTFLAGS='-D warnings' cargo test -j1 --locked --lib
    with NotBootedByEfi and NoSystemTable classed as failed:
    hii_db_published_once_any_file_exists,
    finds_its_source_absent_where_linux_locates_no_system_table and
    probe_answers_false_where_the_source_is_absent failed, 815 passed
  RUSTFLAGS='-D warnings' cargo test -j1 --locked --lib
    with an FDT without linux,uefi-system-table failing and NoUefiInFdt
    classed as failed:
    finds_its_source_absent_where_the_fdt_gives_no_uefi,
    probe_answers_false_where_the_source_is_absent,
    uefi_system_table_reads_its_property_of_chosen and
    arm64_refuses_what_locates_no_system_table failed, 814 passed
  RUSTFLAGS='-D warnings' cargo test -j1 --locked --lib
    without the walk of each list's packages to its End package: the 2
    new tests of read_export failed, 816 passed
  RUSTFLAGS='-D warnings' cargo test -j1 --locked --lib
    without the warning:
    auto_warns_of_each_failed_method_that_it_reads_past failed, 817
    passed
  RUSTFLAGS='-D warnings' cargo test -j1 --locked --lib --bins
    lib 818 passed, bin 50 passed, 0 failed
  25 mutants, one per check of the export reader, of the
  classification and of identify's answer (a guard disabled, the
  pointer space, the GUID or a class changed), 3 more of those checks
  (a header's room at the region's end left unread, the address taken
  as physical only, an export past the cap classed absent), and 8 of
  the End package walk, its class and the warning (an End package
  that ends before the PackageLength or is longer than its header, no
  End package, a package shorter than its header or past the
  PackageLength, a Type above EFI_HII_PACKAGE_END taken as END,
  absent methods warned of, an unterminated export classed absent),
  each followed by
  RUSTFLAGS='-D warnings' cargo test -j1 --locked --lib
    36 killed, each by a named failing test
  identify_machine() given the acpi method only, not the automatic
  order: no test fails; nothing in the tree calls identify_machine(),
  and a unit test of it would read this machine's /sys/firmware

Signed-off-by: Dmitrii Okunev <xaionaro@meta.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants