Skip to main content

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::io;
213
214    #[test]
215    #[cfg(any(target_os = "linux"))]
216    fn test_dev_mem_scan() -> io::Result<()> {
217        let entry_path = std::path::Path::new(SYS_ENTRY_FILE);
218
219        match SMBiosEntryPoint64::try_load_from_file(entry_path) {
220            Ok(entry_point) => {
221                println!(
222                    "SMBIOS {}.{}.{} present.",
223                    entry_point.major_version(),
224                    entry_point.minor_version(),
225                    entry_point.docrev()
226                );
227                println!(
228                    "Occupying {} bytes maximum.",
229                    entry_point.structure_table_maximum_size()
230                );
231                println!("Table at: {:#018X}.", entry_point.structure_table_address());
232            }
233            Err(err) => match err.kind() {
234                ErrorKind::InvalidData => {
235                    let entry_point = SMBiosEntryPoint32::try_load_from_file(entry_path)?;
236                    println!(
237                        "SMBIOS {}.{} present.",
238                        entry_point.major_version(),
239                        entry_point.minor_version()
240                    );
241                    println!(
242                        "{} structures occupying {} bytes.",
243                        entry_point.number_of_smbios_structures(),
244                        entry_point.structure_table_length()
245                    );
246                    println!("Table at: {:#010X}.", entry_point.structure_table_address());
247                }
248                _ => return Err(err),
249            },
250        }
251
252        let data = SMBiosData::try_load_from_file(SYS_TABLE_FILE, None)?;
253        println!("{:#X?}", data);
254
255        Ok(())
256    }
257
258    #[test]
259    #[cfg(any(target_os = "freebsd"))]
260    fn test_dev_mem_scan() -> io::Result<()> {
261        use std::fs::File;
262
263        const RANGE_START: u64 = 0x000F0000u64;
264        const RANGE_END: u64 = 0x000FFFFFu64;
265        let mut dev_mem = File::open(DEV_MEM_FILE)?;
266        let structure_table_address: u64;
267        let structure_table_length: u32;
268
269        match SMBiosEntryPoint32::try_scan_from_file(&mut dev_mem, RANGE_START..=RANGE_END) {
270            Ok(entry_point) => {
271                structure_table_address = entry_point.structure_table_address() as u64;
272                structure_table_length = entry_point.structure_table_length() as u32;
273                println!(
274                    "SMBIOS {}.{} present.",
275                    entry_point.major_version(),
276                    entry_point.minor_version()
277                );
278                println!(
279                    "{} structures occupying {} bytes.",
280                    entry_point.number_of_smbios_structures(),
281                    entry_point.structure_table_length()
282                );
283                println!("Table at: {:#010X}.", entry_point.structure_table_address());
284            }
285            Err(error) => {
286                if error.kind() != ErrorKind::UnexpectedEof {
287                    return Err(error);
288                }
289
290                let entry_point =
291                    SMBiosEntryPoint64::try_scan_from_file(&mut dev_mem, RANGE_START..=RANGE_END)?;
292
293                structure_table_address = entry_point.structure_table_address();
294                structure_table_length = entry_point.structure_table_maximum_size();
295
296                println!(
297                    "SMBIOS {}.{}.{} present.",
298                    entry_point.major_version(),
299                    entry_point.minor_version(),
300                    entry_point.docrev()
301                );
302                println!(
303                    "Occupying {} bytes maximum.",
304                    entry_point.structure_table_maximum_size()
305                );
306                println!("Table at: {:#010X}.", entry_point.structure_table_address());
307            }
308        }
309
310        if structure_table_address + structure_table_length as u64 > RANGE_END {
311            return Err(Error::new(
312                ErrorKind::InvalidData,
313                format!(
314                    "The entry point has given a length which exceeds the range: {}",
315                    structure_table_length
316                ),
317            ));
318        }
319
320        let table = UndefinedStructTable::try_load_from_file_offset(
321            &mut dev_mem,
322            structure_table_address,
323            structure_table_length as usize,
324        )?;
325        println!("{:#X?}", table);
326
327        Ok(())
328    }
329}