mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-01-03 03:15:20 +00:00
pci: Fix the way PCI configuration registers are being written
The way the function write_reg() was implemented, it was not keeping the bits supposed to be read-only whenever the guest was writing to one of those. That's why this commit takes care of protecting those bits, preventing them from being updated. The tricky part is about the BARs since we also need to handle the very specific case where the BAR is being written with all 1's. In that case we want to return the size of the BAR instead of its address. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
parent
185b1082fb
commit
b157181656
@ -247,6 +247,7 @@ pub trait PciCapability {
|
|||||||
pub struct PciConfiguration {
|
pub struct PciConfiguration {
|
||||||
registers: [u32; NUM_CONFIGURATION_REGISTERS],
|
registers: [u32; NUM_CONFIGURATION_REGISTERS],
|
||||||
writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register.
|
writable_bits: [u32; NUM_CONFIGURATION_REGISTERS], // writable bits for each register.
|
||||||
|
bar_size: [u32; NUM_BAR_REGS],
|
||||||
bar_used: [bool; NUM_BAR_REGS],
|
bar_used: [bool; NUM_BAR_REGS],
|
||||||
// Contains the byte offset and size of the last capability.
|
// Contains the byte offset and size of the last capability.
|
||||||
last_capability: Option<(usize, usize)>,
|
last_capability: Option<(usize, usize)>,
|
||||||
@ -330,6 +331,7 @@ impl PciConfiguration {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
|
let mut registers = [0u32; NUM_CONFIGURATION_REGISTERS];
|
||||||
let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
|
let mut writable_bits = [0u32; NUM_CONFIGURATION_REGISTERS];
|
||||||
|
let bar_size = [0u32; NUM_BAR_REGS];
|
||||||
registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
|
registers[0] = u32::from(device_id) << 16 | u32::from(vendor_id);
|
||||||
// TODO(dverkamp): Status should be write-1-to-clear
|
// TODO(dverkamp): Status should be write-1-to-clear
|
||||||
writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
|
writable_bits[1] = 0x0000_ffff; // Status (r/o), command (r/w)
|
||||||
@ -358,6 +360,7 @@ impl PciConfiguration {
|
|||||||
PciConfiguration {
|
PciConfiguration {
|
||||||
registers,
|
registers,
|
||||||
writable_bits,
|
writable_bits,
|
||||||
|
bar_size,
|
||||||
bar_used: [false; NUM_BAR_REGS],
|
bar_used: [false; NUM_BAR_REGS],
|
||||||
last_capability: None,
|
last_capability: None,
|
||||||
msix_cap_reg_idx: None,
|
msix_cap_reg_idx: None,
|
||||||
@ -372,8 +375,17 @@ impl PciConfiguration {
|
|||||||
|
|
||||||
/// Writes a 32bit register to `reg_idx` in the register map.
|
/// Writes a 32bit register to `reg_idx` in the register map.
|
||||||
pub fn write_reg(&mut self, reg_idx: usize, value: u32) {
|
pub fn write_reg(&mut self, reg_idx: usize, value: u32) {
|
||||||
|
let mut mask = self.writable_bits[reg_idx];
|
||||||
|
if reg_idx >= BAR0_REG && reg_idx < BAR0_REG + NUM_BAR_REGS {
|
||||||
|
// Handle very specific case where the BAR is being written with
|
||||||
|
// all 1's to retrieve the BAR size on next BAR reading.
|
||||||
|
if value == 0xffff_ffff {
|
||||||
|
mask = self.bar_size[reg_idx - 4];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(r) = self.registers.get_mut(reg_idx) {
|
if let Some(r) = self.registers.get_mut(reg_idx) {
|
||||||
*r = value & self.writable_bits[reg_idx];
|
*r = (*r & !self.writable_bits[reg_idx]) | (value & mask);
|
||||||
} else {
|
} else {
|
||||||
warn!("bad PCI register write {}", reg_idx);
|
warn!("bad PCI register write {}", reg_idx);
|
||||||
}
|
}
|
||||||
@ -467,7 +479,8 @@ impl PciConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.registers[bar_idx + 1] = (config.addr >> 32) as u32;
|
self.registers[bar_idx + 1] = (config.addr >> 32) as u32;
|
||||||
self.writable_bits[bar_idx + 1] = !((config.size >> 32).wrapping_sub(1)) as u32;
|
self.writable_bits[bar_idx + 1] = 0xffff_ffff;
|
||||||
|
self.bar_size[config.reg_idx + 1] = (config.size >> 32) as u32;
|
||||||
self.bar_used[config.reg_idx + 1] = true;
|
self.bar_used[config.reg_idx + 1] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -481,7 +494,8 @@ impl PciConfiguration {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
|
self.registers[bar_idx] = ((config.addr as u32) & mask) | lower_bits;
|
||||||
self.writable_bits[bar_idx] = !(config.size - 1) as u32;
|
self.writable_bits[bar_idx] = mask;
|
||||||
|
self.bar_size[config.reg_idx] = config.size as u32;
|
||||||
self.bar_used[config.reg_idx] = true;
|
self.bar_used[config.reg_idx] = true;
|
||||||
Ok(config.reg_idx)
|
Ok(config.reg_idx)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user