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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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 @@ use amd_efs::{
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 @@ fn test_bitfield_serde() {
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 @@ -622,7 +670,7 @@ fn dump_psp_directory<T: FlashRead + FlashWrite>(
// 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 @@ fn dump_psp_directory<T: FlashRead + FlashWrite>(
}
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 @@ fn prepare_psp_directory_contents(
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