1use std::marker::PhantomData;
55use std::ops::Range;
56
57use mmio::MmioExt;
58use mmio::region::{MmioRegion, UnsafeMmio};
59use mmio::vmo::VmoMapper;
60
61use crate::atomic::{AtomicMmioPtr, AtomicOperand};
62use crate::cached_vmo::CachedVmoMemory;
63use crate::operand::MmioOperand;
64
65mod after_store;
66mod dispatch;
67mod offset;
68mod passthrough;
69mod range_override;
70mod registry;
71mod w1c;
72
73pub use after_store::AfterStore;
74pub use dispatch::{
75 DispatchBuilder, DispatchHandler, DispatchNode, DispatchNodeHandler, OpDispatcher,
76 RegisterRange,
77};
78pub use offset::Offset;
79pub use passthrough::Passthrough;
80pub use range_override::{MaybeVmoMemoryHandler, RangeOverride};
81pub use registry::{BaseRegistry, Registry, RegistryHandler, ScopedRegistry, StrictRegistry};
82pub use w1c::WriteOneToClear;
83
84#[cfg(test)]
85use range_override::MockMaybeVmoMemoryHandler;
86
87pub trait VmoMemoryWrapper: VmoMemoryHandler {
89 fn new(vmo: &zx::Vmo) -> Self;
91}
92
93pub trait VmoMemoryHandler {
102 fn load8(&self, op: VmoOpHelper<'_, u8>) -> u8;
104
105 fn load16(&self, op: VmoOpHelper<'_, u16>) -> u16;
107
108 fn load32(&self, op: VmoOpHelper<'_, u32>) -> u32;
110
111 fn load64(&self, op: VmoOpHelper<'_, u64>) -> u64;
113
114 fn store8(&self, op: VmoOpHelper<'_, u8>, value: u8);
116
117 fn store16(&self, op: VmoOpHelper<'_, u16>, value: u16);
119
120 fn store32(&self, op: VmoOpHelper<'_, u32>, value: u32);
122
123 fn store64(&self, op: VmoOpHelper<'_, u64>, value: u64);
125}
126
127pub trait GenericVmoMemoryHandler {
132 fn load<T: MmioOperand>(&self, op: VmoOpHelper<'_, T>) -> T;
134
135 fn store<T: MmioOperand>(&self, op: VmoOpHelper<'_, T>, value: T);
137}
138
139impl<O> VmoMemoryHandler for O
140where
141 O: GenericVmoMemoryHandler,
142{
143 fn load8(&self, op: VmoOpHelper<'_, u8>) -> u8 {
144 self.load(op)
145 }
146
147 fn load16(&self, op: VmoOpHelper<'_, u16>) -> u16 {
148 self.load(op)
149 }
150
151 fn load32(&self, op: VmoOpHelper<'_, u32>) -> u32 {
152 self.load(op)
153 }
154
155 fn load64(&self, op: VmoOpHelper<'_, u64>) -> u64 {
156 self.load(op)
157 }
158
159 fn store8(&self, op: VmoOpHelper<'_, u8>, value: u8) {
160 self.store(op, value)
161 }
162
163 fn store16(&self, op: VmoOpHelper<'_, u16>, value: u16) {
164 self.store(op, value)
165 }
166
167 fn store32(&self, op: VmoOpHelper<'_, u32>, value: u32) {
168 self.store(op, value)
169 }
170
171 fn store64(&self, op: VmoOpHelper<'_, u64>, value: u64) {
172 self.store(op, value)
173 }
174}
175
176pub struct VmoMemory<H> {
187 handler: H,
188 vmo: CachedVmoMemory,
189}
190
191impl<H: VmoMemoryWrapper> VmoMemory<H> {
192 pub fn map(offset: usize, size: usize, vmo: zx::Vmo) -> Result<MmioRegion<Self>, zx::Status> {
195 let handler = H::new(&vmo);
196 let mmio = CachedVmoMemory::map(offset, size, vmo)?;
197
198 Ok(mmio.map(move |vmo| Self { handler, vmo }))
199 }
200}
201
202impl<H> VmoMemory<H> {
203 pub fn new(handler: H, vmo: CachedVmoMemory) -> Self {
205 Self { handler, vmo }
206 }
207
208 fn op_helper<T>(&self, offset: usize) -> VmoOpHelper<'_, T> {
209 VmoOpHelper {
210 vmo_offset: offset,
211 relative_offset: offset,
212 inner: &self.vmo,
213 _marker: PhantomData,
214 }
215 }
216}
217
218impl<H: VmoMemoryHandler> UnsafeMmio for VmoMemory<H> {
219 #[inline]
220 fn len(&self) -> usize {
221 self.vmo.len()
222 }
223
224 #[inline]
225 fn align_offset(&self, align: usize) -> usize {
226 self.vmo.align_offset(align)
227 }
228
229 #[inline]
230 unsafe fn load8_unchecked(&self, offset: usize) -> u8 {
231 self.handler.load8(self.op_helper(offset))
232 }
233
234 #[inline]
235 unsafe fn load16_unchecked(&self, offset: usize) -> u16 {
236 self.handler.load16(self.op_helper(offset))
237 }
238
239 #[inline]
240 unsafe fn load32_unchecked(&self, offset: usize) -> u32 {
241 self.handler.load32(self.op_helper(offset))
242 }
243
244 #[inline]
245 unsafe fn load64_unchecked(&self, offset: usize) -> u64 {
246 self.handler.load64(self.op_helper(offset))
247 }
248
249 #[inline]
250 unsafe fn store8_unchecked(&self, offset: usize, v: u8) {
251 self.handler.store8(self.op_helper(offset), v)
252 }
253
254 #[inline]
255 unsafe fn store16_unchecked(&self, offset: usize, v: u16) {
256 self.handler.store16(self.op_helper(offset), v)
257 }
258
259 #[inline]
260 unsafe fn store32_unchecked(&self, offset: usize, v: u32) {
261 self.handler.store32(self.op_helper(offset), v)
262 }
263
264 #[inline]
265 unsafe fn store64_unchecked(&self, offset: usize, v: u64) {
266 self.handler.store64(self.op_helper(offset), v)
267 }
268
269 #[inline]
270 fn write_barrier(&self) {
271 self.vmo.write_barrier();
272 }
273
274 #[inline]
275 fn read_barrier(&self) {
276 self.vmo.read_barrier();
277 }
278}
279
280impl<H> AtomicMmioPtr for VmoMemory<H> {
281 fn ptr<T: AtomicOperand>(&self, offset: usize) -> std::ptr::NonNull<T> {
282 self.vmo.ptr(offset)
283 }
284}
285
286impl<H: VmoMemoryWrapper> VmoMapper for VmoMemory<H> {
287 fn map(offset: usize, size: usize, vmo: zx::Vmo) -> Result<MmioRegion<Self>, zx::Status> {
288 Self::map(offset, size, vmo)
289 }
290}
291
292pub struct VmoOpHelper<'a, T> {
294 vmo_offset: usize,
295 relative_offset: usize,
296 inner: &'a CachedVmoMemory,
297 _marker: PhantomData<T>,
298}
299
300impl<'a, T> VmoOpHelper<'a, T> {
301 pub fn offset(&self) -> usize {
307 self.relative_offset
308 }
309
310 pub fn range(&self) -> Range<usize> {
314 self.relative_offset..(self.relative_offset + std::mem::size_of::<T>())
315 }
316
317 pub fn vmo_offset(&self) -> usize {
322 self.vmo_offset
323 }
324
325 pub fn vmo_range(&self) -> Range<usize> {
330 self.vmo_offset..(self.vmo_offset + std::mem::size_of::<T>())
331 }
332
333 pub fn unsafe_mmio(&self) -> &'a impl UnsafeMmio {
335 self.inner
336 }
337
338 pub fn apply_offset(self, offset: usize) -> Self {
349 let Some(relative_offset) = self.relative_offset.checked_sub(offset) else {
350 panic!("offset {offset} too big for current offset {}", self.relative_offset);
351 };
352 Self { relative_offset, ..self }
353 }
354
355 fn borrow_region(&self) -> MmioRegion<CachedVmoMemory, &'a CachedVmoMemory> {
356 unsafe { MmioRegion::new_unchecked(self.inner, self.vmo_range()) }
363 }
364}
365
366impl<'a, T: MmioOperand> VmoOpHelper<'a, T> {
367 pub fn load(&self) -> T {
369 self.borrow_region().load(0)
370 }
371
372 pub fn store(&self, value: T) {
374 self.borrow_region().store(0, value)
375 }
376}
377
378#[cfg(test)]
383mod mock {
384 #![allow(clippy::extra_unused_lifetimes)]
385
386 use super::*;
387 mockall::mock! {
388 pub VmoMemoryHandler {}
389
390 impl VmoMemoryHandler for VmoMemoryHandler {
391 fn load8<'a>(&self, op: VmoOpHelper<'a, u8>) -> u8;
392 fn load16<'a>(&self, op: VmoOpHelper<'a, u16>) -> u16;
393 fn load32<'a>(&self, op: VmoOpHelper<'a, u32>) -> u32;
394 fn load64<'a>(&self, op: VmoOpHelper<'a, u64>) -> u64;
395 fn store8<'a>(&self, op: VmoOpHelper<'a, u8>, value: u8);
396 fn store16<'a>(&self, op: VmoOpHelper<'a, u16>, value: u16);
397 fn store32<'a>(&self, op: VmoOpHelper<'a, u32>, value: u32);
398 fn store64<'a>(&self, op: VmoOpHelper<'a, u64>, value: u64);
399 }
400 }
401}
402#[cfg(test)]
403pub use mock::*;
404
405#[cfg(test)]
406mod tests {
407 use super::*;
408 use mmio::Mmio as _;
409
410 const VMO_SIZE: usize = 4096;
411
412 #[test]
413 fn test_load8() {
414 let mut mock = MockVmoMemoryHandler::new();
415 let expected_val = 0xab_u8;
416 let _ = mock
417 .expect_load8()
418 .once()
419 .withf(|op| op.offset() == 0 && op.range() == (0..1))
420 .return_const(expected_val);
421
422 let mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
423 .expect("Failed to map VMO.")
424 .map(|vmo| VmoMemory::new(mock, vmo));
425
426 assert_eq!(mmio.load::<u8>(0), expected_val);
427 }
428
429 #[test]
430 fn test_load16() {
431 let mut mock = MockVmoMemoryHandler::new();
432 let expected_val = 0x1234_u16;
433 let _ = mock
434 .expect_load16()
435 .once()
436 .withf(|op| op.offset() == 2 && op.range() == (2..4))
437 .return_const(expected_val);
438
439 let mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
440 .expect("Failed to map VMO.")
441 .map(|vmo| VmoMemory::new(mock, vmo));
442
443 assert_eq!(mmio.load::<u16>(2), expected_val);
444 }
445
446 #[test]
447 fn test_load32() {
448 let mut mock = MockVmoMemoryHandler::new();
449 let expected_val = 0x5678_9abc_u32;
450 let _ = mock
451 .expect_load32()
452 .once()
453 .withf(|op| op.offset() == 4 && op.range() == (4..8))
454 .return_const(expected_val);
455
456 let mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
457 .expect("Failed to map VMO.")
458 .map(|vmo| VmoMemory::new(mock, vmo));
459
460 assert_eq!(mmio.load::<u32>(4), expected_val);
461 }
462
463 #[test]
464 fn test_load64() {
465 let mut mock = MockVmoMemoryHandler::new();
466 let expected_val = 0xdef0_1234_5678_9abc_u64;
467 let _ = mock
468 .expect_load64()
469 .once()
470 .withf(|op| op.offset() == 8 && op.range() == (8..16))
471 .return_const(expected_val);
472
473 let mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
474 .expect("Failed to map VMO.")
475 .map(|vmo| VmoMemory::new(mock, vmo));
476
477 assert_eq!(mmio.load::<u64>(8), expected_val);
478 }
479
480 #[test]
481 fn test_store8() {
482 let mut mock = MockVmoMemoryHandler::new();
483 let val_to_store = 0xab_u8;
484 let _ = mock
485 .expect_store8()
486 .once()
487 .withf(move |op, val| op.offset() == 0 && op.range() == (0..1) && *val == val_to_store)
488 .return_const(());
489
490 let mut mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
491 .expect("Failed to map VMO.")
492 .map(|vmo| VmoMemory::new(mock, vmo));
493
494 mmio.store::<u8>(0, val_to_store);
495 }
496
497 #[test]
498 fn test_store16() {
499 let mut mock = MockVmoMemoryHandler::new();
500 let val_to_store = 0x1234_u16;
501 let _ = mock
502 .expect_store16()
503 .once()
504 .withf(move |op, val| op.offset() == 2 && op.range() == (2..4) && *val == val_to_store)
505 .return_const(());
506
507 let mut mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
508 .expect("Failed to map VMO.")
509 .map(|vmo| VmoMemory::new(mock, vmo));
510
511 mmio.store::<u16>(2, val_to_store);
512 }
513
514 #[test]
515 fn test_store32() {
516 let mut mock = MockVmoMemoryHandler::new();
517 let val_to_store = 0x5678_9abc_u32;
518 let _ = mock
519 .expect_store32()
520 .once()
521 .withf(move |op, val| op.offset() == 4 && op.range() == (4..8) && *val == val_to_store)
522 .return_const(());
523
524 let mut mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
525 .expect("Failed to map VMO.")
526 .map(|vmo| VmoMemory::new(mock, vmo));
527
528 mmio.store::<u32>(4, val_to_store);
529 }
530
531 #[test]
532 fn test_store64() {
533 let mut mock = MockVmoMemoryHandler::new();
534 let val_to_store = 0xdef0_1234_5678_9abc_u64;
535 let _ = mock
536 .expect_store64()
537 .once()
538 .withf(move |op, val| op.offset() == 8 && op.range() == (8..16) && *val == val_to_store)
539 .return_const(());
540
541 let mut mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
542 .expect("Failed to map VMO.")
543 .map(|vmo| VmoMemory::new(mock, vmo));
544
545 mmio.store::<u64>(8, val_to_store);
546 }
547
548 #[test]
549 fn test_vmo_memory_helpers() {
550 let mock = MockVmoMemoryHandler::new();
551 let mmio = CachedVmoMemory::new_mapped(VMO_SIZE)
552 .expect("Failed to map VMO.")
553 .map(|vmo| VmoMemory::new(mock, vmo));
554
555 assert_eq!(mmio.len(), VMO_SIZE);
556 assert_eq!(mmio.align_offset(8), 0);
557 mmio.write_barrier();
558 mmio.read_barrier();
559 }
560}