move more stuff and fix heap

This commit is contained in:
Bryan McShea
2024-04-23 18:08:06 -04:00
parent 5d475b500c
commit b2b17b927a
9 changed files with 282 additions and 105 deletions

View File

@@ -2,7 +2,7 @@ use core::{arch::asm, ops::Range};
use crate::{ use crate::{
arch::{asm, csr, interrupts, paging, wait}, arch::{asm, csr, interrupts, paging, wait},
fdt::FDT, dev::fdt::FDT,
start, start,
}; };
@@ -70,5 +70,8 @@ pub unsafe fn init() -> ! {
end: raw_mem_range.end(), end: raw_mem_range.end(),
}; };
to_supervisor(); to_supervisor();
start(heap_mem, fdt) start(crate::StartInfo {
mem_range: heap_mem,
dt: fdt,
})
} }

View File

@@ -1,15 +1,19 @@
// garbage .1% finished FDT implementation // garbage .1% finished FDT implementation
use alloc::format;
use crate::{ use crate::{
println, println,
util::{bits::{u32_from_bytes, Be}, lazy::LazyConst}, util::bits::{u32_from_bytes, Be},
}; };
use core::{ use core::{
fmt::Debug, mem::{size_of, transmute}, ops::Range, slice fmt::Debug,
mem::{size_of, transmute},
ops::Range,
slice,
}; };
const MAGIC: u32 = 0xd00dfeed; const MAGIC: u32 = 0xd00dfeed;
pub static DT: LazyConst<FDT> = LazyConst::new();
#[repr(u32)] #[repr(u32)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@@ -20,7 +24,9 @@ enum Token {
Nop, Nop,
End, End,
} }
const TOKEN_SIZE: usize = size_of::<Token>(); const TOKEN_SIZE: usize = size_of::<Token>();
impl Token { impl Token {
pub fn from_bytes(bytes: &[u8]) -> Option<Self> { pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
let val = u32_from_bytes(bytes)?.to_be(); let val = u32_from_bytes(bytes)?.to_be();
@@ -63,6 +69,15 @@ pub struct Prop {
pub data: &'static [u8], pub data: &'static [u8],
} }
impl Debug for Prop {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Prop")
.field("name", &self.name)
.field("data_len", &self.data)
.finish()
}
}
impl Prop { impl Prop {
pub fn full_len(&self) -> usize { pub fn full_len(&self) -> usize {
return PROP_SIZE + self.data.len(); return PROP_SIZE + self.data.len();
@@ -213,6 +228,16 @@ pub struct Node {
pub props: &'static [u8], pub props: &'static [u8],
} }
impl Debug for Node {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let props: alloc::vec::Vec<_> = self.into_iter().map(|p| format!("{:?}", p)).collect();
f.debug_struct("Node")
.field("name", &self.name)
.field("props", &props)
.finish()
}
}
impl IntoIterator for &Node { impl IntoIterator for &Node {
type Item = Prop; type Item = Prop;
type IntoIter = PropIter; type IntoIter = PropIter;

View File

@@ -1 +1,2 @@
pub mod uart; pub mod uart;
pub mod fdt;

View File

@@ -9,13 +9,12 @@
use crate::mem::alloc::ALLOCATOR; use crate::mem::alloc::ALLOCATOR;
use core::ops::Range; use core::ops::Range;
use fdt::{DT, FDT}; use dev::fdt::FDT;
extern crate alloc; extern crate alloc;
pub mod arch; pub mod arch;
pub mod dev; pub mod dev;
pub mod fdt;
pub mod log; pub mod log;
pub mod mem; pub mod mem;
pub mod qemu; pub mod qemu;
@@ -23,20 +22,31 @@ pub mod qemu;
mod test; mod test;
pub mod util; pub mod util;
pub fn start(heap_mem: Range<*mut u8>, fdt: FDT) -> ! { pub struct StartInfo {
DT.init(fdt); mem_range: Range<*mut u8>,
unsafe { dt: FDT
ALLOCATOR.init(heap_mem);
} }
pub fn start(info: StartInfo) -> ! {
#[cfg(test)] #[cfg(test)]
{
// un... un bro momento..
test::init(info);
test_main(); test_main();
}
#[cfg(not(test))] #[cfg(not(test))]
main(); main(info);
qemu::exit(0) qemu::exit(0)
} }
pub fn main() { pub fn main(info: StartInfo) {
println!("we out here vibin"); println!("we out here vibin");
unsafe {
ALLOCATOR.init(&info.mem_range);
}
for dev in info.dt.into_iter() {
println!("{:?}", dev);
}
} }
#[panic_handler] #[panic_handler]

View File

@@ -14,9 +14,12 @@ impl Allocator {
pub const fn empty() -> Self { pub const fn empty() -> Self {
Self(Mutex::new(Heap::empty())) Self(Mutex::new(Heap::empty()))
} }
pub unsafe fn init(&self, range: Range<*mut u8>) { pub unsafe fn init(&self, range: &Range<*mut u8>) {
self.0.lock().init(range); self.0.lock().init(range);
} }
pub unsafe fn reset(&self, range: &Range<*mut u8>) {
self.0.lock().reset(range);
}
pub fn print(&self) { pub fn print(&self) {
self.0.lock().print(); self.0.lock().print();
} }

View File

@@ -15,11 +15,12 @@ use crate::println;
const ALIGN: usize = 0b1000; const ALIGN: usize = 0b1000;
const ALIGN_MASK: usize = !(ALIGN - 1); const ALIGN_MASK: usize = !(ALIGN - 1);
#[repr(C)]
pub struct BlockInfo(usize); pub struct BlockInfo(usize);
impl BlockInfo { impl BlockInfo {
pub const fn new(prev_used: bool, size: usize) -> Self { pub const fn new(size: usize) -> Self {
Self(prev_used as usize | size) Self(true as usize | size)
} }
pub fn prev_used(&self) -> bool { pub fn prev_used(&self) -> bool {
self.0 & 1 == 1 self.0 & 1 == 1
@@ -37,6 +38,7 @@ impl BlockInfo {
pub type BlockPointer = *mut BlockInfo; pub type BlockPointer = *mut BlockInfo;
#[repr(C)]
pub struct FreeBlockInfo { pub struct FreeBlockInfo {
info: BlockInfo, info: BlockInfo,
prev: FreePointer, prev: FreePointer,
@@ -62,6 +64,8 @@ pub struct Heap {
head: FreeBlockInfo, head: FreeBlockInfo,
start: *mut u8, start: *mut u8,
end: *mut u8, end: *mut u8,
#[cfg(debug_assertions)]
pub debug: bool,
} }
impl Heap { impl Heap {
@@ -74,17 +78,24 @@ impl Heap {
}, },
start: null_mut(), start: null_mut(),
end: null_mut(), end: null_mut(),
#[cfg(debug_assertions)]
debug: false,
} }
} }
pub unsafe fn init(&mut self, range: Range<*mut u8>) { pub unsafe fn reset(&mut self, range: &Range<*mut u8>) {
*self = Self::empty();
self.init(range);
}
pub unsafe fn init(&mut self, range: &Range<*mut u8>) {
let head = self.head(); let head = self.head();
let first = range.start as FreePointer; let first = range.start as FreePointer;
let size = range.end as usize - range.start as usize; let size = range.end as usize - range.start as usize;
create_free( create_free(
first, first,
FreeBlockInfo { FreeBlockInfo {
info: BlockInfo::new(true, size), info: BlockInfo::new(size),
next: head, next: head,
prev: head, prev: head,
}, },
@@ -108,38 +119,41 @@ impl Heap {
let free_size = (*free).info.size(); let free_size = (*free).info.size();
// free block found // free block found
if free_size >= size { if free_size >= size {
#[cfg(debug_assertions)]
{
if self.debug {
println!(
"-------- \x1b[92malloc\x1b[0m: {:?}; size 0x{:x}",
free, size
);
}
}
// deal with leftover space // deal with leftover space
let leftover = free_size - size; let leftover = free_size - size;
if leftover < FREE_SIZE { if leftover < FREE_SIZE {
// consume if not enough space for another free block
size = free_size; size = free_size;
let mut next_used = free.byte_add(size) as BlockPointer; let mut next_used = free.byte_add(size) as BlockPointer;
if next_used as *mut u8 == self.end { if next_used as *mut u8 == self.end {
next_used = &mut self.head.info; next_used = &mut self.head.info;
} }
(*next_used).set_prev_used(); (*next_used).set_prev_used();
let prev = (*free).prev; remove_free(free);
let next = (*free).next;
(*prev).next = next;
(*next).prev = prev;
} else { } else {
// otherwise create another free
let new_free = free.byte_add(size); let new_free = free.byte_add(size);
let prev = (*free).prev; self.insert_free((*free).prev, (*free).next, new_free, leftover);
let next = (*free).next;
create_free(
new_free,
FreeBlockInfo {
info: BlockInfo::new(true, leftover),
prev,
next,
},
);
(*prev).next = new_free;
(*next).prev = new_free;
} }
// create block // create block
let used = free as BlockPointer; let used = free as BlockPointer;
(*used) = BlockInfo::new(true, size); (*used) = BlockInfo::new(size);
let data = used.byte_add(USED_SIZE) as *mut u8; let data = used.byte_add(USED_SIZE) as *mut u8;
#[cfg(debug_assertions)]
{
if self.debug {
self.print();
}
}
return data; return data;
} }
} }
@@ -148,43 +162,79 @@ impl Heap {
pub unsafe fn dealloc(&mut self, ptr: *mut u8, _: core::alloc::Layout) { pub unsafe fn dealloc(&mut self, ptr: *mut u8, _: core::alloc::Layout) {
let used = ptr.byte_sub(USED_SIZE) as BlockPointer; let used = ptr.byte_sub(USED_SIZE) as BlockPointer;
#[cfg(debug_assertions)]
{
if self.debug {
println!("-------- \x1b[91mdealloc\x1b[0m: {:?}", used);
}
}
let mut size = (*used).size(); let mut size = (*used).size();
let old_size = size;
let mut addr = used as FreePointer; let mut addr = used as FreePointer;
let mut prev = self.head(); let next = self.next(used);
let mut next = self.head.next; // merge with behind if free
if !(*used).prev_used() { if !(*used).prev_used() {
let prev_free = *(used.byte_sub(PTR_SIZE) as *mut FreePointer); let prev = *(used.byte_sub(PTR_SIZE) as *mut FreePointer);
addr = prev_free; addr = prev;
size += (*prev_free).info.size(); size += (*prev).info.size();
prev = (*prev_free).prev; remove_free(prev);
next = (*prev_free).next;
} }
let mut n_block = used.byte_add(old_size); // merge with after if free
if n_block as *mut u8 != self.end { if !self.is_end(next) && self.is_free(next) {
let mut nn_block = n_block.byte_add((*n_block).size()); size += (*next).size();
if nn_block as *mut u8 == self.end { let ahead = next as FreePointer;
nn_block = &mut self.head.info; remove_free(ahead);
} }
if !(*nn_block).prev_used() { // create & insert
size += (*n_block).size(); let head = self.head();
next = (*next).next; self.insert_free(head, self.head.next, addr, size);
(*next).unset_prev_used();
#[cfg(debug_assertions)]
{
if self.debug {
self.print();
} }
} }
}
unsafe fn insert_free(
&mut self,
prev: FreePointer,
next: FreePointer,
addr: FreePointer,
size: usize,
) {
create_free( create_free(
addr, addr,
FreeBlockInfo { FreeBlockInfo {
info: BlockInfo::new(true, size), info: BlockInfo::new(size),
prev, prev,
next, next,
}, },
); );
(*prev).next = addr; (*prev).next = addr;
(*next).prev = addr; (*next).prev = addr;
if n_block as *mut u8 == self.end {
n_block = &mut self.head.info;
} }
(*n_block).unset_prev_used();
unsafe fn next(&mut self, block: BlockPointer) -> BlockPointer {
let mut next = block.byte_add((*block).size());
// head is "end" of list
if next as *mut u8 == self.end {
next = &mut self.head.info;
}
next
}
unsafe fn is_end(&self, block: BlockPointer) -> bool {
block as *mut u8 == self.end
}
unsafe fn is_used(&mut self, block: BlockPointer) -> bool {
(*self.next(block)).prev_used()
}
unsafe fn is_free(&mut self, block: BlockPointer) -> bool {
!self.is_used(block)
} }
pub fn iter_free(&mut self) -> FreeBlockIter { pub fn iter_free(&mut self) -> FreeBlockIter {
@@ -206,21 +256,35 @@ impl Heap {
pub fn print(&mut self) { pub fn print(&mut self) {
unsafe { unsafe {
println!("heap: {:?} -> {:?}", self.start, self.end); println!("heap: {:?} -> {:?}", self.start, self.end,);
let ptyp = if self.head.prev_used() {
"used"
} else {
"free"
};
println!(" - {:?}: head, prev is {}", self.head(), ptyp);
println!(
" L prev: {:?}, next: {:?}",
self.head.prev, self.head.next
);
for block in self.iter_block() { for block in self.iter_block() {
let size = (*block).size(); let size = (*block).size();
let mut n_block = block.byte_add(size); let mut n_block = block.byte_add(size);
if n_block as *mut u8 == self.end { if n_block as *mut u8 == self.end {
n_block = &mut self.head.info; n_block = &mut self.head.info;
} }
let used = if (*n_block).prev_used() { let used = (*n_block).prev_used();
"used" let typ = if used { "used" } else { "free" };
} else { let ptyp = if (*block).prev_used() { "used" } else { "free" };
"free" println!(
}; " - {:?}: {}, prev is {}, size 0x{:x}",
println!(" - {:?}: {}, size 0x{:x}", block, used, size); block, typ, ptyp, size
);
if !used {
let block = block as FreePointer;
println!(" L prev: {:?}, next: {:?}", (*block).prev, (*block).next);
}
} }
println!();
} }
} }
} }
@@ -232,6 +296,13 @@ unsafe fn create_free(addr: FreePointer, info: FreeBlockInfo) {
*end = addr; *end = addr;
} }
unsafe fn remove_free(block: FreePointer) {
let next = (*block).next;
let prev = (*block).prev;
(*next).prev = prev;
(*prev).next = next;
}
pub struct FreeBlockIter { pub struct FreeBlockIter {
end: FreePointer, end: FreePointer,
prev: FreePointer, prev: FreePointer,

View File

@@ -1,7 +1,12 @@
use crate::ALLOCATOR; use crate::{StartInfo, ALLOCATOR};
#[test_case] #[test_case]
fn memory1() { fn init(_: &StartInfo) {
check_empty();
}
#[test_case]
fn vec(_: &StartInfo) {
let mut test = alloc::vec![1, 2, 3]; let mut test = alloc::vec![1, 2, 3];
assert_eq!(test[0], 1); assert_eq!(test[0], 1);
assert_eq!(test[1], 2); assert_eq!(test[1], 2);
@@ -11,7 +16,7 @@ fn memory1() {
assert_eq!(test[1], 2); assert_eq!(test[1], 2);
assert_eq!(test[2], 3); assert_eq!(test[2], 3);
assert_eq!(test[3], 3); assert_eq!(test[3], 3);
// check allocator check_used(1);
let test2 = alloc::vec![-1, -2, -3, -4]; let test2 = alloc::vec![-1, -2, -3, -4];
assert_eq!(test2[0], -1); assert_eq!(test2[0], -1);
@@ -23,15 +28,16 @@ fn memory1() {
assert_eq!(test[1], 2); assert_eq!(test[1], 2);
assert_eq!(test[2], 3); assert_eq!(test[2], 3);
assert_eq!(test[3], 3); assert_eq!(test[3], 3);
// check allocator check_used(2);
drop(test2); drop(test2);
check_used(1);
drop(test); drop(test);
// check allocator check_empty();
} }
#[test_case] #[test_case]
fn memory2() { fn vec_vec(_: &StartInfo) {
let mut test = alloc::vec::Vec::new(); let mut test = alloc::vec::Vec::new();
for i in 0..4 { for i in 0..4 {
let n = i * 4; let n = i * 4;
@@ -44,20 +50,43 @@ fn memory2() {
assert_eq!(test[i][2], n + 2); assert_eq!(test[i][2], n + 2);
assert_eq!(test[i][3], n + 3); assert_eq!(test[i][3], n + 3);
} }
// check allocator check_used(5);
drop(test); drop(test);
// check allocator check_empty();
} }
#[test_case] #[test_case]
fn memory_reuse() { fn reuse(info: &StartInfo) {
for _ in 0..1000 { let size = info.mem_range.end as usize - info.mem_range.start as usize;
let _: alloc::vec::Vec<i32> = alloc::vec::Vec::with_capacity(10_000_0000); for _ in 0..10 {
let _: alloc::vec::Vec<u8> = alloc::vec::Vec::with_capacity(size / 2);
} }
// check allocator check_empty();
} }
pub fn check_alloc_empty() { pub fn check_empty() {
ALLOCATOR.heap(); let mut free = 0;
for _ in ALLOCATOR.heap().iter_free() {
free += 1;
}
let mut total = 0;
for _ in ALLOCATOR.heap().iter_block() {
total += 1;
}
let used = total - free;
assert_eq!((free, used), (1, 0), "(free, used)");
}
pub fn check_used(num: usize) {
let mut free = 0;
for _ in ALLOCATOR.heap().iter_free() {
free += 1;
}
let mut total = 0;
for _ in ALLOCATOR.heap().iter_block() {
total += 1;
}
let used = total - free;
assert_eq!(used, num);
} }

View File

@@ -1,4 +1,6 @@
use crate::{print, println, qemu}; use core::mem::MaybeUninit;
use crate::{mem::alloc::ALLOCATOR, print, println, qemu, StartInfo};
mod mem; mod mem;
@@ -8,30 +10,58 @@ static mut TESTS: &[&dyn Testable] = &[];
static mut TEST: usize = 0; static mut TEST: usize = 0;
static mut FAILED: usize = 0; static mut FAILED: usize = 0;
static mut START_INFO: MaybeUninit<StartInfo> = MaybeUninit::uninit();
pub trait Testable { pub trait Testable {
fn run(&self) -> (); fn run(&self, info: &StartInfo) -> ();
} }
impl<T: Fn()> Testable for T { // bruh...
fn run(&self) {
print!("test {}... ", core::any::type_name::<T>()); // impl<T: Fn()> Testable for T {
self(); // fn run(&self, _: &StartInfo) {
// // TODO: very temp solution that fails if names are changed lmao
// const START: &str = "kernel::test::";
// let name = &core::any::type_name::<T>()[START.len()..];
// print!("test {}... ", name);
// self();
// println!("\x1b[92mok\x1b[0m");
// }
// }
impl<T: Fn(&StartInfo)> Testable for T {
fn run(&self, info: &StartInfo) {
// TODO: very temp solution that fails if names are changed lmao
const START: &str = "kernel::test::";
let name = &core::any::type_name::<T>()[START.len()..];
print!("test {}... ", name);
self(info);
println!("\x1b[92mok\x1b[0m"); println!("\x1b[92mok\x1b[0m");
} }
} }
pub fn init(info: StartInfo) {
unsafe {
*START_INFO.as_mut_ptr() = info;
}
}
pub fn test_runner(tests: &[&dyn Testable]) { pub fn test_runner(tests: &[&dyn Testable]) {
unsafe { TESTS = core::mem::transmute(tests) }; unsafe {
TESTS = core::mem::transmute(tests);
}
println!("Running {} tests", tests.len()); println!("Running {} tests", tests.len());
run_tests(); run_tests();
} }
pub fn run_tests() -> ! { pub fn run_tests() -> ! {
let info = unsafe {START_INFO.assume_init_ref()};
unsafe { unsafe {
for i in TEST..TESTS.len() { for i in TEST..TESTS.len() {
let test = TESTS[i]; let test = TESTS[i];
TEST += 1; TEST += 1;
test.run(); prepare(info);
test.run(info);
} }
print!( print!(
"results: {}. {} passed; {} failed", "results: {}. {} passed; {} failed",
@@ -48,6 +78,12 @@ pub fn run_tests() -> ! {
qemu::exit(0) qemu::exit(0)
} }
fn prepare(info: &StartInfo) {
unsafe {
ALLOCATOR.reset(&info.mem_range);
}
}
pub fn test_panic(info: &core::panic::PanicInfo) -> ! { pub fn test_panic(info: &core::panic::PanicInfo) -> ! {
println!("\x1b[91mFAILED\x1b[0m"); println!("\x1b[91mFAILED\x1b[0m");
println!("\x1b[93m{}\x1b[0m", info); println!("\x1b[93m{}\x1b[0m", info);

View File

@@ -1,40 +1,39 @@
use core::{mem::zeroed, ops::Deref}; use core::{mem::MaybeUninit, ops::Deref};
pub struct LazyConst<T> { pub struct LazyConst<T> {
#[cfg(not(debug_assertions))]
value: T, value: T,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
value: Option<T>, state: u8,
} }
impl<T> Deref for LazyConst<T> { impl<T> Deref for LazyConst<T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
#[cfg(not(debug_assertions))]
{
&self.value
}
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ if self.state == 0 {
self.value.as_ref().expect("Value was not assigned") panic!("Lazy const for {} not assigned", core::any::type_name::<T>());
} }
&self.value
} }
} }
impl<T> LazyConst<T> { impl<T> LazyConst<T> {
pub const fn new() -> Self { pub const fn new() -> Self {
unsafe { Self { value: zeroed() } } unsafe {
Self {
value: MaybeUninit::zeroed().assume_init(),
#[cfg(debug_assertions)]
state: 0,
}
}
} }
pub fn init(&self, value: T) { pub unsafe fn init(&self, value: T) {
#[cfg(not(debug_assertions))] as_mut(self).value = value;
unsafe {
as_mut(self).value = value
}
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
unsafe { {
as_mut(self).value = Some(value) as_mut(self).state = 1;
} }
} }
} }