Skip to content

Commit cd65867

Browse files
committed
mm: add stack based PMA
use 8MiB reserved array to manage up to 4GiB of physical memory (4K Pages only) Signed-off-by: Tianhao Wang <shrik3@mailbox.org>
1 parent 9cf85e8 commit cd65867

File tree

8 files changed

+199
-67
lines changed

8 files changed

+199
-67
lines changed

boot/startup-x86_64.s

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,10 @@ pml4:
186186
pdp:
187187
resb 4096
188188
alignb 4096
189+
190+
; reserve 8MiB for frame alloc.
191+
; (see linker file)
192+
[SECTION .global_free_page_stack]
193+
free_page_stack:
194+
resb 8388608
195+
alignb 4096

defs/x86_64-linker.ld

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ SECTIONS
8383
*(".global_pagetable")
8484
}
8585

86+
. = ALIGN(4096);
87+
/* reserve space for a premitive stack based physical frame allocator */
88+
/* each frame is 4KiB in size and has a 64bit (physical) address. e.g. */
89+
/* for every 1 GiB physical memory we need 2 MiB space reserved for the */
90+
/* free stack. For a easier bootstraping we are using a fix-sized stack */
91+
/* array. Currently using 4GiB, therefore reserve 8MiB. */
92+
PROVIDE (___FREE_PAGE_STACK__ = .);
93+
.global_free_page_stack ALIGN(4096) (NOLOAD) :
94+
{
95+
*("..global_free_page_stack")
96+
}
8697
. = ALIGN(4096);
8798
PROVIDE (___KERNEL_END__ = .);
8899
}

src/defs.rs

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,59 @@
1+
// exported symbols from asm/linker.
2+
// They are always unsafe.
3+
extern "C" {
4+
fn ___KERNEL_START__();
5+
fn ___KERNEL_END__();
6+
fn ___BSS_START__();
7+
fn ___BSS_END__();
8+
}
9+
10+
pub fn pmap_kernel_start() -> u64 {
11+
return ___KERNEL_START__ as u64;
12+
}
13+
14+
pub fn pmap_kernel_end() -> u64 {
15+
return ___KERNEL_END__ as u64;
16+
}
17+
pub fn pmap_bss_start() -> u64 {
18+
return ___BSS_START__ as u64;
19+
}
20+
21+
pub fn pmap_bss_end() -> u64 {
22+
return ___BSS_END__ as u64;
23+
}
24+
125
pub struct Mem;
226
pub struct VAddr(u64);
27+
#[derive(Debug)]
28+
pub struct Range {
29+
pub addr: u64,
30+
pub len: u64,
31+
}
32+
33+
pub fn roundup_4k(addr: u64) -> u64 {
34+
return (addr + 0x1000) & 0xffff_ffff_ffff_0000;
35+
}
36+
37+
pub fn rounddown_4k(addr: u64) -> u64 {
38+
return addr & 0xffff_ffff_ffff_0000;
39+
}
40+
41+
impl Range {
42+
pub fn contains(&self, addr: u64) -> bool {
43+
return self.addr <= addr && addr < self.addr + self.len;
44+
}
45+
}
346

447
impl Mem {
548
// units
6-
pub const K: usize = 1024;
7-
pub const M: usize = 1024 * Mem::K;
8-
pub const G: usize = 1024 * Mem::M;
49+
pub const K: u64 = 1024;
50+
pub const M: u64 = 1024 * Mem::K;
51+
pub const G: u64 = 1024 * Mem::M;
952
// physical memory layout: qemu defaults to 128 MiB phy Memory
10-
pub const PHY_TOP: usize = 128 * Mem::M;
53+
pub const PHY_TOP: u64 = 128 * Mem::M;
1154
// 4 lv 4K paging
12-
pub const PAGE_SIZE: usize = 0x1000;
13-
pub const PAGE_SHIFT: usize = 12;
55+
pub const PAGE_SIZE: u64 = 0x1000;
56+
pub const PAGE_SHIFT: u64 = 12;
1457
pub const PAGE_MASK: u64 = 0xfff;
1558
pub const L0_SHIFT: u8 = 39;
1659
pub const L0_MASK: u64 = 0x1ff << Mem::L0_SHIFT;
@@ -20,11 +63,11 @@ impl Mem {
2063
pub const L2_MASK: u64 = 0x1ff << Mem::L2_SHIFT;
2164
pub const L3_SHIFT: u8 = 12;
2265
pub const L3_MASK: u64 = 0x1ff << Mem::L3_SHIFT;
23-
pub const PHY_PAGES: usize = Mem::PHY_TOP >> Mem::PAGE_SHIFT;
66+
pub const PHY_PAGES: u64 = Mem::PHY_TOP >> Mem::PAGE_SHIFT;
2467
// size of frame allocator bitmap: number of physical frames / 8 for 128M
2568
// memory (37268) 4k pages, 37268 bits are needed, hence
2669
// 4096 bytes, exactly one page!
27-
pub const PHY_BM_SIZE: usize = Mem::PHY_PAGES >> 3;
70+
pub const PHY_BM_SIZE: u64 = Mem::PHY_PAGES >> 3;
2871
}
2972

3073
impl VAddr {

src/lib.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,27 @@ pub extern "C" fn _entry() -> ! {
3131
assert!(multiboot::check(), "bad multiboot info from grub!");
3232
let mbi = multiboot::get_mb_info().expect("bad multiboot info flags");
3333
let mem = unsafe { mbi.get_mem() }.unwrap();
34-
let mmap = unsafe { mbi.get_mmap() }.unwrap();
35-
println!("memory: {:#X?}", mem);
36-
println!("mmap (start): {:#X?}", mmap);
37-
38-
multiboot::_test_mmap();
34+
println!(
35+
"available memory: lower {:#X} KiB, upper:{:#X} KiB",
36+
mem.lower(),
37+
mem.upper()
38+
);
39+
mm::init();
3940
interrupt::init();
4041
pic_8259::allow(PicDeviceInt::KEYBOARD);
4142
interrupt::interrupt_enable();
42-
let mut framemap = mm::pma::FMap::new();
43-
framemap.init();
44-
println!("Bitmap starting from : {:p}", framemap.bm.as_ptr());
45-
println!("Skip first {} bytes", framemap.skip_byte);
46-
println!("system init .. done!");
43+
44+
println!(
45+
"kernel: {:#X} - {:#X}",
46+
defs::pmap_kernel_start(),
47+
defs::pmap_kernel_end()
48+
);
49+
println!(
50+
" BSS: {:#X} - {:#X}",
51+
defs::pmap_bss_start(),
52+
defs::pmap_bss_end()
53+
);
54+
4755
// io::print_welcome();
4856

4957
// busy loop query keyboard

src/machine/multiboot.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::defs::Range;
12
use crate::io::*;
23
use core::fmt;
34
use core::mem::size_of;
@@ -57,6 +58,15 @@ impl MultibootMmap {
5758
pub const MTYPE_RAM_NVS: u32 = 4;
5859
/// defective RAM
5960
pub const MTYPE_RAM_DEFECT: u32 = 5;
61+
pub fn get_range(&self) -> Range {
62+
return Range {
63+
addr: self.addr,
64+
len: self.len,
65+
};
66+
}
67+
pub fn get_end(&self) -> u64 {
68+
return self.addr + self.len;
69+
}
6070
}
6171

6272
#[repr(C)]

src/mm/heap_allocator.rs

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/mm/mod.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,51 @@
1-
pub mod heap_allocator;
1+
use crate::defs::*;
2+
use crate::io::*;
3+
use crate::machine::multiboot;
24
pub mod pma;
5+
6+
use lazy_static::lazy_static;
7+
use spin::Mutex;
8+
lazy_static! {
9+
pub static ref GLOBAL_PMA: Mutex<pma::PageStackAllocator> =
10+
Mutex::new(pma::PageStackAllocator::new());
11+
}
12+
13+
pub fn init() {
14+
let mbi = multiboot::get_mb_info().unwrap();
15+
let mmapinfo = unsafe { mbi.get_mmap() }.unwrap();
16+
let buf_start = mmapinfo.mmap_addr;
17+
let buf_len = mmapinfo.mmap_length;
18+
let buf_end = buf_start + buf_len;
19+
let mut curr = buf_start as u64;
20+
let mut inserted = 0;
21+
loop {
22+
if curr >= buf_end as u64 {
23+
break;
24+
}
25+
let mblock = unsafe { &*(curr as *const multiboot::MultibootMmap) };
26+
curr += mblock.size as u64;
27+
curr += 4;
28+
if mblock.mtype != multiboot::MultibootMmap::MTYPE_RAM {
29+
continue;
30+
}
31+
if mblock.get_end() <= pmap_kernel_start() {
32+
continue;
33+
}
34+
// TODO early break if the array is already full
35+
if mblock.get_range().contains(pmap_kernel_end()) {
36+
let r = Range {
37+
addr: pmap_kernel_end(),
38+
len: mblock.get_end() - pmap_kernel_end(),
39+
};
40+
inserted += GLOBAL_PMA.lock().insert_range(r);
41+
} else {
42+
inserted += GLOBAL_PMA.lock().insert_range(mblock.get_range());
43+
}
44+
println!(
45+
"pma init: {:#X}KiB free memory, {:#X} pages inserted from block {:#X?}",
46+
inserted * 0x4,
47+
inserted,
48+
mblock,
49+
);
50+
}
51+
}

src/mm/pma.rs

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,72 @@
1-
use crate::defs::{self, Mem};
2-
use core::ffi::c_void;
3-
use core::{ptr, slice};
4-
// this is POC code, it will be ugly
1+
use crate::defs::*;
2+
use crate::io::*;
3+
use crate::machine::multiboot::MultibootMmap;
4+
use core::slice;
55

66
extern "C" {
7-
pub fn ___KERNEL_END__();
7+
fn ___FREE_PAGE_STACK__();
88
}
99

10-
type BitU8 = u8;
11-
/// Bitmap for physical frames
12-
pub struct FMap {
13-
pub bm: &'static mut [BitU8],
14-
// skip over the kernel image and the bitmap itself.
15-
pub skip_byte: usize,
10+
/// There should only be one global instance of this.
11+
pub struct PageStackAllocator {
12+
page_stack: &'static mut [u64],
13+
size: usize,
14+
head: usize,
1615
}
1716

18-
pub enum PMAError {
19-
DoubleFree,
20-
}
17+
impl PageStackAllocator {
18+
// covering 4GiB physical memory of 4K frames
19+
const STACK_SIZE: usize = 0x100000;
2120

22-
impl FMap {
2321
pub fn new() -> Self {
24-
let map_start = ___KERNEL_END__ as usize;
25-
let fmap = Self {
26-
bm: unsafe { slice::from_raw_parts_mut(map_start as *mut u8, Mem::PHY_BM_SIZE) },
27-
// looks ugly, perhaps FIXME
28-
// We'll waste several frames for the sake of easy alignment
29-
skip_byte: 1 + ((map_start >> Mem::PAGE_SHIFT) / 8),
22+
let ps = Self {
23+
page_stack: unsafe {
24+
slice::from_raw_parts_mut(
25+
___FREE_PAGE_STACK__ as usize as *mut u64,
26+
Self::STACK_SIZE,
27+
)
28+
},
29+
size: Self::STACK_SIZE,
30+
head: 0,
3031
};
31-
fmap
32+
return ps;
3233
}
3334

34-
/// return : index to the bitmap u8 , bit mask to retrive the bit.
35-
fn locate_bit(addr: usize) -> Option<(usize, u8)> {
36-
if addr >= Mem::PHY_TOP {
37-
return None;
35+
/// push an addr into the free page stack
36+
/// MUST be atomic or bad things happen...
37+
pub fn free_page(&mut self, addr: u64) -> bool {
38+
if self.head >= self.size {
39+
return false;
3840
}
39-
let pn = addr >> Mem::PAGE_SHIFT;
40-
let idx = pn / 8;
41-
let mask: u8 = 1 << (pn % 8);
42-
Some((idx, mask))
41+
self.page_stack[self.head] = addr;
42+
self.head += 1;
43+
return true;
4344
}
4445

45-
pub fn alloc_frame(&mut self) -> usize {
46-
for i in self.skip_byte..self.bm.len() {
47-
if self.bm[i] == 0xff {
48-
continue;
49-
}
50-
todo!()
46+
pub fn alloc_page(&mut self) -> Option<u64> {
47+
if self.head == 0 {
48+
return None;
5149
}
52-
0
50+
self.head -= 1;
51+
Some(self.page_stack[self.head])
5352
}
5453

55-
pub fn dealloc_frame(&mut self) -> Result<(), PMAError> {
56-
Ok(())
57-
}
58-
59-
pub fn init(&mut self) {
60-
for i in 0..self.skip_byte {
61-
self.bm[i] = 0xff;
62-
}
63-
for i in self.skip_byte..self.bm.len() {
64-
self.bm[i] = 0;
54+
/// 4k page only
55+
pub fn insert_range(&mut self, r: Range) -> u64 {
56+
let mut inserted = 0;
57+
let mut page = roundup_4k(r.addr);
58+
loop {
59+
if !r.contains(page) {
60+
break;
61+
}
62+
if !self.free_page(page) {
63+
break;
64+
} else {
65+
println!("inserted: {:#X}", page);
66+
inserted += 1;
67+
}
68+
page += 0x1000;
6569
}
70+
return inserted;
6671
}
6772
}

0 commit comments

Comments
 (0)