pci: Fix BAR reprogramming detection logic

The logic wasn't quite right, as it wasn't detecting BAR reprogramming
when the upper part of the address was identical. For instance, a BAR
moved from 0x7fc0000000 to 0x7fd0000000 wasn't detected properly.

The logic has been updated and cleaned up to fix this issue, which was
observed when running Windows guests. This fixes the network hotplug
support as well.

Fixes #1797
Fixes #1798

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2021-04-20 18:42:13 +02:00 committed by Rob Bradford
parent 3d06657f06
commit 7c457378e5

View File

@ -768,85 +768,77 @@ impl PciConfiguration {
let mask = self.writable_bits[reg_idx]; let mask = self.writable_bits[reg_idx];
if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) { if (BAR0_REG..BAR0_REG + NUM_BAR_REGS).contains(&reg_idx) {
// Ignore the case where the BAR size is being asked for.
if value == 0xffff_ffff {
return None;
}
let bar_idx = reg_idx - 4; let bar_idx = reg_idx - 4;
if (value & mask) != (self.bars[bar_idx].addr & mask) { // Handle special case where the address being written is
// Handle special case where the address being written is // different from the address initially provided. This is a
// different from the address initially provided. This is a // BAR reprogramming case which needs to be properly caught.
// BAR reprogramming case which needs to be properly caught. if let Some(bar_type) = self.bars[bar_idx].r#type {
if let Some(bar_type) = self.bars[bar_idx].r#type { // In case of 64 bits memory BAR, we don't do anything until
match bar_type { // the upper BAR is modified, otherwise we would be moving the
PciBarRegionType::Memory64BitRegion => {} // BAR to a wrong location in memory.
_ => { if bar_type == PciBarRegionType::Memory64BitRegion {
// Ignore the case where the BAR size is being return None;
// asked for.
if value == 0xffff_ffff {
return None;
}
debug!(
"DETECT BAR REPROG: current 0x{:x}, new 0x{:x}",
self.registers[reg_idx], value
);
let old_base = u64::from(self.bars[bar_idx].addr & mask);
let new_base = u64::from(value & mask);
let len = u64::from(
decode_32_bits_bar_size(self.bars[bar_idx].size)
.ok_or(Error::Decode32BarSize)
.unwrap(),
);
let region_type = bar_type;
self.bars[bar_idx].addr = value;
return Some(BarReprogrammingParams {
old_base,
new_base,
len,
region_type,
});
}
}
} else if (reg_idx > BAR0_REG)
&& (self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1])
!= (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1])
{
// Ignore the case where the BAR size is being asked for.
// Because we are in the 64bits case here, we have to check
// if the lower 32bits of the current BAR have already been
// asked for the BAR size too.
if value == 0xffff_ffff
&& self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1]
== self.bars[bar_idx - 1].size & self.writable_bits[reg_idx - 1]
{
return None;
}
debug!(
"DETECT BAR REPROG: current 0x{:x}, new 0x{:x}",
self.registers[reg_idx], value
);
let old_base = u64::from(self.bars[bar_idx].addr & mask) << 32
| u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
let new_base = u64::from(value & mask) << 32
| u64::from(self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1]);
let len = decode_64_bits_bar_size(
self.bars[bar_idx].size,
self.bars[bar_idx - 1].size,
)
.ok_or(Error::Decode64BarSize)
.unwrap();
let region_type = PciBarRegionType::Memory64BitRegion;
self.bars[bar_idx].addr = value;
self.bars[bar_idx - 1].addr = self.registers[reg_idx - 1];
return Some(BarReprogrammingParams {
old_base,
new_base,
len,
region_type,
});
} }
// Ignore the case where the value is unchanged.
if (value & mask) == (self.bars[bar_idx].addr & mask) {
return None;
}
debug!(
"DETECT BAR REPROG: current 0x{:x}, new 0x{:x}",
self.registers[reg_idx], value
);
let old_base = u64::from(self.bars[bar_idx].addr & mask);
let new_base = u64::from(value & mask);
let len = u64::from(
decode_32_bits_bar_size(self.bars[bar_idx].size)
.ok_or(Error::Decode32BarSize)
.unwrap(),
);
let region_type = bar_type;
self.bars[bar_idx].addr = value;
return Some(BarReprogrammingParams {
old_base,
new_base,
len,
region_type,
});
} else if (reg_idx > BAR0_REG)
&& ((self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1])
!= (self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1])
|| (value & mask) != (self.bars[bar_idx].addr & mask))
{
debug!(
"DETECT BAR REPROG: current 0x{:x}, new 0x{:x}",
self.registers[reg_idx], value
);
let old_base = u64::from(self.bars[bar_idx].addr & mask) << 32
| u64::from(self.bars[bar_idx - 1].addr & self.writable_bits[reg_idx - 1]);
let new_base = u64::from(value & mask) << 32
| u64::from(self.registers[reg_idx - 1] & self.writable_bits[reg_idx - 1]);
let len =
decode_64_bits_bar_size(self.bars[bar_idx].size, self.bars[bar_idx - 1].size)
.ok_or(Error::Decode64BarSize)
.unwrap();
let region_type = PciBarRegionType::Memory64BitRegion;
self.bars[bar_idx].addr = value;
self.bars[bar_idx - 1].addr = self.registers[reg_idx - 1];
return Some(BarReprogrammingParams {
old_base,
new_base,
len,
region_type,
});
} }
} else if reg_idx == ROM_BAR_REG && (value & mask) != (self.rom_bar_addr & mask) { } else if reg_idx == ROM_BAR_REG && (value & mask) != (self.rom_bar_addr & mask) {
// Ignore the case where the BAR size is being asked for. // Ignore the case where the BAR size is being asked for.