Skip to content

Commit

Permalink
Allow decoding of SMBIOS Type 11 serialnumbers
Browse files Browse the repository at this point in the history
They're the serialnumbers of what the system was originally assembled
with in the factory.

TODO

- [ ] Cleanup code
- [ ] Make safer with fewer unwraps
- [ ] Custom command, not in info
- [ ] Decode date

Signed-off-by: Daniel Schaefer <dhs@frame.work>
  • Loading branch information
JohnAZoidberg committed Aug 1, 2024
1 parent b6b235c commit 848b433
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 0 deletions.
3 changes: 3 additions & 0 deletions framework_lib/src/commandline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,9 @@ fn smbios_info() {
println!(" Family: {}", family);
}
}
DefinedStruct::OemStrings(data) => {
smbios::dump_oem_strings(data.oem_strings());
}
_ => {}
}
}
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 @@ -31,6 +31,7 @@ pub mod esrt;
pub mod guid;
mod os_specific;
pub mod power;
pub mod serialnum;
pub mod smbios;
#[cfg(feature = "uefi")]
pub mod uefi;
Expand Down
101 changes: 101 additions & 0 deletions framework_lib/src/serialnum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use alloc::string::{String, ToString};
use core::str::FromStr;

#[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,
}

#[derive(Debug)]
pub enum Cfg0 {
SKU = 0x00,
Poc1 = 0x01,
Proto1 = 0x02,
Proto2 = 0x03,
Evt1 = 0x04,
Evt2 = 0x05,
Reserved = 0x06,
Dvt1 = 0x07,
Dvt2 = 0x08,
Pvt = 0x09,
Mp = 0x0A,
}

#[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 = match caps.get(3).unwrap().as_str().chars().next().unwrap() {
'0' => Cfg0::SKU,
'1' => Cfg0::Poc1,
'2' => Cfg0::Proto1,
'3' => Cfg0::Proto2,
'4' => Cfg0::Evt1,
'5' => Cfg0::Evt2,
'6' => Cfg0::Reserved,
'7' => Cfg0::Dvt1,
'8' => Cfg0::Dvt2,
'9' => Cfg0::Pvt,
'A' => Cfg0::Mp,
_ => return Err("Invalid CFG0".to_string()),
};
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(),
})
}
}
108 changes: 108 additions & 0 deletions framework_lib/src/smbios.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Retrieve SMBIOS tables and extract information from them

use core::str::FromStr;
use std::prelude::v1::*;

#[cfg(all(not(feature = "uefi"), not(target_os = "freebsd")))]
use std::io::ErrorKind;

use crate::serialnum::FrameworkSerial;
use crate::util::{Config, Platform};
use num_derive::FromPrimitive;
use smbioslib::*;
Expand Down Expand Up @@ -296,3 +298,109 @@ fn kenv_get(name: &str) -> nix::Result<String> {
Ok(value)
}
}

#[derive(Debug)]
enum SmbiosSerialNumber {
Mainboard = 1,
Laptop,
Camera,
Display,
Battery,
Touchpad,
Keyboard,
Fingerprint,
AudioDaughtercard,
ACover,
BCover,
CCover,
AntennaMain,
AntennaAux,
TouchpadFpc,
FingerprintFfc,
EdpCable,
LcdCable,
ThermalAssembly,
WifiModule,
Speaker,
RamSlot1,
RamSlot2,
Ssd,
AudioFfc,
}

pub fn dump_oem_strings(strings: &SMBiosStringSet) {
for (i, s) in strings.into_iter().enumerate() {
let idx = i + 1;
let sn = match idx {
1 => Some(SmbiosSerialNumber::Mainboard),
2 => Some(SmbiosSerialNumber::Laptop),
3 => Some(SmbiosSerialNumber::Camera),
4 => Some(SmbiosSerialNumber::Display),
5 => Some(SmbiosSerialNumber::Battery),
6 => Some(SmbiosSerialNumber::Touchpad),
7 => Some(SmbiosSerialNumber::Keyboard),
8 => Some(SmbiosSerialNumber::Fingerprint),
10 => Some(SmbiosSerialNumber::AudioDaughtercard),
11 => Some(SmbiosSerialNumber::ACover),
12 => Some(SmbiosSerialNumber::BCover),
13 => Some(SmbiosSerialNumber::CCover),
14 => Some(SmbiosSerialNumber::AntennaMain),
15 => Some(SmbiosSerialNumber::AntennaAux),
16 => Some(SmbiosSerialNumber::TouchpadFpc),
17 => Some(SmbiosSerialNumber::FingerprintFfc),
18 => Some(SmbiosSerialNumber::EdpCable),
19 => Some(SmbiosSerialNumber::LcdCable),
20 => Some(SmbiosSerialNumber::ThermalAssembly),
21 => Some(SmbiosSerialNumber::WifiModule),
22 => Some(SmbiosSerialNumber::Speaker),
23 => Some(SmbiosSerialNumber::RamSlot1),
24 => Some(SmbiosSerialNumber::RamSlot2),
25 => Some(SmbiosSerialNumber::Ssd),
26 => Some(SmbiosSerialNumber::AudioFfc),
_ => None,
};
match sn {
Some(SmbiosSerialNumber::RamSlot1)
| Some(SmbiosSerialNumber::RamSlot2)
| Some(SmbiosSerialNumber::Ssd)
| Some(SmbiosSerialNumber::WifiModule) => {
println!("{} {:<20} (Unused)", s, format!("{:?}", sn.unwrap()))
}
Some(SmbiosSerialNumber::Fingerprint) | Some(SmbiosSerialNumber::CCover) => {
println!(
"{} {:<20} (Only Pre-Built)",
s,
format!("{:?}", sn.unwrap())
);
if let Ok(serial) = FrameworkSerial::from_str(&format!("{:?}", s)) {
println!(
" {} (Config {}) by {}. {:?} Phase ({:?}, Week {}, {})",
serial.product,
serial.cfg1,
serial.oem,
serial.cfg0,
serial.day,
serial.week,
serial.year,
);
}
}
Some(sn) => {
println!("{} {:?}", s, sn);
if let Ok(serial) = FrameworkSerial::from_str(&format!("{:?}", s)) {
println!(
" {} (Config {}) by {}. {:?} Phase ({:?}, Week {}, {})",
serial.product,
serial.cfg1,
serial.oem,
serial.cfg0,
serial.day,
serial.week,
serial.year,
);
}
}
_ => println!("{} Unknown/Reserved", s),
}
}
}

0 comments on commit 848b433

Please sign in to comment.