Skip to content

Commit

Permalink
Simple bitmap page allocator
Browse files Browse the repository at this point in the history
Replaces the freelist-based page allocator and can be used for lifetime of kernel.

Signed-off-by: Graham MacDonald <[email protected]>
  • Loading branch information
gmacd committed Feb 20, 2024
1 parent f57b0cf commit 2c88044
Show file tree
Hide file tree
Showing 10 changed files with 585 additions and 114 deletions.
57 changes: 0 additions & 57 deletions aarch64/src/kalloc.rs

This file was deleted.

27 changes: 7 additions & 20 deletions aarch64/src/kmem.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use port::mem::PhysAddr;

use crate::{param::KZERO, vm::Page4K};
use core::{mem, slice};
use crate::param::KZERO;
use port::mem::{PhysAddr, PhysRange};

// These map to definitions in kernel.ld
extern "C" {
Expand Down Expand Up @@ -52,20 +50,9 @@ pub fn from_ptr_to_physaddr<T>(a: *const T) -> PhysAddr {
from_virt_to_physaddr(a.addr())
}

unsafe fn page_slice_mut<'a>(pstart: *mut Page4K, pend: *mut Page4K) -> &'a mut [Page4K] {
let ustart = pstart.addr();
let uend = pend.addr();
const PAGE_SIZE: usize = mem::size_of::<Page4K>();
assert_eq!(ustart % PAGE_SIZE, 0, "page_slice_mut: unaligned start page");
assert_eq!(uend % PAGE_SIZE, 0, "page_slice_mut: unaligned end page");
assert!(ustart < uend, "page_slice_mut: bad range");

let len = (uend - ustart) / PAGE_SIZE;
unsafe { slice::from_raw_parts_mut(ustart as *mut Page4K, len) }
}

pub fn early_pages() -> &'static mut [Page4K] {
let early_start = early_pagetables_addr() as *mut Page4K;
let early_end = eearly_pagetables_addr() as *mut Page4K;
unsafe { page_slice_mut(early_start, early_end) }
pub fn early_pages_range() -> PhysRange {
PhysRange::new(
from_virt_to_physaddr(early_pagetables_addr()),
from_virt_to_physaddr(eearly_pagetables_addr()),
)
}
10 changes: 5 additions & 5 deletions aarch64/src/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::mem;
use core::mem::MaybeUninit;
use port::fdt::DeviceTree;
use port::mcslock::{Lock, LockNode};
use port::mem::VirtRange;
use port::mem::{PhysAddr, PhysRange, VirtRange};

const MBOX_READ: usize = 0x00;
const MBOX_STATUS: usize = 0x18;
Expand Down Expand Up @@ -191,7 +191,7 @@ pub struct MemoryInfo {
pub end: u32,
}

pub fn get_arm_memory() -> MemoryInfo {
pub fn get_arm_memory() -> PhysRange {
let tags = Tag::<EmptyRequest> {
tag_id0: TagId::GetArmMemory,
tag_buffer_size0: 12,
Expand All @@ -204,10 +204,10 @@ pub fn get_arm_memory() -> MemoryInfo {
let size = res.size;
let end = start + size;

MemoryInfo { start, size, end }
PhysRange::new(PhysAddr::new(start as u64), PhysAddr::new(end as u64))
}

pub fn get_vc_memory() -> MemoryInfo {
pub fn get_vc_memory() -> PhysRange {
let tags = Tag::<EmptyRequest> {
tag_id0: TagId::GetVcMemory,
tag_buffer_size0: 12,
Expand All @@ -220,7 +220,7 @@ pub fn get_vc_memory() -> MemoryInfo {
let size = res.size;
let end = start + size;

MemoryInfo { start, size, end }
PhysRange::new(PhysAddr::new(start as u64), PhysAddr::new(end as u64))
}

pub fn get_firmware_revision() -> u32 {
Expand Down
28 changes: 17 additions & 11 deletions aarch64/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
#![feature(alloc_error_handler)]
#![feature(asm_const)]
#![feature(core_intrinsics)]
#![feature(inline_const)]
#![feature(stdsimd)]
#![feature(strict_provenance)]
#![forbid(unsafe_op_in_unsafe_fn)]

mod devcons;
mod io;
mod kalloc;
mod kmem;
mod mailbox;
mod pagealloc;
mod param;
mod registers;
mod trap;
Expand All @@ -39,7 +40,7 @@ unsafe fn print_memory_range(name: &str, start: &*const c_void, end: &*const c_v
let start = start as *const _ as u64;
let end = end as *const _ as u64;
let size = end - start;
println!(" {name}{start:#x}-{end:#x} ({size:#x})");
println!(" {name}{start:#x}..{end:#x} ({size:#x})");
}

fn print_binary_sections() {
Expand Down Expand Up @@ -67,12 +68,17 @@ fn print_binary_sections() {
}
}

fn print_physical_memory_map() {
fn print_memory_info() {
println!("Physical memory map:");
let mailbox::MemoryInfo { start, size, end } = mailbox::get_arm_memory();
println!(" Memory:\t{start:#018x}-{end:#018x} ({size:#x})");
let mailbox::MemoryInfo { start, size, end } = mailbox::get_vc_memory();
println!(" Video:\t{start:#018x}-{end:#018x} ({size:#x})");
let arm_mem = mailbox::get_arm_memory();
println!(" Memory:\t{arm_mem} ({:#x})", arm_mem.size());
let vc_mem = mailbox::get_vc_memory();
println!(" Video:\t{vc_mem} ({:#x})", vc_mem.size());

println!("Memory usage::");
let (used, total) = pagealloc::usage_bytes();
println!(" Used:\t\t{used:#016x}");
println!(" Total:\t{total:#016x}");
}

// https://github.com/raspberrypi/documentation/blob/develop/documentation/asciidoc/computers/raspberry-pi/revision-codes.adoc
Expand Down Expand Up @@ -121,15 +127,15 @@ pub extern "C" fn main9(dtb_va: usize) {

// Map address space accurately using rust VM code to manage page tables
unsafe {
kalloc::free_pages(kmem::early_pages());

let dtb_range = PhysRange::with_len(from_virt_to_physaddr(dtb_va).addr(), dt.size());
vm::init(&dt, &mut *ptr::addr_of_mut!(KPGTBL), dtb_range);
vm::init(&mut *ptr::addr_of_mut!(KPGTBL), dtb_range, mailbox::get_arm_memory());
vm::switch(&*ptr::addr_of!(KPGTBL));
}

// From this point we can use the global allocator

print_binary_sections();
print_physical_memory_map();
print_memory_info();
print_board_info();

kernel_root().print_recursive_tables();
Expand Down
73 changes: 73 additions & 0 deletions aarch64/src/pagealloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/// This module acts as an interface between the portable allocator and the
/// arch-specific use of it.
///
/// The page allocator is constructed and finalised in a number of phases:
/// 1. `init_page_allocator` to create a fixed size allocator assuming everything
/// is in use except a small number of statically defined pages available for
/// setting up the initial page tables.
/// 2. `free_unused_ranges` to mark available ranges as the inverse of the
/// physical memory map within the bounds of the available memory.
use crate::kmem;
use crate::kmem::physaddr_as_ptr_mut;
use crate::vm::Page4K;
use port::bitmapalloc::BitmapPageAlloc;
use port::bitmapalloc::BitmapPageAllocError;
use port::mem::PhysRange;
use port::{
mcslock::{Lock, LockNode},
mem::PAGE_SIZE_4K,
};

/// Set up bitmap page allocator assuming everything is allocated.
static PAGE_ALLOC: Lock<BitmapPageAlloc<16, PAGE_SIZE_4K>> = Lock::new(
"page_alloc",
const { BitmapPageAlloc::<16, PAGE_SIZE_4K>::new_all_allocated(PAGE_SIZE_4K) },
);

/// The bitmap allocator has all pages marked as allocated initially. We'll
/// add some pages (mark free) to allow us to set up the page tables and build
/// a memory map. Once the memory map has been build, we can mark all the unused
/// space as available. This allows us to use only one page allocator throughout.
pub fn init_page_allocator() {
let node = LockNode::new();
let mut lock = PAGE_ALLOC.lock(&node);
let page_alloc = &mut *lock;

let early_pages_range = kmem::early_pages_range();
if let Err(err) = page_alloc.mark_free(&early_pages_range) {
panic!("Couldn't mark early pages free: range: {} err: {:?}", early_pages_range, err);
}
}

/// Free unused pages in mem that aren't covered by the memory map. Assumes
/// that custom_map is sorted.
pub fn free_unused_ranges<'a>(
available_mem: &PhysRange,
used_ranges: impl Iterator<Item = &'a PhysRange>,
) -> Result<(), BitmapPageAllocError> {
let node = LockNode::new();
let mut lock = PAGE_ALLOC.lock(&node);
let page_alloc = &mut *lock;

page_alloc.free_unused_ranges(available_mem, used_ranges)
}

/// Try to allocate a page
pub fn allocate() -> Result<&'static mut Page4K, BitmapPageAllocError> {
let node = LockNode::new();
let mut lock = PAGE_ALLOC.lock(&node);
let page_alloc = &mut *lock;

match page_alloc.allocate() {
Ok(page_pa) => Ok(unsafe { &mut *physaddr_as_ptr_mut::<Page4K>(page_pa) }),
Err(err) => Err(err),
}
}

/// Return a tuple of (bytes used, total bytes available) based on the page allocator.
pub fn usage_bytes() -> (usize, usize) {
let node = LockNode::new();
let mut lock = PAGE_ALLOC.lock(&node);
let page_alloc = &mut *lock;
page_alloc.usage_bytes()
}
1 change: 0 additions & 1 deletion aarch64/src/uartmini.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use crate::registers::{
/// MiniUart is assigned to UART1 on the Raspberry Pi. It is easier to use with
/// real hardware, as it requires no additional configuration. Conversely, it's
/// harded to use with QEMU, as it can't be used with the `nographic` switch.
#[allow(dead_code)]
pub struct MiniUart {
pub gpio_range: VirtRange,
pub aux_range: VirtRange,
Expand Down
Loading

0 comments on commit 2c88044

Please sign in to comment.