diff --git a/crate/memory/src/cow.rs b/crate/memory/src/cow.rs index 2093e59..aa77c1d 100644 --- a/crate/memory/src/cow.rs +++ b/crate/memory/src/cow.rs @@ -78,12 +78,12 @@ impl CowExt { } use core::mem::uninitialized; let mut temp_data: [u8; PAGE_SIZE] = unsafe { uninitialized() }; - self.read_page(addr, &mut temp_data[..]); + temp_data[..].copy_from_slice(self.get_page_slice_mut(addr)); self.unmap_shared(addr); self.map(addr, alloc_frame()); - self.write_page(addr, &temp_data[..]); + self.get_page_slice_mut(addr).copy_from_slice(&temp_data[..]); true } } diff --git a/crate/memory/src/lib.rs b/crate/memory/src/lib.rs index e75ca48..9f98959 100644 --- a/crate/memory/src/lib.rs +++ b/crate/memory/src/lib.rs @@ -5,7 +5,7 @@ extern crate alloc; pub mod paging; pub mod cow; -//pub mod swap; +pub mod swap; type VirtAddr = usize; type PhysAddr = usize; diff --git a/crate/memory/src/paging/mock_page_table.rs b/crate/memory/src/paging/mock_page_table.rs index fbf4f44..fcba287 100644 --- a/crate/memory/src/paging/mock_page_table.rs +++ b/crate/memory/src/paging/mock_page_table.rs @@ -19,6 +19,7 @@ pub struct MockEntry { dirty: bool, writable_shared: bool, readonly_shared: bool, + swapped: bool, } impl Entry for MockEntry { @@ -32,7 +33,7 @@ impl Entry for MockEntry { fn set_writable(&mut self, value: bool) { self.writable = value; } fn set_present(&mut self, value: bool) { self.present = value; } fn target(&self) -> usize { self.target } - + fn set_target(&mut self, target: usize) { self.target = target; } fn writable_shared(&self) -> bool { self.writable_shared } fn readonly_shared(&self) -> bool { self.readonly_shared } fn set_shared(&mut self, writable: bool) { @@ -43,7 +44,8 @@ impl Entry for MockEntry { self.writable_shared = false; self.readonly_shared = false; } - + fn swapped(&self) -> bool { self.swapped } + fn set_swapped(&mut self, value: bool) { self.swapped = value; } fn user(&self) -> bool { unimplemented!() } fn set_user(&mut self, value: bool) { unimplemented!() } fn execute(&self) -> bool { unimplemented!() } @@ -71,15 +73,11 @@ impl PageTable for MockPageTable { fn get_entry(&mut self, addr: VirtAddr) -> &mut ::Entry { &mut self.entries[addr / PAGE_SIZE] } - fn read_page(&mut self, addr: usize, data: &mut [u8]) { + fn get_page_slice_mut<'a,'b>(&'a mut self, addr: VirtAddr) -> &'b mut [u8] { self._read(addr); let pa = self.translate(addr) & !(PAGE_SIZE - 1); - data.copy_from_slice(&self.data[pa..pa + PAGE_SIZE]); - } - fn write_page(&mut self, addr: usize, data: &[u8]) { - self._write(addr); - let pa = self.translate(addr) & !(PAGE_SIZE - 1); - self.data[pa..pa + PAGE_SIZE].copy_from_slice(data); + let data = unsafe{ &mut *(&mut self.data as *mut [u8; PAGE_SIZE * PAGE_COUNT])}; + &mut data[pa..pa + PAGE_SIZE] } fn read(&mut self, addr: usize) -> u8 { self._read(addr); diff --git a/crate/memory/src/paging/mod.rs b/crate/memory/src/paging/mod.rs index 8356f97..0a3d4c0 100644 --- a/crate/memory/src/paging/mod.rs +++ b/crate/memory/src/paging/mod.rs @@ -15,8 +15,7 @@ pub trait PageTable { fn unmap(&mut self, addr: VirtAddr); fn get_entry(&mut self, addr: VirtAddr) -> &mut Self::Entry; // For testing with mock - fn read_page(&mut self, addr: VirtAddr, data: &mut [u8]); - fn write_page(&mut self, addr: VirtAddr, data: &[u8]); + fn get_page_slice_mut<'a,'b>(&'a mut self, addr: VirtAddr) -> &'b mut [u8]; fn read(&mut self, addr: VirtAddr) -> u8; fn write(&mut self, addr: VirtAddr, data: u8); } @@ -42,6 +41,7 @@ pub trait Entry { fn set_present(&mut self, value: bool); fn target(&self) -> PhysAddr; + fn set_target(&mut self, target: PhysAddr); // For Copy-on-write extension fn writable_shared(&self) -> bool; @@ -49,6 +49,10 @@ pub trait Entry { fn set_shared(&mut self, writable: bool); fn clear_shared(&mut self); + // For Swap extension + fn swapped(&self) -> bool; + fn set_swapped(&mut self, value: bool); + fn user(&self) -> bool; fn set_user(&mut self, value: bool); fn execute(&self) -> bool; diff --git a/crate/memory/src/swap/mock_swapper.rs b/crate/memory/src/swap/mock_swapper.rs index 8b1bbbc..c6b323c 100644 --- a/crate/memory/src/swap/mock_swapper.rs +++ b/crate/memory/src/swap/mock_swapper.rs @@ -1,27 +1,35 @@ use super::Swapper; use alloc::btree_map::BTreeMap; +use core::mem::uninitialized; +const PAGE_SIZE: usize = 4096; + +#[derive(Default)] pub struct MockSwapper { - map: BTreeMap, + map: BTreeMap, } impl Swapper for MockSwapper { - fn swap_out(&mut self, data: &[u8; 4096]) -> Result { + fn swap_out(&mut self, data: &[u8]) -> Result { let id = self.alloc_id(); - self.map.insert(id, data.clone()); + let mut slice: [u8; PAGE_SIZE] = unsafe{ uninitialized() }; + slice.copy_from_slice(data); + self.map.insert(id, slice); Ok(id) } - fn swap_update(&mut self, token: usize, data: &[u8; 4096]) -> Result<(), ()> { + fn swap_update(&mut self, token: usize, data: &[u8]) -> Result<(), ()> { if !self.map.contains_key(&token) { return Err(()); } - self.map.insert(token, data.clone()); + let mut slice: [u8; PAGE_SIZE] = unsafe{ uninitialized() }; + slice.copy_from_slice(data); + self.map.insert(token, slice); Ok(()) } - fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]) -> Result<(), ()> { + fn swap_in(&mut self, token: usize, data: &mut [u8]) -> Result<(), ()> { match self.map.remove(&token) { - Some(d) => *data = d, + Some(d) => data.copy_from_slice(d.as_ref()), None => return Err(()), } Ok(()) @@ -29,9 +37,6 @@ impl Swapper for MockSwapper { } 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() } @@ -40,7 +45,6 @@ impl MockSwapper { #[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()) { @@ -50,7 +54,7 @@ mod test { #[test] fn swap_out_in() { - let mut swapper = MockSwapper::new(); + let mut swapper = MockSwapper::default(); let mut data: [u8; 4096] = unsafe{ uninitialized() }; let data1: [u8; 4096] = unsafe{ uninitialized() }; let token = swapper.swap_out(&data1).unwrap(); @@ -60,7 +64,7 @@ mod test { #[test] fn swap_update() { - let mut swapper = MockSwapper::new(); + let mut swapper = MockSwapper::default(); let mut data: [u8; 4096] = unsafe{ uninitialized() }; let data1: [u8; 4096] = unsafe{ uninitialized() }; let data2: [u8; 4096] = unsafe{ uninitialized() }; @@ -72,7 +76,7 @@ mod test { #[test] fn invalid_token() { - let mut swapper = MockSwapper::new(); + let mut swapper = MockSwapper::default(); let mut data: [u8; 4096] = unsafe{ uninitialized() }; assert_eq!(swapper.swap_in(0, &mut data), Err(())); } diff --git a/crate/memory/src/swap/mod.rs b/crate/memory/src/swap/mod.rs index 292e425..aca78b2 100644 --- a/crate/memory/src/swap/mod.rs +++ b/crate/memory/src/swap/mod.rs @@ -1,13 +1,16 @@ -pub use self::fifo::FifoSwapManager; -pub use self::enhanced_clock::EnhancedClockSwapManager; - use super::*; -use super::paging::PageTable; +use super::paging::*; +use core::ops::{Deref, DerefMut}; + +//pub use self::fifo::FifoSwapManager; +//pub use self::enhanced_clock::EnhancedClockSwapManager; -mod fifo; -mod enhanced_clock; +//mod fifo; +//mod enhanced_clock; +#[cfg(test)] mod mock_swapper; +/// Manage all swappable pages, decide which to swap out pub trait SwapManager { /// Called when tick interrupt occured fn tick(&mut self); @@ -20,11 +23,99 @@ pub trait SwapManager { } pub trait Swapper { - fn swap_out(&mut self, data: &[u8; 4096]) -> Result; - fn swap_update(&mut self, token: usize, data: &[u8; 4096]) -> Result<(), ()>; - fn swap_in(&mut self, token: usize, data: &mut [u8; 4096]) -> Result<(), ()>; + /// Allocate space on device and write data to it. + /// Return a token indicating the location. + fn swap_out(&mut self, data: &[u8]) -> Result; + /// Update data on device. + fn swap_update(&mut self, token: usize, data: &[u8]) -> Result<(), ()>; + /// Recover data from device and deallocate the space. + fn swap_in(&mut self, token: usize, data: &mut [u8]) -> Result<(), ()>; +} + +/// Wrapper for page table, supporting swap functions +struct SwapExt { + page_table: T, + swap_manager: M, + swapper: S, +} + +impl SwapExt { + pub fn new(page_table: T, swap_manager: M, swapper: S) -> Self { + SwapExt { + page_table, + swap_manager, + swapper, + } + } + /// Swap out any one of the swapped pages, return the released PhysAddr. + pub fn swap_out_any(&mut self) -> Result { + match self.swap_manager.pop() { + None => Err(SwapError::NoSwapped), + Some(addr) => self.swap_out(addr), + } + } + /// Swap out page of `addr`, return the origin map target. + fn swap_out(&mut self, addr: VirtAddr) -> Result { + let data = self.page_table.get_page_slice_mut(addr); + let entry = self.page_table.get_entry(addr); + if entry.swapped() { + return Err(SwapError::AlreadySwapped); + } + let token = self.swapper.swap_out(data).map_err(|_| SwapError::IOError)?; + let target = entry.target(); + entry.set_target(token * PAGE_SIZE); + entry.set_swapped(true); + entry.set_present(false); + entry.update(); + Ok(target) + } + /// Map page of `addr` to `target`, then swap in the data. + fn swap_in(&mut self, addr: VirtAddr, target: PhysAddr) -> Result<(), SwapError> { + let token = { + let entry = self.page_table.get_entry(addr); + if !entry.swapped() { + return Err(SwapError::NotSwapped); + } + let token = entry.target() / PAGE_SIZE; + entry.set_target(target); + entry.set_swapped(false); + entry.set_present(true); + entry.update(); + token + }; + let data = self.page_table.get_page_slice_mut(addr); + self.swapper.swap_in(token, data).map_err(|_| SwapError::IOError)?; + Ok(()) + } + pub fn page_fault_handler(&mut self, addr: VirtAddr, alloc_frame: impl FnOnce() -> PhysAddr) -> bool { + { + let entry = self.page_table.get_entry(addr); + if !entry.swapped() { + return false; + } + } + self.swap_in(addr, alloc_frame()).ok().unwrap(); + true + } +} + +pub enum SwapError { + AlreadySwapped, + NotSwapped, + NoSwapped, + IOError, +} + +impl Deref for SwapExt { + type Target = T; + + fn deref(&self) -> &::Target { + &self.page_table + } } -pub trait SwappablePageTable: PageTable { - fn swap_out(&mut self, addr: VirtAddr) -> Result<(), ()>; +impl DerefMut for SwapExt { + fn deref_mut(&mut self) -> &mut ::Target { + &mut self.page_table + } } \ No newline at end of file diff --git a/src/arch/x86_64/paging/mod.rs b/src/arch/x86_64/paging/mod.rs index f99c34e..8a6701f 100644 --- a/src/arch/x86_64/paging/mod.rs +++ b/src/arch/x86_64/paging/mod.rs @@ -58,16 +58,9 @@ impl PageTable for ActivePageTable { unsafe { &mut *(entry_addr as *mut PageEntry) } } - fn read_page(&mut self, addr: usize, data: &mut [u8]) { + fn get_page_slice_mut<'a, 'b>(&'a mut self, addr: VirtAddr) -> &'b mut [u8] { use core::slice; - let mem = unsafe { slice::from_raw_parts((addr & !0xfffusize) as *const u8, 4096) }; - data.copy_from_slice(mem); - } - - fn write_page(&mut self, addr: usize, data: &[u8]) { - use core::slice; - let mem = unsafe { slice::from_raw_parts_mut((addr & !0xfffusize) as *mut u8, 4096) }; - mem.copy_from_slice(data); + unsafe { slice::from_raw_parts_mut((addr & !0xfffusize) as *mut u8, PAGE_SIZE) } } fn read(&mut self, addr: usize) -> u8 { @@ -119,6 +112,10 @@ impl Entry for PageEntry { fn set_writable(&mut self, value: bool) { self.as_flags().set(EF::WRITABLE, value); } fn set_present(&mut self, value: bool) { self.as_flags().set(EF::PRESENT, value); } fn target(&self) -> usize { self.0.addr().as_u64() as usize } + fn set_target(&mut self, target: usize) { + let flags = self.0.flags(); + self.0.set_addr(PhysAddr::new(target as u64), flags); + } fn writable_shared(&self) -> bool { self.0.flags().contains(EF::BIT_10) } fn readonly_shared(&self) -> bool { self.0.flags().contains(EF::BIT_9) } fn set_shared(&mut self, writable: bool) { @@ -127,6 +124,8 @@ impl Entry for PageEntry { flags.set(EF::BIT_9, !writable); } fn clear_shared(&mut self) { self.as_flags().remove(EF::BIT_9 | EF::BIT_10); } + fn swapped(&self) -> bool { self.0.flags().contains(EF::BIT_11) } + fn set_swapped(&mut self, value: bool) { self.as_flags().set(EF::BIT_11, value); } fn user(&self) -> bool { self.0.flags().contains(EF::USER_ACCESSIBLE) } fn set_user(&mut self, value: bool) { self.as_flags().set(EF::USER_ACCESSIBLE, value); diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 9b67d8b..bd884cf 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -14,7 +14,7 @@ mod memory_set; mod stack_allocator; mod address; -const PAGE_SIZE: usize = 1 << 12; +pub const PAGE_SIZE: usize = 1 << 12; lazy_static! { static ref FRAME_ALLOCATOR: Mutex = Mutex::new(BitAlloc64K::default());