Skip to content

Commit 6b70299

Browse files
committed
fix(pci): disable decoding while sizing BARs
Determining the size of a BAR writes all-ones into it and restores the previous value afterwards. While decoding is enabled, the device decodes those intermediate values as real addresses and relocates itself. For a passed-through device the host follows that relocation and tries to map the BAR outside of the guest, which aborts the VM: kvm_set_user_memory_region: KVM_SET_USER_MEMORY_REGION failed, slot=5, start=0xfffffffffe000000, size=0x400000: Invalid argument Disable decoding around the probe, as the PCI specification requires, and route the informational bus dump through get_bar so that it is covered as well.
1 parent d0854f9 commit 6b70299

1 file changed

Lines changed: 18 additions & 4 deletions

File tree

src/drivers/pci.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,23 @@ impl<T: ConfigRegionAccess> PciDevice<T> {
7373

7474
/// Returns the bar at bar-register `slot`.
7575
pub fn get_bar(&self, slot: u8) -> Option<Bar> {
76-
let header = self.header();
77-
let endpoint = EndpointHeader::from_header(header, &self.access)?;
78-
endpoint.bar(slot, &self.access)
76+
let endpoint = EndpointHeader::from_header(self.header(), &self.access)?;
77+
let mut header = self.header();
78+
79+
// Determining the size of a bar writes all-ones into it and restores the
80+
// old value afterwards. While decoding is enabled, the device decodes
81+
// these intermediate values as real addresses and relocates itself. On a
82+
// passed-through device the host follows that relocation and tries to map
83+
// the bar at an address outside of the guest, which kills the VM. The PCI
84+
// specification requires decoding to be disabled while sizing a bar.
85+
let command = header.command(&self.access);
86+
header.update_command(&self.access, |command| {
87+
command & !(CommandRegister::IO_ENABLE | CommandRegister::MEMORY_ENABLE)
88+
});
89+
let bar = endpoint.bar(slot, &self.access);
90+
header.update_command(&self.access, |_| command);
91+
92+
bar
7993
}
8094

8195
/// Configure the bar at register `slot`
@@ -268,7 +282,7 @@ impl<T: ConfigRegionAccess> fmt::Display for PciDevice<T> {
268282

269283
let mut slot: u8 = 0;
270284
while usize::from(slot) < MAX_BARS {
271-
if let Some(pci_bar) = endpoint.bar(slot, &self.access) {
285+
if let Some(pci_bar) = self.get_bar(slot) {
272286
match pci_bar {
273287
Bar::Memory64 {
274288
address,

0 commit comments

Comments
 (0)