machina_virtio_device/
mem.rs1use virtio_device::mem::{DeviceRange, DriverMem, DriverRange};
6use virtio_device::queue::QueueMemory;
7use virtio_device::ring;
8
9pub struct GuestMem {
18 mapping: Option<(usize, usize)>,
19}
20
21impl Drop for GuestMem {
22 fn drop(&mut self) {
23 if let Some((base, len)) = self.mapping.take() {
24 unsafe { fuchsia_runtime::vmar_root_self().unmap(base, len) }.unwrap();
26 }
27 }
28}
29
30impl GuestMem {
31 pub fn new() -> GuestMem {
36 GuestMem { mapping: None }
38 }
39
40 pub fn set_vmo(&mut self, vmo: &zx::Vmo) -> Result<(), zx::Status> {
44 if self.mapping.is_some() {
45 return Err(zx::Status::BAD_STATE);
46 }
47 let vmo_size = vmo.get_size()? as usize;
48 let addr = fuchsia_runtime::vmar_root_self().map(
49 0,
50 vmo,
51 0,
52 vmo_size,
53 zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
54 )?;
55 self.mapping = Some((addr, vmo_size));
56 Ok(())
57 }
58
59 pub fn get_mapping(&self) -> Option<(usize, usize)> {
60 self.mapping
61 }
62}
63
64impl DriverMem for GuestMem {
65 fn translate<'a>(&'a self, driver: DriverRange) -> Option<DeviceRange<'a>> {
66 self.mapping.as_ref().and_then(|&(base, len)| {
67 let mem = unsafe { DeviceRange::new(base..(base + len)) };
74 Some(mem.split_at(driver.0.start)?.1.split_at(driver.len())?.0)
76 })
77 }
78}
79
80pub fn guest_mem_from_vmo(vmo: &zx::Vmo) -> Result<GuestMem, zx::Status> {
84 let mut mem = GuestMem::new();
85 mem.set_vmo(vmo)?;
86 Ok(mem)
87}
88
89pub fn translate_queue<'a, M: DriverMem>(
95 mem: &'a M,
96 size: u16,
97 desc: usize,
98 avail: usize,
99 used: usize,
100) -> Option<QueueMemory<'a>> {
101 let desc_len = std::mem::size_of::<ring::Desc>() * size as usize;
102 let avail_len = ring::Driver::avail_len_for_queue_size(size as u16);
103 let used_len = ring::Device::used_len_for_queue_size(size as u16);
104 let desc = mem.translate((desc..desc.checked_add(desc_len)?).into())?;
105 let avail = mem.translate((avail..avail.checked_add(avail_len)?).into())?;
106 let used = mem.translate((used..used.checked_add(used_len)?).into())?;
107 Some(QueueMemory { desc, avail, used })
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_translate() -> Result<(), anyhow::Error> {
116 let size = zx::system_get_page_size() as usize * 1024;
117 let vmo = zx::Vmo::create(size as u64)?;
118 let koid = vmo.info()?.koid;
119 let base;
120
121 let get_maps = || {
122 let process = fuchsia_runtime::process_self();
123 process.info_maps_vec()
124 };
125
126 {
127 let mem = guest_mem_from_vmo(&vmo)?;
128 base = mem.translate(DriverRange(0..1)).unwrap().get().start;
130 assert!(get_maps()?
132 .into_iter()
133 .find(|info| match info.details() {
134 zx::MapDetails::Mapping(map) => {
135 map.vmo_koid == koid && info.base == base && info.size >= size
136 }
137 _ => false,
138 })
139 .is_some());
140
141 assert_eq!(mem.translate(DriverRange(0..64)).unwrap().get(), base..(base + 64));
143 assert_eq!(
144 mem.translate(DriverRange(size - 64..size)).unwrap().get(),
145 (base + size - 64)..(base + size)
146 );
147 assert_eq!(mem.translate(DriverRange(49..80)).unwrap().get(), (base + 49)..(base + 80));
148 assert_eq!(mem.translate(DriverRange(0..size)).unwrap().get(), base..(base + size));
149
150 assert_eq!(mem.translate(DriverRange(0..(size + 1))), None);
152 assert_eq!(mem.translate(DriverRange((size - 64)..(size + 1))), None);
153 assert_eq!(mem.translate(DriverRange((size + 100)..(size + 200))), None);
154 }
155 assert!(get_maps()?
157 .into_iter()
158 .find(|x| x.details().as_mapping().map_or(false, |map| map.vmo_koid == koid))
159 .is_none());
160 Ok(())
161 }
162}