1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
//! PCI Base Address Registers
use crate::error::{self, UnexpectedValue};
use mycelium_bitfield::{pack::Pack32, FromBits};
/// A PCI Base Address Register (BAR).
///
/// Base Address Registers (or BARs) can be used to hold memory addresses used
/// by the device, or offsets for port addresses. Typically, memory address BARs
/// need to be located in physical RAM, while I/O space BARs can reside at any
/// memory address (even beyond physical memory).
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BaseAddress {
Memory32 { prefetchable: bool, addr: u32 },
Memory64 { prefetchable: bool, addr: u64 },
Io(u32),
}
impl BaseAddress {
const BAR_KIND: Pack32 = Pack32::least_significant(1);
const MEM_TYPE: Pack32<MemoryBarType> = Self::BAR_KIND.then();
const MEM_PREFETCHABLE: Pack32<bool> = Self::MEM_TYPE.then();
const MEM_ADDR: Pack32 = Self::MEM_PREFETCHABLE.remaining();
const MEM_MASK: u32 = Self::MEM_ADDR.raw_mask();
// mask out the two low bits of an IO BAR to determine the address.
const IO_MASK: u32 = !0b11;
pub(crate) fn decode_bars<const BARS: usize>(
bars: &[u32; BARS],
) -> Result<[Option<Self>; BARS], UnexpectedValue<u32>> {
let mut decoded = [None; BARS];
let mut curr_64_bit = None;
for (i, &bits) in bars.iter().enumerate() {
// if we have decoded half of a 64-bit BAR, this word is the upper
// half.
if let Some((prefetchable, low_bits)) = curr_64_bit.take() {
let addr = ((bits as u64) << 32) | (low_bits as u64);
// the previous index is a 64-bit BAR.
decoded[i - 1] = Some(Self::Memory64 { prefetchable, addr });
// this index is the upper half of the 64-bit BAR, so set it to
// `None` in the output array.
decoded[i] = None;
continue;
}
// if the first bit is set, this is an IO BAR.
if Self::BAR_KIND.unpack(bits) == 1 {
decoded[i] = Some(Self::Io(bits & Self::IO_MASK));
continue;
}
// okay, it's a memory BAR.
let prefetchable = Self::MEM_PREFETCHABLE.unpack(bits);
match Self::MEM_TYPE.try_unpack(bits)? {
// this BAR has a 32-bit base address. decode it and continue.
MemoryBarType::Base32 => {
decoded[i] = Some(Self::Memory32 {
prefetchable,
addr: bits & Self::MEM_MASK,
});
}
// this BAR has a 64-bit base address, so we'll use the next
// 32-bit word as the high part of its address.
MemoryBarType::Base64 => curr_64_bit = Some((prefetchable, bits & Self::MEM_MASK)),
// 16-bit base addresses are not supported in PCI v3.0
MemoryBarType::Legacy16 => {
return Err(
error::unexpected(bits).named("unsupported legacy 16-bit memory BAR")
);
}
}
}
Ok(decoded)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
enum MemoryBarType {
/// 32-bit base address
Base32 = 0x0,
/// This is reserved in PCI v3.0. In earlier versions it indicated a 16-bit
Legacy16 = 0x1,
/// 64-bit base address
Base64 = 0x2,
}
// === impl MemoryBarType ===
impl FromBits<u32> for MemoryBarType {
type Error = UnexpectedValue<u32>;
const BITS: u32 = 2;
fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
match bits as u8 {
bits if bits == Self::Base32 as u8 => Ok(Self::Base32),
bits if bits == Self::Base64 as u8 => Ok(Self::Base64),
bits if bits == Self::Legacy16 as u8 => Ok(Self::Legacy16),
_ => Err(crate::error::unexpected(bits).named("MemoryBarType")),
}
}
fn into_bits(self) -> u32 {
self as u8 as u32
}
}