From 3c908f37ae9d9969264231c497bbf7c2f88f654e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 5 Jan 2026 16:36:56 +0800 Subject: [PATCH 1/3] uefi: Fix --ec-hib-delay Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/uefi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index afb5dea5..6ad46bdd 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -476,7 +476,7 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; - } else if arg == "--reboot-ec" { + } else if arg == "--ec-hib-delay" { cli.ec_hib_delay = if args.len() > i + 1 { if let Ok(delay) = args[i + 1].parse::() { if delay == 0 { From 6066337f9c739b7b9552473ef6f67a5ccab0db69 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 5 Jan 2026 16:37:40 +0800 Subject: [PATCH 2/3] Add host command EC_CMD_GET_UPTIME_INFO Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 126 ++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 44 ++++++++ framework_lib/src/util.rs | 9 ++ 4 files changed, 180 insertions(+) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index f1d994a2..56e51664 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -52,6 +52,7 @@ pub enum EcCommands { RebootEc = 0x00D2, /// Get information about PD controller power UsbPdPowerInfo = 0x0103, + GetUptimeInfo = 0x0121, AdcRead = 0x0123, ApReset = 0x0125, LocateChip = 0x0126, diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index a7782146..1984d46c 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -863,6 +863,132 @@ impl EcRequest for EcRequestUsbPdPowerInfo { } } +#[repr(usize)] +#[derive(Debug, FromPrimitive)] +pub enum EcResetFlag { + /// Other known reason + Other, + /// Reset pin asserted + ResetPin, + /// Brownout + Brownout, + /// Power-on reset + PowerOn, + /// Watchdog timer reset + Watchdog, + /// Soft reset trigger by core + Soft, + /// Wake from hibernate + Hibernate, + /// RTC alarm wake + RtcAlarm, + /// Wake pin triggered wake + WakePin, + /// Low battery triggered wake + LowBattery, + /// Jumped directly to this image + Sysjump, + /// Hard reset from software + Hard, + /// Do not power on AP + APOff, + /// Some reset flags preserved from previous boot + Preserved, + /// USB resume triggered wake + UsbResume, + /// USB Type-C debug cable + Rdd, + /// Fixed Reset Functionality + Rbox, + /// Security threat + Security, + /// AP experienced a watchdog reset + ApWatchdog, + /// Do not select RW in EFS. This enables PD in RO for Chromebox + StayInRo, + /// Jumped to this image by EFS + Efs, + /// Leave alone AP + ApIdle, + /// EC had power, then was reset + InitialPwr, + Count, +} + +#[repr(u16)] +#[derive(Debug, FromPrimitive)] +pub enum ResetCause { + ResetUnknown = 0x0000, + /// Custom reason defined by a board.c or baseboard.c file + ResetBoardCustom, + /// Believe that the AP has hung + ResetHangReboot, + /// Reset by EC console command + ResetConsoleCommand, + /// Reset by EC host command + ResetHostCommand, + /// Keyboard module reset key combination + ResetKeyboardSysReset, + /// Keyboard module warm reboot + ResetKeyboardWarmBoot, + /// Debug module warm reboot + ResetDebugWarmBoot, + /// I cannot self-terminate. You must lower me into the steel + ResetApReq, + /// Reset as side-effect of startup sequence + ResetInit, + /// EC detected an AP watchdog event + ResetApWatchdog, + + ShutdownPowerFail = 0x8000, + /// Forcing a shutdown as part of EC initialization + ShutdownInit, + /// Custom reason on a per-board basis. + ShutdownBoardCustom, + /// This is a reason to inhibit startup, not cause shut down. + ShutdownBatteryInhibit, + /// A power_wait_signal is being asserted + ShutdownWait, + /// Critical battery level. + ShutdownBatteryCritical, + /// Because you told me to. + ShutdownConsoleCommand, + /// Forcing a shutdown to effect entry to G3. + ShutdownG3, + /// Force shutdown due to over-temperature. + ShutdownThermal, + /// Force a chipset shutdown from the power button through EC + ShutdownButton, +} + +#[repr(C)] +pub struct EcRequestGetUptimeInfo {} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +pub struct ApResetLogEntry { + pub reset_cause: u16, + pub reserved: u16, + pub reset_time_ms: u32, +} + +#[repr(C, packed)] +#[derive(Debug)] +pub struct EcResponseGetUptimeInfo { + pub time_since_ec_boot: u32, + /// How often the AP was reset by the EC since last EC boot + /// The first AP boot may count as more than one + pub ap_resets_since_ec_boot: u32, + pub ec_reset_flags: u32, + pub recent_ap_resets: [ApResetLogEntry; 4], +} + +impl EcRequest for EcRequestGetUptimeInfo { + fn command_id() -> EcCommands { + EcCommands::GetUptimeInfo + } +} + #[repr(C, packed)] pub struct EcRequestAdcRead { /// ADC Channel, specific to each mainboard schematic diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 40295c06..fb59e08d 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -16,6 +16,8 @@ use crate::smbios; use crate::uefi::shell_get_execution_break_flag; use crate::util::{self, Platform}; +use no_std_compat::time::Duration; + use log::Level; use num_derive::FromPrimitive; @@ -1670,6 +1672,48 @@ impl CrosEc { } Ok(()) } + + pub fn get_uptime_info(&self) -> EcResult<()> { + let res = EcRequestGetUptimeInfo {}.send_command(self)?; + let t_since_boot = Duration::from_millis(res.time_since_ec_boot.into()); + println!("EC Uptime"); + println!( + " Time since EC Boot: {}", + util::format_duration(&t_since_boot) + ); + println!(" AP Resets since EC Boot: {}", { + res.ap_resets_since_ec_boot + }); + println!(" EC Reset Flags"); + for flag in 0..(EcResetFlag::Count as usize) { + if ((1 << flag) & res.ec_reset_flags) > 0 { + // Safe to unwrap unless coding mistake + println!( + " {:?}", + ::from_usize(flag).unwrap() + ); + } + } + println!(" Recent AP Resets"); + for reset in res.recent_ap_resets { + if reset.reset_time_ms == 0 { + // Empty entry + continue; + } + let reset_time = Duration::from_millis(reset.reset_time_ms.into()); + println!( + " Reset Time: {}", + util::format_duration(&reset_time) + ); + let reset_cause: Option = FromPrimitive::from_u16(reset.reset_cause); + if let Some(cause) = reset_cause { + println!(" Cause: {:?}", cause); + } else { + println!(" Cause: Unknown"); + } + } + Ok(()) + } } #[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))] diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index ae966209..cdae531d 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -13,6 +13,8 @@ use spin::{Mutex, MutexGuard}; #[cfg(not(feature = "uefi"))] use std::sync::{Arc, Mutex, MutexGuard}; +use no_std_compat::time::Duration; + use crate::smbios; #[derive(Debug, PartialEq, Clone, Copy)] @@ -264,3 +266,10 @@ pub fn assert_win_len(left: N, #[cfg(not(windows))] assert_eq!(left, right); } + +pub fn format_duration(duration: &Duration) -> String { + let hours = duration.as_secs() / 3600; + let minutes = (duration.as_secs() % 3600) / 60; + let seconds = duration.as_secs() % 60; + format!("{:02}h {:02}m {:02}s", hours, minutes, seconds) +} From 898e6d1cbb2839075b897b61f1c8b67adeb52387 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 5 Jan 2026 16:38:10 +0800 Subject: [PATCH 3/3] commandline: Add --uptimeinfo To see when the EC and APU has reset, how often and why. > sudo framework_tool --uptimeinfo EC Uptime Time since EC Boot: 71h 06m 12s AP Resets since EC Boot: 2 EC Reset Flags PowerOn Hibernate Recent AP Resets Reset Time: 64h 34m 11s Cause: ShutdownG3 Reset Time: 65h 04m 05s Cause: ShtdownThermal Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/clap_std.rs | 4 ++++ framework_lib/src/commandline/mod.rs | 4 ++++ framework_lib/src/commandline/uefi.rs | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index d7f03391..1fbc8ad8 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -240,6 +240,9 @@ struct ClapCli { #[arg(long)] ec_hib_delay: Option>, + #[arg(long)] + uptimeinfo: bool, + /// Hash a file of arbitrary data #[arg(long)] hash: Option, @@ -454,6 +457,7 @@ pub fn parse(args: &[String]) -> Cli { console: args.console, reboot_ec: args.reboot_ec, ec_hib_delay: args.ec_hib_delay, + uptimeinfo: args.uptimeinfo, hash: args.hash.map(|x| x.into_os_string().into_string().unwrap()), driver: args.driver, pd_addrs, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 1d6f085f..545fb8d6 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -211,6 +211,7 @@ pub struct Cli { pub console: Option, pub reboot_ec: Option, pub ec_hib_delay: Option>, + pub uptimeinfo: bool, pub hash: Option, pub pd_addrs: Option<(u16, u16, u16)>, pub pd_ports: Option<(u8, u8, u8)>, @@ -295,6 +296,7 @@ pub fn parse(args: &[String]) -> Cli { console: cli.console, reboot_ec: cli.reboot_ec, // ec_hib_delay + uptimeinfo: cli.uptimeinfo, hash: cli.hash, pd_addrs: cli.pd_addrs, pd_ports: cli.pd_ports, @@ -1174,6 +1176,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { print_err(ec.set_ec_hib_delay(*delay)); } print_err(ec.get_ec_hib_delay()); + } else if args.uptimeinfo { + print_err(ec.get_uptime_info()); } else if args.test { println!("Self-Test"); let result = selftest(&ec); diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 6ad46bdd..52e2cedc 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -78,6 +78,7 @@ pub fn parse(args: &[String]) -> Cli { console: None, reboot_ec: None, ec_hib_delay: None, + uptimeinfo: false, hash: None, // This is the only driver that works on UEFI driver: Some(CrosEcDriverType::Portio), @@ -493,6 +494,9 @@ pub fn parse(args: &[String]) -> Cli { Some(None) }; found_an_option = true; + } else if arg == "--uptimeinfo" { + cli.uptimeinfo = true; + found_an_option = true; } else if arg == "-t" || arg == "--test" { cli.test = true; found_an_option = true;