oxidecomputer / amd-apcb

AMD Generic Encapsulated Software Architecture Platform Security Processor Configuration Block manipulation library
Mozilla Public License 2.0
13 stars 1 forks source link

Purpose

This crate allows you to manipulate APCB (AGESA PSP Configuration Blob), directly in an image (u8 slice).

Usage

Full-featured usage

Add

amd-apcb = { path = "../amd-apcb", features = ["serde", "schemars"] }

to the [dependencies] block in your Cargo.toml.

This way, you get JSON serialization and JSON schema support. That means when you have an Apcb instance in variable apcb, you can do serde_json::to_string_pretty(&apcb) to get matching JSON out. Likewise, you can also deserialize from JSON into a new Apcb instance (using serde_json::from_str, for example).

Enabling these features slightly changes the signature of some functions (like Apcb::load) to take copy-on-write buffers (in order to allow deserialization).

In order to load an existing blob, do this:

let mut apcb = Apcb::load(std::borrow::Cow::Borrowed(&mut buffer[..]),
                          &ApcbIoOptions::default())?

For details, see the generated documentation.

Minimal usage

Add

amd-apcb = { path = "../amd-apcb", default_features = false, features = [] }

to the [dependencies] block in your Cargo.toml.

This gives you a minimal interface without serialization support. It's intended for embedded use.

In order to load an existing blob, do this:

let mut apcb = Apcb::load(&mut buffer[..], &ApcbIoOptions::default())?

There are (about four) groups in the blob. Inside each group there is a variable number of entries. There are a few different entry types for different purposes.

You can use

apcb.groups()? // or apcb.groups_mut()?

to iterate over the groups.

Alternatively, you can use

apcb.group(GroupId::xxx)? // or apcb.group_mut(GroupId::xxx)?

to immediately get a specific group (a useful example for xxx is Memory).

When you have a group in variable group, you can use

group.entries() // or group.entries_mut()

to iterate over the entries of that group. Alternatively, you can use

group.entry_compatible(EntryId::xxx, instance_id, BoardInstances::yyy)

in order to get an entry compatible with the given instance and boards.

Alternatively, you can use

group.entry_exact(EntryId::xxx, instance_id, BoardInstances::yyy)

in order to get an entry that is exactly specified for the given instance and boards.

When you have an entry in variable entry, you can use

entry.body_as_struct::<X>()

in order to interpret the entry as the given struct X. A useful example for X is ErrorOutControl116.

In order to interpret the entry as an array of a given struct X, you can do:

entry.body_as_struct_array::<X>()

In order to interpret the entry as an array of differently-sized records (collected into the enum X), you can do:

entry.body_as_struct_sequence:<X>()

But on a modern AMD platform, the most useful entries are token entries:

In order to get an interface to the the token entries in a user-friendly form, you can use:

let tokens = apcb.tokens(instance_id, BoardInstances::new())?

It's then possible to use a lot of different getters and setters (one each per token) in order to access the respective token. For example, you can do tokens.abl_serial_baud_rate() to get the baud rate for the serial port and/or tokens.set_abl_serial_baud_rate(BaudRate::_9600Baud) in order to set the baud rate for the serial port.

For more dynamic access, you can do tokens.get(entry_id, token_id) directly, where entry_id: u16 and token_id: u32.

For details, see the generated documentation.

Testing

Run

cargo xtask test