Skip to content

Commit d07f890

Browse files
committed
uefi: Add MultiConfigurationStringIter to handle UEFI <MultiConfigResp>
1 parent 7068ea8 commit d07f890

File tree

2 files changed

+78
-10
lines changed

2 files changed

+78
-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: 77 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
let input = "GUID=16d6474bd6a852459d44ccad2e0f4cf9&NAME=00490053004300530049005f0043004f004e004600490047005f004900460052005f004e00560044004100540041&PATH=0104140016d6474bd6a852459d44ccad2e0f4cf97fff0400&OFFSET=01d8&WIDTH=0002&VALUE=0011&OFFSET=01d9&WIDTH=0001&VALUE=00&OFFSET=01da&WIDTH=0001&VALUE=00&OFFSET=01dc&WIDTH=0002&VALUE=03e8&OFFSET=01de&WIDTH=0001&VALUE=00&OFFSET=01df&WIDTH=0001&VALUE=00&OFFSET=05fe&WIDTH=0002&VALUE=0000&OFFSET=062a&WIDTH=0001&VALUE=00&OFFSET=062b&WIDTH=0001&VALUE=01&OFFSET=0fd4&WIDTH=0001&VALUE=00&OFFSET=0fd5&WIDTH=0001&VALUE=00";
@@ -285,4 +319,37 @@ mod tests {
285319
assert_eq!(parsed.elements[10].width, 1);
286320
assert_eq!(&parsed.elements[10].value, &[0x00]);
287321
}
322+
323+
#[test]
324+
fn parse_multiple() {
325+
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";
326+
let parsed: Vec<_> = MultiConfigurationStringIter::new(input)
327+
.collect::<Result<_, _>>()
328+
.unwrap();
329+
330+
assert_eq!(parsed.len(), 2);
331+
332+
assert_eq!(
333+
parsed[0].guid,
334+
guid!("4b47d616-a8d6-4552-9d44-ccad2e0f4cf9")
335+
);
336+
assert_eq!(parsed[0].name, "ISCSI_CONFIG_IFR_NVDATA");
337+
assert_eq!(parsed[0].elements.len(), 1);
338+
assert_eq!(parsed[0].elements[0].offset, 0x01d8);
339+
assert_eq!(parsed[0].elements[0].width, 1);
340+
assert_eq!(&parsed[0].elements[0].value, &[0x00]);
341+
342+
assert_eq!(
343+
parsed[1].guid,
344+
guid!("4b47d616-a8d6-4552-9d44-ccad2e0f4cf9")
345+
);
346+
assert_eq!(parsed[1].name, "ISCSI_CONFIG_IFR_NVDATA");
347+
assert_eq!(parsed[1].elements.len(), 2);
348+
assert_eq!(parsed[1].elements[1].offset, 0x1337);
349+
assert_eq!(parsed[1].elements[1].width, 5);
350+
assert_eq!(
351+
&parsed[1].elements[1].value,
352+
&[0x55, 0x44, 0x33, 0x22, 0x11]
353+
);
354+
}
288355
}

0 commit comments

Comments
 (0)