Skip to content

Fix H2O capsule analysis #92

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 5 commits into from
Mar 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
24 changes: 7 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ see the [Support Matrices](support-matrices.md).
- [x] ESRT table (UEFI, Linux, FreeBSD only) (`--esrt`)
- [x] SMBIOS
- [x] Get firmware version from binary file
- [x] Legacy EC (Intel 13th Gen and earlier) (`--ec-bin`)
- [x] Zephyr EC (AMD) (`--ec-bin`)
- [x] EC (Legacy and Zephyr based) (`--ec-bin`)
- [x] CCG5 PD (11th Gen TigerLake) (`--pd-bin`)
- [x] CCG6 PD (12th Gen AlderLake) (`--pd-bin`)
- [x] CCG8 PD (Framework 16) (`--pd-bin`)
- [x] HO2 BIOS Capsule (`--ho2-capsule`)
- [x] CCG6 PD (Intel systems, Framework Desktop) (`--pd-bin`)
- [x] CCG8 PD (AMD Laptops) (`--pd-bin`)
- [x] H2O BIOS Capsule (`--h2o-capsule`)
- [x] BIOS Version
- [x] EC Version
- [x] CCG5/CCG6 PD Version
- [x] CCG5/CCG6/CCG8 PD Version
- [x] UEFI Capsule (`--capsule`)
- [x] Parse metadata from capsule binary
- [x] Determine type (GUID) of capsule binary
Expand All @@ -53,16 +52,6 @@ see the [Support Matrices](support-matrices.md).
- [x] DisplayPort Expansion Card (`--dp-hdmi-update`)
- [ ] Audio Expansion Card

###### Firmware Update

Note: Use fwupd.

- [ ] Flash firmware
- [ ] BIOS
- [ ] EC
- [ ] PD
- [ ] Expansion Cards

###### System Status

All of these need EC communication support in order to work.
Expand Down Expand Up @@ -90,6 +79,7 @@ All of these need EC communication support in order to work.
- [x] Framework Desktop (AMD Ryzen AI Max 300)
- [x] Port I/O communication on Linux
- [x] Port I/O communication in UEFI
- [x] Port I/O communication on FreeBSD
- [x] Using `cros_ec` driver in Linux kernel
- [x] Using [Framework EC Windows driver](https://github.com/FrameworkComputer/crosecbus) based on [coolstar's](https://github.com/coolstar/crosecbus)
- [x] Using [DHowett's Windows CrosEC driver](https://github.com/DHowett/FrameworkWindowsUtils)
Expand Down Expand Up @@ -182,7 +172,7 @@ Options:
--ec-bin <EC_BIN> Parse versions from EC firmware binary file
--capsule <CAPSULE> Parse UEFI Capsule information from binary file
--dump <DUMP> Dump extracted UX capsule bitmap image to a file
--ho2-capsule <HO2_CAPSULE> Parse UEFI Capsule information from binary file
--h2o-capsule <H2O_CAPSULE> Parse UEFI Capsule information from binary file
--intrusion Show status of intrusion switch
--inputmodules Show status of the input modules (Framework 16 only)
--kblight [<KBLIGHT>] Set keyboard backlight percentage or get, if no value provided
Expand Down
22 changes: 12 additions & 10 deletions framework_lib/src/capsule_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::alloc::string::ToString;
use alloc::string::String;
use core::convert::TryInto;

use crate::ccgx::binary::{CCG5_PD_LEN, CCG6_PD_LEN};
use crate::ccgx::binary::{CCG5_PD_LEN, CCG6_PD_LEN, CCG8_PD_LEN};
use crate::ec_binary::EC_LEN;
use crate::util;

Expand All @@ -29,26 +29,25 @@ pub fn find_bios_version(data: &[u8]) -> Option<BiosCapsule> {
let needle = b"$BVDT";
let found = util::find_sequence(data, needle)?;

// One of: GFW30, HFW3T, HFW30, IFR30, KFM30, JFP30, LFK30, IFGA3, IFGP6, LFR20, LFSP0
let platform_offset = found + 0xA + needle.len() - 1;
let platform = std::str::from_utf8(&data[platform_offset..platform_offset + 4])
let platform = std::str::from_utf8(&data[platform_offset..platform_offset + 5])
.map(|x| x.to_string())
.ok()?;

let ver_offset = found + 0xE + needle.len() - 1;
let version = std::str::from_utf8(&data[ver_offset..ver_offset + 4])
let ver_offset = found + 0x10 + needle.len() - 1;
let version = std::str::from_utf8(&data[ver_offset..ver_offset + 5])
.map(|x| x.to_string())
.ok()?;

Some(BiosCapsule { platform, version })
}

pub fn find_ec_in_bios_cap(data: &[u8]) -> Option<&[u8]> {
let needle = b"_IFLASH_EC_IMG_";
let found_iflash = util::find_sequence(data, needle)?;
// The actual EC binary is a few bytes after `_IFLASH_EC_IMG_`.
// Just earch for the first 4 bytes that seem to appear in all EC images.
let found = util::find_sequence(&data[found_iflash..], &[0x10, 0x00, 0x00, 0xf7])?;
Some(&data[found_iflash + found..found_iflash + found + EC_LEN])
let needle = b"$_IFLASH_EC_IMG_";
let found = util::find_sequence(data, needle)?;
let ec_offset = found + 0x9 + needle.len() - 1;
Some(&data[ec_offset..ec_offset + EC_LEN])
}

pub fn find_pd_in_bios_cap(data: &[u8]) -> Option<&[u8]> {
Expand All @@ -57,10 +56,13 @@ pub fn find_pd_in_bios_cap(data: &[u8]) -> Option<&[u8]> {
// they're the same version
let ccg5_needle = &[0x00, 0x20, 0x00, 0x20, 0x11, 0x00];
let ccg6_needle = &[0x00, 0x40, 0x00, 0x20, 0x11, 0x00];
let ccg8_needle = &[0x00, 0x80, 0x00, 0x20, 0xAD, 0x0C];
if let Some(found_pd1) = util::find_sequence(data, ccg5_needle) {
Some(&data[found_pd1..found_pd1 + CCG5_PD_LEN])
} else if let Some(found_pd1) = util::find_sequence(data, ccg6_needle) {
Some(&data[found_pd1..found_pd1 + CCG6_PD_LEN])
} else if let Some(found_pd1) = util::find_sequence(data, ccg8_needle) {
Some(&data[found_pd1..found_pd1 + CCG8_PD_LEN])
} else {
None
}
Expand Down
5 changes: 3 additions & 2 deletions framework_lib/src/ccgx/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ struct VersionInfo {
silicon_family: u16,
}

pub const CCG5_PD_LEN: usize = 0x2_0000;
pub const CCG6_PD_LEN: usize = 0x2_0000;
pub const CCG5_PD_LEN: usize = 0x20_000;
pub const CCG6_PD_LEN: usize = 0x20_000;
pub const CCG8_PD_LEN: usize = 0x40_000;

/// Information about all the firmware in a PD binary file
///
Expand Down
6 changes: 3 additions & 3 deletions framework_lib/src/commandline/clap_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ struct ClapCli {

/// Parse UEFI Capsule information from binary file
#[arg(long)]
ho2_capsule: Option<std::path::PathBuf>,
h2o_capsule: Option<std::path::PathBuf>,

/// Dump EC flash contents
#[arg(long)]
Expand Down Expand Up @@ -259,8 +259,8 @@ pub fn parse(args: &[String]) -> Cli {
.capsule
.map(|x| x.into_os_string().into_string().unwrap()),
dump: args.dump.map(|x| x.into_os_string().into_string().unwrap()),
ho2_capsule: args
.ho2_capsule
h2o_capsule: args
.h2o_capsule
.map(|x| x.into_os_string().into_string().unwrap()),
dump_ec_flash: args
.dump_ec_flash
Expand Down
18 changes: 12 additions & 6 deletions framework_lib/src/commandline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub struct Cli {
pub ec_bin: Option<String>,
pub capsule: Option<String>,
pub dump: Option<String>,
pub ho2_capsule: Option<String>,
pub h2o_capsule: Option<String>,
pub dump_ec_flash: Option<String>,
pub flash_ec: Option<String>,
pub flash_ro_ec: Option<String>,
Expand Down Expand Up @@ -932,7 +932,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
println!("Capsule is invalid.");
}
}
} else if let Some(capsule_path) = &args.ho2_capsule {
} else if let Some(capsule_path) = &args.h2o_capsule {
#[cfg(feature = "uefi")]
let data = crate::uefi::fs::shell_read_file(capsule_path);
#[cfg(not(feature = "uefi"))]
Expand All @@ -954,10 +954,16 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
println!(" BIOS Version: {:>18}", cap.version);
}
if let Some(ec_bin) = find_ec_in_bios_cap(&data) {
debug!("Found EC binary in BIOS capsule");
analyze_ec_fw(ec_bin);
} else {
debug!("Didn't find EC binary in BIOS capsule");
}
if let Some(pd_bin) = find_pd_in_bios_cap(&data) {
debug!("Found PD binary in BIOS capsule");
analyze_ccgx_pd_fw(pd_bin);
} else {
debug!("Didn't find PD binary in BIOS capsule");
}
}
} else if let Some(dump_path) = &args.dump_ec_flash {
Expand Down Expand Up @@ -1022,7 +1028,7 @@ Options:
--ec-bin <EC_BIN> Parse versions from EC firmware binary file
--capsule <CAPSULE> Parse UEFI Capsule information from binary file
--dump <DUMP> Dump extracted UX capsule bitmap image to a file
--ho2-capsule <HO2_CAPSULE> Parse UEFI Capsule information from binary file
--h2o-capsule <H2O_CAPSULE> Parse UEFI Capsule information from binary file
--dump-ec-flash <DUMP_EC_FLASH> Dump EC flash contents
--flash-ec <FLASH_EC> Flash EC with new firmware from file
--flash-ro-ec <FLASH_EC> Flash EC with new firmware from file
Expand Down Expand Up @@ -1280,7 +1286,7 @@ fn analyze_ccgx_pd_fw(data: &[u8]) {
ccgx::binary::print_fw(&versions.main_fw);
return;
} else {
println!("Failed to read versions")
println!("Failed to read PD versions")
}
}

Expand All @@ -1289,13 +1295,13 @@ pub fn analyze_ec_fw(data: &[u8]) {
if let Some(ver) = ec_binary::read_ec_version(data, true) {
ec_binary::print_ec_version(&ver, true);
} else {
println!("Failed to read version")
println!("Failed to read EC version")
}
// Readwrite firmware
if let Some(ver) = ec_binary::read_ec_version(data, false) {
ec_binary::print_ec_version(&ver, false);
} else {
println!("Failed to read version")
println!("Failed to read EC version")
}
}

Expand Down
8 changes: 4 additions & 4 deletions framework_lib/src/commandline/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub fn parse(args: &[String]) -> Cli {
flash_rw_ec: None,
capsule: None,
dump: None,
ho2_capsule: None,
h2o_capsule: None,
intrusion: false,
inputmodules: false,
input_deck_mode: None,
Expand Down Expand Up @@ -374,11 +374,11 @@ pub fn parse(args: &[String]) -> Cli {
None
};
found_an_option = true;
} else if arg == "--ho2-capsule" {
cli.ho2_capsule = if args.len() > i + 1 {
} else if arg == "--h2o-capsule" {
cli.h2o_capsule = if args.len() > i + 1 {
Some(args[i + 1].clone())
} else {
println!("--ho2-capsule requires extra argument to denote input file");
println!("--h2o-capsule requires extra argument to denote input file");
None
};
found_an_option = true;
Expand Down