Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion app/src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,16 @@ pub fn extract(output_path: Option<&Path>) {
}
#[cfg(target_os = "linux")]
{
result = CrashLog::from_linux_sysfs().map(|crashlog| Vec::from([crashlog]));
let crashlogs: Vec<CrashLog> = [CrashLog::from_acpi_sysfs(), CrashLog::from_pmt_sysfs()]
.into_iter()
.filter_map(|crashlog| crashlog.ok())
.collect();

result = if crashlogs.is_empty() {
Err(Error::NoCrashLogFound)
} else {
Ok(crashlogs)
};
}

match result {
Expand Down
26 changes: 5 additions & 21 deletions lib/src/crashlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@ use crate::bert::{Berr, Bert};
#[cfg(feature = "collateral_manager")]
use crate::collateral::{CollateralManager, CollateralTree};
use crate::cper::Cper;
#[cfg(feature = "extraction")]
use crate::extract;
use crate::metadata::Metadata;
use crate::node::Node;
use crate::region::Region;
#[cfg(not(feature = "std"))]
use alloc::{collections::VecDeque, vec, vec::Vec};
#[cfg(target_os = "uefi")]
use core::ptr::NonNull;
#[cfg(feature = "std")]
use std::collections::VecDeque;
#[cfg(target_os = "uefi")]
use uefi_raw::table::system::SystemTable;

use crate::header::HeaderType;
use crate::header::record_types;
Expand All @@ -33,7 +27,7 @@ pub struct CrashLog {
}

impl CrashLog {
fn from_regions(regions: Vec<Region>) -> Result<Self, Error> {
pub(crate) fn from_regions(regions: Vec<Region>) -> Result<Self, Error> {
let mut queue = VecDeque::from(regions);
let mut regions = Vec::new();

Expand Down Expand Up @@ -85,27 +79,17 @@ impl CrashLog {
CrashLog::from_regions(regions)
}

#[cfg(all(target_os = "uefi", feature = "extraction"))]
/// Reads the Crash Log records from the EFI System Table.
pub fn from_system_table(system_table: Option<NonNull<SystemTable>>) -> Result<Self, Error> {
extract::efi::get_crashlog_from_system_table(system_table)
}

#[cfg(any(all(target_os = "windows", feature = "extraction"), doc))]
/// Searches for any Intel Crash Log logged in the Windows event log
/// Searches for any Intel Crash Log logged in the Windows event logs.
///
/// The Windows event logs typically captures the records reported through ACPI.
pub fn from_windows_event_logs(path: Option<&std::path::Path>) -> Result<Vec<Self>, Error> {
extract::event_log::get_crashlogs_from_event_logs(path).map_err(|err| {
Self::from_event_logs(path).map_err(|err| {
log::error!("Error while accessing windows event logs: {err}");
Error::InternalError
})
}

#[cfg(any(all(target_os = "linux", feature = "extraction"), doc))]
/// Reads the Crash Log reported through ACPI from the linux sysfs
pub fn from_linux_sysfs() -> Result<Self, Error> {
extract::sysfs::read_berr_from_sysfs().and_then(CrashLog::from_berr)
}

/// Extracts the Crash Log records from [Cper] record.
pub(crate) fn from_cper(cper: Cper) -> Result<Self, Error> {
let regions: Vec<Region> = cper
Expand Down
51 changes: 26 additions & 25 deletions lib/src/extract/efi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,32 +73,33 @@ pub fn find_bert() -> Result<Bert, Error> {
})
}

pub(crate) fn get_crashlog_from_system_table(
system_table: Option<NonNull<SystemTable>>,
) -> Result<CrashLog, Error> {
if let Some(system_table) = system_table {
unsafe { uefi::table::set_system_table(system_table.as_ptr()) }
}

let mut crashlog = find_bert()
.and_then(|bert| {
unsafe { bert.berr_from_phys_mem() }.ok_or(Error::InvalidBootErrorRecordRegion)
})
.and_then(CrashLog::from_berr)?;
impl CrashLog {
/// Reads the Crash Log records from the EFI System Table.
pub fn from_system_table(system_table: Option<NonNull<SystemTable>>) -> Result<Self, Error> {
if let Some(system_table) = system_table {
unsafe { uefi::table::set_system_table(system_table.as_ptr()) }
}

crashlog.metadata = metadata::Metadata {
computer: Some("efi".to_string()),
time: uefi::runtime::get_time()
.map(|time| metadata::Time {
year: time.year(),
month: time.month(),
day: time.day(),
hour: time.hour(),
minute: time.minute(),
let mut crashlog = find_bert()
.and_then(|bert| {
unsafe { bert.berr_from_phys_mem() }.ok_or(Error::InvalidBootErrorRecordRegion)
})
.inspect_err(|err| log::warn!("Cannot get time: {err}"))
.ok(),
};
.and_then(CrashLog::from_berr)?;

crashlog.metadata = metadata::Metadata {
computer: Some("efi".to_string()),
time: uefi::runtime::get_time()
.map(|time| metadata::Time {
year: time.year(),
month: time.month(),
day: time.day(),
hour: time.hour(),
minute: time.minute(),
})
.inspect_err(|err| log::warn!("Cannot get time: {err}"))
.ok(),
};

Ok(crashlog)
Ok(crashlog)
}
}
66 changes: 34 additions & 32 deletions lib/src/extract/event_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,37 +209,39 @@ fn query_crashlogs(path: PCWSTR, query: PCWSTR, query_flags: u32) -> Result<Vec<
Ok(crashlogs)
}

pub(crate) fn get_crashlogs_from_event_logs(path: Option<&Path>) -> Result<Vec<CrashLog>> {
let evtx_path_hstring = path.map(HSTRING::from);
let evtx_path = evtx_path_hstring
.as_ref()
.map(|hstring| PCWSTR(hstring.as_ptr()));
let query_flags = if path.is_some() {
EvtQueryFilePath.0
} else {
EvtQueryChannelPath.0
};
impl CrashLog {
pub(crate) fn from_event_logs(path: Option<&Path>) -> Result<Vec<Self>> {
let evtx_path_hstring = path.map(HSTRING::from);
let evtx_path = evtx_path_hstring
.as_ref()
.map(|hstring| PCWSTR(hstring.as_ptr()));
let query_flags = if path.is_some() {
EvtQueryFilePath.0
} else {
EvtQueryChannelPath.0
};

let mut crashlogs = query_crashlogs(
evtx_path.unwrap_or(w!("Microsoft-Windows-Kernel-WHEA/Errors")),
w!("*[System[Provider[@Name=\"Microsoft-Windows-Kernel-WHEA\"]]]"),
query_flags,
)?;
log::info!(
"Extracted {} Crash Logs from Application Event Logs",
crashlogs.len()
);

let mut system_crashlogs = query_crashlogs(
evtx_path.unwrap_or(w!("System")),
w!("*[System[Provider[@Name=\"Microsoft-Windows-WHEA-Logger\"]]]"),
query_flags,
)?;
log::info!(
"Extracted {} Crash Logs from Windows Event Logs",
system_crashlogs.len()
);

crashlogs.append(&mut system_crashlogs);
Ok(crashlogs)
let mut crashlogs = query_crashlogs(
evtx_path.unwrap_or(w!("Microsoft-Windows-Kernel-WHEA/Errors")),
w!("*[System[Provider[@Name=\"Microsoft-Windows-Kernel-WHEA\"]]]"),
query_flags,
)?;
log::info!(
"Extracted {} Crash Logs from Application Event Logs",
crashlogs.len()
);

let mut system_crashlogs = query_crashlogs(
evtx_path.unwrap_or(w!("System")),
w!("*[System[Provider[@Name=\"Microsoft-Windows-WHEA-Logger\"]]]"),
query_flags,
)?;
log::info!(
"Extracted {} Crash Logs from Windows Event Logs",
system_crashlogs.len()
);

crashlogs.append(&mut system_crashlogs);
Ok(crashlogs)
}
}
74 changes: 67 additions & 7 deletions lib/src/extract/sysfs.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,76 @@
// Copyright (C) 2025 Intel Corporation
// SPDX-License-Identifier: MIT

use crate::CrashLog;
use crate::bert::Berr;
use crate::error::Error;
use crate::region::Region;

const BERR_PATH: &str = "/sys/firmware/acpi/tables/data/BERT";
const PMT_PATH: &str = "/sys/class/intel_pmt";

pub(crate) fn read_berr_from_sysfs() -> Result<Berr, Error> {
std::fs::read(BERR_PATH)
.map_err(|err| match err.kind() {
std::io::ErrorKind::NotFound => Error::NoCrashLogFound,
_ => Error::from(err),
})
.and_then(|berr| Berr::from_slice(&berr).ok_or(Error::InvalidBootErrorRecordRegion))
impl CrashLog {
/// Reads the Crash Log reported through ACPI from the linux sysfs
pub fn from_acpi_sysfs() -> Result<Self, Error> {
let berr = std::fs::read(BERR_PATH)
.map_err(|err| {
log::warn!("Cannot read {BERR_PATH}: {err}");
match err.kind() {
std::io::ErrorKind::NotFound => Error::NoCrashLogFound,
_ => Error::from(err),
}
})
.and_then(|berr| {
log::info!("Found ACPI boot error record in sysfs");
Berr::from_slice(&berr).ok_or(Error::InvalidBootErrorRecordRegion)
})?;

Self::from_berr(berr)
}

/// Reads the Crash Log reported through Intel PMT from the linux sysfs
pub fn from_pmt_sysfs() -> Result<Self, Error> {
let regions: Vec<Region> = std::fs::read_dir(PMT_PATH)
.map_err(|err| {
log::warn!("Cannot read {PMT_PATH}: {err}");
match err.kind() {
std::io::ErrorKind::NotFound => Error::NoCrashLogFound,
_ => Error::from(err),
}
})?
.filter_map(|entry| {
entry
.inspect_err(|err| log::error!("Cannot access directory entry: {err}"))
.ok()
})
.filter(|entry| {
let is_dir = entry
.file_type()
.map(|file_type| file_type.is_dir())
.unwrap_or(false);
let is_crashlog_dir = entry
.file_name()
.to_str()
.map(|name| name.starts_with("crashlog"))
.unwrap_or(false);

is_dir && is_crashlog_dir
})
.filter_map(|entry| {
let mut path = entry.path();

log::info!("Found Crash Log entry in PMT sysfs: {}", path.display());
path.push("crashlog");

std::fs::read(&path)
.map_err(Error::IOError)
.and_then(|region| Region::from_slice(&region))
.inspect(|_| log::info!("Extracted valid record from {}", path.display()))
.inspect_err(|err| log::error!("{}: {err}", path.display()))
.ok()
})
.collect();

Self::from_regions(regions)
}
}
21 changes: 17 additions & 4 deletions lib/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,28 @@ pub extern "C" fn crashlog_read_from_windows_event_logs(
.unwrap_or(ptr::null_mut())
}

/// Reads the Crash Log reported through ACPI from the linux sysfs.
/// Reads the Crash Log reported through ACPI from the linux sysfs
///
/// # Errors
///
/// Returns a `NULL` pointer if the Crash Log records cannot be found.
/// Returns a `NULL` pointer if the Crash Log record cannot be found.
#[cfg(any(all(target_os = "linux", feature = "extraction"), doc))]
#[unsafe(no_mangle)]
pub extern "C" fn crashlog_read_from_acpi_sysfs(context: *mut CrashLogContext) -> *mut CrashLog {
CrashLog::from_acpi_sysfs()
.map(alloc)
.unwrap_or(ptr::null_mut())
}

/// Reads the Crash Log reported through Intel PMT from the linux sysfs
///
/// # Errors
///
/// Returns a `NULL` pointer if the Crash Log record cannot be found.
#[cfg(any(all(target_os = "linux", feature = "extraction"), doc))]
#[unsafe(no_mangle)]
pub extern "C" fn crashlog_read_from_linux_sysfs(context: *mut CrashLogContext) -> *mut CrashLog {
CrashLog::from_linux_sysfs()
pub extern "C" fn crashlog_read_from_pmt_sysfs(context: *mut CrashLogContext) -> *mut CrashLog {
CrashLog::from_pmt_sysfs()
.map(alloc)
.unwrap_or(ptr::null_mut())
}
Expand Down