use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt;
use super::config::{COMMAND_IO, COMMAND_MEMORY};
use crate::core::error::{Error, Result};
use crate::core::space::{AddressSpace, Mapping, MappingId, Perms, RegionRef};
use crate::core::sync::{LockRank, Mutex};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BarKind {
Memory,
Io,
ExpansionRom,
}
#[derive(Clone)]
pub struct Bar {
kind: BarKind,
len: u64,
wide: bool,
prefetchable: bool,
region: Option<RegionRef>,
perms: Perms,
}
impl fmt::Debug for Bar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Bar")
.field("kind", &self.kind)
.field("len", &self.len)
.field("wide", &self.wide)
.field("prefetchable", &self.prefetchable)
.field("region", &self.region.as_ref().map(|r| r.name()))
.field("perms", &self.perms)
.finish()
}
}
impl Bar {
#[must_use]
pub fn memory(len: u64) -> Bar {
Bar {
kind: BarKind::Memory,
len,
wide: false,
prefetchable: false,
region: None,
perms: Perms::RW,
}
}
#[must_use]
pub fn io(len: u64) -> Bar {
Bar {
kind: BarKind::Io,
len,
wide: false,
prefetchable: false,
region: None,
perms: Perms::RW,
}
}
#[must_use]
pub fn rom(len: u64) -> Bar {
Bar {
kind: BarKind::ExpansionRom,
len,
wide: false,
prefetchable: false,
region: None,
perms: Perms::RX,
}
}
#[must_use]
pub fn wide(mut self) -> Bar {
self.wide = self.kind == BarKind::Memory;
self
}
#[must_use]
pub fn prefetchable(mut self) -> Bar {
self.prefetchable = self.kind == BarKind::Memory;
self
}
#[must_use]
pub fn decoding(mut self, region: RegionRef, perms: Perms) -> Bar {
self.region = Some(region);
self.perms = perms;
self
}
#[must_use]
pub fn len(&self) -> u64 {
self.len
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub fn kind(&self) -> BarKind {
self.kind
}
fn fixed_bits(&self) -> u32 {
match self.kind {
BarKind::Memory => {
let ty = if self.wide { 0b100 } else { 0b000 };
ty | if self.prefetchable { 0x8 } else { 0x0 }
}
BarKind::Io => 0x1,
BarKind::ExpansionRom => 0x0,
}
}
fn write_mask(&self) -> u32 {
let size = !(self.len.wrapping_sub(1)) as u32;
match self.kind {
BarKind::Memory => size & 0xffff_fff0,
BarKind::Io => size & 0xffff_fffc,
BarKind::ExpansionRom => (size & 0xffff_f800) | 0x1,
}
}
fn high_write_mask(&self) -> u32 {
(!(self.len.wrapping_sub(1)) >> 32) as u32
}
}
const MIN_MEMORY_LEN: u64 = 16;
const MIN_IO_LEN: u64 = 4;
const MIN_ROM_LEN: u64 = 2048;
const BAR0: u16 = 0x10;
const ROM_OFFSET: u16 = 0x30;
const BAR_PRIORITY: i32 = 2;
#[derive(Debug, Clone)]
struct Placed {
space: Arc<AddressSpace>,
ids: BTreeMap<u8, MappingId>,
}
pub struct Bars {
specs: BTreeMap<u8, Bar>,
values: Mutex<[u32; Bars::COUNT as usize]>,
placed: Mutex<Option<Placed>>,
stale: Mutex<bool>,
}
impl fmt::Debug for Bars {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("Bars");
s.field("specs", &self.specs);
match self.values.try_lock() {
Some(v) => s.field("values", &*v),
None => s.field("values", &"<in use>"),
};
s.field("placed", &self.placed.try_lock().map(|p| p.is_some()))
.finish()
}
}
impl Default for Bars {
fn default() -> Bars {
Bars::new()
}
}
impl Bars {
pub const ROM: u8 = 6;
pub const COUNT: u8 = 7;
#[must_use]
pub fn new() -> Bars {
Bars {
specs: BTreeMap::new(),
values: Mutex::with_rank(LockRank::LEAF, [0; Bars::COUNT as usize]),
placed: Mutex::with_rank(LockRank::LEAF, None),
stale: Mutex::with_rank(LockRank::LEAF, false),
}
}
pub fn with(mut self, index: u8, bar: Bar) -> Result<Bars> {
let at = |message: &str| Error::Config {
at: alloc::format!("BAR{index}"),
message: String::from(message),
};
match (index, bar.kind) {
(Bars::ROM, BarKind::ExpansionRom) => {}
(0..=5, BarKind::Memory | BarKind::Io) => {}
(Bars::ROM, _) => {
return Err(at("register 6 is the expansion ROM and holds nothing else"));
}
(_, BarKind::ExpansionRom) => {
return Err(at("the expansion ROM register is index 6, not this one"));
}
_ => return Err(at("a Type 00h header has six base address registers, 0-5")),
}
let min = match bar.kind {
BarKind::Memory => MIN_MEMORY_LEN,
BarKind::Io => MIN_IO_LEN,
BarKind::ExpansionRom => MIN_ROM_LEN,
};
if !bar.len.is_power_of_two() || bar.len < min {
return Err(at(
"a window is a power of two, and no smaller than its kind's minimum: 16 bytes \
of memory, 4 of I/O, 2048 of expansion ROM (Rev 2.1 §6.2.5.1, §6.2.5.2)",
));
}
if bar.wide && index == 5 {
return Err(at(
"a 64-bit register is two consecutive BARs, so it cannot be the last one",
));
}
if self.claimed(index) || (bar.wide && self.claimed(index + 1)) {
return Err(at("something already answers at this register"));
}
self.specs.insert(index, bar);
Ok(self)
}
fn claimed(&self, index: u8) -> bool {
if self.specs.contains_key(&index) {
return true;
}
index > 0
&& self
.specs
.get(&(index - 1))
.is_some_and(|b| b.wide && b.kind == BarKind::Memory)
}
#[must_use]
pub fn spec(&self, index: u8) -> Option<&Bar> {
self.specs.get(&index)
}
fn register_of(offset: u16) -> Option<u8> {
match offset {
0x10..=0x27 => Some(((offset - BAR0) / 4) as u8),
_ if (ROM_OFFSET..ROM_OFFSET + 4).contains(&offset) => Some(Bars::ROM),
_ => None,
}
}
fn value(&self, index: u8) -> u32 {
let raw = self.values.lock()[index as usize];
match self.specs.get(&index) {
Some(bar) => raw | bar.fixed_bits(),
None => raw,
}
}
pub fn config_read(&self, offset: u16, dst: &mut [u8]) {
for (i, slot) in dst.iter_mut().enumerate() {
let at = offset.saturating_add(i as u16);
let Some(index) = Bars::register_of(at) else {
continue;
};
let dword = self.value(index);
let byte = (at & 0x3) as u32;
*slot = (dword >> (byte * 8)) as u8;
}
}
pub fn config_write(&self, offset: u16, src: &[u8]) -> bool {
let mut changed = false;
let mut values = self.values.lock();
for (i, byte) in src.iter().enumerate() {
let at = offset.saturating_add(i as u16);
let Some(index) = Bars::register_of(at) else {
continue;
};
let mask = self.mask_of(index);
let shift = (at & 0x3) * 8;
let byte_mask = (mask >> shift) as u8;
if byte_mask == 0 {
continue;
}
let slot = &mut values[index as usize];
let keep = u32::from(byte_mask) << shift;
let updated = (*slot & !keep) | ((u32::from(*byte) << shift) & keep);
if updated != *slot {
*slot = updated;
changed = true;
}
}
changed
}
fn mask_of(&self, index: u8) -> u32 {
if let Some(bar) = self.specs.get(&index) {
return bar.write_mask();
}
if index > 0
&& let Some(bar) = self.specs.get(&(index - 1))
&& bar.wide
{
return bar.high_write_mask();
}
0
}
#[must_use]
pub fn window(&self, index: u8, command: u16) -> Option<(u64, bool)> {
let bar = self.specs.get(&index)?;
let values = self.values.lock();
let low = values[index as usize];
let base = match bar.kind {
BarKind::Memory if bar.wide => {
let high = values[index as usize + 1];
u64::from(low & bar.write_mask()) | (u64::from(high & bar.high_write_mask()) << 32)
}
_ => u64::from(low & bar.write_mask() & !0x1),
};
let decoding = match bar.kind {
BarKind::Memory => command & COMMAND_MEMORY != 0,
BarKind::Io => command & COMMAND_IO != 0,
BarKind::ExpansionRom => command & COMMAND_MEMORY != 0 && low & 0x1 != 0,
};
Some((base, decoding))
}
pub fn install(&self, space: &Arc<AddressSpace>, command: u16) -> Result<()> {
for (index, bar) in &self.specs {
if bar.region.is_some() && bar.kind == BarKind::Io {
return Err(Error::Config {
at: alloc::format!("BAR{index}"),
message: String::from(
"an I/O BAR cannot carry a region yet: a configuration cycle travels \
through the I/O space, so retopologising it from inside one is the \
case the order-exempt try-lock cannot serve",
),
});
}
}
*self.placed.lock() = Some(Placed {
space: Arc::clone(space),
ids: BTreeMap::new(),
});
self.sync(command, true);
Ok(())
}
pub fn sync(&self, command: u16, blocking: bool) -> bool {
let Some(placed) = self.placed.lock().clone() else {
return true;
};
let wanted: Vec<(u8, RegionRef, u64, Perms)> = self
.specs
.iter()
.filter_map(|(index, bar)| {
let region = bar.region.clone()?;
let (base, decoding) = self.window(*index, command)?;
decoding.then_some((*index, region, base, bar.perms))
})
.collect();
let guard = if blocking {
Some(placed.space.topology())
} else {
placed.space.try_topology()
};
let Some(mut topo) = guard else {
*self.stale.lock() = true;
return false;
};
let mut ids = placed.ids.clone();
let gone: Vec<u8> = ids
.keys()
.copied()
.filter(|index| !wanted.iter().any(|(i, ..)| i == index))
.collect();
for index in gone {
if let Some(id) = ids.remove(&index) {
let _ = topo.unmap(id);
}
}
for (index, region, base, perms) in wanted {
match ids.get(&index) {
Some(id) => {
if topo.remap(*id, base).is_err() {
let _ = topo.unmap(*id);
ids.remove(&index);
}
}
None => {
if let Ok(id) = topo.map_with(
Mapping::new(region, base)
.with_priority(BAR_PRIORITY)
.with_perms(perms),
) {
ids.insert(index, id);
}
}
}
}
drop(topo);
*self.placed.lock() = Some(Placed {
space: Arc::clone(&placed.space),
ids,
});
*self.stale.lock() = false;
true
}
#[must_use]
pub fn is_stale(&self) -> bool {
*self.stale.lock()
}
#[must_use]
pub fn latches(&self) -> [u32; Bars::COUNT as usize] {
*self.values.lock()
}
pub fn set_latches(&self, values: &[u32]) {
let mut slots = self.values.lock();
for (index, slot) in slots.iter_mut().enumerate() {
let mask = self.mask_of(index as u8);
*slot = values.get(index).copied().unwrap_or(0) & mask;
}
}
pub fn reset(&self) {
*self.values.lock() = [0; Bars::COUNT as usize];
}
}