1 // Copyright 2022 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! Utilities to access GuestMemory with IO virtual addresses and iommu
6
7 use std::convert::TryInto;
8 use std::sync::Arc;
9 use sync::Mutex;
10
11 use anyhow::{bail, Context};
12 use data_model::DataInit;
13 use vm_memory::{GuestAddress, GuestMemory};
14
15 use crate::virtio::iommu::IpcMemoryMapper;
16 use crate::virtio::memory_mapper::{Permission, Translate};
17
18 /// A wrapper that works with gpa, or iova and an iommu.
is_valid_wrapper<T: Translate>( mem: &GuestMemory, iommu: &Option<T>, addr: GuestAddress, size: u64, ) -> anyhow::Result<bool>19 pub fn is_valid_wrapper<T: Translate>(
20 mem: &GuestMemory,
21 iommu: &Option<T>,
22 addr: GuestAddress,
23 size: u64,
24 ) -> anyhow::Result<bool> {
25 if let Some(iommu) = iommu {
26 is_valid(mem, iommu, addr.offset(), size)
27 } else {
28 Ok(addr
29 .checked_add(size as u64)
30 .map_or(false, |v| mem.address_in_range(v)))
31 }
32 }
33
34 /// Translates `iova` into gpa regions (or 1 gpa region when it is contiguous), and check if the
35 /// gpa regions are all valid in `mem`.
is_valid<T: Translate>( mem: &GuestMemory, iommu: &T, iova: u64, size: u64, ) -> anyhow::Result<bool>36 pub fn is_valid<T: Translate>(
37 mem: &GuestMemory,
38 iommu: &T,
39 iova: u64,
40 size: u64,
41 ) -> anyhow::Result<bool> {
42 match iommu.translate(iova, size) {
43 Ok(regions) => {
44 for r in regions {
45 if !mem.address_in_range(r.gpa) || mem.checked_offset(r.gpa, r.len).is_none() {
46 return Ok(false);
47 }
48 }
49 }
50 Err(e) => bail!("failed to translate iova to gpa: {}", e),
51 }
52 Ok(true)
53 }
54
55 /// A wrapper that works with gpa, or iova and an iommu.
read_obj_from_addr_wrapper<T: DataInit>( mem: &GuestMemory, iommu: &Option<Arc<Mutex<IpcMemoryMapper>>>, addr: GuestAddress, ) -> anyhow::Result<T>56 pub fn read_obj_from_addr_wrapper<T: DataInit>(
57 mem: &GuestMemory,
58 iommu: &Option<Arc<Mutex<IpcMemoryMapper>>>,
59 addr: GuestAddress,
60 ) -> anyhow::Result<T> {
61 if let Some(iommu) = iommu {
62 read_obj_from_addr::<T>(mem, &iommu.lock(), addr.offset())
63 } else {
64 mem.read_obj_from_addr::<T>(addr)
65 .context("read_obj_from_addr failed")
66 }
67 }
68
69 /// A version of `GuestMemory::read_obj_from_addr` that works with iova and iommu.
read_obj_from_addr<T: DataInit>( mem: &GuestMemory, iommu: &IpcMemoryMapper, iova: u64, ) -> anyhow::Result<T>70 pub fn read_obj_from_addr<T: DataInit>(
71 mem: &GuestMemory,
72 iommu: &IpcMemoryMapper,
73 iova: u64,
74 ) -> anyhow::Result<T> {
75 let regions = iommu
76 .translate(
77 iova,
78 std::mem::size_of::<T>()
79 .try_into()
80 .context("u64 doesn't fit in usize")?,
81 )
82 .context("failed to translate iova to gpa")?;
83 let mut buf = vec![0u8; std::mem::size_of::<T>()];
84 let mut addr: usize = 0;
85 for r in regions {
86 if (r.perm as u8 & Permission::Read as u8) == 0 {
87 bail!("gpa is not readable");
88 }
89 mem.read_at_addr(&mut buf[addr..(addr + r.len as usize)], r.gpa)
90 .context("failed to read from gpa")?;
91 addr += r.len as usize;
92 }
93 Ok(*T::from_slice(&buf).context("failed to construct obj")?)
94 }
95
96 /// A wrapper that works with gpa, or iova and an iommu.
write_obj_at_addr_wrapper<T: DataInit>( mem: &GuestMemory, iommu: &Option<Arc<Mutex<IpcMemoryMapper>>>, val: T, addr: GuestAddress, ) -> anyhow::Result<()>97 pub fn write_obj_at_addr_wrapper<T: DataInit>(
98 mem: &GuestMemory,
99 iommu: &Option<Arc<Mutex<IpcMemoryMapper>>>,
100 val: T,
101 addr: GuestAddress,
102 ) -> anyhow::Result<()> {
103 if let Some(iommu) = iommu {
104 write_obj_at_addr(mem, &iommu.lock(), val, addr.offset())
105 } else {
106 mem.write_obj_at_addr(val, addr)
107 .context("write_obj_at_addr failed")
108 }
109 }
110
111 /// A version of `GuestMemory::write_obj_at_addr` that works with iova and iommu.
write_obj_at_addr<T: DataInit>( mem: &GuestMemory, iommu: &IpcMemoryMapper, val: T, iova: u64, ) -> anyhow::Result<()>112 pub fn write_obj_at_addr<T: DataInit>(
113 mem: &GuestMemory,
114 iommu: &IpcMemoryMapper,
115 val: T,
116 iova: u64,
117 ) -> anyhow::Result<()> {
118 let regions = iommu
119 .translate(
120 iova,
121 std::mem::size_of::<T>()
122 .try_into()
123 .context("u64 doesn't fit in usize")?,
124 )
125 .context("failed to translate iova to gpa")?;
126 let buf = val.as_slice();
127 let mut addr: usize = 0;
128 for r in regions {
129 if (r.perm as u8 & Permission::Read as u8) == 0 {
130 bail!("gpa is not writable");
131 }
132 mem.write_at_addr(&buf[addr..(addr + (r.len as usize))], r.gpa)
133 .context("failed to write to gpa")?;
134 addr += r.len as usize;
135 }
136 Ok(())
137 }
138