mirror of
https://github.com/sgmarz/osblog.git
synced 2024-11-24 02:16:19 +04:00
Added global allocator and tests
This commit is contained in:
parent
a321507a3b
commit
076dceff73
@ -2,7 +2,10 @@
|
|||||||
// Stephen Marz
|
// Stephen Marz
|
||||||
// 21 Sep 2019
|
// 21 Sep 2019
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(panic_info_message,asm)]
|
#![feature(panic_info_message,asm,allocator_api,alloc_error_handler)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
// ///////////////////////////////////
|
// ///////////////////////////////////
|
||||||
// / RUST MACROS
|
// / RUST MACROS
|
||||||
@ -64,6 +67,8 @@ fn abort() -> ! {
|
|||||||
// ///////////////////////////////////
|
// ///////////////////////////////////
|
||||||
// / CONSTANTS
|
// / CONSTANTS
|
||||||
// ///////////////////////////////////
|
// ///////////////////////////////////
|
||||||
|
// const STR_Y: &str = "\x1b[38;2;79;221;13m✓\x1b[m";
|
||||||
|
// const STR_N: &str = "\x1b[38;2;221;41;13m✘\x1b[m";
|
||||||
|
|
||||||
// ///////////////////////////////////
|
// ///////////////////////////////////
|
||||||
// / ENTRY POINT
|
// / ENTRY POINT
|
||||||
@ -87,21 +92,30 @@ fn kmain() {
|
|||||||
println!("This is my operating system!");
|
println!("This is my operating system!");
|
||||||
println!("I'm so awesome. If you start typing something, I'll show you what you typed!");
|
println!("I'm so awesome. If you start typing something, I'll show you what you typed!");
|
||||||
mem::init();
|
mem::init();
|
||||||
let k = mem::alloc(2);
|
mem::print_page_allocations();
|
||||||
let j = mem::alloc(10);
|
let root_ptr = mem::zalloc(1) as *mut mem::Table;
|
||||||
println!("Got 2 pages from {:p}", k);
|
let mut root = unsafe { root_ptr.as_mut().unwrap() };
|
||||||
println!("Got 10 more pages from {:p}", j);
|
mem::map(&mut root, 0x7f2_2000_01f2, 0x8000_0ddd, mem::EntryBits::Read.val());
|
||||||
mem::free(k);
|
|
||||||
let l = mem::alloc(12);
|
|
||||||
println!("Got 12 more pages from {:p}", l);
|
|
||||||
let mut root = unsafe { (mem::zalloc(1) as *mut mem::Table).as_mut().unwrap() };
|
|
||||||
mem::map(&mut root, 0x7f2_2000_0000, l as usize, mem::EntryBits::Read.val());
|
|
||||||
let m = mem::walk(&root, 0x7f2_2000_0234).unwrap_or(0);
|
let m = mem::walk(&root, 0x7f2_2000_0234).unwrap_or(0);
|
||||||
println!("Got 0x{:x}, should be {:p}", m, unsafe {l.add(0x234)});
|
mem::print_page_allocations();
|
||||||
mem::free(l);
|
|
||||||
mem::free(j);
|
|
||||||
mem::unmap(&mut root);
|
mem::unmap(&mut root);
|
||||||
mem::free((root as *mut mem::Table) as *mut u8);
|
mem::print_page_allocations();
|
||||||
|
mem::dealloc(root_ptr as *mut u8);
|
||||||
|
mem::print_page_allocations();
|
||||||
|
println!("Memory = 0x{:x}", m);
|
||||||
|
{
|
||||||
|
// We have the global allocator, so let's see if that works!
|
||||||
|
let k = alloc::boxed::Box::<u8>::new(100);
|
||||||
|
println!("Boxed value = {}", *k);
|
||||||
|
let j = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
println!("Vector [0] = {}, [5] = {}, len = {}", j[0], j[5], j.len());
|
||||||
|
mem::print_page_allocations();
|
||||||
|
}
|
||||||
|
// The box inside of the scope above should be dropped when k goes
|
||||||
|
// out of scope. The Drop trait for Box should call dealloc from the
|
||||||
|
// global allocator.
|
||||||
|
mem::print_page_allocations();
|
||||||
|
|
||||||
// Now see if we can read stuff:
|
// Now see if we can read stuff:
|
||||||
// Usually we can use #[test] modules in Rust, but it would convolute the
|
// Usually we can use #[test] modules in Rust, but it would convolute the
|
||||||
// task at hand. So, we'll just add testing snippets.
|
// task at hand. So, we'll just add testing snippets.
|
||||||
|
@ -63,6 +63,15 @@ impl Page {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The page is a user page.
|
||||||
|
pub fn is_user(&self) -> bool {
|
||||||
|
if self.flags & PageBits::User.val() != 0 {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
// This is the opposite of is_taken().
|
// This is the opposite of is_taken().
|
||||||
pub fn is_free(&self) -> bool {
|
pub fn is_free(&self) -> bool {
|
||||||
!self.is_taken()
|
!self.is_taken()
|
||||||
@ -99,7 +108,6 @@ pub fn init() {
|
|||||||
// Determine where the actual useful memory starts. This will be after all Page
|
// Determine where the actual useful memory starts. This will be after all Page
|
||||||
// structures. We also must align the ALLOC_START to a page-boundary (PAGE_SIZE = 4096).
|
// structures. We also must align the ALLOC_START to a page-boundary (PAGE_SIZE = 4096).
|
||||||
ALLOC_START = (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1) & !(PAGE_SIZE - 1);
|
ALLOC_START = (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1) & !(PAGE_SIZE - 1);
|
||||||
println!("Alloc start = 0x{:x}, last page = {:p}", ALLOC_START, ptr.add(num_pages - 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,8 +183,10 @@ pub fn zalloc(pages: usize) -> *mut u8 {
|
|||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
/// Deallocate a page by its pointer
|
||||||
pub fn free(ptr: *mut u8) {
|
/// The way we've structured this, it will automatically coalesce
|
||||||
|
/// contiguous pages.
|
||||||
|
pub fn dealloc(ptr: *mut u8) {
|
||||||
// Make sure we don't try to free a null pointer.
|
// Make sure we don't try to free a null pointer.
|
||||||
assert!(!ptr.is_null());
|
assert!(!ptr.is_null());
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -192,13 +202,53 @@ pub fn free(ptr: *mut u8) {
|
|||||||
}
|
}
|
||||||
// If the following assertion fails, it is most likely
|
// If the following assertion fails, it is most likely
|
||||||
// caused by a double-free.
|
// caused by a double-free.
|
||||||
assert!((*p).is_last() == true);
|
assert!((*p).is_last() == true, "Possible double-free detected! (Not taken found before last)");
|
||||||
// If we get here, we've taken care of all previous pages and
|
// If we get here, we've taken care of all previous pages and
|
||||||
// we are on the last page.
|
// we are on the last page.
|
||||||
(*p).clear();
|
(*p).clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a debugging function. It prints all allocations in the page table.
|
||||||
|
pub fn print_page_allocations() {
|
||||||
|
unsafe {
|
||||||
|
let num_pages = HEAP_SIZE / PAGE_SIZE;
|
||||||
|
let mut beg = HEAP_START as *const Page;
|
||||||
|
let end = beg.add(num_pages);
|
||||||
|
let alloc_beg = ALLOC_START;
|
||||||
|
let alloc_end = ALLOC_START + num_pages * PAGE_SIZE;
|
||||||
|
println!();
|
||||||
|
println!("PAGE ALLOCATION TABLE\nMETA: {:p} -> {:p}\nPHYS: 0x{:x} -> 0x{:x}", beg, end, alloc_beg, alloc_end);
|
||||||
|
println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||||
|
let mut num = 0;
|
||||||
|
while beg < end {
|
||||||
|
if (*beg).is_taken() {
|
||||||
|
let start = beg as usize;
|
||||||
|
let memaddr = ALLOC_START + (start - HEAP_START) * PAGE_SIZE;
|
||||||
|
print!("0x{:x} => ", memaddr);
|
||||||
|
loop {
|
||||||
|
num += 1;
|
||||||
|
if (*beg).is_last() {
|
||||||
|
let end = beg as usize;
|
||||||
|
let memaddr = ALLOC_START + (end - HEAP_START) * PAGE_SIZE + 0xfff;
|
||||||
|
print!("0x{:x}: {:>3} page(s)", memaddr, (end - start + 1));
|
||||||
|
if (*beg).is_user() {
|
||||||
|
print!(" U");
|
||||||
|
}
|
||||||
|
println!(".");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
beg = beg.add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
beg = beg.add(1);
|
||||||
|
}
|
||||||
|
println!("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||||
|
println!("{} pages allocated.", num);
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ////////////////////////////////
|
// ////////////////////////////////
|
||||||
// // MMU Routines
|
// // MMU Routines
|
||||||
@ -344,6 +394,8 @@ pub fn map(root: &mut Table, vaddr: usize, paddr: usize, bits: i64) {
|
|||||||
/// root: The root table to start freeing.
|
/// root: The root table to start freeing.
|
||||||
/// NOTE: This does NOT free root directly. This must be
|
/// NOTE: This does NOT free root directly. This must be
|
||||||
/// freed manually.
|
/// freed manually.
|
||||||
|
/// The reason we don't free the root is because it is
|
||||||
|
/// usually embedded into the Process structure.
|
||||||
pub fn unmap(root: &mut Table) {
|
pub fn unmap(root: &mut Table) {
|
||||||
// Start with level 2
|
// Start with level 2
|
||||||
for lv2 in 0..512 {
|
for lv2 in 0..512 {
|
||||||
@ -356,14 +408,12 @@ pub fn unmap(root: &mut Table) {
|
|||||||
let ref mut entry_lv1 = table_lv1.entries[lv1];
|
let ref mut entry_lv1 = table_lv1.entries[lv1];
|
||||||
if entry_lv1.is_valid() && entry_lv1.is_branch() {
|
if entry_lv1.is_valid() && entry_lv1.is_branch() {
|
||||||
let memaddr_lv0 = (entry_lv1.get_entry() & !0x3ff) << 2;
|
let memaddr_lv0 = (entry_lv1.get_entry() & !0x3ff) << 2;
|
||||||
print!("Freeing lv1[{}] = 0x{:x}...", lv1, memaddr_lv0);
|
// The next level is level 0, which cannot have branches, therefore,
|
||||||
free(memaddr_lv0 as *mut u8);
|
// we free here.
|
||||||
println!("done");
|
dealloc(memaddr_lv0 as *mut u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print!("Freeing lv2[{}] = 0x{:x}...", lv2, memaddr_lv1);
|
dealloc(memaddr_lv1 as *mut u8);
|
||||||
free(memaddr_lv1 as *mut u8);
|
|
||||||
println!("done");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -383,6 +433,9 @@ pub fn walk(root: &Table, vaddr: usize) -> Option<usize> {
|
|||||||
(vaddr >> 30) & 0x1ff
|
(vaddr >> 30) & 0x1ff
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// The last 12 bits (0xfff) is not translated by
|
||||||
|
// the MMU, so we have to copy it from the vaddr
|
||||||
|
// to the physical address.
|
||||||
let pgoff = vaddr & 0xfff;
|
let pgoff = vaddr & 0xfff;
|
||||||
|
|
||||||
let mut v = &root.entries[vpn[2]];
|
let mut v = &root.entries[vpn[2]];
|
||||||
@ -423,3 +476,40 @@ pub fn walk(root: &Table, vaddr: usize) -> Option<usize> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ///////////////////////////////////
|
||||||
|
// / GLOBAL ALLOCATOR
|
||||||
|
// ///////////////////////////////////
|
||||||
|
|
||||||
|
// The global allocator allows us to use the data structures
|
||||||
|
// in the core library, such as a linked list or B-tree.
|
||||||
|
// We want to use these sparingly since we have a coarse-grained
|
||||||
|
// allocator.
|
||||||
|
use core::alloc::{GlobalAlloc, Layout};
|
||||||
|
|
||||||
|
// The global allocator is a static constant to a global allocator
|
||||||
|
// structure. We don't need any members because we're using this
|
||||||
|
// structure just to implement alloc and dealloc.
|
||||||
|
struct OsGlobalAlloc;
|
||||||
|
|
||||||
|
unsafe impl GlobalAlloc for OsGlobalAlloc {
|
||||||
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
|
let pages = (layout.size() + PAGE_SIZE - 1) & !(PAGE_SIZE - 1);
|
||||||
|
// We align to the next page size so that when
|
||||||
|
// we divide by PAGE_SIZE, we get exactly the number
|
||||||
|
// of pages necessary.
|
||||||
|
alloc(pages / PAGE_SIZE)
|
||||||
|
}
|
||||||
|
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||||
|
dealloc(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static GA: OsGlobalAlloc = OsGlobalAlloc {};
|
||||||
|
|
||||||
|
#[alloc_error_handler]
|
||||||
|
pub fn alloc_error(l: Layout) -> ! {
|
||||||
|
panic!("Allocator failed: {} bytes, {} alignment.", l.size(), l.align());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user