From f65f1dc8d105918f18614c063d38e813d386478a Mon Sep 17 00:00:00 2001 From: Kieran Levin Date: Mon, 29 Apr 2024 21:59:14 -0700 Subject: [PATCH 1/5] create version check function This will check if versions match and return 0 if match or 1 if not match. This can run in UEFI shell etc. Signed-off-by: Kieran Levin --- framework_lib/src/chromium_ec/mod.rs | 11 +++ framework_lib/src/commandline/clap_std.rs | 15 +++- framework_lib/src/commandline/mod.rs | 92 ++++++++++++++++++++++- framework_lib/src/commandline/uefi.rs | 35 ++++++++- framework_lib/src/power.rs | 6 +- framework_tool/src/main.rs | 7 +- framework_uefi/src/main.rs | 4 +- 7 files changed, 162 insertions(+), 8 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 265f5d5..e5a04e3 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -864,6 +864,17 @@ pub enum CrosEcDriverType { Windows, } +#[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))] +#[derive(Clone, Debug, Copy, PartialEq)] +pub enum HardwareDeviceType { + BIOS, + EC, + PD0, + PD1, + RTM01, + RTM23, +} + impl CrosEcDriver for CrosEc { fn read_memory(&self, offset: u16, length: u16) -> Option> { if !smbios::is_framework() { diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index c12e9b4..54f8ba9 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -4,7 +4,9 @@ use clap::Parser; use crate::chromium_ec::CrosEcDriverType; -use crate::commandline::{Cli, ConsoleArg, FpBrightnessArg, InputDeckModeArg, RebootEcArg}; +use crate::commandline::{ + Cli, ConsoleArg, FpBrightnessArg, HardwareDeviceType, InputDeckModeArg, RebootEcArg, +}; /// Swiss army knife for Framework laptops #[derive(Parser)] @@ -25,6 +27,15 @@ struct ClapCli { #[arg(long)] esrt: bool, + // Device type to compare_version string with version string on device + #[clap(value_enum)] + #[arg(long)] + device: Option, + + // version to compare with + #[arg(long)] + compare_version: Option, + /// Show current power status of battery and AC (Add -vv for more details) #[arg(long)] power: bool, @@ -198,6 +209,8 @@ pub fn parse(args: &[String]) -> Cli { versions: args.versions, version: args.version, esrt: args.esrt, + device: args.device, + compare_version: args.compare_version, power: args.power, thermal: args.thermal, sensors: args.sensors, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 591014b..e301da3 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -54,7 +54,7 @@ use sha2::{Digest, Sha256, Sha384, Sha512}; //use smbioslib::*; use smbioslib::{DefinedStruct, SMBiosInformation}; -use crate::chromium_ec::{CrosEc, CrosEcDriverType}; +use crate::chromium_ec::{CrosEc, CrosEcDriverType, HardwareDeviceType}; #[cfg(feature = "uefi")] use core::prelude::rust_2021::derive; @@ -120,6 +120,8 @@ pub struct Cli { pub versions: bool, pub version: bool, pub esrt: bool, + pub device: Option, + pub compare_version: Option, pub power: bool, pub thermal: bool, pub sensors: bool, @@ -506,6 +508,86 @@ fn dump_ec_flash(ec: &CrosEc, dump_path: &str) { } } +fn compare_version(device: Option, version: String, ec: &CrosEc) -> i32 { + println!("Target Version {:?}", version); + + if let Some(smbios) = get_smbios() { + let bios_entries = smbios.collect::(); + let bios = bios_entries.get(0).unwrap(); + + if device == Some(HardwareDeviceType::BIOS) { + println!("Comparing BIOS version {:?}", bios.version().to_string()); + if version.to_uppercase() == bios.version().to_string().to_uppercase() { + return 0; + } else { + return 1; + } + } + } + + if device == Some(HardwareDeviceType::EC) { + let ver = print_err(ec.version_info()).unwrap_or_else(|| "UNKNOWN".to_string()); + println!("Comparing EC version {:?}", ver); + + if ver.contains(&version) { + return 0; + } else { + return 1; + } + } + if device == Some(HardwareDeviceType::PD0) { + if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { + let ver = pd_versions.controller01.main_fw.app.to_string(); + println!("Comparing PD0 version {:?}", ver); + + if ver.contains(&version) { + return 0; + } else { + return 1; + } + } + } + if device == Some(HardwareDeviceType::PD1) { + if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { + let ver = pd_versions.controller23.main_fw.app.to_string(); + println!("Comparing PD1 version {:?}", ver); + + if ver.contains(&version) { + return 0; + } else { + return 1; + } + } + } + + if let Some(esrt) = esrt::get_esrt() { + for entry in &esrt.entries { + match entry.fw_class { + esrt::TGL_RETIMER01_GUID | esrt::ADL_RETIMER01_GUID | esrt::RPL_RETIMER01_GUID => { + if device == Some(HardwareDeviceType::RTM01) { + println!("Comparing RTM01 version {:?}", entry.fw_version.to_string()); + + if entry.fw_version.to_string().contains(&version) { + return 0; + } + } + } + esrt::TGL_RETIMER23_GUID | esrt::ADL_RETIMER23_GUID | esrt::RPL_RETIMER23_GUID => { + if device == Some(HardwareDeviceType::RTM23) { + println!("Comparing RTM23 version {:?}", entry.fw_version.to_string()); + if entry.fw_version.to_string().contains(&version) { + return 0; + } + } + } + _ => {} + } + } + } + + 1 +} + pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { #[cfg(feature = "uefi")] { @@ -563,6 +645,10 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { print_tool_version(); } else if args.esrt { print_esrt(); + } else if let Some(compare_version_ver) = &args.compare_version { + let compare_ret = compare_version(args.device, compare_version_ver.to_string(), &ec); + println!("Compared version: {}", compare_ret); + return compare_ret; } else if args.intrusion { println!("Chassis status:"); if let Some(status) = print_err(ec.get_intrusion_status()) { @@ -653,7 +739,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { return 1; } } else if args.power { - power::get_and_print_power_info(&ec); + return power::get_and_print_power_info(&ec); } else if args.thermal { power::print_thermal(&ec); } else if args.sensors { @@ -835,6 +921,8 @@ Options: --versions List current firmware versions --version Show tool version information (Add -vv for more detailed information) --esrt Display the UEFI ESRT table + --device Device used to compare firmware version [possible values: bios, ec, pd0, pd1, rtm01, rtm23] + --compare-version Version string used to match firmware version (use with --device) --power Show current power status (battery and AC) --thermal Print thermal information (Temperatures and Fan speed) --sensors Print sensor information (ALS, G-Sensor) diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 4d230bf..b2e381f 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -9,7 +9,7 @@ use uefi::proto::shell_params::*; use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams, SearchType}; use uefi::Identify; -use crate::chromium_ec::CrosEcDriverType; +use crate::chromium_ec::{CrosEcDriverType, HardwareDeviceType}; use crate::commandline::Cli; use super::{ConsoleArg, FpBrightnessArg, InputDeckModeArg, RebootEcArg}; @@ -58,6 +58,8 @@ pub fn parse(args: &[String]) -> Cli { versions: false, version: false, esrt: false, + device: None, + compare_version: None, power: false, thermal: false, sensors: false, @@ -403,6 +405,37 @@ pub fn parse(args: &[String]) -> Cli { found_an_option = true; } else if arg == "--raw-command" { cli.raw_command = args[1..].to_vec(); + } else if arg == "--compare-version" { + cli.compare_version = if args.len() > i + 1 { + Some(args[i + 1].clone()) + } else { + println!("--compare-version requires extra argument to denote version"); + None + }; + found_an_option = true; + } else if arg == "--device" { + cli.device = if args.len() > i + 1 { + let console_arg = &args[i + 1]; + if console_arg == "bios" { + Some(HardwareDeviceType::BIOS) + } else if console_arg == "ec" { + Some(HardwareDeviceType::EC) + } else if console_arg == "pd0" { + Some(HardwareDeviceType::PD0) + } else if console_arg == "pd1" { + Some(HardwareDeviceType::PD1) + } else if console_arg == "rtm01" { + Some(HardwareDeviceType::RTM01) + } else if console_arg == "rtm23" { + Some(HardwareDeviceType::RTM23) + } else { + println!("Invalid value for --device: {}", console_arg); + None + } + } else { + println!("Need to provide a value for --console. Either `follow` or `recent`"); + None + }; } } diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index 8b9b64d..286399d 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -306,10 +306,14 @@ pub fn is_standalone(ec: &CrosEc) -> bool { } } -pub fn get_and_print_power_info(ec: &CrosEc) { +pub fn get_and_print_power_info(ec: &CrosEc) -> i32 { if let Some(power_info) = power_info(ec) { print_battery_information(&power_info); + if let Some(_battery) = &power_info.battery { + return 0; + } } + 1 } fn print_battery_information(power_info: &PowerInfo) { diff --git a/framework_tool/src/main.rs b/framework_tool/src/main.rs index d152ccc..3a4d54a 100644 --- a/framework_tool/src/main.rs +++ b/framework_tool/src/main.rs @@ -5,7 +5,10 @@ fn get_args() -> Vec { std::env::args().collect() } -fn main() { +fn main() -> Result<(), &'static str> { let args = commandline::parse(&get_args()); - commandline::run_with_args(&args, false); + if (commandline::run_with_args(&args, false)) != 0 { + return Err("Fail"); + } + Ok(()) } diff --git a/framework_uefi/src/main.rs b/framework_uefi/src/main.rs index 75f5290..b81cbe6 100644 --- a/framework_uefi/src/main.rs +++ b/framework_uefi/src/main.rs @@ -18,7 +18,9 @@ fn main(_handle: Handle, mut system_table: SystemTable) -> Status { let args = commandline::uefi::get_args(bs); let args = commandline::parse(&args); - commandline::run_with_args(&args, false); + if commandline::run_with_args(&args, false) == 0 { + return Status::SUCCESS; + } // Force it go into UEFI shell Status::LOAD_ERROR From 3243cb77090a7f6fb871e00560980a02a0173f1d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 Aug 2024 17:00:13 +0800 Subject: [PATCH 2/5] compare-version: Use correct PD version Make sure it's the active one, and use base version on TGL. Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/mod.rs | 32 ++++++++++++++++++++++++---- framework_lib/src/commandline/mod.rs | 4 ++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index 70ab8f4..46cc515 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -1,11 +1,15 @@ //! Interact with Infineon (formerly Cypress) PD controllers (their firmware binaries) in the CCGx series +use alloc::string::String; +use alloc::string::ToString; #[cfg(feature = "uefi")] use core::prelude::rust_2021::derive; use num_derive::FromPrimitive; use std::fmt; use crate::chromium_ec::{CrosEc, EcResult}; +use crate::smbios; +use crate::util::Platform; use self::device::{FwMode, PdController, PdPort}; @@ -102,7 +106,7 @@ pub enum SiliconId { Ccg8 = 0x3580, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] pub struct BaseVersion { /// Major part of the version. X of X.Y.Z.BB pub major: u8, @@ -138,7 +142,7 @@ impl From for BaseVersion { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum Application { Notebook, Monitor, @@ -146,7 +150,7 @@ pub enum Application { Invalid, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] pub struct AppVersion { pub application: Application, /// Major part of the version. X of X.Y.Z @@ -185,7 +189,7 @@ impl From for AppVersion { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] pub struct ControllerVersion { pub base: BaseVersion, pub app: AppVersion, @@ -199,6 +203,26 @@ pub struct ControllerFirmwares { pub main_fw: ControllerVersion, } +impl ControllerFirmwares { + pub fn active_fw(&self) -> ControllerVersion { + match self.active_fw { + FwMode::MainFw => self.main_fw, + FwMode::BackupFw => self.backup_fw, + FwMode::BootLoader => self.bootloader, + } + } + + pub fn active_fw_ver(&self) -> String { + let active = self.active_fw(); + // On 11th Gen we modified base version instead of app version + if let Some(Platform::IntelGen11) = smbios::get_platform() { + active.base.to_string() + } else { + active.app.to_string() + } + } +} + #[derive(Debug, PartialEq)] pub struct PdVersions { pub controller01: ControllerFirmwares, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index e301da3..e5dfaed 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -537,7 +537,7 @@ fn compare_version(device: Option, version: String, ec: &Cro } if device == Some(HardwareDeviceType::PD0) { if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let ver = pd_versions.controller01.main_fw.app.to_string(); + let ver = pd_versions.controller01.active_fw_ver(); println!("Comparing PD0 version {:?}", ver); if ver.contains(&version) { @@ -549,7 +549,7 @@ fn compare_version(device: Option, version: String, ec: &Cro } if device == Some(HardwareDeviceType::PD1) { if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let ver = pd_versions.controller23.main_fw.app.to_string(); + let ver = pd_versions.controller23.active_fw_ver(); println!("Comparing PD1 version {:?}", ver); if ver.contains(&version) { From 2b93a9b304e92fc6109cf16e6115d9156fc481e4 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 Aug 2024 17:04:09 +0800 Subject: [PATCH 3/5] framework_lib: Add some tracing Signed-off-by: Daniel Schaefer --- framework_lib/src/smbios.rs | 3 +++ framework_lib/src/util.rs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/framework_lib/src/smbios.rs b/framework_lib/src/smbios.rs index 0981dfb..9c0ab1c 100644 --- a/framework_lib/src/smbios.rs +++ b/framework_lib/src/smbios.rs @@ -126,6 +126,7 @@ pub struct Smbios { #[cfg(target_os = "freebsd")] pub fn get_smbios() -> Option { + trace!("get_smbios() FreeBSD entry"); // Get the SMBIOS entrypoint address from the kernel environment let addr_hex = kenv_get("hint.smbios.0.mem").ok()?; let addr_hex = addr_hex.trim_start_matches("0x"); @@ -189,6 +190,7 @@ pub fn get_smbios() -> Option { #[cfg(feature = "uefi")] pub fn get_smbios() -> Option { + trace!("get_smbios() uefi entry"); let data = crate::uefi::smbios_data().unwrap(); let version = None; // TODO: Maybe add the version here let smbios = SMBiosData::from_vec_and_version(data, version); @@ -198,6 +200,7 @@ pub fn get_smbios() -> Option { // On Windows from the kernel API #[cfg(all(not(feature = "uefi"), not(target_os = "freebsd")))] pub fn get_smbios() -> Option { + trace!("get_smbios() linux entry"); match smbioslib::table_load_from_device() { Ok(data) => Some(data), Err(ref e) if e.kind() == ErrorKind::PermissionDenied => { diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index e903c01..e00d6de 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -64,6 +64,7 @@ impl Config { } pub fn get() -> MutexGuard<'static, Option> { + trace!("Config::get() entry"); let unset = { #[cfg(feature = "std")] let config = CONFIG.lock().unwrap(); @@ -88,6 +89,7 @@ impl Config { let mut config = CONFIG.lock(); if new_config.is_some() { + trace!("Config::get() initializing"); *config = new_config; } From 8935ba289b1890ed10337f1eb33238aac43025c4 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 Aug 2024 17:07:44 +0800 Subject: [PATCH 4/5] compare-version: Add example in README Signed-off-by: Daniel Schaefer --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 80ad816..0e186e3 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,38 @@ own EC firmware and flash it. - [x] Basic unit tests - [x] Test parsing real binaries +## Version Check + +Check if the firmware version is what you expect, returns exit code 0 on +succcess, 1 on failure. + +``` +# Check which devices it's available for +> ./framework_system --device + [possible values: bios, ec, pd0, pd1, rtm01, rtm23] + +For more information try '--help' + +# Successful compare +> ./framework_system --device bios --compare-version 03.01 +Target Version "03.01" +Comparing BIOS version "03.01" +Compared version: 0 +> echo $? +0 + +# Failed compare +> ./framework_system --device bios --compare-version 03.00 + Finished dev [unoptimized + debuginfo] target(s) in 0.05s +Target Version "03.00" +Comparing BIOS version "03.01" +Compared version: 1 +Error: "Fail" + +> echo $? +1 +``` + ## Debugging To debug, increase the verbosity from the commandline with `-v`. From 69c97b0ed89b3f27a781eca0c06c13a5f111fecb Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 9 Aug 2024 10:32:27 +0800 Subject: [PATCH 5/5] compare_version: Allow checking ac charging state Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 2 + framework_lib/src/commandline/mod.rs | 80 ++++++++++++++++++++-------- framework_lib/src/power.rs | 1 + 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index e5a04e3..067a3e2 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -873,6 +873,8 @@ pub enum HardwareDeviceType { PD1, RTM01, RTM23, + AcLeft, + AcRight, } impl CrosEcDriver for CrosEc { diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index e5dfaed..471ec07 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -3,6 +3,7 @@ //! Can be easily re-used from any OS or UEFI shell. //! We have implemented both in the `framework_tool` and `framework_uefi` crates. +use alloc::format; use alloc::string::String; use alloc::string::ToString; use alloc::vec::Vec; @@ -525,20 +526,10 @@ fn compare_version(device: Option, version: String, ec: &Cro } } - if device == Some(HardwareDeviceType::EC) { - let ver = print_err(ec.version_info()).unwrap_or_else(|| "UNKNOWN".to_string()); - println!("Comparing EC version {:?}", ver); - - if ver.contains(&version) { - return 0; - } else { - return 1; - } - } - if device == Some(HardwareDeviceType::PD0) { - if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let ver = pd_versions.controller01.active_fw_ver(); - println!("Comparing PD0 version {:?}", ver); + match device { + Some(HardwareDeviceType::EC) => { + let ver = print_err(ec.version_info()).unwrap_or_else(|| "UNKNOWN".to_string()); + println!("Comparing EC version {:?}", ver); if ver.contains(&version) { return 0; @@ -546,18 +537,61 @@ fn compare_version(device: Option, version: String, ec: &Cro return 1; } } - } - if device == Some(HardwareDeviceType::PD1) { - if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let ver = pd_versions.controller23.active_fw_ver(); - println!("Comparing PD1 version {:?}", ver); - - if ver.contains(&version) { - return 0; + Some(HardwareDeviceType::PD0) => { + if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { + let ver = pd_versions.controller01.active_fw_ver(); + println!("Comparing PD0 version {:?}", ver); + + if ver.contains(&version) { + return 0; + } else { + return 1; + } + } + } + Some(HardwareDeviceType::PD1) => { + if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { + let ver = pd_versions.controller23.active_fw_ver(); + println!("Comparing PD1 version {:?}", ver); + + if ver.contains(&version) { + return 0; + } else { + return 1; + } + } + } + Some(HardwareDeviceType::AcLeft) => { + if let Ok((_right, left)) = power::is_charging(ec) { + let ver = format!("{}", left as i32); + println!("Comparing AcLeft {:?}", ver); + if ver == version { + return 0; + } else { + return 1; + } } else { + error!("Failed to get charging information"); + // Not charging is the safe default return 1; } } + Some(HardwareDeviceType::AcRight) => { + if let Ok((right, _left)) = power::is_charging(ec) { + let ver = format!("{}", right as i32); + println!("Comparing AcRight {:?}", ver); + if ver == version { + return 0; + } else { + return 1; + } + } else { + error!("Failed to get charging information"); + // Not charging is the safe default + return 1; + } + } + _ => {} } if let Some(esrt) = esrt::get_esrt() { @@ -647,7 +681,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { print_esrt(); } else if let Some(compare_version_ver) = &args.compare_version { let compare_ret = compare_version(args.device, compare_version_ver.to_string(), &ec); - println!("Compared version: {}", compare_ret); + println!("Comparison Result: {}", compare_ret); return compare_ret; } else if args.intrusion { println!("Chassis status:"); diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index 286399d..13e8a8e 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -560,6 +560,7 @@ pub fn get_and_print_pd_info(ec: &CrosEc) { } // TODO: Improve return type to be more obvious +// (right, left) pub fn is_charging(ec: &CrosEc) -> EcResult<(bool, bool)> { let port0 = check_ac(ec, 0)?.role == UsbPowerRoles::Sink; let port1 = check_ac(ec, 1)?.role == UsbPowerRoles::Sink;