use alloc::string::String;
use core::fmt::{Debug, LowerHex};
use ax_memory_addr::AddrRange;
use axvm_types::GuestPhysAddr;
pub use axvm_types::{AccessWidth, Port, SysRegAddr};
use crate::DeviceVcpuId;
pub trait DeviceAddr: Copy + Eq + Ord + core::fmt::Debug {}
pub trait DeviceAddrRange: Copy + Eq + LowerHex {
type Addr: DeviceAddr;
const BUS_NAME: &'static str;
fn contains(&self, addr: Self::Addr) -> bool;
fn is_empty(&self) -> bool;
fn overlaps(&self, other: &Self) -> bool;
}
impl DeviceAddr for GuestPhysAddr {}
impl DeviceAddrRange for AddrRange<GuestPhysAddr> {
type Addr = GuestPhysAddr;
const BUS_NAME: &'static str = "mmio";
fn contains(&self, addr: Self::Addr) -> bool {
Self::contains(*self, addr)
}
fn is_empty(&self) -> bool {
Self::is_empty(*self)
}
fn overlaps(&self, other: &Self) -> bool {
Self::overlaps(*self, *other)
}
}
impl DeviceAddr for SysRegAddr {}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct SysRegAddrRange {
pub start: SysRegAddr,
pub end: SysRegAddr,
}
impl SysRegAddrRange {
pub fn new(start: SysRegAddr, end: SysRegAddr) -> Self {
Self { start, end }
}
}
impl DeviceAddrRange for SysRegAddrRange {
type Addr = SysRegAddr;
const BUS_NAME: &'static str = "sys_reg";
fn contains(&self, addr: Self::Addr) -> bool {
addr.0 >= self.start.0 && addr.0 <= self.end.0
}
fn is_empty(&self) -> bool {
self.start > self.end
}
fn overlaps(&self, other: &Self) -> bool {
!self.is_empty() && !other.is_empty() && self.start <= other.end && other.start <= self.end
}
}
impl LowerHex for SysRegAddrRange {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#x}..={:#x}", self.start.0, self.end.0)
}
}
impl DeviceAddr for Port {}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct PortRange {
pub start: Port,
pub end: Port,
}
impl PortRange {
pub fn new(start: Port, end: Port) -> Self {
Self { start, end }
}
}
impl DeviceAddrRange for PortRange {
type Addr = Port;
const BUS_NAME: &'static str = "port";
fn contains(&self, addr: Self::Addr) -> bool {
addr.0 >= self.start.0 && addr.0 <= self.end.0
}
fn is_empty(&self) -> bool {
self.start > self.end
}
fn overlaps(&self, other: &Self) -> bool {
!self.is_empty() && !other.is_empty() && self.start <= other.end && other.start <= self.end
}
}
impl LowerHex for PortRange {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#x}..={:#x}", self.start.0, self.end.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BusKind {
Mmio,
Port,
SysReg,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct DeviceAccess {
source_vcpu: DeviceVcpuId,
bus: BusKind,
address: u64,
width: AccessWidth,
}
impl DeviceAccess {
pub const fn new(
source_vcpu: DeviceVcpuId,
bus: BusKind,
address: u64,
width: AccessWidth,
) -> Self {
Self {
source_vcpu,
bus,
address,
width,
}
}
pub const fn source_vcpu(self) -> DeviceVcpuId {
self.source_vcpu
}
pub const fn bus(self) -> BusKind {
self.bus
}
pub const fn address(self) -> u64 {
self.address
}
pub const fn width(self) -> AccessWidth {
self.width
}
}
#[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)]
pub enum DeviceError {
#[error("no device was found for the requested bus access")]
NotFound,
#[error("invalid device access width: expected {expected:?}, got {actual:?}")]
InvalidWidth {
expected: AccessWidth,
actual: AccessWidth,
},
#[error("attempted to write a read-only device register")]
ReadOnly,
#[error("attempted to read a write-only device register")]
WriteOnly,
#[error("device address {addr:#x} is outside the registered range")]
OutOfRange {
addr: u64,
},
#[error("device operation is not implemented")]
Unimplemented,
#[error("internal device error")]
Internal,
#[error("invalid input for device operation {operation}: {detail}")]
InvalidInput {
operation: &'static str,
detail: String,
},
#[error("invalid data for device operation {operation}: {detail}")]
InvalidData {
operation: &'static str,
detail: String,
},
#[error("invalid state for device operation {operation}: {detail}")]
InvalidState {
operation: &'static str,
detail: String,
},
#[error("unsupported device operation {operation}: {detail}")]
Unsupported {
operation: &'static str,
detail: String,
},
#[error("out of memory during device operation {operation}")]
OutOfMemory {
operation: &'static str,
},
#[error("device resource {resource} is busy during {operation}")]
ResourceBusy {
operation: &'static str,
resource: String,
},
#[error("device backend operation {operation} failed: {detail}")]
Backend {
operation: &'static str,
detail: String,
},
}
pub type DeviceResult<T = ()> = Result<T, DeviceError>;