osdev5/kernel/src/mem/phys/manager.rs

136 lines
4.5 KiB
Rust

use super::{PageInfo, PageUsage};
use crate::mem::{virtualize, PAGE_SIZE};
use crate::sync::IrqSafeSpinLock;
use core::mem;
use libsys::{error::Errno, mem::memcpy};
pub unsafe trait Manager {
fn alloc_page(&mut self, pu: PageUsage) -> Result<usize, Errno>;
fn alloc_contiguous_pages(&mut self, pu: PageUsage, count: usize) -> Result<usize, Errno>;
fn free_page(&mut self, page: usize) -> Result<(), Errno>;
fn copy_cow_page(&mut self, src: usize) -> Result<usize, Errno>;
fn fork_page(&mut self, src: usize) -> Result<usize, Errno>;
// TODO status()
}
pub struct SimpleManager {
pages: &'static mut [PageInfo],
base_index: usize,
}
impl SimpleManager {
pub(super) unsafe fn initialize(base: usize, at: usize, count: usize) -> Self {
let pages: &'static mut [PageInfo] =
core::slice::from_raw_parts_mut(virtualize(at) as *mut _, count);
// Initialize uninit pages
for entry in pages.iter_mut() {
mem::forget(mem::replace(
entry,
PageInfo {
refcount: 0,
usage: PageUsage::Reserved,
},
));
}
Self {
base_index: base / PAGE_SIZE,
pages,
}
}
pub(super) unsafe fn add_page(&mut self, addr: usize) {
let page = &mut self.pages[self.page_index(addr)];
assert!(page.refcount == 0 && page.usage == PageUsage::Reserved);
page.usage = PageUsage::Available;
}
fn page_index(&self, page: usize) -> usize {
page / PAGE_SIZE - self.base_index
}
fn alloc_single_index(&mut self, pu: PageUsage) -> Result<usize, Errno> {
for index in 0..self.pages.len() {
let page = &mut self.pages[index];
if page.usage == PageUsage::Available {
page.usage = pu;
page.refcount = 1;
return Ok(index);
}
}
Err(Errno::OutOfMemory)
}
}
unsafe impl Manager for SimpleManager {
fn alloc_page(&mut self, pu: PageUsage) -> Result<usize, Errno> {
self.alloc_single_index(pu)
.map(|r| (self.base_index + r) * PAGE_SIZE)
}
fn alloc_contiguous_pages(&mut self, pu: PageUsage, count: usize) -> Result<usize, Errno> {
'l0: for i in 0..self.pages.len() {
for j in 0..count {
if self.pages[i + j].usage != PageUsage::Available {
continue 'l0;
}
}
for j in 0..count {
let page = &mut self.pages[i + j];
assert!(page.usage == PageUsage::Available);
page.usage = pu;
page.refcount = 1;
}
return Ok((self.base_index + i) * PAGE_SIZE);
}
Err(Errno::OutOfMemory)
}
fn free_page(&mut self, addr: usize) -> Result<(), Errno> {
let index = self.page_index(addr);
let page = &mut self.pages[index];
assert!(page.usage != PageUsage::Reserved && page.usage != PageUsage::Available);
if page.refcount > 1 {
page.refcount -= 1;
} else {
assert_eq!(page.refcount, 1);
page.usage = PageUsage::Available;
page.refcount = 0;
}
Ok(())
}
fn copy_cow_page(&mut self, src: usize) -> Result<usize, Errno> {
let src_index = self.page_index(src);
let page = &mut self.pages[src_index];
let usage = page.usage;
if usage != PageUsage::UserPrivate {
panic!("CoW not available for non-UserPrivate pages: {:?}", usage);
}
if page.refcount > 1 {
page.refcount -= 1;
drop(page);
let dst_index = self.alloc_single_index(usage)?;
let dst = (self.base_index + dst_index) * PAGE_SIZE;
unsafe {
memcpy(virtualize(dst) as *mut u8, virtualize(src) as *mut u8, 4096);
}
Ok(dst)
} else {
assert_eq!(page.refcount, 1);
// No additional operations needed
Ok(src)
}
}
fn fork_page(&mut self, src: usize) -> Result<usize, Errno> {
let src_index = self.page_index(src);
let page = &mut self.pages[src_index];
let usage = page.usage;
if usage != PageUsage::UserPrivate {
todo!("Handle page types other than UserPrivate")
} else {
page.refcount += 1;
}
Ok(src)
}
}
pub(super) static MANAGER: IrqSafeSpinLock<Option<SimpleManager>> = IrqSafeSpinLock::new(None);