//! Bounded Virtio device status and queue lifetime model. pub const ACKNOWLEDGE: u8 = 1; pub const DRIVER: u8 = 2; pub const DRIVER_OK: u8 = 4; pub const FEATURES_OK: u8 = 8; pub const DEVICE_NEEDS_RESET: u8 = 0x40; pub const FAILED: u8 = 0x80; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { NotReset, Full, MissingClaim, StaleClaim, WrongPhase, InvalidWrite, Rejected, StillLive, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Phase { Reset, Acknowledged, Driver, FeaturesOk, Live, NeedsReset, Failed, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Claim { pub generation: u64 } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { pub claim: Option, pub phase: Phase, pub observed: u8, pub queue_live: bool, pub next_generation: u64, } pub struct Lifecycle { claim: Option, phase: Phase, observed: u8, queue_live: bool, next_generation: u64, } impl Lifecycle { pub const fn new() -> Self { Self {claim: None, phase: Phase::Reset, observed: 0, queue_live: false, next_generation: 1} } pub fn snapshot(&self) -> Snapshot { Snapshot {claim: self.claim, phase: self.phase, observed: self.observed, queue_live: self.queue_live, next_generation: self.next_generation} } fn check(&self, claim: Claim) -> Result<(), Error> { match self.claim { None => Err(Error::MissingClaim), Some(current) if current != claim => Err(Error::StaleClaim), Some(_) => Ok(()), } } pub fn claim_reset(&mut self, observed_status: u8, observed_queue: u16) -> Result { if self.claim.is_some() { return Err(Error::Full); } if observed_status != 0 || observed_queue != 0 { return Err(Error::NotReset); } let generation = self.next_generation; self.next_generation = generation.checked_add(1).ok_or(Error::Full)?; let claim = Claim {generation}; self.claim = Some(claim); self.phase = Phase::Reset; self.observed = 0; self.queue_live = false; Ok(claim) } /// `written` is the complete physical status value sent by the driver. pub fn advance(&mut self, claim: Claim, written: u8, observed: u8) -> Result<(), Error> { self.check(claim)?; let (expected, next) = match self.phase { Phase::Reset => (ACKNOWLEDGE, Phase::Acknowledged), Phase::Acknowledged => (ACKNOWLEDGE | DRIVER, Phase::Driver), Phase::Driver => (ACKNOWLEDGE | DRIVER | FEATURES_OK, Phase::FeaturesOk), Phase::FeaturesOk if self.queue_live => (ACKNOWLEDGE | DRIVER | FEATURES_OK | DRIVER_OK, Phase::Live), _ => return Err(Error::WrongPhase), }; if written != expected || written & self.observed != self.observed { return Err(Error::InvalidWrite); } if observed != expected { self.observed = observed; self.phase = Phase::NeedsReset; return Err(Error::Rejected); } self.observed = observed; self.phase = next; Ok(()) } /// Caller first publishes queue memory and observes queue enable=1. pub fn observe_queue_live(&mut self, claim: Claim, queue_enable: u16) -> Result<(), Error> { self.check(claim)?; if self.phase != Phase::FeaturesOk { return Err(Error::WrongPhase); } if queue_enable != 1 { return Err(Error::Rejected); } self.queue_live = true; Ok(()) } /// Device sets this bit asynchronously; the caller must cease queue use. pub fn observe_needs_reset(&mut self, claim: Claim, observed: u8) -> Result<(), Error> { self.check(claim)?; if self.phase != Phase::Live || observed & DEVICE_NEEDS_RESET == 0 || observed & self.observed != self.observed { return Err(Error::Rejected); } self.observed = observed; self.phase = Phase::NeedsReset; Ok(()) } /// Caller writes prior bits plus FAILED and checks the physical readback. pub fn fail(&mut self, claim: Claim, written: u8, observed: u8) -> Result<(), Error> { self.check(claim)?; if self.phase == Phase::Reset || self.phase == Phase::Failed { return Err(Error::WrongPhase); } if written != (self.observed | FAILED) { return Err(Error::InvalidWrite); } if observed & FAILED == 0 || observed & self.observed != self.observed { self.observed = observed; self.phase = Phase::NeedsReset; return Err(Error::Rejected); } self.observed = observed; self.phase = Phase::Failed; Ok(()) } /// Caller has written zero to physical status and read back both fields. pub fn observe_reset(&mut self, claim: Claim, observed_status: u8, observed_queue: u16) -> Result<(), Error> { self.check(claim)?; if observed_status != 0 || observed_queue != 0 { return Err(Error::NotReset); } self.observed = 0; self.queue_live = false; self.phase = Phase::Reset; Ok(()) } pub fn release(&mut self, claim: Claim) -> Result<(), Error> { self.check(claim)?; if false { return Err(Error::StillLive); } self.claim = None; Ok(()) } }