diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index fb5096ca..266c971b 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -50,7 +50,7 @@ sha2 = { version = "0.10.8", default-features = false, features = [ "force-soft" regex = { version = "1.11.1", default-features = false } redox_hwio = { git = "https://github.com/FrameworkComputer/rust-hwio", branch = "freebsd", default-features = false } libc = { version = "0.2.155", optional = true } -clap = { version = "4.5", features = ["derive"], optional = true } +clap = { version = "4.5", features = ["derive", "cargo"], optional = true } clap-num = { version = "1.2.0", optional = true } clap-verbosity-flag = { version = "2.2.1", optional = true } nix = { version = "0.29.0", features = ["ioctl", "user"], optional = true } diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 247ff5ee..0eaa9395 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -88,6 +88,10 @@ pub enum EcCommands { ExpansionBayStatus = 0x3E1B, /// Get hardware diagnostics GetHwDiag = 0x3E1C, + /// Get gpu bay serial + GetGpuSerial = 0x3E1D, + /// Set gpu bay serial and program structure + ProgramGpuEeprom = 0x3E1F, } pub trait EcRequest { diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index c4944db8..6bde4170 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -931,3 +931,49 @@ impl EcRequest for EcRequestFpLedLevelControlV1 { 1 } } + +#[repr(C, packed)] +pub struct EcRequestGetGpuSerial { + pub idx: u8, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct EcResponseGetGpuSerial { + pub idx: u8, + pub valid: u8, + pub serial: [u8; 20], +} + +impl EcRequest for EcRequestGetGpuSerial { + fn command_id() -> EcCommands { + EcCommands::GetGpuSerial + } +} + +#[repr(u8)] +pub enum SetGpuSerialMagic { + /// 7700S config magic value + WriteGPUConfig = 0x0D, + /// SSD config magic value + WriteSSDConfig = 0x55, +} + +#[repr(C, packed)] +pub struct EcRequestSetGpuSerial { + pub magic: u8, + pub idx: u8, + pub serial: [u8; 20], +} + +#[repr(C, packed)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct EcResponseSetGpuSerial { + pub valid: u8, +} + +impl EcRequest for EcRequestSetGpuSerial { + fn command_id() -> EcCommands { + EcCommands::ProgramGpuEeprom + } +} diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 17702689..acbd5c0c 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -33,6 +33,7 @@ use alloc::string::String; use alloc::string::ToString; use alloc::vec; use alloc::vec::Vec; + #[cfg(feature = "uefi")] use core::prelude::rust_2021::derive; use num_traits::FromPrimitive; @@ -789,6 +790,36 @@ impl CrosEc { res } + /// Get the GPU Serial + /// + pub fn get_gpu_serial(&self) -> EcResult { + let gpuserial: EcResponseGetGpuSerial = + EcRequestGetGpuSerial { idx: 0 }.send_command(self)?; + let serial: String = String::from_utf8(gpuserial.serial.to_vec()).unwrap(); + + if gpuserial.valid == 0 { + return Err(EcError::DeviceError("No valid GPU serial".to_string())); + } + + Ok(serial) + } + + /// Set the GPU Serial + /// + /// # Arguments + /// `newserial` - a string that is 18 characters long + pub fn set_gpu_serial(&self, magic: u8, newserial: String) -> EcResult { + let mut array_tmp: [u8; 20] = [0; 20]; + array_tmp[..18].copy_from_slice(newserial.as_bytes()); + let result = EcRequestSetGpuSerial { + magic, + idx: 0, + serial: array_tmp, + } + .send_command(self)?; + Ok(result.valid) + } + /// 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 { diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index f9299e6d..22298a01 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -1,9 +1,12 @@ //! Module to factor out commandline interaction //! This way we can use it in the regular OS commandline tool on Linux and Windows, //! as well as on the UEFI shell tool. +use clap::error::ErrorKind; use clap::Parser; +use clap::{arg, command, Arg, Args, FromArgMatches}; use clap_num::maybe_hex; +use crate::chromium_ec::commands::SetGpuSerialMagic; use crate::chromium_ec::CrosEcDriverType; use crate::commandline::{ Cli, ConsoleArg, FpBrightnessArg, HardwareDeviceType, InputDeckModeArg, RebootEcArg, @@ -203,31 +206,71 @@ struct ClapCli { /// Parse a list of commandline arguments and return the struct pub fn parse(args: &[String]) -> Cli { - let args = ClapCli::parse_from(args); + // Step 1 - Define args that can't be derived + let cli = command!() + .arg(Arg::new("fgd").long("flash-gpu-descriptor").num_args(2)) + .disable_version_flag(true); + // Step 2 - Define args from derived struct + let mut cli = ClapCli::augment_args(cli); + + // Step 3 - Parse args that can't be derived + let matches = cli.clone().get_matches_from(args); + let fgd = matches + .get_many::("fgd") + .unwrap_or_default() + .map(|v| v.as_str()) + .collect::>(); + let flash_gpu_descriptor = if !fgd.is_empty() { + let hex_magic = if let Some(hex_magic) = fgd[0].strip_prefix("0x") { + u8::from_str_radix(hex_magic, 16) + } else { + // Force parse error + u8::from_str_radix("", 16) + }; + + let magic = if let Ok(magic) = fgd[0].parse::() { + magic + } else if let Ok(hex_magic) = hex_magic { + hex_magic + } else if fgd[0].to_uppercase() == "GPU" { + SetGpuSerialMagic::WriteGPUConfig as u8 + } else if fgd[0].to_uppercase() == "SSD" { + SetGpuSerialMagic::WriteSSDConfig as u8 + } else { + cli.error( + ErrorKind::InvalidValue, + "First argument of --flash-gpu-descriptor must be an integer or one of: 'GPU', 'SSD'", + ) + .exit(); + }; + if fgd[1].len() != 18 { + cli.error( + ErrorKind::InvalidValue, + "Second argument of --flash-gpu-descriptor must be an 18 digit serial number", + ) + .exit(); + } + Some((magic, fgd[1].to_string())) + } else { + None + }; + + // Step 4 - Parse from derived struct + let args = ClapCli::from_arg_matches(&matches) + .map_err(|err| err.exit()) + .unwrap(); let pd_addrs = match args.pd_addrs.len() { 2 => Some((args.pd_addrs[0], args.pd_addrs[1])), 0 => None, - _ => { - // Actually unreachable, checked by clap - println!( - "Must provide exactly to PD Addresses. Provided: {:?}", - args.pd_addrs - ); - std::process::exit(1); - } + // Checked by clap + _ => unreachable!(), }; let pd_ports = match args.pd_ports.len() { 2 => Some((args.pd_ports[0], args.pd_ports[1])), 0 => None, - _ => { - // Actually unreachable, checked by clap - println!( - "Must provide exactly to PD Ports. Provided: {:?}", - args.pd_ports - ); - std::process::exit(1); - } + // Checked by clap + _ => unreachable!(), }; Cli { @@ -299,6 +342,7 @@ pub fn parse(args: &[String]) -> Cli { // UEFI only - every command needs to implement a parameter to enable the pager paginate: false, info: args.info, + flash_gpu_descriptor, raw_command: vec![], } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index fa284951..1d6ae7cf 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -181,6 +181,7 @@ pub struct Cli { pub has_mec: Option, pub help: bool, pub info: bool, + pub flash_gpu_descriptor: Option<(u8, String)>, // UEFI only pub allupdate: bool, pub paginate: bool, @@ -1004,6 +1005,13 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!(" Size: {:>20} KB", data.len() / 1024); hash(&data); } + } else if let Some(gpu_descriptor) = &args.flash_gpu_descriptor { + let res = ec.set_gpu_serial(gpu_descriptor.0, gpu_descriptor.1.to_ascii_uppercase()); + match res { + Ok(1) => println!("GPU Descriptor successfully written"), + Ok(x) => println!("GPU Descriptor write failed with status code: {}", x), + Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), + } } 0 @@ -1053,6 +1061,7 @@ Options: --kblight [] Set keyboard backlight percentage or get, if no value provided --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. -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 261720fc..fd4e9528 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; @@ -9,6 +9,7 @@ use uefi::proto::shell_params::*; use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams, SearchType}; use uefi::Identify; +use crate::chromium_ec::commands::SetGpuSerialMagic; use crate::chromium_ec::{CrosEcDriverType, HardwareDeviceType}; use crate::commandline::Cli; @@ -99,6 +100,7 @@ pub fn parse(args: &[String]) -> Cli { has_mec: None, test: false, help: false, + flash_gpu_descriptor: None, allupdate: false, info: false, raw_command: vec![], @@ -513,6 +515,39 @@ pub fn parse(args: &[String]) -> Cli { println!("Need to provide a value for --console. Possible values: bios, ec, pd0, pd1, rtm01, rtm23, ac-left, ac-right"); None }; + } else if arg == "--flash-gpu-descriptor" { + cli.flash_gpu_descriptor = if args.len() > i + 2 { + let sn = args[i + 2].to_string(); + let magic = &args[i + 1]; + + let hex_magic = if let Some(hex_magic) = magic.strip_prefix("0x") { + u8::from_str_radix(hex_magic, 16) + } else { + // Force parse error + u8::from_str_radix("", 16) + }; + + if let Ok(magic) = magic.parse::() { + Some((magic, sn)) + } else if let Ok(hex_magic) = hex_magic { + Some((hex_magic, sn)) + } else if magic.to_uppercase() == "GPU" { + Some((SetGpuSerialMagic::WriteGPUConfig as u8, sn)) + } else if magic.to_uppercase() == "SSD" { + Some((SetGpuSerialMagic::WriteSSDConfig as u8, sn)) + } else { + println!( + "Invalid values for --flash_gpu_descriptor: '{} {}'. Must be u8, 18 character string.", + args[i + 1], + args[i + 2] + ); + None + } + } else { + println!("Need to provide a value for --flash_gpu_descriptor. TYPE_MAGIC SERIAL"); + None + }; + found_an_option = true; } }