use volatile_register::WO;
#[repr(C)]
pub struct Imsic {
pub seteipnum_le: WO<u32>,
pub seteipnum_be: WO<u32>,
}
impl Imsic {
pub const fn size() -> usize {
0x1000 }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Eidelivery(u32);
impl Eidelivery {
pub const DISABLED: Self = Self(0);
pub const ENABLED: Self = Self(1);
pub const PLIC_APLIC_ENABLED: Self = Self(0x40000000);
pub const fn from_raw(value: u32) -> Self {
Self(value)
}
pub const fn raw(self) -> u32 {
self.0
}
pub const fn is_disabled(self) -> bool {
self.0 == Self::DISABLED.0
}
pub const fn is_enabled(self) -> bool {
self.0 == Self::ENABLED.0
}
pub const fn is_plic_aplic_enabled(self) -> bool {
self.0 == Self::PLIC_APLIC_ENABLED.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Eithreshold(u32);
impl Eithreshold {
pub const fn from_raw(value: u32) -> Self {
Self(value)
}
pub const fn raw(self) -> u32 {
self.0
}
pub const fn threshold(self) -> u32 {
self.0
}
pub const fn priority(self) -> u8 {
(self.0 >> 24) as u8
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Eip(u32);
impl Eip {
pub const fn from_raw(value: u32) -> Self {
Self(value)
}
pub const fn raw(self) -> u32 {
self.0
}
pub const fn is_pending(self, index: u32) -> bool {
if index >= 32 {
return false;
}
(self.0 & (1 << index)) != 0
}
pub const fn set_pending(mut self, index: u32, pending: bool) -> Self {
if index >= 32 {
return self;
}
if pending {
self.0 |= 1 << index;
} else {
self.0 &= !(1 << index);
}
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Eie(u32);
impl Eie {
pub const fn from_raw(value: u32) -> Self {
Self(value)
}
pub const fn raw(self) -> u32 {
self.0
}
pub const fn is_enabled(self, index: u32) -> bool {
if index >= 32 {
return false;
}
(self.0 & (1 << index)) != 0
}
pub const fn set_enabled(mut self, index: u32, enabled: bool) -> Self {
if index >= 32 {
return self;
}
if enabled {
self.0 |= 1 << index;
} else {
self.0 &= !(1 << index);
}
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Topei(u32);
impl Topei {
pub const NONE: Self = Self(0);
pub const fn from_raw(value: u32) -> Self {
Self(value)
}
pub const fn raw(self) -> u32 {
self.0
}
pub const fn interrupt_identity(self) -> u16 {
(self.0 >> 16) as u16
}
pub const fn priority(self) -> u16 {
(self.0 & 0x7FF) as u16
}
pub const fn is_pending(self) -> bool {
self.0 != 0
}
}
pub mod select {
pub const EIDELIVERY: u32 = 0x70;
pub const EITHRESHOLD: u32 = 0x72;
pub const EIP_BASE: u32 = 0x80;
pub const EIE_BASE: u32 = 0xC0;
}
pub const MAX_INTERRUPT_IDENTITY: u16 = 2047;
pub const MIN_INTERRUPT_IDENTITY: u16 = 1;
pub mod msi {
use super::{MAX_INTERRUPT_IDENTITY, MIN_INTERRUPT_IDENTITY};
#[inline]
pub const fn encode_le(identity: u16) -> u32 {
identity as u32
}
#[inline]
pub const fn encode_be(identity: u16) -> u32 {
(identity as u32) << 16
}
#[inline]
pub const fn decode_le(data: u32) -> Option<u16> {
let identity = data as u16;
if identity >= MIN_INTERRUPT_IDENTITY && identity <= MAX_INTERRUPT_IDENTITY {
Some(identity)
} else {
None
}
}
#[inline]
pub const fn decode_be(data: u32) -> Option<u16> {
let identity = ((data >> 16) & 0xFFFF) as u16;
if identity >= MIN_INTERRUPT_IDENTITY && identity <= MAX_INTERRUPT_IDENTITY {
Some(identity)
} else {
None
}
}
}
pub mod file_ops {
use super::{Eie, Eip, MAX_INTERRUPT_IDENTITY, select};
#[inline]
pub const fn identity_to_register(identity: u16) -> Option<(u32, u32)> {
if identity == 0 || identity > MAX_INTERRUPT_IDENTITY {
return None;
}
let reg_index = ((identity - 1) / 32) as u32;
let bit_pos = ((identity - 1) % 32) as u32;
Some((reg_index, bit_pos))
}
#[inline]
pub const fn register_to_identity(reg_index: u32, bit_pos: u32) -> Option<u16> {
if reg_index > 63 || bit_pos > 31 {
return None;
}
let identity = (reg_index * 32 + bit_pos + 1) as u16;
if identity <= MAX_INTERRUPT_IDENTITY {
Some(identity)
} else {
None
}
}
#[inline]
pub const fn eip_select(reg_index: u32) -> u32 {
select::EIP_BASE + reg_index
}
#[inline]
pub const fn eie_select(reg_index: u32) -> u32 {
select::EIE_BASE + reg_index
}
#[inline]
pub const fn is_valid_identity(identity: u16) -> bool {
identity >= 1 && identity <= MAX_INTERRUPT_IDENTITY
}
pub struct IdentityIterator {
current: u16,
}
impl IdentityIterator {
pub const fn new() -> Self {
Self { current: 1 }
}
}
impl Iterator for IdentityIterator {
type Item = u16;
fn next(&mut self) -> Option<Self::Item> {
if self.current <= MAX_INTERRUPT_IDENTITY {
let result = self.current;
self.current += 1;
Some(result)
} else {
None
}
}
}
pub mod bulk {
use super::*;
pub fn set_pending_batch<const N: usize>(identities: &[u16; N]) -> [(u32, Eip); 64] {
let mut updates = [(0, Eip::from_raw(0)); 64];
for &identity in identities {
if let Some((reg_idx, bit_pos)) = identity_to_register(identity) {
if reg_idx < 64 {
let eip = &mut updates[reg_idx as usize].1;
updates[reg_idx as usize].0 = reg_idx;
*eip = eip.set_pending(bit_pos, true);
}
}
}
updates
}
pub fn set_enable_batch<const N: usize>(identities: &[u16; N]) -> [(u32, Eie); 64] {
let mut updates = [(0, Eie::from_raw(0)); 64];
for &identity in identities {
if let Some((reg_idx, bit_pos)) = identity_to_register(identity) {
if reg_idx < 64 {
let eie = &mut updates[reg_idx as usize].1;
updates[reg_idx as usize].0 = reg_idx;
*eie = eie.set_enabled(bit_pos, true);
}
}
}
updates
}
}
}
pub mod system {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AddressLayout {
pub machine_base: usize,
pub supervisor_base: usize,
pub guest_base: Option<usize>,
pub hart_index_bits: u32,
pub group_bits: u32,
pub hart_offset_bits: u32,
pub guest_offset_bits: u32,
}
impl AddressLayout {
pub const fn default() -> Self {
Self {
machine_base: 0x2800_0000,
supervisor_base: 0x2800_4000,
guest_base: Some(0x2800_8000),
hart_index_bits: 12,
group_bits: 24,
hart_offset_bits: 12,
guest_offset_bits: 12,
}
}
pub const fn machine_interrupt_file_address(&self, hart_id: u32, group_id: u32) -> usize {
self.machine_base
+ (group_id << self.group_bits) as usize
+ (hart_id << self.hart_offset_bits) as usize
}
pub const fn supervisor_interrupt_file_address(
&self,
hart_id: u32,
group_id: u32,
) -> usize {
self.supervisor_base
+ (group_id << self.group_bits) as usize
+ (hart_id << self.hart_offset_bits) as usize
}
pub fn guest_interrupt_file_address(
&self,
hart_id: u32,
group_id: u32,
guest_index: u32,
) -> Option<usize> {
self.guest_base.map(|base| {
base + (group_id << self.group_bits) as usize
+ (hart_id << self.guest_offset_bits) as usize
+ (guest_index << self.guest_offset_bits) as usize
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Capabilities {
pub max_identities: u16,
pub guest_files_per_hart: u32,
pub big_endian_msi: bool,
pub priority_threshold: bool,
}
impl Capabilities {
pub const fn standard() -> Self {
Self {
max_identities: super::MAX_INTERRUPT_IDENTITY,
guest_files_per_hart: crate::geilen::MAX_GUEST_FILES_PER_HART, big_endian_msi: true,
priority_threshold: true,
}
}
pub const fn minimal() -> Self {
Self {
max_identities: 63, guest_files_per_hart: 0,
big_endian_msi: false,
priority_threshold: false,
}
}
}
}