Skip to content

Commit 32fed3e

Browse files
committed
uefi: Add MultiConfigurationStringIter to handle UEFI <MultiConfigResp>
1 parent 3ca2dd1 commit 32fed3e

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()?;
@@ -246,7 +245,7 @@ impl FromStr for ConfigurationString {
246245
};
247246

248247
while let Some(next) = splitter.peek() {
249-
if next.0 == "OFFSET" {
248+
if next.0 == "OFFSET" || next.0 == "GUID" {
250249
break;
251250
}
252251
let _ = splitter.next(); // drop nvconfig entries for now
@@ -257,6 +256,10 @@ impl FromStr for ConfigurationString {
257256
width,
258257
value,
259258
});
259+
// Found start of a new `ConfigurationString`
260+
if let Some(("GUID", _)) = splitter.peek() {
261+
break;
262+
}
260263
}
261264

262265
Ok(Self {
@@ -268,12 +271,43 @@ impl FromStr for ConfigurationString {
268271
}
269272
}
270273

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

275-
use crate::proto::hii::config_str::ConfigurationString;
276-
277311
#[test]
278312
fn parse_single() {
279313
// exemplary (shortened / manually constructed) UEFI configuration string
@@ -290,4 +324,38 @@ mod tests {
290324
assert_eq!(parsed.elements[10].width, 1);
291325
assert_eq!(&parsed.elements[10].value, &[0x00]);
292326
}
327+
328+
#[test]
329+
fn parse_multiple() {
330+
// exemplary (shortened / manually constructed) UEFI configuration string
331+
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";
332+
let parsed: Vec<_> = MultiConfigurationStringIter::new(input)
333+
.collect::<Result<_, _>>()
334+
.unwrap();
335+
336+
assert_eq!(parsed.len(), 2);
337+
338+
assert_eq!(
339+
parsed[0].guid,
340+
guid!("4b47d616-a8d6-4552-9d44-ccad2e0f4cf9")
341+
);
342+
assert_eq!(parsed[0].name, "ISCSI_CONFIG_IFR_NVDATA");
343+
assert_eq!(parsed[0].elements.len(), 1);
344+
assert_eq!(parsed[0].elements[0].offset, 0x01d8);
345+
assert_eq!(parsed[0].elements[0].width, 1);
346+
assert_eq!(&parsed[0].elements[0].value, &[0x00]);
347+
348+
assert_eq!(
349+
parsed[1].guid,
350+
guid!("4b47d616-a8d6-4552-9d44-ccad2e0f4cf9")
351+
);
352+
assert_eq!(parsed[1].name, "ISCSI_CONFIG_IFR_NVDATA");
353+
assert_eq!(parsed[1].elements.len(), 2);
354+
assert_eq!(parsed[1].elements[1].offset, 0x1337);
355+
assert_eq!(parsed[1].elements[1].width, 5);
356+
assert_eq!(
357+
&parsed[1].elements[1].value,
358+
&[0x55, 0x44, 0x33, 0x22, 0x11]
359+
);
360+
}
293361
}

0 commit comments

Comments
 (0)