commit
5707dfd00a
@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "memory"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["WangRunji <wangrunji0408@163.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
@ -0,0 +1,12 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![feature(alloc)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
pub mod physical;
|
||||||
|
pub mod paging;
|
||||||
|
pub mod memory_set;
|
||||||
|
pub mod swap;
|
||||||
|
|
||||||
|
type VirtAddr = usize;
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
@ -0,0 +1,89 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
type Addr = usize;
|
||||||
|
|
||||||
|
/// 一片连续内存空间,有相同的访问权限
|
||||||
|
/// 对应ucore中 `vma_struct`
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub struct MemoryArea {
|
||||||
|
pub start_addr: Addr,
|
||||||
|
pub end_addr: Addr,
|
||||||
|
pub flags: u32,
|
||||||
|
pub name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryArea {
|
||||||
|
pub fn contains(&self, addr: Addr) -> bool {
|
||||||
|
addr >= self.start_addr && addr < self.end_addr
|
||||||
|
}
|
||||||
|
fn is_overlap_with(&self, other: &MemoryArea) -> bool {
|
||||||
|
!(self.end_addr <= other.start_addr || self.start_addr >= other.end_addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 内存空间集合,包含若干段连续空间
|
||||||
|
/// 对应ucore中 `mm_struct`
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MemorySet {
|
||||||
|
areas: Vec<MemoryArea>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemorySet {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
MemorySet { areas: Vec::<MemoryArea>::new() }
|
||||||
|
}
|
||||||
|
pub fn find_area(&self, addr: Addr) -> Option<&MemoryArea> {
|
||||||
|
self.areas.iter().find(|area| area.contains(addr))
|
||||||
|
}
|
||||||
|
pub fn push(&mut self, area: MemoryArea) {
|
||||||
|
assert!(area.start_addr <= area.end_addr, "invalid memory area");
|
||||||
|
if self.areas.iter()
|
||||||
|
.find(|other| area.is_overlap_with(other))
|
||||||
|
.is_some() {
|
||||||
|
panic!("memory area overlap");
|
||||||
|
}
|
||||||
|
self.areas.push(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn push_and_find() {
|
||||||
|
let mut ms = MemorySet::new();
|
||||||
|
ms.push(MemoryArea {
|
||||||
|
start_addr: 0x0,
|
||||||
|
end_addr: 0x8,
|
||||||
|
flags: 0x0,
|
||||||
|
name: "code",
|
||||||
|
});
|
||||||
|
ms.push(MemoryArea {
|
||||||
|
start_addr: 0x8,
|
||||||
|
end_addr: 0x10,
|
||||||
|
flags: 0x1,
|
||||||
|
name: "data",
|
||||||
|
});
|
||||||
|
assert_eq!(ms.find_area(0x6).unwrap().name, "code");
|
||||||
|
assert_eq!(ms.find_area(0x11), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn push_overlap() {
|
||||||
|
let mut ms = MemorySet::new();
|
||||||
|
ms.push(MemoryArea {
|
||||||
|
start_addr: 0x0,
|
||||||
|
end_addr: 0x8,
|
||||||
|
flags: 0x0,
|
||||||
|
name: "code",
|
||||||
|
});
|
||||||
|
ms.push(MemoryArea {
|
||||||
|
start_addr: 0x4,
|
||||||
|
end_addr: 0x10,
|
||||||
|
flags: 0x1,
|
||||||
|
name: "data",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
pub use self::page_table::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
mod page_table;
|
@ -0,0 +1,130 @@
|
|||||||
|
use alloc::boxed::Box;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const PAGE_COUNT: usize = 16;
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
pub struct MockPageTable {
|
||||||
|
mapped: [bool; PAGE_COUNT],
|
||||||
|
accessed: [bool; PAGE_COUNT],
|
||||||
|
dirty: [bool; PAGE_COUNT],
|
||||||
|
data: [u8; PAGE_SIZE * PAGE_COUNT],
|
||||||
|
page_fault_handler: Option<PageFaultHandler>,
|
||||||
|
capacity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageFaultHandler = Box<FnMut(&mut MockPageTable, VirtAddr)>;
|
||||||
|
|
||||||
|
impl PageTable for MockPageTable {
|
||||||
|
fn accessed(&self, addr: VirtAddr) -> bool {
|
||||||
|
self.accessed[addr / PAGE_SIZE]
|
||||||
|
}
|
||||||
|
fn dirty(&self, addr: VirtAddr) -> bool {
|
||||||
|
self.dirty[addr / PAGE_SIZE]
|
||||||
|
}
|
||||||
|
fn clear_accessed(&mut self, addr: usize) {
|
||||||
|
self.accessed[addr / PAGE_SIZE] = false;
|
||||||
|
}
|
||||||
|
fn clear_dirty(&mut self, addr: usize) {
|
||||||
|
self.dirty[addr / PAGE_SIZE] = false;
|
||||||
|
}
|
||||||
|
/// Map a page, return false if no more space
|
||||||
|
fn map(&mut self, addr: VirtAddr) -> bool {
|
||||||
|
if self.mapped.iter().filter(|&&b| b).count() == self.capacity {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.mapped[addr / PAGE_SIZE] = true;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn unmap(&mut self, addr: VirtAddr) {
|
||||||
|
self.mapped[addr / PAGE_SIZE] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockPageTable {
|
||||||
|
pub fn new(capacity: usize) -> Self {
|
||||||
|
use core::mem::uninitialized;
|
||||||
|
MockPageTable {
|
||||||
|
mapped: [false; PAGE_COUNT],
|
||||||
|
accessed: [false; PAGE_COUNT],
|
||||||
|
dirty: [false; PAGE_COUNT],
|
||||||
|
data: unsafe{ uninitialized() },
|
||||||
|
page_fault_handler: None,
|
||||||
|
capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set_handler(&mut self, page_fault_handler: PageFaultHandler) {
|
||||||
|
self.page_fault_handler = Some(page_fault_handler);
|
||||||
|
}
|
||||||
|
fn trigger_page_fault_if_not_present(&mut self, addr: VirtAddr) {
|
||||||
|
let page_id = addr / PAGE_SIZE;
|
||||||
|
while !self.mapped[page_id] {
|
||||||
|
let self_mut = unsafe{ &mut *(self as *mut Self) };
|
||||||
|
(self.page_fault_handler.as_mut().unwrap())(self_mut, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Read memory, mark accessed, trigger page fault if not present
|
||||||
|
pub fn read(&mut self, addr: VirtAddr) -> u8 {
|
||||||
|
let page_id = addr / PAGE_SIZE;
|
||||||
|
self.trigger_page_fault_if_not_present(addr);
|
||||||
|
self.accessed[page_id] = true;
|
||||||
|
self.data[addr]
|
||||||
|
}
|
||||||
|
/// Write memory, mark accessed and dirty, trigger page fault if not present
|
||||||
|
pub fn write(&mut self, addr: VirtAddr, data: u8) {
|
||||||
|
let page_id = addr / PAGE_SIZE;
|
||||||
|
self.trigger_page_fault_if_not_present(addr);
|
||||||
|
self.accessed[page_id] = true;
|
||||||
|
self.dirty[page_id] = true;
|
||||||
|
self.data[addr] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use alloc::arc::Arc;
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let page_fault_count = Arc::new(RefCell::new(0usize));
|
||||||
|
|
||||||
|
let mut pt = MockPageTable::new(2);
|
||||||
|
pt.set_handler(Box::new({
|
||||||
|
let page_fault_count1 = page_fault_count.clone();
|
||||||
|
move |pt: &mut MockPageTable, addr: VirtAddr| {
|
||||||
|
*page_fault_count1.borrow_mut() += 1;
|
||||||
|
pt.map(addr);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
pt.map(0);
|
||||||
|
pt.read(0);
|
||||||
|
assert_eq!(*page_fault_count.borrow(), 0);
|
||||||
|
assert!(pt.accessed(0));
|
||||||
|
assert!(!pt.dirty(0));
|
||||||
|
|
||||||
|
pt.clear_accessed(0);
|
||||||
|
assert!(!pt.accessed(0));
|
||||||
|
|
||||||
|
pt.read(1);
|
||||||
|
assert_eq!(*page_fault_count.borrow(), 0);
|
||||||
|
assert!(pt.accessed(0));
|
||||||
|
|
||||||
|
pt.write(0x1000, 0xff);
|
||||||
|
assert_eq!(*page_fault_count.borrow(), 1);
|
||||||
|
assert!(pt.accessed(0x1000));
|
||||||
|
assert!(pt.dirty(0x1000));
|
||||||
|
assert_eq!(pt.read(0x1000), 0xff);
|
||||||
|
|
||||||
|
pt.clear_dirty(0x1000);
|
||||||
|
assert!(!pt.dirty(0x1000));
|
||||||
|
|
||||||
|
assert_eq!(pt.map(0x2000), false);
|
||||||
|
|
||||||
|
pt.unmap(0);
|
||||||
|
pt.read(0);
|
||||||
|
assert_eq!(*page_fault_count.borrow(), 2);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
use super::*;
|
||||||
|
pub use self::mock_page_table::MockPageTable;
|
||||||
|
|
||||||
|
mod mock_page_table;
|
||||||
|
|
||||||
|
pub trait PageTable {
|
||||||
|
fn accessed(&self, addr: VirtAddr) -> bool;
|
||||||
|
fn dirty(&self, addr: VirtAddr) -> bool;
|
||||||
|
fn clear_accessed(&mut self, addr: VirtAddr);
|
||||||
|
fn clear_dirty(&mut self, addr: VirtAddr);
|
||||||
|
fn map(&mut self, addr: VirtAddr) -> bool;
|
||||||
|
fn unmap(&mut self, addr: VirtAddr);
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Frame {
|
||||||
|
number: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Frame {
|
||||||
|
pub fn containing_address(address: PhysAddr) -> Frame {
|
||||||
|
Frame{ number: address.get() as usize / PAGE_SIZE }
|
||||||
|
}
|
||||||
|
//TODO: Set private
|
||||||
|
pub fn start_address(&self) -> PhysAddr {
|
||||||
|
PhysAddr::new((self.number * PAGE_SIZE) as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone(&self) -> Frame {
|
||||||
|
Frame { number: self.number }
|
||||||
|
}
|
||||||
|
//TODO: Set private
|
||||||
|
// pub fn range_inclusive(start: Frame, end: Frame) -> FrameIter {
|
||||||
|
// FrameIter {
|
||||||
|
// start: start,
|
||||||
|
// end: end,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Frame {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
panic!("frame must be deallocate");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait FrameAllocator {
|
||||||
|
fn allocate_frame(&mut self) -> Option<Frame>;
|
||||||
|
fn deallocate_frame(&mut self, frame: Frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MemoryArea {
|
||||||
|
fn begin(&self) -> PhysAddr;
|
||||||
|
fn end(&self) -> PhysAddr;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
pub use self::physaddr::PhysAddr;
|
||||||
|
pub use self::frame::Frame;
|
||||||
|
pub use self::frame_allocator::FrameAllocator;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
mod frame;
|
||||||
|
mod physaddr;
|
||||||
|
mod frame_allocator;
|
@ -0,0 +1,50 @@
|
|||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
/// Represents a physical memory address
|
||||||
|
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct PhysAddr(u64);
|
||||||
|
|
||||||
|
impl PhysAddr {
|
||||||
|
pub fn new(addr: u64) -> PhysAddr {
|
||||||
|
PhysAddr(addr)
|
||||||
|
}
|
||||||
|
pub fn get(&self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for PhysAddr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:#x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Binary for PhysAddr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PhysAddr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::LowerHex for PhysAddr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Octal for PhysAddr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::UpperHex for PhysAddr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
use alloc::vec_deque::VecDeque;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct EnhancedClockSwapManager<T: 'static + SwappablePageTable> {
|
||||||
|
page_table: &'static mut T,
|
||||||
|
clock_ptr: usize,
|
||||||
|
deque: VecDeque<VirtAddr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static + SwappablePageTable> SwapManager for EnhancedClockSwapManager<T> {
|
||||||
|
fn tick(&mut self) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, addr: usize) {
|
||||||
|
let pos = if self.clock_ptr == 0 {self.deque.len()} else {self.clock_ptr};
|
||||||
|
self.deque.insert(pos, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(&mut self, addr: usize) {
|
||||||
|
let id = self.deque.iter()
|
||||||
|
.position(|&x| x == addr)
|
||||||
|
.expect("address not found");
|
||||||
|
if id < self.clock_ptr {
|
||||||
|
self.clock_ptr -= 1;
|
||||||
|
}
|
||||||
|
self.deque.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop(&mut self) -> Option<usize> {
|
||||||
|
loop {
|
||||||
|
let addr = self.deque[self.clock_ptr];
|
||||||
|
let accessed = self.page_table.accessed(addr);
|
||||||
|
let dirty = self.page_table.dirty(addr);
|
||||||
|
|
||||||
|
match (accessed, dirty) {
|
||||||
|
(true, _) => {
|
||||||
|
self.page_table.clear_accessed(addr);
|
||||||
|
|
||||||
|
},
|
||||||
|
(false, true) => {
|
||||||
|
if self.page_table.swap_out(addr).is_ok() {
|
||||||
|
self.page_table.clear_dirty(addr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return self.remove_current();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.move_next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T: 'static + SwappablePageTable> EnhancedClockSwapManager<T> {
|
||||||
|
pub fn new(page_table: &'static mut T) -> Self {
|
||||||
|
EnhancedClockSwapManager {
|
||||||
|
page_table,
|
||||||
|
clock_ptr: 0,
|
||||||
|
deque: VecDeque::<VirtAddr>::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn remove_current(&mut self) -> Option<VirtAddr> {
|
||||||
|
let addr = self.deque.remove(self.clock_ptr);
|
||||||
|
if self.clock_ptr == self.deque.len() {
|
||||||
|
self.clock_ptr = 0;
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
fn move_next(&mut self) {
|
||||||
|
self.clock_ptr += 1;
|
||||||
|
if self.clock_ptr == self.deque.len() {
|
||||||
|
self.clock_ptr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use alloc::{arc::Arc, boxed::Box};
|
||||||
|
use core::mem::uninitialized;
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use paging::MockPageTable;
|
||||||
|
|
||||||
|
impl SwappablePageTable for MockPageTable {
|
||||||
|
fn swap_out(&mut self, addr: usize) -> Result<(), ()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MemOp {
|
||||||
|
R(usize), W(usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
use self::MemOp::{R, W};
|
||||||
|
let page_fault_count = Arc::new(RefCell::new(0usize));
|
||||||
|
|
||||||
|
let mut pt = Box::new(MockPageTable::new(4));
|
||||||
|
let static_pt = unsafe{ &mut *(pt.as_mut() as *mut MockPageTable) };
|
||||||
|
pt.set_handler(Box::new({
|
||||||
|
let page_fault_count1 = page_fault_count.clone();
|
||||||
|
let mut clock = EnhancedClockSwapManager::new(static_pt);
|
||||||
|
|
||||||
|
move |pt: &mut MockPageTable, addr: VirtAddr| {
|
||||||
|
*page_fault_count1.borrow_mut() += 1;
|
||||||
|
|
||||||
|
if !pt.map(addr) { // is full?
|
||||||
|
pt.unmap(clock.pop().unwrap());
|
||||||
|
pt.map(addr);
|
||||||
|
}
|
||||||
|
clock.push(addr);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
let op_seq = [
|
||||||
|
R(0x1000), R(0x2000), R(0x3000), R(0x4000),
|
||||||
|
R(0x3000), W(0x1000), R(0x4000), W(0x2000), R(0x5000),
|
||||||
|
R(0x2000), W(0x1000), R(0x2000), R(0x3000), R(0x4000)];
|
||||||
|
let pgfault_count = [
|
||||||
|
1, 2, 3, 4,
|
||||||
|
4, 4, 4, 4, 5,
|
||||||
|
5, 5, 5, 6, 7];
|
||||||
|
for (op, &count) in op_seq.iter().zip(pgfault_count.iter()) {
|
||||||
|
match op {
|
||||||
|
R(addr) => {pt.read(*addr);},
|
||||||
|
W(addr) => pt.write(*addr, 0),
|
||||||
|
}
|
||||||
|
assert_eq!(*(*page_fault_count).borrow(), count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
use alloc::vec_deque::VecDeque;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct FifoSwapManager {
|
||||||
|
deque: VecDeque<VirtAddr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwapManager for FifoSwapManager {
|
||||||
|
fn tick(&mut self) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, addr: usize) {
|
||||||
|
self.deque.push_back(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(&mut self, addr: usize) {
|
||||||
|
let id = self.deque.iter()
|
||||||
|
.position(|&x| x == addr)
|
||||||
|
.expect("address not found");
|
||||||
|
self.deque.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop(&mut self) -> Option<VirtAddr> {
|
||||||
|
self.deque.pop_front()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FifoSwapManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
FifoSwapManager {
|
||||||
|
deque: VecDeque::<VirtAddr>::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use alloc::{arc::Arc, boxed::Box};
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use paging::MockPageTable;
|
||||||
|
|
||||||
|
enum MemOp {
|
||||||
|
R(usize), W(usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
use self::MemOp::{R, W};
|
||||||
|
let page_fault_count = Arc::new(RefCell::new(0usize));
|
||||||
|
|
||||||
|
let mut pt = MockPageTable::new(4);
|
||||||
|
pt.set_handler(Box::new({
|
||||||
|
let page_fault_count1 = page_fault_count.clone();
|
||||||
|
let mut fifo = FifoSwapManager::new();
|
||||||
|
|
||||||
|
move |pt: &mut MockPageTable, addr: VirtAddr| {
|
||||||
|
*page_fault_count1.borrow_mut() += 1;
|
||||||
|
|
||||||
|
if !pt.map(addr) { // is full?
|
||||||
|
pt.unmap(fifo.pop().unwrap());
|
||||||
|
pt.map(addr);
|
||||||
|
}
|
||||||
|
fifo.push(addr);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
let op_seq = [
|
||||||
|
R(0x1000), R(0x2000), R(0x3000), R(0x4000),
|
||||||
|
W(0x3000), W(0x1000), W(0x4000), W(0x2000), W(0x5000),
|
||||||
|
W(0x2000), W(0x1000), W(0x2000), W(0x3000), W(0x4000),
|
||||||
|
W(0x5000), R(0x1000), W(0x1000)];
|
||||||
|
let pgfault_count = [
|
||||||
|
1, 2, 3, 4,
|
||||||
|
4, 4, 4, 4, 5,
|
||||||
|
5, 6, 7, 8, 9,
|
||||||
|
10, 11, 11];
|
||||||
|
for (op, &count) in op_seq.iter().zip(pgfault_count.iter()) {
|
||||||
|
match op {
|
||||||
|
R(addr) => {pt.read(*addr);},
|
||||||
|
W(addr) => pt.write(*addr, 0),
|
||||||
|
}
|
||||||
|
assert_eq!(*(*page_fault_count).borrow(), count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
use super::Swapper;
|
||||||
|
use alloc::btree_map::BTreeMap;
|
||||||
|
|
||||||
|
pub struct MockSwapper {
|
||||||
|
map: BTreeMap<usize, [u8; 4096]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Swapper for MockSwapper {
|
||||||
|
fn swap_out(&mut self, data: &[u8; 4096]) -> Result<usize, ()> {
|
||||||
|
let id = self.alloc_id();
|
||||||
|
self.map.insert(id, data.clone());
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swap_update(&mut self, token: usize, data: &[u8; 4096]) -> Result<(), ()> {
|
||||||
|
if !self.map.contains_key(&token) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
self.map.insert(token, data.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]) -> Result<(), ()> {
|
||||||
|
match self.map.remove(&token) {
|
||||||
|
Some(d) => *data = d,
|
||||||
|
None => return Err(()),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockSwapper {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
MockSwapper {map: BTreeMap::new()}
|
||||||
|
}
|
||||||
|
fn alloc_id(&self) -> usize {
|
||||||
|
(0 .. 100usize).find(|i| !self.map.contains_key(i)).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use core::mem::uninitialized;
|
||||||
|
|
||||||
|
fn assert_data_eq(data1: &[u8; 4096], data2: &[u8; 4096]) {
|
||||||
|
for (&a, &b) in data2.iter().zip(data1.iter()) {
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn swap_out_in() {
|
||||||
|
let mut swapper = MockSwapper::new();
|
||||||
|
let mut data: [u8; 4096] = unsafe{ uninitialized() };
|
||||||
|
let data1: [u8; 4096] = unsafe{ uninitialized() };
|
||||||
|
let token = swapper.swap_out(&data1).unwrap();
|
||||||
|
swapper.swap_in(token, &mut data).unwrap();
|
||||||
|
assert_data_eq(&data, &data1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn swap_update() {
|
||||||
|
let mut swapper = MockSwapper::new();
|
||||||
|
let mut data: [u8; 4096] = unsafe{ uninitialized() };
|
||||||
|
let data1: [u8; 4096] = unsafe{ uninitialized() };
|
||||||
|
let data2: [u8; 4096] = unsafe{ uninitialized() };
|
||||||
|
let token = swapper.swap_out(&data1).unwrap();
|
||||||
|
swapper.swap_update(token, &data2).unwrap();
|
||||||
|
swapper.swap_in(token, &mut data).unwrap();
|
||||||
|
assert_data_eq(&data, &data2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_token() {
|
||||||
|
let mut swapper = MockSwapper::new();
|
||||||
|
let mut data: [u8; 4096] = unsafe{ uninitialized() };
|
||||||
|
assert_eq!(swapper.swap_in(0, &mut data), Err(()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
pub use self::fifo::FifoSwapManager;
|
||||||
|
pub use self::enhanced_clock::EnhancedClockSwapManager;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use super::paging::PageTable;
|
||||||
|
|
||||||
|
mod fifo;
|
||||||
|
mod enhanced_clock;
|
||||||
|
mod mock_swapper;
|
||||||
|
|
||||||
|
pub trait SwapManager {
|
||||||
|
/// Called when tick interrupt occured
|
||||||
|
fn tick(&mut self);
|
||||||
|
/// Called when map a swappable page into the memory
|
||||||
|
fn push(&mut self, addr: VirtAddr);
|
||||||
|
/// Called to delete the addr entry from the swap manager
|
||||||
|
fn remove(&mut self, addr: VirtAddr);
|
||||||
|
/// Try to swap out a page, return then victim
|
||||||
|
fn pop(&mut self) -> Option<VirtAddr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Swapper {
|
||||||
|
fn swap_out(&mut self, data: &[u8; 4096]) -> Result<usize, ()>;
|
||||||
|
fn swap_update(&mut self, token: usize, data: &[u8; 4096]) -> Result<(), ()>;
|
||||||
|
fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]) -> Result<(), ()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SwappablePageTable: PageTable {
|
||||||
|
fn swap_out(&mut self, addr: VirtAddr) -> Result<(), ()>;
|
||||||
|
}
|
@ -1,27 +1,27 @@
|
|||||||
use consts::{KERNEL_OFFSET, KERNEL_SIZE};
|
use consts::{KERNEL_OFFSET, KERNEL_SIZE};
|
||||||
pub use x86_64::{PhysicalAddress};
|
pub use x86_64::PhysicalAddress as PhysAddr;
|
||||||
pub type VirtualAddress = usize;
|
pub type VirtAddr = usize;
|
||||||
|
|
||||||
pub trait FromToVirtualAddress {
|
pub trait FromToVirtualAddress {
|
||||||
fn get(&self) -> usize;
|
fn get(&self) -> usize;
|
||||||
fn to_identity_virtual(&self) -> VirtualAddress;
|
fn to_identity_virtual(&self) -> VirtAddr;
|
||||||
fn to_kernel_virtual(&self) -> VirtualAddress;
|
fn to_kernel_virtual(&self) -> VirtAddr;
|
||||||
fn from_kernel_virtual(addr: VirtualAddress) -> Self;
|
fn from_kernel_virtual(addr: VirtAddr) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromToVirtualAddress for PhysicalAddress {
|
impl FromToVirtualAddress for PhysAddr {
|
||||||
fn get(&self) -> usize {
|
fn get(&self) -> usize {
|
||||||
self.0 as usize
|
self.0 as usize
|
||||||
}
|
}
|
||||||
fn to_identity_virtual(&self) -> VirtualAddress {
|
fn to_identity_virtual(&self) -> VirtAddr {
|
||||||
self.0 as usize
|
self.0 as usize
|
||||||
}
|
}
|
||||||
fn to_kernel_virtual(&self) -> VirtualAddress {
|
fn to_kernel_virtual(&self) -> VirtAddr {
|
||||||
assert!((self.0 as usize) < KERNEL_SIZE);
|
assert!((self.0 as usize) < KERNEL_SIZE);
|
||||||
self.0 as usize + KERNEL_OFFSET
|
self.0 as usize + KERNEL_OFFSET
|
||||||
}
|
}
|
||||||
fn from_kernel_virtual(addr: VirtualAddress) -> Self {
|
fn from_kernel_virtual(addr: VirtAddr) -> Self {
|
||||||
assert!(addr >= KERNEL_OFFSET && addr < KERNEL_OFFSET + KERNEL_SIZE);
|
assert!(addr >= KERNEL_OFFSET && addr < KERNEL_OFFSET + KERNEL_SIZE);
|
||||||
PhysicalAddress((addr - KERNEL_OFFSET) as u64)
|
PhysAddr((addr - KERNEL_OFFSET) as u64)
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in new issue