use std::fmt;
use std::ptr::NonNull;
use crate::common::PhysAddr;
#[derive(Debug)]
pub struct Ram {
bytes: ZeroedBytes,
base: u64,
}
impl Ram {
#[must_use]
pub fn new(base: u64, size: usize) -> Self {
Self { bytes: ZeroedBytes::new(size), base }
}
#[must_use]
pub const fn base(&self) -> u64 {
self.base
}
#[must_use]
pub const fn size(&self) -> u64 {
self.bytes.len as u64
}
#[must_use]
pub const fn contains(&self, addr: PhysAddr, len: u64) -> bool {
let addr = addr.val();
addr >= self.base && addr.saturating_add(len) <= self.base.saturating_add(self.size())
}
#[must_use]
pub const fn bytes(&self) -> &[u8] {
self.bytes.as_slice()
}
pub const fn bytes_mut(&mut self) -> &mut [u8] {
self.bytes.as_mut_slice()
}
#[must_use]
pub fn get(&self, addr: PhysAddr, len: usize) -> Option<&[u8]> {
let offset = self.offset(addr, len)?;
self.bytes.as_slice().get(offset..offset + len)
}
pub fn get_mut(&mut self, addr: PhysAddr, len: usize) -> Option<&mut [u8]> {
let offset = self.offset(addr, len)?;
self.bytes.as_mut_slice().get_mut(offset..offset + len)
}
fn offset(&self, addr: PhysAddr, len: usize) -> Option<usize> {
self.contains(addr, len as u64).then(|| (addr.val() - self.base) as usize)
}
}
struct ZeroedBytes {
ptr: NonNull<u8>,
len: usize,
}
impl fmt::Debug for ZeroedBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ZeroedBytes").field("len", &self.len).finish_non_exhaustive()
}
}
unsafe impl Send for ZeroedBytes {}
unsafe impl Sync for ZeroedBytes {}
impl ZeroedBytes {
fn new(len: usize) -> Self {
if len == 0 {
return Self { ptr: NonNull::dangling(), len };
}
Self { ptr: Self::allocate(len), len }
}
#[cfg(unix)]
fn allocate(len: usize) -> NonNull<u8> {
let raw = unsafe {
libc::mmap(
std::ptr::null_mut(),
len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
)
};
assert!(raw != libc::MAP_FAILED, "cannot map {len} bytes of RAM");
NonNull::new(raw.cast::<u8>()).unwrap_or(NonNull::dangling())
}
#[cfg(not(unix))]
fn allocate(len: usize) -> NonNull<u8> {
let raw = Box::into_raw(vec![0u8; len].into_boxed_slice());
NonNull::new(raw.cast::<u8>()).unwrap_or(NonNull::dangling())
}
const fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
const fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
}
impl Drop for ZeroedBytes {
fn drop(&mut self) {
if self.len == 0 {
return;
}
#[cfg(unix)]
let _ = unsafe { libc::munmap(self.ptr.as_ptr().cast(), self.len) };
#[cfg(not(unix))]
drop(unsafe {
Box::from_raw(std::ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), self.len))
});
}
}