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