diff --git a/Cargo.lock b/Cargo.lock index 79498c64..358ab12d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,12 +82,6 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.10.0" @@ -229,22 +223,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" -[[package]] -name = "core-foundation" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" -dependencies = [ - "core-foundation-sys 0.6.2", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -326,6 +304,16 @@ dependencies = [ "syn", ] +[[package]] +name = "dmidecode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ecc81f210f04aaadfeb5aa067b7d9095ea2af9c0e140ef56b8a3f3381ec4db" +dependencies = [ + "bitflags", + "uuid", +] + [[package]] name = "embed-resource" version = "3.0.6" @@ -399,24 +387,23 @@ dependencies = [ "clap-num", "clap-verbosity-flag", "clap_complete", + "dmidecode", "env_logger", "guid-create", "hidapi", "lazy_static", "libc", "log", - "nix 0.30.1", + "nix", "no-std-compat", "num", "num-derive", "num-traits", "nvml-wrapper", "plain", - "redox_hwio", "regex", "rusb", "sha2", - "smbios-lib", "spin 0.10.0", "uefi", "uefi-raw", @@ -545,15 +532,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getopts" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" -dependencies = [ - "unicode-width", -] - [[package]] name = "getrandom" version = "0.3.4" @@ -572,7 +550,7 @@ version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2b37e2f62729cdada11f0e6b3b6fe383c69c29fc619e391223e12856af308c" dependencies = [ - "bitflags 2.10.0", + "bitflags", "libc", "libgit2-sys", "log", @@ -620,7 +598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", - "core-foundation-sys 0.8.7", + "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "log", @@ -755,28 +733,12 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "io-kit-sys" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0" -dependencies = [ - "core-foundation-sys 0.6.2", - "mach 0.2.3", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itoa" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - [[package]] name = "jiff" version = "0.2.18" @@ -903,60 +865,19 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "mach" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" -dependencies = [ - "libc", -] - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset", - "pin-utils", -] - [[package]] name = "nix" version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.10.0", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -1046,7 +967,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d5c6c0ef9702176a570f06ad94f3198bc29c524c8b498f1b9346e1b1bdcbb3a" dependencies = [ - "bitflags 2.10.0", + "bitflags", "libloading", "nvml-wrapper-sys", "static_assertions", @@ -1173,15 +1094,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "redox_hwio" -version = "0.1.6" -source = "git+https://github.com/FrameworkComputer/rust-hwio?branch=freebsd#9bcff4277d8f3d7dce2b12c6ad81d092ae35c4ba" -dependencies = [ - "lazy_static", - "nix 0.25.1", -] - [[package]] name = "regex" version = "1.12.2" @@ -1278,19 +1190,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - [[package]] name = "serde_spanned" version = "1.0.4" @@ -1329,22 +1228,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "smbios-lib" -version = "0.9.1" -source = "git+https://github.com/FrameworkComputer/smbios-lib.git?branch=no-std#b3e2fff8a6f4b8c2d729467cbbf0c8c41974cd1c" -dependencies = [ - "core-foundation", - "core-foundation-sys 0.6.2", - "getopts", - "io-kit-sys", - "libc", - "mach 0.3.2", - "no-std-compat", - "serde", - "serde_json", -] - [[package]] name = "spin" version = "0.9.8" @@ -1516,7 +1399,7 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71fe9058b73ee2b6559524af9e33199c13b2485ddbf3ad1181b68051cdc50c17" dependencies = [ - "bitflags 2.10.0", + "bitflags", "cfg-if", "log", "ptr_meta", @@ -1543,7 +1426,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f64fe59e11af447d12fd60a403c74106eb104309f34b4c6dbce6e927d97da9d" dependencies = [ - "bitflags 2.10.0", + "bitflags", "uguid", ] @@ -1559,12 +1442,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - [[package]] name = "url" version = "2.5.8" @@ -1589,6 +1466,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2105,9 +1988,3 @@ dependencies = [ "quote", "syn", ] - -[[package]] -name = "zmij" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f63c051f4fe3c1509da62131a678643c5b6fbdc9273b2b79d4378ebda003d2" diff --git a/flake.nix b/flake.nix index 85583979..e436b242 100644 --- a/flake.nix +++ b/flake.nix @@ -83,12 +83,6 @@ isIncludedRoot || builtins.elem baseName includedFiles; }; - # Git dependency output hashes - gitDependencyHashes = { - "redox_hwio-0.1.6" = "sha256-knLIZ7yp42SQYk32NGq3SUGvJFVumFhD64Njr5TRdFs="; - "smbios-lib-0.9.1" = "sha256-3L8JaA75j9Aaqg1z9lVs61m6CvXDeQprEFRq+UDCHQo="; - }; - # Build function for the CLI tool (Linux/macOS) buildFrameworkTool = { release ? false, features ? [] }: let @@ -103,7 +97,6 @@ cargoLock = { lockFile = ./Cargo.lock; - outputHashes = gitDependencyHashes; }; buildType = profile; @@ -157,7 +150,6 @@ cargoLock = { lockFile = ./Cargo.lock; - outputHashes = gitDependencyHashes; }; buildType = profile; @@ -208,7 +200,6 @@ cargoLock = { lockFile = ./Cargo.lock; - outputHashes = gitDependencyHashes; }; buildType = profile; diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 7004b8e2..bf5e27dc 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -24,6 +24,7 @@ built = { version = "0.8", features = ["chrono", "git2"] } [dependencies] lazy_static = "1.4.0" +dmidecode = { version = "1", default-features = false } sha2 = { version = "0.10.8", default-features = false, features = [ "force-soft" ] } regex = { version = "1.11.1", default-features = false } num = { version = "0.4", default-features = false } @@ -40,12 +41,9 @@ guid-create = { version = "0.5.0", default-features = false } uefi = { version = "0.36.1", features = ["alloc", "global_allocator", "panic_handler", "logger"] } uefi-raw = "0.13" plain = "0.2.3" -redox_hwio = { git = "https://github.com/FrameworkComputer/rust-hwio", branch = "freebsd", default-features = false } -smbios-lib = { git = "https://github.com/FrameworkComputer/smbios-lib.git", branch = "no-std", default-features = false } [target.'cfg(windows)'.dependencies] wmi = "0.18" -smbios-lib = { git = "https://github.com/FrameworkComputer/smbios-lib.git", branch = "no-std" } env_logger = "0.11" clap = { version = "4.5", features = ["derive", "cargo"] } clap-num = { version = "1.2.0" } @@ -58,8 +56,6 @@ nvml-wrapper = { version = "0.11.0", optional = true } [target.'cfg(unix)'.dependencies] libc = "0.2.155" nix = { version = "0.30", features = ["ioctl", "user"] } -redox_hwio = { git = "https://github.com/FrameworkComputer/rust-hwio", branch = "freebsd" } -smbios-lib = { git = "https://github.com/FrameworkComputer/smbios-lib.git", branch = "no-std" } env_logger = "0.11" clap = { version = "4.5", features = ["derive", "cargo"] } clap-num = { version = "1.2.0" } @@ -75,6 +71,7 @@ features = [ "Win32_Security", "Win32_System_IO", "Win32_System_Ioctl", + "Win32_System_SystemInformation", "Win32_System_SystemServices", # For HID devices "Win32_Devices_DeviceAndDriverInstallation", diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 94e32b0b..a44b3d79 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -30,6 +30,8 @@ pub mod input_deck; #[cfg(not(windows))] mod portio; #[cfg(not(windows))] +mod portio_hwio; +#[cfg(not(windows))] mod portio_mec; #[allow(dead_code)] mod protocol; diff --git a/framework_lib/src/chromium_ec/portio.rs b/framework_lib/src/chromium_ec/portio.rs index d76f8a6b..7efd6927 100644 --- a/framework_lib/src/chromium_ec/portio.rs +++ b/framework_lib/src/chromium_ec/portio.rs @@ -1,11 +1,10 @@ +use super::portio_hwio::{Io, Pio}; use crate::chromium_ec::{EcError, EcResponseStatus, EcResult}; use alloc::format; use alloc::string::ToString; use alloc::vec; use alloc::vec::Vec; use core::convert::TryInto; -#[cfg(not(windows))] -use hwio::{Io, Pio}; #[cfg(target_os = "linux")] use libc::ioperm; use log::Level; diff --git a/framework_lib/src/chromium_ec/portio_hwio.rs b/framework_lib/src/chromium_ec/portio_hwio.rs new file mode 100644 index 00000000..fbf355fc --- /dev/null +++ b/framework_lib/src/chromium_ec/portio_hwio.rs @@ -0,0 +1,284 @@ +// Inlined from https://github.com/FrameworkComputer/rust-hwio (freebsd branch) +// Original crate: redox_hwio by Jeremy Soller +// SPDX-License-Identifier: MIT + +#[cfg(all( + not(target_os = "freebsd"), + any(target_arch = "x86", target_arch = "x86_64") +))] +use core::arch::asm; + +use core::marker::PhantomData; + +/// Trait for hardware port I/O operations +pub trait Io { + type Value: Copy + + PartialEq + + core::ops::BitAnd + + core::ops::BitOr + + core::ops::Not; + + fn read(&self) -> Self::Value; + fn write(&mut self, value: Self::Value); +} + +// ---- FreeBSD ioctl infrastructure ---- + +#[cfg(target_os = "freebsd")] +use nix::ioctl_readwrite; +#[cfg(target_os = "freebsd")] +use std::os::fd::AsRawFd; + +#[cfg(target_os = "freebsd")] +#[repr(C)] +struct IoDevPioReq { + access: u32, + port: u32, + width: u32, + val: u32, +} + +#[cfg(target_os = "freebsd")] +ioctl_readwrite!(iodev_rw, b'I', 0, IoDevPioReq); +#[cfg(target_os = "freebsd")] +const IODEV_PIO_READ: u32 = 0; +#[cfg(target_os = "freebsd")] +const IODEV_PIO_WRITE: u32 = 1; + +#[cfg(target_os = "freebsd")] +use std::{ + fs::{File, OpenOptions}, + sync::Mutex, +}; + +#[cfg(target_os = "freebsd")] +lazy_static! { + static ref FILE: Mutex = Mutex::new( + OpenOptions::new() + .read(true) + .write(true) + .open("/dev/io") + .expect("failed to open /dev/io") + ); +} + +#[cfg(target_os = "freebsd")] +#[inline(always)] +fn port_read(port: u16, buf: &mut [u8]) { + let file = FILE.lock().unwrap(); + let fd = file.as_raw_fd(); + + let mut req = IoDevPioReq { + access: IODEV_PIO_READ, + port: port as u32, + width: buf.len() as u32, + val: 0, + }; + unsafe { + iodev_rw(fd, &mut req).unwrap(); + } + + match buf.len() { + 1 => { + buf[0] = req.val as u8; + } + 2 => { + let val = u16::to_le_bytes(req.val as u16); + buf[0] = val[0]; + buf[1] = val[1]; + } + _ => panic!("Unsupported port_read width"), + } +} + +#[cfg(target_os = "freebsd")] +#[inline(always)] +fn port_write(port: u16, buf: &[u8]) { + let file = FILE.lock().unwrap(); + let fd = file.as_raw_fd(); + + let val = match buf.len() { + 1 => buf[0] as u32, + 2 => u16::from_le_bytes([buf[0], buf[1]]) as u32, + _ => panic!("Unsupported port_write width"), + }; + + let mut req = IoDevPioReq { + access: IODEV_PIO_WRITE, + port: port as u32, + width: buf.len() as u32, + val, + }; + unsafe { + iodev_rw(fd, &mut req).unwrap(); + } +} + +// ---- Linux /dev/port fallback for non-x86 architectures ---- + +#[cfg(all( + target_os = "linux", + not(any(target_arch = "x86", target_arch = "x86_64")) +))] +use std::{ + fs::{File, OpenOptions}, + io::{Read, Seek, SeekFrom, Write}, + sync::Mutex, +}; + +#[cfg(all( + target_os = "linux", + not(any(target_arch = "x86", target_arch = "x86_64")) +))] +lazy_static! { + static ref FILE: Mutex = Mutex::new( + OpenOptions::new() + .read(true) + .write(true) + .open("/dev/port") + .expect("failed to open /dev/port") + ); +} + +#[cfg(all( + target_os = "linux", + not(any(target_arch = "x86", target_arch = "x86_64")) +))] +#[inline(always)] +fn port_read(port: u16, buf: &mut [u8]) { + let mut file = FILE.lock().unwrap(); + file.seek(SeekFrom::Start(port as u64)).unwrap(); + file.read_exact(buf).unwrap(); +} + +#[cfg(all( + target_os = "linux", + not(any(target_arch = "x86", target_arch = "x86_64")) +))] +#[inline(always)] +fn port_write(port: u16, buf: &[u8]) { + let mut file = FILE.lock().unwrap(); + file.seek(SeekFrom::Start(port as u64)).unwrap(); + file.write_all(buf).unwrap(); +} + +// ---- Pio struct ---- + +/// Port I/O +#[derive(Copy, Clone)] +pub struct Pio { + port: u16, + value: PhantomData, +} + +impl Pio { + /// Create a PIO from a given port + pub const fn new(port: u16) -> Self { + Pio:: { + port, + value: PhantomData, + } + } +} + +// ---- Pio implementation ---- + +impl Io for Pio { + type Value = u8; + + #[cfg(all( + not(target_os = "freebsd"), + any(target_arch = "x86", target_arch = "x86_64") + ))] + #[inline(always)] + fn read(&self) -> u8 { + let value: u8; + unsafe { + asm!("in al, dx", out("al") value, in("dx") self.port, options(nostack)); + } + value + } + + #[cfg(any( + target_os = "freebsd", + not(any(target_arch = "x86", target_arch = "x86_64")) + ))] + #[inline(always)] + fn read(&self) -> u8 { + let mut buf = [0]; + port_read(self.port, &mut buf); + buf[0] + } + + #[cfg(all( + not(target_os = "freebsd"), + any(target_arch = "x86", target_arch = "x86_64") + ))] + #[inline(always)] + fn write(&mut self, value: u8) { + unsafe { + asm!("out dx, al", in("al") value, in("dx") self.port, options(nostack)); + } + } + + #[cfg(any( + target_os = "freebsd", + not(any(target_arch = "x86", target_arch = "x86_64")) + ))] + #[inline(always)] + fn write(&mut self, value: u8) { + let buf = [value]; + port_write(self.port, &buf); + } +} + +// ---- Pio implementation ---- + +impl Io for Pio { + type Value = u16; + + #[cfg(all( + not(target_os = "freebsd"), + any(target_arch = "x86", target_arch = "x86_64") + ))] + #[inline(always)] + fn read(&self) -> u16 { + let value: u16; + unsafe { + asm!("in ax, dx", out("ax") value, in("dx") self.port, options(nostack)); + } + value + } + + #[cfg(any( + target_os = "freebsd", + not(any(target_arch = "x86", target_arch = "x86_64")) + ))] + #[inline(always)] + fn read(&self) -> u16 { + let mut buf = [0, 0]; + port_read(self.port, &mut buf); + buf[0] as u16 | (buf[1] as u16) << 8 + } + + #[cfg(all( + not(target_os = "freebsd"), + any(target_arch = "x86", target_arch = "x86_64") + ))] + #[inline(always)] + fn write(&mut self, value: u16) { + unsafe { + asm!("out dx, ax", in("ax") value, in("dx") self.port, options(nostack)); + } + } + + #[cfg(any( + target_os = "freebsd", + not(any(target_arch = "x86", target_arch = "x86_64")) + ))] + #[inline(always)] + fn write(&mut self, value: u16) { + let buf = [value as u8, (value >> 8) as u8]; + port_write(self.port, &buf); + } +} diff --git a/framework_lib/src/chromium_ec/portio_mec.rs b/framework_lib/src/chromium_ec/portio_mec.rs index 1f855d0f..d08232b9 100644 --- a/framework_lib/src/chromium_ec/portio_mec.rs +++ b/framework_lib/src/chromium_ec/portio_mec.rs @@ -4,7 +4,7 @@ use alloc::vec::Vec; use log::Level; -use hwio::{Io, Pio}; +use super::portio_hwio::{Io, Pio}; #[cfg(target_os = "linux")] use libc::ioperm; diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 477f13a7..4ca9af45 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -58,7 +58,7 @@ use crate::parade_retimer; use crate::power; use crate::smbios; use crate::smbios::ConfigDigit0; -use crate::smbios::{dmidecode_string_val, get_smbios, is_framework}; +use crate::smbios::{get_smbios, is_framework}; #[cfg(feature = "hidapi")] use crate::touchpad::print_touchpad_fw_ver; #[cfg(feature = "hidapi")] @@ -66,11 +66,10 @@ use crate::touchscreen; #[cfg(feature = "rusb")] use crate::usbhub::check_usbhub_version; use crate::util::{self, Config, Platform, PlatformFamily}; +use dmidecode::Structure; #[cfg(feature = "hidapi")] use hidapi::HidApi; use sha2::{Digest, Sha256, Sha384, Sha512}; -//use smbioslib::*; -use smbioslib::{DefinedStruct, SMBiosInformation}; #[cfg(feature = "nvidia")] use nvml_wrapper::{enum_wrappers::device::TemperatureSensor, Nvml}; @@ -495,10 +494,12 @@ fn print_versions(ec: &CrosEc) { } println!("UEFI BIOS"); if let Some(smbios) = get_smbios() { - let bios_entries = smbios.collect::(); - if let Some(bios) = bios_entries.first() { - println!(" Version: {}", bios.version()); - println!(" Release Date: {}", bios.release_date()); + if let Some(bios) = smbios.structures().find_map(|r| match r { + Ok(Structure::Bios(b)) => Some(b), + _ => None, + }) { + println!(" Version: {}", bios.bios_version); + println!(" Release Date: {}", bios.bios_release_date); } else { println!(" Version: Unknown"); } @@ -1159,15 +1160,17 @@ fn compare_version(device: Option, version: String, ec: &Cro println!("Target Version {:?}", version); if let Some(smbios) = get_smbios() { - let bios_entries = smbios.collect::(); - let bios = bios_entries.first().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 let Some(bios) = smbios.structures().find_map(|r| match r { + Ok(Structure::Bios(b)) => Some(b), + _ => None, + }) { + if device == Some(HardwareDeviceType::BIOS) { + println!("Comparing BIOS version {:?}", bios.bios_version); + if version.to_uppercase() == bios.bios_version.to_uppercase() { + return 0; + } else { + return 1; + } } } } @@ -2071,23 +2074,24 @@ fn smbios_info() { error!("Failed to find SMBIOS"); return; } - for undefined_struct in smbios.unwrap().iter() { - match undefined_struct.defined_struct() { - DefinedStruct::Information(data) => { + for result in smbios.unwrap().structures() { + match result { + Ok(Structure::Bios(data)) => { println!("BIOS Information"); - if let Some(vendor) = dmidecode_string_val(&data.vendor()) { - println!(" Vendor: {}", vendor); + if !data.vendor.is_empty() { + println!(" Vendor: {}", data.vendor); } - if let Some(version) = dmidecode_string_val(&data.version()) { - println!(" Version: {}", version); + if !data.bios_version.is_empty() { + println!(" Version: {}", data.bios_version); } - if let Some(release_date) = dmidecode_string_val(&data.release_date()) { - println!(" Release Date: {}", release_date); + if !data.bios_release_date.is_empty() { + println!(" Release Date: {}", data.bios_release_date); } } - DefinedStruct::SystemInformation(data) => { + Ok(Structure::System(data)) => { println!("System Information"); - if let Some(version) = dmidecode_string_val(&data.version()) { + if !data.version.is_empty() { + let version = data.version; // 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); @@ -2099,34 +2103,37 @@ fn smbios_info() { println!(" Version: '{}'", version); } } - if let Some(manufacturer) = dmidecode_string_val(&data.manufacturer()) { - println!(" Manufacturer: {}", manufacturer); + if !data.manufacturer.is_empty() { + println!(" Manufacturer: {}", data.manufacturer); } - if let Some(product_name) = dmidecode_string_val(&data.product_name()) { - println!(" Product Name: {}", product_name); + if !data.product.is_empty() { + println!(" Product Name: {}", data.product); } - if let Some(wake_up_type) = data.wakeup_type() { - println!(" Wake-Up-Type: {:?}", wake_up_type.value); + if let Some(wakeup) = data.wakeup { + println!(" Wake-Up-Type: {:?}", wakeup); } - if let Some(sku_number) = dmidecode_string_val(&data.sku_number()) { - println!(" SKU Number: {}", sku_number); + if let Some(sku) = data.sku { + if !sku.is_empty() { + println!(" SKU Number: {}", sku); + } } - if let Some(sn) = dmidecode_string_val(&data.serial_number()) { - println!(" Serial Number:{}", sn); + if !data.serial.is_empty() { + println!(" Serial Number:{}", data.serial); } - if let Some(family) = dmidecode_string_val(&data.family()) { - println!(" Family: {}", family); + if let Some(family) = data.family { + if !family.is_empty() { + println!(" Family: {}", family); + } } } - DefinedStruct::SystemChassisInformation(data) => { + Ok(Structure::Enclosure(data)) => { println!("System Chassis Information"); - if let Some(chassis) = data.chassis_type() { - println!(" Type: {}", chassis); - } + println!(" Type: {}", data.enclosure_type); } - DefinedStruct::BaseBoardInformation(data) => { + Ok(Structure::BaseBoard(data)) => { println!("BaseBoard Information"); - if let Some(version) = dmidecode_string_val(&data.version()) { + if !data.version.is_empty() { + let version = data.version; // 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); @@ -2138,14 +2145,14 @@ fn smbios_info() { println!(" Version: '{}'", version); } } - if let Some(manufacturer) = dmidecode_string_val(&data.manufacturer()) { - println!(" Manufacturer: {}", manufacturer); + if !data.manufacturer.is_empty() { + println!(" Manufacturer: {}", data.manufacturer); } - if let Some(product_name) = dmidecode_string_val(&data.product()) { - println!(" Product: {}", product_name); + if !data.product.is_empty() { + println!(" Product: {}", data.product); } - if let Some(sn) = dmidecode_string_val(&data.serial_number()) { - println!(" Serial Number:{}", sn); + if !data.serial.is_empty() { + println!(" Serial Number:{}", data.serial); } } _ => {} @@ -2165,7 +2172,7 @@ fn me_info(verbose: bool, dump_path: Option<&str>) { None } }; - data.map(|d| smbioslib::SMBiosData::from_vec_and_version(d, None)) + data.and_then(|d| smbios::SmbiosStore::from_table_data(d, 3, 0)) } else { get_smbios() }; diff --git a/framework_lib/src/csme.rs b/framework_lib/src/csme.rs index 63f806bf..962f759c 100644 --- a/framework_lib/src/csme.rs +++ b/framework_lib/src/csme.rs @@ -4,7 +4,6 @@ //! - Linux sysfs: reads from /sys/class/mei //! - SMBIOS type 0xDB: OEM table with HFSTS registers (works on any platform) -use alloc::string::ToString; use alloc::vec::Vec; use core::fmt; #[cfg(target_os = "linux")] @@ -14,7 +13,8 @@ use std::io; #[cfg(target_os = "linux")] use std::path::Path; -use smbioslib::{DefinedStruct, UndefinedStruct}; +use crate::smbios::SmbiosStore; +use dmidecode::{InfoType, RawStructure, Structure}; /// SMBIOS type for ME Firmware Status (FWSTS) table pub const SMBIOS_TYPE_ME_FWSTS: u8 = 0xDB; @@ -397,14 +397,14 @@ impl MeSmbiosInfo { /// - Offset 6+: records, each 25 bytes: /// - 1 byte: component name /// - 24 bytes: 6 x u32le HFSTS registers -pub fn parse_me_fwsts(undefined_struct: &UndefinedStruct) -> Option { +pub fn parse_me_fwsts(raw: &RawStructure) -> Option { // Verify this is type 0xDB - if undefined_struct.header.struct_type() != SMBIOS_TYPE_ME_FWSTS { + if raw.info != InfoType::Oem(SMBIOS_TYPE_ME_FWSTS) { return None; } - let length = undefined_struct.header.length() as usize; - let handle = *undefined_struct.header.handle(); + let length = raw.length as usize; + let handle = raw.handle; // Minimum header size: 6 bytes (type, length, handle, version, count) if length < 6 { @@ -412,8 +412,8 @@ pub fn parse_me_fwsts(undefined_struct: &UndefinedStruct) -> Option(4).ok()?; + let count = raw.get::(5).ok()?; // Version should be 0x01 if version != 0x01 { @@ -432,15 +432,15 @@ pub fn parse_me_fwsts(undefined_struct: &UndefinedStruct) -> Option(record_offset).ok()?); // Parse 6 HFSTS registers (u32le each) - let hfsts1 = undefined_struct.get_field_dword(record_offset + 1)?; - let hfsts2 = undefined_struct.get_field_dword(record_offset + 5)?; - let hfsts3 = undefined_struct.get_field_dword(record_offset + 9)?; - let hfsts4 = undefined_struct.get_field_dword(record_offset + 13)?; - let hfsts5 = undefined_struct.get_field_dword(record_offset + 17)?; - let hfsts6 = undefined_struct.get_field_dword(record_offset + 21)?; + let hfsts1 = raw.get::(record_offset + 1).ok()?; + let hfsts2 = raw.get::(record_offset + 5).ok()?; + let hfsts3 = raw.get::(record_offset + 9).ok()?; + let hfsts4 = raw.get::(record_offset + 13).ok()?; + let hfsts5 = raw.get::(record_offset + 17).ok()?; + let hfsts6 = raw.get::(record_offset + 21).ok()?; records.push(MeFwstsRecord { component, @@ -492,19 +492,18 @@ impl fmt::Display for MeFviVersion { /// /// Looks for Group Associations with "$MEI" or "Firmware Version Info" group name /// and returns the handles they point to. -pub fn find_me_handles_from_type14(smbios: &smbioslib::SMBiosData) -> Vec { +pub fn find_me_handles_from_type14(smbios: &SmbiosStore) -> Vec { let mut handles = Vec::new(); - for undefined_struct in smbios.iter() { - if let DefinedStruct::GroupAssociations(group) = undefined_struct.defined_struct() { + for result in smbios.structures() { + if let Ok(Structure::GroupAssociations(group)) = result { // Check if this group is ME-related - let group_name = group.group_name().to_string(); - if group_name.contains("$MEI") || group_name.contains("Firmware Version Info") { + if group.group_name.contains("$MEI") + || group.group_name.contains("Firmware Version Info") + { // Collect all handles from this group - for item in group.item_iterator() { - if let Some(handle) = item.item_handle() { - handles.push(*handle); - } + for item in group.items { + handles.push(item.handle); } } } @@ -514,7 +513,7 @@ pub fn find_me_handles_from_type14(smbios: &smbioslib::SMBiosData) -> Vec { } /// Get ME version from SMBIOS type 0xDD (FVI) tables -pub fn me_version_from_smbios(smbios: &smbioslib::SMBiosData) -> Option { +pub fn me_version_from_smbios(smbios: &SmbiosStore) -> Option { // First try to find handles from Type 14 let handles = find_me_handles_from_type14(smbios); @@ -523,11 +522,13 @@ pub fn me_version_from_smbios(smbios: &smbioslib::SMBiosData) -> Option = None; - for undefined_struct in smbios.iter() { - if let Some(version) = parse_me_fvi_version(undefined_struct, &handles) { - // Keep the version with the highest major number - if best_version.is_none() || version.major > best_version.as_ref().unwrap().major { - best_version = Some(version); + for result in smbios.structures() { + if let Ok(Structure::Other(ref raw)) = result { + if let Some(version) = parse_me_fvi_version(raw, &handles) { + // Keep the version with the highest major number + if best_version.is_none() || version.major > best_version.as_ref().unwrap().major { + best_version = Some(version); + } } } } @@ -538,16 +539,13 @@ pub fn me_version_from_smbios(smbios: &smbioslib::SMBiosData) -> Option Option { - if undefined_struct.header.struct_type() != SMBIOS_TYPE_ME_FVI { +fn parse_me_fvi_version(raw: &RawStructure, valid_handles: &[u16]) -> Option { + if raw.info != InfoType::Oem(SMBIOS_TYPE_ME_FVI) { return None; } - let handle = *undefined_struct.header.handle(); - let length = undefined_struct.header.length() as usize; + let handle = raw.handle; + let length = raw.length as usize; let is_valid_handle = if valid_handles.is_empty() { handle == SMBIOS_DD_HANDLE_ME || handle == SMBIOS_DD_HANDLE_ME2 @@ -559,7 +557,7 @@ fn parse_me_fvi_version( return None; } - let count = undefined_struct.get_field_byte(4)?; + let count = raw.get::(4).ok()?; if count == 0 { return None; } @@ -575,14 +573,14 @@ fn parse_me_fvi_version( break; } - let component_name = undefined_struct.get_field_byte(offset)?; + let component_name = raw.get::(offset).ok()?; // Check components 1, 2, and 3 - ME version location varies by system if component_name == 1 || component_name == 2 || component_name == 3 { - let major = undefined_struct.get_field_byte(offset + 2)?; - let minor = undefined_struct.get_field_byte(offset + 3)?; - let patch = undefined_struct.get_field_byte(offset + 4)?; - let build = undefined_struct.get_field_word(offset + 5)?; + let major = raw.get::(offset + 2).ok()?; + let minor = raw.get::(offset + 3).ok()?; + let patch = raw.get::(offset + 4).ok()?; + let build = raw.get::(offset + 5).ok()?; // Skip invalid versions (all 0xFF) if major == 0xFF && minor == 0xFF && patch == 0xFF { @@ -605,15 +603,17 @@ fn parse_me_fvi_version( } /// Get ME FWSTS info from SMBIOS tables -pub fn me_fwsts_from_smbios(smbios: &smbioslib::SMBiosData) -> Option { +pub fn me_fwsts_from_smbios(smbios: &SmbiosStore) -> Option { // For type 0xDB, we look for any table with MEI1 component // (unlike 0xDD, the 0xDB table doesn't need handle validation from Type 14) - for undefined_struct in smbios.iter() { - if undefined_struct.header.struct_type() == SMBIOS_TYPE_ME_FWSTS { - if let Some(info) = parse_me_fwsts(undefined_struct) { - // Only return if we found a MEI1 record - if info.mei1().is_some() { - return Some(info); + for result in smbios.structures() { + if let Ok(Structure::Other(ref raw)) = result { + if raw.info == InfoType::Oem(SMBIOS_TYPE_ME_FWSTS) { + if let Some(info) = parse_me_fwsts(raw) { + // Only return if we found a MEI1 record + if info.mei1().is_some() { + return Some(info); + } } } } @@ -747,19 +747,19 @@ pub fn csme_from_sysfs() -> io::Result { #[cfg(test)] mod tests { use super::*; - use smbioslib::SMBiosData; + use crate::smbios::SmbiosStore; use std::fs; use std::path::PathBuf; /// Load SMBIOS data from a dmidecode binary dump file /// Created with: sudo dmidecode --dump-bin smbios.bin - fn load_smbios_dump(filename: &str) -> Option { + fn load_smbios_dump(filename: &str) -> Option { let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); path.push("test_bins"); path.push(filename); match fs::read(&path) { - Ok(data) => Some(SMBiosData::from_vec_and_version(data, None)), + Ok(data) => SmbiosStore::from_table_data(data, 3, 0), Err(_) => { println!( "Test file not found: {:?}. Create with: sudo dmidecode --dump-bin {:?}", @@ -771,7 +771,7 @@ mod tests { } /// Create synthetic SMBIOS data with ME FWSTS table for testing - fn create_synthetic_smbios_with_me() -> SMBiosData { + fn create_synthetic_smbios_with_me() -> SmbiosStore { let mut data = Vec::new(); // Type 0xDB (219) - ME FWSTS table @@ -826,7 +826,7 @@ mod tests { data.push(0x00); data.push(0x00); - SMBiosData::from_vec_and_version(data, None) + SmbiosStore::from_table_data(data, 3, 0).unwrap() } #[test] @@ -859,7 +859,7 @@ mod tests { mod $name { use super::*; - fn smbios() -> SMBiosData { + fn smbios() -> SmbiosStore { load_smbios_dump($file) .unwrap_or_else(|| panic!("Dump file not found: {}", $file)) } diff --git a/framework_lib/src/fw_uefi/mod.rs b/framework_lib/src/fw_uefi/mod.rs index 82f823a4..e870f7c5 100644 --- a/framework_lib/src/fw_uefi/mod.rs +++ b/framework_lib/src/fw_uefi/mod.rs @@ -51,78 +51,40 @@ pub fn enable_page_break() { unsafe { (shell.enable_page_break)() } } -#[repr(C, packed)] -pub struct Smbios { - pub anchor: [u8; 4], - pub checksum: u8, - pub length: u8, - pub major_version: u8, - pub minor_version: u8, - pub max_structure_size: u16, - pub revision: u8, - pub formatted: [u8; 5], - pub inter_anchor: [u8; 5], - pub inter_checksum: u8, - pub table_length: u16, - pub table_address: u32, - pub structure_count: u16, - pub bcd_revision: u8, -} - -impl Smbios { - pub fn checksum_valid(&self) -> bool { - let mut sum: u8 = self.anchor.iter().sum::(); - sum += self.checksum; - sum += self.length; - sum += self.major_version; - sum += self.minor_version; - sum += self.max_structure_size as u8; - sum += self.revision; - sum += self.formatted.iter().sum::(); - sum == 0 - } -} +/// Size of SMBIOS v2 entry point structure (31 bytes) +const SMBIOS_V2_EP_SIZE: usize = 31; +/// Size of SMBIOS v3 entry point structure (24 bytes) +const SMBIOS_V3_EP_SIZE: usize = 24; -pub struct Smbios3 { - pub anchor: [u8; 5], - pub checksum: u8, - pub length: u8, - pub major_version: u8, - pub minor_version: u8, - pub docrev: u8, - pub revision: u8, - _reserved: u8, - pub table_length: u32, - pub table_address: u64, -} +pub fn smbios_data() -> Option<(Vec, Vec)> { + use dmidecode::EntryPoint; -pub fn smbios_data() -> Option> { with_config_table(|slice| { for i in slice { - let table_data = match i.guid { - ConfigTableEntry::SMBIOS3_GUID => unsafe { - let smbios = &*(i.address as *const Smbios3); - debug!("SMBIOS3 valid: {:?}", smbios.anchor == *b"_SM3_"); - Some(slice::from_raw_parts( - smbios.table_address as *const u8, - smbios.table_length as usize, - )) - }, - ConfigTableEntry::SMBIOS_GUID => unsafe { - let smbios = &*(i.address as *const Smbios); - debug!("SMBIOS valid: {:?}", smbios.checksum_valid()); - Some(slice::from_raw_parts( - smbios.table_address as *const u8, - smbios.table_length as usize, - )) - }, + let ep_size = match i.guid { + ConfigTableEntry::SMBIOS3_GUID => Some(SMBIOS_V3_EP_SIZE), + ConfigTableEntry::SMBIOS_GUID => Some(SMBIOS_V2_EP_SIZE), _ => None, }; - - if let Some(data) = table_data { - // Return directly here because there is only ever the old config - // table or the new V3 config table. Never both. - return Some(data.to_vec()); + if let Some(size) = ep_size { + unsafe { + let ep_ptr = i.address as *const u8; + let ep_bytes = slice::from_raw_parts(ep_ptr, size); + if let Ok(entry) = EntryPoint::search(ep_bytes) { + debug!( + "SMBIOS entry point found, version {}.{}", + entry.major(), + entry.minor() + ); + let table_data = slice::from_raw_parts( + entry.smbios_address() as *const u8, + entry.smbios_len() as usize, + ); + // Return directly here because there is only ever the old config + // table or the new V3 config table. Never both. + return Some((ep_bytes.to_vec(), table_data.to_vec())); + } + } } } diff --git a/framework_lib/src/smbios.rs b/framework_lib/src/smbios.rs index 76ce335e..3ef77b70 100644 --- a/framework_lib/src/smbios.rs +++ b/framework_lib/src/smbios.rs @@ -2,14 +2,11 @@ use std::prelude::v1::*; -#[cfg(all(not(feature = "uefi"), not(target_os = "freebsd")))] -use std::io::ErrorKind; - use crate::util::Config; pub use crate::util::{Platform, PlatformFamily}; +use dmidecode::{EntryPoint, Structure}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; -use smbioslib::*; #[cfg(feature = "uefi")] use spin::Mutex; #[cfg(not(feature = "uefi"))] @@ -24,6 +21,61 @@ static CACHED_PLATFORM: Mutex>> = Mutex::new(None); // TODO: Should cache SMBIOS and values gotten from it // SMBIOS is fixed after boot. Oh, so maybe not cache when we're running in UEFI +/// Wrapper around dmidecode's EntryPoint + raw table data. +/// Owns the data and provides iteration over SMBIOS structures. +pub struct SmbiosStore { + entry_point: EntryPoint, + table_data: Vec, +} + +impl SmbiosStore { + /// Parse from raw table data with a synthetic entry point. + /// Used for tests, dump files, and Windows (where only table data is available). + pub fn from_table_data(data: Vec, major: u8, minor: u8) -> Option { + let ep_bytes = synthetic_entry_point_v3(major, minor, data.len() as u32); + let entry_point = EntryPoint::search(&ep_bytes).ok()?; + Some(SmbiosStore { + entry_point, + table_data: data, + }) + } + + /// Parse from entry point bytes + table data. + /// Used for Linux sysfs, FreeBSD, and UEFI where both are available separately. + pub fn from_parts(entry_point_bytes: &[u8], table_data: Vec) -> Option { + let entry_point = EntryPoint::search(entry_point_bytes).ok()?; + Some(SmbiosStore { + entry_point, + table_data, + }) + } + + /// Iterate SMBIOS structures + pub fn structures(&self) -> dmidecode::Structures<'_> { + self.entry_point.structures(&self.table_data) + } +} + +/// Build a valid 24-byte SMBIOS v3 entry point with correct checksum. +fn synthetic_entry_point_v3(major: u8, minor: u8, table_len: u32) -> [u8; 24] { + let mut ep = [0u8; 24]; + ep[0..5].copy_from_slice(b"_SM3_"); + // [5] = checksum (computed below) + ep[6] = 24; // length + ep[7] = major; + ep[8] = minor; + // [9] = docrev, [11] = reserved — left as 0 + ep[10] = 1; // entry point revision + ep[12..16].copy_from_slice(&table_len.to_le_bytes()); + // [16..24] = smbios_address — left as 0, we pass table data directly + + // Compute checksum so all bytes sum to 0 + let sum: u8 = ep.iter().fold(0u8, |acc, &b| acc.wrapping_add(b)); + ep[5] = 0u8.wrapping_sub(sum); + + ep +} + #[repr(u8)] #[derive(Debug, PartialEq, FromPrimitive, Clone, Copy)] pub enum ConfigDigit0 { @@ -63,159 +115,19 @@ pub fn is_framework() -> bool { return maker == "Framework"; } - let smbios = if let Some(smbios) = get_smbios() { - smbios - } else { + let Some(smbios) = get_smbios() else { return false; }; - for undefined_struct in smbios.iter() { - if let DefinedStruct::SystemInformation(data) = undefined_struct.defined_struct() { - if let Some(manufacturer) = dmidecode_string_val(&data.manufacturer()) { - return manufacturer == "Framework"; - } + for result in smbios.structures() { + if let Ok(Structure::System(sys)) = result { + return sys.manufacturer == "Framework"; } } false } -pub fn dmidecode_string_val(s: &SMBiosString) -> Option { - match s.as_ref() { - Ok(val) if val.is_empty() => Some("Not Specified".to_owned()), - Ok(val) => Some(val.to_owned()), - Err(SMBiosStringError::FieldOutOfBounds) => None, - Err(SMBiosStringError::InvalidStringNumber(_)) => Some("".to_owned()), - Err(SMBiosStringError::Utf8(val)) => { - Some(String::from_utf8_lossy(&val.clone().into_bytes()).to_string()) - } - } -} - -#[cfg(target_os = "freebsd")] -#[repr(C)] -pub struct Smbios3 { - pub anchor: [u8; 5], - pub checksum: u8, - pub length: u8, - pub major_version: u8, - pub minor_version: u8, - pub docrev: u8, - pub revision: u8, - _reserved: u8, - pub table_length: u32, - pub table_address: u64, -} - -#[cfg(target_os = "freebsd")] -#[repr(C, packed)] -pub struct Smbios { - pub anchor: [u8; 4], - pub checksum: u8, - pub length: u8, - pub major_version: u8, - pub minor_version: u8, - pub max_structure_size: u16, - pub revision: u8, - pub formatted: [u8; 5], - pub inter_anchor: [u8; 5], - pub inter_checksum: u8, - pub table_length: u16, - pub table_address: u32, - pub structure_count: u16, - pub bcd_revision: u8, -} - -#[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"); - let addr = u64::from_str_radix(addr_hex, 16).unwrap(); - trace!("SMBIOS Entrypoint Addr: {} 0x{:x}", addr_hex, addr); - - let mut dev_mem = std::fs::File::open("/dev/mem").ok()?; - // Smbios struct is larger than Smbios3 struct - let mut header_buf = [0; std::mem::size_of::()]; - dev_mem.seek(SeekFrom::Start(addr)).ok()?; - dev_mem.read_exact(&mut header_buf).ok()?; - - let entrypoint = unsafe { &*(header_buf.as_ptr() as *const Smbios3) }; - - trace!("SMBIOS Anchor {:?} = ", entrypoint.anchor); - let (addr, len, version) = match entrypoint.anchor { - [b'_', b'S', b'M', b'3', b'_'] => { - trace!("_SM3_"); - let entrypoint = unsafe { &*(header_buf.as_ptr() as *const Smbios3) }; - let ver = Some(SMBiosVersion { - major: entrypoint.major_version, - minor: entrypoint.minor_version, - revision: 0, - }); - - (entrypoint.table_address, entrypoint.table_length, ver) - } - [b'_', b'S', b'M', b'_', _] => { - trace!("_SM_"); - let entrypoint = unsafe { &*(header_buf.as_ptr() as *const Smbios) }; - let ver = Some(SMBiosVersion { - major: entrypoint.major_version, - minor: entrypoint.minor_version, - revision: 0, - }); - - ( - entrypoint.table_address as u64, - entrypoint.table_length as u32, - ver, - ) - } - [b'_', b'D', b'M', b'I', b'_'] => { - error!("_DMI_ - UNSUPPORTED"); - return None; - } - _ => { - error!(" Unknown - UNSUPPORTED"); - return None; - } - }; - - // Get actual SMBIOS table data - let mut smbios_buf = vec![0; len as usize]; - dev_mem.seek(SeekFrom::Start(addr)).ok()?; - dev_mem.read_exact(&mut smbios_buf).ok()?; - - let smbios = SMBiosData::from_vec_and_version(smbios_buf, version); - Some(smbios) -} - -#[cfg(feature = "uefi")] -pub fn get_smbios() -> Option { - trace!("get_smbios() uefi entry"); - let data = crate::fw_uefi::smbios_data().unwrap(); - let version = None; // TODO: Maybe add the version here - let smbios = SMBiosData::from_vec_and_version(data, version); - Some(smbios) -} -// On Linux this reads either from /dev/mem or sysfs -// 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 => { - println!("Must be root to get SMBIOS data."); - None - } - Err(err) => { - println!("Failed to get SMBIOS: {:?}", err); - None - } - } -} - pub fn get_product_name() -> Option { // On FreeBSD we can short-circuit and avoid parsing SMBIOS #[cfg(target_os = "freebsd")] @@ -223,51 +135,38 @@ pub fn get_product_name() -> Option { return Some(product); } - let smbios = get_smbios(); - if smbios.is_none() { + let Some(smbios) = get_smbios() else { println!("Failed to find SMBIOS"); return None; - } - let mut smbios = smbios.into_iter().flatten(); - smbios.find_map(|undefined_struct| { - if let DefinedStruct::SystemInformation(data) = undefined_struct.defined_struct() { - if let Some(product_name) = dmidecode_string_val(&data.product_name()) { - return Some(product_name.as_str().to_string()); - } - } - None + }; + smbios.structures().find_map(|result| match result { + Ok(Structure::System(sys)) if !sys.product.is_empty() => Some(sys.product.to_string()), + _ => None, }) } pub fn get_baseboard_version() -> Option { - // TODO: On FreeBSD we can short-circuit and avoid parsing SMBIOS - // #[cfg(target_os = "freebsd")] - // if let Ok(product) = kenv_get("smbios.system.product") { - // return Some(product); - // } - - let smbios = get_smbios(); - if smbios.is_none() { + let Some(smbios) = get_smbios() else { error!("Failed to find SMBIOS"); return None; - } - let mut smbios = smbios.into_iter().flatten(); - smbios.find_map(|undefined_struct| { - if let DefinedStruct::BaseBoardInformation(data) = undefined_struct.defined_struct() { - if let Some(version) = dmidecode_string_val(&data.version()) { - // 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(::from_u8) - { - return version_config; - } else { - debug!(" Invalid BaseBoard Version: {}'", version); - } + }; + smbios.structures().find_map(|result| { + let Ok(Structure::BaseBoard(board)) = result else { + return None; + }; + let version = board.version; + if version.is_empty() { + return None; + } + // Assumes it's ASCII, which is guaranteed by SMBIOS + let config_digit0 = u8::from_str_radix(&version[0..1], 16); + match config_digit0.map(::from_u8) { + Ok(version_config) => version_config, + Err(_) => { + debug!(" Invalid BaseBoard Version: {}'", version); + None } } - None }) } @@ -326,6 +225,94 @@ pub fn get_platform() -> Option { platform } +#[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"); + let addr = u64::from_str_radix(addr_hex, 16).unwrap(); + trace!("SMBIOS Entrypoint Addr: {} 0x{:x}", addr_hex, addr); + + let mut dev_mem = std::fs::File::open("/dev/mem").ok()?; + // Read enough bytes for either V2 (31 bytes) or V3 (24 bytes) entry point + let mut header_buf = [0u8; 32]; + dev_mem.seek(SeekFrom::Start(addr)).ok()?; + dev_mem.read_exact(&mut header_buf).ok()?; + + let entry = EntryPoint::search(&header_buf).ok()?; + let table_addr = entry.smbios_address(); + let table_len = entry.smbios_len() as usize; + + let mut table_data = vec![0u8; table_len]; + dev_mem.seek(SeekFrom::Start(table_addr)).ok()?; + dev_mem.read_exact(&mut table_data).ok()?; + + SmbiosStore::from_parts(&header_buf, table_data) +} + +#[cfg(feature = "uefi")] +pub fn get_smbios() -> Option { + trace!("get_smbios() uefi entry"); + let (ep_bytes, table_data) = crate::fw_uefi::smbios_data()?; + SmbiosStore::from_parts(&ep_bytes, table_data) +} + +#[cfg(target_os = "linux")] +pub fn get_smbios() -> Option { + trace!("get_smbios() linux entry"); + let ep_bytes = match std::fs::read("/sys/firmware/dmi/tables/smbios_entry_point") { + Ok(data) => data, + Err(ref e) if e.kind() == std::io::ErrorKind::PermissionDenied => { + println!("Must be root to get SMBIOS data."); + return None; + } + Err(err) => { + println!("Failed to get SMBIOS: {:?}", err); + return None; + } + }; + let table_data = match std::fs::read("/sys/firmware/dmi/tables/DMI") { + Ok(data) => data, + Err(err) => { + println!("Failed to read SMBIOS table: {:?}", err); + return None; + } + }; + SmbiosStore::from_parts(&ep_bytes, table_data) +} + +#[cfg(windows)] +pub fn get_smbios() -> Option { + trace!("get_smbios() windows entry"); + use windows::Win32::System::SystemInformation::{ + GetSystemFirmwareTable, FIRMWARE_TABLE_PROVIDER, + }; + + let signature = FIRMWARE_TABLE_PROVIDER(u32::from_be_bytes(*b"RSMB")); + let size = unsafe { GetSystemFirmwareTable(signature, 0, None) }; + if size == 0 { + println!("Failed to get SMBIOS table size"); + return None; + } + + let mut buf = vec![0u8; size as usize]; + let written = unsafe { GetSystemFirmwareTable(signature, 0, Some(&mut buf)) }; + if written == 0 { + println!("Failed to read SMBIOS table data"); + return None; + } + + // RSMB format: [Used20CallingMethod(1), Major(1), Minor(1), DmiRevision(1), Length(4), TableData...] + if buf.len() < 8 { + return None; + } + let major = buf[1]; + let minor = buf[2]; + let table_data = buf[8..].to_vec(); + SmbiosStore::from_table_data(table_data, major, minor) +} + #[cfg(target_os = "freebsd")] fn kenv_get(name: &str) -> nix::Result { use libc::{c_int, KENV_GET, KENV_MVALLEN};