devices: pci: make get_bar_addr work for all BAR types

Previously, PciConfiguration::get_bar_addr would only correctly return
the value of a 32-bit memory region; implement support for the other
valid BAR types as well.

BUG=None
TEST=cargo test -p devices

Change-Id: I221187dfb96b31d7fead73eccf605a0886021d8b
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1880164
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
Daniel Verkamp 2019-10-24 16:27:12 -07:00 committed by Commit Bot
parent 8958407dcb
commit 8ec87d6d33
3 changed files with 127 additions and 10 deletions

View file

@ -203,8 +203,8 @@ impl PciDevice for Ac97Dev {
}
fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
let bar0 = u64::from(self.config_regs.get_bar_addr(0));
let bar1 = u64::from(self.config_regs.get_bar_addr(1));
let bar0 = self.config_regs.get_bar_addr(0);
let bar1 = self.config_regs.get_bar_addr(1);
match addr {
a if a >= bar0 && a < bar0 + MIXER_REGS_SIZE => self.read_mixer(addr - bar0, data),
a if a >= bar1 && a < bar1 + MASTER_REGS_SIZE => {
@ -215,8 +215,8 @@ impl PciDevice for Ac97Dev {
}
fn write_bar(&mut self, addr: u64, data: &[u8]) {
let bar0 = u64::from(self.config_regs.get_bar_addr(0));
let bar1 = u64::from(self.config_regs.get_bar_addr(1));
let bar0 = self.config_regs.get_bar_addr(0);
let bar1 = self.config_regs.get_bar_addr(1);
match addr {
a if a >= bar0 && a < bar0 + MIXER_REGS_SIZE => self.write_mixer(addr - bar0, data),
a if a >= bar1 && a < bar1 + MASTER_REGS_SIZE => {

View file

@ -177,7 +177,7 @@ pub struct PciConfiguration {
}
/// See pci_regs.h in kernel
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PciBarRegionType {
Memory32BitRegion = 0,
IORegion = 0x01,
@ -430,11 +430,38 @@ impl PciConfiguration {
Ok(config.reg_idx)
}
/// Returns the type of the given BAR region.
pub fn get_bar_type(&self, bar_num: usize) -> Option<PciBarRegionType> {
let reg_idx = BAR0_REG + bar_num;
let reg_value = self.registers.get(reg_idx)?;
match (reg_value & 1, (reg_value >> 1u32) & 3) {
(1, _) => Some(PciBarRegionType::IORegion),
(0, 0b00) => Some(PciBarRegionType::Memory32BitRegion),
(0, 0b10) => Some(PciBarRegionType::Memory64BitRegion),
_ => None,
}
}
/// Returns the address of the given BAR region.
pub fn get_bar_addr(&self, bar_num: usize) -> u32 {
pub fn get_bar_addr(&self, bar_num: usize) -> u64 {
let bar_idx = BAR0_REG + bar_num;
self.registers[bar_idx] & BAR_MEM_ADDR_MASK
let bar_type = match self.get_bar_type(bar_num) {
Some(t) => t,
None => return 0,
};
match bar_type {
PciBarRegionType::IORegion => u64::from(self.registers[bar_idx] & BAR_IO_ADDR_MASK),
PciBarRegionType::Memory32BitRegion => {
u64::from(self.registers[bar_idx] & BAR_MEM_ADDR_MASK)
}
PciBarRegionType::Memory64BitRegion => {
u64::from(self.registers[bar_idx] & BAR_MEM_ADDR_MASK)
| u64::from(self.registers[bar_idx + 1]) << 32
}
}
}
/// Configures the IRQ line and pin used by this device.
@ -667,4 +694,94 @@ mod tests {
// The original vendor and device ID should remain.
assert_eq!(cfg.read_reg(0), 0x56781234);
}
#[test]
fn add_pci_bar_mem_64bit() {
let mut cfg = PciConfiguration::new(
0x1234,
0x5678,
PciClassCode::MultimediaController,
&PciMultimediaSubclass::AudioController,
Some(&TestPI::Test),
PciHeaderType::Device,
0xABCD,
0x2468,
);
cfg.add_pci_bar(
&PciBarConfiguration::new(
0,
0x4,
PciBarRegionType::Memory64BitRegion,
PciBarPrefetchable::NotPrefetchable,
)
.set_address(0x01234567_89ABCDE0),
)
.expect("add_pci_bar failed");
assert_eq!(
cfg.get_bar_type(0),
Some(PciBarRegionType::Memory64BitRegion)
);
assert_eq!(cfg.get_bar_addr(0), 0x01234567_89ABCDE0);
}
#[test]
fn add_pci_bar_mem_32bit() {
let mut cfg = PciConfiguration::new(
0x1234,
0x5678,
PciClassCode::MultimediaController,
&PciMultimediaSubclass::AudioController,
Some(&TestPI::Test),
PciHeaderType::Device,
0xABCD,
0x2468,
);
cfg.add_pci_bar(
&PciBarConfiguration::new(
0,
0x4,
PciBarRegionType::Memory32BitRegion,
PciBarPrefetchable::NotPrefetchable,
)
.set_address(0x12345670),
)
.expect("add_pci_bar failed");
assert_eq!(
cfg.get_bar_type(0),
Some(PciBarRegionType::Memory32BitRegion)
);
assert_eq!(cfg.get_bar_addr(0), 0x12345670);
}
#[test]
fn add_pci_bar_io() {
let mut cfg = PciConfiguration::new(
0x1234,
0x5678,
PciClassCode::MultimediaController,
&PciMultimediaSubclass::AudioController,
Some(&TestPI::Test),
PciHeaderType::Device,
0xABCD,
0x2468,
);
cfg.add_pci_bar(
&PciBarConfiguration::new(
0,
0x4,
PciBarRegionType::IORegion,
PciBarPrefetchable::NotPrefetchable,
)
.set_address(0x1230),
)
.expect("add_pci_bar failed");
assert_eq!(cfg.get_bar_type(0), Some(PciBarRegionType::IORegion));
assert_eq!(cfg.get_bar_addr(0), 0x1230);
}
}

View file

@ -458,7 +458,7 @@ impl PciDevice for VirtioPciDevice {
}
fn ioeventfds(&self) -> Vec<(&EventFd, u64, Datamatch)> {
let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize) as u64;
let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize);
let notify_base = bar0 + NOTIFICATION_BAR_OFFSET;
self.queue_evts()
.iter()
@ -504,7 +504,7 @@ impl PciDevice for VirtioPciDevice {
#[allow(clippy::absurd_extreme_comparisons)]
fn read_bar(&mut self, addr: u64, data: &mut [u8]) {
// The driver is only allowed to do aligned, properly sized access.
let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize) as u64;
let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize);
let offset = addr - bar0;
match offset {
o if COMMON_CONFIG_BAR_OFFSET <= o
@ -556,7 +556,7 @@ impl PciDevice for VirtioPciDevice {
#[allow(clippy::absurd_extreme_comparisons)]
fn write_bar(&mut self, addr: u64, data: &[u8]) {
let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize) as u64;
let bar0 = self.config_regs.get_bar_addr(self.settings_bar as usize);
let offset = addr - bar0;
match offset {
o if COMMON_CONFIG_BAR_OFFSET <= o