// K1380 experimental EL0 ring handoff between one owner and one recipient. use super::*; use turkos_kernel::runtime::process_guest::{self, SharedObject}; use turkos_kernel::runtime::process_table::Outcome; use turkos_kernel::runtime::syscall_abi as abi; static mut OS_K1380_OBJECT: Option = None; static mut OS_K1380_STAGE: u8 = 0; static mut OS_K1380_COMPLETED: u64 = 0; static mut OS_K1380_YIELDS: u64 = 0; static mut OS_K1380_CANARY: [u8; 16] = *b"RING-KERNEL-SAFE"; const HEAP: u64 = process_guest::HEAP; fn object() -> &'static mut SharedObject { // The mode runs on one CPU with IRQs masked and a single active EL0 PID. unsafe { (&mut *core::ptr::addr_of_mut!(OS_K1380_OBJECT)) .as_mut().unwrap_or_else(|| park()) } } fn stage() -> u8 { unsafe { read_volatile(core::ptr::addr_of!(OS_K1380_STAGE)) } } fn set_stage(value: u8) { unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1380_STAGE), value); } } fn header(table: &mut OsK1105Table<'_>) -> (u64, u64) { let bytes = table.read_current_shared_header(object()) .unwrap_or_else(|_| park()); (u64::from_le_bytes(bytes[0..8].try_into().unwrap()), u64::from_le_bytes(bytes[8..16].try_into().unwrap())) } pub(super) fn begin() { unsafe { core::ptr::write(core::ptr::addr_of_mut!(OS_K1380_OBJECT), None); write_volatile(core::ptr::addr_of_mut!(OS_K1380_COMPLETED), 0); write_volatile(core::ptr::addr_of_mut!(OS_K1380_YIELDS), 0); 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); write_volatile(core::ptr::addr_of_mut!(OS_K1380_CANARY), *b"RING-KERNEL-SAFE"); } set_stage(0); } pub(super) fn finish() -> Result<(), &'static str> { if stage() != 9 || !object().released() || unsafe { read_volatile(core::ptr::addr_of!(OS_K1380_COMPLETED)) } != 1000 || unsafe { read_volatile(core::ptr::addr_of!(OS_K1380_YIELDS)) } != 62 || 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_K1380_CANARY)) } != *b"RING-KERNEL-SAFE" { return Err("ring-incomplete-or-owner-leak"); } unsafe { core::ptr::write(core::ptr::addr_of_mut!(OS_K1380_OBJECT), None); } 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] { 200 => { if episode.case != 0 || episode.calls != 1 || stage() != 1 || frame.x[0] != 0 { park(); } let shared = table.create_current_shared().unwrap_or_else(|_| park()); let address = table.map_current_shared(&shared, 0, 0) .unwrap_or_else(|_| park()); if address != HEAP || shared.frame_address(0).is_none() || shared.frame_address(1).is_none() { park(); } unsafe { core::ptr::write(core::ptr::addr_of_mut!(OS_K1380_OBJECT), Some(shared)); } frame.x[0] = address; set_stage(2); write_line("TURKOS/OS/K1380R-TRACE: OWNER-RW-PHYSICAL-RING-MAPPED"); } 201 => { if episode.case != 1 || episode.calls != 1 || stage() != 3 || frame.x[0] != 0 { park(); } let parent = table.record(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(); } object().grant(parent, pid).unwrap_or_else(|_| park()); let address = table.map_current_shared(object(), 0, 0) .unwrap_or_else(|_| park()); if address != HEAP || object().sealed() { park(); } frame.x[0] = address; set_stage(4); write_line("TURKOS/OS/K1380R-TRACE: RECIPIENT-RW-SAME-FRAME-MAPPED"); } 202 => { if episode.case != 1 || episode.calls != 1 || stage() != 4 || frame.x[0] != HEAP { park(); } let (published, consumed) = header(table); let before = unsafe { read_volatile(core::ptr::addr_of!(OS_K1380_COMPLETED)) }; if published != consumed || consumed != before + 16 || consumed > 992 || object().released() { park(); } frame.x[0] = 0; unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1380_SAVED_FRAME), *frame); write_volatile(core::ptr::addr_of_mut!(OS_K1380_SAVED_VALID), true); write_volatile(core::ptr::addr_of_mut!(OS_K1380_COMPLETED), consumed); let yields = read_volatile(core::ptr::addr_of!(OS_K1380_YIELDS)); write_volatile(core::ptr::addr_of_mut!(OS_K1380_YIELDS), yields + 1); } 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/K1380R-TRACE: RECIPIENT-YIELDED-TO-PRODUCER"); return 1; } 203 => { if episode.case != 1 || episode.calls != 1 || stage() != 4 || frame.x[0] != HEAP { park(); } let before_table = table.snapshot(); let before_resources = table.record(pid) .unwrap_or_else(|_| park()).resources; let before = [object().frame_address(0), object().frame_address(1)]; let (published, consumed) = header(table); if consumed != 1000 || published <= consumed + 16 || before != [object().frame_address(0), object().frame_address(1)] || table.snapshot() != before_table || table.record(pid).map(|record| record.resources) != Ok(before_resources) || unsafe { read_volatile(core::ptr::addr_of!(OS_K1380_CANARY)) } != *b"RING-KERNEL-SAFE" { park(); } unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1380_COMPLETED), 1000); } frame.x[0] = abi::AbiResult::Err(abi::Errno::Invalid).encode(); set_stage(5); write_line("TURKOS/OS/K1380R-TRACE: CORRUPT-PRODUCER-REJECTED-NO-KERNEL-WRITE"); write_line("TURKOS/OS/K1380R-TRACE: PROCESS-AND-FRAME-COUNTS-UNCHANGED"); } 204 => { if episode.case != 1 || stage() != 5 || frame.x[0] != 0 || header(table) != (1000, 1000) { park(); } table.unmap_current_shared(object(), 0).unwrap_or_else(|_| park()); table.close_current_shared_handle(object()).unwrap_or_else(|_| park()); frame.x[0] = 0; set_stage(6); write_line("TURKOS/OS/K1380R-TRACE: RECIPIENT-MAP-AND-HANDLE-RELEASED"); } 205 => { if episode.case != 0 || episode.calls != 2 || stage() != 7 || frame.x[0] != 0 { park(); } table.unmap_current_shared(object(), 0).unwrap_or_else(|_| park()); table.close_current_shared_handle(object()).unwrap_or_else(|_| park()); table.destroy_current_shared(object()).unwrap_or_else(|_| park()); if !object().released() { park(); } frame.x[0] = 0; set_stage(8); write_line("TURKOS/OS/K1380R-TRACE: OWNER-FRAMES-RELEASED"); } _ => { 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() != 8 { park(); } } else if code != 42 || episode.calls != 1 || stage() != 6 { 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(9); } else { set_stage(7); } 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; if episode.case == 0 { if stage() != 0 { park(); } set_stage(1); } else if stage() != 2 { park(); } else { set_stage(3); } return 0; } if let abi::Call::RunChild(1) = call { if episode.case != 0 || episode.calls != 1 || stage() != 2 && stage() != 4 { park(); } return os_k1215a_run_child(episode as *mut _, frame as *mut _, episode.launch.kernel_root); } park() }