kernel: impl buddy physical allocator/pmm
This commit is contained in:
parent
07b90af46d
commit
238e822751
|
|
@ -46,7 +46,7 @@ extern "C" fn _start() -> ! {
|
|||
kernel::x86_64::paging::get_physical_addr(VirtAddr(_start as *const () as u64))
|
||||
);
|
||||
|
||||
let pmm = kernel::memory::PhysicalMemoryAllocator::from_memory_map(
|
||||
let pmm = kernel::memory::PhysicalMemoryManager::from_memory_map(
|
||||
kernel::boot::BOOT_INFO
|
||||
.get()
|
||||
.expect("Boot info not initialized")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
use core::{alloc::Allocator, cell::Cell, fmt::Debug, num::NonZeroUsize, ptr::NonNull};
|
||||
use core::{
|
||||
alloc::Allocator,
|
||||
cell::Cell,
|
||||
fmt::Debug,
|
||||
num::{NonZero, NonZeroUsize},
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
use bit_field::BitField;
|
||||
use rbtree::RBTree;
|
||||
|
|
@ -9,6 +15,8 @@ pub static HHDM_BASE: OnceLock<u64> = OnceLock::new();
|
|||
|
||||
pub trait VirtAddrTranslationExt: Sized {
|
||||
fn into_phy_addr(self) -> Option<PhyAddr>;
|
||||
|
||||
#[expect(clippy::wrong_self_convention)]
|
||||
fn is_mapped(self) -> bool {
|
||||
self.into_phy_addr().is_some()
|
||||
}
|
||||
|
|
@ -129,7 +137,118 @@ pub struct PhysicalMemoryManager {
|
|||
///
|
||||
/// When a range of pages is freed, we try to locate its buddy and merge
|
||||
/// them into a larger chunk recursively.
|
||||
buddies: [u64; 40],
|
||||
buddies: [RBTree<PhysicalPageNode>; 40],
|
||||
}
|
||||
|
||||
impl Debug for PhysicalMemoryManager {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("PhysicalMemoryManager")
|
||||
.field_with("buddies", |f| {
|
||||
f.debug_list()
|
||||
.entries(self.buddies.iter().enumerate().map(|(i, tree)| {
|
||||
struct BuddyDbg<'a> {
|
||||
index: usize,
|
||||
tree: &'a RBTree<PhysicalPageNode>,
|
||||
}
|
||||
impl Debug for BuddyDbg<'_> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:#x}: {:?}", 1u64 << self.index, self.tree)
|
||||
}
|
||||
}
|
||||
BuddyDbg { index: i, tree }
|
||||
}))
|
||||
.finish()
|
||||
})
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalMemoryManager {
|
||||
pub fn from_memory_map(memory_map: &[crate::boot::MemoryRegion]) -> Self {
|
||||
let usable_regions = memory_map
|
||||
.iter()
|
||||
.filter(|region| region.region_type.is_usable());
|
||||
|
||||
let buddies = [(); 40].map(|_| RBTree::default());
|
||||
|
||||
let mut pmm = PhysicalMemoryManager { buddies };
|
||||
|
||||
usable_regions.for_each(|region| {
|
||||
let idx = region.start / crate::x86_64::PAGE_SIZE as u64;
|
||||
let count = region.length / crate::x86_64::PAGE_SIZE as u64;
|
||||
|
||||
pmm.free_region(idx as usize, count as usize);
|
||||
});
|
||||
|
||||
pmm
|
||||
}
|
||||
|
||||
fn free_region(&mut self, page_index: usize, mut count: usize) {
|
||||
serial_println!(
|
||||
"Freeing region: page_index = {:#x}, count = {:#x}",
|
||||
page_index,
|
||||
count
|
||||
);
|
||||
while let Some(bit) = NonZero::new(count.isolate_lowest_one()).map(NonZero::get) {
|
||||
count -= bit;
|
||||
|
||||
let mut bin = bit.trailing_zeros() as usize;
|
||||
|
||||
let mut index = page_index + count;
|
||||
let mut buddy: NonNull<PhysicalPageNode>;
|
||||
loop {
|
||||
// each bin corresponds to a power-of-two number of pages.
|
||||
// each chunk of pages has a buddy that is the same size (in the same bin)
|
||||
|
||||
// for example, the chunk [0, 4) has a buddy [4, 8).
|
||||
let buddy_index = if index & bit == 0 {
|
||||
// if the chunk is aligned to the size of the chunk, its buddy
|
||||
// is located at page_index + bit.
|
||||
index + bit
|
||||
} else {
|
||||
// if the chunk is not aligned to the size of the chunk, its
|
||||
// buddy is located at page_index - bit.
|
||||
index - bit
|
||||
};
|
||||
|
||||
serial_println!(
|
||||
"looking for buddy: page_index = {:#x}, buddy_index = {:#x}, bin = {}",
|
||||
index,
|
||||
buddy_index,
|
||||
bin
|
||||
);
|
||||
|
||||
match self.buddies[bin].remove(PhysicalPageNodeKey::from_page_index(buddy_index)) {
|
||||
Some(chunk) => {
|
||||
serial_println!(
|
||||
"found buddy: page_index = {:#x}, buddy_index = {:#x}, bin = {}",
|
||||
index,
|
||||
buddy_index,
|
||||
bin
|
||||
);
|
||||
buddy = chunk;
|
||||
index = PhysicalPageNode::page_index(buddy).min(index);
|
||||
bin += 1;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
let node_ptr = PhysicalPageNode::new_from_page_index(index);
|
||||
serial_println!(
|
||||
"inserting node: page_index = {:#x}, size = {:#x}, bin = {}",
|
||||
index,
|
||||
bit,
|
||||
bin
|
||||
);
|
||||
let None = self.buddies[bin].insert_node(node_ptr) else {
|
||||
panic!(
|
||||
"Attempted to free a region that is already free: page_index = {}, count = {}",
|
||||
page_index, bit
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PhysicalMemoryAllocator {
|
||||
|
|
@ -699,6 +818,27 @@ impl Debug for PhysicalPageNode {
|
|||
}
|
||||
|
||||
impl PhysicalPageNode {
|
||||
fn phy(&self) -> Option<PhyAddr> {
|
||||
PhyAddr::from_hhdm_virt(self)
|
||||
}
|
||||
|
||||
fn page_index(this: NonNull<Self>) -> usize {
|
||||
let phy = PhyAddr::from_hhdm_virt(VirtAddr::from(this));
|
||||
phy.expect("PhysicalPageNode is not in HHDM").page_index() as usize
|
||||
}
|
||||
|
||||
fn new_from_page_index(page_index: usize) -> NonNull<Self> {
|
||||
let phy = PhyAddr::from_page_index(page_index);
|
||||
let virt = phy.as_hhdm_virt();
|
||||
let ptr = virt.as_mut::<Self>();
|
||||
|
||||
unsafe {
|
||||
ptr.write(Self::new_red());
|
||||
}
|
||||
|
||||
NonNull::new(ptr).unwrap()
|
||||
}
|
||||
|
||||
fn new_red() -> Self {
|
||||
PhysicalPageNode(Cell::new(1 << 120))
|
||||
}
|
||||
|
|
@ -757,6 +897,26 @@ impl PhysicalPageNode {
|
|||
/// the key does not require any additional bits.
|
||||
struct PhysicalPageNodeKey;
|
||||
|
||||
impl Debug for PhysicalPageNodeKey {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:?}", self.phy())
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalPageNodeKey {
|
||||
fn from_page_index(page_index: usize) -> &'static Self {
|
||||
let phy = PhyAddr::from_page_index(page_index);
|
||||
let virt = phy.as_hhdm_virt();
|
||||
let ptr = virt.as_ptr::<Self>();
|
||||
|
||||
unsafe { &*ptr }
|
||||
}
|
||||
|
||||
fn phy(&self) -> Option<PhyAddr> {
|
||||
PhyAddr::from_hhdm_virt(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for PhysicalPageNodeKey {}
|
||||
|
||||
impl PartialEq for PhysicalPageNodeKey {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(dead_code)]
|
||||
use core::ffi::c_void;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -336,12 +337,10 @@ impl Elf64Sym {
|
|||
if let Some(bytes) = crate::limine::EXECUTABLE_FILE_REQUEST
|
||||
.file()
|
||||
.map(LimineFile::bytes)
|
||||
&& let Ok(ehdr) = plain::from_bytes::<Elf64Ehdr>(bytes)
|
||||
&& let Some(symtab) = ehdr.symtab(bytes)
|
||||
{
|
||||
if let Ok(ehdr) = plain::from_bytes::<Elf64Ehdr>(bytes) {
|
||||
if let Some(symtab) = ehdr.symtab(bytes) {
|
||||
return symtab;
|
||||
}
|
||||
}
|
||||
return symtab;
|
||||
}
|
||||
&[]
|
||||
});
|
||||
|
|
@ -375,6 +374,7 @@ impl Elf64Sym {
|
|||
_ => panic!("Invalid bind value"),
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&self) -> Elf64SymType {
|
||||
match self.info & 0xf {
|
||||
0 => Elf64SymType::Notype,
|
||||
|
|
|
|||
|
|
@ -539,11 +539,15 @@ pub const PF_STACK: u8 = 3;
|
|||
|
||||
const STACK_SIZE: usize = super::PAGE_SIZE * 5;
|
||||
#[repr(align(4096))]
|
||||
pub struct Stack(MaybeUninit<[u8; STACK_SIZE]>);
|
||||
pub struct Stack {
|
||||
_bytes: MaybeUninit<[u8; STACK_SIZE]>,
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
const fn new() -> Self {
|
||||
Stack(MaybeUninit::uninit())
|
||||
Stack {
|
||||
_bytes: MaybeUninit::uninit(),
|
||||
}
|
||||
}
|
||||
fn top(&self) -> *const u8 {
|
||||
unsafe {
|
||||
|
|
|
|||
Loading…
Reference in a new issue