emulated.rs92.11%
1
// Copyright 2024 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
use std::cmp::min;16
use std::fmt::Debug;17
use std::sync::Arc;18
19
use crate::mem::addressable::{Addressable, SlotBackend};20
use crate::mem::{Memory, Result};21
use crate::utils::truncate_u64;22
23
#[cfg(not(test))]24
pub trait ChangeLayout: Debug + Send + Sync + 'static {25
fn change(&self, memory: &Memory) -> Result<()>;26
}27
#[cfg(test)]28
pub trait ChangeLayout: Debug + Send + Sync + std::any::Any + 'static {29
fn change(&self, memory: &Memory) -> Result<()>;30
}31
32
#[derive(Debug)]33
pub enum Action {34
None,35
Shutdown,36
Reset,37
ChangeLayout { callback: Box<dyn ChangeLayout> },38
}39
40
pub trait Mmio: Debug + Send + Sync + 'static {41
fn read(&self, offset: u64, size: u8) -> Result<u64>;42
fn write(&self, offset: u64, size: u8, val: u64) -> Result<Action>;43
fn size(&self) -> u64;44
}45
46
impl Mmio for Arc<dyn Mmio> {47
fn read(&self, offset: u64, size: u8) -> Result<u64> {234x48
Mmio::read(self.as_ref(), offset, size)234x49
}234x50
51
fn write(&self, offset: u64, size: u8, val: u64) -> Result<Action> {93x52
Mmio::write(self.as_ref(), offset, size, val)93x53
}93x54
55
fn size(&self) -> u64 {606x56
Mmio::size(self.as_ref())606x57
}606x58
}59
60
impl SlotBackend for Arc<dyn Mmio> {61
fn size(&self) -> u64 {1767x62
Mmio::size(self.as_ref())1767x63
}1767x64
}65
66
#[macro_export]67
macro_rules! impl_mmio_for_zerocopy {68
($ty:ident) => {69
impl $crate::mem::emulated::Mmio for $ty {70
fn size(&self) -> u64 {3x71
::core::mem::size_of::<Self>() as u643x72
}3x73
74
fn read(&self, offset: u64, size: u8) -> $crate::mem::Result<u64> {205x75
fn read_from_prefix<T: ::zerocopy::FromBytes + Into<u64>>(159x76
bytes: &[u8],159x77
) -> ::core::option::Option<u64> {159x78
let (n, _) = T::read_from_prefix(bytes).ok()?;159x79
Some(n.into())159x80
}159x81
82
let bytes = ::zerocopy::IntoBytes::as_bytes(self);205x83
let offset = offset as usize;205x84
let val = match size {205x85
1 => bytes.get(offset).map(|b| *b as u64),46x86
2 => bytes.get(offset..).and_then(read_from_prefix::<u16>),75x87
4 => bytes.get(offset..).and_then(read_from_prefix::<u32>),81x88
8 => bytes.get(offset..).and_then(read_from_prefix::<u64>),3x89
_ => ::core::option::Option::None,90
};91
92
if let ::core::option::Option::Some(val) = val {205x93
::core::result::Result::Ok(val)205x94
} else {95
::log::error!(96
"{}: invalid read access, offset = {offset:#x}, size = {size}.",97
::core::any::type_name::<Self>()98
);99
::core::result::Result::Ok(0)100
}101
}205x102
103
fn write(3x104
&self,3x105
offset: u64,3x106
size: u8,3x107
val: u64,3x108
) -> $crate::mem::Result<$crate::mem::emulated::Action> {3x109
::log::error!(3x110
"{}: write 0x{val:0width$x} to readonly offset 0x{offset:x}.",111
::core::any::type_name::<Self>(),3x112
width = 2 * size as usize3x113
);114
Ok($crate::mem::emulated::Action::None)3x115
}3x116
}117
};118
}119
120
#[derive(Debug)]121
pub struct MmioBus<R = Arc<dyn Mmio>>122
where123
R: Debug + SlotBackend,124
{125
pub(crate) inner: Addressable<R>,126
}127
128
impl<R> Default for MmioBus<R>129
where130
R: Debug + SlotBackend + Mmio,131
{132
fn default() -> Self {133
Self::new()134
}135
}136
137
impl<R> MmioBus<R>138
where139
R: Debug + SlotBackend + Mmio,140
{141
pub fn new() -> MmioBus<R> {211x142
Self {211x143
inner: Addressable::new(),211x144
}211x145
}211x146
147
pub fn is_empty(&self) -> bool {89x148
self.inner.is_empty()89x149
}89x150
151
pub fn add(&mut self, addr: u64, range: R) -> Result<()> {600x152
self.inner.add(addr, range)?;600x153
Ok(())600x154
}600x155
156
pub(super) fn remove(&mut self, addr: u64) -> Result<R> {18x157
self.inner.remove(addr)18x158
}18x159
160
pub fn read(&self, addr: u64, size: u8) -> Result<u64> {150x161
let mut count = 0;150x162
let mut val = 0;150x163
let size = size as u64;150x164
while count < size {420x165
let base_addr = addr + count;309x166
let Some((start, dev)) = self.inner.search_next(base_addr) else {309x167
break;6x168
};169
count += start.saturating_sub(base_addr);303x170
let offset = base_addr.saturating_sub(start);303x171
let mut read_size = min(Mmio::size(dev) - offset, size.saturating_sub(count));303x172
if read_size == 0 {303x173
break;33x174
}270x175
read_size = min(read_size, 1 << read_size.trailing_zeros());270x176
if offset > 0 {270x177
read_size = min(read_size, 1 << offset.trailing_zeros());42x178
}228x179
val |= truncate_u64(dev.read(offset, read_size as u8)?, read_size) << (count << 3);270x180
count += read_size;270x181
}182
Ok(val)150x183
}150x184
185
pub fn write(&self, addr: u64, size: u8, val: u64) -> Result<Action> {63x186
let mut count = 0;63x187
let size = size as u64;63x188
let mut action = Action::None;63x189
while count < size {162x190
let base_addr = addr + count;117x191
let Some((start, dev)) = self.inner.search_next(base_addr) else {117x192
break;3x193
};194
count += start.saturating_sub(base_addr);114x195
let offset = base_addr.saturating_sub(start);114x196
let mut write_size = min(Mmio::size(dev) - offset, size.saturating_sub(count));114x197
if write_size == 0 {114x198
break;15x199
}99x200
write_size = min(write_size, 1 << write_size.trailing_zeros());99x201
if offset > 0 {99x202
write_size = min(write_size, 1 << offset.trailing_zeros());15x203
}84x204
let write_val = truncate_u64(val >> (count << 3), write_size);99x205
let r = dev.write(offset, write_size as u8, write_val)?;99x206
if matches!(action, Action::None) {99x207
action = r99x208
} else {209
// TODO: handle multiple side effects caused by a single write210
log::error!(211
"Write {write_val:#x} to {:#x}: dropped: {action:#x?}",212
start + offset213
);214
}215
count += write_size;99x216
}217
Ok(action)63x218
}63x219
}220
221
#[cfg(test)]222
#[path = "emulated_test.rs"]223
mod tests;224