//! Transport-neutral modern Virtio feature negotiation and generation model. pub const VERSION_1: u64 = 1u64 << 32; pub const STATUS_ACKNOWLEDGE: u8 = 1; pub const STATUS_DRIVER: u8 = 2; pub const STATUS_DRIVER_OK: u8 = 4; pub const STATUS_FEATURES_OK: u8 = 8; pub const STATUS_FAILED: u8 = 0x80; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { ResetNotObserved, GenerationExhausted, StaleGeneration, WrongPhase, UnsupportedFeature, MissingRequired, MissingDependency, DeviceRejected, StillActive, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Phase { Idle, Reset, Acknowledged, Driver, Proposed, FeaturesOk, DriverOk, Failed } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Generation(pub u64); #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { pub phase: Phase, pub generation: Generation, pub offered: u64, pub accepted: u64, } pub struct Negotiator { phase: Phase, generation: Generation, next_generation: u64, offered: u64, accepted: u64, } impl Negotiator { pub const fn new() -> Self { Self {phase: Phase::Idle, generation: Generation(0), next_generation: 1, offered: 0, accepted: 0} } pub fn snapshot(&self) -> Snapshot { Snapshot {phase: self.phase, generation: self.generation, offered: self.offered, accepted: self.accepted} } fn check(&self, generation: Generation) -> Result<(), Error> { if self.generation != generation { return Err(Error::StaleGeneration); } Ok(()) } /// Caller has read zero from physical device status after reset. pub fn observe_reset(&mut self, status: u8) -> Result { if status != 0 { return Err(Error::ResetNotObserved); } let generation = self.next_generation; self.next_generation = generation.checked_add(1).ok_or(Error::GenerationExhausted)?; self.generation = Generation(generation); self.phase = Phase::Reset; self.offered = 0; self.accepted = 0; Ok(self.generation) } pub fn acknowledge(&mut self, generation: Generation, observed: u8) -> Result<(), Error> { self.check(generation)?; if self.phase != Phase::Reset { return Err(Error::WrongPhase); } if observed != STATUS_ACKNOWLEDGE { return Err(Error::DeviceRejected); } self.phase = Phase::Acknowledged; Ok(()) } pub fn driver(&mut self, generation: Generation, observed: u8) -> Result<(), Error> { self.check(generation)?; if self.phase != Phase::Acknowledged { return Err(Error::WrongPhase); } if observed != STATUS_ACKNOWLEDGE | STATUS_DRIVER { return Err(Error::DeviceRejected); } self.phase = Phase::Driver; Ok(()) } pub fn propose(&mut self, generation: Generation, offered: u64, accepted: u64, supported: u64, required: u64, dependencies: &[(u64, u64)]) -> Result<(), Error> { self.check(generation)?; if self.phase != Phase::Driver { return Err(Error::WrongPhase); } if accepted & !offered != 0 || accepted & !supported != 0 { return Err(Error::UnsupportedFeature); } if accepted & (required | VERSION_1) != required | VERSION_1 { return Err(Error::MissingRequired); } for &(feature, prerequisite) in dependencies { if !feature.is_power_of_two() || !prerequisite.is_power_of_two() || accepted & feature != 0 && accepted & prerequisite == 0 { return Err(Error::MissingDependency); } } self.offered = offered; self.accepted = accepted; self.phase = Phase::Proposed; Ok(()) } /// Called after writing the proposed bits and reading status back. pub fn observe_features_ok(&mut self, generation: Generation, observed: u8) -> Result<(), Error> { self.check(generation)?; if self.phase != Phase::Proposed { return Err(Error::WrongPhase); } if observed & STATUS_FEATURES_OK == 0 || observed & STATUS_FAILED != 0 || observed & (STATUS_ACKNOWLEDGE | STATUS_DRIVER) != STATUS_ACKNOWLEDGE | STATUS_DRIVER { self.phase = Phase::Failed; return Err(Error::DeviceRejected); } self.phase = Phase::FeaturesOk; Ok(()) } pub fn observe_driver_ok(&mut self, generation: Generation, observed: u8) -> Result<(), Error> { self.check(generation)?; if self.phase != Phase::FeaturesOk { return Err(Error::WrongPhase); } if observed & (STATUS_ACKNOWLEDGE | STATUS_DRIVER | STATUS_FEATURES_OK | STATUS_DRIVER_OK) != (STATUS_ACKNOWLEDGE | STATUS_DRIVER | STATUS_FEATURES_OK | STATUS_DRIVER_OK) || observed & STATUS_FAILED != 0 { self.phase = Phase::Failed; return Err(Error::DeviceRejected); } self.phase = Phase::DriverOk; Ok(()) } pub fn release(&mut self, generation: Generation) -> Result<(), Error> { self.check(generation)?; if self.phase != Phase::Reset { return Err(Error::StillActive); } self.phase = Phase::Idle; Ok(()) } }