Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 963217e

Browse files
Merge pull request #1684 from seijikun/mr-hiicfg
uefi: Add HiiKeywordHandler and HiiConfigAccess protocol
2 parents 8049e5f + d917e14 commit 963217e

File tree

5 files changed

+302
-0
lines changed

5 files changed

+302
-0
lines changed

‎uefi/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
- Added `ConfigTableEntry::MEMORY_ATTRIBUTES_GUID` and `ConfigTableEntry::IMAGE_SECURITY_DATABASE_GUID`.
55
- Added `proto::usb::io::UsbIo`.
66
- Added `proto::pci::PciRootBridgeIo`.
7+
- Added `proto::hii::config::ConfigKeywordHandler`.
8+
- Added `proto::hii::config::HiiConfigAccess`.
9+
- Added `proto::hii::config_str::ConfigurationString`.
710

811
## Changed
912
- **Breaking:** `boot::stall` now take `core::time::Duration` instead of `usize`.

‎uefi/src/proto/hii/config.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! HII Configuration protocols.
4+
5+
use uefi_macros::unsafe_protocol;
6+
use uefi_raw::protocol::hii::config::{ConfigKeywordHandlerProtocol, HiiConfigAccessProtocol};
7+
8+
/// The HII Keyword Handler Protocol.
9+
///
10+
/// # UEFI Spec Description
11+
///
12+
/// This protocol provides the mechanism to set and get the values associated
13+
/// with a keyword exposed through a x-UEFI- prefixed configuration language namespace.
14+
#[derive(Debug)]
15+
#[repr(transparent)]
16+
#[unsafe_protocol(ConfigKeywordHandlerProtocol::GUID)]
17+
pub struct ConfigKeywordHandler(ConfigKeywordHandlerProtocol);
18+
19+
/// The HII Configuration Access Protocol.
20+
///
21+
/// # UEFI Spec Description
22+
///
23+
/// This protocol is responsible for facilitating access to configuration data from HII.
24+
/// It is typically invoked by the HII Configuration Routing Protocol for handling
25+
/// configuration requests. Forms browsers also interact with this protocol through
26+
/// the `Callback()` function.
27+
#[derive(Debug)]
28+
#[repr(transparent)]
29+
#[unsafe_protocol(HiiConfigAccessProtocol::GUID)]
30+
pub struct HiiConfigAccess(HiiConfigAccessProtocol);

‎uefi/src/proto/hii/config_str.rs

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! UEFI Configuration String parsing according to Spec 35.2.1
4+
5+
use alloc::boxed::Box;
6+
use alloc::string::{String, ToString};
7+
use alloc::vec::Vec;
8+
use core::slice;
9+
use core::str::{self, FromStr};
10+
use uguid::Guid;
11+
12+
use crate::proto::device_path::DevicePath;
13+
use crate::{CStr16, Char16};
14+
15+
/// A helper struct to split and parse a UEFI Configuration String.
16+
///
17+
/// Configuration strings consist of key-value pairs separated by `&`. Keys
18+
/// and values are separated by `=`. This struct provides an iterator for
19+
/// easy traversal of the key-value pairs.
20+
///
21+
/// For reasons of developer sanity, this is operating on &str instead of &CStr16.
22+
#[derive(Debug)]
23+
pub struct ConfigurationStringIter<'a> {
24+
bfr: &'a str,
25+
}
26+
27+
impl<'a> ConfigurationStringIter<'a> {
28+
/// Creates a new splitter instance for a given configuration string buffer.
29+
#[must_use]
30+
pub const fn new(bfr: &'a str) -> Self {
31+
Self { bfr }
32+
}
33+
}
34+
35+
impl<'a> Iterator for ConfigurationStringIter<'a> {
36+
type Item = (&'a str, Option<&'a str>);
37+
38+
fn next(&mut self) -> Option<Self::Item> {
39+
if self.bfr.is_empty() {
40+
return None;
41+
}
42+
let (keyval, remainder) = self
43+
.bfr
44+
.split_once('&')
45+
.unwrap_or((self.bfr, &self.bfr[0..0]));
46+
self.bfr = remainder;
47+
let (key, value) = keyval
48+
.split_once('=')
49+
.map(|(key, value)| (key, Some(value)))
50+
.unwrap_or((keyval, None));
51+
Some((key, value))
52+
}
53+
}
54+
55+
/// Enum representing different sections of a UEFI Configuration Header.
56+
///
57+
/// These sections include GUID, Name, and Path elements, which provide
58+
/// routing and identification information for UEFI components.
59+
#[derive(Debug, PartialEq, Eq)]
60+
pub enum ConfigHdrSection {
61+
/// UEFI ConfigurationString {GuidHdr} element
62+
Guid,
63+
/// UEFI ConfigurationString {NameHdr} element
64+
Name,
65+
/// UEFI ConfigurationString {PathHdr} element
66+
Path,
67+
}
68+
69+
/// Enum representing possible parsing errors encountered when processing
70+
/// UEFI Configuration Strings.
71+
#[derive(Debug)]
72+
pub enum ParseError {
73+
/// Error while parsing the UEFI {ConfigHdr} configuration string section.
74+
ConfigHdr(ConfigHdrSection),
75+
/// Error while parsing the UEFI {BlockName} configuration string section.
76+
BlockName,
77+
/// Error while parsing the UEFI {BlockConfig} configuration string section.
78+
BlockConfig,
79+
}
80+
81+
/// Represents an individual element within a UEFI Configuration String.
82+
///
83+
/// Each element contains an offset, width, and value, defining the data
84+
/// stored at specific memory locations within the configuration.
85+
#[derive(Debug, Default)]
86+
pub struct ConfigurationStringElement {
87+
/// Byte offset in the configuration block
88+
pub offset: u64,
89+
/// Length of the value starting at offset
90+
pub width: u64,
91+
/// Value bytes
92+
pub value: Vec<u8>,
93+
// TODO
94+
// nvconfig: HashMap<String, Vec<u8>>,
95+
}
96+
97+
/// A full UEFI Configuration String representation.
98+
///
99+
/// This structure contains routing information such as GUID and device path,
100+
/// along with the parsed configuration elements.
101+
#[derive(Debug)]
102+
pub struct ConfigurationString {
103+
/// GUID used for identifying the configuration
104+
pub guid: Guid,
105+
/// Name field (optional identifier)
106+
pub name: String,
107+
/// Associated UEFI device path
108+
pub device_path: Box<DevicePath>,
109+
/// Parsed UEFI {ConfigElement} sections
110+
pub elements: Vec<ConfigurationStringElement>,
111+
}
112+
113+
impl ConfigurationString {
114+
fn try_parse_with<T, F: FnOnce() -> Option<T>>(
115+
err: ParseError,
116+
parse_fn: F,
117+
) -> Result<T, ParseError> {
118+
parse_fn().ok_or(err)
119+
}
120+
121+
/// Parses a hexadecimal string into an iterator of bytes.
122+
///
123+
/// # Arguments
124+
///
125+
/// * `hex` - The hexadecimal string representing binary data.
126+
///
127+
/// # Returns
128+
///
129+
/// An iterator over bytes.
130+
pub fn parse_bytes_from_hex(hex: &str) -> impl Iterator<Item = u8> {
131+
hex.as_bytes().chunks(2).map(|chunk| {
132+
let chunk = str::from_utf8(chunk).unwrap_or_default();
133+
u8::from_str_radix(chunk, 16).unwrap_or_default()
134+
})
135+
}
136+
137+
/// Converts a hexadecimal string representation into a numeric value.
138+
///
139+
/// # Arguments
140+
///
141+
/// * `data` - The hexadecimal string to convert.
142+
///
143+
/// # Returns
144+
///
145+
/// An `Option<u64>` representing the parsed number.
146+
#[must_use]
147+
pub fn parse_number_from_hex(data: &str) -> Option<u64> {
148+
let data: Vec<_> = Self::parse_bytes_from_hex(data).collect();
149+
match data.len() {
150+
8 => Some(u64::from_be_bytes(data.try_into().unwrap())),
151+
4 => Some(u32::from_be_bytes(data.try_into().unwrap()) as u64),
152+
2 => Some(u16::from_be_bytes(data.try_into().unwrap()) as u64),
153+
1 => Some(u8::from_be_bytes(data.try_into().unwrap()) as u64),
154+
_ => None,
155+
}
156+
}
157+
158+
/// Converts a hexadecimal string into a UTF-16 string.
159+
///
160+
/// # Arguments
161+
///
162+
/// * `data` - The hexadecimal representation of a string.
163+
///
164+
/// # Returns
165+
///
166+
/// An `Option<String>` containing the parsed string.
167+
#[must_use]
168+
pub fn parse_string_from_hex(data: &str) -> Option<String> {
169+
if data.len() % 2 != 0 {
170+
return None;
171+
}
172+
let mut data: Vec<_> = Self::parse_bytes_from_hex(data).collect();
173+
data.chunks_exact_mut(2).for_each(|c| c.swap(0, 1));
174+
data.extend_from_slice(&[0, 0]);
175+
let data: &[Char16] =
176+
unsafe { slice::from_raw_parts(data.as_slice().as_ptr().cast(), data.len() / 2) };
177+
Some(CStr16::from_char16_with_nul(data).ok()?.to_string())
178+
}
179+
180+
/// Parses a hexadecimal string into a UEFI GUID.
181+
///
182+
/// # Arguments
183+
///
184+
/// * `data` - The hexadecimal GUID representation.
185+
///
186+
/// # Returns
187+
///
188+
/// An `Option<Guid>` containing the parsed GUID.
189+
#[must_use]
190+
pub fn parse_guid_from_hex(data: &str) -> Option<Guid> {
191+
let v: Vec<_> = Self::parse_bytes_from_hex(data).collect();
192+
Some(Guid::from_bytes(v.try_into().ok()?))
193+
}
194+
}
195+
196+
impl FromStr for ConfigurationString {
197+
type Err = ParseError;
198+
199+
fn from_str(bfr: &str) -> Result<Self, Self::Err> {
200+
let mut splitter = ConfigurationStringIter::new(bfr).peekable();
201+
202+
let guid = Self::try_parse_with(ParseError::ConfigHdr(ConfigHdrSection::Guid), || {
203+
let v = splitter.next()?;
204+
let v = (v.0 == "GUID").then_some(v.1).flatten()?;
205+
Self::parse_guid_from_hex(v)
206+
})?;
207+
let name = Self::try_parse_with(ParseError::ConfigHdr(ConfigHdrSection::Name), || {
208+
let v = splitter.next()?;
209+
let v = (v.0 == "NAME").then_some(v.1).flatten()?;
210+
Self::parse_string_from_hex(v)
211+
})?;
212+
let device_path =
213+
Self::try_parse_with(ParseError::ConfigHdr(ConfigHdrSection::Path), || {
214+
let v = splitter.next()?.1?;
215+
let v: Vec<_> = Self::parse_bytes_from_hex(v).collect();
216+
let v = <&DevicePath>::try_from(v.as_slice()).ok()?;
217+
Some(v.to_boxed())
218+
})?;
219+
220+
let mut elements = Vec::new();
221+
loop {
222+
let offset = match splitter.next() {
223+
Some(("OFFSET", Some(data))) => {
224+
Self::parse_number_from_hex(data).ok_or(ParseError::BlockName)?
225+
}
226+
None => break,
227+
_ => return Err(ParseError::BlockName),
228+
};
229+
let width = match splitter.next() {
230+
Some(("WIDTH", Some(data))) => {
231+
Self::parse_number_from_hex(data).ok_or(ParseError::BlockName)?
232+
}
233+
_ => return Err(ParseError::BlockName),
234+
};
235+
let value = match splitter.next() {
236+
Some(("VALUE", Some(data))) => Self::parse_bytes_from_hex(data).collect(),
237+
_ => return Err(ParseError::BlockConfig),
238+
};
239+
240+
while let Some(next) = splitter.peek() {
241+
if next.0 == "OFFSET" {
242+
break;
243+
}
244+
let _ = splitter.next(); // drop nvconfig entries for now
245+
}
246+
247+
elements.push(ConfigurationStringElement {
248+
offset,
249+
width,
250+
value,
251+
});
252+
}
253+
254+
Ok(Self {
255+
guid,
256+
name,
257+
device_path,
258+
elements,
259+
})
260+
}
261+
}

‎uefi/src/proto/hii/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! HII Protocols
4+
5+
pub mod config;
6+
#[cfg(feature = "alloc")]
7+
pub mod config_str;

‎uefi/src/proto/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub mod console;
3737
pub mod debug;
3838
pub mod device_path;
3939
pub mod driver;
40+
pub mod hii;
4041
pub mod loaded_image;
4142
pub mod media;
4243
pub mod misc;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /