1use crate::memory::Memory;
8use crate::region::{MmioRegion, UnsafeMmio};
9use core::ptr::{NonNull, with_exposed_provenance_mut};
10use zx::{CachePolicy, VmarFlags, Vmo};
11use zx_status::Status;
12
13pub struct VmoMapping {
17 map_addr: usize,
18 map_size: usize,
19}
20
21pub type VmoMemory = Memory<VmoMapping>;
23
24impl VmoMapping {
25 pub fn map(offset: usize, size: usize, vmo: Vmo) -> Result<MmioRegion<VmoMemory>, Status> {
33 Self::map_with_cache_policy(offset, size, vmo, CachePolicy::UnCachedDevice)
34 }
35
36 pub fn map_with_cache_policy(
44 offset: usize,
45 size: usize,
46 vmo: Vmo,
47 cache_policy: CachePolicy,
48 ) -> Result<MmioRegion<VmoMemory>, Status> {
49 if size > isize::MAX as usize {
50 return Err(Status::OUT_OF_RANGE);
51 }
52
53 let page_size = zx::system_get_page_size() as usize;
54 let page_offset = offset % page_size;
56
57 let offset = (offset - page_offset) as u64;
59
60 let map_size = (size + page_offset).next_multiple_of(page_size);
62
63 let info = vmo.info()?;
64 if info.cache_policy() != cache_policy {
65 vmo.set_cache_policy(cache_policy)?;
66 }
67 if offset.saturating_add(map_size as u64) > info.size_bytes {
68 return Err(Status::OUT_OF_RANGE);
69 }
70
71 let root_self = fuchsia_runtime::vmar_root_self();
72 let map_addr = root_self.map(
73 0,
74 &vmo,
75 offset,
76 map_size,
77 VmarFlags::PERM_READ | VmarFlags::PERM_WRITE | VmarFlags::MAP_RANGE,
78 )?;
79 let base_ptr =
80 NonNull::<u8>::new(with_exposed_provenance_mut(map_addr + page_offset)).unwrap();
81 let len = size;
82
83 let mapping = Self { map_addr, map_size };
84
85 let memory = unsafe { Memory::new_unchecked(mapping, base_ptr, len) };
91 Ok(MmioRegion::new(memory))
92 }
93}
94
95impl Drop for VmoMapping {
97 fn drop(&mut self) {
98 let root_self = fuchsia_runtime::vmar_root_self();
99 let _ = unsafe { root_self.unmap(self.map_addr, self.map_size) };
104 }
105}
106
107pub trait VmoMapper: UnsafeMmio + Sized {
110 fn map(offset: usize, size: usize, vmo: Vmo) -> Result<MmioRegion<Self>, Status>;
114}
115
116impl VmoMapper for VmoMemory {
117 fn map(offset: usize, size: usize, vmo: Vmo) -> Result<MmioRegion<Self>, Status> {
118 VmoMapping::map(offset, size, vmo)
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::Mmio;
126 use zx::Rights;
127
128 #[test]
129 fn test_mapping() {
130 const TEST_LEN: usize = 256;
131 let vmo = Vmo::create(TEST_LEN as u64).unwrap();
132
133 let mut mmio = VmoMapping::map(0, TEST_LEN, vmo).unwrap().into_split_send();
134
135 for i in 0..TEST_LEN {
136 assert_eq!(mmio.try_store8(i, i as u8), Ok(()));
137 }
138
139 for i in 0..TEST_LEN {
140 assert_eq!(mmio.try_load8(i), Ok(i as u8));
141 }
142 }
143
144 #[test]
145 fn test_page_offset() {
146 const VMO_SIZE: u64 = 1024;
147 let vmo = Vmo::create(VMO_SIZE).unwrap();
148
149 for i in (0..VMO_SIZE).step_by(2) {
151 let addr = i as u16;
152 vmo.write(&addr.to_le_bytes(), i).unwrap();
153 }
154
155 const TEST_OFFSET: usize = 128;
156 const TEST_LEN: usize = 256;
157 let mmio = VmoMapping::map(TEST_OFFSET, TEST_LEN, vmo).unwrap();
158
159 for i in (0..TEST_LEN).step_by(2) {
160 assert_eq!(mmio.try_load16(i), Ok((i + TEST_OFFSET) as u16));
161 }
162 }
163
164 #[test]
165 fn test_mapping_unmaps() {
166 const TEST_LEN: usize = 256;
167 let vmo = Vmo::create(TEST_LEN as u64).unwrap();
168 let vmo_read_handle: Vmo = vmo.duplicate_handle(Rights::READ).unwrap();
169
170 {
171 let _mapping = VmoMapping::map(0, TEST_LEN, vmo).unwrap();
172 assert_eq!(vmo_read_handle.info().unwrap().num_mappings, 1);
174 }
175
176 assert_eq!(vmo_read_handle.info().unwrap().num_mappings, 0);
178 }
179}