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, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy