use alloc::string::String;
use axdevice_base::{InterruptTrigger, ItsId};
use crate::{
CpuInterfaceState, EventId, GicAffinity, GicVcpuId, IntId, ItsDeviceId, LpiId, PhysicalIrqId,
VgicError, VgicResult,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum HostGicVersion {
V2,
V3,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VgicBackendCapabilities {
host_version: HostGicVersion,
list_register_count: usize,
priority_bits: u8,
v2_compatibility: bool,
}
impl VgicBackendCapabilities {
pub const fn new(
host_version: HostGicVersion,
list_register_count: usize,
priority_bits: u8,
v2_compatibility: bool,
) -> Self {
Self {
host_version,
list_register_count,
priority_bits,
v2_compatibility,
}
}
pub const fn host_version(self) -> HostGicVersion {
self.host_version
}
pub const fn list_register_count(self) -> usize {
self.list_register_count
}
pub const fn priority_bits(self) -> u8 {
self.priority_bits
}
pub const fn v2_compatibility(self) -> bool {
self.v2_compatibility
}
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[error("backend operation {operation} failed: {detail}")]
pub struct GicV3BackendError {
operation: &'static str,
detail: String,
}
impl GicV3BackendError {
pub fn new(operation: &'static str, detail: impl Into<String>) -> Self {
Self {
operation,
detail: detail.into(),
}
}
pub const fn operation(&self) -> &'static str {
self.operation
}
pub fn detail(&self) -> &str {
&self.detail
}
}
impl From<GicV3BackendError> for VgicError {
fn from(error: GicV3BackendError) -> Self {
Self::Backend {
operation: error.operation,
detail: error.detail,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PhysicalInterruptBinding {
guest: IntId,
host: PhysicalIrqId,
target: GicVcpuId,
affinity: GicAffinity,
trigger: InterruptTrigger,
}
impl PhysicalInterruptBinding {
pub const fn new(
guest: IntId,
host: PhysicalIrqId,
target: GicVcpuId,
affinity: GicAffinity,
trigger: InterruptTrigger,
) -> Self {
Self {
guest,
host,
target,
affinity,
trigger,
}
}
pub const fn guest(self) -> IntId {
self.guest
}
pub const fn host(self) -> PhysicalIrqId {
self.host
}
pub const fn target(self) -> GicVcpuId {
self.target
}
pub const fn affinity(self) -> GicAffinity {
self.affinity
}
pub const fn trigger(self) -> InterruptTrigger {
self.trigger
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PhysicalMsiBinding {
its: ItsId,
device: ItsDeviceId,
event: EventId,
lpi: LpiId,
target: GicVcpuId,
affinity: GicAffinity,
}
impl PhysicalMsiBinding {
pub const fn new(
its: ItsId,
device: ItsDeviceId,
event: EventId,
lpi: LpiId,
target: GicVcpuId,
affinity: GicAffinity,
) -> Self {
Self {
its,
device,
event,
lpi,
target,
affinity,
}
}
pub const fn its(self) -> ItsId {
self.its
}
pub const fn device(self) -> ItsDeviceId {
self.device
}
pub const fn event(self) -> EventId {
self.event
}
pub const fn lpi(self) -> LpiId {
self.lpi
}
pub const fn target(self) -> GicVcpuId {
self.target
}
pub const fn affinity(self) -> GicAffinity {
self.affinity
}
}
pub trait GicV3Backend: Send + Sync {
fn capabilities(&self) -> VgicBackendCapabilities {
VgicBackendCapabilities::new(HostGicVersion::V3, 16, 5, false)
}
fn load_cpu_interface(
&self,
vcpu: GicVcpuId,
state: &CpuInterfaceState,
) -> Result<(), GicV3BackendError>;
fn save_cpu_interface(
&self,
vcpu: GicVcpuId,
state: &mut CpuInterfaceState,
) -> Result<(), GicV3BackendError>;
fn retire_emulated_interrupt(
&self,
_vcpu: GicVcpuId,
_intid: IntId,
) -> Result<(), GicV3BackendError> {
Ok(())
}
fn complete_physical_interrupt(
&self,
vcpu: GicVcpuId,
binding: PhysicalInterruptBinding,
) -> Result<(), GicV3BackendError> {
self.deactivate_physical_interrupt(vcpu, binding)
}
fn deactivate_physical_interrupt(
&self,
_vcpu: GicVcpuId,
_binding: PhysicalInterruptBinding,
) -> Result<(), GicV3BackendError> {
Err(GicV3BackendError::new(
"deactivate physical interrupt",
"the backend does not support trapped physical interrupt deactivation",
))
}
fn bind_physical_interrupt(
&self,
_binding: PhysicalInterruptBinding,
) -> Result<(), GicV3BackendError> {
Err(GicV3BackendError::new(
"bind physical interrupt",
"the backend does not support physical interrupt ownership",
))
}
fn set_physical_interrupt_enabled(
&self,
_binding: PhysicalInterruptBinding,
_enabled: bool,
) -> Result<(), GicV3BackendError> {
Err(GicV3BackendError::new(
"set physical interrupt enable state",
"the backend does not support physical interrupt ownership",
))
}
fn bind_physical_msi(&self, _binding: PhysicalMsiBinding) -> Result<(), GicV3BackendError> {
Err(GicV3BackendError::new(
"bind physical MSI",
"the backend does not support physical ITS ownership",
))
}
fn signal_physical_msi(&self, _binding: PhysicalMsiBinding) -> Result<(), GicV3BackendError> {
Err(GicV3BackendError::new(
"signal physical MSI",
"the backend does not support physical ITS delivery",
))
}
fn unbind_physical_interrupt(
&self,
_binding: PhysicalInterruptBinding,
) -> Result<(), GicV3BackendError> {
Ok(())
}
fn unbind_physical_msi(&self, _binding: PhysicalMsiBinding) -> Result<(), GicV3BackendError> {
Ok(())
}
}
pub use GicV3Backend as VgicBackend;
#[derive(Debug, Default)]
pub struct SoftwareGicV3Backend;
impl GicV3Backend for SoftwareGicV3Backend {
fn load_cpu_interface(
&self,
_vcpu: GicVcpuId,
_state: &CpuInterfaceState,
) -> Result<(), GicV3BackendError> {
Ok(())
}
fn save_cpu_interface(
&self,
_vcpu: GicVcpuId,
_state: &mut CpuInterfaceState,
) -> Result<(), GicV3BackendError> {
Ok(())
}
}
pub(crate) fn backend_result<T>(result: Result<T, GicV3BackendError>) -> VgicResult<T> {
result.map_err(Into::into)
}