1use core::convert::TryInto;
14use core::fmt;
15use core::hash::{Hash, Hasher};
16use core::slice::Chunks;
17
18use crate::{
19 bitfield::{BitField, FlagType, Layout},
20 InfoType,
21 MalformedStructureError::{self, InvalidFormattedSectionLength},
22 RawStructure,
23};
24
25pub mod log_record_format;
26pub use self::log_record_format::{EventLogType, VariableDataFormatType};
27
28#[derive(Clone, Debug, Eq, Hash, PartialEq)]
30pub struct SystemEventLog<'a> {
31 pub handle: u16,
33 pub log_area_length: u16,
36 pub log_header_start_offset: u16,
40 pub log_data_start_offset: u16,
44 pub access_method: AccessMethod,
45 pub log_status: LogStatus,
46 pub log_change_token: u32,
49 pub log_header_format: Option<LogHeaderFormat>,
51 pub supported_event_log_type_descriptors: Option<SupportedEventLogTypeDescriptors<'a>>,
53}
54
55#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
59pub enum AccessMethod {
60 IndexedIoOne8bitIndexOne8bitData { index: u8, data: u8 },
62 IndexedIoTwo8bitIndexOne8bitData { index: [u8; 2], data: u8 },
64 IndexedIoOne16bitIndexOne8bitData { index: u16, data: u8 },
66 MemoryMappedPhysicaAddress { physical_address: u32 },
68 GeneralPurposeNonVolatileData { gpnv_handle: u16 },
70 Available { method: u8, address: u32 },
72 OemSpecific { method: u8, address: u32 },
74}
75
76#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
81pub struct LogStatus(u8);
82
83#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
85pub enum LogHeaderFormat {
86 NoHeader,
88 LogHeaderType1,
90 Available(u8),
92 OemSpecific(u8),
94}
95
96#[derive(Clone, Debug)]
141pub struct SupportedEventLogTypeDescriptors<'a>(Chunks<'a, u8>);
142
143#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
149pub struct EventLogTypeDescriptor {
150 pub log_type: EventLogType,
151 pub variable_data_format_type: VariableDataFormatType,
152}
153
154impl<'a> SystemEventLog<'a> {
155 pub(crate) fn try_from(structure: RawStructure<'a>) -> Result<Self, MalformedStructureError> {
156 let handle = structure.handle;
157 let number_of_supported_log_type_descriptors = structure.get::<u8>(0x15).ok();
158 let length_of_each_log_type_descriptor = structure.get::<u8>(0x16).ok();
159 let len_gt_2_1 = number_of_supported_log_type_descriptors
160 .and_then(|x| length_of_each_log_type_descriptor.map(|y| 0x17 + x as usize * y as usize));
161 match (
162 (structure.version.major, structure.version.minor),
163 structure.data.len() + 4,
164 ) {
165 (v, l) if v == (2, 0) && l != 0x14 => Err(InvalidFormattedSectionLength(
166 InfoType::SystemEventLog,
167 handle,
168 "",
169 0x14,
170 )),
171 (v, l) if v >= (2, 1) && Some(l) != len_gt_2_1 => {
172 if let Some(len) = len_gt_2_1 {
173 Err(InvalidFormattedSectionLength(
174 InfoType::SystemEventLog,
175 handle,
176 "17h+(x*y) = ",
177 len as u8,
178 ))
179 } else {
180 Err(InvalidFormattedSectionLength(
181 InfoType::SystemEventLog,
182 handle,
183 "minimum of ",
184 0,
185 ))
186 }
187 }
188 _ => {
189 let access_method = {
190 let method = structure.get::<u8>(0x0A)?;
191 let address = structure.get::<u32>(0x10)?;
192 AccessMethod::new(method, address)
193 };
194 let supported_event_log_type_descriptors = (|| {
195 let number = number_of_supported_log_type_descriptors? as usize;
196 let length = length_of_each_log_type_descriptor? as usize;
197 let data = structure.get_slice(0x17, number * length)?;
198 Some(SupportedEventLogTypeDescriptors::new(data, length))
199 })();
200 Ok(Self {
201 handle,
202 log_area_length: structure.get::<u16>(0x04)?,
203 log_header_start_offset: structure.get::<u16>(0x06)?,
204 log_data_start_offset: structure.get::<u16>(0x08)?,
205 access_method,
206 log_status: structure.get::<u8>(0x0B)?.into(),
207 log_change_token: structure.get::<u32>(0x0C)?,
208 log_header_format: structure.get::<u8>(0x14).ok().map(Into::into),
209 supported_event_log_type_descriptors,
210 })
211 }
212 }
213 }
214}
215
216impl AccessMethod {
217 pub fn new(method: u8, address: u32) -> Self {
227 let [index_lsb, index_msb, data_lsb, _data_msb] = u32::to_le_bytes(address);
228 match method {
229 0x00 => Self::IndexedIoOne8bitIndexOne8bitData {
230 index: index_lsb,
231 data: data_lsb,
232 },
233 0x01 => Self::IndexedIoTwo8bitIndexOne8bitData {
234 index: [index_lsb, index_msb],
235 data: data_lsb,
236 },
237 0x02 => Self::IndexedIoOne16bitIndexOne8bitData {
238 index: u16::from_le_bytes([index_lsb, index_msb]),
239 data: data_lsb,
240 },
241 0x03 => Self::MemoryMappedPhysicaAddress {
242 physical_address: address,
243 },
244 0x04 => Self::GeneralPurposeNonVolatileData {
245 gpnv_handle: u16::from_le_bytes([index_lsb, index_msb]),
246 },
247 method @ 0x80..=0xFF => Self::OemSpecific { method, address },
248 method => Self::Available { method, address },
249 }
250 }
251 pub fn address(&self) -> u32 {
252 match self {
253 Self::IndexedIoOne8bitIndexOne8bitData { index, data } => u32::from_le_bytes([*index, 0, *data, 0]),
254 Self::IndexedIoTwo8bitIndexOne8bitData { index, data } => {
255 u32::from_le_bytes([index[0], index[1], *data, 0])
256 }
257 Self::IndexedIoOne16bitIndexOne8bitData { index, data } => {
258 let index = u16::to_le_bytes(*index);
259 u32::from_le_bytes([index[0], index[1], *data, 0])
260 }
261 Self::MemoryMappedPhysicaAddress { physical_address } => *physical_address,
262 Self::GeneralPurposeNonVolatileData { gpnv_handle } => *gpnv_handle as u32,
263 Self::OemSpecific { address, .. } => *address,
264 Self::Available { address, .. } => *address,
265 }
266 }
267}
268impl fmt::Display for AccessMethod {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 match (f.alternate(), self) {
271 (false, Self::IndexedIoOne8bitIndexOne8bitData { .. }) => {
272 write!(f, "Indexed I/O, one 8-bit index port, one 8-bit data port")
273 }
274 (false, Self::IndexedIoTwo8bitIndexOne8bitData { .. }) => {
275 write!(f, "Indexed I/O, two 8-bit index ports, one 8-bit data port")
276 }
277 (false, Self::IndexedIoOne16bitIndexOne8bitData { .. }) => {
278 write!(f, "Indexed I/O, one 16-bit index port, one 8-bit data port")
279 }
280 (false, Self::MemoryMappedPhysicaAddress { .. }) => write!(f, "Memory-mapped physical 32-bit address"),
281 (false, Self::GeneralPurposeNonVolatileData { .. }) => {
282 write!(f, "General-purpose non-volatile data functions")
283 }
284 (false, Self::OemSpecific { method, .. }) => write!(f, "OEM-specific: {}", method),
285 (false, Self::Available { method, .. }) => write!(f, "Available: {}", method),
286 (true, Self::IndexedIoOne8bitIndexOne8bitData { index, data }) => write!(
288 f,
289 "Indexed I/O, one 8-bit index port, one 8-bit data port: Index 0x{:02X}, Data 0x{:02X}",
290 index, data
291 ),
292 (true, Self::IndexedIoTwo8bitIndexOne8bitData { index, data }) => write!(
293 f,
294 "Indexed I/O, two 8-bit index ports, one 8-bit data port: Index {:X?}, Data 0x{:02X}",
295 index, data
296 ),
297 (true, Self::IndexedIoOne16bitIndexOne8bitData { index, data }) => write!(
298 f,
299 "Indexed I/O, one 16-bit index port, one 8-bit data port: Index 0x{:04X}, Data 0x{:02X}",
300 index, data
301 ),
302 (true, Self::MemoryMappedPhysicaAddress { physical_address }) => {
303 write!(f, "Memory-mapped physical 32-bit address: 0x{:08X}", physical_address)
304 }
305 (true, Self::GeneralPurposeNonVolatileData { gpnv_handle }) => write!(
306 f,
307 "General-Purpose NonVolatile Data functions, handle 0x{:04X}",
308 gpnv_handle
309 ),
310 (true, Self::OemSpecific { method, address }) => write!(
311 f,
312 "BIOS Vendor/OEM-specific: Method {}, Address 0x{:08X}",
313 method, address
314 ),
315 (true, Self::Available { method, address }) => {
316 write!(f, "Available: Method {}, Address 0x{:08X}", method, address)
317 }
318 }
319 }
320}
321
322impl BitField<'_> for LogStatus {
323 type Size = u8;
324 fn value(&self) -> Self::Size {
325 self.0
326 }
327 layout!(
328 length = 8;
329 "Valid" "Log area valid",
330 "Full" "Log area full",
331 "Reserved": 6,
332 );
333}
334impl From<u8> for LogStatus {
335 fn from(byte: u8) -> Self {
336 Self(byte)
337 }
338}
339
340impl From<u8> for LogHeaderFormat {
341 fn from(byte: u8) -> Self {
342 match byte {
343 0x00 => Self::NoHeader,
344 0x01 => Self::LogHeaderType1,
345 v @ 0x80..=0xFF => Self::OemSpecific(v),
346 v => Self::Available(v),
347 }
348 }
349}
350impl fmt::Display for LogHeaderFormat {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 match (f.alternate(), self) {
353 (_, Self::NoHeader) => write!(f, "No Header"),
354 (true, Self::LogHeaderType1) => write!(f, "Type 1 log header"),
355 (false, Self::LogHeaderType1) => write!(f, "Type 1"),
356 (true, Self::OemSpecific(v)) => write!(f, "BIOS vendor or OEM-specific format: {}", v),
357 (false, Self::OemSpecific(_)) => write!(f, "OEM-specific"),
358 (_, Self::Available(v)) => write!(f, "Available: {}", v),
359 }
360 }
361}
362
363impl<'a> SupportedEventLogTypeDescriptors<'a> {
364 fn new(data: &'a [u8], size: usize) -> Self {
365 Self(data.chunks(size))
366 }
367}
368impl PartialEq for SupportedEventLogTypeDescriptors<'_> {
369 fn eq(&self, other: &Self) -> bool {
370 self.0.clone().eq(other.0.clone())
371 }
372}
373impl Eq for SupportedEventLogTypeDescriptors<'_> {}
374impl Hash for SupportedEventLogTypeDescriptors<'_> {
375 fn hash<H: Hasher>(&self, state: &mut H) {
376 self.0.clone().for_each(|c| c.hash(state));
377 }
378}
379impl Iterator for SupportedEventLogTypeDescriptors<'_> {
380 type Item = EventLogTypeDescriptor;
381
382 fn next(&mut self) -> Option<Self::Item> {
383 let next = self.0.next()?;
384 next.try_into().ok().map(|a: [u8; 2]| EventLogTypeDescriptor {
385 log_type: a[0].into(),
386 variable_data_format_type: a[1].into(),
387 })
388 }
389}
390
391impl From<[u8; 2]> for EventLogTypeDescriptor {
392 fn from(a: [u8; 2]) -> Self {
393 Self {
394 log_type: a[0].into(),
395 variable_data_format_type: a[1].into(),
396 }
397 }
398}
399impl From<EventLogTypeDescriptor> for [u8; 2] {
400 fn from(d: EventLogTypeDescriptor) -> Self {
401 [d.log_type.into(), d.variable_data_format_type.into()]
402 }
403}
404
405#[cfg(test)]
406mod tests {
407 use pretty_assertions::assert_eq;
408 use std::prelude::v1::*;
409
410 #[test]
411 fn access_method() {
412 use super::AccessMethod;
413
414 let address = u32::from_le_bytes([0x78, 0x56, 0x34, 0x12]);
415 let data = &[
416 (
417 0,
418 "Indexed I/O, one 8-bit index port, one 8-bit data port: Index 0x78, Data 0x34",
419 ),
420 (
421 1,
422 "Indexed I/O, two 8-bit index ports, one 8-bit data port: Index [78, 56], Data 0x34",
423 ),
424 (
425 2,
426 "Indexed I/O, one 16-bit index port, one 8-bit data port: Index 0x5678, Data 0x34",
427 ),
428 (3, "Memory-mapped physical 32-bit address: 0x12345678"),
429 (4, "General-Purpose NonVolatile Data functions, handle 0x5678"),
430 (5, "Available: Method 5, Address 0x12345678"),
431 (0x80, "BIOS Vendor/OEM-specific: Method 128, Address 0x12345678"),
432 ];
433 for (m, s) in data {
434 assert_eq!(*s, format!("{:#}", AccessMethod::new(*m, address)));
435 }
436 }
437
438 #[test]
439 fn log_status() {
440 use super::LogStatus;
441 use crate::bitfield::BitField;
442
443 let byte: u8 = 0b111;
444 let ls: LogStatus = byte.into();
445 let sample = vec!["Log area valid", "Log area full"];
446 assert_eq!(
447 sample,
448 ls.significants().map(|v| format!("{:#}", v)).collect::<Vec<_>>()
449 );
450 }
451
452 #[test]
453 fn log_header_format() {
454 use super::LogHeaderFormat;
455
456 let data = &[
457 (0u8, "No Header"),
458 (1, "Type 1 log header"),
459 (2, "Available: 2"),
460 (0xFF, "BIOS vendor or OEM-specific format: 255"),
461 ];
462 for (v, s) in data {
463 assert_eq!(*s, format!("{:#}", LogHeaderFormat::from(*v)));
464 }
465 }
466
467 #[test]
468 fn supported_event_log_type_descriptors() {
469 use super::{
470 EventLogType as T, EventLogTypeDescriptor as Desc, SupportedEventLogTypeDescriptors,
471 VariableDataFormatType as D,
472 };
473
474 let data = &[
475 0x02, 0x00, 0x04, 0x01, 0x08, 0x02, 0x16, 0x03, 0x66, 0x00, 0xEE, 0x00, 0xFF, 0x00,
476 ];
477 let sample = vec![
478 Desc {
479 log_type: T::MultiBitEccMemoryError,
480 variable_data_format_type: D::None,
481 },
482 Desc {
483 log_type: T::BusTimeOut,
484 variable_data_format_type: D::Handle { handle: 0 },
485 },
486 Desc {
487 log_type: T::PostError,
488 variable_data_format_type: D::MultipleEvent { counter: 0 },
489 },
490 Desc {
491 log_type: T::LogAreaReset,
492 variable_data_format_type: D::MultipleEventHandle { handle: 0, counter: 0 },
493 },
494 Desc {
495 log_type: T::Unused(0x66),
496 variable_data_format_type: D::None,
497 },
498 Desc {
499 log_type: T::Available(0xEE),
500 variable_data_format_type: D::None,
501 },
502 Desc {
503 log_type: T::EndOfLog,
504 variable_data_format_type: D::None,
505 },
506 ];
507 let result = SupportedEventLogTypeDescriptors::new(data, 2);
508 assert_eq!(sample, result.collect::<Vec<_>>());
509 }
510
511 #[test]
512 fn system_event_log() {
513 use super::EventLogType as T;
514 use super::VariableDataFormatType as D;
515 use super::*;
516 use crate::{bitfield::Position, InfoType, RawStructure};
517
518 let length = 77 - 4;
519 let (data, strings) =
520 include_bytes!("../../../tests/data/02daadcd/entries/15-0/bin")[4..].split_at(length as usize);
521 let structure = RawStructure {
522 version: (2, 7).into(),
523 info: InfoType::SystemEventLog,
524 length,
525 handle: 0x0036,
526 data,
527 strings,
528 };
529 let result = SystemEventLog::try_from(structure).unwrap();
530
531 let access_method = AccessMethod::MemoryMappedPhysicaAddress {
532 physical_address: 0xFFC40000,
533 };
534 assert_eq!(access_method, result.access_method, "AccessMethod");
535
536 let log_status = [Position(0)].iter().collect::<u8>().into();
537 assert_eq!(log_status, result.log_status, "LogStatus");
538
539 let seltd_length = result.supported_event_log_type_descriptors.clone().unwrap().count();
540 assert_eq!(27, seltd_length, "Supported Log Type Descriptors count");
541
542 let seltd_sample = [
543 (T::SingleBitEccMemoryError, D::None),
544 (T::MultiBitEccMemoryError, D::None),
545 (T::ParityMemoryError, D::None),
546 (T::BusTimeOut, D::None),
547 (T::IoChannelCheck, D::None),
548 (T::SoftwareNmi, D::None),
549 (T::PostMemoryResize, D::None),
550 (T::PostError, D::PostResults(0.into())),
551 (T::PciParityError, D::None),
552 (T::PciSystemError, D::None),
553 (T::CpuFailure, D::None),
554 (T::EisaFailSafeTimerTimeOut, D::None),
555 (T::CorrectableMemoryLogDisabled, D::None),
556 (T::LoggingDisabledForSpecificEventType, D::None),
557 (T::SystemLimitExceeded, D::None),
558 (T::AsynchronousHardwareTimerExpired, D::None),
559 (T::SystemConfigurationInformation, D::None),
560 (T::HardDiskInformation, D::None),
561 (T::SystemReconfigured, D::None),
562 (T::UncorrectableCpuComplexError, D::None),
563 (T::LogAreaReset, D::None),
564 (T::SystemBoot, D::None),
565 (T::EndOfLog, D::None),
566 (T::Available(0xB0), D::OemAssigned(0xB0)),
567 (T::Available(0xB1), D::OemAssigned(0xB1)),
568 (T::Available(0xE0), D::OemAssigned(0xE0)),
569 (T::Available(0xE1), D::OemAssigned(0xE1)),
570 ]
571 .iter()
572 .map(|(t, d)| EventLogTypeDescriptor {
573 log_type: *t,
574 variable_data_format_type: *d,
575 })
576 .collect::<Vec<_>>();
577 let seltd_result = result
578 .supported_event_log_type_descriptors
579 .clone()
580 .unwrap()
581 .collect::<Vec<_>>();
582 assert_eq!(seltd_sample, seltd_result, "SupportedEventLogTypeDescriptors");
583
584 let sample_bytes = seltd_sample.iter().fold(Vec::new(), |mut vec: Vec<u8>, eltd| {
585 vec.push(eltd.log_type.into());
586 vec.push(eltd.variable_data_format_type.into());
587 vec
588 });
589 let sample = SystemEventLog {
590 handle: 0x0036,
591 log_area_length: 16383,
592 log_header_start_offset: 0x0000,
593 log_data_start_offset: 0x0010,
594 access_method,
595 log_status,
596 log_change_token: 0x00000001,
597 log_header_format: Some(LogHeaderFormat::LogHeaderType1),
598 supported_event_log_type_descriptors: Some(SupportedEventLogTypeDescriptors::new(&sample_bytes, 2)),
599 };
600 assert_eq!(sample, result, "SystemEventLog");
601 }
602}