d2c82add9d
* Fix Mmio repr from transparent to packed. * Rename Mmio::from to Mmio::new. * Remove dead imports around affected code. * Minor formatting.
212 lines
5.7 KiB
Rust
212 lines
5.7 KiB
Rust
use std::fmt;
|
|
|
|
use crate::driver_interface::PciBar;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use common::io::{Io, Mmio};
|
|
|
|
/// The address and data to use for MSI and MSI-X.
|
|
///
|
|
/// For MSI using this only works when you need a single interrupt vector.
|
|
/// For MSI-X you can have a single [MsiEntry] for each interrupt vector.
|
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
|
pub struct MsiAddrAndData {
|
|
pub addr: u64,
|
|
pub data: u32,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct MsiInfo {
|
|
pub log2_multiple_message_capable: u8,
|
|
pub is_64bit: bool,
|
|
pub has_per_vector_masking: bool,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct MsixInfo {
|
|
pub table_bar: u8,
|
|
pub table_offset: u32,
|
|
pub table_size: u16,
|
|
pub pba_bar: u8,
|
|
pub pba_offset: u32,
|
|
}
|
|
|
|
impl MsixInfo {
|
|
pub fn validate(&self, bars: [PciBar; 6]) {
|
|
if self.table_bar > 5 {
|
|
panic!(
|
|
"MSI-X Table BIR contained a reserved enum value: {}",
|
|
self.table_bar
|
|
);
|
|
}
|
|
if self.pba_bar > 5 {
|
|
panic!(
|
|
"MSI-X PBA BIR contained a reserved enum value: {}",
|
|
self.pba_bar
|
|
);
|
|
}
|
|
|
|
let table_size = self.table_size;
|
|
let table_offset = self.table_offset as usize;
|
|
let table_min_length = table_size * 16;
|
|
|
|
let pba_offset = self.pba_offset as usize;
|
|
let pba_min_length = table_size.div_ceil(8);
|
|
|
|
let (_, table_bar_size) = bars[self.table_bar as usize].expect_mem();
|
|
let (_, pba_bar_size) = bars[self.pba_bar as usize].expect_mem();
|
|
|
|
// Ensure that the table and PBA are within the BAR.
|
|
|
|
if !(0..table_bar_size as u64).contains(&(table_offset as u64 + table_min_length as u64)) {
|
|
panic!(
|
|
"Table {:#x}:{:#x} outside of BAR with length {:#x}",
|
|
table_offset,
|
|
table_offset + table_min_length as usize,
|
|
table_bar_size
|
|
);
|
|
}
|
|
|
|
if !(0..pba_bar_size as u64).contains(&(pba_offset as u64 + pba_min_length as u64)) {
|
|
panic!(
|
|
"PBA {:#x}:{:#x} outside of BAR with length {:#x}",
|
|
pba_offset,
|
|
pba_offset + pba_min_length as usize,
|
|
pba_bar_size
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[repr(packed)]
|
|
pub struct MsixTableEntry {
|
|
pub addr_lo: Mmio<u32>,
|
|
pub addr_hi: Mmio<u32>,
|
|
pub msg_data: Mmio<u32>,
|
|
pub vec_ctl: Mmio<u32>,
|
|
}
|
|
|
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
pub mod x86 {
|
|
#[repr(u8)]
|
|
pub enum TriggerMode {
|
|
Edge = 0,
|
|
Level = 1,
|
|
}
|
|
|
|
#[repr(u8)]
|
|
pub enum LevelTriggerMode {
|
|
Deassert = 0,
|
|
Assert = 1,
|
|
}
|
|
|
|
#[repr(u8)]
|
|
pub enum DeliveryMode {
|
|
Fixed = 0b000,
|
|
LowestPriority = 0b001,
|
|
Smi = 0b010,
|
|
// 0b011 is reserved
|
|
Nmi = 0b100,
|
|
Init = 0b101,
|
|
// 0b110 is reserved
|
|
ExtInit = 0b111,
|
|
}
|
|
|
|
// TODO: should the reserved field be preserved?
|
|
pub const fn message_address(
|
|
destination_id: u8,
|
|
redirect_hint: bool,
|
|
dest_mode_logical: bool,
|
|
) -> u64 {
|
|
0x0000_0000_FEE0_0000u64
|
|
| ((destination_id as u64) << 12)
|
|
| ((redirect_hint as u64) << 3)
|
|
| ((dest_mode_logical as u64) << 2)
|
|
}
|
|
pub const fn message_data(
|
|
trigger_mode: TriggerMode,
|
|
level_trigger_mode: LevelTriggerMode,
|
|
delivery_mode: DeliveryMode,
|
|
vector: u8,
|
|
) -> u32 {
|
|
((trigger_mode as u32) << 15)
|
|
| ((level_trigger_mode as u32) << 14)
|
|
| ((delivery_mode as u32) << 8)
|
|
| vector as u32
|
|
}
|
|
pub const fn message_data_level_triggered(
|
|
level_trigger_mode: LevelTriggerMode,
|
|
delivery_mode: DeliveryMode,
|
|
vector: u8,
|
|
) -> u32 {
|
|
message_data(
|
|
TriggerMode::Level,
|
|
level_trigger_mode,
|
|
delivery_mode,
|
|
vector,
|
|
)
|
|
}
|
|
pub const fn message_data_edge_triggered(delivery_mode: DeliveryMode, vector: u8) -> u32 {
|
|
message_data(
|
|
TriggerMode::Edge,
|
|
LevelTriggerMode::Deassert,
|
|
delivery_mode,
|
|
vector,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl MsixTableEntry {
|
|
pub fn addr_lo(&self) -> u32 {
|
|
self.addr_lo.read()
|
|
}
|
|
pub fn addr_hi(&self) -> u32 {
|
|
self.addr_hi.read()
|
|
}
|
|
pub fn set_addr_lo(&mut self, value: u32) {
|
|
self.addr_lo.write(value);
|
|
}
|
|
pub fn set_addr_hi(&mut self, value: u32) {
|
|
self.addr_hi.write(value);
|
|
}
|
|
pub fn msg_data(&self) -> u32 {
|
|
self.msg_data.read()
|
|
}
|
|
pub fn vec_ctl(&self) -> u32 {
|
|
self.vec_ctl.read()
|
|
}
|
|
pub fn set_msg_data(&mut self, value: u32) {
|
|
self.msg_data.write(value);
|
|
}
|
|
pub fn addr(&self) -> u64 {
|
|
u64::from(self.addr_lo()) | (u64::from(self.addr_hi()) << 32)
|
|
}
|
|
pub const VEC_CTL_MASK_BIT: u32 = 1;
|
|
|
|
pub fn set_masked(&mut self, masked: bool) {
|
|
self.vec_ctl.writef(Self::VEC_CTL_MASK_BIT, masked)
|
|
}
|
|
pub fn mask(&mut self) {
|
|
self.set_masked(true);
|
|
}
|
|
pub fn unmask(&mut self) {
|
|
self.set_masked(false);
|
|
}
|
|
|
|
pub fn write_addr_and_data(&mut self, entry: MsiAddrAndData) {
|
|
self.set_addr_lo(entry.addr as u32);
|
|
self.set_addr_hi((entry.addr >> 32) as u32);
|
|
self.set_msg_data(entry.data);
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for MsixTableEntry {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
f.debug_struct("MsixTableEntry")
|
|
.field("addr", &self.addr())
|
|
.field("msg_data", &self.msg_data())
|
|
.field("vec_ctl", &self.vec_ctl())
|
|
.finish()
|
|
}
|
|
}
|