virtio-devices: iommu: Report request error back to guest

Improve the request parsing/handling code by allowing an error status to
be returned back to the guest driver before we return an error
internally.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2022-04-26 11:52:19 +02:00
parent bbd0667b98
commit 6df8f0bbf3

View File

@ -403,10 +403,14 @@ impl Request {
// Create the reply // Create the reply
let mut reply: Vec<u8> = Vec::new(); let mut reply: Vec<u8> = Vec::new();
let mut status = VIRTIO_IOMMU_S_OK;
let mut hdr_len = 0;
let hdr_len = match req_head.type_ { let result = (|| {
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>() {
status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidAttachRequest); return Err(Error::InvalidAttachRequest);
} }
@ -436,11 +440,10 @@ impl Request {
bypass, bypass,
}; };
domains.entry(domain_id).or_insert_with(|| domain); domains.entry(domain_id).or_insert_with(|| domain);
0
} }
VIRTIO_IOMMU_T_DETACH => { VIRTIO_IOMMU_T_DETACH => {
if desc_size_left != size_of::<VirtioIommuReqDetach>() { if desc_size_left != size_of::<VirtioIommuReqDetach>() {
status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidDetachRequest); return Err(Error::InvalidDetachRequest);
} }
@ -471,11 +474,10 @@ impl Request {
{ {
mapping.domains.write().unwrap().remove(&domain_id); mapping.domains.write().unwrap().remove(&domain_id);
} }
0
} }
VIRTIO_IOMMU_T_MAP => { VIRTIO_IOMMU_T_MAP => {
if desc_size_left != size_of::<VirtioIommuReqMap>() { if desc_size_left != size_of::<VirtioIommuReqMap>() {
status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidMapRequest); return Err(Error::InvalidMapRequest);
} }
@ -490,9 +492,11 @@ impl Request {
if let Some(domain) = mapping.domains.read().unwrap().get(&domain_id) { if let Some(domain) = mapping.domains.read().unwrap().get(&domain_id) {
if domain.bypass { if domain.bypass {
status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidMapRequestBypassDomain); return Err(Error::InvalidMapRequestBypassDomain);
} }
} else { } else {
status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidMapRequestMissingDomain); return Err(Error::InvalidMapRequestMissingDomain);
} }
@ -531,11 +535,10 @@ impl Request {
size: req.virt_end - req.virt_start + 1, size: req.virt_end - req.virt_start + 1,
}, },
); );
0
} }
VIRTIO_IOMMU_T_UNMAP => { VIRTIO_IOMMU_T_UNMAP => {
if desc_size_left != size_of::<VirtioIommuReqUnmap>() { if desc_size_left != size_of::<VirtioIommuReqUnmap>() {
status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidUnmapRequest); return Err(Error::InvalidUnmapRequest);
} }
@ -551,9 +554,11 @@ impl Request {
if let Some(domain) = mapping.domains.read().unwrap().get(&domain_id) { if let Some(domain) = mapping.domains.read().unwrap().get(&domain_id) {
if domain.bypass { if domain.bypass {
status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidUnmapRequestBypassDomain); return Err(Error::InvalidUnmapRequestBypassDomain);
} }
} else { } else {
status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidUnmapRequestMissingDomain); return Err(Error::InvalidUnmapRequestMissingDomain);
} }
@ -586,11 +591,10 @@ impl Request {
.unwrap() .unwrap()
.mappings .mappings
.remove(&virt_start); .remove(&virt_start);
0
} }
VIRTIO_IOMMU_T_PROBE => { VIRTIO_IOMMU_T_PROBE => {
if desc_size_left != size_of::<VirtioIommuReqProbe>() { if desc_size_left != size_of::<VirtioIommuReqProbe>() {
status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidProbeRequest); return Err(Error::InvalidProbeRequest);
} }
@ -614,10 +618,15 @@ impl Request {
}; };
reply.extend_from_slice(resv_mem.as_slice()); reply.extend_from_slice(resv_mem.as_slice());
PROBE_PROP_SIZE hdr_len = PROBE_PROP_SIZE;
} }
_ => return Err(Error::InvalidRequest), _ => {
}; status = VIRTIO_IOMMU_S_INVAL;
return Err(Error::InvalidRequest);
}
}
Ok(())
})();
let status_desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?; let status_desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
@ -631,16 +640,21 @@ impl Request {
} }
let tail = VirtioIommuReqTail { let tail = VirtioIommuReqTail {
status: VIRTIO_IOMMU_S_OK, status,
..Default::default() ..Default::default()
}; };
reply.extend_from_slice(tail.as_slice()); reply.extend_from_slice(tail.as_slice());
// Make sure we return the result of the request to the guest before
// we return a potential error internally.
desc_chain desc_chain
.memory() .memory()
.write_slice(reply.as_slice(), status_desc.addr()) .write_slice(reply.as_slice(), status_desc.addr())
.map_err(Error::GuestMemory)?; .map_err(Error::GuestMemory)?;
// Return the error if the result was not Ok().
result?;
Ok((hdr_len as usize) + size_of::<VirtioIommuReqTail>()) Ok((hdr_len as usize) + size_of::<VirtioIommuReqTail>())
} }
} }