vm-virtio: Implement VIRTIO_IOMMU_F_PROBE feature

By implementing this virtio feature, we let the virtio-iommu driver call
the device backend so that it can probe each device that gets attached.

Through this probing, the device provides a range of reserved memory
related to MSI. This is mandatory for x86 architecture as we want to
avoid the default MSI range assigned by the virtio-iommu driver if no
range is provided at all. The default range is 0x8000000-0x80FFFFF but
it only makes sense for ARM architectures.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2020-01-06 16:45:49 +01:00
parent ae6f27277b
commit e1822cfdad

View File

@ -44,6 +44,17 @@ const KILL_EVENT: DeviceEventT = 2;
/// The device should be paused. /// The device should be paused.
const PAUSE_EVENT: DeviceEventT = 3; const PAUSE_EVENT: DeviceEventT = 3;
/// PROBE properties size.
/// This is the minimal size to provide at least one RESV_MEM property.
/// Because virtio-iommu expects one MSI reserved region, we must provide it,
/// otherwise the driver in the guest will define a predefined one between
/// 0x8000000 and 0x80FFFFF, which is only relevant for ARM architecture, but
/// will conflict with x86.
const PROBE_PROP_SIZE: u32 =
(size_of::<VirtioIommuProbeProperty>() + size_of::<VirtioIommuProbeResvMem>()) as u32;
const MSI_IOVA_START: u64 = 0xfee0_0000;
const MSI_IOVA_END: u64 = 0xfeef_ffff;
/// Virtio IOMMU features /// Virtio IOMMU features
#[allow(unused)] #[allow(unused)]
const VIRTIO_IOMMU_F_INPUT_RANGE: u32 = 0; const VIRTIO_IOMMU_F_INPUT_RANGE: u32 = 0;
@ -53,31 +64,47 @@ const VIRTIO_IOMMU_F_DOMAIN_BITS: u32 = 1;
const VIRTIO_IOMMU_F_MAP_UNMAP: u32 = 2; const VIRTIO_IOMMU_F_MAP_UNMAP: u32 = 2;
#[allow(unused)] #[allow(unused)]
const VIRTIO_IOMMU_F_BYPASS: u32 = 3; const VIRTIO_IOMMU_F_BYPASS: u32 = 3;
#[allow(unused)]
const VIRTIO_IOMMU_F_PROBE: u32 = 4; const VIRTIO_IOMMU_F_PROBE: u32 = 4;
// Support 2MiB and 4KiB page sizes. // Support 2MiB and 4KiB page sizes.
#[allow(unused)]
const VIRTIO_IOMMU_PAGE_SIZE_MASK: u64 = (2 << 20) | (4 << 10); const VIRTIO_IOMMU_PAGE_SIZE_MASK: u64 = (2 << 20) | (4 << 10);
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuRange { struct VirtioIommuRange32 {
start: u32,
end: u32,
}
unsafe impl ByteValued for VirtioIommuRange32 {}
#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
struct VirtioIommuRange64 {
start: u64, start: u64,
end: u64, end: u64,
} }
unsafe impl ByteValued for VirtioIommuRange {} unsafe impl ByteValued for VirtioIommuRange64 {}
#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
struct VirtioIommuTopoConfig {
offset: u32,
num_items: u32,
item_length: u32,
}
unsafe impl ByteValued for VirtioIommuTopoConfig {}
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuConfig { struct VirtioIommuConfig {
page_size_mask: u64, page_size_mask: u64,
input_range: VirtioIommuRange, input_range: VirtioIommuRange64,
domain_bits: u8, domain_range: VirtioIommuRange32,
padding: [u8; 3],
probe_size: u32, probe_size: u32,
topo_config: VirtioIommuTopoConfig,
} }
unsafe impl ByteValued for VirtioIommuConfig {} unsafe impl ByteValued for VirtioIommuConfig {}
@ -89,7 +116,6 @@ const VIRTIO_IOMMU_T_MAP: u8 = 3;
const VIRTIO_IOMMU_T_UNMAP: u8 = 4; const VIRTIO_IOMMU_T_UNMAP: u8 = 4;
const VIRTIO_IOMMU_T_PROBE: u8 = 5; const VIRTIO_IOMMU_T_PROBE: u8 = 5;
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuReqHead { struct VirtioIommuReqHead {
@ -116,7 +142,6 @@ const VIRTIO_IOMMU_S_NOENT: u8 = 6;
#[allow(unused)] #[allow(unused)]
const VIRTIO_IOMMU_S_FAULT: u8 = 7; const VIRTIO_IOMMU_S_FAULT: u8 = 7;
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuReqTail { struct VirtioIommuReqTail {
@ -127,7 +152,6 @@ struct VirtioIommuReqTail {
unsafe impl ByteValued for VirtioIommuReqTail {} unsafe impl ByteValued for VirtioIommuReqTail {}
/// ATTACH request /// ATTACH request
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuReqAttach { struct VirtioIommuReqAttach {
@ -139,7 +163,6 @@ struct VirtioIommuReqAttach {
unsafe impl ByteValued for VirtioIommuReqAttach {} unsafe impl ByteValued for VirtioIommuReqAttach {}
/// DETACH request /// DETACH request
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuReqDetach { struct VirtioIommuReqDetach {
@ -161,7 +184,6 @@ const VIRTIO_IOMMU_MAP_F_EXEC: u32 = 1 << 2;
const VIRTIO_IOMMU_MAP_F_MMIO: u32 = 1 << 3; const VIRTIO_IOMMU_MAP_F_MMIO: u32 = 1 << 3;
/// MAP request /// MAP request
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuReqMap { struct VirtioIommuReqMap {
@ -175,7 +197,6 @@ struct VirtioIommuReqMap {
unsafe impl ByteValued for VirtioIommuReqMap {} unsafe impl ByteValued for VirtioIommuReqMap {}
/// UNMAP request /// UNMAP request
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuReqUnmap { struct VirtioIommuReqUnmap {
@ -189,14 +210,12 @@ unsafe impl ByteValued for VirtioIommuReqUnmap {}
/// Virtio IOMMU request PROBE types /// Virtio IOMMU request PROBE types
#[allow(unused)] #[allow(unused)]
const VIRTIO_IOMMU_PROBE_T_MASK: u32 = 0xfff; const VIRTIO_IOMMU_PROBE_T_MASK: u16 = 0xfff;
#[allow(unused)] #[allow(unused)]
const VIRTIO_IOMMU_PROBE_T_NONE: u32 = 0; const VIRTIO_IOMMU_PROBE_T_NONE: u16 = 0;
#[allow(unused)] const VIRTIO_IOMMU_PROBE_T_RESV_MEM: u16 = 1;
const VIRTIO_IOMMU_PROBE_T_RESV_MEM: u32 = 1;
/// PROBE request /// PROBE request
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuReqProbe { struct VirtioIommuReqProbe {
@ -206,7 +225,6 @@ struct VirtioIommuReqProbe {
unsafe impl ByteValued for VirtioIommuReqProbe {} unsafe impl ByteValued for VirtioIommuReqProbe {}
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuProbeProperty { struct VirtioIommuProbeProperty {
@ -218,15 +236,12 @@ unsafe impl ByteValued for VirtioIommuProbeProperty {}
/// Virtio IOMMU request PROBE property RESV_MEM subtypes /// Virtio IOMMU request PROBE property RESV_MEM subtypes
#[allow(unused)] #[allow(unused)]
const VIRTIO_IOMMU_RESV_MEM_T_RESERVED: u32 = 0; const VIRTIO_IOMMU_RESV_MEM_T_RESERVED: u8 = 0;
#[allow(unused)] const VIRTIO_IOMMU_RESV_MEM_T_MSI: u8 = 1;
const VIRTIO_IOMMU_RESV_MEM_T_MSI: u32 = 1;
#[allow(unused)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default)]
#[repr(packed)] #[repr(packed)]
struct VirtioIommuProbeResvMem { struct VirtioIommuProbeResvMem {
head: VirtioIommuProbeProperty,
subtype: u8, subtype: u8,
reserved: [u8; 3], reserved: [u8; 3],
start: u64, start: u64,
@ -320,20 +335,7 @@ impl Display for Error {
} }
} }
#[derive(Debug, PartialEq)] struct Request {}
enum RequestType {
Attach,
Detach,
Map,
Unmap,
Probe,
}
struct Request {
#[allow(unused)]
type_: RequestType,
status_addr: GuestAddress,
}
impl Request { impl Request {
// Parse the available vring buffer. Based on the hashmap table of external // Parse the available vring buffer. Based on the hashmap table of external
@ -350,7 +352,7 @@ impl Request {
mapping: &Arc<IommuMapping>, mapping: &Arc<IommuMapping>,
ext_mapping: &BTreeMap<u32, Arc<dyn ExternalDmaMapping>>, ext_mapping: &BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
ext_domain_mapping: &mut BTreeMap<u32, Arc<dyn ExternalDmaMapping>>, ext_domain_mapping: &mut BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
) -> result::Result<Request, Error> { ) -> result::Result<usize, Error> {
// The head contains the request type which MUST be readable. // The head contains the request type which MUST be readable.
if avail_desc.is_write_only() { if avail_desc.is_write_only() {
return Err(Error::UnexpectedWriteOnlyDescriptor); return Err(Error::UnexpectedWriteOnlyDescriptor);
@ -370,7 +372,10 @@ impl Request {
return Err(Error::InvalidRequest); return Err(Error::InvalidRequest);
}; };
let request_type = match req_head.type_ { // Create the reply
let mut reply: Vec<u8> = Vec::new();
let hdr_len = match req_head.type_ {
VIRTIO_IOMMU_T_ATTACH => { VIRTIO_IOMMU_T_ATTACH => {
if desc_size_left != size_of::<VirtioIommuReqAttach>() { if desc_size_left != size_of::<VirtioIommuReqAttach>() {
return Err(Error::InvalidAttachRequest); return Err(Error::InvalidAttachRequest);
@ -401,7 +406,7 @@ impl Request {
mappings.insert(domain, BTreeMap::new()); mappings.insert(domain, BTreeMap::new());
} }
RequestType::Attach 0
} }
VIRTIO_IOMMU_T_DETACH => { VIRTIO_IOMMU_T_DETACH => {
if desc_size_left != size_of::<VirtioIommuReqDetach>() { if desc_size_left != size_of::<VirtioIommuReqDetach>() {
@ -427,7 +432,7 @@ impl Request {
// Remove endpoint associated with specific domain // Remove endpoint associated with specific domain
mapping.endpoints.write().unwrap().remove(&endpoint); mapping.endpoints.write().unwrap().remove(&endpoint);
RequestType::Detach 0
} }
VIRTIO_IOMMU_T_MAP => { VIRTIO_IOMMU_T_MAP => {
if desc_size_left != size_of::<VirtioIommuReqMap>() { if desc_size_left != size_of::<VirtioIommuReqMap>() {
@ -463,7 +468,7 @@ impl Request {
return Err(Error::InvalidMapRequest); return Err(Error::InvalidMapRequest);
} }
RequestType::Map 0
} }
VIRTIO_IOMMU_T_UNMAP => { VIRTIO_IOMMU_T_UNMAP => {
if desc_size_left != size_of::<VirtioIommuReqUnmap>() { if desc_size_left != size_of::<VirtioIommuReqUnmap>() {
@ -492,7 +497,7 @@ impl Request {
entry.remove(&virt_start); entry.remove(&virt_start);
} }
RequestType::Unmap 0
} }
VIRTIO_IOMMU_T_PROBE => { VIRTIO_IOMMU_T_PROBE => {
if desc_size_left != size_of::<VirtioIommuReqProbe>() { if desc_size_left != size_of::<VirtioIommuReqProbe>() {
@ -504,7 +509,21 @@ impl Request {
.map_err(Error::GuestMemory)?; .map_err(Error::GuestMemory)?;
debug!("Probe request {:?}", req); debug!("Probe request {:?}", req);
RequestType::Probe let probe_prop = VirtioIommuProbeProperty {
type_: VIRTIO_IOMMU_PROBE_T_RESV_MEM,
length: size_of::<VirtioIommuProbeResvMem>() as u16,
};
reply.extend_from_slice(probe_prop.as_slice());
let resv_mem = VirtioIommuProbeResvMem {
subtype: VIRTIO_IOMMU_RESV_MEM_T_MSI,
start: MSI_IOVA_START,
end: MSI_IOVA_END,
..Default::default()
};
reply.extend_from_slice(resv_mem.as_slice());
PROBE_PROP_SIZE
} }
_ => return Err(Error::InvalidRequest), _ => return Err(Error::InvalidRequest),
}; };
@ -518,14 +537,20 @@ impl Request {
return Err(Error::UnexpectedReadOnlyDescriptor); return Err(Error::UnexpectedReadOnlyDescriptor);
} }
if (status_desc.len as usize) < size_of::<VirtioIommuReqTail>() { if status_desc.len < hdr_len + size_of::<VirtioIommuReqTail>() as u32 {
return Err(Error::BufferLengthTooSmall); return Err(Error::BufferLengthTooSmall);
} }
Ok(Request { let tail = VirtioIommuReqTail {
type_: request_type, status: VIRTIO_IOMMU_S_OK,
status_addr: status_desc.addr, ..Default::default()
}) };
reply.extend_from_slice(tail.as_slice());
mem.write_slice(reply.as_slice(), status_desc.addr)
.map_err(Error::GuestMemory)?;
Ok((hdr_len as usize) + size_of::<VirtioIommuReqTail>())
} }
} }
@ -554,22 +579,9 @@ impl IommuEpollHandler {
&self.ext_mapping, &self.ext_mapping,
&mut self.ext_domain_mapping, &mut self.ext_domain_mapping,
) { ) {
Ok(ref req) => { Ok(len) => len as u32,
let reply = VirtioIommuReqTail {
status: VIRTIO_IOMMU_S_OK,
..Default::default()
};
match mem.write_obj(reply, req.status_addr) {
Ok(_) => size_of::<VirtioIommuReqTail>() as u32,
Err(e) => {
error!("bad guest memory address: {}", e);
0
}
}
}
Err(e) => { Err(e) => {
error!("Failed to parse available descriptor chain: {:?}", e); error!("failed parsing descriptor: {}", e);
0 0
} }
}; };
@ -759,6 +771,7 @@ impl Iommu {
pub fn new() -> io::Result<(Self, Arc<IommuMapping>)> { pub fn new() -> io::Result<(Self, Arc<IommuMapping>)> {
let config = VirtioIommuConfig { let config = VirtioIommuConfig {
page_size_mask: VIRTIO_IOMMU_PAGE_SIZE_MASK, page_size_mask: VIRTIO_IOMMU_PAGE_SIZE_MASK,
probe_size: PROBE_PROP_SIZE,
..Default::default() ..Default::default()
}; };
@ -771,7 +784,9 @@ impl Iommu {
Iommu { Iommu {
kill_evt: None, kill_evt: None,
pause_evt: None, pause_evt: None,
avail_features: 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP, avail_features: 1u64 << VIRTIO_F_VERSION_1
| 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP
| 1u64 << VIRTIO_IOMMU_F_PROBE,
acked_features: 0u64, acked_features: 0u64,
config, config,
mapping: mapping.clone(), mapping: mapping.clone(),