// Host-only source-bound copy of the frozen asynchronous handoff fixture prefix. // Real frozen Table/AsyncBank APIs execute; no assembly or actual CPU is claimed. #[path = "../../os-context-switch/tests/support/mod.rs"] #[allow(dead_code)] mod saved; use std::{cell::RefCell, rc::Rc}; use turkos_os_context_switch::context::*; use turkos_os_threads::threads::{StackObservation, ThreadRecord}; #[derive(Default)] struct Cpu { nonce: u64, registers: Option, depart_fail: Option, arrive_fail: bool, departures: u64, arrivals: u64, } struct Device { inner: saved::Backend, cpu: Rc>, waits: Rc; 4]>>, } unsafe impl Backend for Device { type Binding = saved::Binding; type Prepared = saved::Prepared; type IdlePrepared = saved::IdlePrepared; fn execution(&self) -> Execution { self.inner.execution() } fn record(&self, b: &Self::Binding) -> Result { self.inner.record(b) } fn stack(&self, b: &Self::Binding) -> Result { self.inner.stack(b) } fn root(&self, b: &Self::Binding) -> Result { self.inner.root(b) } fn executable(&self, pc: u64) -> bool { self.inner.executable(pc) } fn initial_return_pc(&self, b: &Self::Binding) -> Result { self.inner.initial_return_pc(b) } fn capture_matches(&self, b: &Self::Binding, c: &Capture) -> bool { self.inner.capture_matches(b, c) } fn registers(&self) -> Registers { self.cpu.borrow().registers.unwrap() } fn prepare( &mut self, s: Option<&Self::Binding>, t: &Self::Binding, i: &Image, d: Disposition, ) -> Result { self.inner.prepare(s, t, i, d) } fn commit( &mut self, _: &mut Self::Prepared, _: Option<&Self::Binding>, _: &Self::Binding, _: &Image, _: Disposition, ) -> Result<(), Failure> { panic!("synchronous commit must never span an asynchronous handoff") } fn finish(&mut self, p: Self::Prepared) -> Result<(), (Error, Self::Prepared)> { self.inner.finish(p) } fn abort(&mut self, p: Self::Prepared) -> Result<(), (Error, Self::Prepared)> { self.inner.abort(p) } fn release(&mut self, p: Self::Binding) -> Result<(), (Error, Self::Binding)> { self.inner.release(p) } fn ready_empty(&self) -> bool { self.inner.ready_empty() } fn wake_epoch(&self) -> u64 { self.inner.wake_epoch() } fn idle_prepare(&mut self, e: u64) -> Result { self.inner.idle_prepare(e) } fn idle_commit(&mut self, p: &mut Self::IdlePrepared, e: u64) -> Result { self.inner.idle_commit(p, e) } fn idle_finish(&mut self, p: Self::IdlePrepared) -> Result<(), (Error, Self::IdlePrepared)> { self.inner.idle_finish(p) } fn idle_abort(&mut self, p: Self::IdlePrepared) -> Result<(), (Error, Self::IdlePrepared)> { self.inner.idle_abort(p) } } unsafe impl HandoffBackend for Device { fn depart( &mut self, p: &mut Self::Prepared, source: Option<&Self::Binding>, _: u64, d: Disposition, ) -> Result<(), Failure> { assert!(p.nonce > 0); let failure = self.cpu.borrow().depart_fail; if failure == Some(FailurePhase::BeforeSwitch) { return Err(Failure { error: Error::Backend, phase: FailurePhase::BeforeSwitch, }); } if let Some(source) = source { match d { Disposition::Ready => self .inner .table .borrow_mut() .yield_current(source.id) .unwrap(), Disposition::Sleeping => { self.waits.borrow_mut()[source.id.slot()] = Some( self.inner .table .borrow_mut() .sleep_current(source.id) .unwrap(), ); } } } self.inner.hardware.borrow_mut().current = None; self.cpu.borrow_mut().departures += 1; if failure == Some(FailurePhase::AfterSwitch) { return Err(Failure { error: Error::Backend, phase: FailurePhase::AfterSwitch, }); } Ok(()) } fn arrive( &mut self, _: &mut Self::Prepared, target: &Self::Binding, _: &Image, nonce: u64, ) -> Result<(), Error> { let cpu = self.cpu.borrow(); if cpu.nonce != nonce || cpu.registers.is_none() || cpu.arrive_fail { return Err(Error::Capture); } drop(cpu); self.inner .table .borrow_mut() .activate(target.id) .map_err(|_| Error::State)?; self.inner.hardware.borrow_mut().current = Some(target.id); self.cpu.borrow_mut().arrivals += 1; Ok(()) } } type Setup = ( AsyncBank, [Handle; 3], Rc>, Rc>, ); fn setup() -> Setup { let (b, pins, h) = saved::backend(3); let cpu = Rc::new(RefCell::new(Cpu::default())); let mut bank = ContextBank::<_, 4>::new(Device { inner: b, cpu: cpu.clone(), waits: Rc::new(RefCell::new(core::array::from_fn(|_| None))), }) .ok() .unwrap(); let mut handles = pins.into_iter().map(|p| bank.attach(p).ok().unwrap()); let a = handles.next().unwrap(); let b = handles.next().unwrap(); let c = handles.next().unwrap(); (AsyncBank::new(bank).ok().unwrap(), [a, b, c], h, cpu) } fn physical_step(bank: &AsyncBank, token: &OwnedHandoff) { let image = bank.target_image(token).unwrap(); bank.bank().with_backend_observation(|d| { let actual = saved::Backend::expected(image); d.inner.hardware.borrow_mut().sp = image.sp() - 64; let mut cpu = d.cpu.borrow_mut(); cpu.nonce = token.nonce(); cpu.registers = Some(actual); }); } fn land(bank: &mut AsyncBank, token: OwnedHandoff) { let token = bank.depart(token).ok().unwrap(); physical_step(bank, &token); assert!(bank.landed(token).is_ok()); } fn capture( bank: &AsyncBank, source: Handle, h: &Rc>, value: u64, timer: bool, ) -> Capture { let r = bank.bank().record(source).unwrap(); let image = if timer { let mut f = turkos_os_exceptions::exceptions::Frame::EMPTY; f.interrupted_sp = 0x80004000 + r.thread.slot() as u64 * 0x20000; f.elr = saved::ENTRY + 0x100; f.spsr = 0x305; f.vector_slot = 5; f.current_el = 4; f.canary = turkos_os_exceptions::exceptions::FRAME_CANARY; Image::Timer(f) } else { let mut f = Cooperative::EMPTY; f.sp = 0x80003000 + r.thread.slot() as u64 * 0x20000; f.x19_x30 = core::array::from_fn(|i| value + i as u64); f.x19_x30[11] = saved::ENTRY + 0x200 + value % 64 * 4; f.q = core::array::from_fn(|i| [value * 100 + i as u64, value * 200 + i as u64]); f.fpcr = value as u32; f.fpsr = value as u32 + 1; Image::Cooperative(f) }; let episode = h.borrow().capture_episode + 1; let c = Capture { image, address: image.sp() - if timer { 848 } else { 640 }, abi: Abi::COOPERATIVE, episode, }; let mut bytes = Vec::new(); let mut word = |v: u64| bytes.extend_from_slice(&v.to_le_bytes()); match image { Image::Cooperative(f) => { word(f.sp); for v in f.x19_x30 { word(v) } word(f.padding); for q in f.q { for v in q { word(v) } } word(u64::from(f.fpcr) | u64::from(f.fpsr) << 32); word(f.canary) } Image::Timer(f) => { for v in f.x { word(v) } for v in [ f.interrupted_sp, f.sp_el0, f.elr, f.spsr, f.esr, f.far, f.vector_slot, ] { word(v) } for q in f.q { for v in q { word(v) } } word(u64::from(f.fpcr) | u64::from(f.fpsr) << 32); for v in [f.current_el, f.canary, f.padding] { word(v) } } _ => panic!(), } bank.bank().with_backend_observation(|d| { let record = d.inner.table.borrow().record(r.thread).unwrap(); let offset = (c.address - record.layout.base) as usize; d.inner.stacks[r.thread.slot()].borrow_mut()[offset..offset + bytes.len()] .copy_from_slice(&bytes); }); let mut hw = h.borrow_mut(); hw.sp = c.address - 64; hw.capture = Some(c); hw.capture_episode = episode; hw.in_irq = timer; c }