mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-02-22 11:22:26 +00:00
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:
parent
ae6f27277b
commit
e1822cfdad
@ -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(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user