Skip to content

Allow decoding of SMBIOS Type 11 serialnumbers #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion framework_lib/src/commandline/clap_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,14 @@ struct ClapCli {
#[arg(long)]
pdports: bool,

/// Show info from SMBIOS (Only on UEFI)
/// Show info from SMBIOS
#[arg(long)]
info: bool,

/// Show info about system serial numbers
#[arg(long)]
serialnums: bool,

/// Show details about the PD controllers
#[arg(long)]
pd_info: bool,
Expand Down Expand Up @@ -457,6 +461,7 @@ pub fn parse(args: &[String]) -> Cli {
paginate: false,
info: args.info,
flash_gpu_descriptor,
serialnums: args.serialnums,
raw_command: vec![],
}
}
27 changes: 21 additions & 6 deletions framework_lib/src/commandline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ use crate::esrt;
#[cfg(feature = "rusb")]
use crate::inputmodule::check_inputmodule_version;
use crate::power;
use crate::serialnum::Cfg0;
use crate::smbios;
use crate::smbios::ConfigDigit0;
use crate::smbios::{dmidecode_string_val, get_smbios, is_framework};
#[cfg(feature = "hidapi")]
use crate::touchpad::print_touchpad_fw_ver;
Expand Down Expand Up @@ -201,6 +201,7 @@ pub struct Cli {
pub help: bool,
pub info: bool,
pub flash_gpu_descriptor: Option<(u8, String)>,
pub serialnums: bool,
// UEFI only
pub allupdate: bool,
pub paginate: bool,
Expand Down Expand Up @@ -1012,6 +1013,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
power::get_and_print_pd_info(&ec);
} else if args.info {
smbios_info();
} else if args.serialnums {
serialnum_info();
} else if args.pd_info {
print_pd_details(&ec);
} else if let Some(pd) = args.pd_reset {
Expand Down Expand Up @@ -1249,7 +1252,8 @@ Options:
--fansetrpm Set fan RPM (limited by EC fan table max RPM)
--autofanctrl Turn on automatic fan speed control
--pdports Show information about USB-C PD ports
--info Show info from SMBIOS (Only on UEFI)
--info Show info from SMBIOS
--serialnums Show info about system serial numbers
--pd-info Show details about the PD controllers
--privacy Show privacy switch statuses (camera and microphone)
--pd-bin <PD_BIN> Parse versions from PD firmware binary file
Expand Down Expand Up @@ -1426,8 +1430,7 @@ fn smbios_info() {
// Assumes it's ASCII, which is guaranteed by SMBIOS
let config_digit0 = &version[0..1];
let config_digit0 = u8::from_str_radix(config_digit0, 16);
if let Ok(version_config) =
config_digit0.map(<ConfigDigit0 as FromPrimitive>::from_u8)
if let Ok(version_config) = config_digit0.map(<Cfg0 as FromPrimitive>::from_u8)
{
println!(" Version: {:?} ({})", version_config, version);
} else {
Expand Down Expand Up @@ -1465,8 +1468,7 @@ fn smbios_info() {
// Assumes it's ASCII, which is guaranteed by SMBIOS
let config_digit0 = &version[0..1];
let config_digit0 = u8::from_str_radix(config_digit0, 16);
if let Ok(version_config) =
config_digit0.map(<ConfigDigit0 as FromPrimitive>::from_u8)
if let Ok(version_config) = config_digit0.map(<Cfg0 as FromPrimitive>::from_u8)
{
println!(" Version: {:?} ({})", version_config, version);
} else {
Expand All @@ -1488,6 +1490,19 @@ fn smbios_info() {
}
}

fn serialnum_info() {
let smbios = get_smbios();
if smbios.is_none() {
error!("Failed to find SMBIOS");
return;
}
for undefined_struct in smbios.unwrap().iter() {
if let DefinedStruct::OemStrings(data) = undefined_struct.defined_struct() {
smbios::dump_oem_strings(data.oem_strings());
}
}
}

fn analyze_ccgx_pd_fw(data: &[u8]) {
if let Some(versions) = ccgx::binary::read_versions(data, Ccg3) {
println!("Detected CCG3 firmware");
Expand Down
4 changes: 4 additions & 0 deletions framework_lib/src/commandline/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ pub fn parse(args: &[String]) -> Cli {
flash_gpu_descriptor: None,
allupdate: false,
info: false,
serialnums: false,
raw_command: vec![],
};

Expand Down Expand Up @@ -232,6 +233,9 @@ pub fn parse(args: &[String]) -> Cli {
} else if arg == "--info" {
cli.info = true;
found_an_option = true;
} else if arg == "--serialnums" {
cli.serialnums = true;
found_an_option = true;
} else if arg == "--intrusion" {
cli.intrusion = true;
found_an_option = true;
Expand Down
1 change: 1 addition & 0 deletions framework_lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub mod ec_binary;
pub mod esrt;
mod os_specific;
pub mod power;
pub mod serialnum;
pub mod smbios;
#[cfg(feature = "uefi")]
pub mod uefi;
Expand Down
104 changes: 104 additions & 0 deletions framework_lib/src/serialnum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use alloc::string::{String, ToString};
use core::str::FromStr;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;

#[derive(Debug)]
pub struct FrameworkSerial {
// brand: Always FR for Framework
// format: Always A
/// Three letter string
pub product: String,
/// Two letter string
pub oem: String,
/// Development state
pub cfg0: Cfg0,
/// Defines config of that specific product
pub cfg1: char,
pub year: u16,
pub week: u8,
pub day: WeekDay,
/// Four letter/digit string
pub part: String,
}

#[repr(u8)]
#[derive(Debug, PartialEq, FromPrimitive, Clone, Copy)]
pub enum Cfg0 {
SKU = 0x00,
Poc1 = 0x01,
Proto1 = 0x02,
Proto2 = 0x03,
Evt1 = 0x04,
Evt2 = 0x05,
Reserved = 0x06,
Dvt1 = 0x07,
Dvt2 = 0x08,
Pvt = 0x09,
MassProduction = 0x0A,
MassProductionB = 0x0B,
MassProductionC = 0x0C,
MassProductionD = 0x0D,
MassProductionE = 0x0E,
MassProductionF = 0x0F,
}

#[derive(Debug)]
pub enum WeekDay {
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}

impl FromStr for FrameworkSerial {
type Err = String;

// TODO: !!! PROPER ERROR HANDLING !!!
fn from_str(s: &str) -> Result<Self, Self::Err> {
let pattern =
r"FRA([A-Z]{3})([A-Z]{2})([0-9A-F])([0-9A-F])([0-9A-Z])([0-9]{2})([0-7])([0-9A-Z]{4})";
let re = regex::Regex::new(pattern).unwrap();

let caps = re.captures(s).ok_or("Invalid Serial".to_string())?;

let cfg0 = caps.get(3).unwrap().as_str().chars().next();
let cfg0 = cfg0.and_then(|x| str::parse::<u8>(&x.to_string()).ok());
let cfg0 = cfg0.and_then(<Cfg0 as FromPrimitive>::from_u8);
let cfg0 = if let Some(cfg0) = cfg0 {
cfg0
} else {
error!("Invalid CFG0 '{:?}'", cfg0);
return Err(format!("Invalid CFG0 '{:?}'", cfg0));
};
let cfg1 = caps.get(4).unwrap().as_str().chars().next().unwrap();
let year = str::parse::<u16>(caps.get(5).unwrap().as_str()).unwrap();
let year = 2020 + year;
let week = str::parse::<u8>(caps.get(6).unwrap().as_str()).unwrap();
// TODO: Decode into date
let day = match str::parse::<u8>(caps.get(7).unwrap().as_str()).unwrap() {
1 => WeekDay::Monday,
2 => WeekDay::Tuesday,
3 => WeekDay::Wednesday,
4 => WeekDay::Thursday,
5 => WeekDay::Friday,
6 => WeekDay::Saturday,
7 => WeekDay::Sunday,
_ => return Err("Invalid Day".to_string()),
};

Ok(FrameworkSerial {
product: caps.get(1).unwrap().as_str().to_string(),
oem: caps.get(2).unwrap().as_str().to_string(),
cfg0,
cfg1,
year,
week,
day,
part: caps.get(2).unwrap().as_str().to_string(),
})
}
}
Loading
Loading