1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
|
use crate::*;
use std::{io::Error, io::ErrorKind};
#[cfg(any(target_os = "linux"))]
/// Full path to smbios_entry_point file on Linux (contains entry point data)
pub const SYS_ENTRY_FILE: &'static str = "/sys/firmware/dmi/tables/smbios_entry_point";
#[cfg(any(target_os = "linux"))]
/// Full path to the DMI file on Linux (contains BIOS table data)
pub const SYS_TABLE_FILE: &'static str = "/sys/firmware/dmi/tables/DMI";
/// Full path to the memory device (contains BIOS entry point and table data on *nix platforms)
pub const DEV_MEM_FILE: &'static str = "/dev/mem";
// Example of Linux structure:
/*
/sys/firmware/dmi/tables$ sudo hexdump -C smbios_entry_point
00000000 5f 53 4d 33 5f 7e 18 03 03 00 01 00 83 04 00 00 |_SM3_~..........|
00000010 00 20 b0 7b 00 00 00 00 |. .{....|
00000018
Note: _SM3_ indicates the version of the entry point. Offsets 0x7-0x9 are
the BIOS version 0x03, 0x03, 0x00 (3.0.0)
jeff@blacktop:/sys/firmware/dmi/tables$ sudo hexdump -C DMI
00000000 00 1a 00 00 01 02 00 00 03 ff 80 18 19 0c 00 00 |................|
00000010 00 00 03 0d ff ff ff ff 00 00 4d 69 63 72 6f 73 |..........Micros|
00000020 6f 66 74 20 43 6f 72 70 6f 72 61 74 69 6f 6e 00 |oft Corporation.|
00000030 39 2e 31 30 32 2e 31 34 30 00 31 31 2f 31 36 2f |9.102.140.11/16/|
00000040 32 30 32 30 00 00 01 1b 01 00 01 02 03 04 86 76 |2020...........v|
00000050 fb 97 d5 7b 15 d0 b2 39 6b ba a4 df c0 45 02 05 |...{...9k....E..|
00000060 06 4d 69 63 72 6f 73 6f 66 74 20 43 6f 72 70 6f |.Microsoft Corpo|
00000070 72 61 74 69 6f 6e 00 53 75 72 66 61 63 65 20 4c |ration.Surface L|
00000080 61 70 74 6f 70 20 33 00 31 32 34 49 3a 30 30 30 |aptop 3.124I:000|
00000090 33 36 54 3a 30 30 30 4d 3a 30 33 30 30 30 30 30 |36T:000M:0300000|
000000a0 44 3a 30 42 3a 30 37 46 3a 31 43 3a 30 35 50 3a |D:0B:07F:1C:05P:|
000000b0 34 38 53 3a 30 31 45 3a 30 59 3a 31 4b 3a 30 55 |48S:01E:0Y:1K:0U|
000000c0 3a 30 38 00 30 30 31 39 35 33 33 30 32 30 35 37 |:08.001953302057|
000000d0 00 53 75 72 66 61 63 65 5f 4c 61 70 74 6f 70 5f |.Surface_Laptop_|
000000e0 33 5f 31 38 37 32 00 53 75 72 66 61 63 65 00 00 |3_1872.Surface..|
*/
// Note: /sys/class/dmi/id contains some of the BIOS values, already parsed by the kernel.
// These are useful for cross checking against the results this library produces when reading
// /sys/firmware/dmi/tables/DMI
#[cfg(any(target_os = "linux"))]
/// Loads [SMBiosData] from the device via /sys/firmware/dmi/tables (on Linux)
pub fn table_load_from_device() -> Result<SMBiosData, Error> {
let version: SMBiosVersion;
let entry_path = std::path::Path::new(SYS_ENTRY_FILE);
match SMBiosEntryPoint64::try_load_from_file(entry_path) {
Ok(entry_point) => {
version = SMBiosVersion {
major: entry_point.major_version(),
minor: entry_point.minor_version(),
revision: entry_point.docrev(),
}
}
Err(err) => match err.kind() {
ErrorKind::InvalidData => match SMBiosEntryPoint32::try_load_from_file(entry_path) {
Ok(entry_point) => {
version = SMBiosVersion {
major: entry_point.major_version(),
minor: entry_point.minor_version(),
revision: 0,
}
}
Err(err) => return Err(err),
},
_ => return Err(err),
},
}
SMBiosData::try_load_from_file(SYS_TABLE_FILE, Some(version))
}
#[cfg(any(target_os = "freebsd"))]
/// Loads [SMBiosData] from the device via /dev/mem (on FreeBSD)
pub fn table_load_from_device() -> Result<SMBiosData, Error> {
const RANGE_START: u64 = 0x000F0000u64;
const RANGE_END: u64 = 0x000FFFFFu64;
let structure_table_address: u64;
let structure_table_length: u32;
let version: SMBiosVersion;
let mut dev_mem = std::fs::File::open(DEV_MEM_FILE)?;
match SMBiosEntryPoint32::try_scan_from_file(&mut dev_mem, RANGE_START..=RANGE_END) {
Ok(entry_point) => {
structure_table_address = entry_point.structure_table_address() as u64;
structure_table_length = entry_point.structure_table_length() as u32;
version = SMBiosVersion {
major: entry_point.major_version(),
minor: entry_point.minor_version(),
revision: 0,
}
}
Err(error) => {
if error.kind() != ErrorKind::UnexpectedEof {
return Err(error);
}
let entry_point =
SMBiosEntryPoint64::try_scan_from_file(&mut dev_mem, RANGE_START..=RANGE_END)?;
structure_table_address = entry_point.structure_table_address();
structure_table_length = entry_point.structure_table_maximum_size();
version = SMBiosVersion {
major: entry_point.major_version(),
minor: entry_point.minor_version(),
revision: entry_point.docrev(),
}
}
}
if structure_table_address + structure_table_length as u64 > RANGE_END {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"The entry point has given a length which exceeds the range: {}",
structure_table_length
),
));
}
let table = UndefinedStructTable::try_load_from_file_offset(
&mut dev_mem,
structure_table_address,
structure_table_length as usize,
)?;
Ok(SMBiosData::new(table, Some(version)))
}
#[cfg(any(target_os = "linux"))]
/// Returns smbios raw data via /sys/firmware/dmi/tables (on Linux)
pub fn raw_smbios_from_device() -> Result<Vec<u8>, Error> {
Ok(std::fs::read(SYS_TABLE_FILE)?)
}
#[cfg(any(target_os = "freebsd"))]
/// Returns smbios raw data via /dev/mem (on FreeBSD)
pub fn raw_smbios_from_device() -> Result<Vec<u8>, Error> {
use std::io::{prelude::*, SeekFrom};
const RANGE_START: u64 = 0x000F0000u64;
const RANGE_END: u64 = 0x000FFFFFu64;
let structure_table_address: u64;
let structure_table_length: usize;
let mut dev_mem = std::fs::File::open(DEV_MEM_FILE)?;
match SMBiosEntryPoint32::try_scan_from_file(&mut dev_mem, RANGE_START..=RANGE_END) {
Ok(entry_point) => {
structure_table_address = entry_point.structure_table_address() as u64;
structure_table_length = entry_point.structure_table_length() as usize;
}
Err(error) => {
if error.kind() != ErrorKind::UnexpectedEof {
return Err(error);
}
let entry_point =
SMBiosEntryPoint64::try_scan_from_file(&mut dev_mem, RANGE_START..=RANGE_END)?;
structure_table_address = entry_point.structure_table_address();
structure_table_length = entry_point.structure_table_maximum_size() as usize;
}
}
if structure_table_address < RANGE_START || structure_table_address > RANGE_END {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"The entry point has given an out of range start address for the table: {}",
structure_table_address
),
));
}
if structure_table_address + structure_table_length as u64 > RANGE_END {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"The entry point has given a length which exceeds the range: {}",
structure_table_length
),
));
}
if structure_table_length < Header::SIZE + 2 {
return Err(Error::new(
ErrorKind::InvalidData,
format!("The table has an invalid size: {}", structure_table_length),
));
}
dev_mem.seek(SeekFrom::Start(structure_table_address))?;
let mut table = Vec::with_capacity(structure_table_length);
table.resize(structure_table_length, 0);
dev_mem.read_exact(&mut table)?;
Ok(table)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::io;
#[test]
#[ignore = "/dev/mem is not available in the test environment"]
fn test_dev_mem_scan() -> io::Result<()> {
const RANGE_START: u64 = 0x000F0000u64;
const RANGE_END: u64 = 0x000FFFFFu64;
let mut dev_mem = File::open(DEV_MEM_FILE)?;
let structure_table_address: u64;
let structure_table_length: u32;
match SMBiosEntryPoint32::try_scan_from_file(&mut dev_mem, RANGE_START..=RANGE_END) {
Ok(entry_point) => {
structure_table_address = entry_point.structure_table_address() as u64;
structure_table_length = entry_point.structure_table_length() as u32;
println!(
"SMBIOS {}.{} present.",
entry_point.major_version(),
entry_point.minor_version()
);
println!(
"{} structures occupying {} bytes.",
entry_point.number_of_smbios_structures(),
entry_point.structure_table_length()
);
println!("Table at: {:#010X}.", entry_point.structure_table_address());
}
Err(error) => {
if error.kind() != ErrorKind::UnexpectedEof {
return Err(error);
}
let entry_point =
SMBiosEntryPoint64::try_scan_from_file(&mut dev_mem, RANGE_START..=RANGE_END)?;
structure_table_address = entry_point.structure_table_address();
structure_table_length = entry_point.structure_table_maximum_size();
println!(
"SMBIOS {}.{}.{} present.",
entry_point.major_version(),
entry_point.minor_version(),
entry_point.docrev()
);
println!(
"Occupying {} bytes maximum.",
entry_point.structure_table_maximum_size()
);
println!("Table at: {:#010X}.", entry_point.structure_table_address());
}
}
if structure_table_address + structure_table_length as u64 > RANGE_END {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"The entry point has given a length which exceeds the range: {}",
structure_table_length
),
));
}
let table = UndefinedStructTable::try_load_from_file_offset(
&mut dev_mem,
structure_table_address,
structure_table_length as usize,
)?;
println!("{:#X?}", table);
Ok(())
}
}
|