5
5
// Use of this source code is governed by a BSD-style license that can be
6
6
// found in the THIRD-PARTY file.
7
7
8
+ use std:: mem:: size_of;
8
9
use std:: os:: unix:: io:: AsRawFd ;
9
10
use std:: ptr:: null_mut;
10
11
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
+ } ;
12
15
use vmm_sys_util:: errno;
13
16
14
17
/// Wrappers over KVM device ioctls.
@@ -26,6 +29,100 @@ pub mod vm;
26
29
/// is otherwise a direct mapping to Result.
27
30
pub type Result < T > = std:: result:: Result < T , errno:: Error > ;
28
31
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
+
29
126
/// Safe wrapper over the `kvm_run` struct.
30
127
///
31
128
/// The wrapper is needed for sending the pointer to `kvm_run` between
0 commit comments