Files
RedBear-OS/src/scheme/memory.rs
T
2024-01-17 13:52:01 -07:00

270 lines
8.3 KiB
Rust

use core::num::NonZeroUsize;
use alloc::{sync::Arc, vec::Vec};
use rmm::PhysicalAddress;
use spin::RwLock;
use crate::{
context::memory::{handle_notify_files, AddrSpace, Grant, PageSpan},
memory::{free_frames, used_frames, Frame, PAGE_SIZE},
paging::VirtualAddress,
};
use crate::paging::entry::EntryFlags;
use crate::syscall::{
data::{Map, StatVfs},
error::*,
flag::MapFlags,
usercopy::UserSliceWo,
};
use super::{CallerCtx, KernelScheme, OpenResult};
pub struct MemoryScheme;
// TODO: Use crate that autogenerates conversion functions.
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
enum HandleTy {
Allocated = 0,
PhysBorrow = 1,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MemoryType {
Writeback = 0,
Uncacheable = 1,
WriteCombining = 2,
DeviceMemory = 3,
}
bitflags! {
struct HandleFlags: u16 {
// TODO: below 32 bits?
const PHYS_CONTIGUOUS = 1;
}
}
fn from_raw(raw: u32) -> Option<(HandleTy, MemoryType, HandleFlags)> {
Some((
match raw & 0xFF {
0 => HandleTy::Allocated,
1 => HandleTy::PhysBorrow,
_ => return None,
},
match (raw >> 8) & 0xFF {
0 => MemoryType::Writeback,
1 => MemoryType::Uncacheable,
2 => MemoryType::WriteCombining,
3 => MemoryType::DeviceMemory,
_ => return None,
},
HandleFlags::from_bits_truncate((raw >> 16) as u16),
))
}
impl MemoryScheme {
pub fn fmap_anonymous(
addr_space: &Arc<RwLock<AddrSpace>>,
map: &Map,
is_phys_contiguous: bool,
) -> Result<usize> {
let span = PageSpan::validate_nonempty(VirtualAddress::new(map.address), map.size)
.ok_or(Error::new(EINVAL))?;
let page_count = NonZeroUsize::new(span.count).ok_or(Error::new(EINVAL))?;
let mut notify_files = Vec::new();
if is_phys_contiguous && map.flags.contains(MapFlags::MAP_SHARED) {
// TODO: Should this be supported?
return Err(Error::new(EOPNOTSUPP));
}
let page = addr_space.write().mmap(
(map.address != 0).then_some(span.base),
page_count,
map.flags,
&mut notify_files,
|dst_page, flags, mapper, flusher| {
let span = PageSpan::new(dst_page, page_count.get());
if is_phys_contiguous {
Ok(Grant::zeroed_phys_contiguous(span, flags, mapper, flusher)?)
} else {
Ok(Grant::zeroed(
span,
flags,
mapper,
flusher,
map.flags.contains(MapFlags::MAP_SHARED),
)?)
}
},
)?;
handle_notify_files(notify_files);
Ok(page.start_address().data())
}
pub fn physmap(
physical_address: usize,
size: usize,
flags: MapFlags,
memory_type: MemoryType,
) -> Result<usize> {
// TODO: Check physical_address against the real MAXPHYADDR.
let end = 1 << 52;
if (physical_address.saturating_add(size) as u64) > end || physical_address % PAGE_SIZE != 0
{
return Err(Error::new(EINVAL));
}
if size % PAGE_SIZE != 0 {
log::warn!(
"physmap size {} is not multiple of PAGE_SIZE {}",
size,
PAGE_SIZE
);
return Err(Error::new(EINVAL));
}
let page_count = NonZeroUsize::new(size.div_ceil(PAGE_SIZE)).ok_or(Error::new(EINVAL))?;
AddrSpace::current()?
.write()
.mmap_anywhere(
page_count,
flags,
|dst_page, mut page_flags, dst_mapper, dst_flusher| {
match memory_type {
// Default
MemoryType::Writeback => (),
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] // TODO: AARCH64
MemoryType::WriteCombining => {
page_flags = page_flags.custom_flag(EntryFlags::HUGE_PAGE.bits(), true)
}
MemoryType::Uncacheable => {
page_flags = page_flags.custom_flag(EntryFlags::NO_CACHE.bits(), true)
}
// MemoryType::DeviceMemory doesn't exist on x86 && x86_64, which instead support
// uncacheable, write-combining, write-through, write-protect, and write-back.
#[cfg(target_arch = "aarch64")]
MemoryType::DeviceMemory => {
page_flags = page_flags.custom_flag(EntryFlags::DEV_MEM.bits(), true)
}
_ => (),
}
Grant::physmap(
Frame::containing_address(PhysicalAddress::new(physical_address)),
PageSpan::new(dst_page, page_count.get()),
page_flags,
dst_mapper,
dst_flusher,
)
},
)
.map(|page| page.start_address().data())
}
}
impl KernelScheme for MemoryScheme {
fn kopen(&self, path: &str, _flags: usize, ctx: CallerCtx) -> Result<OpenResult> {
if path.len() > 64 {
return Err(Error::new(ENOENT));
}
let path = path.trim_start_matches('/');
let (before_memty, memty_str) = path.split_once('@').unwrap_or((path, ""));
let (before_ty, type_str) = memty_str.split_once('?').unwrap_or((memty_str, ""));
let handle_ty = match before_memty {
"" | "zeroed" => HandleTy::Allocated,
"physical" => HandleTy::PhysBorrow,
_ => return Err(Error::new(ENOENT)),
};
let mem_ty = match before_ty {
"" | "wb" => MemoryType::Writeback,
"wc" => MemoryType::WriteCombining,
"uc" => MemoryType::Uncacheable,
"dev" => MemoryType::DeviceMemory,
_ => return Err(Error::new(ENOENT)),
};
let flags = type_str
.split(',')
.filter_map(|ty_str| match ty_str {
//"32" => HandleFlags::BELOW_4G,
"phys_contiguous" => Some(Some(HandleFlags::PHYS_CONTIGUOUS)),
"" => None,
_ => Some(None),
})
.collect::<Option<HandleFlags>>()
.ok_or(Error::new(ENOENT))?;
// TODO: Support arches with other default memory types?
if ctx.uid != 0
&& (!flags.is_empty()
|| !matches!(
(handle_ty, mem_ty),
(HandleTy::Allocated, MemoryType::Writeback)
))
{
return Err(Error::new(EACCES));
}
Ok(OpenResult::SchemeLocal(
(handle_ty as usize) | ((mem_ty as usize) << 8) | (usize::from(flags.bits()) << 16),
))
}
fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
Ok(0)
}
fn close(&self, _id: usize) -> Result<()> {
Ok(())
}
fn kfmap(
&self,
id: usize,
addr_space: &Arc<RwLock<AddrSpace>>,
map: &Map,
_consume: bool,
) -> Result<usize> {
let (handle_ty, mem_ty, flags) = u32::try_from(id)
.ok()
.and_then(from_raw)
.ok_or(Error::new(EBADF))?;
match handle_ty {
HandleTy::Allocated => Self::fmap_anonymous(
addr_space,
map,
flags.contains(HandleFlags::PHYS_CONTIGUOUS),
),
HandleTy::PhysBorrow => Self::physmap(map.offset, map.size, map.flags, mem_ty),
}
}
fn kfstatvfs(&self, _file: usize, dst: UserSliceWo) -> Result<()> {
let used = used_frames() as u64;
let free = free_frames() as u64;
let stat = StatVfs {
f_bsize: PAGE_SIZE.try_into().map_err(|_| Error::new(EOVERFLOW))?,
f_blocks: used + free,
f_bfree: free,
f_bavail: free,
};
dst.copy_exactly(&stat)?;
Ok(())
}
}