diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bb7ba3e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "libc" +version = "0.2.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" + +[[package]] +name = "memmap2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +dependencies = [ + "libc", +] + +[[package]] +name = "rvem" +version = "0.1.0" +dependencies = [ + "memmap2", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f844555 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rvem" +version = "0.1.0" +edition = "2024" + +[dependencies] +memmap2 = "0.9.8" diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000..a3e33a2 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,7 @@ +pub type Byte = u8; +pub type HWord = u16; +pub type Word = u32; +pub type DWord = u64; + +pub type Reg = DWord; +pub type Addr = DWord; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8484460 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +mod consts; +pub mod mem; diff --git a/src/mem.rs b/src/mem.rs new file mode 100644 index 0000000..5959f10 --- /dev/null +++ b/src/mem.rs @@ -0,0 +1,226 @@ +use std::sync::{ + Arc, + atomic::{AtomicU8, AtomicU16, AtomicU32, AtomicU64, Ordering::Relaxed}, +}; + +use memmap2::MmapMut; + +use crate::consts::{Byte, DWord, HWord, Word}; + +type PageNum = usize; + +const PAGE_SIZE: usize = 4096; + +#[derive(Clone)] +pub struct MemConfig { + ram: Arc, + ram_start: PageNum, + devices: Box<[DeviceEntry]>, +} + +impl MemConfig { + pub fn find_device_by_page(&self, page: PageNum) -> Option> { + for entry in self.devices.iter() { + if page_in_range(page, entry.base, entry.size) { + return Some(entry.interface.clone()); + } + } + + None + } + + pub fn read_dword(&self, page: PageNum, offset: u16) -> Result { + if page_in_range(page, self.ram_start, self.ram.pages) { + self.ram.read_dword(page, offset) + } else { + self.find_device_by_page(page) + .ok_or(MemAccessFault)? + .read_dword(page, offset) + } + } +} + +fn page_in_range(page: PageNum, start: PageNum, pages: PageNum) -> bool { + page >= start && page - start < pages +} + +pub struct Ram { + buf: MmapMut, + pages: PageNum, +} + +#[cfg(target_endian = "big")] +compile_error!("Current RAM implementation requires a little-endian host."); + +impl Ram { + pub fn try_new(pages: PageNum) -> Result { + Ok(Self { + buf: MmapMut::map_anon(pages * PAGE_SIZE)?, + pages, + }) + } + + pub fn buf_mut(&mut self) -> &mut [u8] { + self.buf.as_mut() + } + + pub fn pages(&self) -> PageNum { + self.pages + } + + /// # Safety + /// Safe if T has a size divisible by page size (4kb) (or is known to have a size divisible by the full ram size) and you know that the RAM is made up of valid naturally aligned values of T + #[inline] + pub unsafe fn buf_transmuted(&self) -> &[T] { + debug_assert!(self.buf.len().is_multiple_of(std::mem::size_of::())); + unsafe { + std::slice::from_raw_parts( + self.buf.as_ptr() as *const T, + self.buf.len() / std::mem::size_of::(), + ) + } + } + + #[inline] + pub fn buf_atomic(&self) -> &[AtomicU8] { + unsafe { std::slice::from_raw_parts(self.buf.as_ptr() as *const AtomicU8, self.buf.len()) } + } + + #[inline] + pub fn read_dword(&self, page: PageNum, offset: u16) -> Result { + debug_assert!(((offset * 8) as usize) < PAGE_SIZE); + let index = page * (PAGE_SIZE / 8) + (offset as usize); + Ok(unsafe { + self.buf_transmuted::() + .get(index) + .ok_or(MemAccessFault) + }? + .load(Relaxed)) + } + #[inline] + pub fn read_word(&self, page: PageNum, offset: u16) -> Result { + debug_assert!(((offset * 4) as usize) < PAGE_SIZE); + let index = page * (PAGE_SIZE / 4) + (offset as usize); + Ok(unsafe { + self.buf_transmuted::() + .get(index) + .ok_or(MemAccessFault) + }? + .load(Relaxed)) + } + #[inline] + pub fn read_hword(&self, page: PageNum, offset: u16) -> Result { + debug_assert!(((offset * 2) as usize) < PAGE_SIZE); + let index = page * (PAGE_SIZE / 2) + (offset as usize); + Ok(unsafe { + self.buf_transmuted::() + .get(index) + .ok_or(MemAccessFault) + }? + .load(Relaxed)) + } + #[inline] + pub fn read_byte(&self, page: PageNum, offset: u16) -> Result { + debug_assert!((offset as usize) < PAGE_SIZE); + let index = page * PAGE_SIZE + (offset as usize); + Ok(self + .buf_atomic() + .get(index) + .ok_or(MemAccessFault)? + .load(Relaxed)) + } + + #[inline] + pub fn write_dword( + &self, + page: PageNum, + offset: u16, + value: DWord, + ) -> Result<(), MemAccessFault> { + debug_assert!(((offset * 8) as usize) < PAGE_SIZE); + let index = page * (PAGE_SIZE / 8) + (offset as usize); + unsafe { + self.buf_transmuted::() + .get(index) + .ok_or(MemAccessFault) + }? + .store(value, Relaxed); + Ok(()) + } + #[inline] + pub fn write_word( + &self, + page: PageNum, + offset: u16, + value: Word, + ) -> Result<(), MemAccessFault> { + debug_assert!(((offset * 4) as usize) < PAGE_SIZE); + let index = page * (PAGE_SIZE / 4) + (offset as usize); + unsafe { + self.buf_transmuted::() + .get(index) + .ok_or(MemAccessFault) + }? + .store(value, Relaxed); + Ok(()) + } + #[inline] + pub fn write_hword( + &self, + page: PageNum, + offset: u16, + value: HWord, + ) -> Result<(), MemAccessFault> { + debug_assert!(((offset * 2) as usize) < PAGE_SIZE); + let index = page * (PAGE_SIZE / 2) + (offset as usize); + unsafe { + self.buf_transmuted::() + .get(index) + .ok_or(MemAccessFault) + }? + .store(value, Relaxed); + Ok(()) + } + #[inline] + pub fn write_byte( + &self, + page: PageNum, + offset: u16, + value: Byte, + ) -> Result<(), MemAccessFault> { + debug_assert!((offset as usize) < PAGE_SIZE); + let index = page * PAGE_SIZE + (offset as usize); + self.buf_atomic() + .get(index) + .ok_or(MemAccessFault)? + .store(value, Relaxed); + Ok(()) + } +} + +#[derive(Clone)] +pub struct DeviceEntry { + base: PageNum, + size: PageNum, + interface: Arc, +} + +pub trait MemDeviceInterface { + fn write_dword(&self, page: PageNum, offset: u16, value: DWord) -> Result<(), MemAccessFault>; + fn write_word(&self, page: PageNum, offset: u16, value: Word) -> Result<(), MemAccessFault>; + fn write_hword(&self, page: PageNum, offset: u16, value: HWord) -> Result<(), MemAccessFault>; + fn write_byte(&self, page: PageNum, offset: u16, value: Byte) -> Result<(), MemAccessFault>; + + fn read_dword(&self, page: PageNum, offset: u16) -> Result; + fn read_word(&self, page: PageNum, offset: u16) -> Result; + fn read_hword(&self, page: PageNum, offset: u16) -> Result; + fn read_byte(&self, page: PageNum, offset: u16) -> Result; + + fn get_atomic_word(&self, page: PageNum, offset: u16) -> Result<&AtomicU32, MemAccessFault>; + fn get_atomic_dword(&self, page: PageNum, offset: u16) -> Result<&AtomicU64, MemAccessFault>; +} + +/// Error that means something has gone wrong accessing memory +/// Examples are: Accessing unmapped memory, accessing an MMIO register at the wrong size +#[derive(Debug)] +pub struct MemAccessFault;