Skip to main content

intel_crashlog/source/acpi/
sysfs.rs

1// Copyright (C) 2026 Intel Corporation
2// SPDX-License-Identifier: MIT
3
4use crate::CrashLog;
5use crate::bert::Berr;
6use crate::error::Error;
7use crate::metadata::Time;
8use std::path::{Path, PathBuf};
9
10pub(super) struct AcpiSysFs {
11    root: PathBuf,
12}
13
14impl Default for AcpiSysFs {
15    fn default() -> Self {
16        Self::new(Path::new("/"))
17    }
18}
19
20impl AcpiSysFs {
21    pub fn new(path: &Path) -> Self {
22        Self {
23            root: path.to_owned(),
24        }
25    }
26
27    fn tables_path(&self) -> PathBuf {
28        let mut path = self.root.clone();
29        path.push("sys");
30        path.push("firmware");
31        path.push("acpi");
32        path.push("tables");
33        path
34    }
35
36    fn berr_path(&self) -> PathBuf {
37        let mut path = self.tables_path();
38        path.push("data");
39        path.push("BERT");
40        path
41    }
42
43    pub fn extract(&self) -> Result<CrashLog, Error> {
44        let path = self.berr_path();
45        let berr = std::fs::read(&path)
46            .map_err(|err| {
47                log::warn!("Cannot read {}: {err}", path.display());
48                match err.kind() {
49                    std::io::ErrorKind::NotFound => Error::NoCrashLogFound,
50                    _ => Error::from(err),
51                }
52            })
53            .and_then(|berr| {
54                log::info!("Found ACPI boot error record in sysfs");
55                Berr::from_slice(&berr).ok_or(Error::InvalidBootErrorRecordRegion)
56            })?;
57
58        let mut crashlog = CrashLog::from_berr(berr)?;
59        crashlog.metadata.time = Time::now();
60        Ok(crashlog)
61    }
62}
63
64impl CrashLog {
65    /// Reads the Crash Log reported through ACPI from the Linux sysfs
66    pub fn from_acpi_sysfs() -> Result<Self, Error> {
67        AcpiSysFs::default().extract()
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use tempfile;
75
76    #[test]
77    fn extract() {
78        let root = tempfile::tempdir().unwrap();
79
80        let acpi = AcpiSysFs::new(root.path());
81
82        let mut berr_path = root.path().to_owned();
83        berr_path.push("sys");
84        berr_path.push("firmware");
85        berr_path.push("acpi");
86        berr_path.push("tables");
87        berr_path.push("data");
88        std::fs::create_dir_all(&berr_path).unwrap();
89
90        berr_path.push("BERT");
91
92        let bert = std::fs::read("tests/samples/dummy.bert").unwrap();
93        let crashlog = CrashLog::from_slice(&bert).unwrap();
94
95        let berr = Berr::from_crashlog(&crashlog);
96        std::fs::write(berr_path, berr.to_bytes()).unwrap();
97
98        let mut extracted_crashlog = acpi.extract().unwrap();
99        assert!(extracted_crashlog.metadata.time.is_some());
100
101        extracted_crashlog.metadata.time = None;
102
103        assert_eq!(crashlog.to_bytes(), extracted_crashlog.to_bytes());
104    }
105}