1use alloc::vec::Vec;
22
23pub const PAGE_SIZE: u32 = 1 << 12;
25
26pub mod perm {
28 pub const NONE: u8 = 0;
30 pub const RO: u8 = 1;
32 pub const RW: u8 = 2;
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum Access {
40 ReadOnly,
41 ReadWrite,
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum MemAccess {
48 Ok,
49 PageFault(u32),
51 WriteProtected(u32),
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub enum MapError {
58 UnalignedStart(u64),
60 UnalignedSize(u64),
62 Overflow,
65}
66
67pub trait Memory {
84 fn read_u8(&self, addr: u32) -> Option<u8>;
86 fn read_u16_le(&self, addr: u32) -> Option<u16>;
87 fn read_u32_le(&self, addr: u32) -> Option<u32>;
88 fn read_u64_le(&self, addr: u32) -> Option<u64>;
89
90 fn write_u8(&mut self, addr: u32, val: u8) -> bool;
92 fn write_u16_le(&mut self, addr: u32, val: u16) -> bool;
93 fn write_u32_le(&mut self, addr: u32, val: u32) -> bool;
94 fn write_u64_le(&mut self, addr: u32, val: u64) -> bool;
95
96 fn map_region(
101 &mut self,
102 start: u64,
103 size: u64,
104 access: Access,
105 init: Option<&[u8]>,
106 ) -> Result<(), MapError>;
107
108 fn perm_of(&self, addr: u32) -> u8;
111
112 fn read(&self, addr: u32, len: usize) -> Result<Vec<u8>, MemAccess>;
114
115 fn write(&mut self, addr: u32, data: &[u8]) -> Result<(), MemAccess>;
118}
119
120#[derive(Clone, Debug)]
127pub struct CopyingMemory {
128 pub flat_mem: Vec<u8>,
130 pub perms: Vec<u8>,
133 pub heap_base: u32,
135 pub heap_top: u32,
137 pub max_heap_pages: u32,
139}
140
141impl Default for CopyingMemory {
142 fn default() -> Self {
143 Self::new()
144 }
145}
146
147pub type Mem = CopyingMemory;
151
152impl CopyingMemory {
153 pub fn new() -> Self {
155 Self {
156 flat_mem: Vec::new(),
157 perms: Vec::new(),
158 heap_base: 0,
159 heap_top: 0,
160 max_heap_pages: 0,
161 }
162 }
163
164 pub fn with_pages(n_pages: u32, default_perm: u8) -> Self {
167 let bytes = (n_pages as usize) * (PAGE_SIZE as usize);
168 Self {
169 flat_mem: vec![0u8; bytes],
170 perms: vec![default_perm; n_pages as usize],
171 heap_base: 0,
172 heap_top: 0,
173 max_heap_pages: 0,
174 }
175 }
176
177 #[inline(always)]
179 pub fn is_in_bounds(&self, addr: u32) -> bool {
180 (addr as usize) < self.flat_mem.len()
181 }
182
183 pub fn perm_of(&self, addr: u32) -> u8 {
186 let page = (addr / PAGE_SIZE) as usize;
187 self.perms.get(page).copied().unwrap_or(perm::NONE)
188 }
189
190 #[inline(always)]
193 pub fn read_u8(&self, addr: u32) -> Option<u8> {
194 let a = addr as usize;
195 if a < self.flat_mem.len() {
196 Some(unsafe { *self.flat_mem.get_unchecked(a) })
198 } else {
199 None
200 }
201 }
202
203 #[inline(always)]
204 pub fn read_u16_le(&self, addr: u32) -> Option<u16> {
205 let a = addr as usize;
206 if a + 2 <= self.flat_mem.len() {
207 Some(unsafe { self.flat_mem.as_ptr().add(a).cast::<u16>().read_unaligned() })
208 } else {
209 None
210 }
211 }
212
213 #[inline(always)]
214 pub fn read_u32_le(&self, addr: u32) -> Option<u32> {
215 let a = addr as usize;
216 if a + 4 <= self.flat_mem.len() {
217 Some(unsafe { self.flat_mem.as_ptr().add(a).cast::<u32>().read_unaligned() })
218 } else {
219 None
220 }
221 }
222
223 #[inline(always)]
224 pub fn read_u64_le(&self, addr: u32) -> Option<u64> {
225 let a = addr as usize;
226 if a + 8 <= self.flat_mem.len() {
227 Some(unsafe { self.flat_mem.as_ptr().add(a).cast::<u64>().read_unaligned() })
228 } else {
229 None
230 }
231 }
232
233 #[inline(always)]
236 pub fn write_u8(&mut self, addr: u32, val: u8) -> bool {
237 let a = addr as usize;
238 if a < self.flat_mem.len() {
239 unsafe {
240 *self.flat_mem.get_unchecked_mut(a) = val;
241 }
242 true
243 } else {
244 false
245 }
246 }
247
248 #[inline(always)]
249 pub fn write_u16_le(&mut self, addr: u32, val: u16) -> bool {
250 let a = addr as usize;
251 if a + 2 <= self.flat_mem.len() {
252 unsafe {
253 self.flat_mem
254 .as_mut_ptr()
255 .add(a)
256 .cast::<u16>()
257 .write_unaligned(val);
258 }
259 true
260 } else {
261 false
262 }
263 }
264
265 #[inline(always)]
266 pub fn write_u32_le(&mut self, addr: u32, val: u32) -> bool {
267 let a = addr as usize;
268 if a + 4 <= self.flat_mem.len() {
269 unsafe {
270 self.flat_mem
271 .as_mut_ptr()
272 .add(a)
273 .cast::<u32>()
274 .write_unaligned(val);
275 }
276 true
277 } else {
278 false
279 }
280 }
281
282 #[inline(always)]
283 pub fn write_u64_le(&mut self, addr: u32, val: u64) -> bool {
284 let a = addr as usize;
285 if a + 8 <= self.flat_mem.len() {
286 unsafe {
287 self.flat_mem
288 .as_mut_ptr()
289 .add(a)
290 .cast::<u64>()
291 .write_unaligned(val);
292 }
293 true
294 } else {
295 false
296 }
297 }
298
299 pub fn map_region(
314 &mut self,
315 start: u64,
316 size: u64,
317 access: Access,
318 init: Option<&[u8]>,
319 ) -> Result<(), MapError> {
320 let page = PAGE_SIZE as u64;
321 if !start.is_multiple_of(page) {
322 return Err(MapError::UnalignedStart(start));
323 }
324 if !size.is_multiple_of(page) {
325 return Err(MapError::UnalignedSize(size));
326 }
327 let end = start.checked_add(size).ok_or(MapError::Overflow)?;
328 let end_usize: usize = end.try_into().map_err(|_| MapError::Overflow)?;
329
330 if end_usize > self.flat_mem.len() {
332 self.flat_mem.resize(end_usize, 0);
333 let needed_pages = end_usize.div_ceil(PAGE_SIZE as usize);
334 if self.perms.len() < needed_pages {
335 self.perms.resize(needed_pages, perm::NONE);
336 }
337 }
338
339 let perm_byte = match access {
341 Access::ReadOnly => perm::RO,
342 Access::ReadWrite => perm::RW,
343 };
344 let first_page = (start / page) as usize;
345 let last_page = ((end / page) as usize).saturating_sub(1);
346 if size > 0 {
347 for p in first_page..=last_page {
348 self.perms[p] = perm_byte;
349 }
350 }
351
352 if let Some(bytes) = init {
356 let n = bytes.len().min(size as usize);
357 let s = start as usize;
358 self.flat_mem[s..s + n].copy_from_slice(&bytes[..n]);
359 }
360
361 Ok(())
362 }
363
364 pub fn read(&self, addr: u32, len: usize) -> Result<Vec<u8>, MemAccess> {
368 let a = addr as usize;
369 let end = a
370 .checked_add(len)
371 .ok_or(MemAccess::PageFault(addr & !(PAGE_SIZE - 1)))?;
372 if end > self.flat_mem.len() {
373 return Err(MemAccess::PageFault(addr & !(PAGE_SIZE - 1)));
374 }
375 Ok(self.flat_mem[a..end].to_vec())
376 }
377
378 pub fn write(&mut self, addr: u32, data: &[u8]) -> Result<(), MemAccess> {
382 let a = addr as usize;
383 let end = a
384 .checked_add(data.len())
385 .ok_or(MemAccess::PageFault(addr & !(PAGE_SIZE - 1)))?;
386 if end > self.flat_mem.len() {
387 return Err(MemAccess::PageFault(addr & !(PAGE_SIZE - 1)));
388 }
389 let start_page = a / (PAGE_SIZE as usize);
391 let last_page = (end - 1) / (PAGE_SIZE as usize);
392 for p in start_page..=last_page {
393 if self.perms.get(p).copied().unwrap_or(perm::NONE) != perm::RW {
394 return Err(MemAccess::WriteProtected((p as u32) * PAGE_SIZE));
395 }
396 }
397 self.flat_mem[a..end].copy_from_slice(data);
398 Ok(())
399 }
400}
401
402impl Memory for CopyingMemory {
406 #[inline(always)]
407 fn read_u8(&self, addr: u32) -> Option<u8> {
408 CopyingMemory::read_u8(self, addr)
409 }
410 #[inline(always)]
411 fn read_u16_le(&self, addr: u32) -> Option<u16> {
412 CopyingMemory::read_u16_le(self, addr)
413 }
414 #[inline(always)]
415 fn read_u32_le(&self, addr: u32) -> Option<u32> {
416 CopyingMemory::read_u32_le(self, addr)
417 }
418 #[inline(always)]
419 fn read_u64_le(&self, addr: u32) -> Option<u64> {
420 CopyingMemory::read_u64_le(self, addr)
421 }
422 #[inline(always)]
423 fn write_u8(&mut self, addr: u32, val: u8) -> bool {
424 CopyingMemory::write_u8(self, addr, val)
425 }
426 #[inline(always)]
427 fn write_u16_le(&mut self, addr: u32, val: u16) -> bool {
428 CopyingMemory::write_u16_le(self, addr, val)
429 }
430 #[inline(always)]
431 fn write_u32_le(&mut self, addr: u32, val: u32) -> bool {
432 CopyingMemory::write_u32_le(self, addr, val)
433 }
434 #[inline(always)]
435 fn write_u64_le(&mut self, addr: u32, val: u64) -> bool {
436 CopyingMemory::write_u64_le(self, addr, val)
437 }
438 #[inline]
439 fn map_region(
440 &mut self,
441 start: u64,
442 size: u64,
443 access: Access,
444 init: Option<&[u8]>,
445 ) -> Result<(), MapError> {
446 CopyingMemory::map_region(self, start, size, access, init)
447 }
448 #[inline]
449 fn perm_of(&self, addr: u32) -> u8 {
450 CopyingMemory::perm_of(self, addr)
451 }
452 #[inline]
453 fn read(&self, addr: u32, len: usize) -> Result<Vec<u8>, MemAccess> {
454 CopyingMemory::read(self, addr, len)
455 }
456 #[inline]
457 fn write(&mut self, addr: u32, data: &[u8]) -> Result<(), MemAccess> {
458 CopyingMemory::write(self, addr, data)
459 }
460}
461
462#[cfg(test)]
463mod tests {
464 use super::*;
465
466 #[test]
467 fn read_u8_in_bounds() {
468 let mut m = Mem::with_pages(1, perm::RW);
469 m.write_u8(0x100, 0xAB);
470 assert_eq!(m.read_u8(0x100), Some(0xAB));
471 }
472
473 #[test]
474 fn read_u8_out_of_bounds_returns_none() {
475 let m = Mem::with_pages(1, perm::RW);
476 assert_eq!(m.read_u8(PAGE_SIZE), None);
477 assert_eq!(m.read_u8(u32::MAX), None);
478 }
479
480 #[test]
481 fn read_write_u32_le() {
482 let mut m = Mem::with_pages(1, perm::RW);
483 m.write_u32_le(0x10, 0xDEAD_BEEF);
484 assert_eq!(m.read_u32_le(0x10), Some(0xDEAD_BEEF));
485 }
486
487 #[test]
488 fn unaligned_access_works() {
489 let mut m = Mem::with_pages(1, perm::RW);
490 m.write_u32_le(0x103, 0x1234_5678);
491 assert_eq!(m.read_u32_le(0x103), Some(0x1234_5678));
492 }
493
494 #[test]
495 fn read_u32_straddling_end_returns_none() {
496 let m = Mem::with_pages(1, perm::RW);
497 assert_eq!(m.read_u32_le(PAGE_SIZE - 2), None);
499 }
500
501 #[test]
502 fn ro_page_write_via_slow_path_faults() {
503 let mut m = Mem::with_pages(1, perm::RO);
504 let res = m.write(0, &[1]);
505 assert!(matches!(res, Err(MemAccess::WriteProtected(_))));
506 }
507
508 #[test]
509 fn perm_of_page_after_set() {
510 let m = Mem::with_pages(2, perm::RW);
511 assert_eq!(m.perm_of(0), perm::RW);
512 assert_eq!(m.perm_of(PAGE_SIZE), perm::RW);
513 assert_eq!(m.perm_of(2 * PAGE_SIZE), perm::NONE);
515 }
516
517 #[test]
518 fn slow_path_read_write_round_trip() {
519 let mut m = Mem::with_pages(1, perm::RW);
520 m.write(0, &[1, 2, 3, 4]).unwrap();
521 assert_eq!(m.read(0, 4).unwrap(), vec![1, 2, 3, 4]);
522 }
523
524 #[test]
525 fn map_region_grows_buffer_and_sets_perms() {
526 let mut m = Mem::new();
527 m.map_region(
528 2 * PAGE_SIZE as u64,
529 2 * PAGE_SIZE as u64,
530 Access::ReadWrite,
531 None,
532 )
533 .unwrap();
534 assert_eq!(m.flat_mem.len(), 4 * PAGE_SIZE as usize);
535 assert_eq!(m.perm_of(0), perm::NONE);
537 assert_eq!(m.perm_of(PAGE_SIZE), perm::NONE);
538 assert_eq!(m.perm_of(2 * PAGE_SIZE), perm::RW);
539 assert_eq!(m.perm_of(3 * PAGE_SIZE), perm::RW);
540 }
541
542 #[test]
543 fn map_region_copies_init_bytes_and_zero_fills_tail() {
544 let mut m = Mem::new();
545 m.map_region(
546 0,
547 PAGE_SIZE as u64,
548 Access::ReadOnly,
549 Some(&[0xAA, 0xBB, 0xCC]),
550 )
551 .unwrap();
552 assert_eq!(m.flat_mem[0], 0xAA);
553 assert_eq!(m.flat_mem[1], 0xBB);
554 assert_eq!(m.flat_mem[2], 0xCC);
555 assert_eq!(m.flat_mem[3], 0x00);
556 assert_eq!(m.perm_of(0), perm::RO);
557 }
558
559 #[test]
560 fn map_region_truncates_oversize_init() {
561 let mut m = Mem::new();
562 let init = vec![0x77u8; (PAGE_SIZE as usize) * 3];
563 m.map_region(0, PAGE_SIZE as u64, Access::ReadWrite, Some(&init))
565 .unwrap();
566 assert_eq!(m.flat_mem.len(), PAGE_SIZE as usize);
567 assert_eq!(m.flat_mem[PAGE_SIZE as usize - 1], 0x77);
568 }
569
570 #[test]
571 fn map_region_rejects_unaligned_start() {
572 let mut m = Mem::new();
573 assert_eq!(
574 m.map_region(123, PAGE_SIZE as u64, Access::ReadOnly, None),
575 Err(MapError::UnalignedStart(123))
576 );
577 }
578
579 #[test]
580 fn map_region_rejects_unaligned_size() {
581 let mut m = Mem::new();
582 assert_eq!(
583 m.map_region(0, 123, Access::ReadOnly, None),
584 Err(MapError::UnalignedSize(123))
585 );
586 }
587
588 #[test]
589 fn map_region_overlapping_overwrites_perms() {
590 let mut m = Mem::with_pages(2, perm::RO);
591 m.map_region(0, 2 * PAGE_SIZE as u64, Access::ReadWrite, None)
592 .unwrap();
593 assert_eq!(m.perm_of(0), perm::RW);
594 assert_eq!(m.perm_of(PAGE_SIZE), perm::RW);
595 }
596}