vm-virtio: Allocate BARs for virtio-block devices in 32-bit hole

Currently all devices and guest memory share the same 64GiB
allocation. With guest memory working upwards and devices working
downwards. This creates issues if you want to either have a VM with a
large amount of memory or want to have devices with a large allocation
(e.g. virtio-pmem.)

As it is possible for the hypervisor to place devices anywhere in its
address range it is required for simplistic users like the firmware to
set up an identity page table mapping across the full range. Currently
the hypervisor sets up an identify mapping of 1GiB which the firmware
extends to 64GiB to match the current address space size of the
hypervisor.

A simpler solution is to place the device needed for booting with the
firmware (virtio-block) inside the 32-bit memory hole. This allows the
firmware to easily access the block device and paves the way for
increasing the address space beyond the current 64GiB limit.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2019-09-17 17:34:51 +01:00
parent f0360c92d9
commit 180e6d1e78

View File

@ -239,6 +239,9 @@ pub struct VirtioPciDevice {
// Setting PCI BAR
settings_bar: u8,
// Whether to use 64-bit bar location or 32-bit
use_64bit_bar: bool,
}
impl VirtioPciDevice {
@ -268,15 +271,22 @@ impl VirtioPciDevice {
(None, None)
};
// All device types *except* virtio block devices should be allocated a 64-bit bar
// The block devices should be given a 32-bit BAR so that they are easily accessible
// to firmware without requiring excessive identity mapping.
let mut use_64bit_bar = true;
let (class, subclass) = match VirtioDeviceType::from(device.device_type()) {
VirtioDeviceType::TYPE_NET => (
PciClassCode::NetworkController,
&PciNetworkControllerSubclass::EthernetController as &dyn PciSubclass,
),
VirtioDeviceType::TYPE_BLOCK => (
PciClassCode::MassStorage,
&PciMassStorageSubclass::MassStorage as &dyn PciSubclass,
),
VirtioDeviceType::TYPE_BLOCK => {
use_64bit_bar = false;
(
PciClassCode::MassStorage,
&PciMassStorageSubclass::MassStorage as &dyn PciSubclass,
)
}
_ => (
PciClassCode::Other,
&PciVirtioSubclass::NonTransitionalBase as &dyn PciSubclass,
@ -315,6 +325,7 @@ impl VirtioPciDevice {
queue_evts,
memory: Some(memory),
settings_bar: 0,
use_64bit_bar,
})
}
@ -531,9 +542,28 @@ impl PciDevice for VirtioPciDevice {
// Allocate the virtio-pci capability BAR.
// See http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1.0-cs04.html#x1-740004
let virtio_pci_bar_addr = allocator
.allocate_mmio_addresses(None, CAPABILITY_BAR_SIZE, None)
.ok_or(PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE))?;
let virtio_pci_bar_addr = if self.use_64bit_bar {
let addr = allocator
.allocate_mmio_addresses(None, CAPABILITY_BAR_SIZE, None)
.ok_or(PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE))?;
ranges.push((
addr,
CAPABILITY_BAR_SIZE,
PciBarRegionType::Memory64BitRegion,
));
addr
} else {
let addr = allocator
.allocate_mmio_hole_addresses(None, CAPABILITY_BAR_SIZE, None)
.ok_or(PciDeviceError::IoAllocationFailed(CAPABILITY_BAR_SIZE))?;
ranges.push((
addr,
CAPABILITY_BAR_SIZE,
PciBarRegionType::Memory32BitRegion,
));
addr
};
let config = PciBarConfiguration::default()
.set_register_index(0)
.set_address(virtio_pci_bar_addr.raw_value())
@ -543,12 +573,6 @@ impl PciDevice for VirtioPciDevice {
PciDeviceError::IoRegistrationFailed(virtio_pci_bar_addr.raw_value(), e)
})? as u8;
ranges.push((
virtio_pci_bar_addr,
CAPABILITY_BAR_SIZE,
PciBarRegionType::Memory64BitRegion,
));
// Once the BARs are allocated, the capabilities can be added to the PCI configuration.
self.add_pci_capabilities(virtio_pci_bar)?;