master
rs 189 lines 5.5 KB
Raw
1 // Copyright 2024 Red Hat, Inc.
2 // Author(s): Paolo Bonzini <pbonzini@redhat.com>
3 // SPDX-License-Identifier: GPL-2.0-or-later
4
5 //! Bindings for `MemoryRegion`, `MemoryRegionOps` and `MemTxAttrs`
6
7 use std::{
8 ffi::{c_uint, c_void, CStr, CString},
9 marker::PhantomData,
10 };
11
12 use common::{callbacks::FnCall, uninit::MaybeUninitField, zeroable::Zeroable, Opaque};
13 use qom::prelude::*;
14
15 use crate::bindings::{self, device_endian, memory_region_init_io};
16 pub use crate::bindings::{hwaddr, MemTxAttrs};
17
18 pub struct MemoryRegionOps<T>(
19 bindings::MemoryRegionOps,
20 // Note: quite often you'll see PhantomData<fn(&T)> mentioned when discussing
21 // covariance and contravariance; you don't need any of those to understand
22 // this usage of PhantomData. Quite simply, MemoryRegionOps<T> *logically*
23 // holds callbacks that take an argument of type &T, except the type is erased
24 // before the callback is stored in the bindings::MemoryRegionOps field.
25 // The argument of PhantomData is a function pointer in order to represent
26 // that relationship; while that will also provide desirable and safe variance
27 // for T, variance is not the point but just a consequence.
28 PhantomData<fn(&T)>,
29 );
30
31 // SAFETY: When a *const T is passed to the callbacks, the call itself
32 // is done in a thread-safe manner. The invocation is okay as long as
33 // T itself is `Sync`.
34 unsafe impl<T: Sync> Sync for MemoryRegionOps<T> {}
35
36 #[derive(Clone)]
37 pub struct MemoryRegionOpsBuilder<T>(bindings::MemoryRegionOps, PhantomData<fn(&T)>);
38
39 unsafe extern "C" fn memory_region_ops_read_cb<T, F: for<'a> FnCall<(&'a T, hwaddr, u32), u64>>(
40 opaque: *mut c_void,
41 addr: hwaddr,
42 size: c_uint,
43 ) -> u64 {
44 F::call((unsafe { &*(opaque.cast::<T>()) }, addr, size))
45 }
46
47 unsafe extern "C" fn memory_region_ops_write_cb<T, F: for<'a> FnCall<(&'a T, hwaddr, u64, u32)>>(
48 opaque: *mut c_void,
49 addr: hwaddr,
50 data: u64,
51 size: c_uint,
52 ) {
53 F::call((unsafe { &*(opaque.cast::<T>()) }, addr, data, size))
54 }
55
56 impl<T> MemoryRegionOpsBuilder<T> {
57 #[must_use]
58 pub const fn read<F: for<'a> FnCall<(&'a T, hwaddr, u32), u64>>(mut self, _f: &F) -> Self {
59 self.0.read = Some(memory_region_ops_read_cb::<T, F>);
60 self
61 }
62
63 #[must_use]
64 pub const fn write<F: for<'a> FnCall<(&'a T, hwaddr, u64, u32)>>(mut self, _f: &F) -> Self {
65 self.0.write = Some(memory_region_ops_write_cb::<T, F>);
66 self
67 }
68
69 #[must_use]
70 pub const fn big_endian(mut self) -> Self {
71 self.0.endianness = device_endian::DEVICE_BIG_ENDIAN;
72 self
73 }
74
75 #[must_use]
76 pub const fn little_endian(mut self) -> Self {
77 self.0.endianness = device_endian::DEVICE_LITTLE_ENDIAN;
78 self
79 }
80
81 #[must_use]
82 pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self {
83 self.0.valid.min_access_size = min;
84 self.0.valid.max_access_size = max;
85 self
86 }
87
88 #[must_use]
89 pub const fn valid_unaligned(mut self) -> Self {
90 self.0.valid.unaligned = true;
91 self
92 }
93
94 #[must_use]
95 pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self {
96 self.0.impl_.min_access_size = min;
97 self.0.impl_.max_access_size = max;
98 self
99 }
100
101 #[must_use]
102 pub const fn impl_unaligned(mut self) -> Self {
103 self.0.impl_.unaligned = true;
104 self
105 }
106
107 #[must_use]
108 pub const fn build(self) -> MemoryRegionOps<T> {
109 MemoryRegionOps::<T>(self.0, PhantomData)
110 }
111
112 #[must_use]
113 pub const fn new() -> Self {
114 Self(bindings::MemoryRegionOps::ZERO, PhantomData)
115 }
116 }
117
118 impl<T> Default for MemoryRegionOpsBuilder<T> {
119 fn default() -> Self {
120 Self::new()
121 }
122 }
123
124 /// A safe wrapper around [`bindings::MemoryRegion`].
125 #[repr(transparent)]
126 #[derive(common::Wrapper)]
127 pub struct MemoryRegion(Opaque<bindings::MemoryRegion>);
128
129 unsafe impl Send for MemoryRegion {}
130 unsafe impl Sync for MemoryRegion {}
131
132 impl MemoryRegion {
133 unsafe fn do_init_io(
134 slot: *mut bindings::MemoryRegion,
135 owner: *mut qom::bindings::Object,
136 ops: &'static bindings::MemoryRegionOps,
137 name: &'static str,
138 size: u64,
139 ) {
140 unsafe {
141 let cstr = CString::new(name).unwrap();
142 memory_region_init_io(
143 slot,
144 owner,
145 ops,
146 owner.cast::<c_void>(),
147 cstr.as_ptr(),
148 size,
149 );
150 }
151 }
152
153 pub fn init_io<T: IsA<Object>>(
154 this: &mut MaybeUninitField<'_, T, Self>,
155 ops: &'static MemoryRegionOps<T>,
156 name: &'static str,
157 size: u64,
158 ) {
159 unsafe {
160 Self::do_init_io(
161 this.as_mut_ptr().cast(),
162 MaybeUninitField::parent_mut(this).cast(),
163 &ops.0,
164 name,
165 size,
166 );
167 }
168 }
169 }
170
171 unsafe impl ObjectType for MemoryRegion {
172 type Class = bindings::MemoryRegionClass;
173 const TYPE_NAME: &'static CStr =
174 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) };
175 }
176
177 qom_isa!(MemoryRegion: Object);
178
179 /// A special `MemTxAttrs` constant, used to indicate that no memory
180 /// attributes are specified.
181 ///
182 /// Bus masters which don't specify any attributes will get this,
183 /// which has all attribute bits clear except the topmost one
184 /// (so that we can distinguish "all attributes deliberately clear"
185 /// from "didn't specify" if necessary).
186 pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs {
187 unspecified: true,
188 ..Zeroable::ZERO
189 };