smbioslib/unix/
platform.rs

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