Skip to main content

ostd/mm/dma/
dma_coherent.rs

1// SPDX-License-Identifier: MPL-2.0
2use 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
37/// A coherent (or consistent) DMA mapping,
38/// which guarantees that the device and the CPU can
39/// access the data in parallel.
40///
41/// The mapping will be destroyed automatically when
42/// the object is dropped.
43pub struct DmaCoherent<M: AnyUFrameMeta + ?Sized> {
44    pub inner: RwArc<DmaCoherentInnerAtomic<M>>,
45}
46
47/// The tracked owner for the inner part of a [`DmaCoherent`].
48///
49/// This mirrors [`DmaStreamVmIoOwner`](super::dma_stream::DmaStreamVmIoOwner):
50/// the real memory ownership lives in the inner `RwArc`, and this token is
51/// only the public `VmIo` permission parameter.
52pub struct DmaCoherentVmIoOwner<M: ?Sized>(pub PhantomData<M>);
53
54/// The inner part of a [`DmaCoherent`].
55pub struct DmaCoherentInner<M: AnyUFrameMeta + ?Sized> {
56    /// Verus does not support trait objects here, so the metadata type is
57    /// represented explicitly by `M`.
58    pub segment: Segment<M>,
59    pub start_daddr: Daddr,
60    /// TODO: restore cache-policy switching once the page-table API is verified.
61    #[expect(unused)]
62    pub is_cache_coherent: bool,
63}
64
65/// The owner of the inner part of a [`DmaCoherent`].
66pub tracked struct DmaCoherentInnerOwner<M: AnyUFrameMeta + ?Sized> {
67    pub _marker: PhantomData<M>,
68}
69
70// This owner contains only PhantomData and carries no subjective memory permissions.
71#[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    /// Creates a coherent DMA mapping backed by `segment`.
116    ///
117    /// The `is_cache_coherent` argument specifies whether the target device can
118    /// access main memory in a CPU-cache-coherent way.
119    #[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        }/*
132        // Original cache policy change (removed during Verus migration):
133        if !is_cache_coherent {
134            let page_table = KERNEL_PAGE_TABLE.get().unwrap();
135            let vaddr = paddr_to_vaddr(start_paddr);
136            let va_range = vaddr..vaddr + (frame_count * PAGE_SIZE);
137            unsafe {
138                page_table
139                    .protect_flush_tlb(&va_range, |p| p.cache = CachePolicy::Uncacheable)
140                    .unwrap();
141            }
142        }
143        */
144
145        let start_daddr = match dma_type() {
146            DmaType::Direct => {
147                /*
148                // Original TDX unprotect (removed during Verus migration):
149                #[cfg(target_arch = "x86_64")]
150                crate::arch::if_tdx_enabled!({
151                    unsafe {
152                        tdx_guest::unprotect_gpa_range(start_paddr, frame_count).unwrap();
153                    }
154                });
155                */
156                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                    /*
174                    // Original IOMMU map (removed during Verus migration):
175                    unsafe {
176                        iommu::map(paddr as Daddr, paddr).unwrap();
177                    }
178                    */
179                    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    /// Returns the starting device address from a read guard.
200    #[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    /// Returns the starting physical address from a read guard.
212    #[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    /// Returns the number of frames from a read guard.
224    #[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    /// Returns the number of bytes from a read guard.
236    #[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    /// Returns a reader to read data from it using a read guard.
248    #[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    /// Returns a writer to write data to it using a read guard.
300    #[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    /// Acquires a read guard for the inner DMA-coherent state.
353    #[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    /// Returns the starting device address.
368    #[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    /// Returns the starting physical address.
381    #[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    /// Returns the number of frames.
394    #[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    /// Returns the number of bytes in the DMA mapping.
407    #[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    /// Returns a reader to read data from it.
420    #[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    /// Returns a writer to write data into it.
450    #[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
505/*
506// Original Deref impl (removed during Verus migration):
507impl Deref for DmaCoherent {
508    type Target = USegment;
509    fn deref(&self) -> &Self::Target {
510        &self.inner.segment
511    }
512}
513*/
514
515/*
516// Original Drop impl for DmaCoherentInner (removed during Verus migration):
517impl Drop for DmaCoherentInner {
518    fn drop(&mut self) {
519        let start_paddr = self.segment.start_paddr();
520        let frame_count = self.segment.size() / PAGE_SIZE;
521
522        match dma_type() {
523            DmaType::Direct => {
524                #[cfg(target_arch = "x86_64")]
525                crate::arch::if_tdx_enabled!({
526                    unsafe {
527                        tdx_guest::protect_gpa_range(start_paddr, frame_count).unwrap();
528                    }
529                });
530            }
531            DmaType::Iommu => {
532                for i in 0..frame_count {
533                    let paddr = start_paddr + (i * PAGE_SIZE);
534                    iommu::unmap(paddr as Daddr).unwrap();
535                }
536            }
537        }
538
539        if !self.is_cache_coherent {
540            let page_table = KERNEL_PAGE_TABLE.get().unwrap();
541            let vaddr = paddr_to_vaddr(start_paddr);
542            let va_range = vaddr..vaddr + (frame_count * PAGE_SIZE);
543            unsafe {
544                page_table
545                    .protect_flush_tlb(&va_range, |p| p.cache = CachePolicy::Writeback)
546                    .unwrap();
547            }
548        }
549
550        remove_dma_mapping(start_paddr, frame_count);
551    }
552}
553*/
554
555impl<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} // verus!