#![allow(missing_docs)]
#![allow(clippy::float_cmp)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::items_after_statements)]
#![allow(clippy::field_reassign_with_default)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::assertions_on_constants)]
#![allow(clippy::unnecessary_unwrap)]
use smmu::fault::processing::{FaultMode, FaultProcessingError, FaultProcessor};
use smmu::types::{AccessType, FaultRecord, FaultType, SecurityState, StreamID, IOVA, PASID};
use std::time::Duration;
fn create_fault(stream_id: u32, pasid: u32, fault_type: FaultType) -> FaultRecord {
FaultRecord::builder()
.stream_id(StreamID::new(stream_id).unwrap())
.pasid(PASID::new(pasid).unwrap())
.address(IOVA::new(0x1000_0000).unwrap())
.fault_type(fault_type)
.access_type(AccessType::Read)
.security_state(SecurityState::NonSecure)
.build()
}
fn create_fault_with_address(stream_id: u32, pasid: u32, address: u64, fault_type: FaultType) -> FaultRecord {
FaultRecord::builder()
.stream_id(StreamID::new(stream_id).unwrap())
.pasid(PASID::new(pasid).unwrap())
.address(IOVA::new(address).unwrap())
.fault_type(fault_type)
.access_type(AccessType::Read)
.security_state(SecurityState::NonSecure)
.build()
}
fn create_fault_with_access(stream_id: u32, fault_type: FaultType, access_type: AccessType) -> FaultRecord {
FaultRecord::builder()
.stream_id(StreamID::new(stream_id).unwrap())
.pasid(PASID::new(1).unwrap())
.address(IOVA::new(0x1000_0000).unwrap())
.fault_type(fault_type)
.access_type(access_type)
.security_state(SecurityState::NonSecure)
.build()
}
fn create_fault_with_timestamp(stream_id: u32, fault_type: FaultType, timestamp: u64) -> FaultRecord {
FaultRecord::builder()
.stream_id(StreamID::new(stream_id).unwrap())
.pasid(PASID::new(1).unwrap())
.address(IOVA::new(0x1000_0000).unwrap())
.fault_type(fault_type)
.access_type(AccessType::Read)
.timestamp(timestamp)
.build()
}
#[test]
fn test_terminate_mode_basic_processing() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
let result = processor.process_fault(fault.clone());
assert!(result.is_err());
match result {
Err(FaultProcessingError::Terminated(f)) => {
assert_eq!(f.fault_type(), fault.fault_type());
assert_eq!(f.stream_id(), fault.stream_id());
},
_ => panic!("Expected Terminated error"),
}
}
#[test]
fn test_terminate_mode_event_recording() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let fault = create_fault(0x100, 1, FaultType::PermissionFault);
let _ = processor.process_fault(fault);
let events = processor.get_events();
assert_eq!(events.len(), 1);
assert_eq!(events[0].fault_type(), FaultType::PermissionFault);
}
#[test]
fn test_terminate_mode_statistics_update() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::PermissionFault));
let _ = processor.process_fault(create_fault(0x300, 1, FaultType::AccessFlagFault));
assert_eq!(processor.get_total_fault_count(), 3);
assert_eq!(processor.get_translation_fault_count(), 1);
assert_eq!(processor.get_permission_fault_count(), 1);
}
#[test]
fn test_terminate_mode_timestamp_auto_generation() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
let result = processor.process_fault(fault);
match result {
Err(FaultProcessingError::Terminated(f)) => {
assert!(f.timestamp() > 0, "Timestamp should be auto-generated");
},
_ => panic!("Expected Terminated error"),
}
}
#[test]
fn test_terminate_mode_error_display() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
let result = processor.process_fault(fault);
match result {
Err(e) => {
let display = format!("{e}");
assert!(display.contains("Fault terminated"));
},
_ => panic!("Expected error"),
}
}
#[test]
fn test_stall_mode_basic_processing() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
let result = processor.process_fault(fault);
assert!(result.is_ok());
assert_eq!(processor.get_queued_fault_count(), 1);
}
#[test]
fn test_stall_mode_fault_queuing() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault1 = create_fault(0x100, 1, FaultType::TranslationFault);
let fault2 = create_fault(0x200, 1, FaultType::PermissionFault);
let fault3 = create_fault(0x300, 1, FaultType::AccessFlagFault);
processor.process_fault(fault1).unwrap();
processor.process_fault(fault2).unwrap();
processor.process_fault(fault3).unwrap();
assert_eq!(processor.get_queued_fault_count(), 3);
let queued = processor.get_queued_faults();
assert_eq!(queued.len(), 3);
}
#[test]
fn test_stall_mode_get_next_stalled_fault() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
processor.process_fault(fault).unwrap();
let stalled = processor.get_next_stalled_fault();
assert!(stalled.is_some());
assert_eq!(stalled.unwrap().fault_type(), FaultType::TranslationFault);
assert_eq!(processor.get_queued_fault_count(), 0);
}
#[test]
fn test_stall_mode_no_stalled_fault_when_empty() {
let processor = FaultProcessor::new(FaultMode::Stall);
let stalled = processor.get_next_stalled_fault();
assert!(stalled.is_none());
}
#[test]
fn test_stall_mode_resume_stalled_fault_success() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
processor.process_fault(fault).unwrap();
let stalled = processor.get_next_stalled_fault().unwrap();
let result = processor.resume_stalled_fault(stalled, true);
assert!(result.is_ok());
}
#[test]
fn test_stall_mode_resume_stalled_fault_failure() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
processor.process_fault(fault).unwrap();
let stalled = processor.get_next_stalled_fault().unwrap();
let result = processor.resume_stalled_fault(stalled, false);
assert!(result.is_ok());
}
#[test]
fn test_stall_mode_invalid_resume_in_terminate_mode() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
let result = processor.resume_stalled_fault(fault, true);
assert!(result.is_err());
match result {
Err(FaultProcessingError::InvalidResume) => {},
_ => panic!("Expected InvalidResume error"),
}
}
#[test]
fn test_stall_mode_queue_full_error() {
let processor = FaultProcessor::with_config(FaultMode::Stall, 2);
processor
.process_fault(create_fault(0x100, 1, FaultType::TranslationFault))
.unwrap();
processor
.process_fault(create_fault(0x200, 1, FaultType::PermissionFault))
.unwrap();
let result = processor.process_fault(create_fault(0x300, 1, FaultType::AccessFlagFault));
assert!(result.is_err());
match result {
Err(FaultProcessingError::QueueFull) => {},
_ => panic!("Expected QueueFull error"),
}
}
#[test]
fn test_stall_mode_with_custom_queue_size() {
let processor = FaultProcessor::with_config(FaultMode::Stall, 500);
for i in 0..100 {
let fault = create_fault(i, 1, FaultType::TranslationFault);
processor.process_fault(fault).unwrap();
}
assert_eq!(processor.get_queued_fault_count(), 100);
}
#[test]
fn test_stall_mode_fifo_ordering() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault1 = create_fault(0x100, 1, FaultType::TranslationFault);
let fault2 = create_fault(0x200, 1, FaultType::PermissionFault);
let fault3 = create_fault(0x300, 1, FaultType::AccessFlagFault);
processor.process_fault(fault1).unwrap();
processor.process_fault(fault2).unwrap();
processor.process_fault(fault3).unwrap();
assert_eq!(processor.get_next_stalled_fault().unwrap().stream_id().as_u32(), 0x100);
assert_eq!(processor.get_next_stalled_fault().unwrap().stream_id().as_u32(), 0x200);
assert_eq!(processor.get_next_stalled_fault().unwrap().stream_id().as_u32(), 0x300);
}
#[test]
fn test_get_events_all() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x200, 2, FaultType::PermissionFault));
let _ = processor.process_fault(create_fault(0x300, 3, FaultType::AccessFlagFault));
let events = processor.get_events();
assert_eq!(events.len(), 3);
}
#[test]
fn test_get_events_by_stream() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x100, 2, FaultType::PermissionFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::AccessFlagFault));
let stream_100_events = processor.get_events_by_stream(StreamID::new(0x100).unwrap());
assert_eq!(stream_100_events.len(), 2);
let stream_200_events = processor.get_events_by_stream(StreamID::new(0x200).unwrap());
assert_eq!(stream_200_events.len(), 1);
let stream_300_events = processor.get_events_by_stream(StreamID::new(0x300).unwrap());
assert_eq!(stream_300_events.len(), 0);
}
#[test]
fn test_get_events_by_pasid() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::PermissionFault));
let _ = processor.process_fault(create_fault(0x300, 2, FaultType::AccessFlagFault));
let pasid_1_events = processor.get_events_by_pasid(PASID::new(1).unwrap());
assert_eq!(pasid_1_events.len(), 2);
let pasid_2_events = processor.get_events_by_pasid(PASID::new(2).unwrap());
assert_eq!(pasid_2_events.len(), 1);
let pasid_3_events = processor.get_events_by_pasid(PASID::new(3).unwrap());
assert_eq!(pasid_3_events.len(), 0);
}
#[test]
fn test_get_events_by_type() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x300, 1, FaultType::PermissionFault));
let _ = processor.process_fault(create_fault(0x400, 1, FaultType::AccessFlagFault));
let translation_faults = processor.get_events_by_type(FaultType::TranslationFault);
assert_eq!(translation_faults.len(), 2);
let permission_faults = processor.get_events_by_type(FaultType::PermissionFault);
assert_eq!(permission_faults.len(), 1);
let access_faults = processor.get_events_by_type(FaultType::AccessFlagFault);
assert_eq!(access_faults.len(), 1);
let address_faults = processor.get_events_by_type(FaultType::AddressSizeFault);
assert_eq!(address_faults.len(), 0);
}
#[test]
fn test_get_fault_count() {
let processor = FaultProcessor::new(FaultMode::Terminate);
assert_eq!(processor.get_fault_count(), 0);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
assert_eq!(processor.get_fault_count(), 1);
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::PermissionFault));
assert_eq!(processor.get_fault_count(), 2);
}
#[test]
fn test_get_fault_count_by_type() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x300, 1, FaultType::PermissionFault));
assert_eq!(processor.get_fault_count_by_type(FaultType::TranslationFault), 2);
assert_eq!(processor.get_fault_count_by_type(FaultType::PermissionFault), 1);
assert_eq!(processor.get_fault_count_by_type(FaultType::AccessFlagFault), 0);
}
#[test]
fn test_statistics_total_fault_count() {
let processor = FaultProcessor::new(FaultMode::Terminate);
assert_eq!(processor.get_total_fault_count(), 0);
for i in 0..10 {
let _ = processor.process_fault(create_fault(i, 1, FaultType::TranslationFault));
}
assert_eq!(processor.get_total_fault_count(), 10);
}
#[test]
fn test_statistics_translation_fault_count() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x300, 1, FaultType::PermissionFault));
assert_eq!(processor.get_translation_fault_count(), 2);
}
#[test]
fn test_statistics_permission_fault_count() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::PermissionFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::PermissionFault));
let _ = processor.process_fault(create_fault(0x300, 1, FaultType::TranslationFault));
assert_eq!(processor.get_permission_fault_count(), 2);
}
#[test]
fn test_statistics_access_flag_fault_count() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::AccessFlagFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::AccessFlagFault));
let _ = processor.process_fault(create_fault(0x300, 1, FaultType::AccessFlagFault));
let _ = processor.process_fault(create_fault(0x400, 1, FaultType::TranslationFault));
assert_eq!(processor.get_total_fault_count(), 4);
}
#[test]
fn test_statistics_address_size_fault_count() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::AddressSizeFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::AddressSizeFault));
let _ = processor.process_fault(create_fault(0x300, 1, FaultType::TranslationFault));
assert_eq!(processor.get_total_fault_count(), 3);
}
#[test]
fn test_statistics_multiple_fault_types() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::PermissionFault));
let _ = processor.process_fault(create_fault(0x300, 1, FaultType::AccessFlagFault));
let _ = processor.process_fault(create_fault(0x400, 1, FaultType::AddressSizeFault));
let _ = processor.process_fault(create_fault(0x500, 1, FaultType::TranslationFault));
assert_eq!(processor.get_total_fault_count(), 5);
assert_eq!(processor.get_translation_fault_count(), 2);
assert_eq!(processor.get_permission_fault_count(), 1);
}
#[test]
fn test_statistics_other_fault_types_not_tracked() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::ExternalAbort));
let _ = processor.process_fault(create_fault(0x200, 1, FaultType::TLBConflictAbort));
assert_eq!(processor.get_total_fault_count(), 2);
assert_eq!(processor.get_translation_fault_count(), 0);
assert_eq!(processor.get_permission_fault_count(), 0);
}
#[test]
fn test_events_in_time_window_basic() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let current_time = processor.get_current_timestamp();
let fault1 = create_fault_with_timestamp(0x100, FaultType::TranslationFault, current_time - 500_000);
let fault2 = create_fault_with_timestamp(0x200, FaultType::PermissionFault, current_time - 250_000);
let fault3 = create_fault_with_timestamp(0x300, FaultType::AccessFlagFault, current_time);
let _ = processor.process_fault(fault1);
let _ = processor.process_fault(fault2);
let _ = processor.process_fault(fault3);
let window = Duration::from_secs(1);
let events = processor.get_events_in_window(current_time, window);
assert_eq!(events.len(), 3);
}
#[test]
fn test_events_in_time_window_filtering() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let current_time = processor.get_current_timestamp();
let old_fault = create_fault_with_timestamp(0x100, FaultType::TranslationFault, current_time - 2_000_000);
let recent_fault = create_fault_with_timestamp(0x200, FaultType::PermissionFault, current_time - 500_000);
let new_fault = create_fault_with_timestamp(0x300, FaultType::AccessFlagFault, current_time);
let _ = processor.process_fault(old_fault);
let _ = processor.process_fault(recent_fault);
let _ = processor.process_fault(new_fault);
let window = Duration::from_secs(1);
let events = processor.get_events_in_window(current_time, window);
assert_eq!(events.len(), 2);
}
#[test]
fn test_events_in_time_window_empty() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let current_time = processor.get_current_timestamp();
let old_fault = create_fault_with_timestamp(0x100, FaultType::TranslationFault, current_time - 10_000_000);
let _ = processor.process_fault(old_fault);
let window = Duration::from_secs(1);
let events = processor.get_events_in_window(current_time, window);
assert_eq!(events.len(), 0);
}
#[test]
fn test_events_in_time_window_small_window() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let current_time = processor.get_current_timestamp();
let fault1 = create_fault_with_timestamp(0x100, FaultType::TranslationFault, current_time - 100_000);
let fault2 = create_fault_with_timestamp(0x200, FaultType::PermissionFault, current_time - 50_000);
let fault3 = create_fault_with_timestamp(0x300, FaultType::AccessFlagFault, current_time);
let _ = processor.process_fault(fault1);
let _ = processor.process_fault(fault2);
let _ = processor.process_fault(fault3);
let window = Duration::from_millis(75);
let events = processor.get_events_in_window(current_time, window);
assert_eq!(events.len(), 2);
}
#[test]
fn test_get_current_timestamp() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let ts1 = processor.get_current_timestamp();
std::thread::sleep(Duration::from_millis(10));
let ts2 = processor.get_current_timestamp();
assert!(ts2 > ts1, "Timestamp should increase over time");
}
#[test]
fn test_error_recovery_stall_mode_workflow() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
processor.process_fault(fault).unwrap();
let stalled = processor.get_next_stalled_fault();
assert!(stalled.is_some());
let recovered = stalled.unwrap();
let result = processor.resume_stalled_fault(recovered, true);
assert!(result.is_ok());
}
#[test]
fn test_error_recovery_failed_recovery() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault = create_fault(0x100, 1, FaultType::PermissionFault);
processor.process_fault(fault).unwrap();
let stalled = processor.get_next_stalled_fault().unwrap();
let result = processor.resume_stalled_fault(stalled, false);
assert!(result.is_ok()); }
#[test]
fn test_error_recovery_multiple_faults() {
let processor = FaultProcessor::new(FaultMode::Stall);
for i in 0..5 {
let fault = create_fault(i, 1, FaultType::TranslationFault);
processor.process_fault(fault).unwrap();
}
for _ in 0..5 {
let stalled = processor.get_next_stalled_fault();
assert!(stalled.is_some());
processor.resume_stalled_fault(stalled.unwrap(), true).unwrap();
}
assert_eq!(processor.get_queued_fault_count(), 0);
}
#[test]
fn test_fault_propagation_event_queue() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
let _ = processor.process_fault(fault.clone());
let events = processor.get_events();
assert_eq!(events.len(), 1);
assert_eq!(events[0].fault_type(), fault.fault_type());
assert_eq!(events[0].stream_id(), fault.stream_id());
}
#[test]
fn test_fault_propagation_stall_queue() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
processor.process_fault(fault.clone()).unwrap();
let queued = processor.get_queued_faults();
assert_eq!(queued.len(), 1);
assert_eq!(queued[0].fault_type(), fault.fault_type());
}
#[test]
fn test_fault_propagation_both_queues_stall_mode() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
processor.process_fault(fault).unwrap();
assert_eq!(processor.get_events().len(), 1);
assert_eq!(processor.get_queued_faults().len(), 1);
}
#[test]
fn test_fault_propagation_serialization() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let fault1 = create_fault(0x100, 1, FaultType::TranslationFault);
let fault2 = create_fault(0x200, 1, FaultType::PermissionFault);
let _ = processor.process_fault(fault1);
let _ = processor.process_fault(fault2);
let events = processor.get_events();
let serialized = processor.serialize_events(&events);
assert!(!serialized.is_empty());
}
#[test]
fn test_fault_propagation_deserialization_empty() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let data: Vec<u8> = vec![];
let result = processor.deserialize_events(&data);
assert!(result.is_ok());
assert_eq!(result.unwrap().len(), 0);
}
#[test]
fn test_fault_propagation_deserialization_non_empty() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
let _ = processor.process_fault(fault);
let events = processor.get_events();
let serialized = processor.serialize_events(&events);
let result = processor.deserialize_events(&serialized);
assert!(result.is_ok());
}
#[test]
fn test_error_display_queue_full() {
let error = FaultProcessingError::QueueFull;
let display = format!("{error}");
assert_eq!(display, "Fault queue is full");
}
#[test]
fn test_error_display_no_stalled_fault() {
let error = FaultProcessingError::NoStalledFault;
let display = format!("{error}");
assert_eq!(display, "No stalled fault available");
}
#[test]
fn test_error_display_invalid_resume() {
let error = FaultProcessingError::InvalidResume;
let display = format!("{error}");
assert_eq!(display, "Invalid fault resume");
}
#[test]
fn test_error_display_serialization_error() {
let error = FaultProcessingError::SerializationError("test error".to_string());
let display = format!("{error}");
assert!(display.contains("Serialization error"));
assert!(display.contains("test error"));
}
#[test]
fn test_max_event_queue_size_enforcement() {
let processor = FaultProcessor::new(FaultMode::Terminate);
for i in 0..100 {
let _ = processor.process_fault(create_fault(i, 1, FaultType::TranslationFault));
}
assert_eq!(processor.get_fault_count(), 100);
}
#[test]
fn test_timestamp_preservation() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let timestamp = 123_456_789;
let fault = create_fault_with_timestamp(0x100, FaultType::TranslationFault, timestamp);
let result = processor.process_fault(fault);
match result {
Err(FaultProcessingError::Terminated(f)) => {
assert_eq!(f.timestamp(), timestamp, "Timestamp should be preserved");
},
_ => panic!("Expected Terminated error"),
}
}
#[test]
fn test_concurrent_statistics_updates() {
use std::sync::Arc;
use std::thread;
let processor = Arc::new(FaultProcessor::new(FaultMode::Terminate));
let mut handles = vec![];
for i in 0..4 {
let proc = Arc::clone(&processor);
let handle = thread::spawn(move || {
for j in 0..25 {
let fault = create_fault((i * 25 + j) as u32, 1, FaultType::TranslationFault);
let _ = proc.process_fault(fault);
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(processor.get_total_fault_count(), 100);
}
#[test]
fn test_queued_fault_count_terminate_mode() {
let processor = FaultProcessor::new(FaultMode::Terminate);
assert_eq!(processor.get_queued_fault_count(), 0);
let _ = processor.process_fault(create_fault(0x100, 1, FaultType::TranslationFault));
assert_eq!(processor.get_queued_fault_count(), 0);
}
#[test]
fn test_get_queued_faults_terminate_mode() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let queued = processor.get_queued_faults();
assert_eq!(queued.len(), 0);
}
#[test]
fn test_various_access_types() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault_with_access(0x100, FaultType::TranslationFault, AccessType::Read));
let _ = processor.process_fault(create_fault_with_access(0x200, FaultType::PermissionFault, AccessType::Write));
let _ = processor.process_fault(create_fault_with_access(0x300, FaultType::AccessFlagFault, AccessType::Execute));
assert_eq!(processor.get_total_fault_count(), 3);
}
#[test]
fn test_various_addresses() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let _ = processor.process_fault(create_fault_with_address(0x100, 1, 0x1000, FaultType::TranslationFault));
let _ = processor.process_fault(create_fault_with_address(0x200, 1, 0xFFFF_FFFF, FaultType::PermissionFault));
let _ = processor.process_fault(create_fault_with_address(
0x300,
1,
0xDEAD_BEEF_0000,
FaultType::AccessFlagFault,
));
assert_eq!(processor.get_total_fault_count(), 3);
}
#[test]
fn test_max_event_queue_overflow() {
let processor = FaultProcessor::new(FaultMode::Terminate);
for i in 0..10_100 {
let fault = create_fault(i % 1000, 1, FaultType::TranslationFault);
let _ = processor.process_fault(fault);
}
let events = processor.get_events();
assert!(events.len() <= 10_000, "Event queue should not exceed max size");
}
#[test]
fn test_arm_smmu_v3_section_6_2_terminate_mode() {
let processor = FaultProcessor::new(FaultMode::Terminate);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
let result = processor.process_fault(fault);
assert!(result.is_err());
assert_eq!(processor.get_events().len(), 1);
assert_eq!(processor.get_total_fault_count(), 1);
}
#[test]
fn test_arm_smmu_v3_section_6_2_stall_mode() {
let processor = FaultProcessor::new(FaultMode::Stall);
let fault = create_fault(0x100, 1, FaultType::TranslationFault);
let result = processor.process_fault(fault);
assert!(result.is_ok());
assert_eq!(processor.get_queued_fault_count(), 1);
assert_eq!(processor.get_events().len(), 1);
}
#[test]
fn test_arm_smmu_v3_event_generation() {
let terminate_proc = FaultProcessor::new(FaultMode::Terminate);
let stall_proc = FaultProcessor::new(FaultMode::Stall);
let fault1 = create_fault(0x100, 1, FaultType::TranslationFault);
let fault2 = create_fault(0x200, 1, FaultType::PermissionFault);
let _ = terminate_proc.process_fault(fault1);
let _ = stall_proc.process_fault(fault2);
assert_eq!(terminate_proc.get_events().len(), 1);
assert_eq!(stall_proc.get_events().len(), 1);
}