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 native_endian(mut self) -> Self { 83 self.0.endianness = device_endian::DEVICE_NATIVE_ENDIAN; 84 self 85 } 86 87 #[must_use] 88 pub const fn valid_sizes(mut self, min: u32, max: u32) -> Self { 89 self.0.valid.min_access_size = min; 90 self.0.valid.max_access_size = max; 91 self 92 } 93 94 #[must_use] 95 pub const fn valid_unaligned(mut self) -> Self { 96 self.0.valid.unaligned = true; 97 self 98 } 99 100 #[must_use] 101 pub const fn impl_sizes(mut self, min: u32, max: u32) -> Self { 102 self.0.impl_.min_access_size = min; 103 self.0.impl_.max_access_size = max; 104 self 105 } 106 107 #[must_use] 108 pub const fn impl_unaligned(mut self) -> Self { 109 self.0.impl_.unaligned = true; 110 self 111 } 112 113 #[must_use] 114 pub const fn build(self) -> MemoryRegionOps<T> { 115 MemoryRegionOps::<T>(self.0, PhantomData) 116 } 117 118 #[must_use] 119 pub const fn new() -> Self { 120 Self(bindings::MemoryRegionOps::ZERO, PhantomData) 121 } 122 } 123 124 impl<T> Default for MemoryRegionOpsBuilder<T> { 125 fn default() -> Self { 126 Self::new() 127 } 128 } 129 130 /// A safe wrapper around [`bindings::MemoryRegion`]. 131 #[repr(transparent)] 132 #[derive(common::Wrapper)] 133 pub struct MemoryRegion(Opaque<bindings::MemoryRegion>); 134 135 unsafe impl Send for MemoryRegion {} 136 unsafe impl Sync for MemoryRegion {} 137 138 impl MemoryRegion { 139 unsafe fn do_init_io( 140 slot: *mut bindings::MemoryRegion, 141 owner: *mut bindings::Object, 142 ops: &'static bindings::MemoryRegionOps, 143 name: &'static str, 144 size: u64, 145 ) { 146 unsafe { 147 let cstr = CString::new(name).unwrap(); 148 memory_region_init_io( 149 slot, 150 owner, 151 ops, 152 owner.cast::<c_void>(), 153 cstr.as_ptr(), 154 size, 155 ); 156 } 157 } 158 159 pub fn init_io<T: IsA<Object>>( 160 this: &mut MaybeUninitField<'_, T, Self>, 161 ops: &'static MemoryRegionOps<T>, 162 name: &'static str, 163 size: u64, 164 ) { 165 unsafe { 166 Self::do_init_io( 167 this.as_mut_ptr().cast(), 168 MaybeUninitField::parent_mut(this).cast(), 169 &ops.0, 170 name, 171 size, 172 ); 173 } 174 } 175 } 176 177 unsafe impl ObjectType for MemoryRegion { 178 type Class = bindings::MemoryRegionClass; 179 const TYPE_NAME: &'static CStr = 180 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_MEMORY_REGION) }; 181 } 182 183 qom_isa!(MemoryRegion: Object); 184 185 /// A special `MemTxAttrs` constant, used to indicate that no memory 186 /// attributes are specified. 187 /// 188 /// Bus masters which don't specify any attributes will get this, 189 /// which has all attribute bits clear except the topmost one 190 /// (so that we can distinguish "all attributes deliberately clear" 191 /// from "didn't specify" if necessary). 192 pub const MEMTXATTRS_UNSPECIFIED: MemTxAttrs = MemTxAttrs { 193 unspecified: true, 194 ..Zeroable::ZERO 195 }; 196