#[path = "context-fixture.rs"] #[allow(dead_code)] mod saved; use std::{ cell::{Cell, RefCell}, rc::Rc, vec::Vec, }; use turkos_os_context_switch::context::{self as ctx, Backend as _, ContextBank}; use turkos_os_runqueue::runqueue::*; use turkos_os_threads::threads::{self as thread, ThreadId}; pub(crate) struct Member { pub(crate) context: ctx::Handle, pub(crate) thread: ThreadId, pub(crate) nonce: Rc>, pub(crate) pins: Rc>, } impl Drop for Member { fn drop(&mut self) { self.pins.set(self.pins.get() - 1); } } pub(crate) struct Tick { pub(crate) observation: TickObservation, pub(crate) nonce: u64, } pub(crate) struct Prepared { pub(crate) nonce: u64, pub(crate) epoch: u64, pub(crate) capture: Option, } #[derive(Default)] pub(crate) struct Control { pub(crate) prepare_fail: bool, pub(crate) fail: Option, pub(crate) finish_fail: bool, pub(crate) abort_fail: bool, pub(crate) prepares: u64, pub(crate) commits: u64, pub(crate) consumed: u64, pub(crate) sequence: u64, pub(crate) counter: u64, pub(crate) epoch: u64, pub(crate) tick_live: Option, pub(crate) bad_observation: bool, } pub(crate) struct Backend { pub(crate) bank: ContextBank, pub(crate) handles: Vec, pub(crate) hardware: Rc>, pub(crate) control: Rc>, pub(crate) pins: Rc>, pub(crate) nonces: RefCell>>>, } impl Backend { pub(crate) fn mint(&self, index: usize) -> Member { let handle = self.handles[index]; let id = self.bank.record(handle).unwrap().thread; self.pins.set(self.pins.get() + 1); let nonce = Rc::new(Cell::new(id.generation())); self.nonces.borrow_mut().push(nonce.clone()); Member { context: handle, thread: id, nonce, pins: self.pins.clone(), } } } unsafe impl turkos_os_runqueue::runqueue::Backend for Backend { type Member = Member; type Prepared = Prepared; type Tick = Tick; fn execution(&self) -> Execution { let e = self.bank.with_backend_observation(|b| b.execution()); Execution { cpu: e.cpu, masked: e.daif & 128 != 0, in_irq: e.in_irq, thread: e.thread, root: e.root, } } fn observe(&self, member: &Member) -> Result { if member.nonce.get() != member.thread.generation() { return Err(Error::Stale); } let context = self.bank.record(member.context).map_err(|_| Error::Stale)?; if context.thread != member.thread { return Err(Error::Stale); } self.bank.with_backend_observation(|b| { let t = b .table .borrow() .record(member.thread) .map_err(|_| Error::Stale)?; let bytes = b.stacks[member.thread.slot()].borrow(); let h = self.hardware.borrow(); let stack = thread::StackObservation { layout: t.layout, guard_descriptor: u64::from(h.corrupt_guard), low_canary: u64::from_le_bytes(bytes[..8].try_into().unwrap()) ^ u64::from(h.corrupt_canary), high_canary: u64::from_le_bytes(bytes[bytes.len() - 8..].try_into().unwrap()), initially_zeroed: false, }; Ok(Observation { thread: t, context, stack, }) }) } fn tick(&self, tick: &Tick) -> Result { if self.control.borrow().tick_live != Some(tick.nonce) { Err(Error::Tick) } else { Ok(tick.observation) } } fn consume_tick(&mut self, tick: Tick) { let mut c = self.control.borrow_mut(); assert_eq!(c.tick_live, Some(tick.nonce)); c.tick_live = None; c.consumed += 1; } fn prepare( &mut self, source: Option<&Member>, _: &Member, epoch: u64, _: Disposition, ) -> Result { let mut c = self.control.borrow_mut(); if c.prepare_fail { return Err(Error::Backend); } c.prepares += 1; let capture = source.map(|_| self.hardware.borrow().capture.unwrap()); Ok(Prepared { nonce: c.prepares, epoch, capture, }) } fn commit( &mut self, p: &mut Prepared, source: Option<&Member>, target: &Member, epoch: u64, disposition: Disposition, ) -> Result<(), Failure> { assert_eq!(p.epoch, epoch); assert_ne!(p.nonce, 0); let failure = self.control.borrow().fail; if failure == Some(ctx::FailurePhase::BeforeSwitch) { return Err(Failure { error: Error::Backend, phase: ctx::FailurePhase::BeforeSwitch, }); } let dispatch = match source { None => self.bank.start(target.context), Some(source) => match disposition { Disposition::Ready => { self.bank .cooperate(source.context, target.context, p.capture.unwrap()) } Disposition::Sleeping => { self.bank .sleep_to(source.context, target.context, p.capture.unwrap()) } }, } .map_err(|_| Failure { error: Error::Backend, phase: ctx::FailurePhase::BeforeSwitch, })?; match dispatch.complete() { Ok(()) => {} Err(ctx::DispatchError::Before(_, d)) => { return match d.abort() { Ok(()) => Err(Failure { error: Error::Backend, phase: ctx::FailurePhase::BeforeSwitch, }), Err(_) => Err(Failure { error: Error::Backend, phase: ctx::FailurePhase::AfterSwitch, }), }; } Err(ctx::DispatchError::Quarantined(_)) => { return Err(Failure { error: Error::Backend, phase: ctx::FailurePhase::AfterSwitch, }); } } let mut c = self.control.borrow_mut(); c.commits += 1; c.epoch = epoch; if failure == Some(ctx::FailurePhase::AfterSwitch) { return Err(Failure { error: Error::Backend, phase: ctx::FailurePhase::AfterSwitch, }); } if c.bad_observation { self.hardware.borrow_mut().current = None; } Ok(()) } fn finish(&mut self, p: Prepared) -> Result<(), (Error, Prepared)> { if self.control.borrow().finish_fail { Err((Error::Backend, p)) } else { Ok(()) } } fn abort(&mut self, p: Prepared) -> Result<(), (Error, Prepared)> { if self.control.borrow().abort_fail { Err((Error::Backend, p)) } else { Ok(()) } } } pub(crate) fn backend(count: usize) -> (Backend, Vec, Rc>) { let (b, pins, h) = saved::backend(count); let mut bank = ContextBank::new(b).ok().unwrap(); let handles = pins .into_iter() .map(|p| bank.attach(p).ok().unwrap()) .collect(); let control = Rc::new(RefCell::new(Control::default())); let b = Backend { bank, handles, hardware: h, control: control.clone(), pins: Rc::new(Cell::new(0)), nonces: RefCell::new(Vec::new()), }; let members = (0..count).map(|i| b.mint(i)).collect(); (b, members, control) } pub(crate) fn setup(count: usize) -> (Queue, Vec, Rc>) { let (b, members, c) = backend(count); let mut q = Queue::new(b).ok().unwrap(); let handles = members .into_iter() .map(|m| q.enqueue(m, Class::Normal).ok().unwrap()) .collect(); (q, handles, c) } pub(crate) fn arm(q: &Queue) { q.with_backend_observation(|b| { let id = b.hardware.borrow().current; if let Some(id) = id { let handle = *b .handles .iter() .find(|h| b.bank.record(**h).unwrap().thread == id) .unwrap(); saved::capture( &b.bank, handle, &b.hardware, 17 + b.control.borrow().prepares, false, ); } }) } pub(crate) fn select(q: &mut Queue, disposition: Disposition) -> Handle { arm(q); q.select(disposition).unwrap().complete().ok().unwrap() } pub(crate) fn tick(q: &Queue, handle: Handle) -> Tick { let r = q.record(handle).unwrap(); q.with_backend_observation(|b| { let mut c = b.control.borrow_mut(); c.sequence += 1; c.counter += 100; c.tick_live = Some(c.sequence); Tick { observation: TickObservation { cpu: 0, source: 0x1060, sequence: c.sequence, counter: c.counter, thread: r.thread, slice: r.slice, }, nonce: c.sequence, } }) } pub(crate) fn inventory(q: &Queue) -> (usize, usize, u64) { q.with_backend_observation(|b| { ( b.pins.get(), b.hardware.borrow().pins, b.hardware.borrow().stack_pages, ) }) }