Alioth Code Coverage

emulated.rs92.11%

1// Copyright 2024 Google LLC
2//
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 at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// 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 and
13// limitations under the License.
14
15use std::cmp::min;
16use std::fmt::Debug;
17use std::sync::Arc;
18
19use crate::mem::addressable::{Addressable, SlotBackend};
20use crate::mem::{Memory, Result};
21use crate::utils::truncate_u64;
22
23#[cfg(not(test))]
24pub trait ChangeLayout: Debug + Send + Sync + 'static {
25 fn change(&self, memory: &Memory) -> Result<()>;
26}
27#[cfg(test)]
28pub trait ChangeLayout: Debug + Send + Sync + std::any::Any + 'static {
29 fn change(&self, memory: &Memory) -> Result<()>;
30}
31
32#[derive(Debug)]
33pub enum Action {
34 None,
35 Shutdown,
36 Reset,
37 ChangeLayout { callback: Box<dyn ChangeLayout> },
38}
39
40pub 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
46impl Mmio for Arc<dyn Mmio> {
47 fn read(&self, offset: u64, size: u8) -> Result<u64> {234x
48 Mmio::read(self.as_ref(), offset, size)234x
49 }234x
50
51 fn write(&self, offset: u64, size: u8, val: u64) -> Result<Action> {93x
52 Mmio::write(self.as_ref(), offset, size, val)93x
53 }93x
54
55 fn size(&self) -> u64 {606x
56 Mmio::size(self.as_ref())606x
57 }606x
58}
59
60impl SlotBackend for Arc<dyn Mmio> {
61 fn size(&self) -> u64 {1767x
62 Mmio::size(self.as_ref())1767x
63 }1767x
64}
65
66#[macro_export]
67macro_rules! impl_mmio_for_zerocopy {
68 ($ty:ident) => {
69 impl $crate::mem::emulated::Mmio for $ty {
70 fn size(&self) -> u64 {3x
71 ::core::mem::size_of::<Self>() as u643x
72 }3x
73
74 fn read(&self, offset: u64, size: u8) -> $crate::mem::Result<u64> {205x
75 fn read_from_prefix<T: ::zerocopy::FromBytes + Into<u64>>(159x
76 bytes: &[u8],159x
77 ) -> ::core::option::Option<u64> {159x
78 let (n, _) = T::read_from_prefix(bytes).ok()?;159x
79 Some(n.into())159x
80 }159x
81
82 let bytes = ::zerocopy::IntoBytes::as_bytes(self);205x
83 let offset = offset as usize;205x
84 let val = match size {205x
85 1 => bytes.get(offset).map(|b| *b as u64),46x
86 2 => bytes.get(offset..).and_then(read_from_prefix::<u16>),75x
87 4 => bytes.get(offset..).and_then(read_from_prefix::<u32>),81x
88 8 => bytes.get(offset..).and_then(read_from_prefix::<u64>),3x
89 _ => ::core::option::Option::None,
90 };
91
92 if let ::core::option::Option::Some(val) = val {205x
93 ::core::result::Result::Ok(val)205x
94 } 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 }205x
102
103 fn write(3x
104 &self,3x
105 offset: u64,3x
106 size: u8,3x
107 val: u64,3x
108 ) -> $crate::mem::Result<$crate::mem::emulated::Action> {3x
109 ::log::error!(3x
110 "{}: write 0x{val:0width$x} to readonly offset 0x{offset:x}.",
111 ::core::any::type_name::<Self>(),3x
112 width = 2 * size as usize3x
113 );
114 Ok($crate::mem::emulated::Action::None)3x
115 }3x
116 }
117 };
118}
119
120#[derive(Debug)]
121pub struct MmioBus<R = Arc<dyn Mmio>>
122where
123 R: Debug + SlotBackend,
124{
125 pub(crate) inner: Addressable<R>,
126}
127
128impl<R> Default for MmioBus<R>
129where
130 R: Debug + SlotBackend + Mmio,
131{
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137impl<R> MmioBus<R>
138where
139 R: Debug + SlotBackend + Mmio,
140{
141 pub fn new() -> MmioBus<R> {211x
142 Self {211x
143 inner: Addressable::new(),211x
144 }211x
145 }211x
146
147 pub fn is_empty(&self) -> bool {89x
148 self.inner.is_empty()89x
149 }89x
150
151 pub fn add(&mut self, addr: u64, range: R) -> Result<()> {600x
152 self.inner.add(addr, range)?;600x
153 Ok(())600x
154 }600x
155
156 pub(super) fn remove(&mut self, addr: u64) -> Result<R> {18x
157 self.inner.remove(addr)18x
158 }18x
159
160 pub fn read(&self, addr: u64, size: u8) -> Result<u64> {150x
161 let mut count = 0;150x
162 let mut val = 0;150x
163 let size = size as u64;150x
164 while count < size {420x
165 let base_addr = addr + count;309x
166 let Some((start, dev)) = self.inner.search_next(base_addr) else {309x
167 break;6x
168 };
169 count += start.saturating_sub(base_addr);303x
170 let offset = base_addr.saturating_sub(start);303x
171 let mut read_size = min(Mmio::size(dev) - offset, size.saturating_sub(count));303x
172 if read_size == 0 {303x
173 break;33x
174 }270x
175 read_size = min(read_size, 1 << read_size.trailing_zeros());270x
176 if offset > 0 {270x
177 read_size = min(read_size, 1 << offset.trailing_zeros());42x
178 }228x
179 val |= truncate_u64(dev.read(offset, read_size as u8)?, read_size) << (count << 3);270x
180 count += read_size;270x
181 }
182 Ok(val)150x
183 }150x
184
185 pub fn write(&self, addr: u64, size: u8, val: u64) -> Result<Action> {63x
186 let mut count = 0;63x
187 let size = size as u64;63x
188 let mut action = Action::None;63x
189 while count < size {162x
190 let base_addr = addr + count;117x
191 let Some((start, dev)) = self.inner.search_next(base_addr) else {117x
192 break;3x
193 };
194 count += start.saturating_sub(base_addr);114x
195 let offset = base_addr.saturating_sub(start);114x
196 let mut write_size = min(Mmio::size(dev) - offset, size.saturating_sub(count));114x
197 if write_size == 0 {114x
198 break;15x
199 }99x
200 write_size = min(write_size, 1 << write_size.trailing_zeros());99x
201 if offset > 0 {99x
202 write_size = min(write_size, 1 << offset.trailing_zeros());15x
203 }84x
204 let write_val = truncate_u64(val >> (count << 3), write_size);99x
205 let r = dev.write(offset, write_size as u8, write_val)?;99x
206 if matches!(action, Action::None) {99x
207 action = r99x
208 } else {
209 // TODO: handle multiple side effects caused by a single write
210 log::error!(
211 "Write {write_val:#x} to {:#x}: dropped: {action:#x?}",
212 start + offset
213 );
214 }
215 count += write_size;99x
216 }
217 Ok(action)63x
218 }63x
219}
220
221#[cfg(test)]
222#[path = "emulated_test.rs"]
223mod tests;
224