1#![warn(missing_docs)]
6
7use anyhow::{Error, anyhow};
10use std::collections::BTreeMap;
11use std::ops::{Deref, Range};
12use std::sync::Arc;
13use zerocopy::{FromBytes, Immutable, IntoBytes};
14use zx::sys::{zx_handle_t, zx_paddr_t};
15
16mod ffi;
17
18pub struct FakeBtiPinnedVmos {
20 map: BTreeMap<usize, (Arc<zx::Vmo>, Range<u64>)>,
21}
22
23impl FakeBtiPinnedVmos {
24 pub fn find_phys(&self, physical_address: usize) -> Option<(Arc<zx::Vmo>, Range<u64>)> {
29 let (&start_paddr, (vmo, offset_range)) =
30 self.map.range(..=physical_address).next_back()?;
31 let diff = (physical_address - start_paddr) as u64;
32 let len = offset_range.end - offset_range.start;
33 if diff < len {
34 Some((vmo.clone(), (offset_range.start + diff)..offset_range.end))
35 } else {
36 None
37 }
38 }
39
40 pub fn read_bytes(&self, mut phys_addr: usize, len: usize) -> Result<Vec<u8>, Error> {
44 let mut buf = vec![0u8; len];
45 let mut read_offset = 0;
46
47 while read_offset < len {
48 let (vmo, range) = self
49 .find_phys(phys_addr)
50 .ok_or_else(|| anyhow!("Physical address {phys_addr:#x} not pinned"))?;
51 let available = (range.end - range.start) as usize;
52 let to_read = std::cmp::min(available, len - read_offset);
53 vmo.read(&mut buf[read_offset..read_offset + to_read], range.start)?;
54 read_offset += to_read;
55 phys_addr += to_read;
56 }
57
58 Ok(buf)
59 }
60
61 pub fn read<T: FromBytes>(&self, phys_addr: usize) -> Result<T, Error> {
63 Ok(T::read_from_bytes(&self.read_bytes(phys_addr, std::mem::size_of::<T>())?).unwrap())
64 }
65
66 pub fn write_bytes(&self, mut phys_addr: usize, mut data: &[u8]) -> Result<(), Error> {
70 while !data.is_empty() {
71 let (vmo, range) = self
72 .find_phys(phys_addr)
73 .ok_or_else(|| anyhow!("Physical address {phys_addr:#x} not pinned"))?;
74 let amount = std::cmp::min(data.len(), (range.end - range.start) as usize);
75 vmo.write(data.split_off(..amount).unwrap(), range.start)?;
76 phys_addr += amount;
77 }
78 Ok(())
79 }
80
81 pub fn write<T: IntoBytes + Immutable>(&self, phys_addr: usize, value: T) -> Result<(), Error> {
83 self.write_bytes(phys_addr, value.as_bytes())
84 }
85}
86
87#[derive(Debug)]
89pub struct FakeBti(zx::Bti);
90
91impl Deref for FakeBti {
92 type Target = zx::Bti;
93
94 fn deref(&self) -> &Self::Target {
95 &self.0
96 }
97}
98
99impl FakeBti {
100 #[inline(always)]
102 pub fn phys_addr() -> usize {
103 unsafe { ffi::g_fake_bti_phys_addr }
105 }
106
107 pub fn create() -> Result<Self, zx::Status> {
109 let handle = {
110 let mut raw_handle = zx_handle_t::default();
111 unsafe {
113 zx::ok(ffi::fake_bti_create(&mut raw_handle))?;
114 }
115 unsafe { zx::NullableHandle::from_raw(raw_handle) }
117 };
118 Ok(Self(handle.into()))
119 }
120
121 pub fn set_paddrs(&self, paddrs: &[zx_paddr_t]) {
125 unsafe {
127 zx::ok(ffi::fake_bti_set_paddrs(self.0.raw_handle(), paddrs.as_ptr(), paddrs.len()))
129 .unwrap();
130 }
131 }
132
133 pub fn get_pinned_vmo(&self) -> FakeBtiPinnedVmos {
135 let mut actual = 64;
136 let vmo_infos = loop {
137 let mut vmo_infos = Vec::with_capacity(actual);
138 unsafe {
140 zx::ok(ffi::fake_bti_get_pinned_vmo(
141 self.0.raw_handle(),
142 vmo_infos.as_mut_ptr(),
143 vmo_infos.capacity(),
144 &mut actual as *mut usize,
145 ))
146 .unwrap();
147
148 if actual <= vmo_infos.capacity() {
149 vmo_infos.set_len(actual);
150 break vmo_infos;
151 }
152 vmo_infos.set_len(vmo_infos.capacity());
153 for info in vmo_infos {
155 zx::NullableHandle::from_raw(info.vmo);
156 }
157 actual += 16; }
159 };
160
161 let page_size = zx::system_get_page_size() as usize;
162 let mut map = BTreeMap::new();
163 for info in vmo_infos {
164 let mut actual_paddrs = 64;
165 let paddrs = loop {
166 let mut paddrs = Vec::with_capacity(actual_paddrs);
167 unsafe {
169 match zx::ok(ffi::fake_bti_get_vmo_phys_address(
170 self.0.raw_handle(),
171 &info,
172 paddrs.as_mut_ptr(),
173 paddrs.capacity(),
174 &mut actual_paddrs,
175 )) {
176 Ok(()) => {}
177 Err(zx::Status::NOT_FOUND) => {
178 break vec![];
181 }
182 Err(status) => panic!("Unexpected error: {status:?}"),
183 }
184 if actual_paddrs <= paddrs.capacity() {
185 paddrs.set_len(actual_paddrs);
186 break paddrs;
187 }
188 actual_paddrs += 16; }
190 };
191
192 let vmo: Arc<zx::Vmo> =
194 Arc::new(unsafe { zx::NullableHandle::from_raw(info.vmo).into() });
195
196 let mut paddrs = paddrs.into_iter();
197 if let Some(a) = paddrs.next() {
198 let mut offset = 0;
199 let mut range = a..a + page_size;
200 for a in paddrs {
201 if a == range.end {
202 range.end += page_size;
203 } else {
204 let len = (range.end - range.start) as u64;
205 map.insert(range.start, (vmo.clone(), offset..offset + len));
206 offset += len;
207 range = a..a + page_size;
208 }
209 }
210 let len = (range.end - range.start) as u64;
211 map.insert(range.start, (vmo, offset..offset + len));
212 }
213 }
214
215 FakeBtiPinnedVmos { map }
216 }
217}
218
219impl From<zx::Bti> for FakeBti {
220 fn from(bti: zx::Bti) -> Self {
221 Self(bti)
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[fuchsia::test]
230 fn create() {
231 let bti = FakeBti::create().expect("create failed");
232 let info = bti.info().expect("info failed");
233 assert!(info.minimum_contiguity > 0);
234 }
235
236 #[fuchsia::test]
237 fn pin_vmo() {
238 let bti = FakeBti::create().expect("create failed");
239 let vmo = zx::Vmo::create(16384).expect("create VMO failed");
240 let mut paddrs = [zx_paddr_t::default(); 4];
241 let _pmt = bti
242 .pin(zx::BtiOptions::PERM_READ, &vmo, 0, 16384, &mut paddrs[..])
243 .expect("pin failed");
244
245 let expected = FakeBti::phys_addr();
246 assert_eq!(paddrs, [expected, expected, expected, expected]);
247 }
248
249 #[fuchsia::test]
250 fn create_and_pin_contiguous_vmo() {
251 let bti = FakeBti::create().expect("create failed");
252 let vmo = zx::Vmo::create_contiguous(&bti, 16384, 0).expect("create contiguous VMO failed");
253 let mut paddr = [zx_paddr_t::default()];
254 let _pmt = bti
255 .pin(
256 zx::BtiOptions::PERM_READ | zx::BtiOptions::CONTIGUOUS,
257 &vmo,
258 0,
259 16384,
260 &mut paddr[..],
261 )
262 .expect("pin failed");
263
264 assert_eq!(paddr, [FakeBti::phys_addr()]);
265 }
266
267 #[fuchsia::test]
268 fn pin_with_paddrs() {
269 let bti = FakeBti::create().expect("create failed");
270 bti.set_paddrs(&[4096, 16384, 65536]);
271 let vmo = zx::Vmo::create(3 * 4096).expect("create VMO failed");
272 {
273 let mut paddrs = [zx_paddr_t::default(); 3];
274 let _pmt = bti
275 .pin(zx::BtiOptions::PERM_READ, &vmo, 0, 3 * 4096, &mut paddrs[..])
276 .expect("pin failed");
277 assert_eq!(paddrs, [4096, 16384, 65536]);
278 }
279 bti.set_paddrs(&[4096, 8192]);
280 {
281 let mut paddrs = [zx_paddr_t::default(); 2];
282 let _pmt = bti
283 .pin(zx::BtiOptions::PERM_READ, &vmo, 0, 2 * 4096, &mut paddrs[..])
284 .expect("pin failed");
285 assert_eq!(paddrs, [4096, 8192]);
286 }
287 }
288
289 #[fuchsia::test]
290 fn test_get_pinned_vmo() {
291 let bti = FakeBti::create().expect("create failed");
292 bti.set_paddrs(&[0x1000, 0x2000, 0x3000, 0x4000]);
293
294 let vmo1 = zx::Vmo::create(0x2000).expect("create VMO1 failed");
295 let mut paddrs1 = [zx_paddr_t::default(); 2];
296 let _pmt1 = bti
297 .pin(zx::BtiOptions::PERM_READ, &vmo1, 0, 0x2000, &mut paddrs1[..])
298 .expect("pin1 failed");
299
300 let vmo2 = zx::Vmo::create(0x2000).expect("create VMO2 failed");
301 let mut paddrs2 = [zx_paddr_t::default(); 2];
302 let _pmt2 = bti
303 .pin(zx::BtiOptions::PERM_READ, &vmo2, 0, 0x2000, &mut paddrs2[..])
304 .expect("pin2 failed");
305
306 let pinned = bti.get_pinned_vmo();
307
308 let koid1 = vmo1.koid().unwrap();
309 let koid2 = vmo2.koid().unwrap();
310
311 let (v1, r1) = pinned.find_phys(0x1000).expect("lookup 0x1000 failed");
312 assert_eq!(v1.koid().unwrap(), koid1);
313 assert_eq!(r1, 0..0x2000);
314
315 let (v1_2, r1_2) = pinned.find_phys(0x2000).expect("lookup 0x2000 failed");
316 assert_eq!(v1_2.koid().unwrap(), koid1);
317 assert_eq!(r1_2, 0x1000..0x2000);
318
319 let (v1_3, r1_3) = pinned.find_phys(0x2fff).expect("lookup 0x2fff failed");
320 assert_eq!(v1_3.koid().unwrap(), koid1);
321 assert_eq!(r1_3, 0x1fff..0x2000);
322
323 let (v2, r2) = pinned.find_phys(0x3000).expect("lookup 0x3000 failed");
324 assert_eq!(v2.koid().unwrap(), koid2);
325 assert_eq!(r2, 0..0x2000);
326
327 let (v2_2, r2_2) = pinned.find_phys(0x4000).expect("lookup 0x4000 failed");
328 assert_eq!(v2_2.koid().unwrap(), koid2);
329 assert_eq!(r2_2, 0x1000..0x2000);
330
331 assert!(pinned.find_phys(0x0fff).is_none());
332 assert!(pinned.find_phys(0x5000).is_none());
333 }
334
335 #[fuchsia::test]
336 fn test_read() {
337 let bti = FakeBti::create().expect("create failed");
338
339 {
340 bti.set_paddrs(&[0x1000, 0x2000]); let vmo = zx::Vmo::create(0x2000).expect("create VMO failed");
342 let data = [0x11u8, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
343 vmo.write(&data, 0xffc).expect("write failed");
344
345 let mut paddrs = [0; 2];
346 let _pmt = bti
347 .pin(zx::BtiOptions::PERM_READ, &vmo, 0, 0x2000, &mut paddrs)
348 .expect("pin failed");
349
350 let pinned = bti.get_pinned_vmo();
351
352 let val: u64 = pinned.read(0x1ffc).expect("read failed");
353 assert_eq!(val, 0x8877665544332211);
354 }
355
356 {
358 let vmo = zx::Vmo::create(0x2000).expect("create VMO failed");
359 let data = [0x11u8, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
360 vmo.write(&data, 0xffc).expect("write failed");
361 vmo.write(&data, 0x1000).expect("write failed");
362
363 bti.set_paddrs(&[0x14000, 0x16000]);
365 let mut paddrs2 = [0; 2];
366 let _pmt2 = bti
367 .pin(zx::BtiOptions::PERM_READ, &vmo, 0, 0x2000, &mut paddrs2)
368 .expect("pin failed");
369 assert_eq!(paddrs2, [0x14000, 0x16000]);
370
371 let pinned2 = bti.get_pinned_vmo();
372 assert!(pinned2.read::<u64>(0x14ffc).is_err());
374
375 let val1: u32 = pinned2.read(0x14ffc).expect("read boundary 1 failed");
377 assert_eq!(val1, 0x44332211);
378 let val2: u32 = pinned2.read(0x16000).expect("read boundary 2 failed");
379 assert_eq!(val2, 0x44332211);
380 }
381 }
382}