// Mode 31 runs the same static Rust ELF with a rejected unaligned SP and // then a healthy aligned SP. The bootstrap owner collects both outcomes. static mut OS_K1205_EL0_ERROR: Option<&'static str> = None; #[unsafe(no_mangle)] static mut OS_K1205_ACTIVE: u64 = 0; #[unsafe(no_mangle)] static mut OS_K1205_KERNEL_ROOT: u64 = 0; #[unsafe(no_mangle)] static mut OS_K1205_USER_TTBR: u64 = 0; #[unsafe(no_mangle)] static mut OS_K1205_TRAP_TTBR: u64 = 0; #[unsafe(no_mangle)] static mut OS_K1205_KERNEL_FRAME: u64 = 0; struct OsK1205El0Episode { table: *mut u8, pid: turkos_kernel::runtime::process_table::ProcessId, launch: turkos_kernel::runtime::process_table::GuestLaunch, case: usize, old_vbar: u64, outcome: Option, } #[inline(never)] fn os_k1205_el0_on_work_stack( memory: &mut OsK660Memory, tables: &mmu::Tables, ) -> Result<(), &'static str> { let guard = core::ptr::addr_of!(OS_K1105_WORK_STACK) as u64; let low = guard + 4096; let end = low + 131072; let before = os_k1120_registers(); if before.sp >= guard && before.sp < end || before.cpu != 0 || before.daif & 128 == 0 || before.current_el != 4 || before.irq_active || before.ttbr != tables.root { return Err("exit-work-stack-context"); } let (slot, descriptor) = unsafe { tables.os_k1105_acquire_work_guard(guard, end, core::ptr::addr_of!(_image_start) as u64, core::ptr::addr_of!(_image_end) as u64) }.map_err(|_| "exit-work-stack-guard")?; unsafe { write_volatile(low as *mut u64, OS_K1105_LOW_CANARY); write_volatile((end - 8) as *mut u64, OS_K1105_HIGH_CANARY); write_volatile(core::ptr::addr_of_mut!(OS_K1205_EL0_ERROR), None); } unsafe extern "C" { fn __os_k1205_el0_on_stack(memory: *mut u8, tables: *const u8) -> u64; } let status = unsafe { __os_k1205_el0_on_stack( (memory as *mut OsK660Memory).cast(), (tables as *const mmu::Tables).cast()) }; let after = os_k1120_registers(); let canaries = unsafe { read_volatile(low as *const u64) == OS_K1105_LOW_CANARY && read_volatile((end - 8) as *const u64) == OS_K1105_HIGH_CANARY }; unsafe { tables.os_k1105_restore_work_guard(guard, end, slot, descriptor) } .map_err(|_| "exit-work-stack-restore")?; if after.sp != before.sp || after.spsel != before.spsel || after.ttbr != before.ttbr || after.tcr != before.tcr || after.sctlr != before.sctlr || after.daif != before.daif || !canaries { return Err("exit-work-stack-return"); } if status != 0 { return Err(unsafe { read_volatile(core::ptr::addr_of!(OS_K1205_EL0_ERROR)) } .unwrap_or("exit-work-stack-body")); } Ok(()) } #[unsafe(no_mangle)] unsafe extern "C" fn __os_k1205_el0_body(memory: *mut u8, tables: *const u8) -> u64 { let result = os_k1205_el0_probe( unsafe { &mut *memory.cast::() }, unsafe { &*tables.cast::() }); match result { Ok(()) => 0, Err(error) => { unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1205_EL0_ERROR), Some(error)); } 1 } } } #[unsafe(no_mangle)] extern "C" fn os_k1205_dispatch(pointer: *mut os_k920_exception::Frame) -> u64 { use turkos_kernel::runtime::process_table as process; use turkos_kernel::runtime::syscall_abi as abi; let kernel_root = unsafe { read_volatile(core::ptr::addr_of!(OS_K1205_KERNEL_ROOT)) }; let user_ttbr = unsafe { read_volatile(core::ptr::addr_of!(OS_K1205_USER_TTBR)) }; let trap_ttbr = unsafe { read_volatile(core::ptr::addr_of!(OS_K1205_TRAP_TTBR)) }; let active = unsafe { read_volatile(core::ptr::addr_of!(OS_K1205_ACTIVE)) }; let registers = os_k1120_registers(); if kernel_root == 0 || user_ttbr == 0 || active == 0 || pointer.is_null() || registers.cpu != 0 || registers.current_el != 4 || registers.daif & 128 == 0 || registers.irq_active || registers.ttbr != kernel_root || registers.spsel != 1 || trap_ttbr != user_ttbr { park(); } let episode = unsafe { &mut *(active as *mut OsK1205El0Episode) }; let table = unsafe { &mut *episode.table.cast::>() }; let launch = table.current_launch(episode.pid).unwrap_or_else(|_| park()); if launch != episode.launch || launch.kernel_root != kernel_root || launch.user_root | ((launch.user_asid as u64) << 48) != user_ttbr { park(); } let address = pointer as u64; if address < launch.kernel_stack_low || address.checked_add(os_k920_exception::FRAME_BYTES as u64) .is_none_or(|end| end > launch.kernel_stack_high) || !address.is_multiple_of(os_k920_exception::FRAME_ALIGNMENT as u64) { park(); } let frame = unsafe { &mut *pointer }; let offset = 0x58; if frame.validate_header().is_err() || frame.vector_slot != 8 || frame.spsr & 0x3ff != launch.user_spsr || frame.sp_el0 != launch.user_sp + u64::from(episode.case == 0) || frame.interrupted_sp != address + os_k920_exception::FRAME_BYTES as u64 || frame.elr != launch.user_pc + offset || episode.outcome.is_some() { park(); } let expected = if episode.case == 0 { 126 } else { 42 }; let mut abi_frame = turkos_os_exceptions::exceptions::Frame::EMPTY; abi_frame.x = frame.x; if frame.esr != abi::SVC_ESR || frame.x[8] != abi::CALL_EXIT || frame.x[7] != abi::ABI_VERSION || abi::decode_call(&abi_frame) != Ok(abi::Call::Exit(expected)) { park(); } if table.exit_current(expected as i32).is_err() { park(); } if episode.case == 0 { write_line("TURKOS/OS/K1205-TRACE: UNALIGNED-SP-REJECTED-126"); } else { write_line("TURKOS/OS/K1205-TRACE: RUST-MAIN-ARGV-EXIT-42"); } let outcome = process::Outcome::Exited(expected as i32); if table.record(episode.pid).map(|r| r.outcome) != Ok(Some(outcome)) || table.snapshot().zombies != 1 { park(); } episode.outcome = Some(outcome); 1 // Never ERET after process termination. } #[unsafe(no_mangle)] unsafe extern "C" fn __os_k1205_el0_process_body( table: *mut u8, pid: *const u8, kernel_root: u64, ) -> u64 { use turkos_kernel::runtime::process_table as process; let table_ptr = table.cast::>(); let pid = unsafe { *pid.cast::() }; if unsafe { (&mut *table_ptr).register_actual_landing(pid) }.is_err() { park(); } let result = (|| -> Result<(), &'static str> { let launch = unsafe { &*table_ptr }.current_launch(pid) .map_err(|_| "exit-launch-owner")?; if launch.kernel_root != kernel_root || launch.user_asid == 0 || launch.user_root == kernel_root || launch.user_root == 0 { return Err("exit-launch-root"); } let case = unsafe { read_volatile(core::ptr::addr_of!(OS_K1205_CASE)) } as usize; if case >= 2 { return Err("exit-case"); } let old_vbar: u64; unsafe { asm!("mrs {vbar}, VBAR_EL1", vbar=out(reg) old_vbar, options(nomem, nostack, preserves_flags)); } let mut episode = OsK1205El0Episode {table, pid, launch, case, old_vbar, outcome: None}; let user_ttbr = launch.user_root | ((launch.user_asid as u64) << 48); unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1205_KERNEL_ROOT), kernel_root); write_volatile(core::ptr::addr_of_mut!(OS_K1205_USER_TTBR), user_ttbr); write_volatile(core::ptr::addr_of_mut!(OS_K1205_TRAP_TTBR), 0); write_volatile(core::ptr::addr_of_mut!(OS_K1205_KERNEL_FRAME), 0); write_volatile(core::ptr::addr_of_mut!(OS_K1205_ACTIVE), (&mut episode as *mut OsK1205El0Episode) as u64); } unsafe extern "C" { fn __os_k1205_enter_el0(user_ttbr: u64, pc: u64, sp: u64, spsr: u64); } unsafe { __os_k1205_enter_el0(user_ttbr, launch.user_pc, launch.user_sp + u64::from(case == 0), launch.user_spsr); } unsafe { asm!("msr VBAR_EL1, {vbar}", "isb", vbar=in(reg) episode.old_vbar, options(nostack, preserves_flags)); write_volatile(core::ptr::addr_of_mut!(OS_K1205_ACTIVE), 0); write_volatile(core::ptr::addr_of_mut!(OS_K1205_KERNEL_ROOT), 0); write_volatile(core::ptr::addr_of_mut!(OS_K1205_USER_TTBR), 0); write_volatile(core::ptr::addr_of_mut!(OS_K1205_TRAP_TTBR), 0); write_volatile(core::ptr::addr_of_mut!(OS_K1205_KERNEL_FRAME), 0); } write_str("TURKOS/OS/K1205-TRACE: RETURN OUTCOME=0x"); write_hex(episode.outcome.is_some() as usize); write_str(" TTBR=0x"); write_hex(os_k1120_registers().ttbr as usize); write_str(" ROOT=0x"); write_hex(kernel_root as usize); write_str(" RECORD=0x"); write_hex((unsafe { &*table_ptr }.record(pid).map(|r| r.outcome) == Ok(episode.outcome)) as usize); write_line(""); if episode.outcome.is_none() || os_k1120_registers().ttbr != kernel_root || unsafe { &*table_ptr }.record(pid).map(|r| r.outcome) != Ok(episode.outcome) { return Err("exit-completion-owner"); } unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1205_RESULT), episode.outcome); } Ok(()) })(); // exit_current/fault_current already made the thread Dead and cleared current. match result { Ok(()) => 0, Err(error) => { unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1205_EL0_ERROR), Some(error)); } 1 } } } static mut OS_K1205_CASE: u64 = 0; static mut OS_K1205_RESULT: Option = None; #[inline(never)] fn os_k1205_el0_probe( memory: &mut OsK660Memory, tables: &mmu::Tables, ) -> Result<(), &'static str> { use turkos_kernel::runtime::address_spaces as spaces; use turkos_kernel::runtime::process_guest as guest; use turkos_kernel::runtime::process_image_recipe::{Recipe, TrustedImage}; use turkos_kernel::runtime::process_table as process; let before = memory.allocator.os_authority().audit() .map_err(|_| "exit-before-inventory")?; let images: [&[u8]; 2] = [&OS_K1205_USER_ELF, &OS_K1205_USER_ELF]; let mut outcomes: [Option; 2] = [None; 2]; let mut ids: [Option; 2] = [None; 2]; for (case, bytes) in images.into_iter().enumerate() { let hardware = OsK1120Hardware { tables }; let registers = os_k1120_registers(); let guard = core::ptr::addr_of!(OS_K1105_WORK_STACK) as u64; let binding = guest::StackBinding { owner: guard, low: guard + 4096 + 16, high: guard + 4096 + 131072 - 16, spsel: registers.spsel }; let gate = unsafe { guest::Gate::new(&hardware, binding) } .map_err(|_| "exit-primary-gate")?; let authority = guest::AuthorityCell::new(memory.allocator.os_authority_mut()); let store = guest::FrameStore::new(0x1205 + case as u64, &authority, &gate) .map_err(|_| "exit-frame-store")?; let frame_baseline = store.snapshot().map_err(|_| "exit-frame-baseline")?; let trusted = unsafe { TrustedImage::from_kernel(bytes, bytes.len()) } .map_err(|_| "exit-trusted-image")?; let recipe = Recipe::new(trusted, &[b"runtime", b"two"], &[]) .map_err(|_| "exit-image-recipe")?; let image = OsK1120PreparedImage(recipe); let config = spaces::Configuration::from_registers(5, registers.tcr) .map_err(|_| "exit-space-config")?; let table_ptr = os_k1105_install_table(&gate, &authority, &store, &image, config)?; let table = unsafe { &mut *table_ptr }; let request = process::Spawn { handles: 3, code_entry: guest::CODE, user_stack_top: guest::STACK_TOP }; let reservation = table.begin(request).map_err(|_| "exit-begin")?; let prepared = table.prepare(reservation).map_err(|_| "exit-prepare")?; let pid = table.publish(prepared).map_err(|_| "exit-publish")?; ids[case] = Some(pid); unsafe { write_volatile(core::ptr::addr_of_mut!(OS_K1205_CASE), case as u64); write_volatile(core::ptr::addr_of_mut!(OS_K1205_RESULT), None); } let (_, stack_top) = table.landing_stack(pid).map_err(|_| "exit-stack")?; unsafe extern "C" { fn __os_k1205_enter_el0_process_stack( table: *mut u8, pid: *const u8, stack_top: u64, kernel_root: u64) -> u64; } let status = unsafe { __os_k1205_enter_el0_process_stack( table_ptr.cast(), (&pid as *const process::ProcessId).cast(), stack_top, tables.root) }; if status != 0 { return Err(unsafe { read_volatile(core::ptr::addr_of!(OS_K1205_EL0_ERROR)) } .unwrap_or("exit-process-body")); } let observed = unsafe { read_volatile(core::ptr::addr_of!(OS_K1205_RESULT)) } .ok_or("exit-missing-result")?; if table.record(pid).map(|r| r.outcome) != Ok(Some(observed)) || table.snapshot().zombies != 1 { return Err("exit-zombie-result"); } let reaped = table.wait_child_outcome(pid) .map_err(|_| "exit-typed-wait")?; if reaped != observed || table.snapshot() != process::Snapshot::default() { return Err("exit-reap-result"); } outcomes[case] = Some(reaped); let backend = os_k1105_finish_table(table_ptr)?; if backend.inventory().map_err(|_| "exit-owner-inventory")? != [0, 0, 0, before.live_pages] { return Err("exit-owner-leak"); } drop(backend); if gate.faulted() || store.snapshot() != Ok(frame_baseline) || authority.observe(&gate, |allocator| allocator.audit()) .map_err(|_| "exit-after-gate")? .map_err(|_| "exit-after-inventory")? != before { return Err("exit-physical-rollback"); } write_line("TURKOS/OS/K1205-TRACE: INIT-RECORDED-RESULT"); } if outcomes != [Some(process::Outcome::Exited(126)), Some(process::Outcome::Exited(42))] || ids[0] == ids[1] { return Err("rust-start-ledger"); } write_line("TURKOS/OS/K1205-EL0: BAD-SP=EXIT126 RUST-ARGV=2 EXIT42 OWNER=RELEASED"); write_line("TURKOS/OS/K1205: PASS"); Ok(()) }