//! EL0 duplicate descriptor and in-flight close probe. use super::*; use turkos_kernel::runtime::process_table::Outcome; use turkos_kernel::runtime::syscall_abi as abi; use os_vfs_dup::{Error as DupError, FdTable, ReadLease}; static mut OS_K1580_DUP_TABLE: FdTable = FdTable::new(); static mut OS_K1580_DUP_LEASE: Option = None; static mut OS_K1580_DUP_COUNT: u64 = 0; static mut OS_K1580_DUP_CANARY: [u8; 16] = *b"DUP-USER-GUARD!!"; fn count() -> u64 { unsafe { read_volatile(core::ptr::addr_of!(OS_K1580_DUP_COUNT)) } } pub(super) fn begin() { unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1580_DUP_TABLE), FdTable::new()); write_volatile(core::ptr::addr_of_mut!(OS_K1580_DUP_LEASE), None); write_volatile(core::ptr::addr_of_mut!(OS_K1580_DUP_COUNT), 0); write_volatile(core::ptr::addr_of_mut!(OS_K1580_DUP_CANARY), *b"DUP-USER-GUARD!!"); } } pub(super) fn finish() -> Result<(), &'static str> { let table = unsafe { read_volatile(core::ptr::addr_of!(OS_K1580_DUP_TABLE)) }; let lease = unsafe { read_volatile(core::ptr::addr_of!(OS_K1580_DUP_LEASE)) }; let description = table.description(); if count() != 13 || lease.is_some() || description.live || description.open_fds != 0 || description.in_flight != 0 || description.offset != 9 || unsafe { read_volatile(core::ptr::addr_of!(OS_K1580_DUP_CANARY)) } != *b"DUP-USER-GUARD!!" { return Err("dup-guest-incomplete"); } Ok(()) } fn errno(error: DupError) -> i64 { match error { DupError::BadFd => -9, DupError::NoSpace => -24, DupError::Busy => -16, DupError::Overflow => -75, DupError::Stale => -116, } } 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 { if episode.case != 0 || episode.calls != 1 || count() >= 13 || unsafe { read_volatile(core::ptr::addr_of!(OS_K1580_DUP_CANARY)) } != *b"DUP-USER-GUARD!!" { park(); } const OPS: [u64; 13] = [231, 232, 233, 235, 232, 233, 235, 234, 232, 234, 233, 235, 233]; const ARGS: [u64; 13] = [1, 1, 3, 2, 2, 2, 1, 1, 2, 2, 4, 2, 1]; const EXPECTED: [i64; 13] = [2, 0, 3, 3, 0, 5, 5, 0, 0, 0, 9, -9, -116]; let stage = count() as usize; let op = frame.x[8]; let arg = frame.x[0]; if op != OPS[stage] || arg != ARGS[stage] { park(); } let before = table.snapshot(); let resources = table.record(pid).unwrap_or_else(|_| park()).resources; let mut model = unsafe { read_volatile(core::ptr::addr_of!(OS_K1580_DUP_TABLE)) }; let mut lease = unsafe { read_volatile(core::ptr::addr_of!(OS_K1580_DUP_LEASE)) }; let result = match op { 231 => model.dup(arg).map(|fd| fd as i64).unwrap_or_else(errno), 232 => match model.begin_read(arg) { Ok(next) => { lease = Some(next); 0 }, Err(error) => errno(error), }, 233 => match lease { Some(active) => match model.finish_read(active, arg) { Ok(next) => { lease = None; next as i64 }, Err(error) => errno(error), }, None => errno(DupError::Stale), }, 234 => model.close(arg).map(|_| 0).unwrap_or_else(errno), 235 => model.offset(arg).map(|offset| offset as i64) .unwrap_or_else(errno), _ => park(), }; if result != EXPECTED[stage] { park(); } unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1580_DUP_TABLE), model); write_volatile(core::ptr::addr_of_mut!(OS_K1580_DUP_LEASE), lease); write_volatile(core::ptr::addr_of_mut!(OS_K1580_DUP_COUNT), count() + 1); } frame.x[0] = result as u64; if table.snapshot() != before || table.record(pid).map(|row| row.resources) != Ok(resources) { park(); } match stage { 3 => write_line("TURKOS/OS/K1580-DUP: SHARED-OFFSET=3"), 6 => write_line("TURKOS/OS/K1580-DUP: SHARED-OFFSET=5"), 9 => { let description = model.description(); if (description.open_fds, description.in_flight, description.live) != (0, 1, true) { park(); } write_line("TURKOS/OS/K1580-DUP: LAST-CLOSE-READ-HELD"); }, 10 => { let description = model.description(); if (description.open_fds, description.in_flight, description.live, description.offset) != (0, 0, false, 9) { park(); } write_line("TURKOS/OS/K1580-DUP: READ-COMPLETED-RETIRED"); }, 11 => write_line("TURKOS/OS/K1580-DUP: CLOSED-FD-REJECTED"), 12 => write_line("TURKOS/OS/K1580-DUP: REPEATED-COMPLETION-REJECTED"), _ => {}, } 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 || count() != 13 { park(); } } else if code != 42 || episode.calls != 1 || count() != 13 { park(); } table.exit_current(code).unwrap_or_else(|_| park()); let outcome = Outcome::Exited(code); if table.record(pid).map(|row| row.outcome) != Ok(Some(outcome)) { park(); } 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 != 1 || count() != 13 { park(); } return os_k1215a_run_child(episode as *mut _, frame as *mut _, episode.launch.kernel_root); } park() }