Skip to content

uefi-raw: Add HII IFR bindings - #2064

Open
crawfxrd wants to merge 3 commits into
rust-osdev:mainfrom
crawfxrd:ifr-bindings
Open

crawfxrd wants to merge 3 commits into
rust-osdev:mainfrom
crawfxrd:ifr-bindings

Conversation

@crawfxrd

@crawfxrd crawfxrd commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

I've had this change sitting for a while, so I'll just push it.
This is a follow up to my HII work in #1822.

The Internal Forms Representation is a binary encoding of HII objects, used to construct UI elements of UEFI modules.

Ref: UEFI 2.11: 33.3.8 Forms Package
Ref: edk2@edk2-stable202608

Checklist

  • Sensible git history (for example, squash "typo" or "fix" commits). See the Rewriting History guide for help.
  • Update the changelog (if necessary)

Comment thread uefi-raw/src/protocol/hii/ifr.rs Outdated
Comment thread uefi-raw/src/protocol/hii/mod.rs Outdated
Comment thread uefi-raw/src/protocol/hii/ifr.rs Outdated
Comment thread uefi-raw/src/protocol/hii/ifr.rs Outdated
Comment thread uefi-raw/src/protocol/hii/mod.rs
@crawfxrd
crawfxrd marked this pull request as ready for review September 10, 2026 21:24

@phip1611 phip1611 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for better reviewability, please split into multiple commits.

For example:

  • add new types
  • delete old types

@crawfxrd
crawfxrd force-pushed the ifr-bindings branch 2 times, most recently from 9d5490b to 4365f65 Compare September 14, 2026 20:15
@crawfxrd
crawfxrd requested a review from phip1611 September 14, 2026 20:29
@crawfxrd

Copy link
Copy Markdown
Contributor Author

The packing requirement of all the structs seems to be offhandedly declared by these statements in 33.3.8 Forms Package (33.3.8.1 Binary Encoding).

The IFR is a binary encoding for HII-related objects.
[...]
The binary objects are encoded as byte stream.

@phip1611 phip1611 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much! I checked (with the help of an LLM) the bindings against UefiInternalFormRepresentation.h at
edk2-stable202508.01: all 100 opcodes and all 111 struct layouts match, and
packed is right (edk2 wraps that header in #pragma pack(1)). No layout bugs.

But I have six remarks, please address them.

  1. Missing **Breaking** changelog entry. HiiDate/HiiTime/HiiRef move
    to hii, IfrTypeValue to hii::ifr. All four were pub in v0.16.0, so
    downstream use statements break.

  2. IfrCheckboxFlags should be bitflags! (ifr.rs:285). DEFAULT (0x01)
    and DEFAULT_MFG (0x02) combine; as a newtype_enum, IfrCheckboxFlags(3)
    equals neither, so flags == IfrCheckboxFlags::DEFAULT silently fails. Did I at first recommended to use newtype_enum here? I am not sure anymore, I lost context. Sorry.

  3. IfrDateFlags::STORAGE_NORMAL = empty() is a trap (ifr.rs:324).

    SORRY THIS IS COPIED FROM AN LLM. I am currently having lots on my desk, but I value your work! I hope this helps.

    bitflags implements contains(other) as (self.bits & other.bits) == other.bits. For a constant with no bits set that is 0 == 0, i.e. always
    true. So flags.contains(IfrDateFlags::STORAGE_NORMAL) returns true for
    a date stored in the wakeup-time register - the one check a caller would
    naturally write is the one that cannot fail.

    The underlying reason is that EFI_QF_DATE_STORAGE (0x30) is not two
    independent bits but a 2-bit field with three defined values: NORMAL (0x00),
    STORAGE_TIME (0x10), STORAGE_WAKEUP (0x20). Modelling it as bitflags also
    makes from_bits_retain(0x30) report contains(STORAGE_TIME) && contains(STORAGE_WAKEUP), which has no meaning in the spec.

    Minimal fix: drop the impl IfrDateFlags { pub const STORAGE_NORMAL } block.
    That alone removes the trap - callers then have to write the mask themselves,
    which at least fails loudly.

    Pragmatic fix: keep the suppress bits as flags, and expose the selector as
    its own type. Roughly:

    bitflags::bitflags! {
        pub struct IfrDateFlags: u8 {
            const YEAR_SUPPRESS = 1 << 0;
            const MONTH_SUPPRESS = 1 << 1;
            const DAY_SUPPRESS = 1 << 2;
            /// Mask of the storage selector, see [`IfrDateFlags::storage`].
            const STORAGE = 0x30;
        }
    }
    
    newtype_enum! {
        /// Storage selector of [`IfrDateFlags`] (`EFI_QF_DATE_STORAGE`).
        pub enum IfrDateStorage: u8 => {
            NORMAL = 0x00,
            TIME = 0x10,
            WAKEUP = 0x20,
        }
    }
    
    impl IfrDateFlags {
        /// Returns where the date is stored.
        #[must_use]
        pub const fn storage(self) -> IfrDateStorage {
            IfrDateStorage(self.bits() & Self::STORAGE.bits())
        }
    }

    IfrTime has the same shape (EFI_QF_TIME_STORAGE, also 0x30), so whatever
    you pick here should apply there too if flags: u8 ever gets a type.

  4. IfrDefault ignores the types this PR adds (ifr.rs:341). Suggestion:
    default_id: DefaultId, ifr_type: IfrType. ifr_type selects the active
    IfrTypeValue arm, so it is untyped exactly where it matters most. Same for
    IfrDefaultStore::default_id.

  5. IfrNumeric/IfrOneOf: flags is the IfrNumericData discriminant
    (ifr.rs:687, 711). flags & EFI_IFR_NUMERIC_SIZE (0x03) picks the arm, but
    the constant is not exported and nothing says so. Suggestion: export the mask
    (and EFI_IFR_DISPLAY) and document it on the field.

  6. IfrDate::quest (ifr.rs:332) - the only question-header field not named
    question, across 15 structs. Suggestion: pub question: IfrQuestionHeader,

Minor, feel free to skip: bitflags::bitflags! fully qualified vs the crate's
use bitflags::bitflags;; IfrFormMap/IfrFormSet flexible members commented
out while others use [T; 0]; HiiFormPackageHdr in mod.rs without an ABI
assert; HiiPackageListHeader missing the Clone, Copy its sibling got;
VarstoreInfo/IfrNumeric* undocumented; commit trailers say
edk2-stable202608, the PR description edk2-stable202508.01.

`HiiDate`, `HiiTime`, and `HiiRef` are not specific to the config protocol.
Move these definitions to the HII root.

Signed-off-by: Tim Crawford <tcrawford@system76.com>
Match the edk2 definitions and make these structs packed.

Ref: tianocore/edk2@edk2-stable202608
Signed-off-by: Tim Crawford <tcrawford@system76.com>
@crawfxrd

Copy link
Copy Markdown
Contributor Author
  1. Missing **Breaking** changelog entry. HiiDate/HiiTime/HiiRef move
    to hii, IfrTypeValue to hii::ifr. All four were pub in v0.16.0, so
    downstream use statements break.

Done.


  1. IfrCheckboxFlags should be bitflags! (ifr.rs:285). DEFAULT (0x01)
    and DEFAULT_MFG (0x02) combine; as a newtype_enum, IfrCheckboxFlags(3)
    equals neither, so flags == IfrCheckboxFlags::DEFAULT silently fails. Did I at first recommended to use newtype_enum here? I am not sure anymore, I lost context. Sorry.

Done.


  1. IfrDateFlags::STORAGE_NORMAL = empty() is a trap (ifr.rs:324).

TODO


  1. IfrDefault ignores the types this PR adds (ifr.rs:341). Suggestion:
    default_id: DefaultId, ifr_type: IfrType. ifr_type selects the active
    IfrTypeValue arm, so it is untyped exactly where it matters most. Same for
    IfrDefaultStore::default_id.

Done.

Also:

  • Added missing EFI_IFR_DEFAULT_2
  • Changed the field to r#type to match the spec names.

  1. IfrNumeric/IfrOneOf: flags is the IfrNumericData discriminant
    (ifr.rs:687, 711). flags & EFI_IFR_NUMERIC_SIZE (0x03) picks the arm, but
    the constant is not exported and nothing says so. Suggestion: export the mask
    (and EFI_IFR_DISPLAY) and document it on the field.

I've added constants for the mask and values to match UEFI.

Probably can make a struct IfrNumericFlags(pub u8) just to group them all?


  1. IfrDate::quest (ifr.rs:332) - the only question-header field not named
    question, across 15 structs. Suggestion: pub question: IfrQuestionHeader,

Done.


bitflags::bitflags! fully qualified vs the crate's use bitflags::bitflags;;

Done.


IfrFormMap/IfrFormSet flexible members commented out while others use [T; 0];

IfrFormMap and IfrFormSet have the members commented out in both the spec and in edk2, so I left them commented out.

There's also something like EFI_IFR_EQ_ID_VAL_LIST, where the spec has ValueList[1], but is also a VLA.

Then there's EFI_IFR_VARSTORE, where it's a string, "but not part of the struct" (it is in edk2).

Should they all be added as [T; 0]?


HiiFormPackageHdr in mod.rs without an ABI assert;

Done.


HiiPackageListHeader missing the Clone, Copy its sibling got;

Done.


VarstoreInfo/IfrNumeric* undocumented;

I've tried to add basic documentation for them.


commit trailers say edk2-stable202608, the PR description edk2-stable202508.01.

Updated PR message.

The Internal Forms Representation is a binary encoding of HII objects, used to
construct UI elements of UEFI modules.

Ref: UEFI 2.11: 33.3.8 Forms Package
Ref: tianocore/edk2@edk2-stable202608
Signed-off-by: Tim Crawford <tcrawford@system76.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