//! Typed process termination carried through the real ProcessTable wait path. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { Syndrome, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Fault { esr: u64, far: u64, } impl Fault { /// Accept only a data abort taken from a lower exception level. /// The caller must supply ESR_EL1 and FAR_EL1 from the same saved trap. pub fn from_lower_el_data_abort(esr: u64, far: u64) -> Result { if (esr >> 26) & 0x3f != 0x24 { return Err(Error::Syndrome); } Ok(Self { esr, far }) } pub const fn esr(self) -> u64 { self.esr } pub const fn far(self) -> u64 { self.far } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Outcome { Exited(i32), Fault(Fault), } impl Outcome { pub const fn exit_code(self) -> Option { match self { Self::Exited(code) => Some(code), Self::Fault(_) => None, } } } #[cfg(test)] mod tests { use super::*; #[test] fn zero_and_forty_two_are_distinct_normal_exits() { assert_ne!(Outcome::Exited(0), Outcome::Exited(42)); assert_eq!(Outcome::Exited(0).exit_code(), Some(0)); assert_eq!(Outcome::Exited(42).exit_code(), Some(42)); } #[test] fn far_fault_cannot_be_reported_as_success() { let fault = Fault::from_lower_el_data_abort(0x9000_0004, 0x9000_0000).unwrap(); assert_eq!(fault.far(), 0x9000_0000); assert_eq!(Outcome::Fault(fault).exit_code(), None); assert_ne!(Outcome::Fault(fault), Outcome::Exited(0)); } #[test] fn syscall_and_same_level_abort_are_not_process_faults() { assert_eq!(Fault::from_lower_el_data_abort(0x5600_0000, 0x9000_0000), Err(Error::Syndrome)); assert_eq!(Fault::from_lower_el_data_abort(0x9400_0004, 0x9000_0000), Err(Error::Syndrome)); } }