Skip to content
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
2 changes: 0 additions & 2 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ Options:
Parse UEFI Capsule information from binary file
--dump <DUMP>
Dump extracted UX capsule bitmap image to a 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>
Expand Down
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,11 @@ On UEFI and FreeBSD raw port I/O is used - on Linux this can also be used as a f
- [x] CCG5 PD (11th Gen TigerLake) (`--pd-bin`)
- [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/CCG8 PD Version
- [x] UEFI Capsule (`--capsule`)
- [x] Parse metadata from capsule binary
- [x] Determine type (GUID) of capsule binary
- [x] Extract bitmap image from winux capsule to file
- [x] Determine type (GUID) of capsule binary
- [x] Extract embedded BIOS, EC, and PD versions
- [x] Extract bitmap image from winux capsule to file
- [x] Fallback to raw H2O BIOS files without capsule header
- [x] Get firmware version from system (`--versions`)
- [x] BIOS
- [x] EC
Expand Down
6 changes: 1 addition & 5 deletions completions/bash/framework_tool
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ _framework_tool() {

case "${cmd}" in
framework_tool)
opts="-v -q -t -f -h --flash-gpu-descriptor --verbose --quiet --versions --version --features --esrt --device --compare-version --power --thermal --sensors --fansetduty --fansetrpm --autofanctrl --pdports --info --meinfo --pd-info --pd-reset --pd-disable --pd-enable --dp-hdmi-info --dp-hdmi-update --audio-card-info --privacy --pd-bin --ec-bin --capsule --dump --h2o-capsule --dump-ec-flash --flash-ec --flash-ro-ec --flash-rw-ec --intrusion --inputdeck --inputdeck-mode --expansion-bay --charge-limit --charge-current-limit --charge-rate-limit --get-gpio --fp-led-level --fp-brightness --kblight --remap-key --rgbkbd --ps2-enable --tablet-mode --touchscreen-enable --stylus-battery --console --reboot-ec --ec-hib-delay --uptimeinfo --s0ix-counter --hash --driver --pd-addrs --pd-ports --test --test-retimer --boardid --force --dry-run --flash-gpu-descriptor-file --dump-gpu-descriptor-file --nvidia --host-command --generate-completions --help"
opts="-v -q -t -f -h --flash-gpu-descriptor --verbose --quiet --versions --version --features --esrt --device --compare-version --power --thermal --sensors --fansetduty --fansetrpm --autofanctrl --pdports --info --meinfo --pd-info --pd-reset --pd-disable --pd-enable --dp-hdmi-info --dp-hdmi-update --audio-card-info --privacy --pd-bin --ec-bin --capsule --dump --dump-ec-flash --flash-ec --flash-ro-ec --flash-rw-ec --intrusion --inputdeck --inputdeck-mode --expansion-bay --charge-limit --charge-current-limit --charge-rate-limit --get-gpio --fp-led-level --fp-brightness --kblight --remap-key --rgbkbd --ps2-enable --tablet-mode --touchscreen-enable --stylus-battery --console --reboot-ec --ec-hib-delay --uptimeinfo --s0ix-counter --hash --driver --pd-addrs --pd-ports --test --test-retimer --boardid --force --dry-run --flash-gpu-descriptor-file --dump-gpu-descriptor-file --nvidia --host-command --generate-completions --help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
Expand Down Expand Up @@ -89,10 +89,6 @@ _framework_tool() {
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--h2o-capsule)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--dump-ec-flash)
COMPREPLY=($(compgen -f "${cur}"))
return 0
Expand Down
1 change: 0 additions & 1 deletion completions/fish/framework_tool.fish
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ complete -c framework_tool -l pd-bin -d 'Parse versions from PD firmware binary
complete -c framework_tool -l ec-bin -d 'Parse versions from EC firmware binary file' -r -F
complete -c framework_tool -l capsule -d 'Parse UEFI Capsule information from binary file' -r -F
complete -c framework_tool -l dump -d 'Dump extracted UX capsule bitmap image to a file' -r -F
complete -c framework_tool -l h2o-capsule -d 'Parse UEFI Capsule information from binary file' -r -F
complete -c framework_tool -l dump-ec-flash -d 'Dump EC flash contents' -r -F
complete -c framework_tool -l flash-ec -d 'Flash EC (RO+RW) with new firmware from file - may render your hardware unbootable!' -r -F
complete -c framework_tool -l flash-ro-ec -d 'Flash EC with new RO firmware from file - may render your hardware unbootable!' -r -F
Expand Down
1 change: 0 additions & 1 deletion completions/zsh/_framework_tool
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ _framework_tool() {
'--ec-bin=[Parse versions from EC firmware binary file]:EC_BIN:_files' \
'--capsule=[Parse UEFI Capsule information from binary file]:CAPSULE:_files' \
'--dump=[Dump extracted UX capsule bitmap image to a file]:DUMP:_files' \
'--h2o-capsule=[Parse UEFI Capsule information from binary file]:H2O_CAPSULE:_files' \
'--dump-ec-flash=[Dump EC flash contents]:DUMP_EC_FLASH:_files' \
'--flash-ec=[Flash EC (RO+RW) with new firmware from file - may render your hardware unbootable!]:FLASH_EC:_files' \
'--flash-ro-ec=[Flash EC with new RO firmware from file - may render your hardware unbootable!]:FLASH_RO_EC:_files' \
Expand Down
48 changes: 34 additions & 14 deletions framework_lib/src/capsule_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use crate::alloc::string::ToString;
use alloc::string::String;
use alloc::vec::Vec;
use core::convert::TryInto;

use crate::ccgx::binary::{CCG5_PD_LEN, CCG6_PD_LEN, CCG8_PD_LEN};
Expand Down Expand Up @@ -51,19 +52,38 @@ pub fn find_ec_in_bios_cap(data: &[u8]) -> Option<&[u8]> {
}

pub fn find_pd_in_bios_cap(data: &[u8]) -> Option<&[u8]> {
// Just search for the first couple of bytes in PD binaries
// TODO: There's a second one but unless the capsule is bad, we can assume
// 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
find_all_pds_in_bios_cap(data).into_iter().next()
}

/// PD binary signatures and their corresponding lengths
const CCG5_NEEDLE: &[u8] = &[0x00, 0x20, 0x00, 0x20, 0x11, 0x00];
const CCG6_NEEDLE: &[u8] = &[0x00, 0x40, 0x00, 0x20, 0x11, 0x00];
const CCG8_NEEDLE: &[u8] = &[0x00, 0x80, 0x00, 0x20, 0xAD, 0x0C];

/// Find all PD firmware binaries embedded in a BIOS capsule
pub fn find_all_pds_in_bios_cap(data: &[u8]) -> Vec<&[u8]> {
let mut results = Vec::new();

// Search for CCG5 PDs
for offset in util::find_all_sequences(data, CCG5_NEEDLE) {
if offset + CCG5_PD_LEN <= data.len() {
results.push(&data[offset..offset + CCG5_PD_LEN]);
}
}

// Search for CCG6 PDs
for offset in util::find_all_sequences(data, CCG6_NEEDLE) {
if offset + CCG6_PD_LEN <= data.len() {
results.push(&data[offset..offset + CCG6_PD_LEN]);
}
}

// Search for CCG8 PDs
for offset in util::find_all_sequences(data, CCG8_NEEDLE) {
if offset + CCG8_PD_LEN <= data.len() {
results.push(&data[offset..offset + CCG8_PD_LEN]);
}
}

results
}
7 changes: 0 additions & 7 deletions framework_lib/src/commandline/clap_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,6 @@ struct ClapCli {
#[arg(long)]
dump: Option<std::path::PathBuf>,

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

/// Dump EC flash contents
#[arg(long)]
dump_ec_flash: Option<std::path::PathBuf>,
Expand Down Expand Up @@ -491,9 +487,6 @@ 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()),
h2o_capsule: args
.h2o_capsule
.map(|x| x.into_os_string().into_string().unwrap()),
dump_ec_flash: args
.dump_ec_flash
.map(|x| x.into_os_string().into_string().unwrap()),
Expand Down
189 changes: 125 additions & 64 deletions framework_lib/src/commandline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::built_info;
use crate::camera::check_camera_version;
use crate::capsule;
use crate::capsule_content::{
find_bios_version, find_ec_in_bios_cap, find_pd_in_bios_cap, find_retimer_version,
find_all_pds_in_bios_cap, find_bios_version, find_ec_in_bios_cap, find_retimer_version,
};
use crate::ccgx::device::{FwMode, PdController, PdPort};
#[cfg(feature = "hidapi")]
Expand Down Expand Up @@ -184,7 +184,6 @@ pub struct Cli {
pub ec_bin: Option<String>,
pub capsule: Option<String>,
pub dump: 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 @@ -272,7 +271,6 @@ pub fn parse(args: &[String]) -> Cli {
ec_bin: cli.ec_bin,
capsule: cli.capsule,
dump: cli.dump,
h2o_capsule: cli.h2o_capsule,
// dump_ec_flash
// flash_ec
// flash_ro_ec
Expand Down Expand Up @@ -1678,41 +1676,30 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
}
}
} else {
println!("Capsule is invalid.");
}
}
} else if let Some(capsule_path) = &args.h2o_capsule {
#[cfg(feature = "uefi")]
let data = crate::fw_uefi::fs::shell_read_file(capsule_path);
#[cfg(not(feature = "uefi"))]
let data = match fs::read(capsule_path) {
Ok(data) => Some(data),
// TODO: Perhaps a more user-friendly error
Err(e) => {
println!("Error {:?}", e);
None
}
};

if let Some(data) = data {
println!("File");
println!(" Size: {:>20} B", data.len());
println!(" Size: {:>20} KB", data.len() / 1024);
if let Some(cap) = find_bios_version(&data) {
println!(" BIOS Platform:{:>18}", cap.platform);
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");
// No valid capsule header - try to extract embedded firmware directly
// This handles raw H2O BIOS files that aren't wrapped in a UEFI capsule
println!("No valid capsule header, searching for embedded firmware...");
let mut found_any = false;
if let Some(cap) = find_bios_version(&data) {
found_any = true;
println!("BIOS");
println!(" Platform: {:>18}", cap.platform);
println!(" Version: {:>18}", cap.version);
}
if let Some(ec_bin) = find_ec_in_bios_cap(&data) {
found_any = true;
println!("Embedded EC");
analyze_ec_fw(ec_bin);
}
let pd_bins = find_all_pds_in_bios_cap(&data);
for (i, pd_bin) in pd_bins.iter().enumerate() {
found_any = true;
println!("Embedded PD {}", i + 1);
analyze_ccgx_pd_fw(pd_bin);
}
if !found_any {
println!("No embedded firmware found.");
}
}
}
} else if let Some(dump_path) = &args.dump_ec_flash {
Expand Down Expand Up @@ -1829,7 +1816,6 @@ 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
--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 @@ -2333,53 +2319,128 @@ pub fn analyze_capsule(data: &[u8]) -> Option<capsule::EfiCapsuleHeader> {
let header = capsule::parse_capsule_header(data)?;
capsule::print_capsule_header(&header);

match GUID::from(header.capsule_guid) {
esrt::TGL_BIOS_GUID => {
println!(" Type: Framework TGL Insyde BIOS");
let guid_kind = esrt::match_guid_kind(&header.capsule_guid);
match guid_kind {
esrt::FrameworkGuidKind::TglBios => {
println!(" Type: Framework 13 TGL Insyde BIOS");
}
esrt::FrameworkGuidKind::AdlBios => {
println!(" Type: Framework 13 ADL Insyde BIOS");
}
esrt::FrameworkGuidKind::RplBios => {
println!(" Type: Framework 13 RPL Insyde BIOS");
}
esrt::FrameworkGuidKind::MtlBios => {
println!(" Type: Framework 13 MTL Insyde BIOS");
}
esrt::FrameworkGuidKind::Fw12RplBios => {
println!(" Type: Framework 12 RPL Insyde BIOS");
}
esrt::FrameworkGuidKind::Fl16Bios => {
println!(" Type: Framework 16 AMD Insyde BIOS");
}
esrt::FrameworkGuidKind::Amd16Ai300Bios => {
println!(" Type: Framework 16 AMD AI 300 Insyde BIOS");
}
esrt::FrameworkGuidKind::Amd13Ryzen7040Bios => {
println!(" Type: Framework 13 AMD Ryzen 7040 Insyde BIOS");
}
esrt::FrameworkGuidKind::Amd13Ai300Bios => {
println!(" Type: Framework 13 AMD AI 300 Insyde BIOS");
}
esrt::FrameworkGuidKind::DesktopAmdAi300Bios => {
println!(" Type: Framework Desktop AMD AI 300 Insyde BIOS");
}
esrt::FrameworkGuidKind::TglRetimer01 => {
println!(" Type: Framework TGL Retimer01 (Right)");
}
esrt::ADL_BIOS_GUID => {
println!(" Type: Framework ADL Insyde BIOS");
esrt::FrameworkGuidKind::TglRetimer23 => {
println!(" Type: Framework TGL Retimer23 (Left)");
}
esrt::RPL_BIOS_GUID => {
println!(" Type: Framework RPL Insyde BIOS");
esrt::FrameworkGuidKind::AdlRetimer01 => {
println!(" Type: Framework ADL Retimer01 (Right)");
}
esrt::TGL_RETIMER01_GUID => {
println!(" Type: Framework TGL Retimer01 (Right)");
esrt::FrameworkGuidKind::AdlRetimer23 => {
println!(" Type: Framework ADL Retimer23 (Left)");
}
esrt::TGL_RETIMER23_GUID => {
println!(" Type: Framework TGL Retimer23 (Left)");
esrt::FrameworkGuidKind::RplRetimer01 => {
println!(" Type: Framework RPL Retimer01 (Right)");
}
esrt::ADL_RETIMER01_GUID => {
println!(" Type: Framework ADL Retimer01 (Right)");
esrt::FrameworkGuidKind::RplRetimer23 => {
println!(" Type: Framework RPL Retimer23 (Left)");
}
esrt::ADL_RETIMER23_GUID => {
println!(" Type: Framework ADL Retimer23 (Left)");
esrt::FrameworkGuidKind::MtlRetimer01 => {
println!(" Type: Framework MTL Retimer01 (Right)");
}
esrt::RPL_RETIMER01_GUID => {
println!(" Type: Framework RPL Retimer01 (Right)");
esrt::FrameworkGuidKind::MtlRetimer23 => {
println!(" Type: Framework MTL Retimer23 (Left)");
}
esrt::RPL_RETIMER23_GUID => {
println!(" Type: Framework RPL Retimer23 (Left)");
esrt::FrameworkGuidKind::RplCsme => {
println!(" Type: Framework RPL CSME");
}
esrt::WINUX_GUID => {
println!(" Type: Windows UX capsule");
esrt::FrameworkGuidKind::RplUCsme => {
println!(" Type: Framework RPL-U CSME");
}
esrt::FrameworkGuidKind::MtlCsme => {
println!(" Type: Framework MTL CSME");
}
esrt::FrameworkGuidKind::WinUx => {
println!(" Type: Windows UX capsule");
let ux_header = capsule::parse_ux_header(data);
capsule::print_ux_header(&ux_header);
}
_ => {
esrt::FrameworkGuidKind::Unknown => {
println!(" Type: Unknown");
}
}

match esrt::match_guid_kind(&header.capsule_guid) {
// Extract retimer version if this is a retimer capsule
match guid_kind {
esrt::FrameworkGuidKind::TglRetimer01
| esrt::FrameworkGuidKind::TglRetimer23
| esrt::FrameworkGuidKind::AdlRetimer01
| esrt::FrameworkGuidKind::AdlRetimer23
| esrt::FrameworkGuidKind::RplRetimer01
| esrt::FrameworkGuidKind::RplRetimer23 => {
| esrt::FrameworkGuidKind::RplRetimer23
| esrt::FrameworkGuidKind::MtlRetimer01
| esrt::FrameworkGuidKind::MtlRetimer23 => {
if let Some(ver) = find_retimer_version(data) {
println!(" Version: {:>18?}", ver);
println!(" Retimer Version: {:>15}", ver);
}
}
_ => {}
}

// Extract embedded firmware versions for BIOS capsules
match guid_kind {
esrt::FrameworkGuidKind::TglBios
| esrt::FrameworkGuidKind::AdlBios
| esrt::FrameworkGuidKind::RplBios
| esrt::FrameworkGuidKind::MtlBios
| esrt::FrameworkGuidKind::Fw12RplBios
| esrt::FrameworkGuidKind::Fl16Bios
| esrt::FrameworkGuidKind::Amd16Ai300Bios
| esrt::FrameworkGuidKind::Amd13Ryzen7040Bios
| esrt::FrameworkGuidKind::Amd13Ai300Bios
| esrt::FrameworkGuidKind::DesktopAmdAi300Bios => {
if let Some(cap) = find_bios_version(data) {
println!("BIOS");
println!(" Platform: {:>18}", cap.platform);
println!(" Version: {:>18}", cap.version);
}
if let Some(ec_bin) = find_ec_in_bios_cap(data) {
println!("Embedded EC");
if let Some(ver) = ec_binary::read_ec_version(ec_bin, true) {
println!(" RO Version: {:>18}", ver.version);
}
if let Some(ver) = ec_binary::read_ec_version(ec_bin, false) {
println!(" RW Version: {:>18}", ver.version);
}
}
let pd_bins = find_all_pds_in_bios_cap(data);
for (i, pd_bin) in pd_bins.iter().enumerate() {
println!("Embedded PD {}", i + 1);
analyze_ccgx_pd_fw(pd_bin);
}
}
_ => {}
Expand Down
Loading