// K1335 native EL0 message handle transfer through a kernel escrow. use super::*; use turkos_kernel::runtime::handles::{Error, Handle, Object, Rights, TransferTable}; use turkos_kernel::runtime::process_table::{Outcome, ProcessId}; use turkos_kernel::runtime::syscall_abi as abi; static mut OS_K1335_TABLE: TransferTable = TransferTable::new(); static mut OS_K1335_STAGE: u8 = 0; const OLD: u64 = (1u64 << 32) | 1; const NEW: u64 = (2u64 << 32) | 1; fn table() -> &'static mut TransferTable { // Mode 58 runs on one CPU with IRQs masked and no concurrent table user. unsafe { &mut *core::ptr::addr_of_mut!(OS_K1335_TABLE) } } fn stage() -> u8 { unsafe { read_volatile(core::ptr::addr_of!(OS_K1335_STAGE)) } } fn set_stage(value: u8) { unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1335_STAGE), value); } } pub(super) fn begin() { unsafe { core::ptr::write(core::ptr::addr_of_mut!(OS_K1335_TABLE), TransferTable::new()); } set_stage(0); } pub(super) fn finish() -> Result<(), &'static str> { let snapshot = table().snapshot(); if stage() != 11 || snapshot.entries != [None; 2] || snapshot.escrow != [None; 2] || snapshot.generations != [2, 0] || snapshot.head != 1 || snapshot.len != 0 || snapshot.next_ticket != 2 { return Err("message handle transfer owner leak"); } Ok(()) } 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() >= 5 { 2 } else { 1 }; if episode.calls != expected_calls || process_table.record(episode.pid).is_err() { park(); } let owner = episode.pid; match frame.x[8] { 40 => { if episode.case != 1 || stage() != 2 || frame.x[0] != OLD || frame.x[2] != 4 { park(); } let recipient = process_table.record(owner) .unwrap_or_else(|_| park()).parent.unwrap_or_else(|| park()); let ticket = table().send_from(owner, Handle::from_raw(OLD), recipient, Rights::READ, 4, |buffer| process_table.copy_current_user_data( owner, frame.x[1], buffer).map_err(|_| Error::Fault)) .unwrap_or_else(|_| park()); if ticket != 1 || table().owned(owner) != 0 || table().queued() != 1 || table().query(owner, Handle::from_raw(OLD)) != Err(Error::BadFd) { park(); } let preview = table().peek(recipient) .unwrap_or_else(|_| park()).unwrap_or_else(|| park()); if preview.ticket != 1 || preview.object != Object::File(55) || preview.rights != Rights::READ || preview.message.as_slice() != b"HND!" { park(); } frame.x[0] = ticket; set_stage(3); write_line("TURKOS/OS/K1335T-TRACE: CHILD-MOVED-FILE-TO-MESSAGE-ESCROW"); 0 } 41 => { if episode.case != 1 || stage() != 3 || frame.x[0] != OLD { park(); } let before = table().snapshot(); if table().query(owner, Handle::from_raw(OLD)) != Err(Error::BadFd) || table().snapshot() != before { park(); } frame.x[0] = abi::AbiResult::Err(abi::Errno::BadFd).encode(); set_stage(4); write_line("TURKOS/OS/K1335T-TRACE: SENDER-OLD-HANDLE-EBADF"); 0 } 42 => { if episode.case != 0 || stage() != 5 || frame.x[1] != 4 { park(); } let preview = table().peek(owner) .unwrap_or_else(|_| park()).unwrap_or_else(|| park()); if preview.ticket != 1 || preview.object != Object::File(55) || preview.rights != Rights::READ || preview.message.as_slice() != b"HND!" { park(); } process_table.write_current_user_data(owner, frame.x[0], preview.message.as_slice()).unwrap_or_else(|_| park()); let received = table().receive(owner) .unwrap_or_else(|_| park()).unwrap_or_else(|| park()); if received.handle.raw() != NEW || received.ticket != 1 || received.message.as_slice() != b"HND!" || received.sender_object != Object::File(55) || received.rights != Rights::READ || table().queued() != 0 { park(); } frame.x[0] = received.handle.raw(); set_stage(6); write_line("TURKOS/OS/K1335T-TRACE: SHELL-RECEIVED-FRESH-RO-HANDLE"); 0 } 43 => { if episode.case != 0 || stage() != 6 || frame.x[0] != NEW || table().query(owner, Handle::from_raw(NEW)) != Ok((Object::File(55), Rights::READ)) { park(); } frame.x[0] = 55; set_stage(7); write_line("TURKOS/OS/K1335T-TRACE: RECEIVER-QUERIED-TYPED-FILE"); 0 } 44 => { if episode.case != 0 || stage() != 7 || frame.x[0] != NEW { park(); } let before = table().snapshot(); let (_, rights) = table().query(owner, Handle::from_raw(NEW)) .unwrap_or_else(|_| park()); if rights.contains(Rights::WRITE) || table().snapshot() != before { park(); } frame.x[0] = abi::AbiResult::Err(abi::Errno::Access).encode(); set_stage(8); write_line("TURKOS/OS/K1335T-TRACE: RECEIVER-WRITE-EACCES-NO-MUTATION"); 0 } 45 => { if episode.case != 0 || stage() != 9 || frame.x[0] != NEW || table().close(owner, Handle::from_raw(NEW)) != Ok(()) || table().owned(owner) != 0 { park(); } frame.x[0] = 0; set_stage(10); write_line("TURKOS/OS/K1335T-TRACE: RECEIVER-CLOSED-FINAL-HANDLE"); 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() != 10 { park(); } } else if code != 42 || episode.calls != 1 || stage() != 4 || table().owned(episode.pid) != 0 { 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(5); write_line("TURKOS/OS/K1335T-TRACE: CHILD-EXIT-ESCROW-PERSISTS"); } else { set_stage(11); } 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(); } set_stage(1); write_line("TURKOS/OS/K1335T-TRACE: SHELL-READY-TO-RECEIVE"); } else { if stage() != 1 { park(); } let handle = table().open_kernel(episode.pid, Object::File(55), Rights::READ_WRITE) .unwrap_or_else(|_| park()); if handle.raw() != OLD { park(); } set_stage(2); write_line("TURKOS/OS/K1335T-TRACE: CHILD-OWNS-RW-FILE"); } 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() != 1 { 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 != 0 || request.fd() != 1 || request.bytes() != 16 || stage() != 8 { 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"XFER:55:RO:E13!\n" { park(); } for byte in bytes { write_byte(byte); } frame.x[0] = 16; set_stage(9); write_line("TURKOS/OS/K1335T-TRACE: USER-WROTE-TRANSFER-RESULT"); return 0; } park() } }