55// Use of this source code is governed by a BSD-style license that can be
66// found in the THIRD-PARTY file.
77
8+ use std:: mem:: size_of;
89use std:: os:: unix:: io:: AsRawFd ;
910use std:: ptr:: null_mut;
1011
11- use kvm_bindings:: kvm_run;
12+ use kvm_bindings:: {
13+ kvm_coalesced_mmio, kvm_coalesced_mmio_ring, kvm_run, KVM_COALESCED_MMIO_PAGE_OFFSET ,
14+ } ;
1215use vmm_sys_util:: errno;
1316
1417/// Wrappers over KVM device ioctls.
@@ -26,6 +29,100 @@ pub mod vm;
2629/// is otherwise a direct mapping to Result.
2730pub type Result < T > = std:: result:: Result < T , errno:: Error > ;
2831
32+ /// A wrapper around the coalesced MMIO ring page.
33+ #[ derive( Debug ) ]
34+ pub ( crate ) struct KvmCoalescedIoRing {
35+ addr : * mut kvm_coalesced_mmio_ring ,
36+ page_size : usize ,
37+ }
38+
39+ impl KvmCoalescedIoRing {
40+ /// Maps the coalesced MMIO ring from the vCPU file descriptor.
41+ pub ( crate ) fn mmap_from_fd < F : AsRawFd > ( fd : & F ) -> Result < Self > {
42+ // SAFETY: We trust the sysconf libc function and we're calling it
43+ // with a correct parameter.
44+ let page_size = match unsafe { libc:: sysconf ( libc:: _SC_PAGESIZE) } {
45+ -1 => return Err ( errno:: Error :: last ( ) ) ,
46+ ps => ps as usize ,
47+ } ;
48+
49+ let offset = KVM_COALESCED_MMIO_PAGE_OFFSET * page_size as u32 ;
50+ // SAFETY: KVM guarantees that there is a page at offset
51+ // KVM_COALESCED_MMIO_PAGE_OFFSET * PAGE_SIZE if the appropriate
52+ // capability is available. If it is not, the call will simply
53+ // fail.
54+ let addr = unsafe {
55+ libc:: mmap (
56+ null_mut ( ) ,
57+ page_size,
58+ libc:: PROT_READ | libc:: PROT_WRITE ,
59+ libc:: MAP_SHARED ,
60+ fd. as_raw_fd ( ) ,
61+ offset. into ( ) ,
62+ )
63+ } ;
64+ if addr == libc:: MAP_FAILED {
65+ return Err ( errno:: Error :: last ( ) ) ;
66+ }
67+ Ok ( Self {
68+ addr : addr. cast ( ) ,
69+ page_size,
70+ } )
71+ }
72+
73+ /// Compute the size of the MMIO ring.
74+ /// Taken from [include/uapi/linux/kvm.h](https://elixir.bootlin.com/linux/v6.6/source/include/uapi/linux/kvm.h#L562)
75+ const fn ring_max ( & self ) -> usize {
76+ ( self . page_size - size_of :: < kvm_coalesced_mmio_ring > ( ) ) / size_of :: < kvm_coalesced_mmio > ( )
77+ }
78+
79+ /// Gets a mutable reference to the ring
80+ fn ring_mut ( & mut self ) -> & mut kvm_coalesced_mmio_ring {
81+ // SAFETY: We have a `&mut self` and the pointer is private, so this
82+ // access is exclusive.
83+ unsafe { & mut * self . addr }
84+ }
85+
86+ /// Reads a single entry from the MMIO ring.
87+ ///
88+ /// # Returns
89+ ///
90+ /// An entry from the MMIO ring buffer, or [`None`] if the ring is empty.
91+ pub ( crate ) fn read_entry ( & mut self ) -> Option < kvm_coalesced_mmio > {
92+ let ring_max = self . ring_max ( ) ;
93+
94+ let ring = self . ring_mut ( ) ;
95+ if ring. first == ring. last {
96+ return None ;
97+ }
98+
99+ let entries = ring. coalesced_mmio . as_ptr ( ) ;
100+ // SAFETY: `ring.first` is an `u32` coming from mapped memory filled
101+ // by the kernel, so we trust it. `entries` is a pointer coming from
102+ // mmap(), so pointer arithmetic cannot overflow. We have a `&mut self`,
103+ // so nobody else has access to the contents of the pointer.
104+ let elem = unsafe { entries. add ( ring. first as usize ) . read ( ) } ;
105+ ring. first = ( ring. first + 1 ) % ring_max as u32 ;
106+
107+ Some ( elem)
108+ }
109+ }
110+
111+ impl Drop for KvmCoalescedIoRing {
112+ fn drop ( & mut self ) {
113+ // SAFETY: This is safe because we mmap the page ourselves, and nobody
114+ // else is holding a reference to it.
115+ unsafe {
116+ libc:: munmap ( self . addr . cast ( ) , self . page_size ) ;
117+ }
118+ }
119+ }
120+
121+ // SAFETY: See safety comments about [`KvmRunWrapper`].
122+ unsafe impl Send for KvmCoalescedIoRing { }
123+ // SAFETY: See safety comments about [`KvmRunWrapper`].
124+ unsafe impl Sync for KvmCoalescedIoRing { }
125+
29126/// Safe wrapper over the `kvm_run` struct.
30127///
31128/// The wrapper is needed for sending the pointer to `kvm_run` between
0 commit comments