• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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