Skip to content

Commit e331f06

Browse files
committed
uefi: Add MultiConfigurationStringIter to handle UEFI <MultiConfigResp>
1 parent b4cc1b1 commit e331f06

File tree

2 files changed

+79
-10
lines changed

2 files changed

+79
-10
lines changed

uefi/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Added `proto::hii::config_str::ConfigurationString`.
1010
- Added `proto::acpi::AcpiTable`.
1111
- Added `proto::hii::database::HiiDatabase`.
12+
- Added `proto::hii::config_str::MultiConfigurationStringIter`.
1213

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

uefi/src/proto/hii/config_str.rs

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use alloc::boxed::Box;
66
use alloc::string::{String, ToString};
77
use alloc::vec::Vec;
8+
use core::iter::Peekable;
89
use core::slice;
910
use core::str::{self, FromStr};
1011
use uguid::Guid;
@@ -128,6 +129,7 @@ impl ConfigurationString {
128129
/// # Returns
129130
///
130131
/// An iterator over bytes.
132+
#[must_use]
131133
pub fn parse_bytes_from_hex(hex: &str) -> impl DoubleEndedIterator<Item = u8> {
132134
hex.as_bytes().chunks(2).map(|chunk| {
133135
let chunk = str::from_utf8(chunk).unwrap_or_default();
@@ -196,14 +198,11 @@ impl ConfigurationString {
196198
let v: Vec<_> = Self::parse_bytes_from_hex(data).collect();
197199
Some(Guid::from_bytes(v.try_into().ok()?))
198200
}
199-
}
200-
201-
impl FromStr for ConfigurationString {
202-
type Err = ParseError;
203-
204-
fn from_str(bfr: &str) -> Result<Self, Self::Err> {
205-
let mut splitter = ConfigurationStringIter::new(bfr).peekable();
206201

202+
/// Parse an instance of `Peekable<ConfigurationStringIter>` from the given kv-pair iterator.
203+
fn parse_from(
204+
splitter: &mut Peekable<ConfigurationStringIter<'_>>,
205+
) -> Result<Self, ParseError> {
207206
let guid = Self::try_parse_with(ParseError::ConfigHdr(ConfigHdrSection::Guid), || {
208207
let v = splitter.next()?;
209208
let v = (v.0 == "GUID").then_some(v.1).flatten()?;
@@ -243,7 +242,7 @@ impl FromStr for ConfigurationString {
243242
};
244243

245244
while let Some(next) = splitter.peek() {
246-
if next.0 == "OFFSET" {
245+
if next.0 == "OFFSET" || next.0 == "GUID" {
247246
break;
248247
}
249248
let _ = splitter.next(); // drop nvconfig entries for now
@@ -254,6 +253,10 @@ impl FromStr for ConfigurationString {
254253
width,
255254
value,
256255
});
256+
// Found start of a new `ConfigurationString`
257+
if let Some(("GUID", _)) = splitter.peek() {
258+
break;
259+
}
257260
}
258261

259262
Ok(Self {
@@ -265,12 +268,43 @@ impl FromStr for ConfigurationString {
265268
}
266269
}
267270

271+
impl FromStr for ConfigurationString {
272+
type Err = ParseError;
273+
274+
fn from_str(bfr: &str) -> Result<Self, Self::Err> {
275+
Self::parse_from(&mut ConfigurationStringIter::new(bfr).peekable())
276+
}
277+
}
278+
279+
/// Iterator over `ConfigurationString`'s in a multi configuration string buffer.
280+
#[derive(Debug)]
281+
pub struct MultiConfigurationStringIter<'a> {
282+
splitter: Peekable<ConfigurationStringIter<'a>>,
283+
}
284+
impl<'a> MultiConfigurationStringIter<'a> {
285+
/// Creates a new iterator instance for a given configuration string buffer.
286+
#[must_use]
287+
pub fn new(bfr: &'a str) -> Self {
288+
let splitter = ConfigurationStringIter::new(bfr).peekable();
289+
Self { splitter }
290+
}
291+
}
292+
impl<'a> Iterator for MultiConfigurationStringIter<'a> {
293+
type Item = Result<ConfigurationString, ParseError>;
294+
295+
fn next(&mut self) -> Option<Self::Item> {
296+
self.splitter.peek()?; // end of iterator?
297+
// try parsing the next full `ConfigurationString` from the splitter
298+
Some(ConfigurationString::parse_from(&mut self.splitter))
299+
}
300+
}
301+
268302
#[cfg(test)]
269303
mod tests {
304+
use crate::proto::hii::config_str::{ConfigurationString, MultiConfigurationStringIter};
305+
use alloc::vec::Vec;
270306
use core::str::FromStr;
271307

272-
use crate::proto::hii::config_str::ConfigurationString;
273-
274308
#[test]
275309
fn parse_single() {
276310
// exemplary (shortened / manually constructed) UEFI configuration string
@@ -287,4 +321,38 @@ mod tests {
287321
assert_eq!(parsed.elements[10].width, 1);
288322
assert_eq!(&parsed.elements[10].value, &[0x00]);
289323
}
324+
325+
#[test]
326+
fn parse_multiple() {
327+
// exemplary (shortened / manually constructed) UEFI configuration string
328+
let input = "GUID=16d6474bd6a852459d44ccad2e0f4cf9&NAME=00490053004300530049005f0043004f004e004600490047005f004900460052005f004e00560044004100540041&PATH=0104140016d6474bd6a852459d44ccad2e0f4cf97fff0400&OFFSET=01d8&WIDTH=0001&VALUE=00&GUID=16d6474bd6a852459d44ccad2e0f4cf9&NAME=00490053004300530049005f0043004f004e004600490047005f004900460052005f004e00560044004100540041&PATH=0104140016d6474bd6a852459d44ccad2e0f4cf97fff0400&OFFSET=01d8&WIDTH=0001&VALUE=00&OFFSET=1337&WIDTH=0005&VALUE=1122334455";
329+
let parsed: Vec<_> = MultiConfigurationStringIter::new(input)
330+
.collect::<Result<_, _>>()
331+
.unwrap();
332+
333+
assert_eq!(parsed.len(), 2);
334+
335+
assert_eq!(
336+
parsed[0].guid,
337+
guid!("4b47d616-a8d6-4552-9d44-ccad2e0f4cf9")
338+
);
339+
assert_eq!(parsed[0].name, "ISCSI_CONFIG_IFR_NVDATA");
340+
assert_eq!(parsed[0].elements.len(), 1);
341+
assert_eq!(parsed[0].elements[0].offset, 0x01d8);
342+
assert_eq!(parsed[0].elements[0].width, 1);
343+
assert_eq!(&parsed[0].elements[0].value, &[0x00]);
344+
345+
assert_eq!(
346+
parsed[1].guid,
347+
guid!("4b47d616-a8d6-4552-9d44-ccad2e0f4cf9")
348+
);
349+
assert_eq!(parsed[1].name, "ISCSI_CONFIG_IFR_NVDATA");
350+
assert_eq!(parsed[1].elements.len(), 2);
351+
assert_eq!(parsed[1].elements[1].offset, 0x1337);
352+
assert_eq!(parsed[1].elements[1].width, 5);
353+
assert_eq!(
354+
&parsed[1].elements[1].value,
355+
&[0x55, 0x44, 0x33, 0x22, 0x11]
356+
);
357+
}
290358
}

0 commit comments

Comments
 (0)