Skip to main content

ostd/mm/dma/
dma_coherent.rs

1// SPDX-License-Identifier: MPL-2.0
2use 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
32/// A coherent (or consistent) DMA mapping,
33/// which guarantees that the device and the CPU can
34/// access the data in parallel.
35///
36/// The mapping will be destroyed automatically when
37/// the object is dropped.
38pub struct DmaCoherent<M: AnyUFrameMeta + ?Sized> {
39    pub inner: RwArc<DmaCoherentInnerAtomic<M>>,
40}
41
42/// The tracked owner for the inner part of a [`DmaCoherent`].
43///
44/// This mirrors [`DmaStreamVmIoOwner`](super::dma_stream::DmaStreamVmIoOwner):
45/// the real memory ownership lives in the inner `RwArc`, and this token is
46/// only the public `VmIo` permission parameter.
47pub struct DmaCoherentVmIoOwner<M: ?Sized>(pub PhantomData<M>);
48
49/// The inner part of a [`DmaCoherent`].
50pub struct DmaCoherentInner<M: AnyUFrameMeta + ?Sized> {
51    /// Verus does not support trait objects here, so the metadata type is
52    /// represented explicitly by `M`.
53    pub segment: Segment<M>,
54    pub start_daddr: Daddr,
55    /// TODO: restore cache-policy switching once the page-table API is verified.
56    #[expect(unused)]
57    pub is_cache_coherent: bool,
58}
59
60/// The owner of the inner part of a [`DmaCoherent`].
61pub 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    /// Creates a coherent DMA mapping backed by `segment`.
92    ///
93    /// The `is_cache_coherent` argument specifies whether the target device can
94    /// access main memory in a CPU-cache-coherent way.
95    #[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        }/*
108        // Original cache policy change (removed during Verus migration):
109        if !is_cache_coherent {
110            let page_table = KERNEL_PAGE_TABLE.get().unwrap();
111            let vaddr = paddr_to_vaddr(start_paddr);
112            let va_range = vaddr..vaddr + (frame_count * PAGE_SIZE);
113            unsafe {
114                page_table
115                    .protect_flush_tlb(&va_range, |p| p.cache = CachePolicy::Uncacheable)
116                    .unwrap();
117            }
118        }
119        */
120
121        let start_daddr = match dma_type() {
122            DmaType::Direct => {
123                /*
124                // Original TDX unprotect (removed during Verus migration):
125                #[cfg(target_arch = "x86_64")]
126                crate::arch::if_tdx_enabled!({
127                    unsafe {
128                        tdx_guest::unprotect_gpa_range(start_paddr, frame_count).unwrap();
129                    }
130                });
131                */
132                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                    /*
150                    // Original IOMMU map (removed during Verus migration):
151                    unsafe {
152                        iommu::map(paddr as Daddr, paddr).unwrap();
153                    }
154                    */
155                    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    /// Returns the starting device address from a read guard.
175    #[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    /// Returns the starting physical address from a read guard.
187    #[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    /// Returns the number of frames from a read guard.
199    #[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    /// Returns the number of bytes from a read guard.
211    #[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    /// Returns a reader to read data from it using a read guard.
223    #[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    /// Returns a writer to write data to it using a read guard.
275    #[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    /// Acquires a read guard for the inner DMA-coherent state.
328    #[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    /// Returns the starting device address.
343    #[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    /// Returns the starting physical address.
356    #[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    /// Returns the number of frames.
369    #[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    /// Returns the number of bytes in the DMA mapping.
382    #[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    /// Returns a reader to read data from it.
395    #[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    /// Returns a writer to write data into it.
425    #[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
488/*
489// Original Deref impl (removed during Verus migration):
490impl Deref for DmaCoherent {
491    type Target = USegment;
492    fn deref(&self) -> &Self::Target {
493        &self.inner.segment
494    }
495}
496*/
497
498/*
499// Original Drop impl for DmaCoherentInner (removed during Verus migration):
500impl Drop for DmaCoherentInner {
501    fn drop(&mut self) {
502        let start_paddr = self.segment.start_paddr();
503        let frame_count = self.segment.size() / PAGE_SIZE;
504
505        match dma_type() {
506            DmaType::Direct => {
507                #[cfg(target_arch = "x86_64")]
508                crate::arch::if_tdx_enabled!({
509                    unsafe {
510                        tdx_guest::protect_gpa_range(start_paddr, frame_count).unwrap();
511                    }
512                });
513            }
514            DmaType::Iommu => {
515                for i in 0..frame_count {
516                    let paddr = start_paddr + (i * PAGE_SIZE);
517                    iommu::unmap(paddr as Daddr).unwrap();
518                }
519            }
520        }
521
522        if !self.is_cache_coherent {
523            let page_table = KERNEL_PAGE_TABLE.get().unwrap();
524            let vaddr = paddr_to_vaddr(start_paddr);
525            let va_range = vaddr..vaddr + (frame_count * PAGE_SIZE);
526            unsafe {
527                page_table
528                    .protect_flush_tlb(&va_range, |p| p.cache = CachePolicy::Writeback)
529                    .unwrap();
530            }
531        }
532
533        remove_dma_mapping(start_paddr, frame_count);
534    }
535}
536*/
537
538impl<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} // verus!