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))
|
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
|
kernel::boot::BOOT_INFO
|
||||||
.get()
|
.get()
|
||||||
.expect("Boot info not initialized")
|
.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 bit_field::BitField;
|
||||||
use rbtree::RBTree;
|
use rbtree::RBTree;
|
||||||
|
|
@ -9,6 +15,8 @@ pub static HHDM_BASE: OnceLock<u64> = OnceLock::new();
|
||||||
|
|
||||||
pub trait VirtAddrTranslationExt: Sized {
|
pub trait VirtAddrTranslationExt: Sized {
|
||||||
fn into_phy_addr(self) -> Option<PhyAddr>;
|
fn into_phy_addr(self) -> Option<PhyAddr>;
|
||||||
|
|
||||||
|
#[expect(clippy::wrong_self_convention)]
|
||||||
fn is_mapped(self) -> bool {
|
fn is_mapped(self) -> bool {
|
||||||
self.into_phy_addr().is_some()
|
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
|
/// When a range of pages is freed, we try to locate its buddy and merge
|
||||||
/// them into a larger chunk recursively.
|
/// 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 {
|
pub struct PhysicalMemoryAllocator {
|
||||||
|
|
@ -699,6 +818,27 @@ impl Debug for PhysicalPageNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
fn new_red() -> Self {
|
||||||
PhysicalPageNode(Cell::new(1 << 120))
|
PhysicalPageNode(Cell::new(1 << 120))
|
||||||
}
|
}
|
||||||
|
|
@ -757,6 +897,26 @@ impl PhysicalPageNode {
|
||||||
/// the key does not require any additional bits.
|
/// the key does not require any additional bits.
|
||||||
struct PhysicalPageNodeKey;
|
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 Eq for PhysicalPageNodeKey {}
|
||||||
|
|
||||||
impl PartialEq for PhysicalPageNodeKey {
|
impl PartialEq for PhysicalPageNodeKey {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
use core::ffi::c_void;
|
use core::ffi::c_void;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -336,12 +337,10 @@ impl Elf64Sym {
|
||||||
if let Some(bytes) = crate::limine::EXECUTABLE_FILE_REQUEST
|
if let Some(bytes) = crate::limine::EXECUTABLE_FILE_REQUEST
|
||||||
.file()
|
.file()
|
||||||
.map(LimineFile::bytes)
|
.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) {
|
return symtab;
|
||||||
if let Some(symtab) = ehdr.symtab(bytes) {
|
|
||||||
return symtab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&[]
|
&[]
|
||||||
});
|
});
|
||||||
|
|
@ -375,6 +374,7 @@ impl Elf64Sym {
|
||||||
_ => panic!("Invalid bind value"),
|
_ => panic!("Invalid bind value"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind(&self) -> Elf64SymType {
|
fn kind(&self) -> Elf64SymType {
|
||||||
match self.info & 0xf {
|
match self.info & 0xf {
|
||||||
0 => Elf64SymType::Notype,
|
0 => Elf64SymType::Notype,
|
||||||
|
|
|
||||||
|
|
@ -539,11 +539,15 @@ pub const PF_STACK: u8 = 3;
|
||||||
|
|
||||||
const STACK_SIZE: usize = super::PAGE_SIZE * 5;
|
const STACK_SIZE: usize = super::PAGE_SIZE * 5;
|
||||||
#[repr(align(4096))]
|
#[repr(align(4096))]
|
||||||
pub struct Stack(MaybeUninit<[u8; STACK_SIZE]>);
|
pub struct Stack {
|
||||||
|
_bytes: MaybeUninit<[u8; STACK_SIZE]>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Stack {
|
impl Stack {
|
||||||
const fn new() -> Self {
|
const fn new() -> Self {
|
||||||
Stack(MaybeUninit::uninit())
|
Stack {
|
||||||
|
_bytes: MaybeUninit::uninit(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn top(&self) -> *const u8 {
|
fn top(&self) -> *const u8 {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue