From eec9a1e4e4071416ed7306cbb9668ee19c8de072 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 17:38:54 +0800 Subject: [PATCH 01/52] gh-actions: Split UEFI build into separate action Signed-off-by: Daniel Schaefer --- .github/workflows/ci.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f8dc7d1..61c0f70a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: path: target/x86_64-unknown-freebsd/debug/framework_tool build: - name: Build Linux and UEFI + name: Build Linux runs-on: ubuntu-24.04 env: CARGO_NET_GIT_FETCH_WITH_CLI: true @@ -65,6 +65,17 @@ jobs: name: framework_tool path: target/debug/framework_tool + build-uefi: + name: Build UEFI + runs-on: ubuntu-24.04 + env: + CARGO_NET_GIT_FETCH_WITH_CLI: true + steps: + - uses: actions/checkout@v4 + + - name: Setup Rust toolchain + run: rustup show + - name: Build UEFI application (no ESP) run: make -C framework_uefi build/x86_64-unknown-uefi/boot.efi From b470e3fefb98dcea9fd32565151ef2816572381c Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 21:32:19 +0800 Subject: [PATCH 02/52] esrt: Add framework desktop GUID Signed-off-by: Daniel Schaefer --- framework_lib/src/esrt/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index 15daea4c..982d3bc0 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -131,6 +131,12 @@ pub const AMD13_AI300_BIOS_GUID: GUID = GUID::build_from_components( 0x5d68, &[0xbe, 0x61, 0x6b, 0x17, 0x88, 0x10, 0x14, 0xa7], ); +pub const DESKTOP_AMD_AI300_BIOS_GUID: GUID = GUID::build_from_components( + 0xeb68dbae, + 0x3aef, + 0x5077, + &[0x92, 0xae, 0x90, 0x16, 0xd1, 0xf0, 0xc8, 0x56], +); pub const RPL_CSME_GUID: GUID = GUID::build_from_components( 0x865d322c, 0x6ac7, @@ -182,6 +188,7 @@ pub enum FrameworkGuidKind { Fl16Bios, Amd13Ryzen7040Bios, Amd13Ai300Bios, + DesktopAmdAi300Bios, WinUx, Unknown, } @@ -196,6 +203,7 @@ pub fn match_guid_kind(guid: &Guid) -> FrameworkGuidKind { FL16_BIOS_GUID => FrameworkGuidKind::Fl16Bios, AMD13_RYZEN7040_BIOS_GUID => FrameworkGuidKind::Amd13Ryzen7040Bios, AMD13_AI300_BIOS_GUID => FrameworkGuidKind::Amd13Ai300Bios, + DESKTOP_AMD_AI300_BIOS_GUID => FrameworkGuidKind::DesktopAmdAi300Bios, TGL_RETIMER01_GUID => FrameworkGuidKind::TglRetimer01, TGL_RETIMER23_GUID => FrameworkGuidKind::TglRetimer23, ADL_RETIMER01_GUID => FrameworkGuidKind::AdlRetimer01, From a75264781cf7e44e4cbe1c4cea0544b0020ef20d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 21:32:34 +0800 Subject: [PATCH 03/52] EXAMPLES: Add --esrt example Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index f688c635..6c3d50aa 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -439,3 +439,28 @@ The scancodes are the same. ### Framework 16 It's not controlled by the EC, use https://keyboard.frame.work. + +## Advanced commands + +Mostly for debugging firmware. + +### Check EFI Resource Table + +On Framework Desktop: + +``` +> sudo framework_tool --esrt +ESRT Table + ResourceCount: 1 + ResourceCountMax: 1 + ResourceVersion: 1 +ESRT Entry 0 + GUID: EB68DBAE-3AEF-5077-92AE-9016D1F0C856 + GUID: DesktopAmdAi300Bios + Type: SystemFirmware + Version: 0x204 (516) + Min FW Version: 0x100 (256) + Capsule Flags: 0x0 + Last Attempt Version: 0x108 (264) + Last Attempt Status: Success +``` From 184f89ba53220424ceee750dfb3b25e4076821ee Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 18 May 2025 13:27:50 +0800 Subject: [PATCH 04/52] Also require root before initializing MEC portio Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/portio.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/framework_lib/src/chromium_ec/portio.rs b/framework_lib/src/chromium_ec/portio.rs index 152d5f40..df489921 100644 --- a/framework_lib/src/chromium_ec/portio.rs +++ b/framework_lib/src/chromium_ec/portio.rs @@ -90,14 +90,6 @@ fn init() -> bool { Initialized::NotYet => {} } - // First try on MEC - portio_mec::init(); - let ec_id = portio_mec::transfer_read(MEC_MEMMAP_OFFSET + EC_MEMMAP_ID, 2); - if ec_id[0] == b'E' && ec_id[1] == b'C' { - *init = Initialized::SucceededMec; - return true; - } - // In Linux userspace has to first request access to ioports // TODO: Close these again after we're done #[cfg(target_os = "linux")] @@ -106,6 +98,15 @@ fn init() -> bool { *init = Initialized::Failed; return false; } + + // First try on MEC + portio_mec::init(); + let ec_id = portio_mec::transfer_read(MEC_MEMMAP_OFFSET + EC_MEMMAP_ID, 2); + if ec_id[0] == b'E' && ec_id[1] == b'C' { + *init = Initialized::SucceededMec; + return true; + } + #[cfg(target_os = "linux")] unsafe { // 8 for request/response header, 0xFF for response From 056ec3103f6c577e0d809ba8744804159c30d158 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 18 May 2025 13:28:16 +0800 Subject: [PATCH 05/52] mec init: Fail if ioperm didn't succeed Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/portio_mec.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/chromium_ec/portio_mec.rs b/framework_lib/src/chromium_ec/portio_mec.rs index 9d3664e2..774acb99 100644 --- a/framework_lib/src/chromium_ec/portio_mec.rs +++ b/framework_lib/src/chromium_ec/portio_mec.rs @@ -22,12 +22,20 @@ const _MEC_LPC_DATA_REGISTER1: u16 = 0x0805; const MEC_LPC_DATA_REGISTER2: u16 = 0x0806; const _MEC_LPC_DATA_REGISTER3: u16 = 0x0807; -pub fn init() { +pub fn init() -> bool { #[cfg(target_os = "linux")] unsafe { - ioperm(EC_LPC_ADDR_HOST_DATA as u64, 8, 1); - ioperm(MEC_LPC_ADDRESS_REGISTER0 as u64, 10, 1); + println!("Init MEC"); + let res = ioperm(EC_LPC_ADDR_HOST_DATA as u64, 8, 1); + if res != 0 { + error!("ioperm failed. portio driver is likely block by Linux kernel lockdown mode"); + return false; + } + let res = ioperm(MEC_LPC_ADDRESS_REGISTER0 as u64, 10, 1); + assert_eq!(res, 0); } + + true } // TODO: Create a wrapper From 42c816ef07bef8a5191dd26ede384ca4eaa08ade Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 18 May 2025 22:17:55 +0800 Subject: [PATCH 06/52] portio: Don't segfault if ioperm failed Have to return and never try again Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/portio.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/portio.rs b/framework_lib/src/chromium_ec/portio.rs index df489921..d76f8a6b 100644 --- a/framework_lib/src/chromium_ec/portio.rs +++ b/framework_lib/src/chromium_ec/portio.rs @@ -100,7 +100,10 @@ fn init() -> bool { } // First try on MEC - portio_mec::init(); + if !portio_mec::init() { + *init = Initialized::Failed; + return false; + } let ec_id = portio_mec::transfer_read(MEC_MEMMAP_OFFSET + EC_MEMMAP_ID, 2); if ec_id[0] == b'E' && ec_id[1] == b'C' { *init = Initialized::SucceededMec; @@ -113,6 +116,7 @@ fn init() -> bool { let res = ioperm(EC_LPC_ADDR_HOST_ARGS as u64, 8 + 0xFF, 1); if res != 0 { error!("ioperm failed. portio driver is likely block by Linux kernel lockdown mode"); + *init = Initialized::Failed; return false; } From 06ff4b74b0bc28ce706b01d28b860946e09c01b2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 24 Apr 2025 00:45:02 +0800 Subject: [PATCH 07/52] Add devenv.nix Must have only Nix and devenv installed ``` devenv shell cargo build sudo ./target/debug/framework_tool ``` Signed-off-by: Daniel Schaefer --- .envrc | 5 ++ .gitignore | 12 +++++ devenv.lock | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++ devenv.nix | 11 +++++ devenv.yaml | 8 +++ nix.md | 12 +++++ 6 files changed, 188 insertions(+) create mode 100644 .envrc create mode 100644 devenv.lock create mode 100644 devenv.nix create mode 100644 devenv.yaml create mode 100644 nix.md diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..30da14fd --- /dev/null +++ b/.envrc @@ -0,0 +1,5 @@ +export DIRENV_WARN_TIMEOUT=20s + +eval "$(devenv direnvrc)" + +use devenv diff --git a/.gitignore b/.gitignore index a34615ec..5e33b24d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,15 @@ build/ *.efi *.exe *.pdb + +# Devenv +.devenv* +devenv.local.nix +# Nix +result* + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 00000000..bd0c7fbf --- /dev/null +++ b/devenv.lock @@ -0,0 +1,140 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1747543288, + "owner": "cachix", + "repo": "devenv", + "rev": "3a8a52386bde1cf14fc2f4c4df80f91417348480", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1747392669, + "owner": "nix-community", + "repo": "fenix", + "rev": "c3c27e603b0d9b5aac8a16236586696338856fbb", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1747372754, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1746807397, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "c5208b594838ea8e6cca5997fbf784b7cca1ca90", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "fenix": "fenix", + "git-hooks": "git-hooks", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": [ + "git-hooks" + ] + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1747557850, + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "e464ff8c755c6e12540a45b83274ec4de4829191", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 00000000..413d6a66 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,11 @@ +{ pkgs, lib, config, inputs, ... }: + +{ + packages = with pkgs; [ + systemd # libudev + ]; + + languages.rust.enable = true; + # https://devenv.sh/reference/options/#languagesrustchannel + languages.rust.channel = "stable"; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 00000000..1d180750 --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,8 @@ +inputs: + fenix: + url: github:nix-community/fenix + inputs: + nixpkgs: + follows: nixpkgs + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling diff --git a/nix.md b/nix.md new file mode 100644 index 00000000..19b140ca --- /dev/null +++ b/nix.md @@ -0,0 +1,12 @@ +# Building with Nix + +## Building with devenv + + +Must have Nix and devenv installed + +``` +devenv shell +cargo build +sudo ./target/debug/framework_tool +``` From bb3909364a871eac2c2a5c500bdfdceea84795df Mon Sep 17 00:00:00 2001 From: SHENGYI HUNG Date: Fri, 23 May 2025 00:14:01 +0800 Subject: [PATCH 08/52] esrt: fix ESRT GUID for EFI device The EFI GUID in FreeBSD is layouted as following typedef struct efi_guid { uint32_t Data1; uint16_t Data2; uint16_t Data3; uint8_t Data4[8]; } efi_guid_t; Which means the Data1, Data2, Data3 should follows the little endian rule as the MSB should be in the right side. Also, the table_len in the ioctl argument of EFI_GET_TABLE should be follow the uuid structure. Fix it by reorder it. Sponsored By: FreeBSD Foundation Sponsored By: Framework Laptop Inc. --- framework_lib/src/esrt/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index 982d3bc0..90bfdbdd 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -15,7 +15,7 @@ use log::{debug, error, info, trace}; use std::prelude::v1::*; use core::prelude::v1::derive; -use guid_create::{Guid, GUID}; +use guid_create::{GUID, Guid}; #[cfg(target_os = "linux")] use std::fs; @@ -506,8 +506,8 @@ pub fn get_esrt() -> Option { let mut table = EfiGetTableIoc { buf: std::ptr::null_mut(), uuid: SYSTEM_RESOURCE_TABLE_GUID_BYTES, - buf_len: 0, table_len: 0, + buf_len: 0, }; unsafe { let fd = file.as_raw_fd(); @@ -532,7 +532,7 @@ pub const SYSTEM_RESOURCE_TABLE_GUID: GUID = GUID::build_from_components( &[0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80], ); pub const SYSTEM_RESOURCE_TABLE_GUID_BYTES: [u8; 16] = [ - 0xb1, 0x22, 0xa2, 0x63, 0x36, 0x61, 0x4f, 0x68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80, + 0x63, 0xa2, 0x22, 0xb1, 0x61, 0x36, 0x68, 0x4f, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80, ]; #[cfg(feature = "uefi")] From db61c36f543c85c72045a9df57c4ff94fae0f761 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 23 May 2025 18:10:05 +0800 Subject: [PATCH 09/52] Add support for EcRequestReadPdVersionV1 Not all systems have exactly 2 PD controllers, e.g. Framework Desktop has just one. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/commands.rs | 26 ++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 4a8fd2bf..0a367ab3 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -987,18 +987,38 @@ impl EcRequest for EcRequestChassisIntrusionC } #[repr(C, packed)] -pub struct EcRequestReadPdVersion {} +pub struct EcRequestReadPdVersionV0 {} #[repr(C, packed)] -pub struct _EcResponseReadPdVersion { +pub struct _EcResponseReadPdVersionV0 { pub controller01: [u8; 8], pub controller23: [u8; 8], } -impl EcRequest<_EcResponseReadPdVersion> for EcRequestReadPdVersion { +impl EcRequest<_EcResponseReadPdVersionV0> for EcRequestReadPdVersionV0 { fn command_id() -> EcCommands { EcCommands::ReadPdVersion } + fn command_version() -> u8 { + 0 + } +} + +#[repr(C, packed)] +pub struct EcRequestReadPdVersionV1 {} +#[repr(C, packed)] +pub struct _EcResponseReadPdVersionV1 { + pub pd_chip_count: u8, + pub pd_controllers: [u8; 0], +} + +impl EcRequest<_EcResponseReadPdVersionV1> for EcRequestReadPdVersionV1 { + fn command_id() -> EcCommands { + EcCommands::ReadPdVersion + } + fn command_version() -> u8 { + 1 + } } #[repr(C, packed)] From ace46f9d9cd4ef6ae99a73fb6760e6d7f68aab76 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 23 May 2025 18:26:54 +0800 Subject: [PATCH 10/52] --versions: Return any number of PD versions Based on EC host command. Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/mod.rs | 8 ++++-- framework_lib/src/commandline/mod.rs | 23 +++++++++++++-- framework_lib/src/power.rs | 43 ++++++++++++++++++++++------ 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index 80b1b763..ea89f4b6 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -3,6 +3,7 @@ use alloc::format; use alloc::string::String; use alloc::string::ToString; +use alloc::vec::Vec; #[cfg(feature = "uefi")] use core::prelude::rust_2021::derive; use num_derive::FromPrimitive; @@ -241,9 +242,10 @@ pub struct PdVersions { /// Same as PdVersions but only the main FW #[derive(Debug)] -pub struct MainPdVersions { - pub controller01: ControllerVersion, - pub controller23: ControllerVersion, +pub enum MainPdVersions { + RightLeft((ControllerVersion, ControllerVersion)), + Single(ControllerVersion), + Many(Vec), } pub fn get_pd_controller_versions(ec: &CrosEc) -> EcResult { diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 7f11c87f..057826db 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -33,7 +33,7 @@ use crate::capsule_content::{ use crate::ccgx::device::{FwMode, PdController, PdPort}; #[cfg(feature = "hidapi")] use crate::ccgx::hid::{check_ccg_fw_version, find_devices, DP_CARD_PID, HDMI_CARD_PID}; -use crate::ccgx::{self, SiliconId::*}; +use crate::ccgx::{self, MainPdVersions, SiliconId::*}; use crate::chromium_ec; use crate::chromium_ec::commands::DeckStateMode; use crate::chromium_ec::commands::FpLedBrightnessLevel; @@ -478,8 +478,25 @@ fn print_versions(ec: &CrosEc) { } } else if let Ok(pd_versions) = power::read_pd_version(ec) { // As fallback try to get it from the EC. But not all EC versions have this command - println!(" Right (01): {}", pd_versions.controller01.app); - println!(" Left (23): {}", pd_versions.controller23.app); + match pd_versions { + MainPdVersions::RightLeft((controller01, controller23)) => { + if let Some(Platform::IntelGen11) = smbios::get_platform() { + println!(" Right (01): {}", controller01.base); + println!(" Left (23): {}", controller23.base); + } else { + println!(" Right (01): {}", controller01.app); + println!(" Left (23): {}", controller23.app); + } + } + MainPdVersions::Single(version) => { + println!(" Version: {}", version.app); + } + MainPdVersions::Many(versions) => { + for (i, version) in versions.into_iter().enumerate() { + println!(" PD {}: {}", i, version.app); + } + } + } } else { println!(" Unknown") } diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index ba89af1d..af5963ec 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -1,7 +1,7 @@ //! Get information about system power (battery, AC, PD ports) use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; use core::convert::TryInto; @@ -798,6 +798,11 @@ pub fn is_charging(ec: &CrosEc) -> EcResult<(bool, bool)> { Ok((port0 || port1, port2 || port3)) } +fn parse_pd_ver_slice(data: &[u8]) -> ControllerVersion { + parse_pd_ver(&[ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]) +} fn parse_pd_ver(data: &[u8; 8]) -> ControllerVersion { ControllerVersion { base: BaseVersion { @@ -815,15 +820,37 @@ fn parse_pd_ver(data: &[u8; 8]) -> ControllerVersion { } } -// NOTE: Only works on ADL at the moment! -// TODO: Not on TGL, need to check if RPL and later have it. +// NOTE: TGL (hx20) does not have this host command pub fn read_pd_version(ec: &CrosEc) -> EcResult { - let info = EcRequestReadPdVersion {}.send_command(ec)?; + let info = EcRequestReadPdVersionV1 {}.send_command_vec(ec); - Ok(MainPdVersions { - controller01: parse_pd_ver(&info.controller01), - controller23: parse_pd_ver(&info.controller23), - }) + // If v1 not available, fall back + if let Err(EcError::Response(EcResponseStatus::InvalidVersion)) = info { + let info = EcRequestReadPdVersionV0 {}.send_command(ec)?; + + return Ok(if info.controller23 == [0, 0, 0, 0, 0, 0, 0, 0] { + MainPdVersions::Single(parse_pd_ver(&info.controller01)) + } else { + MainPdVersions::RightLeft(( + parse_pd_ver(&info.controller01), + parse_pd_ver(&info.controller23), + )) + }); + } + // If any other error, exit + let info = info?; + + let mut versions = vec![]; + let pd_count = info[0] as usize; + for i in 0..pd_count { + // TODO: Is there a safer way to check the range? + if info.len() < 1 + 8 * (i + 1) { + return Err(EcError::DeviceError("Not enough data returned".to_string())); + } + versions.push(parse_pd_ver_slice(&info[1 + 8 * i..1 + 8 * (i + 1)])); + } + + Ok(MainPdVersions::Many(versions)) } pub fn standalone_mode(ec: &CrosEc) -> bool { From 3e0d595ee4f24410b9e123f47b362e7689275eb8 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 25 May 2025 21:05:45 +0800 Subject: [PATCH 11/52] --version: Properly fetch single PD controller ver from ccgx For Framework Desktop. It has only a single PD controller. Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 38 +++++++++++++++++++++++----- framework_lib/src/ccgx/mod.rs | 23 ++++++++++++----- framework_lib/src/commandline/mod.rs | 33 ++++++++++++++++-------- 3 files changed, 70 insertions(+), 24 deletions(-) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 532ad10e..7cdd6839 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -26,6 +26,7 @@ enum ControlRegisters { pub enum PdPort { Left01, Right23, + Back, } impl PdPort { @@ -33,6 +34,9 @@ impl PdPort { fn i2c_address(&self) -> EcResult { let config = Config::get(); let platform = &(*config).as_ref().unwrap().platform; + let unsupported = Err(EcError::DeviceError( + "Controller does not exist on this platform".to_string(), + )); Ok(match (platform, self) { (Platform::GenericFramework((left, _), _), PdPort::Left01) => *left, @@ -50,14 +54,29 @@ impl PdPort { | Platform::Framework16Amd7080, PdPort::Right23, ) => 0x40, - // TODO: It only has a single PD controller - (Platform::FrameworkDesktopAmdAiMax300, _) => 0x08, + (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 0x08, + (Platform::FrameworkDesktopAmdAiMax300, _) => unsupported?, + // Framework Intel Platforms (CCG5 and CCG6) + ( + Platform::Framework12IntelGen13 + | Platform::IntelGen11 + | Platform::IntelGen12 + | Platform::IntelGen13 + | Platform::IntelCoreUltra1, + PdPort::Left01, + ) => 0x08, + ( + Platform::Framework12IntelGen13 + | Platform::IntelGen11 + | Platform::IntelGen12 + | Platform::IntelGen13 + | Platform::IntelCoreUltra1, + PdPort::Right23, + ) => 0x40, (Platform::UnknownSystem, _) => { Err(EcError::DeviceError("Unsupported platform".to_string()))? } - // Framework Intel Platforms (CCG5 and CCG6) - (_, PdPort::Left01) => 0x08, - (_, PdPort::Right23) => 0x40, + (_, PdPort::Back) => unsupported?, }) } @@ -65,6 +84,10 @@ impl PdPort { fn i2c_port(&self) -> EcResult { let config = Config::get(); let platform = &(*config).as_ref().unwrap().platform; + let unsupported = Err(EcError::DeviceError(format!( + "Controller {:?}, does not exist on {:?}", + self, platform + ))); Ok(match (platform, self) { (Platform::GenericFramework(_, (left, _)), PdPort::Left01) => *left, @@ -88,11 +111,12 @@ impl PdPort { | Platform::Framework12IntelGen13, PdPort::Right23, ) => 2, - // TODO: It only has a single PD controller - (Platform::FrameworkDesktopAmdAiMax300, _) => 1, + (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 1, + (Platform::FrameworkDesktopAmdAiMax300, _) => unsupported?, (Platform::UnknownSystem, _) => { Err(EcError::DeviceError("Unsupported platform".to_string()))? } + (_, PdPort::Back) => unsupported?, }) } } diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index ea89f4b6..38997ace 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -3,6 +3,7 @@ use alloc::format; use alloc::string::String; use alloc::string::ToString; +use alloc::vec; use alloc::vec::Vec; #[cfg(feature = "uefi")] use core::prelude::rust_2021::derive; @@ -235,9 +236,10 @@ impl ControllerFirmwares { } #[derive(Debug, PartialEq)] -pub struct PdVersions { - pub controller01: ControllerFirmwares, - pub controller23: ControllerFirmwares, +pub enum PdVersions { + RightLeft((ControllerFirmwares, ControllerFirmwares)), + Single(ControllerFirmwares), + Many(Vec), } /// Same as PdVersions but only the main FW @@ -249,10 +251,17 @@ pub enum MainPdVersions { } pub fn get_pd_controller_versions(ec: &CrosEc) -> EcResult { - Ok(PdVersions { - controller01: PdController::new(PdPort::Left01, ec.clone()).get_fw_versions()?, - controller23: PdController::new(PdPort::Right23, ec.clone()).get_fw_versions()?, - }) + let pd01 = PdController::new(PdPort::Left01, ec.clone()).get_fw_versions(); + let pd23 = PdController::new(PdPort::Right23, ec.clone()).get_fw_versions(); + let pd_back = PdController::new(PdPort::Back, ec.clone()).get_fw_versions(); + + match (pd01, pd23, pd_back) { + (Err(_), Err(_), Ok(pd_back)) => Ok(PdVersions::Single(pd_back)), + (Ok(pd01), Ok(pd23), Err(_)) => Ok(PdVersions::RightLeft((pd01, pd23))), + (Ok(pd01), Ok(pd23), Ok(pd_back)) => Ok(PdVersions::Many(vec![pd01, pd23, pd_back])), + (Err(err), _, _) => Err(err), + (_, Err(err), _) => Err(err), + } } fn parse_metadata_ccg3(buffer: &[u8]) -> Option<(u32, u32)> { diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 057826db..581f884e 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -33,7 +33,7 @@ use crate::capsule_content::{ use crate::ccgx::device::{FwMode, PdController, PdPort}; #[cfg(feature = "hidapi")] use crate::ccgx::hid::{check_ccg_fw_version, find_devices, DP_CARD_PID, HDMI_CARD_PID}; -use crate::ccgx::{self, MainPdVersions, SiliconId::*}; +use crate::ccgx::{self, MainPdVersions, PdVersions, SiliconId::*}; use crate::chromium_ec; use crate::chromium_ec::commands::DeckStateMode; use crate::chromium_ec::commands::FpLedBrightnessLevel; @@ -398,11 +398,8 @@ fn print_versions(ec: &CrosEc) { } println!("PD Controllers"); - - if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let right = &pd_versions.controller01; - let left = &pd_versions.controller23; - // let active_mode = + let ccgx_pd_vers = ccgx::get_pd_controller_versions(ec); + if let Ok(PdVersions::RightLeft((right, left))) = ccgx_pd_vers { if let Some(Platform::IntelGen11) = smbios::get_platform() { if right.main_fw.base != right.backup_fw.base { println!(" Right (01)"); @@ -476,8 +473,24 @@ fn print_versions(ec: &CrosEc) { left.main_fw.app, left.active_fw ); } + } else if let Ok(PdVersions::Single(pd)) = ccgx_pd_vers { + if pd.main_fw.app != pd.backup_fw.app { + println!( + " Main: {}{}", + pd.main_fw.app, + active_mode(&pd.active_fw, FwMode::MainFw) + ); + println!( + " Backup: {}{}", + pd.backup_fw.app, + active_mode(&pd.active_fw, FwMode::BackupFw) + ); + } else { + println!(" Version: {} ({:?})", pd.main_fw.app, pd.active_fw); + } } else if let Ok(pd_versions) = power::read_pd_version(ec) { // As fallback try to get it from the EC. But not all EC versions have this command + debug!(" Fallback to PD Host command"); match pd_versions { MainPdVersions::RightLeft((controller01, controller23)) => { if let Some(Platform::IntelGen11) = smbios::get_platform() { @@ -655,8 +668,8 @@ fn compare_version(device: Option, version: String, ec: &Cro } } Some(HardwareDeviceType::PD0) => { - if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let ver = pd_versions.controller01.active_fw_ver(); + if let Ok(PdVersions::RightLeft((pd01, _pd23))) = ccgx::get_pd_controller_versions(ec) { + let ver = pd01.active_fw_ver(); println!("Comparing PD0 version {:?}", ver); if ver.contains(&version) { @@ -667,8 +680,8 @@ fn compare_version(device: Option, version: String, ec: &Cro } } Some(HardwareDeviceType::PD1) => { - if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let ver = pd_versions.controller23.active_fw_ver(); + if let Ok(PdVersions::RightLeft((_pd01, pd23))) = ccgx::get_pd_controller_versions(ec) { + let ver = pd23.active_fw_ver(); println!("Comparing PD1 version {:?}", ver); if ver.contains(&version) { From dd599124a415b13efb0881ea2f1d729a58e70a1a Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 25 May 2025 21:09:17 +0800 Subject: [PATCH 12/52] --versions: Hide CSME on AMD Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 5 ++--- framework_lib/src/util.rs | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 581f884e..3906cbb1 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -59,8 +59,7 @@ use crate::touchpad::print_touchpad_fw_ver; use crate::touchscreen; #[cfg(feature = "uefi")] use crate::uefi::enable_page_break; -use crate::util; -use crate::util::{Config, Platform, PlatformFamily}; +use crate::util::{self, Config, Platform, PlatformFamily}; #[cfg(feature = "hidapi")] use hidapi::HidApi; use sha2::{Digest, Sha256, Sha384, Sha512}; @@ -557,7 +556,7 @@ fn print_versions(ec: &CrosEc) { } #[cfg(target_os = "linux")] - { + if smbios::get_platform().and_then(Platform::which_cpu_vendor) != Some(util::CpuVendor::Amd) { println!("CSME"); if let Ok(csme) = csme::csme_from_sysfs() { info!(" Enabled: {}", csme.enabled); diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index 022475f1..69bdbd32 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -49,7 +49,28 @@ pub enum PlatformFamily { FrameworkDesktop, } +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum CpuVendor { + Intel, + Amd, +} + impl Platform { + pub fn which_cpu_vendor(self) -> Option { + match self { + Platform::Framework12IntelGen13 + | Platform::IntelGen11 + | Platform::IntelGen12 + | Platform::IntelGen13 + | Platform::IntelCoreUltra1 => Some(CpuVendor::Intel), + Platform::Framework13Amd7080 + | Platform::Framework13AmdAi300 + | Platform::Framework16Amd7080 + | Platform::FrameworkDesktopAmdAiMax300 => Some(CpuVendor::Amd), + Platform::GenericFramework(..) => None, + Platform::UnknownSystem => None, + } + } pub fn which_family(self) -> Option { match self { Platform::Framework12IntelGen13 => Some(PlatformFamily::Framework12), From bfe232e54bf9ea903dbff5ad88af0912fd27001e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 26 May 2025 10:01:49 +0800 Subject: [PATCH 13/52] Fix formatting Broken by bb3909364a871eac2c2a5c500bdfdceea84795df Signed-off-by: Daniel Schaefer --- framework_lib/src/esrt/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index 90bfdbdd..aa517bf4 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -15,7 +15,7 @@ use log::{debug, error, info, trace}; use std::prelude::v1::*; use core::prelude::v1::derive; -use guid_create::{GUID, Guid}; +use guid_create::{Guid, GUID}; #[cfg(target_os = "linux")] use std::fs; From 39e20f5dcbd1aa0057a3eefce44d921502420d6b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 26 May 2025 23:49:17 +0800 Subject: [PATCH 14/52] ec_binary: Make it less misleading if legacy cookie not found Signed-off-by: Daniel Schaefer --- framework_lib/src/ec_binary.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/framework_lib/src/ec_binary.rs b/framework_lib/src/ec_binary.rs index 092367de..15193105 100644 --- a/framework_lib/src/ec_binary.rs +++ b/framework_lib/src/ec_binary.rs @@ -145,38 +145,47 @@ pub fn parse_ec_version_str(version: &str) -> Option { /// Parse version information from EC FW image buffer pub fn read_ec_version(data: &[u8], ro: bool) -> Option { + // First try to find the legacy EC version let offset = if ro { EC_RO_VER_OFFSET } else { EC_RW_VER_OFFSET }; - let offset_zephyr = if ro { - EC_RO_VER_OFFSET_ZEPHYR - } else { - EC_RW_VER_OFFSET_ZEPHYR - }; - if data.len() < offset + core::mem::size_of::<_ImageVersionData>() { return None; } let v: _ImageVersionData = unsafe { std::ptr::read(data[offset..].as_ptr() as *const _) }; if v.cookie1 != CROS_EC_IMAGE_DATA_COOKIE1 { - debug!("Failed to find Cookie 1. Found: {:X?}", { v.cookie1 }); + debug!("Failed to find legacy Cookie 1. Found: {:X?}", { + v.cookie1 + }); } else if v.cookie2 != CROS_EC_IMAGE_DATA_COOKIE2 { - debug!("Failed to find Cookie 2. Found: {:X?}", { v.cookie2 }); + debug!("Failed to find legacy Cookie 2. Found: {:X?}", { + v.cookie2 + }); } else { return parse_ec_version(&v); } + // If not present, find Zephyr EC version + let offset_zephyr = if ro { + EC_RO_VER_OFFSET_ZEPHYR + } else { + EC_RW_VER_OFFSET_ZEPHYR + }; if data.len() < offset_zephyr + core::mem::size_of::<_ImageVersionData>() { return None; } let v: _ImageVersionData = unsafe { std::ptr::read(data[offset_zephyr..].as_ptr() as *const _) }; if v.cookie1 != CROS_EC_IMAGE_DATA_COOKIE1 { - debug!("Failed to find Cookie 1. Found: {:X?}", { v.cookie1 }); + debug!("Failed to find Zephyr Cookie 1. Found: {:X?}", { + v.cookie1 + }); } else if v.cookie2 != CROS_EC_IMAGE_DATA_COOKIE2 { - debug!("Failed to find Cookie 2. Found: {:X?}", { v.cookie2 }); + debug!("Failed to find Zephyr Cookie 2. Found: {:X?}", { + v.cookie2 + }); } else { return parse_ec_version(&v); } From 0a6f8a79aeeeee4a80454612f86b294c55e26bb7 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 13:49:56 +0800 Subject: [PATCH 15/52] chromium_ec: Erase EC in sections Erasing a big section takes too long sometimes and the linux kernel driver times out, so split it up into chunks. One chunk is 1/8 of EC ROM size. Found while flashing RW on lotus: `framework_tool --flash-rw-ec ec.bin` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 9bd367a0..147754f9 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -832,7 +832,30 @@ impl CrosEc { } fn erase_ec_flash(&self, offset: u32, size: u32) -> EcResult<()> { - EcRequestFlashErase { offset, size }.send_command(self) + // Erasing a big section takes too long sometimes and the linux kernel driver times out, so + // split it up into chunks. One chunk is 1/8 of EC ROM size. + let chunk_size = 0x10000; + let mut cur_offset = offset; + + while cur_offset < offset + size { + let rem_size = offset + size - cur_offset; + let cur_size = if rem_size < chunk_size { + rem_size + } else { + chunk_size + }; + debug!( + "EcRequestFlashErase (0x{:05X}, 0x{:05X})", + cur_offset, cur_size + ); + EcRequestFlashErase { + offset: cur_offset, + size: cur_size, + } + .send_command(self)?; + cur_offset += chunk_size; + } + Ok(()) } pub fn flash_notify(&self, flag: MecFlashNotify) -> EcResult<()> { From 3e25b80566f1c7db2f04668701a252788389cbdc Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 15:40:43 +0800 Subject: [PATCH 16/52] chromium_ec: Simplify flash output a bit ``` Will write flash from 0x40000 to 0x39000 in 1824*128B chunks Writing chunk 0: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 100: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 200: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 300: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 400: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 500: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 600: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 700: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 800: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 900: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1000: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1100: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1200: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1300: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1400: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1500: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1600: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1700: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1800: XXXXXXXXXXXXXXXXXXXXXXXX ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 37 +++++++++++++++++----------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 147754f9..689c591d 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -742,14 +742,16 @@ impl CrosEc { println!("Erasing RW region"); self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE)?; + println!(" Done"); println!("Writing RW region"); self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data)?; + println!(" Done"); println!("Verifying RW region"); let flash_rw_data = self.read_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE)?; if rw_data == flash_rw_data { - println!("RW verify success"); + println!(" RW verify success"); } else { println!("RW verify fail"); } @@ -760,14 +762,16 @@ impl CrosEc { println!("Erasing RO region"); self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE)?; + println!(" Done"); println!("Writing RO region"); self.write_ec_flash(FLASH_BASE + FLASH_RO_BASE, ro_data)?; + println!(" Done"); println!("Verifying RO region"); let flash_ro_data = self.read_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE)?; if ro_data == flash_ro_data { - println!("RO verify success"); + println!(" RO verify success"); } else { println!("RO verify fail"); } @@ -777,34 +781,39 @@ impl CrosEc { self.flash_notify(MecFlashNotify::AccessSpiDone)?; self.flash_notify(MecFlashNotify::FirmwareDone)?; - println!("Flashing EC done. You can reboot the EC now"); - // TODO: Should we force a reboot if currently running one was reflashed? + if res.is_ok() { + println!("Flashing EC done. You can reboot the EC now"); + } Ok(()) } /// Write a big section of EC flash. Must be unlocked already fn write_ec_flash(&self, addr: u32, data: &[u8]) -> EcResult<()> { - let info = EcRequestFlashInfo {}.send_command(self)?; - println!("Flash info: {:?}", info); + // TODO: Use flash info to help guide ideal chunk size + // let info = EcRequestFlashInfo {}.send_command(self)?; //let chunk_size = ((0x80 / info.write_ideal_size) * info.write_ideal_size) as usize; + let chunk_size = 0x80; let chunks = data.len() / chunk_size; + println!( + " Will write flash from 0x{:X} to 0x{:X} in {}*{}B chunks", + addr, + data.len(), + chunks, + chunk_size + ); for chunk_no in 0..chunks { let offset = chunk_no * chunk_size; // Current chunk might be smaller if it's the last let cur_chunk_size = std::cmp::min(chunk_size, data.len() - chunk_no * chunk_size); if chunk_no % 100 == 0 { - println!(); - print!( - "Writing chunk {:>4}/{:>4} ({:>6}/{:>6}): X", - chunk_no, - chunks, - offset, - cur_chunk_size * chunks - ); + if chunk_no != 0 { + println!(); + } + print!(" Chunk {:>4}: X", chunk_no); } else { print!("X"); } From 5baf7a99e473167b21edc64ffdc284e1fd4f2ed9 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 23:07:35 +0800 Subject: [PATCH 17/52] --flash-ec: Fail if verify failed The whole operation should fail Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 689c591d..a8e47c0d 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -704,6 +704,7 @@ impl CrosEc { /// | 40000 | 3C000 | 39000 | RO Region | /// | 79000 | 79FFF | 01000 | Flash Flags | pub fn reflash(&self, data: &[u8], ft: EcFlashType) -> EcResult<()> { + let mut res = Ok(()); if ft == EcFlashType::Full || ft == EcFlashType::Ro { if let Some(version) = ec_binary::read_ec_version(data, true) { println!("EC RO Version in File: {:?}", version.version); @@ -753,7 +754,8 @@ impl CrosEc { if rw_data == flash_rw_data { println!(" RW verify success"); } else { - println!("RW verify fail"); + error!("RW verify fail!"); + res = Err(EcError::DeviceError("RW verify fail!".to_string())); } } @@ -773,7 +775,8 @@ impl CrosEc { if ro_data == flash_ro_data { println!(" RO verify success"); } else { - println!("RO verify fail"); + error!("RO verify fail!"); + res = Err(EcError::DeviceError("RW verify fail!".to_string())); } } @@ -785,7 +788,7 @@ impl CrosEc { println!("Flashing EC done. You can reboot the EC now"); } - Ok(()) + res } /// Write a big section of EC flash. Must be unlocked already From a470530f7ca9f348f2aeda50c8600cca3bd859f7 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 23:16:25 +0800 Subject: [PATCH 18/52] --flash-rw-ec: Allow --dry-run If you want to simulate flashing the EC but not actually do it. Goes through all the steps to make sure the execution plan is correct. Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 20 +++++++++++ framework_lib/src/chromium_ec/mod.rs | 43 +++++++++++------------ framework_lib/src/commandline/clap_std.rs | 14 ++++++-- framework_lib/src/commandline/mod.rs | 22 +++++++++--- framework_lib/src/commandline/uefi.rs | 8 +++++ 5 files changed, 78 insertions(+), 29 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 6c3d50aa..51c2b0e8 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -464,3 +464,23 @@ ESRT Entry 0 Last Attempt Version: 0x108 (264) Last Attempt Status: Success ``` + +## Flashing EC firmware + +**IMPORTANT** Flashing EC firmware yourself is not recommended. It may render +your hardware unbootable. Please update your firmware using the official BIOS +update methods (Windows .exe, LVFS/FWUPD, EFI updater)! + +This command has not been thoroughly tested on all Framework Computer systems + +``` +# Simulate flashing RW (to see which blocks are updated) +> framework_tool --flash-rw-ec ec.bin --dry-run + +# Actually flash RW +> framework_tool --flash-rw-ec ec.bin + +# Boot into EC RW firmware (will crash your OS and reboot immediately) +# EC will boot back into RO if the system turned off for 30s +> framework_tool --reboot-ec jump-rw +``` diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index a8e47c0d..d9bd6375 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -703,7 +703,7 @@ impl CrosEc { /// | 3C000 | 3FFFF | 04000 | Preserved | /// | 40000 | 3C000 | 39000 | RO Region | /// | 79000 | 79FFF | 01000 | Flash Flags | - pub fn reflash(&self, data: &[u8], ft: EcFlashType) -> EcResult<()> { + pub fn reflash(&self, data: &[u8], ft: EcFlashType, dry_run: bool) -> EcResult<()> { let mut res = Ok(()); if ft == EcFlashType::Full || ft == EcFlashType::Ro { if let Some(version) = ec_binary::read_ec_version(data, true) { @@ -724,11 +724,6 @@ impl CrosEc { } } - if ft == EcFlashType::Full || ft == EcFlashType::Ro { - println!("For safety reasons flashing RO firmware is disabled."); - return Ok(()); - } - println!("Unlocking flash"); self.flash_notify(MecFlashNotify::AccessSpi)?; self.flash_notify(MecFlashNotify::FirmwareStart)?; @@ -741,12 +736,12 @@ impl CrosEc { if ft == EcFlashType::Full || ft == EcFlashType::Rw { let rw_data = &data[FLASH_RW_BASE as usize..(FLASH_RW_BASE + FLASH_RW_SIZE) as usize]; - println!("Erasing RW region"); - self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE)?; + println!("Erasing RW region{}", if dry_run { " (DRY RUN)" } else { "" }); + self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE, dry_run)?; println!(" Done"); - println!("Writing RW region"); - self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data)?; + println!("Writing RW region{}", if dry_run { " (DRY RUN)" } else { "" }); + self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data, dry_run)?; println!(" Done"); println!("Verifying RW region"); @@ -763,11 +758,11 @@ impl CrosEc { let ro_data = &data[FLASH_RO_BASE as usize..(FLASH_RO_BASE + FLASH_RO_SIZE) as usize]; println!("Erasing RO region"); - self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE)?; + self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE, dry_run)?; println!(" Done"); println!("Writing RO region"); - self.write_ec_flash(FLASH_BASE + FLASH_RO_BASE, ro_data)?; + self.write_ec_flash(FLASH_BASE + FLASH_RO_BASE, ro_data, dry_run)?; println!(" Done"); println!("Verifying RO region"); @@ -792,7 +787,7 @@ impl CrosEc { } /// Write a big section of EC flash. Must be unlocked already - fn write_ec_flash(&self, addr: u32, data: &[u8]) -> EcResult<()> { + fn write_ec_flash(&self, addr: u32, data: &[u8], dry_run: bool) -> EcResult<()> { // TODO: Use flash info to help guide ideal chunk size // let info = EcRequestFlashInfo {}.send_command(self)?; //let chunk_size = ((0x80 / info.write_ideal_size) * info.write_ideal_size) as usize; @@ -822,10 +817,12 @@ impl CrosEc { } let chunk = &data[offset..offset + cur_chunk_size]; - let res = self.write_ec_flash_chunk(addr + offset as u32, chunk); - if let Err(err) = res { - println!(" Failed to write chunk: {:?}", err); - return Err(err); + if !dry_run { + let res = self.write_ec_flash_chunk(addr + offset as u32, chunk); + if let Err(err) = res { + println!(" Failed to write chunk: {:?}", err); + return Err(err); + } } } println!(); @@ -843,7 +840,7 @@ impl CrosEc { .send_command_extra(self, data) } - fn erase_ec_flash(&self, offset: u32, size: u32) -> EcResult<()> { + fn erase_ec_flash(&self, offset: u32, size: u32, dry_run: bool) -> EcResult<()> { // Erasing a big section takes too long sometimes and the linux kernel driver times out, so // split it up into chunks. One chunk is 1/8 of EC ROM size. let chunk_size = 0x10000; @@ -860,11 +857,13 @@ impl CrosEc { "EcRequestFlashErase (0x{:05X}, 0x{:05X})", cur_offset, cur_size ); - EcRequestFlashErase { - offset: cur_offset, - size: cur_size, + if !dry_run { + EcRequestFlashErase { + offset: cur_offset, + size: cur_size, + } + .send_command(self)?; } - .send_command(self)?; cur_offset += chunk_size; } Ok(()) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index aabd5468..2796057d 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -123,11 +123,11 @@ struct ClapCli { #[arg(long)] dump_ec_flash: Option, - /// Flash EC with new firmware from file + /// Flash EC (RO+RW) with new firmware from file - may render your hardware unbootable! #[arg(long)] flash_ec: Option, - /// Flash EC with new RO firmware from file + /// Flash EC with new RO firmware from file - may render your hardware unbootable! #[arg(long)] flash_ro_ec: Option, @@ -250,6 +250,14 @@ struct ClapCli { /// Run self-test to check if interaction with EC is possible #[arg(long, short)] test: bool, + + /// Force execution of an unsafe command - may render your hardware unbootable! + #[arg(long, short)] + force: bool, + + /// Simulate execution of a command (e.g. --flash-ec) + #[arg(long)] + dry_run: bool, } /// Parse a list of commandline arguments and return the struct @@ -424,6 +432,8 @@ pub fn parse(args: &[String]) -> Cli { pd_addrs, pd_ports, test: args.test, + dry_run: args.dry_run, + force: args.force, // TODO: Set help. Not very important because Clap handles this by itself help: false, // UEFI only for now. Don't need to handle diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 3906cbb1..ce7071ed 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -170,6 +170,8 @@ pub struct Cli { pub flash_rw_ec: Option, pub driver: Option, pub test: bool, + pub dry_run: bool, + pub force: bool, pub intrusion: bool, pub inputdeck: bool, pub inputdeck_mode: Option, @@ -594,7 +596,7 @@ fn print_esrt() { } } -fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType) { +fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType, dry_run: bool) { #[cfg(feature = "uefi")] let data = crate::uefi::fs::shell_read_file(ec_bin_path); #[cfg(not(feature = "uefi"))] @@ -613,7 +615,7 @@ fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType) { println!("File"); println!(" Size: {:>20} B", data.len()); println!(" Size: {:>20} KB", data.len() / 1024); - if let Err(err) = ec.reflash(&data, flash_type) { + if let Err(err) = ec.reflash(&data, flash_type, dry_run) { println!("Error: {:?}", err); } else { println!("Success!"); @@ -1107,11 +1109,19 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { // TODO: Should have progress indicator dump_ec_flash(&ec, dump_path); } else if let Some(ec_bin_path) = &args.flash_ec { - flash_ec(&ec, ec_bin_path, EcFlashType::Full); + if args.force { + flash_ec(&ec, ec_bin_path, EcFlashType::Full, args.dry_run); + } else { + error!("Flashing EC RO region is unsafe. Use --flash-ec-rw instead"); + } } else if let Some(ec_bin_path) = &args.flash_ro_ec { - flash_ec(&ec, ec_bin_path, EcFlashType::Ro); + if args.force { + flash_ec(&ec, ec_bin_path, EcFlashType::Ro, args.dry_run); + } else { + error!("Flashing EC RO region is unsafe. Use --flash-ec-rw instead"); + } } else if let Some(ec_bin_path) = &args.flash_rw_ec { - flash_ec(&ec, ec_bin_path, EcFlashType::Rw); + flash_ec(&ec, ec_bin_path, EcFlashType::Rw, args.dry_run); } else if let Some(hash_file) = &args.hash { println!("Hashing file: {}", hash_file); #[cfg(feature = "uefi")] @@ -1193,6 +1203,8 @@ Options: --console Get EC console, choose whether recent or to follow the output [possible values: recent, follow] --hash Hash a file of arbitrary data --flash-gpu-descriptor <18 DIGIT SN> Overwrite the GPU bay descriptor SN and type. + -f, --force Force execution of an unsafe command - may render your hardware unbootable! + --dry-run Simulate execution of a command (e.g. --flash-ec) -t, --test Run self-test to check if interaction with EC is possible -h, --help Print help information -b Print output one screen at a time diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 097c006f..09a43399 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -109,6 +109,8 @@ pub fn parse(args: &[String]) -> Cli { pd_addrs: None, pd_ports: None, test: false, + dry_run: false, + force: false, help: false, flash_gpu_descriptor: None, allupdate: false, @@ -504,6 +506,12 @@ pub fn parse(args: &[String]) -> Cli { } else if arg == "-t" || arg == "--test" { cli.test = true; found_an_option = true; + } else if arg == "-f" || arg == "--force" { + cli.force = true; + found_an_option = true; + } else if arg == "--dry-run" { + cli.dry_run = true; + found_an_option = true; } else if arg == "-h" || arg == "--help" { cli.help = true; found_an_option = true; From aafd4cd63631f699152fbc9676ce2d657492f74b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 23:45:31 +0800 Subject: [PATCH 19/52] Use flashinfo to determine optimal erase size Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 52 ++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index d9bd6375..02631e50 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -724,6 +724,23 @@ impl CrosEc { } } + // Determine recommended flash parameters + let info = EcRequestFlashInfo {}.send_command(self)?; + + // Check that our hardcoded offsets are valid for the available flash + if FLASH_RO_SIZE + FLASH_RW_SIZE > info.flash_size { + return Err(EcError::DeviceError(format!( + "RO+RW larger than flash 0x{:X}", + { info.flash_size } + ))); + } + if FLASH_RW_BASE + FLASH_RW_SIZE > info.flash_size { + return Err(EcError::DeviceError(format!( + "RW overruns end of flash 0x{:X}", + { info.flash_size } + ))); + } + println!("Unlocking flash"); self.flash_notify(MecFlashNotify::AccessSpi)?; self.flash_notify(MecFlashNotify::FirmwareStart)?; @@ -736,11 +753,22 @@ impl CrosEc { if ft == EcFlashType::Full || ft == EcFlashType::Rw { let rw_data = &data[FLASH_RW_BASE as usize..(FLASH_RW_BASE + FLASH_RW_SIZE) as usize]; - println!("Erasing RW region{}", if dry_run { " (DRY RUN)" } else { "" }); - self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE, dry_run)?; + println!( + "Erasing RW region{}", + if dry_run { " (DRY RUN)" } else { "" } + ); + self.erase_ec_flash( + FLASH_BASE + FLASH_RW_BASE, + FLASH_RW_SIZE, + dry_run, + info.erase_block_size, + )?; println!(" Done"); - println!("Writing RW region{}", if dry_run { " (DRY RUN)" } else { "" }); + println!( + "Writing RW region{}", + if dry_run { " (DRY RUN)" } else { "" } + ); self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data, dry_run)?; println!(" Done"); @@ -758,7 +786,12 @@ impl CrosEc { let ro_data = &data[FLASH_RO_BASE as usize..(FLASH_RO_BASE + FLASH_RO_SIZE) as usize]; println!("Erasing RO region"); - self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE, dry_run)?; + self.erase_ec_flash( + FLASH_BASE + FLASH_RO_BASE, + FLASH_RO_SIZE, + dry_run, + info.erase_block_size, + )?; println!(" Done"); println!("Writing RO region"); @@ -840,10 +873,15 @@ impl CrosEc { .send_command_extra(self, data) } - fn erase_ec_flash(&self, offset: u32, size: u32, dry_run: bool) -> EcResult<()> { + fn erase_ec_flash( + &self, + offset: u32, + size: u32, + dry_run: bool, + chunk_size: u32, + ) -> EcResult<()> { // Erasing a big section takes too long sometimes and the linux kernel driver times out, so - // split it up into chunks. One chunk is 1/8 of EC ROM size. - let chunk_size = 0x10000; + // split it up into chunks. let mut cur_offset = offset; while cur_offset < offset + size { From 22e39c259f614e58047203101c5bc44a99f91d18 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 28 May 2025 00:42:15 +0800 Subject: [PATCH 20/52] EXAMPLES: Add intel core ultra No example for retimer version and ESRT yet. Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 6c3d50aa..43d3e5e4 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -2,7 +2,7 @@ ## Check firmware versions -### BIOS (Mainboard, UEFI, EC, PD) +### BIOS (Mainboard, UEFI, EC, PD, Retimer) Example on Framework 13 AMD Ryzen AI 300 Series: @@ -23,6 +23,25 @@ PD Controllers [...] ``` +Example on Framework 13 Intel Core Ultra Series 1: + +``` +> framework_tool --versions +Mainboard Hardware + Type: Laptop 13 (AMD Ryzen AI 300 Series) + Revision: MassProduction +UEFI BIOS + Version: 03.03 + Release Date: 10/07/2024 +EC Firmware + Build version: marigold-3.0.3-278d300 2024-10-04 03:03:58 marigold1@ip-172-26-3-226 + Current image: RO +PD Controllers + Right (01): 0.0.08 (MainFw) + Left (23): 0.0.08 (MainFw) +[...] +``` + ### Camera (Framework 12, Framework 13, Framework 16) Example on Framework 12: @@ -135,6 +154,56 @@ CSME [...] ``` +### Firmware Version using ESRT (BIOS, Retimer, CSME) + +All systems have at least an entry for BIOS. Intel systems also have CSME and some Retimers. + +Example on Framework 13 Intel Core Ultra Series 1: + +``` +> sudo framework_tool --esrt +ESRT Table + ResourceCount: 4 + ResourceCountMax: 4 + ResourceVersion: 1 +ESRT Entry 0 + GUID: BDFFCE36-809C-4FA6-AECC-54536922F0E0 + GUID: MtlRetimer23 + Type: DeviceFirmware + Version: 0x270 (624) + Min FW Version: 0x0 (0) + Capsule Flags: 0x0 + Last Attempt Version: 0x270 (624) + Last Attempt Status: Success +ESRT Entry 1 + GUID: 32D8D677-EEBC-4947-8F8A-0693A45240E5 + GUID: MtlCsme + Type: DeviceFirmware + Version: 0x85D (2141) + Min FW Version: 0x3E8 (1000) + Capsule Flags: 0x0 + Last Attempt Version: 0x0 (0) + Last Attempt Status: Success +ESRT Entry 2 + GUID: C57FD615-2AC9-4154-BF34-4DC715344408 + GUID: MtlRetimer01 + Type: DeviceFirmware + Version: 0x270 (624) + Min FW Version: 0x0 (0) + Capsule Flags: 0x0 + Last Attempt Version: 0x270 (624) + Last Attempt Status: Success +ESRT Entry 3 + GUID: 72CECB9B-2B37-5EC2-A9FF-C739AABAADF3 + GUID: MtlBios + Type: SystemFirmware + Version: 0x303 (771) + Min FW Version: 0x303 (771) + Capsule Flags: 0x0 + Last Attempt Version: 0x303 (771) + Last Attempt Status: Success +``` + ## Check input deck status ### On Framework 12 From 51c266685dedbb804b26a2038954fe80d1c067ff Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 28 May 2025 22:38:37 +0800 Subject: [PATCH 21/52] README: Explain how to install with winget Available since https://github.com/microsoft/winget-pkgs/pull/259873 Signed-off-by: Daniel Schaefer --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7fbcea32..38f2a2be 100644 --- a/README.md +++ b/README.md @@ -253,11 +253,13 @@ Capsule Header ``` ###### Running on Windows -Windows does not ship with a Chrome EC driver. However there is an open-source implementation that this tool can take advantage of. -The project is hosted on GitHub and you can download pre-built binaries -[there](https://github.com/DHowett/FrameworkWindowsUtils/releases). -The driver is not signed by Microsoft, so you will have to enable testsigning. +On newly released systems since 2025 the Framework driver installer includes the EC driver. +This includes Framework 12, Framework 13 AMD Ryzen AI 300, Framework Desktop. + +Previous platforms will be enabled next. + +Installing: `winget install FrameworkComputer.framework_tool` ##### Running on ChromeOS From 55e127a8993e76a3a01a073e13d1a34e765dfa68 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 3 Jun 2025 17:04:48 +0800 Subject: [PATCH 22/52] Add support for directly querying 3 PDs ``` > framework_tool --versions [...] PD Controllers PD 0: 0.0.0C (MainFw) PD 1: 0.0.0C (MainFw) PD 2: 0.0.0C (MainFw) [...] ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ce7071ed..793182d8 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -474,6 +474,27 @@ fn print_versions(ec: &CrosEc) { left.main_fw.app, left.active_fw ); } + } else if let Ok(PdVersions::Many(versions)) = ccgx_pd_vers { + for (i, version) in versions.into_iter().enumerate() { + if version.main_fw.app != version.backup_fw.app { + println!(" PD {}", 1); + println!( + " Main: {}{}", + version.main_fw.app, + active_mode(&version.active_fw, FwMode::MainFw) + ); + println!( + " Backup: {}{}", + version.backup_fw.app, + active_mode(&version.active_fw, FwMode::BackupFw) + ); + } else { + println!( + " PD {}: {} ({:?})", + i, version.main_fw.app, version.active_fw + ); + } + } } else if let Ok(PdVersions::Single(pd)) = ccgx_pd_vers { if pd.main_fw.app != pd.backup_fw.app { println!( From 28be5a2573a4c3e4d36d2e04a79356f889fa5771 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 3 Jun 2025 17:08:09 +0800 Subject: [PATCH 23/52] Framework16Amd7080: Add support for 3rd PD Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 7cdd6839..c22bf30e 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -54,6 +54,7 @@ impl PdPort { | Platform::Framework16Amd7080, PdPort::Right23, ) => 0x40, + (Platform::Framework16Amd7080, PdPort::Back) => 0x42, (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 0x08, (Platform::FrameworkDesktopAmdAiMax300, _) => unsupported?, // Framework Intel Platforms (CCG5 and CCG6) @@ -111,6 +112,7 @@ impl PdPort { | Platform::Framework12IntelGen13, PdPort::Right23, ) => 2, + (Platform::Framework16Amd7080, PdPort::Back) => 5, (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 1, (Platform::FrameworkDesktopAmdAiMax300, _) => unsupported?, (Platform::UnknownSystem, _) => { From 1e58fdfda2a8af07ac000243dcf51dc5eff1fa2c Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 18:24:10 +0800 Subject: [PATCH 24/52] Show ALS sensor on unknown systems If the system doesn't have motionsense we can't detect whether ALS is present or not based on EC information. Signed-off-by: Daniel Schaefer --- framework_lib/src/power.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index af5963ec..91f87bda 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -305,10 +305,11 @@ pub fn print_sensors(ec: &CrosEc) { err => _ = print_err(err), } - // If we can't detect it based on motionsense + // If we can't detect it based on motionsense, check the system family + // If family is unknown, assume it has let als_family = matches!( smbios::get_family(), - Some(PlatformFamily::Framework13) | Some(PlatformFamily::Framework16) + Some(PlatformFamily::Framework13) | Some(PlatformFamily::Framework16) | None ); if has_als || als_family { From b7d82f28b9df38572cdba65b924b8c0a5816d54e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 18:40:11 +0800 Subject: [PATCH 25/52] EXAMPLES: Document fingerprint reader brightness Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 52b8c22c..73b4fb81 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -453,6 +453,37 @@ Keyboard backlight: 5% > framework_tool.exe --kblight 20 ``` +## Fingerprint/Powerbutton brightness + +On Framework 13 and Framework 16 the power button has an integrated fingerprint reader, hence the name. +On Framework 12 it does not, but the same command can be used. + +``` +# Check the current brightness +> framework_tool --fp-brightness +Fingerprint LED Brightness + Requested: Auto + Brightness: 55% + +# Set it to a custom perfentage +> framework_tool --fp-brightness 42 +Fingerprint LED Brightness + Requested: Custom + Brightness: 42% + +# Set to a specific level (like the BIOS setting does) +> framework_tool --fp-led-level high +Fingerprint LED Brightness + Requested: High + Brightness: 55% + +# Set it back to auto +> framework_tool --fp-led-level auto +Fingerprint LED Brightness + Requested: Auto + Brightness: 15% +``` + ## RGB LED (Framework Desktop) ``` From c8494e4a779aa250d87a9c929c5b3451407cdb15 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 3 Jun 2025 17:16:38 +0800 Subject: [PATCH 26/52] Allow --pd-ports and --pd-addrs to accept 3 ports If the platforms has only 2 or 1, pass same arguments. Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 10 +++++---- framework_lib/src/commandline/clap_std.rs | 8 +++---- framework_lib/src/commandline/mod.rs | 4 ++-- framework_lib/src/commandline/uefi.rs | 26 +++++++++++++---------- framework_lib/src/smbios.rs | 4 ++-- framework_lib/src/util.rs | 2 +- 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index c22bf30e..ed55c8ee 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -39,8 +39,9 @@ impl PdPort { )); Ok(match (platform, self) { - (Platform::GenericFramework((left, _), _), PdPort::Left01) => *left, - (Platform::GenericFramework((_, right), _), PdPort::Right23) => *right, + (Platform::GenericFramework((left, _, _), _), PdPort::Left01) => *left, + (Platform::GenericFramework((_, right, _), _), PdPort::Right23) => *right, + (Platform::GenericFramework((_, _, back), _), PdPort::Back) => *back, // Framework AMD Platforms (CCG8) ( Platform::Framework13Amd7080 @@ -91,8 +92,9 @@ impl PdPort { ))); Ok(match (platform, self) { - (Platform::GenericFramework(_, (left, _)), PdPort::Left01) => *left, - (Platform::GenericFramework(_, (_, right)), PdPort::Right23) => *right, + (Platform::GenericFramework(_, (left, _, _)), PdPort::Left01) => *left, + (Platform::GenericFramework(_, (_, right, _)), PdPort::Right23) => *right, + (Platform::GenericFramework(_, (_, _, back)), PdPort::Back) => *back, (Platform::IntelGen11, _) => 6, (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Left01) => 6, (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Right23) => 7, diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 2796057d..8203e7c9 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -238,12 +238,12 @@ struct ClapCli { driver: Option, /// Specify I2C addresses of the PD chips (Advanced) - #[clap(number_of_values = 2, requires("pd_ports"))] + #[clap(number_of_values = 3, requires("pd_ports"))] #[arg(long)] pd_addrs: Vec, /// Specify I2C ports of the PD chips (Advanced) - #[clap(number_of_values = 2, requires("pd_addrs"))] + #[clap(number_of_values = 3, requires("pd_addrs"))] #[arg(long)] pd_ports: Vec, @@ -317,13 +317,13 @@ pub fn parse(args: &[String]) -> Cli { .unwrap(); let pd_addrs = match args.pd_addrs.len() { - 2 => Some((args.pd_addrs[0], args.pd_addrs[1])), + 3 => Some((args.pd_addrs[0], args.pd_addrs[1], args.pd_addrs[2])), 0 => None, // Checked by clap _ => unreachable!(), }; let pd_ports = match args.pd_ports.len() { - 2 => Some((args.pd_ports[0], args.pd_ports[1])), + 3 => Some((args.pd_ports[0], args.pd_ports[1], args.pd_ports[2])), 0 => None, // Checked by clap _ => unreachable!(), diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 793182d8..cc7283bb 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -193,8 +193,8 @@ pub struct Cli { pub reboot_ec: Option, pub ec_hib_delay: Option>, pub hash: Option, - pub pd_addrs: Option<(u16, u16)>, - pub pd_ports: Option<(u8, u8)>, + pub pd_addrs: Option<(u16, u16, u16)>, + pub pd_ports: Option<(u8, u8, u8)>, pub help: bool, pub info: bool, pub flash_gpu_descriptor: Option<(u8, String)>, diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 09a43399..98c27152 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -602,35 +602,39 @@ pub fn parse(args: &[String]) -> Cli { }; found_an_option = true; } else if arg == "--pd-addrs" { - cli.pd_addrs = if args.len() > i + 2 { + cli.pd_addrs = if args.len() > i + 3 { let left = args[i + 1].parse::(); let right = args[i + 2].parse::(); - if left.is_ok() && right.is_ok() { - Some((left.unwrap(), right.unwrap())) + let back = args[i + 3].parse::(); + if left.is_ok() && right.is_ok() && back.is_ok() { + Some((left.unwrap(), right.unwrap(), back.unwrap())) } else { println!( - "Invalid values for --pd-addrs: '{} {}'. Must be u16 integers.", + "Invalid values for --pd-addrs: '{} {} {}'. Must be u16 integers.", args[i + 1], - args[i + 2] + args[i + 2], + args[i + 3] ); None } } else { - println!("--pd-addrs requires two arguments, one for each address"); + println!("--pd-addrs requires three arguments, one for each address"); None }; found_an_option = true; } else if arg == "--pd-ports" { - cli.pd_ports = if args.len() > i + 2 { + cli.pd_ports = if args.len() > i + 3 { let left = args[i + 1].parse::(); let right = args[i + 2].parse::(); - if left.is_ok() && right.is_ok() { - Some((left.unwrap(), right.unwrap())) + let back = args[i + 3].parse::(); + if left.is_ok() && right.is_ok() && back.is_ok() { + Some((left.unwrap(), right.unwrap(), back.unwrap())) } else { println!( - "Invalid values for --pd-ports: '{} {}'. Must be u16 integers.", + "Invalid values for --pd-ports: '{} {} {}'. Must be u16 integers.", args[i + 1], - args[i + 2] + args[i + 2], + args[i + 3] ); None } diff --git a/framework_lib/src/smbios.rs b/framework_lib/src/smbios.rs index 935f0dc7..144ad210 100644 --- a/framework_lib/src/smbios.rs +++ b/framework_lib/src/smbios.rs @@ -47,7 +47,7 @@ pub enum ConfigDigit0 { pub fn is_framework() -> bool { if matches!( get_platform(), - Some(Platform::GenericFramework((_, _), (_, _))) | Some(Platform::UnknownSystem) + Some(Platform::GenericFramework((_, _, _), (_, _, _))) | Some(Platform::UnknownSystem) ) { return true; } @@ -292,7 +292,7 @@ pub fn get_platform() -> Option { let platform = &(*config).as_ref().unwrap().platform; if matches!( platform, - Platform::GenericFramework((_, _), (_, _)) | Platform::UnknownSystem + Platform::GenericFramework((_, _, _), (_, _, _)) | Platform::UnknownSystem ) { return Some(*platform); } diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index 69bdbd32..1716ed65 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -37,7 +37,7 @@ pub enum Platform { FrameworkDesktopAmdAiMax300, /// Generic Framework device /// pd_addrs, pd_ports - GenericFramework((u16, u16), (u8, u8)), + GenericFramework((u16, u16, u16), (u8, u8, u8)), UnknownSystem, } From 26d6eca2d0d6ed86a79b41ceb1454e4b1d4f4d28 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 18:49:29 +0800 Subject: [PATCH 27/52] --pd-info: Print 3rd pd info ``` > framework_tool --pd-info [...] Back Silicon ID: 0x???? Mode: MainFw Flash Row Size: 256 B Bootloader Version: Base: 3.6.0.009, App: 0.0.01 FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0D FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0D ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index cc7283bb..9df8ae37 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -234,11 +234,14 @@ fn print_pd_details(ec: &CrosEc) { } let pd_01 = PdController::new(PdPort::Left01, ec.clone()); let pd_23 = PdController::new(PdPort::Right23, ec.clone()); + let pd_back = PdController::new(PdPort::Back, ec.clone()); println!("Left / Ports 01"); print_single_pd_details(&pd_01); println!("Right / Ports 23"); print_single_pd_details(&pd_23); + println!("Back"); + print_single_pd_details(&pd_back); } #[cfg(feature = "hidapi")] From b1169beb3c3162f805b58d39705a07e5b2e69eb7 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:01:49 +0800 Subject: [PATCH 28/52] ccgx: Add silicon ID of Framework 13 Intel 12th Gen Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/binary.rs | 3 ++- framework_lib/src/ccgx/mod.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/ccgx/binary.rs b/framework_lib/src/ccgx/binary.rs index 09c16855..cb324221 100644 --- a/framework_lib/src/ccgx/binary.rs +++ b/framework_lib/src/ccgx/binary.rs @@ -106,7 +106,7 @@ fn read_metadata( let buffer = read_256_bytes(file_buffer, metadata_offset, flash_row_size)?; match ccgx { SiliconId::Ccg3 => parse_metadata_ccg3(&buffer), - SiliconId::Ccg5 | SiliconId::Ccg6 => parse_metadata_cyacd(&buffer), + SiliconId::Ccg5 | SiliconId::Ccg6Adl | SiliconId::Ccg6 => parse_metadata_cyacd(&buffer), SiliconId::Ccg8 => parse_metadata_cyacd2(&buffer) .map(|(fw_row_start, fw_size)| (fw_row_start / (flash_row_size as u32), fw_size)), } @@ -172,6 +172,7 @@ pub fn read_versions(file_buffer: &[u8], ccgx: SiliconId) -> Option (SMALL_ROW, 0x03FF, 0x03FE), SiliconId::Ccg5 => (LARGE_ROW, FW1_METADATA_ROW, FW2_METADATA_ROW_CCG5), + SiliconId::Ccg6Adl => (SMALL_ROW, FW1_METADATA_ROW, FW2_METADATA_ROW_CCG6), SiliconId::Ccg6 => (SMALL_ROW, FW1_METADATA_ROW, FW2_METADATA_ROW_CCG6), SiliconId::Ccg8 => (LARGE_ROW, FW1_METADATA_ROW_CCG8, FW2_METADATA_ROW_CCG8), }; diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index 38997ace..1dea7ddf 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -105,7 +105,8 @@ struct CyAcd2Metadata { pub enum SiliconId { Ccg3 = 0x1D00, Ccg5 = 0x2100, - Ccg6 = 0x3000, + Ccg6Adl = 0x3000, + Ccg6 = 0x30A0, Ccg8 = 0x3580, } From f6b1353a3b692faa1d210f3f78a8793aaa92af0e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:05:17 +0800 Subject: [PATCH 29/52] ccgx: Add function to write to ccgx Pass writes through via I2C passthrough on the EC Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 44 +++++++++++++++++++ .../src/chromium_ec/i2c_passthrough.rs | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index ed55c8ee..298e7f14 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -182,6 +182,21 @@ impl PdController { ) } + pub fn i2c_write(&self, addr: u16, data: &[u8]) -> EcResult { + trace!( + "I2C passthrough from I2C Port {} to I2C Addr {}", + self.port.i2c_port()?, + self.port.i2c_address()? + ); + i2c_write( + &self.ec, + self.port.i2c_port()?, + self.port.i2c_address()?, + addr, + data, + ) + } + fn ccgx_read(&self, reg: ControlRegisters, len: u16) -> EcResult> { let mut data: Vec = Vec::with_capacity(len.into()); @@ -204,6 +219,35 @@ impl PdController { Ok(data) } + fn ccgx_write(&self, reg: ControlRegisters, data: &[u8]) -> EcResult<()> { + let addr = reg as u16; + trace!( + "ccgx_write(reg: {:?}, addr: {}, data.len(): {}", + reg, + addr, + data.len() + ); + let mut data_written = 0; + + while data_written < data.len() { + let chunk_len = std::cmp::min(MAX_I2C_CHUNK, data.len()); + let buffer = &data[data_written..data_written + chunk_len]; + let offset = addr + data_written as u16; + + let i2c_response = self.i2c_write(offset, buffer)?; + if let Err(EcError::DeviceError(err)) = i2c_response.is_successful() { + return Err(EcError::DeviceError(format!( + "I2C write was not successful: {:?}", + err + ))); + } + + data_written += chunk_len; + } + + Ok(()) + } + pub fn get_silicon_id(&self) -> EcResult { let data = self.ccgx_read(ControlRegisters::SiliconId, 2)?; assert_win_len(data.len(), 2); diff --git a/framework_lib/src/chromium_ec/i2c_passthrough.rs b/framework_lib/src/chromium_ec/i2c_passthrough.rs index dfebaafc..03cb974e 100644 --- a/framework_lib/src/chromium_ec/i2c_passthrough.rs +++ b/framework_lib/src/chromium_ec/i2c_passthrough.rs @@ -155,7 +155,7 @@ pub fn i2c_write( let data = ec.send_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?; let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) }; - assert_eq!(data.len(), size_of::<_EcI2cPassthruResponse>()); // No extra data other than the header + util::assert_win_len(data.len(), size_of::<_EcI2cPassthruResponse>()); // No extra data other than the header debug_assert_eq!(res.messages as usize, messages.len()); Ok(EcI2cPassthruResponse { i2c_status: res.i2c_status, From 7c9c7dcadaf03796feb51a9fd644e115f3853bc1 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:13:20 +0800 Subject: [PATCH 30/52] ccgx: Add more command definitions Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 298e7f14..4a0134e9 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -14,12 +14,30 @@ use crate::util::{assert_win_len, Config, Platform}; use super::*; +const _HPI_FLASH_ENTER_SIGNATURE: char = 'P'; +const _HPI_JUMP_TO_ALT_SIGNATURE: char = 'A'; +const _HPI_JUMP_TO_BOOT_SIGNATURE: char = 'J'; +const HPI_RESET_SIGNATURE: char = 'R'; +const _HPI_FLASH_RW_SIGNATURE: char = 'F'; +const HPI_RESET_DEV_CMD: u8 = 1; +const _HPI_FLASH_READ_CMD: u8 = 0; +const _HPI_FLASH_WRITE_CMD: u8 = 1; + enum ControlRegisters { DeviceMode = 0, SiliconId = 2, // Two bytes long, First LSB, then MSB + _InterruptStatus = 0x06, + _JumpToBoot = 0x07, + ResetRequest = 0x08, + _FlashmodeEnter = 0x0A, + _ValidateFw = 0x0B, + _FlashSignature = 0x0C, BootLoaderVersion = 0x10, Firmware1Version = 0x18, Firmware2Version = 0x20, + PdPortsEnable = 0x2C, + _ResponseType = 0x7E, + _FlashRwMem = 0x0200, } #[derive(Debug)] From cdf538d927cf0f1fa671c1a08ab58d1d65ef54f2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:14:19 +0800 Subject: [PATCH 31/52] ccgx: Derive more traits on the structs Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 4a0134e9..31945b32 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -23,6 +23,7 @@ const HPI_RESET_DEV_CMD: u8 = 1; const _HPI_FLASH_READ_CMD: u8 = 0; const _HPI_FLASH_WRITE_CMD: u8 = 1; +#[derive(Debug, Copy, Clone)] enum ControlRegisters { DeviceMode = 0, SiliconId = 2, // Two bytes long, First LSB, then MSB @@ -40,7 +41,7 @@ enum ControlRegisters { _FlashRwMem = 0x0200, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Clone, Copy)] pub enum PdPort { Left01, Right23, @@ -148,7 +149,7 @@ pub struct PdController { ec: CrosEc, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone, Copy)] pub enum FwMode { BootLoader = 0, /// Backup CCGX firmware (No 1) From c71105ff81c6046ed1414138532d32f618d4fcd8 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:15:00 +0800 Subject: [PATCH 32/52] ccgx: Add reset_device function framwork_tool --pd-reset 0 framwork_tool --pd-reset 1 Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 8 ++++++++ framework_lib/src/commandline/clap_std.rs | 5 +++++ framework_lib/src/commandline/mod.rs | 12 ++++++++++++ framework_lib/src/commandline/uefi.rs | 17 +++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 31945b32..c3038c5b 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -358,4 +358,12 @@ impl PdController { base_ver, app_ver ); } + + pub fn reset_device(&self) -> EcResult<()> { + self.ccgx_write( + ControlRegisters::ResetRequest, + &[HPI_RESET_SIGNATURE as u8, HPI_RESET_DEV_CMD], + )?; + Ok(()) + } } diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 8203e7c9..1c201ce9 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -83,6 +83,10 @@ struct ClapCli { #[arg(long)] pd_info: bool, + /// Reset a specific PD controller (for debugging only) + #[arg(long)] + pd_reset: Option, + /// Show details about connected DP or HDMI Expansion Cards #[arg(long)] dp_hdmi_info: bool, @@ -376,6 +380,7 @@ pub fn parse(args: &[String]) -> Cli { autofanctrl: args.autofanctrl, pdports: args.pdports, pd_info: args.pd_info, + pd_reset: args.pd_reset, dp_hdmi_info: args.dp_hdmi_info, dp_hdmi_update: args .dp_hdmi_update diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 9df8ae37..b3adb575 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -156,6 +156,7 @@ pub struct Cli { pub pdports: bool, pub privacy: bool, pub pd_info: bool, + pub pd_reset: Option, pub dp_hdmi_info: bool, pub dp_hdmi_update: Option, pub audio_card_info: bool, @@ -999,6 +1000,17 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { smbios_info(); } else if args.pd_info { print_pd_details(&ec); + } else if let Some(pd) = args.pd_reset { + println!("Resetting PD {}...", pd); + print_err(match pd { + 0 => PdController::new(PdPort::Left01, ec.clone()).reset_device(), + 1 => PdController::new(PdPort::Right23, ec.clone()).reset_device(), + 2 => PdController::new(PdPort::Back, ec.clone()).reset_device(), + _ => { + error!("PD {} does not exist", pd); + Ok(()) + } + }); } else if args.dp_hdmi_info { #[cfg(feature = "hidapi")] print_dp_hdmi_details(true); diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 98c27152..49c42490 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -70,6 +70,7 @@ pub fn parse(args: &[String]) -> Cli { autofanctrl: false, pdports: false, pd_info: false, + pd_reset: None, dp_hdmi_info: false, dp_hdmi_update: None, audio_card_info: false, @@ -518,6 +519,22 @@ pub fn parse(args: &[String]) -> Cli { } else if arg == "--pd-info" { cli.pd_info = true; found_an_option = true; + } else if arg == "--pd-reset" { + cli.pd_reset = if args.len() > i + 1 { + if let Ok(pd) = args[i + 1].parse::() { + Some(pd) + } else { + println!( + "Invalid value for --pd-reset: '{}'. Must be 0 or 1.", + args[i + 1], + ); + None + } + } else { + println!("--pd-reset requires specifying the PD controller"); + None + }; + found_an_option = true; } else if arg == "--privacy" { cli.privacy = true; found_an_option = true; From 5b10d0ffec0053d716d67c1a3bbab6302dd6f353 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:16:29 +0800 Subject: [PATCH 33/52] ccgx: Add enable_ports function To disable or enable all ports of a PD controller framwork_tool --pd-disable 0 framwork_tool --pd-enable 1 framwork_tool --pd-disable 0 framwork_tool --pd-enable 1 Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 6 ++++ framework_lib/src/commandline/clap_std.rs | 10 +++++++ framework_lib/src/commandline/mod.rs | 24 ++++++++++++++++ framework_lib/src/commandline/uefi.rs | 34 +++++++++++++++++++++++ 4 files changed, 74 insertions(+) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index c3038c5b..8f841b4f 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -366,4 +366,10 @@ impl PdController { )?; Ok(()) } + + pub fn enable_ports(&self, enable: bool) -> EcResult<()> { + let mask = if enable { 0b11 } else { 0b00 }; + self.ccgx_write(ControlRegisters::PdPortsEnable, &[mask])?; + Ok(()) + } } diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 1c201ce9..52eddbeb 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -87,6 +87,14 @@ struct ClapCli { #[arg(long)] pd_reset: Option, + /// Disable all ports on a specific PD controller (for debugging only) + #[arg(long)] + pd_disable: Option, + + /// Enable all ports on a specific PD controller (for debugging only) + #[arg(long)] + pd_enable: Option, + /// Show details about connected DP or HDMI Expansion Cards #[arg(long)] dp_hdmi_info: bool, @@ -381,6 +389,8 @@ pub fn parse(args: &[String]) -> Cli { pdports: args.pdports, pd_info: args.pd_info, pd_reset: args.pd_reset, + pd_disable: args.pd_disable, + pd_enable: args.pd_enable, dp_hdmi_info: args.dp_hdmi_info, dp_hdmi_update: args .dp_hdmi_update diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index b3adb575..637b8f3f 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -157,6 +157,8 @@ pub struct Cli { pub privacy: bool, pub pd_info: bool, pub pd_reset: Option, + pub pd_disable: Option, + pub pd_enable: Option, pub dp_hdmi_info: bool, pub dp_hdmi_update: Option, pub audio_card_info: bool, @@ -1011,6 +1013,28 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Ok(()) } }); + } else if let Some(pd) = args.pd_disable { + println!("Disabling PD {}...", pd); + print_err(match pd { + 0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(false), + 1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(false), + 2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(false), + _ => { + error!("PD {} does not exist", pd); + Ok(()) + } + }); + } else if let Some(pd) = args.pd_enable { + println!("Enabling PD {}...", pd); + print_err(match pd { + 0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(true), + 1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(true), + 2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(true), + _ => { + error!("PD {} does not exist", pd); + Ok(()) + } + }); } else if args.dp_hdmi_info { #[cfg(feature = "hidapi")] print_dp_hdmi_details(true); diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 49c42490..d21938ae 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -71,6 +71,8 @@ pub fn parse(args: &[String]) -> Cli { pdports: false, pd_info: false, pd_reset: None, + pd_disable: None, + pd_enable: None, dp_hdmi_info: false, dp_hdmi_update: None, audio_card_info: false, @@ -535,6 +537,38 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--pd-disable" { + cli.pd_reset = if args.len() > i + 1 { + if let Ok(pd) = args[i + 1].parse::() { + Some(pd) + } else { + println!( + "Invalid value for --pd-disable: '{}'. Must be 0 or 1.", + args[i + 1], + ); + None + } + } else { + println!("--pd-disable requires specifying the PD controller"); + None + }; + found_an_option = true; + } else if arg == "--pd-enable" { + cli.pd_enable = if args.len() > i + 1 { + if let Ok(pd) = args[i + 1].parse::() { + Some(pd) + } else { + println!( + "Invalid value for --pd-enable: '{}'. Must be 0 or 1.", + args[i + 1], + ); + None + } + } else { + println!("--pd-enable requires specifying the PD controller"); + None + }; + found_an_option = true; } else if arg == "--privacy" { cli.privacy = true; found_an_option = true; From 64381fcc03ca5b9406d4f1514d188736b76836a5 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 14:39:27 +0800 Subject: [PATCH 34/52] EXAMPLES: Move advanced examples to a separate file Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 41 +---------------------- EXAMPLES_ADVANCED.md | 78 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 EXAMPLES_ADVANCED.md diff --git a/EXAMPLES.md b/EXAMPLES.md index 73b4fb81..1ea7f82c 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -544,43 +544,4 @@ It's not controlled by the EC, use https://keyboard.frame.work. Mostly for debugging firmware. -### Check EFI Resource Table - -On Framework Desktop: - -``` -> sudo framework_tool --esrt -ESRT Table - ResourceCount: 1 - ResourceCountMax: 1 - ResourceVersion: 1 -ESRT Entry 0 - GUID: EB68DBAE-3AEF-5077-92AE-9016D1F0C856 - GUID: DesktopAmdAi300Bios - Type: SystemFirmware - Version: 0x204 (516) - Min FW Version: 0x100 (256) - Capsule Flags: 0x0 - Last Attempt Version: 0x108 (264) - Last Attempt Status: Success -``` - -## Flashing EC firmware - -**IMPORTANT** Flashing EC firmware yourself is not recommended. It may render -your hardware unbootable. Please update your firmware using the official BIOS -update methods (Windows .exe, LVFS/FWUPD, EFI updater)! - -This command has not been thoroughly tested on all Framework Computer systems - -``` -# Simulate flashing RW (to see which blocks are updated) -> framework_tool --flash-rw-ec ec.bin --dry-run - -# Actually flash RW -> framework_tool --flash-rw-ec ec.bin - -# Boot into EC RW firmware (will crash your OS and reboot immediately) -# EC will boot back into RO if the system turned off for 30s -> framework_tool --reboot-ec jump-rw -``` +See [EXAMPLES_ADVANCED.md](EXAMPLES_ADVANCED.md) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md new file mode 100644 index 00000000..18146858 --- /dev/null +++ b/EXAMPLES_ADVANCED.md @@ -0,0 +1,78 @@ +# Advanced debugging + +## PD + +### Check PD state + +Example on Framework 13 AMD Ryzen AI 300 +``` +> sudo framework_tool.exe --pd-info +Left / Ports 01 + Silicon ID: 0x3580 + Mode: MainFw + Flash Row Size: 256 B + Bootloader Version: Base: 3.6.0.009, App: 0.0.01 + FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B + FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B +Right / Ports 23 + Silicon ID: 0x3580 + Mode: MainFw + Flash Row Size: 256 B + Bootloader Version: Base: 3.6.0.009, App: 0.0.01 + FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B + FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B +``` + +### Disable/enable/reset PD + +``` +# Disable all ports on PD 0 +> sudo framework_tool --pd-disable 0 + +# Reset PD 0 (enables all ports again) +> sudo framework_tool --pd-reset 0 + +# Or enable all ports on PD 0 without resetting it +> sudo framework_tool --pd-enable 0 +``` + +### Check EFI Resource Table + +On Framework Desktop: + +``` +> sudo framework_tool --esrt +ESRT Table + ResourceCount: 1 + ResourceCountMax: 1 + ResourceVersion: 1 +ESRT Entry 0 + GUID: EB68DBAE-3AEF-5077-92AE-9016D1F0C856 + GUID: DesktopAmdAi300Bios + Type: SystemFirmware + Version: 0x204 (516) + Min FW Version: 0x100 (256) + Capsule Flags: 0x0 + Last Attempt Version: 0x108 (264) + Last Attempt Status: Success +``` + +## Flashing EC firmware + +**IMPORTANT** Flashing EC firmware yourself is not recommended. It may render +your hardware unbootable. Please update your firmware using the official BIOS +update methods (Windows .exe, LVFS/FWUPD, EFI updater)! + +This command has not been thoroughly tested on all Framework Computer systems + +``` +# Simulate flashing RW (to see which blocks are updated) +> framework_tool --flash-rw-ec ec.bin --dry-run + +# Actually flash RW +> framework_tool --flash-rw-ec ec.bin + +# Boot into EC RW firmware (will crash your OS and reboot immediately) +# EC will boot back into RO if the system turned off for 30s +> framework_tool --reboot-ec jump-rw +``` From 8c5ce06b6768cd4df72ed72aa7dc0ef54b0b8df7 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 14:51:02 +0800 Subject: [PATCH 35/52] --pd-info: Print which ports are enabled Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 3 +++ framework_lib/src/ccgx/device.rs | 6 ++++++ framework_lib/src/commandline/mod.rs | 11 +++++++++++ 3 files changed, 20 insertions(+) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index 18146858..cd7d9b25 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -5,12 +5,14 @@ ### Check PD state Example on Framework 13 AMD Ryzen AI 300 + ``` > sudo framework_tool.exe --pd-info Left / Ports 01 Silicon ID: 0x3580 Mode: MainFw Flash Row Size: 256 B + Ports Enabled: 0, 1 Bootloader Version: Base: 3.6.0.009, App: 0.0.01 FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B @@ -18,6 +20,7 @@ Right / Ports 23 Silicon ID: 0x3580 Mode: MainFw Flash Row Size: 256 B + Ports Enabled: 0, 1 Bootloader Version: Base: 3.6.0.009, App: 0.0.01 FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 8f841b4f..064e4acf 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -372,4 +372,10 @@ impl PdController { self.ccgx_write(ControlRegisters::PdPortsEnable, &[mask])?; Ok(()) } + + pub fn get_port_status(&self) -> EcResult { + let data = self.ccgx_read(ControlRegisters::PdPortsEnable, 1)?; + assert_win_len(data.len(), 1); + Ok(data[0]) + } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 637b8f3f..dab6b10f 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -227,6 +227,17 @@ fn print_single_pd_details(pd: &PdController) { } else { println!(" Failed to device info"); } + if let Ok(port_mask) = pd.get_port_status() { + let ports = match port_mask { + 1 => "0", + 2 => "1", + 3 => "0, 1", + _ => "None", + }; + println!(" Ports Enabled: {}", ports); + } else { + println!(" Ports Enabled: Unknown"); + } pd.print_fw_info(); } From 3f02cdcb805aee212b801ecf9befd970622c3fa2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 15:14:20 +0800 Subject: [PATCH 36/52] --pd-info: Bail early if PD isn't there If silicon ID can't be read then likely the PD isn't there. Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index dab6b10f..2da426ac 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -220,6 +220,7 @@ fn print_single_pd_details(pd: &PdController) { println!(" Silicon ID: 0x{:X}", si); } else { println!(" Failed to read Silicon ID/Family"); + return; } if let Ok((mode, frs)) = pd.get_device_info() { println!(" Mode: {:?}", mode); From a36356dcfccb318f207f1de672d4708aae4d329b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 15:53:41 +0800 Subject: [PATCH 37/52] Bump version to 0.4.3 Signed-off-by: Daniel Schaefer --- Cargo.lock | 6 +++--- framework_lib/Cargo.toml | 2 +- framework_tool/Cargo.toml | 2 +- framework_uefi/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f9f5c9a..f6c79ef6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,7 +408,7 @@ dependencies = [ [[package]] name = "framework_lib" -version = "0.4.2" +version = "0.4.3" dependencies = [ "built", "clap", @@ -440,7 +440,7 @@ dependencies = [ [[package]] name = "framework_tool" -version = "0.4.2" +version = "0.4.3" dependencies = [ "embed-resource", "framework_lib", @@ -451,7 +451,7 @@ dependencies = [ [[package]] name = "framework_uefi" -version = "0.4.2" +version = "0.4.3" dependencies = [ "framework_lib", "log", diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 8d362370..b3af9f25 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_lib" -version = "0.4.2" +version = "0.4.3" description = "Library to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index 8da6a35c..5af4bf0d 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_tool" -version = "0.4.2" +version = "0.4.3" description = "Tool to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" diff --git a/framework_uefi/Cargo.toml b/framework_uefi/Cargo.toml index 852fa7ec..adc8b14b 100644 --- a/framework_uefi/Cargo.toml +++ b/framework_uefi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_uefi" -version = "0.4.2" +version = "0.4.3" description = "UEFI Tool to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" From f8e4022a8f362978f68500e177691db32b7dfb88 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 29 May 2025 18:05:19 +0800 Subject: [PATCH 38/52] README: Add nix-shell Signed-off-by: Daniel Schaefer --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 38f2a2be..fc913e21 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,14 @@ make -C framework_uefi ls -l framework_uefi/build/x86_64-unknown-uefi/boot.efi ``` +### Dependencies + +``` +# NixOS +nix-shell --run fish -p cargo systemd udev hidapi pkg-config +direnv shell +``` + ## Install local package ``` From 3daf204fe965b5a87d06638f2b5f8d5352f6af2a Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 16:11:21 +0800 Subject: [PATCH 39/52] portio_mec: Don't print init Would also appear on non-mec systems if we can't detect whether they have mec or not. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/portio_mec.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/portio_mec.rs b/framework_lib/src/chromium_ec/portio_mec.rs index 774acb99..1f855d0f 100644 --- a/framework_lib/src/chromium_ec/portio_mec.rs +++ b/framework_lib/src/chromium_ec/portio_mec.rs @@ -25,7 +25,6 @@ const _MEC_LPC_DATA_REGISTER3: u16 = 0x0807; pub fn init() -> bool { #[cfg(target_os = "linux")] unsafe { - println!("Init MEC"); let res = ioperm(EC_LPC_ADDR_HOST_DATA as u64, 8, 1); if res != 0 { error!("ioperm failed. portio driver is likely block by Linux kernel lockdown mode"); From 0b0fa1d6b0128a4d79d23bf9bfcc500eb205b244 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 16:32:03 +0800 Subject: [PATCH 40/52] Correctly identify right and left PD controllers I wrote the PD code long long ago when I was confused about which PD controller was which, now I know that on every system ports 0 and 1 are on the right side, ports 1 and 2 on the left Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 4 +-- framework_lib/src/ccgx/device.rs | 28 +++++++++---------- framework_lib/src/ccgx/mod.rs | 4 +-- framework_lib/src/commandline/mod.rs | 40 ++++++++++++++-------------- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index cd7d9b25..e69817ee 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -8,7 +8,7 @@ Example on Framework 13 AMD Ryzen AI 300 ``` > sudo framework_tool.exe --pd-info -Left / Ports 01 +Right / Ports 01 Silicon ID: 0x3580 Mode: MainFw Flash Row Size: 256 B @@ -16,7 +16,7 @@ Left / Ports 01 Bootloader Version: Base: 3.6.0.009, App: 0.0.01 FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B -Right / Ports 23 +Left / Ports 23 Silicon ID: 0x3580 Mode: MainFw Flash Row Size: 256 B diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 064e4acf..bd13f6df 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -43,8 +43,8 @@ enum ControlRegisters { #[derive(Debug, PartialEq, Clone, Copy)] pub enum PdPort { - Left01, - Right23, + Right01, + Left23, Back, } @@ -58,21 +58,21 @@ impl PdPort { )); Ok(match (platform, self) { - (Platform::GenericFramework((left, _, _), _), PdPort::Left01) => *left, - (Platform::GenericFramework((_, right, _), _), PdPort::Right23) => *right, + (Platform::GenericFramework((left, _, _), _), PdPort::Right01) => *left, + (Platform::GenericFramework((_, right, _), _), PdPort::Left23) => *right, (Platform::GenericFramework((_, _, back), _), PdPort::Back) => *back, // Framework AMD Platforms (CCG8) ( Platform::Framework13Amd7080 | Platform::Framework13AmdAi300 | Platform::Framework16Amd7080, - PdPort::Left01, + PdPort::Right01, ) => 0x42, ( Platform::Framework13Amd7080 | Platform::Framework13AmdAi300 | Platform::Framework16Amd7080, - PdPort::Right23, + PdPort::Left23, ) => 0x40, (Platform::Framework16Amd7080, PdPort::Back) => 0x42, (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 0x08, @@ -84,7 +84,7 @@ impl PdPort { | Platform::IntelGen12 | Platform::IntelGen13 | Platform::IntelCoreUltra1, - PdPort::Left01, + PdPort::Right01, ) => 0x08, ( Platform::Framework12IntelGen13 @@ -92,7 +92,7 @@ impl PdPort { | Platform::IntelGen12 | Platform::IntelGen13 | Platform::IntelCoreUltra1, - PdPort::Right23, + PdPort::Left23, ) => 0x40, (Platform::UnknownSystem, _) => { Err(EcError::DeviceError("Unsupported platform".to_string()))? @@ -111,19 +111,19 @@ impl PdPort { ))); Ok(match (platform, self) { - (Platform::GenericFramework(_, (left, _, _)), PdPort::Left01) => *left, - (Platform::GenericFramework(_, (_, right, _)), PdPort::Right23) => *right, + (Platform::GenericFramework(_, (left, _, _)), PdPort::Right01) => *left, + (Platform::GenericFramework(_, (_, right, _)), PdPort::Left23) => *right, (Platform::GenericFramework(_, (_, _, back)), PdPort::Back) => *back, (Platform::IntelGen11, _) => 6, - (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Left01) => 6, - (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Right23) => 7, + (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Right01) => 6, + (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Left23) => 7, ( Platform::Framework13Amd7080 | Platform::Framework16Amd7080 | Platform::IntelCoreUltra1 | Platform::Framework13AmdAi300 | Platform::Framework12IntelGen13, - PdPort::Left01, + PdPort::Right01, ) => 1, ( Platform::Framework13Amd7080 @@ -131,7 +131,7 @@ impl PdPort { | Platform::IntelCoreUltra1 | Platform::Framework13AmdAi300 | Platform::Framework12IntelGen13, - PdPort::Right23, + PdPort::Left23, ) => 2, (Platform::Framework16Amd7080, PdPort::Back) => 5, (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 1, diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index 1dea7ddf..f7269b90 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -252,8 +252,8 @@ pub enum MainPdVersions { } pub fn get_pd_controller_versions(ec: &CrosEc) -> EcResult { - let pd01 = PdController::new(PdPort::Left01, ec.clone()).get_fw_versions(); - let pd23 = PdController::new(PdPort::Right23, ec.clone()).get_fw_versions(); + let pd01 = PdController::new(PdPort::Right01, ec.clone()).get_fw_versions(); + let pd23 = PdController::new(PdPort::Left23, ec.clone()).get_fw_versions(); let pd_back = PdController::new(PdPort::Back, ec.clone()).get_fw_versions(); match (pd01, pd23, pd_back) { diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 2da426ac..ca92266a 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -247,13 +247,13 @@ fn print_pd_details(ec: &CrosEc) { println!("Only supported on Framework systems"); return; } - let pd_01 = PdController::new(PdPort::Left01, ec.clone()); - let pd_23 = PdController::new(PdPort::Right23, ec.clone()); + let pd_01 = PdController::new(PdPort::Right01, ec.clone()); + let pd_23 = PdController::new(PdPort::Left23, ec.clone()); let pd_back = PdController::new(PdPort::Back, ec.clone()); - println!("Left / Ports 01"); + println!("Right / Ports 01"); print_single_pd_details(&pd_01); - println!("Right / Ports 23"); + println!("Left / Ports 23"); print_single_pd_details(&pd_23); println!("Back"); print_single_pd_details(&pd_back); @@ -570,13 +570,13 @@ fn print_versions(ec: &CrosEc) { | esrt::ADL_RETIMER01_GUID | esrt::RPL_RETIMER01_GUID | esrt::MTL_RETIMER01_GUID => { - left_retimer = Some(entry.fw_version); + right_retimer = Some(entry.fw_version); } esrt::TGL_RETIMER23_GUID | esrt::ADL_RETIMER23_GUID | esrt::RPL_RETIMER23_GUID | esrt::MTL_RETIMER23_GUID => { - right_retimer = Some(entry.fw_version); + left_retimer = Some(entry.fw_version); } _ => {} } @@ -1017,8 +1017,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if let Some(pd) = args.pd_reset { println!("Resetting PD {}...", pd); print_err(match pd { - 0 => PdController::new(PdPort::Left01, ec.clone()).reset_device(), - 1 => PdController::new(PdPort::Right23, ec.clone()).reset_device(), + 0 => PdController::new(PdPort::Right01, ec.clone()).reset_device(), + 1 => PdController::new(PdPort::Left23, ec.clone()).reset_device(), 2 => PdController::new(PdPort::Back, ec.clone()).reset_device(), _ => { error!("PD {} does not exist", pd); @@ -1028,8 +1028,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if let Some(pd) = args.pd_disable { println!("Disabling PD {}...", pd); print_err(match pd { - 0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(false), - 1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(false), + 0 => PdController::new(PdPort::Right01, ec.clone()).enable_ports(false), + 1 => PdController::new(PdPort::Left23, ec.clone()).enable_ports(false), 2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(false), _ => { error!("PD {} does not exist", pd); @@ -1039,8 +1039,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if let Some(pd) = args.pd_enable { println!("Enabling PD {}...", pd); print_err(match pd { - 0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(true), - 1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(true), + 0 => PdController::new(PdPort::Right01, ec.clone()).enable_ports(true), + 1 => PdController::new(PdPort::Left23, ec.clone()).enable_ports(true), 2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(true), _ => { error!("PD {} does not exist", pd); @@ -1376,8 +1376,8 @@ fn selftest(ec: &CrosEc) -> Option<()> { println!(" - OK"); } - let pd_01 = PdController::new(PdPort::Left01, ec.clone()); - let pd_23 = PdController::new(PdPort::Right23, ec.clone()); + let pd_01 = PdController::new(PdPort::Right01, ec.clone()); + let pd_23 = PdController::new(PdPort::Left23, ec.clone()); print!(" Getting PD01 info through I2C tunnel"); print_err(pd_01.get_silicon_id())?; print_err(pd_01.get_device_info())?; @@ -1554,22 +1554,22 @@ pub fn analyze_capsule(data: &[u8]) -> Option { println!(" Type: Framework RPL Insyde BIOS"); } esrt::TGL_RETIMER01_GUID => { - println!(" Type: Framework TGL Retimer01 (Left)"); + println!(" Type: Framework TGL Retimer01 (Right)"); } esrt::TGL_RETIMER23_GUID => { - println!(" Type: Framework TGL Retimer23 (Right)"); + println!(" Type: Framework TGL Retimer23 (Left)"); } esrt::ADL_RETIMER01_GUID => { - println!(" Type: Framework ADL Retimer01 (Left)"); + println!(" Type: Framework ADL Retimer01 (Right)"); } esrt::ADL_RETIMER23_GUID => { - println!(" Type: Framework ADL Retimer23 (Right)"); + println!(" Type: Framework ADL Retimer23 (Left)"); } esrt::RPL_RETIMER01_GUID => { - println!(" Type: Framework RPL Retimer01 (Left)"); + println!(" Type: Framework RPL Retimer01 (Right)"); } esrt::RPL_RETIMER23_GUID => { - println!(" Type: Framework RPL Retimer23 (Right)"); + println!(" Type: Framework RPL Retimer23 (Left)"); } esrt::WINUX_GUID => { println!(" Type: Windows UX capsule"); From f8b5f58cbd115c8721c9b1f3b0aa124ddfc4ad08 Mon Sep 17 00:00:00 2001 From: Kieran Levin Date: Thu, 5 Jun 2025 01:38:07 -0700 Subject: [PATCH 41/52] fw16: add command to write expansion bay eeprom from a file Example invocation: framework_tool.efi --flash-gpu-descriptor-file pcie_4x2.bin TEST=fail: if the write fails we will error correctly TEST=pass: the command will succeed. TEST=dump contents using ec console: i2c read I2C_PORT5 0x50 0x0000 16 00000000: 32 ac 00 00 30 00 00 00 00 00 01 00 08 00 00 00 |2...0... ........| And compare to xxd dump of source file to ensure they match. Signed-off-by: Kieran Levin --- framework_lib/src/chromium_ec/mod.rs | 45 ++++++++++++++++++++++- framework_lib/src/commandline/clap_std.rs | 7 ++++ framework_lib/src/commandline/mod.rs | 30 +++++++++++++++ framework_lib/src/commandline/uefi.rs | 9 +++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 02631e50..c768b33c 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1177,7 +1177,50 @@ impl CrosEc { Ok(result.valid) } - /// Requests console output from EC and constantly asks for more + pub fn write_ec_gpu_chunk(&self, offset: u16, data: &[u8]) -> EcResult<()> { + let result = i2c_passthrough::i2c_write(self, 5, 0x50, offset, data)?; + result.is_successful() + } + + /// Writes EC GPU descriptor to the GPU EEPROM. + pub fn set_gpu_descriptor(&self, data: &[u8]) -> EcResult<()> { + // Need to program the EEPROM 32 bytes at a time. + let chunk_size = 32; + + let chunks = data.len() / chunk_size; + for chunk_no in 0..chunks { + let offset = chunk_no * chunk_size; + // Current chunk might be smaller if it's the last + let cur_chunk_size = std::cmp::min(chunk_size, data.len() - chunk_no * chunk_size); + + if chunk_no % 100 == 0 { + println!(); + print!( + "Writing chunk {:>4}/{:>4} ({:>6}/{:>6}): X", + chunk_no, + chunks, + offset, + cur_chunk_size * chunks + ); + } else { + print!("X"); + } + + let chunk = &data[offset..offset + cur_chunk_size]; + let offset_le = (((offset as u16) << 8) & 0xff00) | (((offset as u16) >> 8) & 0x00ff); + let res = self.write_ec_gpu_chunk(offset_le, chunk); + // Don't read too fast, wait 100ms before writing more to allow for page erase/write cycle. + os_specific::sleep(100_000); + if let Err(err) = res { + println!(" Failed to write chunk: {:?}", err); + return Err(err); + } + } + println!(); + Ok(()) + } + + /// Requests recent console output from EC and constantly asks for more /// Prints the output and returns it when an error is encountered pub fn console_read(&self) -> EcResult<()> { EcRequestConsoleSnapshot {}.send_command(self)?; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 52eddbeb..bd65139e 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -270,6 +270,10 @@ struct ClapCli { /// Simulate execution of a command (e.g. --flash-ec) #[arg(long)] dry_run: bool, + + /// File to write to the gpu EEPROM + #[arg(long)] + flash_gpu_descriptor_file: Option, } /// Parse a list of commandline arguments and return the struct @@ -457,6 +461,9 @@ pub fn parse(args: &[String]) -> Cli { paginate: false, info: args.info, flash_gpu_descriptor, + flash_gpu_descriptor_file: args + .flash_gpu_descriptor_file + .map(|x| x.into_os_string().into_string().unwrap()), raw_command: vec![], } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ca92266a..80831d83 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -201,6 +201,7 @@ pub struct Cli { pub help: bool, pub info: bool, pub flash_gpu_descriptor: Option<(u8, String)>, + pub flash_gpu_descriptor_file: Option, // UEFI only pub allupdate: bool, pub paginate: bool, @@ -1220,6 +1221,34 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Ok(x) => println!("GPU Descriptor write failed with status code: {}", x), Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), } + } else if let Some(gpu_descriptor_file) = &args.flash_gpu_descriptor_file { + if let Some(PlatformFamily::Framework16) = + smbios::get_platform().and_then(Platform::which_family) + { + #[cfg(feature = "uefi")] + let data: Option> = crate::uefi::fs::shell_read_file(gpu_descriptor_file); + #[cfg(not(feature = "uefi"))] + let data = match fs::read(gpu_descriptor_file) { + 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); + let res = ec.set_gpu_descriptor(&data); + match res { + Ok(()) => println!("GPU Descriptor successfully written"), + Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), + } + } + } else { + println!("Unsupported on this platform"); + } } 0 @@ -1275,6 +1304,7 @@ Options: --console Get EC console, choose whether recent or to follow the output [possible values: recent, follow] --hash Hash a file of arbitrary data --flash-gpu-descriptor <18 DIGIT SN> Overwrite the GPU bay descriptor SN and type. + --flash-gpu-descriptor-file Write the GPU bay descriptor with a descriptor file. -f, --force Force execution of an unsafe command - may render your hardware unbootable! --dry-run Simulate execution of a command (e.g. --flash-ec) -t, --test Run self-test to check if interaction with EC is possible diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index d21938ae..a5e8c4c5 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -116,6 +116,7 @@ pub fn parse(args: &[String]) -> Cli { force: false, help: false, flash_gpu_descriptor: None, + flash_gpu_descriptor_file: None, allupdate: false, info: false, raw_command: vec![], @@ -764,6 +765,14 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--flash-gpu-descriptor-file" { + cli.flash_gpu_descriptor_file = if args.len() > i + 1 { + Some(args[i + 1].clone()) + } else { + println!("Need to provide a value for --flash_gpu_descriptor_file. PATH"); + None + }; + found_an_option = true; } } From e7fc0d96abd8ec641a0f6a8749cb001e5f8b8944 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:23:06 +0800 Subject: [PATCH 42/52] --flash-gpu-descriptor-file: Allow --dry-run Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 9 ++++++++- framework_lib/src/commandline/mod.rs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index c768b33c..e6247e26 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1183,7 +1183,11 @@ impl CrosEc { } /// Writes EC GPU descriptor to the GPU EEPROM. - pub fn set_gpu_descriptor(&self, data: &[u8]) -> EcResult<()> { + pub fn set_gpu_descriptor(&self, data: &[u8], dry_run: bool) -> EcResult<()> { + println!( + "Writing GPU EEPROM {}", + if dry_run { " (DRY RUN)" } else { "" } + ); // Need to program the EEPROM 32 bytes at a time. let chunk_size = 32; @@ -1205,6 +1209,9 @@ impl CrosEc { } else { print!("X"); } + if dry_run { + continue; + } let chunk = &data[offset..offset + cur_chunk_size]; let offset_le = (((offset as u16) << 8) & 0xff00) | (((offset as u16) >> 8) & 0x00ff); diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 80831d83..08c755e8 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -1240,7 +1240,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!("File"); println!(" Size: {:>20} B", data.len()); println!(" Size: {:>20} KB", data.len() / 1024); - let res = ec.set_gpu_descriptor(&data); + let res = ec.set_gpu_descriptor(&data, args.dry_run); match res { Ok(()) => println!("GPU Descriptor successfully written"), Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), From 0d65135be31076776750e7a104f63d2712cb758f Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:24:05 +0800 Subject: [PATCH 43/52] --flash-gpu-descriptor-file: Don't block if unknown system Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 08c755e8..ae6c1f08 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -1222,9 +1222,10 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), } } else if let Some(gpu_descriptor_file) = &args.flash_gpu_descriptor_file { - if let Some(PlatformFamily::Framework16) = - smbios::get_platform().and_then(Platform::which_family) - { + if matches!( + smbios::get_family(), + Some(PlatformFamily::Framework16) | None + ) { #[cfg(feature = "uefi")] let data: Option> = crate::uefi::fs::shell_read_file(gpu_descriptor_file); #[cfg(not(feature = "uefi"))] From 74042d268e8f1f526ae6ef1427b2822bc8e8bb05 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:36:04 +0800 Subject: [PATCH 44/52] write_ec_gpu_chunk: Use std to swap byte order X64 is LE natively, so BE must be intended. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index e6247e26..a6a2fed7 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1214,8 +1214,7 @@ impl CrosEc { } let chunk = &data[offset..offset + cur_chunk_size]; - let offset_le = (((offset as u16) << 8) & 0xff00) | (((offset as u16) >> 8) & 0x00ff); - let res = self.write_ec_gpu_chunk(offset_le, chunk); + let res = self.write_ec_gpu_chunk((offset as u16).to_be(), chunk); // Don't read too fast, wait 100ms before writing more to allow for page erase/write cycle. os_specific::sleep(100_000); if let Err(err) = res { From c8a1f957bbdf827fff6d8bea231adb8732acc03e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:45:52 +0800 Subject: [PATCH 45/52] framework_tool: Add copyright into .exe properties Windows right-click => Properties => Details => Copyright Signed-off-by: Daniel Schaefer --- framework_tool/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index 8da6a35c..4ad76b8f 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -26,3 +26,6 @@ version = "0.3.9" features = [ "wincon" ] + +[package.metadata.winresource] +LegalCopyright = "Framework Computer Inc © 2022" From 6fa84c72223e3c370a29393f9c181273a8e3d91d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:06:39 +0800 Subject: [PATCH 46/52] check_mem_magic: Return error instead of printing Let the caller decide whether or not to print the error Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 26 +++++++++++++------------- framework_lib/src/commandline/mod.rs | 3 ++- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 02631e50..fa23d2bb 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -276,25 +276,25 @@ impl CrosEc { } } - pub fn check_mem_magic(&self) -> Option<()> { + pub fn check_mem_magic(&self) -> EcResult<()> { match self.read_memory(EC_MEMMAP_ID, 2) { Some(ec_id) => { if ec_id.len() != 2 { - error!(" Unexpected length returned: {:?}", ec_id.len()); - return None; - } - if ec_id[0] != b'E' || ec_id[1] != b'C' { - error!(" This machine doesn't look like it has a Framework EC"); - None + Err(EcError::DeviceError(format!( + " Unexpected length returned: {:?}", + ec_id.len() + ))) + } else if ec_id[0] != b'E' || ec_id[1] != b'C' { + Err(EcError::DeviceError( + "This machine doesn't look like it has a Framework EC".to_string(), + )) } else { - println!(" Verified that Framework EC is present!"); - Some(()) + Ok(()) } } - None => { - error!(" Failed to read EC ID from memory map"); - None - } + None => Err(EcError::DeviceError( + "Failed to read EC ID from memory map".to_string(), + )), } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ca92266a..8b57fa77 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -1340,7 +1340,8 @@ fn selftest(ec: &CrosEc) -> Option<()> { } println!(" Checking EC memory mapped magic bytes"); - ec.check_mem_magic()?; + print_err(ec.check_mem_magic())?; + println!(" Verified that Framework EC is present!"); println!(" Reading EC Build Version"); print_err(ec.version_info())?; From 249b8f0a76f6f7292bf6bfb55a0043590dbd1b04 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:07:15 +0800 Subject: [PATCH 47/52] windows: Print a hint if no driver is available Avoid confusion on platforms where the driver is not enabled in bios and driver bundle yet. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/windows.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/framework_lib/src/chromium_ec/windows.rs b/framework_lib/src/chromium_ec/windows.rs index 5b0cca68..557ab430 100644 --- a/framework_lib/src/chromium_ec/windows.rs +++ b/framework_lib/src/chromium_ec/windows.rs @@ -14,6 +14,8 @@ use windows::{ use crate::chromium_ec::protocol::HEADER_LEN; use crate::chromium_ec::EC_MEMMAP_SIZE; use crate::chromium_ec::{EcError, EcResponseStatus, EcResult}; +use crate::smbios; +use crate::util::Platform; // Create a wrapper around HANDLE to mark it as Send. // I'm not sure, but I think it's safe to do that for this type of HANDL. @@ -46,6 +48,20 @@ fn init() -> bool { let handle = match res { Ok(h) => h, Err(err) => { + let platform = smbios::get_platform(); + match platform { + Some(platform @ Platform::IntelGen11) + | Some(platform @ Platform::IntelGen12) + | Some(platform @ Platform::IntelGen13) + | Some(platform @ Platform::Framework13Amd7080) + | Some(platform @ Platform::Framework16Amd7080) => { + println!("The windows driver is not enabled on {:?}.", platform); + println!("Please stay tuned for future BIOS and driver updates."); + println!(); + } + _ => (), + } + error!("Failed to find Windows driver. {:?}", err); return false; } From 62bdc229e55f5c0e1d41bd2bfbfffbf93278d474 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:13:26 +0800 Subject: [PATCH 48/52] --features: Don't unwrap, just print error Less verbose and doesn't crash Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 8b57fa77..05247bca 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -844,7 +844,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if args.version { print_tool_version(); } else if args.features { - ec.get_features().unwrap(); + print_err(ec.get_features()); } else if args.esrt { print_esrt(); } else if let Some(compare_version_ver) = &args.compare_version { From 85c4b2f6a89e81465bdd1fe751d1e12f639f5957 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:58:20 +0800 Subject: [PATCH 49/52] EXAMPLES: Document flashing eeprom Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index e69817ee..6882c761 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -79,3 +79,19 @@ This command has not been thoroughly tested on all Framework Computer systems # EC will boot back into RO if the system turned off for 30s > framework_tool --reboot-ec jump-rw ``` + +## Flashing Expansion Bay EEPROM (Framework 16) + +This will render your dGPU unsuable if you flash the wrong file! +It's intended for advanced users who build their own expansion bay module. +The I2C address of the EEPROM is hardcoded to 0x50. + +``` +# Update just the serial number +> framework_tool --flash_gpu_descriptor GPU FRAKMQCP41500ASSY1 +> framework_tool --flash_gpu_descriptor 13 FRAKMQCP41500ASSY1 +> framework_tool --flash_gpu_descriptor 0x0D FRAKMQCP41500ASSY1 + +# Update everything from a file +> framework_tool --flash-gpu-descriptor-file pcie_4x2.bin +``` From e8acfa7fac3ad7a5b9c8a65897987d8cbde4202d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:58:43 +0800 Subject: [PATCH 50/52] --expansion-bay: Read eeprom directly and dump extra details Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 5 +++ framework_lib/src/chromium_ec/mod.rs | 62 ++++++++++++++++++++++++++++ framework_lib/src/commandline/mod.rs | 20 +++++++++ 3 files changed, 87 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 1ea7f82c..61b4aac8 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -317,8 +317,13 @@ Expansion Bay Serial Number: FRAXXXXXXXXXXXXXXX Config: Pcie4x2 Vendor: SsdHolder + Expansion Bay EEPROM + Valid: true + HW Version: 8.0 ``` +Add `-vv` for more verbose details. + ## Check charger and battery status (Framework 12/13/16) ``` diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index a6a2fed7..a596c7cd 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1177,6 +1177,34 @@ impl CrosEc { Ok(result.valid) } + pub fn read_ec_gpu_chunk(&self, addr: u16, len: u16) -> EcResult> { + let eeprom_port = 0x05; + let eeprom_addr = 0x50; + let mut data: Vec = Vec::with_capacity(len.into()); + + while data.len() < len.into() { + let remaining = len - data.len() as u16; + let chunk_len = std::cmp::min(i2c_passthrough::MAX_I2C_CHUNK, remaining.into()); + let offset = addr + data.len() as u16; + let i2c_response = i2c_passthrough::i2c_read( + self, + eeprom_port, + eeprom_addr, + offset, + chunk_len as u16, + )?; + if let Err(EcError::DeviceError(err)) = i2c_response.is_successful() { + return Err(EcError::DeviceError(format!( + "I2C read was not successful: {:?}", + err + ))); + } + data.extend(i2c_response.data); + } + + Ok(data) + } + pub fn write_ec_gpu_chunk(&self, offset: u16, data: &[u8]) -> EcResult<()> { let result = i2c_passthrough::i2c_write(self, 5, 0x50, offset, data)?; result.is_successful() @@ -1226,6 +1254,15 @@ impl CrosEc { Ok(()) } + pub fn read_gpu_desc_header(&self) -> EcResult { + let bytes = + self.read_ec_gpu_chunk(0x00, core::mem::size_of::() as u16)?; + let header: *const GpuCfgDescriptor = unsafe { std::mem::transmute(bytes.as_ptr()) }; + let header = unsafe { *header }; + + Ok(header) + } + /// Requests recent console output from EC and constantly asks for more /// Prints the output and returns it when an error is encountered pub fn console_read(&self) -> EcResult<()> { @@ -1633,3 +1670,28 @@ pub struct IntrusionStatus { /// That means we only know if it was opened at least once, while off, not how many times. pub vtr_open_count: u8, } + +#[derive(Clone, Debug, Copy, PartialEq)] +#[repr(C, packed)] +pub struct GpuCfgDescriptor { + /// Expansion bay card magic value that is unique + pub magic: [u8; 4], + /// Length of header following this field + pub length: u32, + /// descriptor version, if EC max version is lower than this, ec cannot parse + pub desc_ver_major: u16, + pub desc_ver_minor: u16, + /// Hardware major version + pub hardware_version: u16, + /// Hardware minor revision + pub hardware_revision: u16, + /// 18 digit Framework Serial that starts with FRA + /// the first 10 digits must be allocated by framework + pub serial: [u8; 20], + /// Length of descriptor following heade + pub descriptor_length: u32, + /// CRC of descriptor + pub descriptor_crc32: u32, + /// CRC of header before this value + pub crc32: u32, +} diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ae6c1f08..a7a10111 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -884,6 +884,26 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { if let Err(err) = ec.check_bay_status() { error!("{:?}", err); } + if let Ok(header) = ec.read_gpu_desc_header() { + println!(" Expansion Bay EEPROM"); + println!( + " Valid: {:?}", + header.magic == [0x32, 0xAC, 0x00, 0x00] + ); + println!(" HW Version: {}.{}", { header.hardware_version }, { + header.hardware_revision + }); + if log_enabled!(Level::Info) { + println!(" Hdr Length {} B", { header.length }); + println!(" Desc Ver: {}.{}", { header.desc_ver_major }, { + header.desc_ver_minor + }); + println!(" Serialnumber:{:X?}", { header.serial }); + println!(" Desc Length: {} B", { header.descriptor_length }); + println!(" Desc CRC: {:X}", { header.descriptor_crc32 }); + println!(" Hdr CRC: {:X}", { header.crc32 }); + } + } } else if let Some(maybe_limit) = args.charge_limit { print_err(handle_charge_limit(&ec, maybe_limit)); } else if let Some((limit, soc)) = args.charge_current_limit { From d13b41b3fa6154eb93899c53638c5b9a3180ffb1 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 19:12:11 +0800 Subject: [PATCH 51/52] Add --dump-gpu-descriptor-file Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 5 +++++ framework_lib/src/chromium_ec/mod.rs | 10 ++++++++++ framework_lib/src/commandline/clap_std.rs | 7 +++++++ framework_lib/src/commandline/mod.rs | 22 ++++++++++++++++++++++ framework_lib/src/commandline/uefi.rs | 9 +++++++++ 5 files changed, 53 insertions(+) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index 6882c761..f8fb2172 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -87,6 +87,11 @@ It's intended for advanced users who build their own expansion bay module. The I2C address of the EEPROM is hardcoded to 0x50. ``` +# Dump current descriptor (e.g. for backup) +> framework_tool --dump-gpu-descriptor-file foo.bin +Dumping to foo.bin +Wrote 153 bytes to foo.bin + # Update just the serial number > framework_tool --flash_gpu_descriptor GPU FRAKMQCP41500ASSY1 > framework_tool --flash_gpu_descriptor 13 FRAKMQCP41500ASSY1 diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index a596c7cd..22e7134e 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1254,6 +1254,16 @@ impl CrosEc { Ok(()) } + pub fn read_gpu_descriptor(&self) -> EcResult> { + let header = self.read_gpu_desc_header()?; + if header.magic != [0x32, 0xAC, 0x00, 0x00] { + return Err(EcError::DeviceError( + "Invalid descriptor hdr magic".to_string(), + )); + } + self.read_ec_gpu_chunk(0x00, header.descriptor_length as u16) + } + pub fn read_gpu_desc_header(&self) -> EcResult { let bytes = self.read_ec_gpu_chunk(0x00, core::mem::size_of::() as u16)?; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index bd65139e..148042b2 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -274,6 +274,10 @@ struct ClapCli { /// File to write to the gpu EEPROM #[arg(long)] flash_gpu_descriptor_file: Option, + + /// File to dump the gpu EEPROM to + #[arg(long)] + dump_gpu_descriptor_file: Option, } /// Parse a list of commandline arguments and return the struct @@ -464,6 +468,9 @@ pub fn parse(args: &[String]) -> Cli { flash_gpu_descriptor_file: args .flash_gpu_descriptor_file .map(|x| x.into_os_string().into_string().unwrap()), + dump_gpu_descriptor_file: args + .dump_gpu_descriptor_file + .map(|x| x.into_os_string().into_string().unwrap()), raw_command: vec![], } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index a7a10111..11a64cce 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -202,6 +202,7 @@ pub struct Cli { pub info: bool, pub flash_gpu_descriptor: Option<(u8, String)>, pub flash_gpu_descriptor_file: Option, + pub dump_gpu_descriptor_file: Option, // UEFI only pub allupdate: bool, pub paginate: bool, @@ -680,6 +681,24 @@ fn dump_ec_flash(ec: &CrosEc, dump_path: &str) { } } +fn dump_dgpu_eeprom(ec: &CrosEc, dump_path: &str) { + let flash_bin = ec.read_gpu_descriptor().unwrap(); + + #[cfg(not(feature = "uefi"))] + { + let mut file = fs::File::create(dump_path).unwrap(); + file.write_all(&flash_bin).unwrap(); + } + #[cfg(feature = "uefi")] + { + let ret = crate::uefi::fs::shell_write_file(dump_path, &flash_bin); + if ret.is_err() { + println!("Failed to dump EC FW image."); + } + } + println!("Wrote {} bytes to {}", flash_bin.len(), dump_path); +} + fn compare_version(device: Option, version: String, ec: &CrosEc) -> i32 { println!("Target Version {:?}", version); @@ -1270,6 +1289,9 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else { println!("Unsupported on this platform"); } + } else if let Some(dump_path) = &args.dump_gpu_descriptor_file { + println!("Dumping to {}", dump_path); + dump_dgpu_eeprom(&ec, dump_path); } 0 diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index a5e8c4c5..82d6c253 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -117,6 +117,7 @@ pub fn parse(args: &[String]) -> Cli { help: false, flash_gpu_descriptor: None, flash_gpu_descriptor_file: None, + dump_gpu_descriptor_file: None, allupdate: false, info: false, raw_command: vec![], @@ -773,6 +774,14 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--dump-gpu-descriptor-file" { + cli.dump_gpu_descriptor_file = if args.len() > i + 1 { + Some(args[i + 1].clone()) + } else { + println!("Need to provide a value for --dump_gpu_descriptor_file. PATH"); + None + }; + found_an_option = true; } } From 00f7100946f875a7919ecf13a00c0e04688db116 Mon Sep 17 00:00:00 2001 From: Josh Vander Hook Date: Thu, 5 Jun 2025 10:00:20 -0500 Subject: [PATCH 52/52] commandline: Fix docstring for --remap-key Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/clap_std.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 52eddbeb..c744ad31 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -193,7 +193,7 @@ struct ClapCli { #[arg(long)] kblight: Option>, - /// Set keyboard backlight percentage or get, if no value provided + /// Remap a key by changing the scancode #[arg(long, value_parser=maybe_hex::)] #[clap(num_args = 3)] remap_key: Vec,