mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
pci: rollback BAR address on failed move_bar
When BAR reprogramming is detected, detect_bar_reprogramming() eagerly updates the BAR address in config space before the actual MMIO remapping occurs. If the subsequent move_bar() fails (e.g. the new address falls outside the allocator range), the config register retains the new address while the MMIO bus still uses the old one, leaving the device broken. Add restore_bar_addr() to undo the config space update when move_bar() fails, so the device remains functional at its original address. For 64-bit BARs, restore both the low and high BAR slots as well as the corresponding config registers, mirroring the two-slot update logic in detect_bar_reprogramming(). Implement restore_bar_addr() for all PciDevice implementations (VirtioPciDevice, VfioPciDevice, VfioUserPciDevice, IvshmemDevice, PvPanicDevice, and PvmemcontrolPciDevice) by delegating to their respective PciConfiguration::restore_bar_addr(). Signed-off-by: CMGS <ilskdw@gmail.com>
This commit is contained in:
@@ -1093,6 +1093,55 @@ impl PciConfiguration {
|
||||
pub(crate) fn clear_pending_bar_reprogram(&mut self) {
|
||||
self.pending_bar_reprogram = Vec::new();
|
||||
}
|
||||
|
||||
/// Restore BAR address after a failed move. This undoes the premature
|
||||
/// address update in detect_bar_reprogramming() so that config space
|
||||
/// stays consistent with the actual MMIO mapping.
|
||||
pub fn restore_bar_addr(&mut self, params: &BarReprogrammingParams) {
|
||||
match params.region_type {
|
||||
PciBarRegionType::Memory64BitRegion => {
|
||||
// 64-bit BAR spans two slots: bars[i] (low, type Memory64BitRegion)
|
||||
// and bars[i+1] (high, type None). Mirror detect_bar_reprogramming
|
||||
// by matching the combined address and restoring both halves.
|
||||
for i in 0..NUM_BAR_REGS - 1 {
|
||||
if self.bars[i].r#type != Some(PciBarRegionType::Memory64BitRegion) {
|
||||
continue;
|
||||
}
|
||||
let low_mask = self.writable_bits[BAR0_REG + i];
|
||||
let high_mask = self.writable_bits[BAR0_REG + i + 1];
|
||||
let current = (u64::from(self.bars[i + 1].addr & high_mask) << 32)
|
||||
| u64::from(self.bars[i].addr & low_mask);
|
||||
if current == params.new_base {
|
||||
let old_low = params.old_base as u32;
|
||||
let old_high = (params.old_base >> 32) as u32;
|
||||
self.bars[i].addr = old_low;
|
||||
self.bars[i + 1].addr = old_high;
|
||||
self.registers[BAR0_REG + i] =
|
||||
(self.registers[BAR0_REG + i] & !low_mask) | (old_low & low_mask);
|
||||
self.registers[BAR0_REG + i + 1] = (self.registers[BAR0_REG + i + 1]
|
||||
& !high_mask)
|
||||
| (old_high & high_mask);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// 32-bit Memory or IO BAR
|
||||
for i in 0..NUM_BAR_REGS {
|
||||
let mask = self.writable_bits[BAR0_REG + i];
|
||||
if self.bars[i].r#type == Some(params.region_type)
|
||||
&& u64::from(self.bars[i].addr & mask) == params.new_base
|
||||
{
|
||||
let old = params.old_base as u32;
|
||||
self.bars[i].addr = old;
|
||||
self.registers[BAR0_REG + i] =
|
||||
(self.registers[BAR0_REG + i] & !mask) | (old & mask);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pausable for PciConfiguration {}
|
||||
|
||||
Reference in New Issue
Block a user