Skip to content

Construct PspSoftFuseChain bitfield from serde input and/or serialize to serde. #239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 118 additions & 2 deletions ahib-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use amd_efs::flash::Location;
use amd_efs::{
AddressMode, ComboDirectoryEntryFilter, EfhBulldozerSpiMode,
EfhEspiConfiguration, EfhNaplesSpiMode, EfhRomeSpiMode,
ProcessorGeneration,
ProcessorGeneration, PspSoftFuseChain,
};
use amd_efs::{
BhdDirectoryEntry, BhdDirectoryEntryRegionType, BhdDirectoryEntryType,
Expand All @@ -30,6 +30,8 @@ pub enum Error {
Io(std::io::Error),
#[error("image too big")]
ImageTooBig,
#[error("psp entry source {0} unknown")]
PspEntrySourceUnknown(PspDirectoryEntryType),
}

impl From<amd_efs::Error> for Error {
Expand Down Expand Up @@ -106,11 +108,125 @@ impl TryFromSerdeDirectoryEntryWithContext<SerdePspDirectoryEntry>
}
}

#[derive(Clone, serde::Serialize, schemars::JsonSchema)]
#[serde(rename = "SerdePspEntrySourceValue")]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub enum SerdePspEntrySourceValue {
PspSoftFuseChain(PspSoftFuseChain),
#[serde(deserialize_with = "deserialize_raw")]
Unknown(u64),
}

impl<'de> serde::de::Deserialize<'de> for SerdePspEntrySourceValue {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct PspVisitor;

impl<'de> serde::de::Visitor<'de> for PspVisitor {
type Value = SerdePspEntrySourceValue;

fn expecting(
&self,
formatter: &mut std::fmt::Formatter,
) -> std::fmt::Result {
formatter
.write_str("a u64 or a SerdePspEntrySourceValue variant")
}

fn visit_u64<E>(
self,
value: u64,
) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(SerdePspEntrySourceValue::Unknown(value))
}

fn visit_i64<E>(
self,
value: i64,
) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
if value >= 0 {
Ok(SerdePspEntrySourceValue::Unknown(value as u64))
} else {
Err(E::invalid_value(
serde::de::Unexpected::Signed(value),
&"a positive integer or SerdePspEntrySourceValue variant",
))
}
}

fn visit_map<A>(
self,
mut map: A,
) -> std::result::Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
if let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"PspSoftFuseChain" => {
Ok(SerdePspEntrySourceValue::PspSoftFuseChain(
map.next_value::<PspSoftFuseChain>()?,
))
}
_ => Err(serde::de::Error::custom(
"expected SerdePspEntrySourceValue variant",
)),
}
} else {
Err(serde::de::Error::custom(
"expected SerdePspEntrySourceValue variant",
))
}
}
}

deserializer.deserialize_any(PspVisitor)
}
}

impl SerdePspEntrySourceValue {
pub fn from_u64(value: u64, typ: PspDirectoryEntryType) -> Self {
match typ {
PspDirectoryEntryType::PspSoftFuseChain => {
Self::PspSoftFuseChain(PspSoftFuseChain::from(value))
}
_ => SerdePspEntrySourceValue::Unknown(value),
}
}

pub fn to_u64(
&self,
typ_or_err: std::result::Result<PspDirectoryEntryType, amd_efs::Error>,
) -> Result<u64> {
if let SerdePspEntrySourceValue::Unknown(x) = self {
Ok(*x)
} else {
let typ = typ_or_err.unwrap();
match typ {
PspDirectoryEntryType::PspSoftFuseChain => match self {
Self::PspSoftFuseChain(x) => Ok(u64::from(*x)),
_ => Err(Error::PspEntrySourceUnknown(typ)),
},
_ => Err(Error::PspEntrySourceUnknown(typ)),
}
}
}
}

#[derive(Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
#[serde(rename = "PspEntrySource")]
#[serde(deny_unknown_fields)]
pub enum SerdePspEntrySource {
Value(u64),
Value(SerdePspEntrySourceValue),
BlobFile(PathBuf),
SecondLevelDirectory(SerdePspDirectory),
}
Expand Down
54 changes: 51 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
ProcessorGeneration, PspDirectory, PspDirectoryEntry,
PspDirectoryEntryType, PspDirectoryHeader, ValueOrLocation,
};
use amd_host_image_builder_config::SerdePspEntrySourceValue;
use amd_host_image_builder_config::{
Error, Result, SerdeBhdDirectory, SerdeBhdDirectoryEntry,
SerdeBhdDirectoryEntryAttrs, SerdeBhdDirectoryEntryBlob,
Expand Down Expand Up @@ -56,6 +57,53 @@
assert_eq!(result.address_mode(), AddressMode::PhysicalAddress);
}

#[test]
fn test_valid_compat_serde_psp_entry_source_value_deserialization() {
let json = "16"; // force_security_policy_loading_even_if_insecure
let result =
serde_json::from_str::<SerdePspEntrySourceValue>(json).unwrap();
if let SerdePspEntrySourceValue::Unknown(x) = result {
assert_eq!(x, 16);
} else {
panic!("got the wrong SerdePspEntrySourceValue variant")
}
}

#[test]
fn test_valid_serde_psp_entry_source_value_deserialization() {
use amd_efs::PspSoftFuseChain32MiBSpiDecoding;
use amd_efs::PspSoftFuseChainPostCodeDecoding;
let json = r#"{"PspSoftFuseChain": {"early_secure_debug_unlock": true, "spi_decoding": "UpperHalf", "postcode_decoding": "Lpc"}}"#;
let result =
serde_json::from_str::<SerdePspEntrySourceValue>(json).unwrap();
if let SerdePspEntrySourceValue::PspSoftFuseChain(x) = result {
assert_eq!(
x.spi_decoding(),
PspSoftFuseChain32MiBSpiDecoding::UpperHalf
);
assert_eq!(
x.postcode_decoding(),
PspSoftFuseChainPostCodeDecoding::Lpc
);
assert!(x.early_secure_debug_unlock());
assert!(!x.force_recovery_booting());
} else {
panic!("got the wrong SerdePspEntrySourceValue variant")
}
}

#[test]
fn test_invalid_string_deserialization() {
let json = r#""x""#;
assert!(serde_json::from_str::<SerdePspEntrySourceValue>(json).is_err());
}

#[test]
fn test_invalid_wrong_key_name() {
let json = r#"{"WrongName": {"some": "data"}}"#;
assert!(serde_json::from_str::<SerdePspEntrySourceValue>(json).is_err());
}

mod hole;
use hole::Hole;

Expand Down Expand Up @@ -365,10 +413,10 @@
) -> std::io::Result<(PspDirectory, ErasableRange, Option<ErasableLocation>)> {
let filename = output_filename;
let efs_to_io_error = |e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("EFS error: {e:?} in file {filename:?}"),
)

Check warning on line 419 in src/main.rs

View workflow job for this annotation

GitHub Actions / clippy

this can be `std::io::Error::other(_)`

warning: this can be `std::io::Error::other(_)` --> src/main.rs:416:9 | 416 | / std::io::Error::new( 417 | | std::io::ErrorKind::Other, 418 | | format!("EFS error: {e:?} in file {filename:?}"), 419 | | ) | |_________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error help: use `std::io::Error::other` | 416 ~ std::io::Error::other( 417 ~ format!("EFS error: {e:?} in file {filename:?}"), |
};

let mut first_payload_range_beginning: Option<ErasableLocation> = None;
Expand Down Expand Up @@ -475,10 +523,10 @@
) -> std::io::Result<(BhdDirectory, ErasableRange, Option<ErasableLocation>)> {
let filename = output_filename;
let efs_to_io_error = |e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("EFS error: {e:?} in file {filename:?}"),
)

Check warning on line 529 in src/main.rs

View workflow job for this annotation

GitHub Actions / clippy

this can be `std::io::Error::other(_)`

warning: this can be `std::io::Error::other(_)` --> src/main.rs:526:9 | 526 | / std::io::Error::new( 527 | | std::io::ErrorKind::Other, 528 | | format!("EFS error: {e:?} in file {filename:?}"), 529 | | ) | |_________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error help: use `std::io::Error::other` | 526 ~ std::io::Error::other( 527 ~ format!("EFS error: {e:?} in file {filename:?}"), |
};

let mut first_payload_range_beginning: Option<ErasableLocation> = None;
Expand Down Expand Up @@ -622,7 +670,7 @@
// TODO: Handle the other variant (PspComboDirectory)
let mut blob_dump_filenames = HashSet::<PathBuf>::new();
SerdePspDirectoryVariant::PspDirectory(SerdePspDirectory {
entries: psp_directory.entries().map_while(|e| {
entries: psp_directory.entries().map_while(|e| -> Option<SerdePspEntry> {
if let Ok(typ) = e.typ_or_err() {
match typ {
PspDirectoryEntryType::SecondLevelDirectory => {
Expand Down Expand Up @@ -684,7 +732,7 @@
}
None => {
let value = e.value().unwrap();
SerdePspEntrySource::Value(value)
SerdePspEntrySource::Value(SerdePspEntrySourceValue::from_u64(value, typ))
}
},
target: SerdePspDirectoryEntry {
Expand Down Expand Up @@ -1096,7 +1144,7 @@
SerdePspEntrySource::Value(x) => {
// FIXME: assert!(blob_slot_settings.is_none()); fails for some reason
// DirectoryRelativeOffset is the one that can always be overridden
raw_entry.set_source(AddressMode::DirectoryRelativeOffset, ValueOrLocation::Value(x)).unwrap();
raw_entry.set_source(AddressMode::DirectoryRelativeOffset, ValueOrLocation::Value(x.to_u64(raw_entry.typ_or_err()).unwrap())).unwrap();
vec![(raw_entry, None, None)]
}
SerdePspEntrySource::BlobFile(
Expand Down Expand Up @@ -1210,13 +1258,13 @@
) -> BhdDirectoryContents<'a> {
let mut custom_bios_reset_entry: bool = false;
let apcb_to_io_error = |e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"APCB error: {e:?} in file {:?}",
efs_configuration_filename
),
)

Check warning on line 1267 in src/main.rs

View workflow job for this annotation

GitHub Actions / clippy

this can be `std::io::Error::other(_)`

warning: this can be `std::io::Error::other(_)` --> src/main.rs:1261:9 | 1261 | / std::io::Error::new( 1262 | | std::io::ErrorKind::Other, 1263 | | format!( 1264 | | "APCB error: {e:?} in file {:?}", 1265 | | efs_configuration_filename 1266 | | ), 1267 | | ) | |_________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error help: use `std::io::Error::other` | 1261 ~ std::io::Error::other( 1262 ~ format!( |
};
let bhd_directory_address_mode = AddressMode::EfsRelativeOffset;
let mut custom_apob = Option::<u64>::None;
Expand Down Expand Up @@ -1321,42 +1369,42 @@
) -> std::io::Result<()> {
let filename = output_filename;
let flash_to_io_error = |e: amd_efs::flash::Error| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("Flash error: {e:?} in file {filename:?}"),
)

Check warning on line 1375 in src/main.rs

View workflow job for this annotation

GitHub Actions / clippy

this can be `std::io::Error::other(_)`

warning: this can be `std::io::Error::other(_)` --> src/main.rs:1372:9 | 1372 | / std::io::Error::new( 1373 | | std::io::ErrorKind::Other, 1374 | | format!("Flash error: {e:?} in file {filename:?}"), 1375 | | ) | |_________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error help: use `std::io::Error::other` | 1372 ~ std::io::Error::other( 1373 ~ format!("Flash error: {e:?} in file {filename:?}"), |
};
let efs_to_io_error = |e: amd_efs::Error| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Config error: {e:?} in file {:?}",
efs_configuration_filename
),
)

Check warning on line 1384 in src/main.rs

View workflow job for this annotation

GitHub Actions / clippy

this can be `std::io::Error::other(_)`

warning: this can be `std::io::Error::other(_)` --> src/main.rs:1378:9 | 1378 | / std::io::Error::new( 1379 | | std::io::ErrorKind::Other, 1380 | | format!( 1381 | | "Config error: {e:?} in file {:?}", 1382 | | efs_configuration_filename 1383 | | ), 1384 | | ) | |_________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error help: use `std::io::Error::other` | 1378 ~ std::io::Error::other( 1379 ~ format!( |
};
let json5_to_io_error = |e: json5::Error| match e {
json5::Error::Message { ref msg, ref location } => std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"JSON5 error: {msg} in file {:?} at {}",
efs_configuration_filename,
match location {
None => "unknown location".to_owned(),
Some(x) => format!("{x:?}"),
}
),
),

Check warning on line 1397 in src/main.rs

View workflow job for this annotation

GitHub Actions / clippy

this can be `std::io::Error::other(_)`

warning: this can be `std::io::Error::other(_)` --> src/main.rs:1387:60 | 1387 | json5::Error::Message { ref msg, ref location } => std::io::Error::new( | ____________________________________________________________^ 1388 | | std::io::ErrorKind::Other, 1389 | | format!( 1390 | | "JSON5 error: {msg} in file {:?} at {}", ... | 1396 | | ), 1397 | | ), | |_________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error help: use `std::io::Error::other` | 1387 ~ json5::Error::Message { ref msg, ref location } => std::io::Error::other( 1388 ~ format!( |
};
let amd_host_image_builder_config_error_to_io_error =
|e: amd_host_image_builder_config::Error| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Config error: {e:?} in file {:?}",
reset_image_filename
),
)

Check warning on line 1407 in src/main.rs

View workflow job for this annotation

GitHub Actions / clippy

this can be `std::io::Error::other(_)`

warning: this can be `std::io::Error::other(_)` --> src/main.rs:1401:13 | 1401 | / std::io::Error::new( 1402 | | std::io::ErrorKind::Other, 1403 | | format!( 1404 | | "Config error: {e:?} in file {:?}", 1405 | | reset_image_filename 1406 | | ), 1407 | | ) | |_____________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error help: use `std::io::Error::other` | 1401 ~ std::io::Error::other( 1402 ~ format!( |
};
let blobdirs = &blobdirs;
let resolve_blob = |blob_filename: PathBuf| -> std::io::Result<PathBuf> {
Expand All @@ -1364,12 +1412,12 @@
if blob_filename.exists() {
Ok(blob_filename)
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Blob read error: Could not find file {blob_filename:?}",
),
))

Check warning on line 1420 in src/main.rs

View workflow job for this annotation

GitHub Actions / clippy

this can be `std::io::Error::other(_)`

warning: this can be `std::io::Error::other(_)` --> src/main.rs:1415:21 | 1415 | Err(std::io::Error::new( | _____________________^ 1416 | | std::io::ErrorKind::Other, 1417 | | format!( 1418 | | "Blob read error: Could not find file {blob_filename:?}", 1419 | | ), 1420 | | )) | |_________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error help: use `std::io::Error::other` | 1415 ~ Err(std::io::Error::other( 1416 ~ format!( |
}
} else {
for blobdir in blobdirs {
Expand All @@ -1381,13 +1429,13 @@
return Ok(fullname);
}
}
Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Blob read error: Could not find file {blob_filename:?} \
(neither directly nor in any of the directories {blobdirs:?})",
),
))

Check warning on line 1438 in src/main.rs

View workflow job for this annotation

GitHub Actions / clippy

this can be `std::io::Error::other(_)`

warning: this can be `std::io::Error::other(_)` --> src/main.rs:1432:17 | 1432 | Err(std::io::Error::new( | _________________^ 1433 | | std::io::ErrorKind::Other, 1434 | | format!( 1435 | | "Blob read error: Could not find file {blob_filename:?} \ 1436 | | (neither directly nor in any of the directories {blobdirs:?})", 1437 | | ), 1438 | | )) | |_____________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error help: use `std::io::Error::other` | 1432 ~ Err(std::io::Error::other( 1433 ~ format!( |
}
};

Expand Down
Loading