// K1395: two successive real EL0 service processes, with a reaped first PID. use super::*; use turkos_kernel::runtime::process_table::{Outcome, ProcessId}; use turkos_kernel::runtime::syscall_abi as abi; static mut OS_K1395_STAGE: u8 = 0; static mut OS_K1395_OLD: Option = None; static mut OS_K1395_NEW: Option = None; static mut OS_K1395_REQUEST: Option<(u64, ProcessId)> = None; static mut OS_K1395_REPLIED: bool = false; static mut OS_K1395_CANARY: [u8; 16] = *b"RESTART-GUARD!!!"; fn stage() -> u8 { unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_STAGE)) } } fn set_stage(value: u8) { unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1395_STAGE), value); } } fn request() -> Option<(u64, ProcessId)> { unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_REQUEST)) } } pub(super) fn begin() { unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1395_STAGE), 0); write_volatile(core::ptr::addr_of_mut!(OS_K1395_OLD), None); write_volatile(core::ptr::addr_of_mut!(OS_K1395_NEW), None); write_volatile(core::ptr::addr_of_mut!(OS_K1395_REQUEST), None); write_volatile(core::ptr::addr_of_mut!(OS_K1395_REPLIED), false); write_volatile(core::ptr::addr_of_mut!(OS_K1395_CANARY), *b"RESTART-GUARD!!!"); write_volatile(core::ptr::addr_of_mut!(OS_K1380_SAVED_FRAME), os_k920_exception::Frame::EMPTY); write_volatile(core::ptr::addr_of_mut!(OS_K1380_SAVED_VALID), false); write_volatile(core::ptr::addr_of_mut!(OS_K1380_SUSPENDED), false); } } pub(super) fn finish() -> Result<(), &'static str> { let old = unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_OLD)) }; let new = unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_NEW)) }; if stage() != 7 || old.is_none() || new.is_none() || old == new || request().is_some() || unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_REPLIED)) } || unsafe { read_volatile(core::ptr::addr_of!(OS_K1380_SAVED_VALID)) } || unsafe { read_volatile(core::ptr::addr_of!(OS_K1380_SUSPENDED)) } || unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_CANARY)) } != *b"RESTART-GUARD!!!" { return Err("restart-incomplete-or-pid-reused"); } Ok(()) } pub(super) fn dispatch(episode: &mut OsK1215AEl0Episode, frame: &mut os_k920_exception::Frame, table: &mut OsK1105Table<'_>) -> u64 { if frame.elr < episode.launch.user_pc || frame.elr >= episode.launch.user_pc + 4096 || !frame.elr.is_multiple_of(4) { park(); } let pid = episode.pid; if frame.x[7] == 3 { match frame.x[8] { 210 => { if episode.case != 1 || episode.calls != 1 || frame.x[0] != 0 || frame.x[1] != 0 { park(); } frame.x[0] = match stage() { 0 => 0, 3 => 1, _ => park(), }; } 220 => { let phase = frame.x[0]; if episode.case != 1 || episode.calls != 1 || frame.x[1] != 0x92 || request().is_some() || phase > 1 { park(); } if phase == 0 { if stage() != 0 { park(); } unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1395_OLD), Some(pid)); } set_stage(1); } else { let old = unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_OLD)) } .unwrap_or_else(|| park()); if stage() != 3 || pid == old || table.record(old).is_ok() { park(); } unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1395_NEW), Some(pid)); } set_stage(4); write_line("TURKOS/OS/K1395G-TRACE: OLD-PID-REAPED-NEW-PID-PUBLISHED"); } unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1395_REQUEST), Some((phase, pid))); write_volatile(core::ptr::addr_of_mut!(OS_K1380_SAVED_FRAME), *frame); (*core::ptr::addr_of_mut!(OS_K1380_SAVED_FRAME)).x[0] = 0; write_volatile(core::ptr::addr_of_mut!(OS_K1380_SAVED_VALID), true); } if unsafe { table.release_actual_current_to_ready() }.is_err() { park(); } unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1380_SUSPENDED), true); } write_line("TURKOS/OS/K1395G-TRACE: SERVICE-YIELDED-TO-CLIENT"); return 1; } 221 => { if episode.case != 0 || episode.calls == 0 || unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_REPLIED)) } || !matches!(stage(), 1 | 4) { park(); } let (phase, child) = request().unwrap_or_else(|| park()); if table.record(child).map(|r| r.parent) != Ok(Some(pid)) || child.slot() >= u32::MAX as usize { park(); } frame.x[0] = phase; frame.x[1] = child.slot() as u64 + 1; frame.x[2] = child.generation().checked_add(1) .unwrap_or_else(|| park()); if phase == 1 { write_line("TURKOS/OS/K1395G-TRACE: TRUSTED-REPLACEMENT-IDENTITY"); } } 222 => { if episode.case != 0 || episode.calls == 0 || frame.x[0] != 0 || frame.x[1] != 0 || request().is_none() || unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_REPLIED)) } { park(); } let phase = request().unwrap_or_else(|| park()).0; if (phase == 0 && stage() != 1) || (phase == 1 && stage() != 4) { park(); } unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1395_REPLIED), true); } set_stage(if phase == 0 { 2 } else { 5 }); frame.x[0] = 0; if phase == 0 { write_line("TURKOS/OS/K1395G-TRACE: OLD-SESSION-OPENED"); } else { write_line("TURKOS/OS/K1395G-TRACE: STALE-REPLAY-REJECTED-NEW-SESSION-OPENED"); } } 223 => { if episode.case != 1 || episode.calls != 1 || !unsafe { read_volatile(core::ptr::addr_of!(OS_K1395_REPLIED)) } || request().map(|(_, child)| child) != Some(pid) || !matches!(stage(), 2 | 5) { park(); } frame.x[0] = 0; unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1395_REQUEST), None); write_volatile(core::ptr::addr_of_mut!(OS_K1395_REPLIED), false); } } _ => frame.x[0] = abi::AbiResult::Err(abi::Errno::NoSys).encode(), } return 0; } if frame.x[7] == 2 && frame.x[8] == 3 { let request = turkos_os_user_runtime_abi::decode( frame.x[7], frame.x[8], core::array::from_fn(|i| frame.x[i])) .unwrap_or_else(|_| park()); let turkos_os_user_runtime_abi::Request::Exit(code) = request else { park(); }; if episode.case == 0 { if code != 0 || episode.calls != 2 || stage() != 6 { park(); } } else if code != 42 || episode.calls != 1 || !matches!(stage(), 2 | 5) { park(); } table.exit_current(code).unwrap_or_else(|_| park()); let outcome = Outcome::Exited(code); if table.record(pid).map(|r| r.outcome) != Ok(Some(outcome)) { park(); } if episode.case == 0 { set_stage(7); } else if stage() == 2 { set_stage(3); } else { set_stage(6); } episode.outcome = Some(outcome); return 1; } if frame.x[7] != abi::ABI_VERSION { park(); } let mut abi_frame = turkos_os_exceptions::exceptions::Frame::EMPTY; abi_frame.x = frame.x; let call = abi::decode_call(&abi_frame).unwrap_or_else(|_| park()); if call == abi::Call::Role { if episode.calls != 0 { park(); } episode.calls = 1; frame.x[0] = episode.case as u64; return 0; } if let abi::Call::RunChild(1) = call { if episode.case != 0 || episode.calls == 0 || episode.calls > 2 || !matches!(stage(), 0 | 2 | 3 | 5) { park(); } return os_k1215a_run_child(episode as *mut _, frame as *mut _, episode.launch.kernel_root); } park() }