1use qcode::{context::Context, space::MemorySpaceId};
19use qcode_emulator::{DomainMemory, DomainValue, EmulatorErrorKind, EmulatorMemory, SizedValue};
20
21use crate::{
22 flat::FlatSpaces,
23 mmu::{MemFault, Mmu},
24};
25
26#[derive(Default)]
28pub struct VmMemory {
29 pub mmu: Mmu,
31 flat: FlatSpaces,
33 ram: Option<MemorySpaceId>,
37 fault: Option<MemFault>,
45}
46
47impl VmMemory {
48 pub fn new() -> Self {
49 Self::default()
50 }
51
52 pub fn take_fault(&mut self) -> Option<MemFault> {
54 self.fault.take()
55 }
56
57 pub fn fault(&self) -> Option<MemFault> {
63 self.fault
64 }
65
66 pub(crate) fn record_read_fault(&mut self, fault: MemFault) {
68 self.record(fault, false);
69 }
70
71 pub(crate) fn record_write_fault(&mut self, fault: MemFault) {
73 self.record(fault, true);
74 }
75
76 fn is_ram(&self, space: MemorySpaceId) -> bool {
78 self.ram == Some(space)
79 }
80
81 pub fn is_flat(&self, space: MemorySpaceId) -> bool {
84 !self.is_ram(space)
85 }
86
87 pub fn flat_mut(&mut self) -> &mut FlatSpaces {
89 &mut self.flat
90 }
91
92 fn record(&mut self, fault: MemFault, writing: bool) -> EmulatorErrorKind {
96 self.fault = Some(fault);
97 if writing {
98 EmulatorErrorKind::MemoryWriteError(fault.addr)
99 } else {
100 EmulatorErrorKind::MemoryReadError(fault.addr)
101 }
102 }
103}
104
105impl DomainMemory for VmMemory {
106 type V = SizedValue;
107
108 fn read(
109 &self,
110 space: MemorySpaceId,
111 addr: Self::V,
112 size: usize,
113 ) -> Result<Self::V, EmulatorErrorKind> {
114 if !self.is_ram(space) {
115 let bits = self.flat.read_u128(space, addr.value()?, size)?;
116 return Ok(SizedValue::from_bits(bits, size));
117 }
118 let addr = addr.value()?;
119 let mut bytes = vec![0u8; size.min(16)];
122 self.mmu
126 .read(addr, &mut bytes)
127 .map_err(|fault| EmulatorErrorKind::MemoryReadError(fault.addr))?;
128 let mut bits = 0u128;
129 for (index, byte) in bytes.iter().enumerate() {
130 bits |= u128::from(*byte) << (index * 8);
131 }
132 Ok(SizedValue::from_bits(bits, size))
133 }
134
135 fn write(
136 &mut self,
137 space: MemorySpaceId,
138 addr: Self::V,
139 size: usize,
140 value: Self::V,
141 ) -> Result<(), EmulatorErrorKind> {
142 if !self.is_ram(space) {
143 return self
144 .flat
145 .entry(space)
146 .write_u128(addr.value()?, size, value.as_bits());
147 }
148 let addr = addr.value()?;
149 let bits = value.as_bits();
150 let width = size.min(16);
151 let bytes: Vec<u8> = (0..width).map(|i| (bits >> (i * 8)) as u8).collect();
152 self.mmu
153 .write(addr, &bytes)
154 .map_err(|fault| self.record(fault, true))
155 }
156}
157
158impl EmulatorMemory for VmMemory {
159 fn configure_spaces(&mut self, ctx: &Context<'_>) {
160 self.ram = Some(MemorySpaceId::Shared(ctx.shared.default_space));
163 self.flat.configure(ctx);
164 }
165
166 fn read_bytes(
167 &self,
168 space: MemorySpaceId,
169 addr: u64,
170 size: usize,
171 ) -> Result<Vec<u8>, EmulatorErrorKind> {
172 if !self.is_ram(space) {
173 return self.flat.read_bytes(space, addr, size);
174 }
175 let mut bytes = vec![0u8; size];
176 self.mmu
177 .read(addr, &mut bytes)
178 .map_err(|fault| EmulatorErrorKind::MemoryReadError(fault.addr))?;
179 Ok(bytes)
180 }
181
182 fn write_bytes(
183 &mut self,
184 space: MemorySpaceId,
185 addr: u64,
186 bytes: &[u8],
187 ) -> Result<(), EmulatorErrorKind> {
188 if !self.is_ram(space) {
189 return self.flat.entry(space).write_bytes(addr, bytes);
190 }
191 self.mmu
192 .write(addr, bytes)
193 .map_err(|fault| self.record(fault, true))
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 use crate::mmu::{FaultKind, PAGE_SIZE, perm};
201 use qcode::space::SpaceId;
202
203 fn context() -> Context<'static> {
205 Context::default()
206 }
207
208 fn configured() -> (Context<'static>, VmMemory) {
209 let ctx = context();
210 let mut memory = VmMemory::new();
211 memory.configure_spaces(&ctx);
212 (ctx, memory)
213 }
214
215 fn ram(ctx: &Context<'_>) -> MemorySpaceId {
216 MemorySpaceId::Shared(ctx.shared.default_space)
217 }
218
219 #[test]
220 fn ram_accesses_go_through_the_mmu() {
221 let (ctx, mut memory) = configured();
222 memory.mmu.map(0x1000, PAGE_SIZE, perm::RW_INIT).unwrap();
223
224 let addr = SizedValue::from_u64(0x1000);
225 memory
226 .write(ram(&ctx), addr, 4, SizedValue::from_bits(0xdead_beef, 4))
227 .unwrap();
228 let read = memory.read(ram(&ctx), addr, 4).unwrap();
229 assert_eq!(read.as_bits(), 0xdead_beef);
230 let mut raw = [0; 4];
232 memory.mmu.read(0x1000, &mut raw).unwrap();
233 assert_eq!(raw, [0xef, 0xbe, 0xad, 0xde]);
234 }
235
236 #[test]
237 fn unmapped_ram_write_faults_and_is_recorded() {
238 let (ctx, mut memory) = configured();
239 let addr = SizedValue::from_u64(0x1000);
240 let error = memory
241 .write(ram(&ctx), addr, 4, SizedValue::from_bits(1, 4))
242 .unwrap_err();
243 assert!(matches!(error, EmulatorErrorKind::MemoryWriteError(0x1000)));
244 assert_eq!(
245 memory.take_fault(),
246 Some(MemFault {
247 kind: FaultKind::WriteUnmapped,
248 addr: 0x1000
249 })
250 );
251 assert_eq!(memory.take_fault(), None);
253 }
254
255 #[test]
256 fn read_only_ram_refuses_a_write() {
257 let (ctx, mut memory) = configured();
258 memory.mmu.map(0x1000, PAGE_SIZE, perm::RX_INIT).unwrap();
259 let addr = SizedValue::from_u64(0x1000);
260 memory
261 .write(ram(&ctx), addr, 1, SizedValue::from_bits(1, 1))
262 .unwrap_err();
263 assert_eq!(
264 memory.take_fault().map(|f| f.kind),
265 Some(FaultKind::WritePerm)
266 );
267 }
268
269 #[test]
270 fn non_ram_spaces_bypass_the_mmu_entirely() {
271 let ctx = context();
272 let mut memory = VmMemory::new();
273 memory.configure_spaces(&ctx);
274
275 let register = MemorySpaceId::Shared(SpaceId::from(1));
278 let addr = SizedValue::from_u64(0x40);
279 memory
280 .write(register, addr, 8, SizedValue::from_bits(0x1234, 8))
281 .unwrap();
282 assert_eq!(memory.read(register, addr, 8).unwrap().as_bits(), 0x1234);
283 assert_eq!(memory.mmu.resident_pages(), 0);
284 assert_eq!(memory.take_fault(), None);
285 }
286
287 #[test]
288 fn byte_level_access_routes_the_same_way() {
289 let (ctx, mut memory) = configured();
290 memory.mmu.map(0x2000, PAGE_SIZE, perm::RW_INIT).unwrap();
291 memory
292 .write_bytes(ram(&ctx), 0x2000, &[1, 2, 3, 4])
293 .unwrap();
294 assert_eq!(
295 memory.read_bytes(ram(&ctx), 0x2000, 4).unwrap(),
296 vec![1, 2, 3, 4]
297 );
298 }
299
300 #[test]
301 fn before_configuration_nothing_is_routed_to_the_mmu() {
302 let mut memory = VmMemory::new();
305 let addr = SizedValue::from_u64(0x1000);
306 let space = MemorySpaceId::Shared(SpaceId::from(0));
307 memory
308 .write(space, addr, 4, SizedValue::from_bits(7, 4))
309 .unwrap();
310 assert_eq!(memory.mmu.resident_pages(), 0);
311 }
312}