use std::collections::HashMap;
use std::iter::zip;
use std::sync::Arc;
use parking_lot::{Mutex, RwLock};
use crate::device::{self, Pause};
use crate::mem::emulated::{Action, Mmio};
use crate::pci::config::{BAR_IO, BAR_MEM64, BAR_PREFETCHABLE, PciConfig};
use crate::pci::{Bdf, Pci, Result};
use crate::{align_up, mem};
#[derive(Debug)]
struct EmptyDevice;
impl Pause for EmptyDevice {
fn pause(&self) -> device::Result<()> {
Ok(())
}
fn resume(&self) -> device::Result<()> {
Ok(())
}
}
impl Pci for EmptyDevice {
fn name(&self) -> &str {
"empty_device"
}
fn config(&self) -> &dyn PciConfig {
unreachable!()
}
fn reset(&self) -> Result<()> {
unreachable!()
}
}
#[derive(Debug)]
pub struct PciSegment {
devices: RwLock<HashMap<Bdf, Arc<dyn Pci>>>,
next_bdf: Mutex<Bdf>,
placeholder: Arc<dyn Pci>,
}
impl PciSegment {
pub fn new() -> Self {
Self {
devices: RwLock::new(HashMap::new()),
next_bdf: Mutex::new(Bdf::new(0, 0, 0)),
placeholder: Arc::new(EmptyDevice),
}
}
pub fn max_bus(&self) -> Option<u8> {
let devices = self.devices.read();
devices.keys().map(|bdf| bdf.bus()).max()
}
pub fn add(&self, bdf: Bdf, dev: Arc<dyn Pci>) -> Option<Arc<dyn Pci>> {
let mut configs = self.devices.write();
if let Some(exist_dev) = configs.insert(bdf, dev) {
if Arc::ptr_eq(&exist_dev, &self.placeholder) {
None
} else {
configs.insert(bdf, exist_dev)
}
} else {
None
}
}
pub fn reserve(&self, bdf: Option<Bdf>) -> Option<Bdf> {
let mut empty_dev = self.placeholder.clone();
match bdf {
Some(bdf) => {
if self.add(bdf, empty_dev).is_none() {
Some(bdf)
} else {
None
}
}
None => {
let mut next_bdf = self.next_bdf.lock();
let init = *next_bdf;
loop {
let bdf = *next_bdf;
*next_bdf = Bdf(next_bdf.0.wrapping_add(8));
match self.add(bdf, empty_dev) {
None => break Some(bdf),
Some(d) => empty_dev = d,
}
if *next_bdf == init {
break None;
}
}
}
}
}
pub fn assign_resources(&self, resources: &[(u64, u64); 4]) {
let mut bar_lists = [const { vec![] }; 4];
let devices = self.devices.read();
for (bdf, dev) in devices.iter() {
let config = dev.config();
let header = config.get_header().data.read();
let mut index = 0;
while index < 6 {
let bar_index = index;
index += 1;
let (val, mask) = header.get_bar(bar_index);
let mut mask = mask as u64;
if val & BAR_MEM64 == BAR_MEM64 {
let (_, mask_hi) = header.get_bar(bar_index + 1);
mask |= (mask_hi as u64) << 32;
index += 1;
}
if mask == 0 {
continue;
}
let bar_list = if val & BAR_IO == BAR_IO {
&mut bar_lists[0]
} else if val & (BAR_MEM64 | BAR_PREFETCHABLE) == BAR_MEM64 | BAR_PREFETCHABLE {
&mut bar_lists[3]
} else if val & (BAR_MEM64 | BAR_PREFETCHABLE) == BAR_MEM64 {
unreachable!("{bdf}: BAR {index} is 64-bit but not prefetchable")
} else if val & BAR_PREFETCHABLE == BAR_PREFETCHABLE {
&mut bar_lists[2]
} else {
&mut bar_lists[1]
};
bar_list.push((*bdf, dev, bar_index, 1 << mask.trailing_zeros()));
}
}
for bar_list in bar_lists.iter_mut() {
bar_list.sort_by_key(|(bdf, _, index, size)| (u64::MAX - size, *bdf, *index));
}
for (bar_list, (start, end)) in zip(bar_lists, resources) {
let mut addr = *start;
for (bdf, dev, index, size) in bar_list {
let config = dev.config();
let mut header = config.get_header().data.write();
let aligned_addr = align_up!(addr, size.trailing_zeros());
if aligned_addr + size > *end {
log::error!(
"{bdf}: cannot map BAR {index} into address range {start:#x}..{end:#x}"
);
continue;
}
header.set_bar(index, aligned_addr as u32);
if aligned_addr > u32::MAX as u64 {
header.set_bar(index + 1, (aligned_addr >> 32) as u32);
}
addr = aligned_addr + size;
}
}
}
pub fn reset(&self) -> Result<()> {
let devices = self.devices.read();
for (_, dev) in devices.iter() {
dev.reset()?;
dev.config().reset()?;
}
Ok(())
}
}
impl Default for PciSegment {
fn default() -> Self {
Self::new()
}
}
impl Mmio for PciSegment {
fn size(&self) -> u64 {
256 * 32 * 8 * 4096
}
fn read(&self, offset: u64, size: u8) -> Result<u64, mem::Error> {
let bdf = Bdf((offset >> 12) as u16);
let devices = self.devices.read();
if let Some(dev) = devices.get(&bdf) {
dev.config().read(offset & 0xfff, size)
} else {
Ok(u64::MAX)
}
}
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {
let bdf = Bdf((offset >> 12) as u16);
let devices = self.devices.read();
if let Some(dev) = devices.get(&bdf) {
dev.config().write(offset & 0xfff, size, val)
} else {
Ok(Action::None)
}
}
}
#[cfg(test)]
#[path = "segment_test.rs"]
mod tests;