// K1325 native EL0 bounded channel FIFO and rollback probe. use super::*; use turkos_kernel::runtime::handles::{BoundedChannel, CloseResult, Error, Handle, Rights}; use turkos_kernel::runtime::process_table::{Outcome, ProcessId}; use turkos_kernel::runtime::syscall_abi as abi; static mut OS_K1325_QUEUE: BoundedChannel = BoundedChannel::new(88); static mut OS_K1325_STAGE: u8 = 0; const SHELL: u64 = (1u64 << 32) | 1; const CHILD: u64 = (1u64 << 32) | 2; fn queue() -> &'static mut BoundedChannel { // Mode 56 runs on one CPU with IRQs masked and no concurrent queue user. unsafe { &mut *core::ptr::addr_of_mut!(OS_K1325_QUEUE) } } fn stage() -> u8 { unsafe { read_volatile(core::ptr::addr_of!(OS_K1325_STAGE)) } } fn set_stage(value: u8) { unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1325_STAGE), value); } } pub(super) fn begin() { unsafe { core::ptr::write(core::ptr::addr_of_mut!(OS_K1325_QUEUE), BoundedChannel::new(88)); } set_stage(0); } pub(super) fn finish() -> Result<(), &'static str> { let snapshot = queue().snapshot(); if stage() != 17 || snapshot.len != 0 || snapshot.head != 0 || snapshot.slots != [None; 2] || snapshot.objects.entries != [None; 2] || snapshot.objects.resources != [None; 2] || snapshot.objects.watchers != [None; 2] || snapshot.objects.generations != [1, 1] || snapshot.objects.next_wait != 1 { return Err("bounded queue owner leak"); } Ok(()) } fn errno(error: Error) -> abi::Errno { match error { Error::BadFd => abi::Errno::BadFd, Error::Access => abi::Errno::Access, Error::MessageTooLarge => abi::Errno::MsgSize, Error::WouldBlock => abi::Errno::Again, Error::Fault => abi::Errno::Fault, Error::IdExhausted => abi::Errno::Overflow, _ => abi::Errno::Invalid, } } fn error_word(error: Error) -> u64 { abi::AbiResult::Err(errno(error)).encode() } pub(super) fn dispatch( episode: &mut OsK1215AEl0Episode, frame: &mut os_k920_exception::Frame, process_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(); } if frame.x[7] == 3 { let expected_calls = if episode.case == 0 && stage() == 15 { 2 } else { 1 }; if episode.calls != expected_calls || process_table.record(episode.pid).is_err() { park(); } let owner = episode.pid; match frame.x[8] { 33 => { let expected = if episode.case == 0 { SHELL } else { CHILD }; if frame.x[0] != expected { park(); } let length = usize::try_from(frame.x[2]).unwrap_or_else(|_| park()); let before = queue().snapshot(); let result = queue().send_from(owner, Handle::from_raw(expected), length, |buffer| process_table.copy_current_user_data( owner, frame.x[1], buffer).map_err(|_| Error::Fault)); match (episode.case, stage(), result) { (0, 1, Ok(())) if length == 4 => { if queue().len() != 1 || queue().snapshot().slots[0] .as_ref().map(|m| m.as_slice()) != Some(b"ABCD".as_slice()) { park(); } frame.x[0] = 0; set_stage(2); write_line("TURKOS/OS/K1325Q-TRACE: EXACT-MAX-FOUR-QUEUED"); } (0, 2, Err(Error::Fault)) if length == 4 => { if frame.x[1] != u64::MAX || queue().snapshot() != before { park(); } frame.x[0] = error_word(Error::Fault); set_stage(3); write_line("TURKOS/OS/K1325Q-TRACE: USER-COPY-EFAULT-NO-PUBLISH"); } (0, 3, Ok(())) if length == 2 => { if queue().len() != 2 || queue().snapshot().slots[1] .as_ref().map(|m| m.as_slice()) != Some(b"XY".as_slice()) { park(); } frame.x[0] = 0; set_stage(4); write_line("TURKOS/OS/K1325Q-TRACE: SECOND-MESSAGE-QUEUED"); } (0, 4, Err(Error::WouldBlock)) if length == 1 => { if queue().snapshot() != before { park(); } frame.x[0] = error_word(Error::WouldBlock); set_stage(5); write_line("TURKOS/OS/K1325Q-TRACE: FULL-EAGAIN-NO-PUBLISH"); } (0, 5, Err(Error::MessageTooLarge)) if length == 5 => { if queue().snapshot() != before { park(); } frame.x[0] = error_word(Error::MessageTooLarge); set_stage(6); write_line("TURKOS/OS/K1325Q-TRACE: OVERSIZE-EMSGSIZE-NO-PUBLISH"); } (1, 10, Ok(())) if length == 1 => { if queue().len() != 1 || queue().peek(owner, Handle::from_raw(CHILD)).ok().flatten() .as_ref().map(|m| m.as_slice()) != Some(b"Z".as_slice()) { park(); } frame.x[0] = 0; set_stage(11); write_line("TURKOS/OS/K1325Q-TRACE: HEALTHY-SEND-AFTER-DENIALS"); } _ => park(), } 0 } 34 => { if episode.case != 1 || frame.x[0] != CHILD || frame.x[2] != 4 { park(); } let handle = Handle::from_raw(CHILD); let before = queue().snapshot(); let message = queue().peek(owner, handle).unwrap_or_else(|_| park()); match (stage(), message) { (9, None) => { if queue().snapshot() != before { park(); } frame.x[0] = error_word(Error::WouldBlock); set_stage(10); write_line("TURKOS/OS/K1325Q-TRACE: EMPTY-EAGAIN"); } (7 | 8 | 11, Some(message)) => { let (expected, next) = match stage() { 7 => (b"ABCD".as_slice(), 8), 8 => (b"XY".as_slice(), 9), 11 => (b"Z".as_slice(), 12), _ => park(), }; if message.as_slice() != expected { park(); } process_table.write_current_user_data(owner, frame.x[1], message.as_slice()).unwrap_or_else(|_| park()); if queue().receive(owner, handle) != Ok(Some(message)) { park(); } frame.x[0] = message.len() as u64; set_stage(next); write_line("TURKOS/OS/K1325Q-TRACE: FIFO-RECEIVE"); } _ => park(), } 0 } 35 => { let (handle, result, next) = match (episode.case, stage()) { (1, 13) => (CHILD, CloseResult::Remaining(1), 14), (0, 15) => (SHELL, CloseResult::LastClosed, 16), _ => park(), }; if frame.x[0] != handle || queue().close(owner, Handle::from_raw(handle)) != Ok(result) { park(); } frame.x[0] = if result == CloseResult::LastClosed { 1 } else { 0 }; set_stage(next); if result == CloseResult::LastClosed { write_line("TURKOS/OS/K1325Q-TRACE: LAST-CLOSE-QUEUE-EMPTY"); } 0 } _ => { frame.x[0] = abi::AbiResult::Err(abi::Errno::NoSys).encode(); 0 } } } else 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() != 16 { park(); } } else if code != 42 || episode.calls != 1 || stage() != 14 { park(); } process_table.exit_current(code).unwrap_or_else(|_| park()); let outcome = Outcome::Exited(code); if process_table.record(episode.pid).map(|r| r.outcome) != Ok(Some(outcome)) { park(); } if episode.case == 1 { set_stage(15); write_line("TURKOS/OS/K1325Q-TRACE: CHILD-EXIT-OWNER-EMPTY"); } else { set_stage(17); } episode.outcome = Some(outcome); 1 } else { 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(); } if episode.case == 0 { if stage() != 0 { park(); } let handle = queue().open_kernel(episode.pid).unwrap_or_else(|_| park()); if handle.raw() != SHELL { park(); } set_stage(1); write_line("TURKOS/OS/K1325Q-TRACE: SHELL-OWNS-QUEUE"); } else { if stage() != 6 { park(); } let parent = process_table.record(episode.pid) .unwrap_or_else(|_| park()).parent.unwrap_or_else(|| park()); if unsafe { read_volatile(core::ptr::addr_of!(OS_K1215A_INIT_PID)) } != Some(parent) { park(); } let handle = queue().grant_kernel(parent, episode.pid, Handle::from_raw(SHELL), Rights::READ_WRITE) .unwrap_or_else(|_| park()); if handle.raw() != CHILD { park(); } set_stage(7); write_line("TURKOS/OS/K1325Q-TRACE: TWO-REAL-PROCESSES-QUEUE-OWNERS"); } 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 != 1 || stage() != 6 { park(); } return os_k1215a_run_child(episode as *mut _, frame as *mut _, episode.launch.kernel_root); } if let abi::Call::Write(request) = call { if episode.case != 1 || request.fd() != 1 || request.bytes() != 16 || stage() != 12 { park(); } let mut bytes = [0u8; 16]; process_table.copy_current_user_data(episode.pid, request.address(), &mut bytes).unwrap_or_else(|_| park()); if &bytes != b"QUEUE:4,2,1:E11\n" { park(); } for byte in bytes { write_byte(byte); } frame.x[0] = 16; set_stage(13); write_line("TURKOS/OS/K1325Q-TRACE: USER-WROTE-QUEUE-RESULT"); return 0; } park() } }