123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- use super::address::{PAddr, PFN};
- use crate::intrusive_list::Link;
- use crate::{container_of, prelude::*};
- use bitflags::bitflags;
- use core::sync::atomic::Ordering;
- use core::{ptr::NonNull, sync::atomic::AtomicU32};
- use lazy_static::lazy_static;
- const MAX_PAGE_ORDER: u32 = 10;
- const PAGE_ARRAY: *mut Page = 0xffffff8040000000 as *mut Page;
- pub(super) type PagePtr = Ptr<Page>;
- #[repr(transparent)]
- pub struct Ptr<T>(Option<NonNull<T>>);
- impl<T> Clone for Ptr<T> {
- fn clone(&self) -> Self {
- Self(self.0)
- }
- }
- impl<T> Copy for Ptr<T> {}
- impl<T> Ptr<T> {
- pub const fn new(ptr: Option<NonNull<T>>) -> Self {
- Self(ptr)
- }
- pub fn from_raw(ptr: *mut T) -> Self {
- Self::new(NonNull::new(ptr))
- }
- pub fn null() -> Self {
- Self::new(None)
- }
- pub fn is_none(&self) -> bool {
- self.0.is_none()
- }
- pub fn is_some(&self) -> bool {
- self.0.is_some()
- }
- pub fn as_ptr(&self) -> *mut T {
- self.0.unwrap().as_ptr()
- }
- pub fn as_ref<'a>(&self) -> &'a T {
- unsafe { &*self.as_ptr() }
- }
- pub fn as_mut<'a>(&self) -> &'a mut T {
- unsafe { &mut *self.as_ptr() }
- }
- }
- impl PagePtr {
- pub unsafe fn increase_refcount(&self) -> u32 {
- self.as_mut().increase_refcount()
- }
- pub unsafe fn decrease_refcount(&self) -> u32 {
- self.as_mut().decrease_refcount()
- }
- pub unsafe fn load_refcount(&self) -> u32 {
- self.as_ref().refcount.load(Ordering::Acquire)
- }
- fn get_order(&self) -> u32 {
- self.as_ref().order
- }
- pub fn is_valid(&self, order: u32) -> bool {
- self.is_some() && self.get_order() == order
- }
- fn offset(&self, count: usize) -> Self {
- match self.0 {
- Some(non_null_ptr) => {
- let new_raw_ptr = unsafe { non_null_ptr.as_ptr().add(count) };
- Self::from_raw(new_raw_ptr)
- }
- None => Self::null(),
- }
- }
- }
- impl Into<PFN> for PagePtr {
- fn into(self) -> PFN {
- unsafe { PFN::from(self.as_ptr().offset_from(PAGE_ARRAY) as usize) }
- }
- }
- impl From<PFN> for PagePtr {
- fn from(pfn: PFN) -> Self {
- unsafe { Self::from_raw(PAGE_ARRAY.add(pfn.0)) }
- }
- }
- bitflags! {
- // TODO: Use atomic
- struct PageFlags: usize {
- const PRESENT = 1 << 0;
- const LOCKED = 1 << 1;
- const BUDDY = 1 << 2;
- const SLAB = 1 << 3;
- const DIRTY = 1 << 4;
- const FREE = 1 << 5;
- }
- }
- pub(super) struct Page {
- // Now only used for free page links in the buddy system.
- // Can be used for LRU page swap in the future.
- link: Link,
- flags: PageFlags, // TODO: This should be atomic.
- /// # Safety
- /// This field is only used in buddy system, which is protected by the global lock.
- order: u32,
- refcount: AtomicU32,
- }
- struct FreeArea {
- free_list: Link,
- count: usize,
- }
- /// Safety: `Zone` is `Send` because the `PAGE_ARRAY` is shared between cores.
- unsafe impl Send for Zone {}
- // /// Safety: TODO
- // unsafe impl Sync for Zone {}
- struct Zone {
- free_areas: [FreeArea; MAX_PAGE_ORDER as usize + 1],
- }
- impl Page {
- fn set_flags(&mut self, flags: PageFlags) {
- self.flags.insert(flags);
- }
- fn remove_flags(&mut self, flags: PageFlags) {
- self.flags.remove(flags);
- }
- fn set_order(&mut self, order: u32) {
- self.order = order;
- }
- unsafe fn increase_refcount(&mut self) -> u32 {
- self.refcount.fetch_add(1, Ordering::Relaxed)
- }
- unsafe fn decrease_refcount(&mut self) -> u32 {
- self.refcount.fetch_sub(1, Ordering::AcqRel)
- }
- pub fn is_buddy(&self) -> bool {
- self.flags.contains(PageFlags::BUDDY)
- }
- #[allow(dead_code)]
- pub fn is_slab(&self) -> bool {
- self.flags.contains(PageFlags::SLAB)
- }
- pub fn is_present(&self) -> bool {
- self.flags.contains(PageFlags::PRESENT)
- }
- pub fn is_free(&self) -> bool {
- self.flags.contains(PageFlags::FREE)
- }
- }
- impl FreeArea {
- const fn new() -> Self {
- Self {
- free_list: Link::new(),
- count: 0,
- }
- }
- fn alloc_pages(&mut self) -> PagePtr {
- if let Some(pages_link) = self.free_list.next_mut() {
- assert_ne!(self.count, 0);
- let pages_ptr = unsafe { container_of!(pages_link, Page, link) };
- let pages_ptr = Ptr::from_raw(pages_ptr);
- self.count -= 1;
- pages_ptr.as_mut().remove_flags(PageFlags::FREE);
- pages_link.remove();
- pages_ptr
- } else {
- PagePtr::null()
- }
- }
- fn add_pages(&mut self, pages_ptr: PagePtr) {
- self.count += 1;
- pages_ptr.as_mut().set_flags(PageFlags::FREE);
- self.free_list.insert(&mut pages_ptr.as_mut().link)
- }
- fn del_pages(&mut self, pages_ptr: PagePtr) {
- assert!(self.count >= 1 && pages_ptr.as_ref().is_free());
- self.count -= 1;
- pages_ptr.as_mut().remove_flags(PageFlags::FREE);
- pages_ptr.as_mut().link.remove();
- }
- }
- impl Zone {
- const fn new() -> Self {
- Self {
- free_areas: [const { FreeArea::new() }; MAX_PAGE_ORDER as usize + 1],
- }
- }
- fn alloc_pages(&mut self, order: u32) -> PagePtr {
- for current_order in order..=MAX_PAGE_ORDER {
- let pages_ptr = self.free_areas[current_order as usize].alloc_pages();
- if pages_ptr.is_none() {
- continue;
- }
- unsafe {
- pages_ptr.as_mut().increase_refcount();
- }
- pages_ptr.as_mut().set_order(order);
- if current_order > order {
- self.expand(pages_ptr, current_order, order);
- }
- assert!(pages_ptr.as_ref().is_present() && !pages_ptr.as_ref().is_free());
- return pages_ptr;
- }
- PagePtr::new(None)
- }
- fn expand(&mut self, pages_ptr: PagePtr, order: u32, target_order: u32) {
- assert!(pages_ptr.is_some());
- let mut offset = 1 << order;
- for order in (target_order..order).rev() {
- offset >>= 1;
- let split_pages_ptr = pages_ptr.offset(offset);
- split_pages_ptr.as_mut().set_order(order);
- split_pages_ptr.as_mut().set_flags(PageFlags::BUDDY);
- self.free_areas[order as usize].add_pages(split_pages_ptr);
- }
- }
- fn free_pages(&mut self, mut pages_ptr: PagePtr, order: u32) {
- assert_eq!(unsafe { pages_ptr.load_refcount() }, 0);
- assert_eq!(pages_ptr.get_order(), order);
- let mut pfn: PFN = pages_ptr.into();
- let mut current_order = order;
- while current_order < MAX_PAGE_ORDER {
- let buddy_pfn = pfn.buddy_pfn(current_order);
- let buddy_pages_ptr = PagePtr::from(buddy_pfn);
- if !self.buddy_check(buddy_pages_ptr, current_order) {
- break;
- }
- pages_ptr.as_mut().remove_flags(PageFlags::BUDDY);
- buddy_pages_ptr.as_mut().remove_flags(PageFlags::BUDDY);
- self.free_areas[current_order as usize].del_pages(buddy_pages_ptr);
- pages_ptr = PagePtr::from(pfn.combined_pfn(buddy_pfn));
- pages_ptr.as_mut().set_flags(PageFlags::BUDDY);
- pfn = pfn.combined_pfn(buddy_pfn);
- current_order += 1;
- }
- pages_ptr.as_mut().set_order(current_order);
- self.free_areas[current_order as usize].add_pages(pages_ptr);
- }
- /// This function checks whether a page is free && is the buddy
- /// we can coalesce a page and its buddy if
- /// - the buddy is valid(present) &&
- /// - the buddy is right now in free_areas &&
- /// - a page and its buddy have the same order &&
- /// - a page and its buddy are in the same zone. // check when smp
- fn buddy_check(&self, pages_ptr: PagePtr, order: u32) -> bool {
- if !pages_ptr.as_ref().is_present() {
- return false;
- }
- if !(pages_ptr.as_ref().is_free()) {
- return false;
- }
- if pages_ptr.as_ref().order != order {
- return false;
- }
- assert_eq!(unsafe { pages_ptr.load_refcount() }, 0);
- true
- }
- /// Only used on buddy initialization
- fn create_pages(&mut self, start: usize, end: usize) {
- let mut start_pfn = PAddr::from(start).ceil_pfn();
- let end_pfn = PAddr::from(end).floor_pfn();
- while start_pfn < end_pfn {
- let mut order = usize::from(start_pfn).trailing_zeros().min(MAX_PAGE_ORDER);
- while start_pfn + order as usize > end_pfn {
- order -= 1;
- }
- let page_ptr: PagePtr = start_pfn.into();
- page_ptr.as_mut().set_flags(PageFlags::BUDDY);
- self.free_areas[order as usize].add_pages(page_ptr);
- start_pfn = start_pfn + (1 << order) as usize;
- }
- }
- }
- lazy_static! {
- static ref ZONE: Spin<Zone> = Spin::new(Zone::new());
- }
- pub(super) fn alloc_page() -> PagePtr {
- ZONE.lock().alloc_pages(0)
- }
- pub(super) fn alloc_pages(order: u32) -> PagePtr {
- ZONE.lock().alloc_pages(order)
- }
- pub(super) fn free_pages(page_ptr: PagePtr, order: u32) {
- ZONE.lock().free_pages(page_ptr, order)
- }
- #[no_mangle]
- pub extern "C" fn mark_present(start: usize, end: usize) {
- let mut start_pfn = PAddr::from(start).ceil_pfn();
- let end_pfn = PAddr::from(end).floor_pfn();
- while start_pfn < end_pfn {
- PagePtr::from(start_pfn)
- .as_mut()
- .set_flags(PageFlags::PRESENT);
- start_pfn = start_pfn + 1;
- }
- }
- #[no_mangle]
- pub extern "C" fn create_pages(start: usize, end: usize) {
- ZONE.lock().create_pages(start, end);
- }
- #[no_mangle]
- pub extern "C" fn page_to_pfn(page: *const Page) -> usize {
- unsafe { page.offset_from(PAGE_ARRAY) as usize }
- }
- #[no_mangle]
- pub extern "C" fn c_alloc_page() -> *const Page {
- ZONE.lock().alloc_pages(0).as_ptr() as *const Page
- }
- #[no_mangle]
- pub extern "C" fn c_alloc_pages(order: u32) -> *const Page {
- ZONE.lock().alloc_pages(order).as_ptr() as *const Page
- }
- #[no_mangle]
- pub extern "C" fn c_alloc_page_table() -> usize {
- let pfn: PFN = ZONE.lock().alloc_pages(0).into();
- let paddr: usize = usize::from(pfn) << 12;
- unsafe {
- core::ptr::write_bytes(paddr as *mut u8, 0, 4096);
- }
- paddr
- }
|