use crate::{SMBiosStruct, UndefinedStruct};
use serde::{ser::SerializeSeq, ser::SerializeStruct, Serialize, Serializer};
use std::fmt;
use std::ops::Deref;
pub struct SMBiosSystemEventLog<'a> {
parts: &'a UndefinedStruct,
}
impl<'a> SMBiosStruct<'a> for SMBiosSystemEventLog<'a> {
const STRUCT_TYPE: u8 = 15u8;
fn new(parts: &'a UndefinedStruct) -> Self {
Self { parts }
}
fn parts(&self) -> &'a UndefinedStruct {
self.parts
}
}
impl<'a> SMBiosSystemEventLog<'a> {
const LOG_TYPE_DESCRIPTORS_OFFSET: usize = 0x17usize;
pub fn log_area_length(&self) -> Option<u16> {
self.parts.get_field_word(0x04)
}
pub fn log_header_start_offset(&self) -> Option<u16> {
self.parts.get_field_word(0x06)
}
pub fn log_data_start_offset(&self) -> Option<u16> {
self.parts.get_field_word(0x08)
}
pub fn access_method(&self) -> Option<AccessMethodData> {
self.parts
.get_field_byte(0x0A)
.map(|raw| AccessMethodData::from(raw))
}
pub fn log_status(&self) -> Option<LogStatus> {
self.parts
.get_field_byte(0x0B)
.map(|raw| LogStatus::from(raw))
}
pub fn log_change_token(&self) -> Option<u32> {
self.parts.get_field_dword(0x0C)
}
pub fn access_method_address(&self) -> Option<u32> {
self.parts.get_field_dword(0x10)
}
pub fn log_header_format(&self) -> Option<HeaderFormatData> {
self.parts
.get_field_byte(0x14)
.map(|raw| HeaderFormatData::from(raw))
}
pub fn number_of_supported_log_type_descriptors(&self) -> Option<u8> {
self.parts.get_field_byte(0x15)
}
pub fn length_of_each_log_type_descriptor(&self) -> Option<u8> {
self.parts.get_field_byte(0x16)
}
pub fn type_descriptors(&self) -> Option<TypeDescriptors<'_>> {
TypeDescriptors::new(self)
}
}
impl fmt::Debug for SMBiosSystemEventLog<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<SMBiosSystemEventLog<'_>>())
.field("header", &self.parts.header)
.field("log_area_length", &self.log_area_length())
.field("log_header_start_offset", &self.log_header_start_offset())
.field("log_data_start_offset", &self.log_data_start_offset())
.field("access_method", &self.access_method())
.field("log_status", &self.log_status())
.field("log_change_token", &self.log_change_token())
.field("access_method_address", &self.access_method_address())
.field("log_header_format", &self.log_header_format())
.field(
"number_of_supported_log_type_descriptors",
&self.number_of_supported_log_type_descriptors(),
)
.field(
"length_of_each_log_type_descriptor",
&self.length_of_each_log_type_descriptor(),
)
.field("type_descriptors", &self.type_descriptors())
.finish()
}
}
impl Serialize for SMBiosSystemEventLog<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("SMBiosSystemEventLog", 12)?;
state.serialize_field("header", &self.parts.header)?;
state.serialize_field("log_area_length", &self.log_area_length())?;
state.serialize_field("log_header_start_offset", &self.log_header_start_offset())?;
state.serialize_field("log_data_start_offset", &self.log_data_start_offset())?;
state.serialize_field("access_method", &self.access_method())?;
state.serialize_field("log_status", &self.log_status())?;
state.serialize_field("log_change_token", &self.log_change_token())?;
state.serialize_field("access_method_address", &self.access_method_address())?;
state.serialize_field("log_header_format", &self.log_header_format())?;
state.serialize_field(
"number_of_supported_log_type_descriptors",
&self.number_of_supported_log_type_descriptors(),
)?;
state.serialize_field(
"length_of_each_log_type_descriptor",
&self.length_of_each_log_type_descriptor(),
)?;
state.serialize_field("type_descriptors", &self.type_descriptors())?;
state.end()
}
}
pub struct LogTypeData {
pub raw: u8,
pub value: LogType,
}
impl fmt::Debug for LogTypeData {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<LogTypeData>())
.field("raw", &self.raw)
.field("value", &self.value)
.finish()
}
}
impl Serialize for LogTypeData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("LogTypeData", 2)?;
state.serialize_field("raw", &self.raw)?;
state.serialize_field("value", &self.value)?;
state.end()
}
}
impl fmt::Display for LogTypeData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.value {
LogType::None => write!(f, "{}", &self.raw),
_ => write!(f, "{:?}", &self.value),
}
}
}
impl Deref for LogTypeData {
type Target = LogType;
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Serialize, Debug, PartialEq, Eq)]
pub enum LogType {
SingleBitEccMemoryError,
MultiBitEccMemoryError,
ParityMemoryError,
BusTimeOut,
IOChannelCheck,
SoftwareNmi,
PostMemoryResize,
PostError,
PciParityError,
PciSystemError,
CpuFailure,
EisaFailSafeTimerTimeout,
CorrectableMemoryLogDisabled,
LoggingDisabledForSpecificEventType,
Reserved0F,
SystemLimitExceeded,
AsyncHardwareTimerExpired,
SystemConfigurationInformation,
HardDiskInformation,
SystemReconfigured,
UncorrectableCpuComplexError,
LogAreaReset,
SystemBoot,
None,
}
impl From<u8> for LogTypeData {
fn from(raw: u8) -> Self {
LogTypeData {
value: match raw {
0x01 => LogType::SingleBitEccMemoryError,
0x02 => LogType::MultiBitEccMemoryError,
0x03 => LogType::ParityMemoryError,
0x04 => LogType::BusTimeOut,
0x05 => LogType::IOChannelCheck,
0x06 => LogType::SoftwareNmi,
0x07 => LogType::PostMemoryResize,
0x08 => LogType::PostError,
0x09 => LogType::PciParityError,
0x0A => LogType::PciSystemError,
0x0B => LogType::CpuFailure,
0x0C => LogType::EisaFailSafeTimerTimeout,
0x0D => LogType::CorrectableMemoryLogDisabled,
0x0E => LogType::LoggingDisabledForSpecificEventType,
0x0F => LogType::Reserved0F,
0x10 => LogType::SystemLimitExceeded,
0x11 => LogType::AsyncHardwareTimerExpired,
0x12 => LogType::SystemConfigurationInformation,
0x13 => LogType::HardDiskInformation,
0x14 => LogType::SystemReconfigured,
0x15 => LogType::UncorrectableCpuComplexError,
0x16 => LogType::LogAreaReset,
0x17 => LogType::SystemBoot,
_ => LogType::None,
},
raw,
}
}
}
pub struct VariableDataFormatTypeData {
pub raw: u8,
pub value: VariableDataFormatType,
}
impl fmt::Debug for VariableDataFormatTypeData {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<VariableDataFormatTypeData>())
.field("raw", &self.raw)
.field("value", &self.value)
.finish()
}
}
impl Serialize for VariableDataFormatTypeData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("VariableDataFormatTypeData", 2)?;
state.serialize_field("raw", &self.raw)?;
state.serialize_field("value", &self.value)?;
state.end()
}
}
impl fmt::Display for VariableDataFormatTypeData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.value {
VariableDataFormatType::None => write!(f, "{}", &self.raw),
_ => write!(f, "{:?}", &self.value),
}
}
}
impl Deref for VariableDataFormatTypeData {
type Target = VariableDataFormatType;
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Serialize, Debug, PartialEq, Eq)]
pub enum VariableDataFormatType {
NoStandardFormat,
Handle,
MultipleEvent,
MultipleEventHandle,
PostResultsBitmap,
SystemManagementType,
MultipleEventSystemManagementType,
None,
}
impl From<u8> for VariableDataFormatTypeData {
fn from(raw: u8) -> Self {
VariableDataFormatTypeData {
value: match raw {
0x00 => VariableDataFormatType::NoStandardFormat,
0x01 => VariableDataFormatType::Handle,
0x02 => VariableDataFormatType::MultipleEvent,
0x03 => VariableDataFormatType::MultipleEventHandle,
0x04 => VariableDataFormatType::PostResultsBitmap,
0x05 => VariableDataFormatType::SystemManagementType,
0x06 => VariableDataFormatType::MultipleEventSystemManagementType,
_ => VariableDataFormatType::None,
},
raw,
}
}
}
pub struct AccessMethodData {
pub raw: u8,
pub value: AccessMethod,
}
impl fmt::Debug for AccessMethodData {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<AccessMethodData>())
.field("raw", &self.raw)
.field("value", &self.value)
.finish()
}
}
impl Serialize for AccessMethodData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("AccessMethodData", 2)?;
state.serialize_field("raw", &self.raw)?;
state.serialize_field("value", &self.value)?;
state.end()
}
}
impl Deref for AccessMethodData {
type Target = AccessMethod;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl From<u8> for AccessMethodData {
fn from(raw: u8) -> Self {
AccessMethodData {
value: AccessMethod::from(raw),
raw,
}
}
}
#[derive(Serialize, Debug, PartialEq, Eq)]
pub enum AccessMethod {
IndexedIO18Bit,
IndexedIO28Bit,
IndexedIO116Bit,
MemoryMapped32Bit,
GeneralPurposeNonVolatile,
None,
}
impl From<u8> for AccessMethod {
fn from(raw: u8) -> Self {
match raw {
0x00 => AccessMethod::IndexedIO18Bit,
0x01 => AccessMethod::IndexedIO28Bit,
0x02 => AccessMethod::IndexedIO116Bit,
0x03 => AccessMethod::MemoryMapped32Bit,
0x04 => AccessMethod::GeneralPurposeNonVolatile,
_ => AccessMethod::None,
}
}
}
pub struct EventLogTypeDescriptor<'a> {
pub raw: &'a [u8],
}
impl<'a> EventLogTypeDescriptor<'a> {
const MINIMUM_RAW_SIZE: usize = 2usize;
const LOG_TYPE_OFFSET: usize = 0usize;
const VARIABLE_DATA_FORMAT_TYPE_OFFSET: usize = 1usize;
fn new(raw: &'a [u8]) -> Option<Self> {
if raw.len() < Self::MINIMUM_RAW_SIZE {
None
} else {
Some(Self { raw })
}
}
pub fn log_type(&self) -> LogTypeData {
LogTypeData::from(self.raw[Self::LOG_TYPE_OFFSET])
}
pub fn variable_data_format_type(&self) -> VariableDataFormatTypeData {
VariableDataFormatTypeData::from(self.raw[Self::VARIABLE_DATA_FORMAT_TYPE_OFFSET])
}
}
impl<'a> fmt::Debug for EventLogTypeDescriptor<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<EventLogTypeDescriptor<'_>>())
.field("raw", &self.raw)
.field("log_type", &self.log_type())
.field(
"variable_data_format_type",
&self.variable_data_format_type(),
)
.finish()
}
}
impl<'a> Serialize for EventLogTypeDescriptor<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("EventLogTypeDescriptor", 3)?;
state.serialize_field("raw", &self.raw)?;
state.serialize_field("log_type", &self.log_type())?;
state.serialize_field(
"variable_data_format_type",
&self.variable_data_format_type(),
)?;
state.end()
}
}
pub struct TypeDescriptors<'a> {
raw: &'a [u8],
record_count: usize,
record_length: usize,
}
impl<'a> TypeDescriptors<'a> {
fn new(system_event_log: &'a SMBiosSystemEventLog<'a>) -> Option<Self> {
system_event_log
.length_of_each_log_type_descriptor()
.and_then(|record_length| {
system_event_log
.number_of_supported_log_type_descriptors()
.and_then(|record_count| {
system_event_log
.parts()
.get_field_data(
SMBiosSystemEventLog::LOG_TYPE_DESCRIPTORS_OFFSET,
SMBiosSystemEventLog::LOG_TYPE_DESCRIPTORS_OFFSET
+ (record_length as usize * record_count as usize),
)
.and_then(|raw| {
Some(Self {
raw,
record_count: record_count as usize,
record_length: record_length as usize,
})
})
})
})
}
}
impl<'a> fmt::Debug for TypeDescriptors<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<TypeDescriptors<'_>>())
.field("descriptors", &self.into_iter())
.finish()
}
}
impl<'a> Serialize for TypeDescriptors<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.record_count))?;
for e in self {
seq.serialize_element(&e)?;
}
seq.end()
}
}
pub struct TypeDescriptorsIterator<'a> {
descriptors: &'a TypeDescriptors<'a>,
current_index: usize,
current_entry: usize,
}
impl<'a> TypeDescriptorsIterator<'a> {
fn reset(&mut self) {
self.current_index = 0;
self.current_entry = 0;
}
}
impl<'a> IntoIterator for &'a TypeDescriptors<'a> {
type Item = EventLogTypeDescriptor<'a>;
type IntoIter = TypeDescriptorsIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
TypeDescriptorsIterator {
descriptors: self,
current_index: 0,
current_entry: 0,
}
}
}
impl<'a> Iterator for TypeDescriptorsIterator<'a> {
type Item = EventLogTypeDescriptor<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_entry == self.descriptors.record_count {
self.reset();
return None;
}
let next_index = self.current_index + self.descriptors.record_length;
match EventLogTypeDescriptor::new(&self.descriptors.raw[self.current_index..next_index]) {
Some(event_log_type_descriptor) => {
self.current_index = next_index;
self.current_entry += 1;
Some(event_log_type_descriptor)
}
None => {
self.reset();
None
}
}
}
}
impl<'a> fmt::Debug for TypeDescriptorsIterator<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_list()
.entries(self.descriptors.into_iter())
.finish()
}
}
impl<'a> Serialize for TypeDescriptorsIterator<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let descriptors: Vec<EventLogTypeDescriptor<'_>> = self.descriptors.into_iter().collect();
let mut seq = serializer.serialize_seq(Some(descriptors.len()))?;
for e in descriptors {
seq.serialize_element(&e)?;
}
seq.end()
}
}
#[derive(PartialEq, Eq)]
pub struct LogStatus {
pub raw: u8,
}
impl Deref for LogStatus {
type Target = u8;
fn deref(&self) -> &Self::Target {
&self.raw
}
}
impl From<u8> for LogStatus {
fn from(raw: u8) -> Self {
LogStatus { raw }
}
}
impl LogStatus {
pub fn log_area_valid(&self) -> bool {
self.raw & 0x01 == 0x01
}
pub fn log_area_full(&self) -> bool {
self.raw & 0x02 == 0x02
}
}
impl fmt::Debug for LogStatus {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<LogStatus>())
.field("raw", &self.raw)
.field("log_area_valid", &self.log_area_valid())
.field("log_area_full", &self.log_area_full())
.finish()
}
}
impl Serialize for LogStatus {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("LogStatus", 3)?;
state.serialize_field("raw", &self.raw)?;
state.serialize_field("log_area_valid", &self.log_area_valid())?;
state.serialize_field("log_area_full", &self.log_area_full())?;
state.end()
}
}
pub struct HeaderFormatData {
pub raw: u8,
pub value: HeaderFormat,
}
impl fmt::Debug for HeaderFormatData {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(std::any::type_name::<HeaderFormatData>())
.field("raw", &self.raw)
.field("value", &self.value)
.finish()
}
}
impl Serialize for HeaderFormatData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("HeaderFormatData", 2)?;
state.serialize_field("raw", &self.raw)?;
state.serialize_field("value", &self.value)?;
state.end()
}
}
impl Deref for HeaderFormatData {
type Target = HeaderFormat;
fn deref(&self) -> &Self::Target {
&self.value
}
}
#[derive(Serialize, Debug, PartialEq, Eq)]
pub enum HeaderFormat {
NoHeader,
Type1LogHeader,
None,
}
impl From<u8> for HeaderFormatData {
fn from(raw: u8) -> Self {
HeaderFormatData {
value: match raw {
0x00 => HeaderFormat::NoHeader,
0x01 => HeaderFormat::Type1LogHeader,
_ => HeaderFormat::None,
},
raw,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unit_test() {
let struct_type15 = vec![
0x0F, 0x49, 0x3D, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x03, 0x01, 0x05, 0x00,
0x00, 0x00, 0x18, 0x20, 0xAE, 0x6A, 0x01, 0x19, 0x02, 0x01, 0x03, 0x02, 0x03, 0x03,
0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x04, 0x09, 0x03, 0x0A,
0x03, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12,
0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0xFF, 0x00, 0xE0,
0xE0, 0xE1, 0xE1, 0x00, 0x00,
];
let parts = UndefinedStruct::new(&struct_type15);
let test_struct = SMBiosSystemEventLog::new(&parts);
println!("{:?}", test_struct);
assert_eq!(test_struct.log_area_length(), Some(4096));
assert_eq!(test_struct.log_header_start_offset(), Some(0));
assert_eq!(test_struct.log_data_start_offset(), Some(16));
assert_eq!(
*test_struct.access_method().unwrap(),
AccessMethod::MemoryMapped32Bit
);
assert_eq!(
test_struct.log_status().unwrap(),
LogStatus::from(0b0000_0001)
);
assert_eq!(test_struct.log_change_token(), Some(5));
assert_eq!(test_struct.access_method_address(), Some(1789796376));
assert_eq!(
*test_struct.log_header_format().unwrap(),
HeaderFormat::Type1LogHeader
);
assert_eq!(
test_struct.number_of_supported_log_type_descriptors(),
Some(25)
);
assert_eq!(test_struct.length_of_each_log_type_descriptor(), Some(2));
let type_descriptors = test_struct.type_descriptors().unwrap();
let mut iterator = type_descriptors.into_iter();
let first = iterator.next().unwrap();
assert_eq!(*first.log_type(), LogType::SingleBitEccMemoryError);
}
}