1use core::marker::PhantomData;
3
4use vstd::{predicate::Predicate, prelude::*};
5
6use vstd_extra::ownership::{Inv, OwnerOf};
7
8use crate::{
9 error::Error,
10 mm::{
11 HasPaddr, Paddr,
12 dma::{Daddr, DmaType, dma_type},
13 frame::{Segment, untyped::AnyUFrameMeta},
14 io::{
15 FallibleVmRead, FallibleVmWrite, Infallible, PodOnce, VmIo, VmIoMemView, VmIoOnce,
16 VmIoOwner, VmReader, VmWriter, axiom_kernel_mem_view,
17 },
18 kspace::{KERNEL_BASE_VADDR, KERNEL_END_VADDR, VMALLOC_BASE_VADDR},
19 paddr_to_vaddr,
20 },
21 specs::{
22 arch::{PAGE_SIZE, lemma_max_paddr_range, lemma_paddr_to_vaddr_properties},
23 mm::virt_mem::VirtPtr,
24 },
25 sync::{AtomicDataWithOwner, PreemptDisabled, RwArc, RwLockReadGuard},
26};
27
28use super::{DmaError, HasDaddr, check_and_insert_dma_mapping, is_valid_daddr};
29
30verus! {
31
32pub struct DmaCoherent<M: AnyUFrameMeta + ?Sized> {
39 pub inner: RwArc<DmaCoherentInnerAtomic<M>>,
40}
41
42pub struct DmaCoherentVmIoOwner<M: ?Sized>(pub PhantomData<M>);
48
49pub struct DmaCoherentInner<M: AnyUFrameMeta + ?Sized> {
51 pub segment: Segment<M>,
54 pub start_daddr: Daddr,
55 #[expect(unused)]
57 pub is_cache_coherent: bool,
58}
59
60pub tracked struct DmaCoherentInnerOwner<M: AnyUFrameMeta + ?Sized> {
62 pub _marker: PhantomData<M>,
63}
64
65pub type DmaCoherentInnerAtomic<M> = AtomicDataWithOwner<
66 DmaCoherentInner<M>,
67 DmaCoherentInnerOwner<M>,
68>;
69
70impl<M: AnyUFrameMeta + ?Sized> Inv for DmaCoherent<M> {
71 open spec fn inv(self) -> bool {
72 true
73 }
74}
75
76impl<M: AnyUFrameMeta + ?Sized> Inv for DmaCoherentInner<M> {
77 open spec fn inv(self) -> bool {
78 &&& self.segment.inv()
79 &&& is_valid_daddr(self.start_daddr)
80 }
81}
82
83impl<M: AnyUFrameMeta + ?Sized> Inv for DmaCoherentInnerOwner<M> {
84 open spec fn inv(self) -> bool {
85 true
86 }
87}
88
89#[verus_verify]
90impl<M: AnyUFrameMeta + ?Sized + OwnerOf> DmaCoherent<M> {
91 #[verus_spec(r =>
96 requires
97 segment.inv(),
98 ensures
99 r matches Ok(r) ==> r.inner.wf(),
100 )]
101 pub fn map(segment: Segment<M>, is_cache_coherent: bool) -> Result<Self, DmaError> {
102 let start_paddr = segment.start_paddr();
103 let frame_count = segment.size() / PAGE_SIZE;
104
105 if !check_and_insert_dma_mapping(start_paddr, frame_count) {
106 return Err(DmaError::AlreadyMapped);
107 }let start_daddr = match dma_type() {
122 DmaType::Direct => {
123 start_paddr as Daddr
133 },
134 DmaType::Iommu => {
135 let mut i = 0;
136
137 #[verus_spec(
138 invariant
139 i <= frame_count,
140 segment.inv(),
141 start_paddr == segment.start_paddr(),
142 frame_count == segment.size() / PAGE_SIZE,
143 decreases
144 frame_count - i,
145 )]
146 while i < frame_count {
147 assume(start_paddr + i * PAGE_SIZE <= usize::MAX);
148 let _paddr = start_paddr + i * PAGE_SIZE;
149 i += 1;
156 }
157
158 start_paddr as Daddr
159 },
160 };
161
162 let tracked inner_owner = DmaCoherentInnerOwner { _marker: PhantomData::<M> };
163
164 let inner = RwArc::new(
165 AtomicDataWithOwner::new(
166 DmaCoherentInner { segment, start_daddr, is_cache_coherent },
167 Tracked(inner_owner),
168 ),
169 );
170
171 Ok(DmaCoherent { inner })
172 }
173
174 #[inline(always)]
176 #[verus_spec(r =>
177 requires
178 this@.inv(),
179 returns
180 this@.data.start_daddr,
181 )]
182 fn daddr_inner(this: &RwLockReadGuard<DmaCoherentInnerAtomic<M>, PreemptDisabled>) -> Daddr {
183 this.start_daddr
184 }
185
186 #[inline(always)]
188 #[verus_spec(r =>
189 requires
190 this@.inv(),
191 returns
192 this@.data.segment.start_paddr(),
193 )]
194 fn paddr_inner(this: &RwLockReadGuard<DmaCoherentInnerAtomic<M>, PreemptDisabled>) -> Paddr {
195 this.segment.start_paddr()
196 }
197
198 #[inline(always)]
200 #[verus_spec(r =>
201 requires
202 this@.inv(),
203 returns
204 this@.data.segment.size() / PAGE_SIZE,
205 )]
206 fn nframes_inner(this: &RwLockReadGuard<DmaCoherentInnerAtomic<M>, PreemptDisabled>) -> usize {
207 this.segment.size() / PAGE_SIZE
208 }
209
210 #[inline(always)]
212 #[verus_spec(r =>
213 requires
214 this@.inv(),
215 returns
216 this@.data.segment.size(),
217 )]
218 fn nbytes_inner(this: &RwLockReadGuard<DmaCoherentInnerAtomic<M>, PreemptDisabled>) -> usize {
219 this.segment.size()
220 }
221
222 #[inline(always)]
224 #[verus_spec(r =>
225 with
226 -> reader_owner: Tracked<VmIoOwner>,
227 requires
228 this@.inv(),
229 ensures
230 r.inv(),
231 reader_owner@.inv(),
232 reader_owner@.is_kernel,
233 reader_owner@.has_read_view(),
234 reader_owner@.read_view_initialized(),
235 r.wf(reader_owner@),
236 KERNEL_BASE_VADDR <= r.cursor.range@.start,
237 r.cursor.range@.end <= KERNEL_END_VADDR,
238 )]
239 fn reader_inner<'a>(
240 this: RwLockReadGuard<'a, DmaCoherentInnerAtomic<M>, PreemptDisabled>,
241 ) -> VmReader<'a, Infallible> {
242 proof_decl! {
243 let ghost id: nat;
244 let tracked mut reader_owner: VmIoOwner;
245 }
246 proof {
247 lemma_max_paddr_range();
248 lemma_paddr_to_vaddr_properties(this@.data.segment.start_paddr());
249 }
250
251 let vaddr = paddr_to_vaddr(this.segment.start_paddr());
252 let len = this.segment.size();
253 let ghost range = vaddr..(vaddr + len) as usize;
254 let ptr = VirtPtr { vaddr, range: Ghost(range) };
255 proof {
256 assert(KERNEL_BASE_VADDR > 0) by (compute_only);
257 assert(vaddr > 0);
258 assert(VMALLOC_BASE_VADDR <= KERNEL_END_VADDR) by (compute_only);
259 assert(ptr.inv());
260 }
261
262 let reader = unsafe {
263 proof_with!(Ghost(id) => Tracked(reader_owner));
264 VmReader::from_kernel_space(ptr, len)
265 };
266 proof {
267 let tracked mem_view = axiom_kernel_mem_view(range);
268 reader_owner.mem_view = Some(VmIoMemView::ReadView(mem_view));
269 }
270 proof_with!(|= Tracked(reader_owner));
271 reader
272 }
273
274 #[inline(always)]
276 #[verus_spec(r =>
277 with
278 -> writer_owner: Tracked<VmIoOwner>,
279 requires
280 this@.inv(),
281 ensures
282 r.inv(),
283 writer_owner@.inv(),
284 writer_owner@.is_kernel,
285 writer_owner@.has_write_view(),
286 r.wf(writer_owner@),
287 KERNEL_BASE_VADDR <= r.cursor.range@.start,
288 r.cursor.range@.end <= KERNEL_END_VADDR,
289 )]
290 fn writer_inner<'a>(
291 this: RwLockReadGuard<'a, DmaCoherentInnerAtomic<M>, PreemptDisabled>,
292 ) -> VmWriter<'a, Infallible> {
293 proof_decl! {
294 let ghost id: nat;
295 let tracked mut writer_owner: VmIoOwner;
296 }
297 proof {
298 lemma_max_paddr_range();
299 lemma_paddr_to_vaddr_properties(this@.data.segment.start_paddr());
300 }
301
302 let vaddr = paddr_to_vaddr(this.segment.start_paddr());
303 let len = this.segment.size();
304 let ghost range = vaddr..(vaddr + len) as usize;
305 let ptr = VirtPtr { vaddr, range: Ghost(range) };
306 proof {
307 assert(KERNEL_BASE_VADDR > 0) by (compute_only);
308 assert(vaddr > 0);
309 assert(VMALLOC_BASE_VADDR <= KERNEL_END_VADDR) by (compute_only);
310 assert(ptr.inv());
311 }
312
313 let writer = unsafe {
314 proof_with!(Ghost(id), Tracked(false) => Tracked(writer_owner));
315 VmWriter::from_kernel_space(ptr, len)
316 };
317 proof {
318 let tracked mem_view = axiom_kernel_mem_view(range);
319 writer_owner.mem_view = Some(VmIoMemView::WriteView(mem_view));
320 }
321 proof_with!(|= Tracked(writer_owner));
322 writer
323 }
324}
325
326impl<M: AnyUFrameMeta + ?Sized> DmaCoherent<M> {
327 #[inline(always)]
329 #[verifier::external_body]
330 #[verus_spec(r =>
331 ensures
332 self.inner.wf(),
333 r@.inv(),
334 )]
335 pub fn read_inner(&self) -> RwLockReadGuard<'_, DmaCoherentInnerAtomic<M>, PreemptDisabled> {
336 self.inner.read()
337 }
338}
339
340#[verus_verify]
341impl<M: AnyUFrameMeta + ?Sized + OwnerOf> DmaCoherent<M> {
342 #[inline(always)]
344 #[verus_spec(r =>
345 requires
346 self.inner.wf(),
347 ensures
348 self.inner.wf(),
349 )]
350 pub fn daddr(&self) -> Daddr {
351 let inner = self.read_inner();
352 Self::daddr_inner(&inner)
353 }
354
355 #[inline(always)]
357 #[verus_spec(r =>
358 requires
359 self.inner.wf(),
360 ensures
361 self.inner.wf(),
362 )]
363 pub fn paddr(&self) -> Paddr {
364 let inner = self.read_inner();
365 Self::paddr_inner(&inner)
366 }
367
368 #[inline(always)]
370 #[verus_spec(r =>
371 requires
372 self.inner.wf(),
373 ensures
374 self.inner.wf(),
375 )]
376 pub fn nframes(&self) -> usize {
377 let inner = self.read_inner();
378 Self::nframes_inner(&inner)
379 }
380
381 #[inline(always)]
383 #[verus_spec(r =>
384 requires
385 self.inner.wf(),
386 ensures
387 self.inner.wf(),
388 )]
389 pub fn nbytes(&self) -> usize {
390 let inner = self.read_inner();
391 Self::nbytes_inner(&inner)
392 }
393
394 #[inline(always)]
396 #[verus_spec(r =>
397 with
398 -> reader_perm: Tracked<VmIoOwner>,
399 requires
400 self.inner.wf(),
401 ensures
402 self.inner.wf(),
403 r.inv(),
404 reader_perm@.inv(),
405 reader_perm@.is_kernel,
406 reader_perm@.has_read_view(),
407 r.wf(reader_perm@),
408 KERNEL_BASE_VADDR <= r.cursor.range@.start,
409 r.cursor.range@.end <= KERNEL_END_VADDR,
410 )]
411 pub fn reader<'a>(&'a self) -> VmReader<'a, Infallible> {
412 let inner = self.read_inner();
413 proof_with!(=> Tracked(reader_perm));
414 let reader = Self::reader_inner(inner);
415 proof_with!(|= Tracked(reader_perm));
416 VmReader {
417 ghost_id: reader.ghost_id,
418 cursor: reader.cursor,
419 end: reader.end,
420 phantom: PhantomData,
421 }
422 }
423
424 #[inline(always)]
426 #[verus_spec(r =>
427 with
428 -> writer_perm: Tracked<VmIoOwner>,
429 requires
430 self.inner.wf(),
431 ensures
432 self.inner.wf(),
433 r.inv(),
434 writer_perm@.inv(),
435 writer_perm@.is_kernel,
436 writer_perm@.has_write_view(),
437 r.wf(writer_perm@),
438 )]
439 pub fn writer<'a>(&'a self) -> VmWriter<'a, Infallible> {
440 let inner = self.read_inner();
441 proof_with!(=> Tracked(writer_perm));
442 let writer = Self::writer_inner(inner);
443 proof_with!(|= Tracked(writer_perm));
444 VmWriter {
445 ghost_id: writer.ghost_id,
446 cursor: writer.cursor,
447 end: writer.end,
448 phantom: PhantomData,
449 }
450 }
451}
452
453impl<M: AnyUFrameMeta + ?Sized> Predicate<DmaCoherentInner<M>> for DmaCoherentInnerOwner<M> {
454 #[verifier::inline]
455 open spec fn predicate(&self, v: DmaCoherentInner<M>) -> bool {
456 &&& self.inv()
457 &&& v.inv()
458 }
459}
460
461#[verus_verify]
462impl<M: AnyUFrameMeta + ?Sized> Clone for DmaCoherent<M> {
463 #[verus_spec(r =>
464 ensures
465 r == self,
466 )]
467 fn clone(&self) -> Self {
468 DmaCoherent { inner: self.inner.clone() }
469 }
470}
471
472impl<M: AnyUFrameMeta + ?Sized + OwnerOf> HasDaddr for DmaCoherent<M> {
473 #[inline]
474 fn daddr(&self) -> Daddr {
475 let inner = self.read_inner();
476 Self::daddr_inner(&inner)
477 }
478}
479
480impl<M: AnyUFrameMeta + ?Sized + OwnerOf> HasPaddr for DmaCoherent<M> {
481 #[inline]
482 fn paddr(&self) -> Paddr {
483 let inner = self.read_inner();
484 Self::paddr_inner(&inner)
485 }
486}
487
488impl<M: AnyUFrameMeta + ?Sized + Send + Sync + OwnerOf> VmIo<
539 DmaCoherentVmIoOwner<M>,
540> for DmaCoherent<M> {
541 closed spec fn obeys_vmio_spec() -> bool {
542 true
543 }
544
545 closed spec fn obeys_vmio_read_requires() -> bool {
546 true
547 }
548
549 closed spec fn obeys_vmio_write_requires() -> bool {
550 true
551 }
552
553 closed spec fn obeys_vmio_read_spec() -> bool {
554 false
555 }
556
557 closed spec fn obeys_vmio_write_spec() -> bool {
558 false
559 }
560
561 open spec fn read_requires(
562 self,
563 offset: usize,
564 writer: VmWriter<'_>,
565 writer_own: VmIoOwner,
566 owner: DmaCoherentVmIoOwner<M>,
567 ) -> bool {
568 &&& self.inv()
569 &&& self.inner.wf()
570 &&& writer.inv()
571 &&& writer_own.inv()
572 &&& writer.wf(writer_own)
573 &&& writer_own.mem_view matches Some(VmIoMemView::WriteView(_))
574 &&& writer.cursor.vaddr > 0
575 &&& (writer.cursor.range@.end <= KERNEL_BASE_VADDR || KERNEL_END_VADDR
576 <= writer.cursor.range@.start)
577 }
578
579 open spec fn write_requires(
580 self,
581 offset: usize,
582 reader: VmReader<'_>,
583 reader_own: VmIoOwner,
584 owner: DmaCoherentVmIoOwner<M>,
585 ) -> bool {
586 &&& self.inv()
587 &&& self.inner.wf()
588 &&& reader.inv()
589 &&& reader_own.inv()
590 &&& reader.wf(reader_own)
591 &&& reader_own.mem_view matches Some(VmIoMemView::ReadView(_))
592 &&& reader_own.read_view_initialized()
593 &&& reader.cursor.vaddr > 0
594 &&& (reader.cursor.range@.end <= KERNEL_BASE_VADDR || KERNEL_END_VADDR
595 <= reader.cursor.range@.start)
596 }
597
598 open spec fn read_spec(
599 self,
600 offset: usize,
601 old_writer: VmWriter<'_>,
602 new_writer: VmWriter<'_>,
603 old_writer_own: VmIoOwner,
604 new_writer_own: VmIoOwner,
605 old_owner: DmaCoherentVmIoOwner<M>,
606 new_owner: DmaCoherentVmIoOwner<M>,
607 r: core::result::Result<(), Error>,
608 ) -> bool {
609 &&& self.inv()
610 &&& new_owner == old_owner
611 &&& new_writer.inv()
612 &&& new_writer_own.inv()
613 &&& new_writer.wf(new_writer_own)
614 &&& match r {
615 Ok(_) => {
616 &&& new_writer.avail_spec() == 0
617 &&& new_writer.cursor.vaddr == old_writer.cursor.vaddr + old_writer.avail_spec()
618 &&& new_writer_own.range.start == old_writer_own.range.start
619 + old_writer.avail_spec()
620 },
621 Err(_) => {
622 &&& new_writer == old_writer
623 &&& new_writer_own == old_writer_own
624 },
625 }
626 }
627
628 open spec fn write_spec(
629 self,
630 offset: usize,
631 old_reader: VmReader<'_>,
632 new_reader: VmReader<'_>,
633 old_reader_own: VmIoOwner,
634 new_reader_own: VmIoOwner,
635 old_owner: DmaCoherentVmIoOwner<M>,
636 new_owner: DmaCoherentVmIoOwner<M>,
637 r: core::result::Result<(), Error>,
638 ) -> bool {
639 &&& self.inv()
640 &&& new_owner == old_owner
641 &&& new_reader.inv()
642 &&& new_reader_own.inv()
643 &&& new_reader.wf(new_reader_own)
644 &&& match r {
645 Ok(_) => {
646 &&& new_reader.remain_spec() == 0
647 &&& new_reader.cursor.vaddr == old_reader.cursor.vaddr + old_reader.remain_spec()
648 &&& new_reader_own.range.start == old_reader_own.range.start
649 + old_reader.remain_spec()
650 },
651 Err(_) => {
652 &&& new_reader == old_reader
653 &&& new_reader_own == old_reader_own
654 },
655 }
656 }
657
658 fn read(
659 &self,
660 offset: usize,
661 writer: &mut VmWriter<'_>,
662 Tracked(writer_own): Tracked<&mut VmIoOwner>,
663 Tracked(owner): Tracked<&mut DmaCoherentVmIoOwner<M>>,
664 ) -> core::result::Result<(), Error> {
665 proof {
666 assert(Self::obeys_vmio_spec());
667 assert(Self::obeys_vmio_read_requires());
668 assert(!Self::obeys_vmio_read_spec());
669 assert(Self::read_requires(*self, offset, *writer, *writer_own, *owner));
670 }
671 proof_decl! {
672 let tracked mut reader_own;
673 }
674 let mut reader = {
675 let inner = self.read_inner();
676 #[verus_spec(with => Tracked(reader_own))]
677 Self::reader_inner(inner)
678 };
679
680 let remain_before_skip = reader.remain();
681 if offset > remain_before_skip {
682 return Err(Error::InvalidArgs);
683 }
684 let remain = remain_before_skip - offset;
685 let len = writer.avail();
686 if remain < len {
687 return Err(Error::InvalidArgs);
688 }
689 if len == 0 {
690 proof {
691 assert(writer.avail_spec() == 0);
692 }
693 return Ok(());
694 }
695 let reader = reader.skip(offset);
696 proof {
697 reader_own.advance(offset);
698 assert(reader_own.inv());
699 assert(writer_own.inv());
700 assert(reader_own.mem_view matches Some(VmIoMemView::ReadView(_)));
701 assert(reader_own.read_view_initialized());
702 assert(writer_own.mem_view matches Some(VmIoMemView::WriteView(_)));
703 assert(reader.remain_spec() >= len);
704 }
705
706 let copied = match reader.read_fallible(writer) {
707 Ok(copied) => copied,
708 Err((err, copied)) => {
709 writer.cursor = writer.cursor.sub(copied);
710 return Err(err);
711 },
712 };
713
714 proof {
715 reader_own.advance(copied);
716 writer_own.advance(copied);
717 assert(*owner == *old(owner));
718 }
719
720 Ok(())
721 }
722
723 fn write(
724 &self,
725 offset: usize,
726 reader: &mut VmReader<'_>,
727 Tracked(reader_own): Tracked<&mut VmIoOwner>,
728 Tracked(owner): Tracked<&mut DmaCoherentVmIoOwner<M>>,
729 ) -> core::result::Result<(), Error> {
730 proof {
731 assert(Self::obeys_vmio_spec());
732 assert(Self::obeys_vmio_write_requires());
733 assert(!Self::obeys_vmio_write_spec());
734 assert(Self::write_requires(*self, offset, *reader, *reader_own, *owner));
735 }
736 proof_decl! {
737 let tracked mut writer_own;
738 }
739 let mut writer = {
740 let inner = self.read_inner();
741 #[verus_spec(with => Tracked(writer_own))]
742 Self::writer_inner(inner)
743 };
744
745 let avail_before_skip = writer.avail();
746 if offset > avail_before_skip {
747 return Err(Error::InvalidArgs);
748 }
749 let avail = avail_before_skip - offset;
750 let len = reader.remain();
751 if avail < len {
752 return Err(Error::InvalidArgs);
753 }
754 if len == 0 {
755 proof {
756 assert(reader.remain_spec() == 0);
757 }
758 return Ok(());
759 }
760 let writer = writer.skip(offset);
761 proof {
762 writer_own.advance(offset);
763
764 assert(writer.avail_spec() >= len);
765 }
766 let copied = match writer.write_fallible(reader) {
767 Ok(copied) => copied,
768 Err((err, copied)) => {
769 reader.cursor = reader.cursor.sub(copied);
770 return Err(err);
771 },
772 };
773 proof {
774 writer_own.advance(copied);
775 reader_own.advance(copied);
776 assert(*owner == *old(owner));
777 }
778
779 Ok(())
780 }
781}
782
783impl<M: AnyUFrameMeta + ?Sized + Send + Sync + OwnerOf> VmIoOnce for DmaCoherent<M> {
784 closed spec fn obeys_vmio_once_read_requires() -> bool {
785 true
786 }
787
788 closed spec fn obeys_vmio_once_write_requires() -> bool {
789 true
790 }
791
792 closed spec fn obeys_vmio_once_read_ensures() -> bool {
793 true
794 }
795
796 closed spec fn obeys_vmio_once_write_ensures() -> bool {
797 true
798 }
799
800 #[verifier::external_body]
801 fn read_once<T: PodOnce>(&self, offset: usize) -> core::result::Result<T, Error> {
802 self.reader().skip(offset).read_once()
803 }
804
805 #[verifier::external_body]
806 fn write_once<T: PodOnce>(&self, offset: usize, new_val: &T) -> core::result::Result<
807 (),
808 Error,
809 > {
810 self.writer().skip(offset).write_once(new_val)
811 }
812}
813
814}