From 7b7857a97a8ba0f4f50047848ce2e447a3ae714d Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 12 Sep 2025 20:46:18 -0700 Subject: [PATCH 1/2] blood init system + numerous size decreases also fixes a memory leak in turntbl --- Cargo.toml | 2 +- blood/.gitignore | 2 + blood/Cargo.lock | 14 +++++ blood/Cargo.toml | 25 +++++++++ blood/build.rs | 16 ++++++ blood/src/arch/mod.rs | 2 + blood/src/arch/riscv32/asm/linker.ld | 34 ++++++++++++ blood/src/arch/riscv32/mod.rs | 10 ++++ blood/src/main.rs | 83 ++++++++++++++++++++++++++++ copy_to_fs.sh | 1 + liblbos/src/lib.rs | 2 + liblbos/src/syscalls.rs | 7 +++ src/arch/virt/mod.rs | 1 + src/main.rs | 15 +++-- src/memory.rs | 2 +- src/trafficcontrol.rs | 52 +++++++++++++++-- turntable/src/main.rs | 17 +++++- 17 files changed, 270 insertions(+), 15 deletions(-) create mode 100644 blood/.gitignore create mode 100644 blood/Cargo.lock create mode 100644 blood/Cargo.toml create mode 100644 blood/build.rs create mode 100644 blood/src/arch/mod.rs create mode 100644 blood/src/arch/riscv32/asm/linker.ld create mode 100644 blood/src/arch/riscv32/mod.rs create mode 100644 blood/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 6eb2953..622b280 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = ["liblbos"] -exclude = ["turntable", "makeddi", "ddi", "example"] +exclude = ["blood", "turntable", "makeddi", "ddi", "example"] [package] name = "lbos" diff --git a/blood/.gitignore b/blood/.gitignore new file mode 100644 index 0000000..40d9aca --- /dev/null +++ b/blood/.gitignore @@ -0,0 +1,2 @@ +/target +/.idea \ No newline at end of file diff --git a/blood/Cargo.lock b/blood/Cargo.lock new file mode 100644 index 0000000..f5140f8 --- /dev/null +++ b/blood/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "blood" +version = "0.1.0" +dependencies = [ + "liblbos", +] + +[[package]] +name = "liblbos" +version = "0.1.1" diff --git a/blood/Cargo.toml b/blood/Cargo.toml new file mode 100644 index 0000000..6980835 --- /dev/null +++ b/blood/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "blood" +version = "0.1.0" +edition = "2024" + +[dependencies] +liblbos = { path = "../liblbos" } + +[profile.dev] +panic = "abort" +opt-level = "s" +debug-assertions = false +overflow-checks = false + +[profile.release] +panic = "abort" +opt-level = "z" +debug-assertions = false +overflow-checks = false +lto = true +codegen-units = 1 + +[features] +default = ["arch_riscv32"] +arch_riscv32 = ["liblbos/arch_riscv32"] \ No newline at end of file diff --git a/blood/build.rs b/blood/build.rs new file mode 100644 index 0000000..0cc9f78 --- /dev/null +++ b/blood/build.rs @@ -0,0 +1,16 @@ +fn main() { + let arch = std::env::var("LBOS_ARCH").unwrap_or("riscv32".to_string()); + println!("cargo:rerun-if-env-changed=LBOS_ARCH"); + println!("cargo:rustc-cfg=feature=\"arch_{}\"", arch); + + println!("cargo:rerun-if-changed=src/arch/{}/asm", arch); + + // specify the linker.ld script + println!("cargo:rustc-link-arg=-Tsrc/arch/{arch}/asm/linker.ld"); + + // output relocation info + println!("cargo:rustc-link-arg=--emit-relocs"); + + // don't page align sections + println!("cargo:rustc-link-arg=-n"); +} \ No newline at end of file diff --git a/blood/src/arch/mod.rs b/blood/src/arch/mod.rs new file mode 100644 index 0000000..cdd1bdb --- /dev/null +++ b/blood/src/arch/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "arch_riscv32")] +mod riscv32; \ No newline at end of file diff --git a/blood/src/arch/riscv32/asm/linker.ld b/blood/src/arch/riscv32/asm/linker.ld new file mode 100644 index 0000000..5a9155c --- /dev/null +++ b/blood/src/arch/riscv32/asm/linker.ld @@ -0,0 +1,34 @@ +OUTPUT_FORMAT( "elf32-littleriscv" ) +OUTPUT_ARCH( "riscv" ) + +ENTRY( _start ) + +MEMORY +{ + program (wxa) : ORIGIN = 0x0, LENGTH = 20480 +} + +PHDRS +{ + prg PT_LOAD ; +} + +SECTIONS { + .text : { + . = ALIGN(512); + *(.text .text.*) + } >program AT>program :prg + + .rodata : { + *(.rodata .rodata.*) + } >program AT>program :prg + + .data : { + *(.sdata .sdata.*) *(.data .data.*) + } >program AT>program :prg + + .bss : { + *(.sbss .sbss.*) + *(.bss .bss.*) + } >program AT>program :prg +} \ No newline at end of file diff --git a/blood/src/arch/riscv32/mod.rs b/blood/src/arch/riscv32/mod.rs new file mode 100644 index 0000000..693d814 --- /dev/null +++ b/blood/src/arch/riscv32/mod.rs @@ -0,0 +1,10 @@ +use crate::main; + +#[unsafe(no_mangle)] +extern "C" fn _start() -> isize { + unsafe { + main(); + } + + 0 +} \ No newline at end of file diff --git a/blood/src/main.rs b/blood/src/main.rs new file mode 100644 index 0000000..df2eaca --- /dev/null +++ b/blood/src/main.rs @@ -0,0 +1,83 @@ +#![no_std] +#![no_main] + +use liblbos::fs::{DirectoryReader, FileSystem}; + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + liblbos::syscalls::write_terminal(b"abort\n"); + loop {} +} +mod arch; + +#[unsafe(no_mangle)] +extern "C" fn main() { + loop { + const TURNTBL: &str = "TURNTBL.DDI"; + let mut fs = FileSystem::empty(); + if liblbos::ktask::ktask_fsopen(&mut fs) != 0 { + liblbos::syscalls::write_terminal(b"failed to open fs\n"); + continue; + } + let mut dir = DirectoryReader::empty(); + if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, b"/") != 0 { + liblbos::syscalls::write_terminal(b"failed to open dir\n"); + continue; + } + let mut record = liblbos::fs::Record { + name: [0; 12], + record_type: 0, + target: 0, + total_size_bytes: 0, + }; + fn namecmp(record: &liblbos::fs::Record, name: &[u8]) -> bool { + for (i, &x) in record.name.iter().enumerate() { + if x == 0 { + return &record.name[..i] == name; + } + } + false + } + while !namecmp(&record, TURNTBL.as_bytes()) { + if liblbos::ktask::ktask_fsreaddir(&fs, &mut dir, &mut record) != 0 { + liblbos::syscalls::write_terminal(b"failed to read dir\n"); + continue; + } + if record.record_type == 0 { + liblbos::syscalls::write_terminal(b"unexpected eod\n"); + } + } + // found turntable, load it + let mut reader = liblbos::fs::FileReader::empty(); + if liblbos::ktask::ktask_fsopenfile(&fs, &record, &mut reader) != 0 { + liblbos::syscalls::write_terminal(b"failed to open file\n"); + continue; + } + let size = record.total_size_bytes as usize; + let buf = liblbos::syscalls::alloc_blocks(size.div_ceil(512)); + if liblbos::ktask::ktask_fsreadfile(&fs, &mut reader, unsafe { core::slice::from_raw_parts_mut(buf as *mut _, size) }) != 0 { + liblbos::syscalls::write_terminal(b"failed to read file\n"); + } + + let mut task_setup = liblbos::TaskSetup { + epc: 0, + stack_block_count: 2, + ddi_first_addr: 0, + ddi_size: 0, + environment: 0, + wait_for_task_exit: false, + }; + + if liblbos::ktask::ktask_loadddifile(buf, size, &mut task_setup as *mut _ as usize) != 0 { + liblbos::syscalls::write_terminal(b"failed to load ddi\n"); + liblbos::syscalls::free_blocks(buf, size.div_ceil(512)); + continue; + } + + liblbos::syscalls::free_blocks(buf, size.div_ceil(512)); + + liblbos::syscalls::create_task(task_setup); + + liblbos::syscalls::wait_until_alone(); + } +} diff --git a/copy_to_fs.sh b/copy_to_fs.sh index 1ba2618..3e1494e 100755 --- a/copy_to_fs.sh +++ b/copy_to_fs.sh @@ -8,6 +8,7 @@ mkdir -p mount mount "${storage_device}" mount echo "COPYING..." +cp -v blood/target/riscv32imac-unknown-none-elf/release/BLOOD.DDI mount cp -v turntable/target/riscv32imac-unknown-none-elf/release/TURNTBL.DDI mount cp -v example/target/riscv32imac-unknown-none-elf/release/EXAMPLE.DDI mount diff --git a/liblbos/src/lib.rs b/liblbos/src/lib.rs index 8f1b05c..707fad2 100644 --- a/liblbos/src/lib.rs +++ b/liblbos/src/lib.rs @@ -9,6 +9,8 @@ mod arch; #[repr(C)] pub struct TaskSetup { pub epc: usize, + pub stack_block_count: usize, + /// MUST BE NON-ZERO pub ddi_first_addr: usize, pub ddi_size: usize, pub environment: usize, diff --git a/liblbos/src/syscalls.rs b/liblbos/src/syscalls.rs index 795c655..b999052 100644 --- a/liblbos/src/syscalls.rs +++ b/liblbos/src/syscalls.rs @@ -81,6 +81,8 @@ pub enum SysCall { /// if there is no device providing a gpu, this will not do anything FlushFramebufferRect = 18, + /// doens't return until all other tasks besides the current task and the ktask have exited + WaitUntilAlone = 333, /// initializes kernel, ONLY CALL ONCE! IN FACT, YOU PROBABLY NEVER NEED TO CALL THIS InitKernel = 666 } @@ -110,6 +112,7 @@ pub fn usize2sc(u: usize) -> SysCall { 16 => SysCall::EnableFramebufferConsole, 17 => SysCall::FramebufferPointer, 18 => SysCall::FlushFramebufferRect, + 333 => SysCall::WaitUntilAlone, 666 => SysCall::InitKernel, _ => SysCall::NoAction, } @@ -199,6 +202,10 @@ pub fn flush_framebuffer_rect(x: usize, y: usize, w: usize, h: usize) { syscall(SysCall::FlushFramebufferRect, x, y, w, h, 0, 0); } +pub fn wait_until_alone() { + syscall(SysCall::WaitUntilAlone, 0, 0, 0, 0, 0, 0); +} + pub fn init_kernel() { syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0); } \ No newline at end of file diff --git a/src/arch/virt/mod.rs b/src/arch/virt/mod.rs index 30f6422..1fc6daf 100644 --- a/src/arch/virt/mod.rs +++ b/src/arch/virt/mod.rs @@ -81,6 +81,7 @@ extern "C" fn _virt_init() -> ! { create_task(TaskSetup { epc: ktask as usize, + stack_block_count: 2, ddi_first_addr: 0, ddi_size: 0, environment: 0, diff --git a/src/main.rs b/src/main.rs index 9de0f95..de238f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,7 @@ pub extern "C" fn ktask() -> ! { syscalls::init_kernel(); print(b"kernel ready!\n"); - // load TURNTBL.DDI + // load BLOOD.DDI { fn fserr() { @@ -62,13 +62,13 @@ pub extern "C" fn ktask() -> ! { record_name == name } - while !namecmp(&record, b"TURNTBL.DDI") { + while !namecmp(&record, b"BLOOD.DDI") { if !fs::read_one_record( unsafe { &*(&fs as *const _ as *const _) }, unsafe { &mut *(&mut dir as *mut _ as *mut _) }, unsafe { &mut *(&mut record as *mut _ as *mut _) } ) { - print(b"TURNTBL.DDI NOT FOUND ):"); + print(b"BLOOD.DDI NOT FOUND ):"); print(b"\n"); rough_panic(['t', 't', 'b']); } @@ -89,23 +89,28 @@ pub extern "C" fn ktask() -> ! { unsafe { &mut *(&mut reader as *mut _ as *mut _) }, unsafe { core::slice::from_raw_parts_mut(buf as *mut _, size) } ) != 0 { - print(b"TURNTBL.DDI READ ERROR ):"); + print(b"BLOOD.DDI READ ERROR ):"); print(b"\n"); rough_panic(['f', 'r', 'e']); } // load the ddi let file_contents = unsafe { core::slice::from_raw_parts(buf as *const _, size) }; + let file_size = size; if let Ok((entrypoint, first_addr, size)) = ddi::load_ddi(&file_contents) { + // free the buffer + syscalls::free_blocks(buf, file_size.div_ceil(512)); + create_task(TaskSetup { epc: entrypoint, + stack_block_count: 1, ddi_first_addr: first_addr, ddi_size: size, environment: 0, wait_for_task_exit: false, }); } else { - print(b"TURNTBL.DDI INVALID DDI ):"); + print(b"BLOOD.DDI INVALID DDI ):"); print(b"\n"); rough_panic(['t', 't', 'b']); } diff --git a/src/memory.rs b/src/memory.rs index 8a29588..8f4e2d1 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -26,7 +26,7 @@ impl MemoryManager { uart.putstr("heap_start: "); uart.put_bytes(&u32_hex(_heap_start as u32)); uart.putstr("totalmem: "); - uart.put_bytes(&u32_hex(_heap_size as u32)); + uart.put_bytes(&u32_hex(_heap_size as u32 / BLOCK_SIZE as u32)); uart.putstr("\n"); } } diff --git a/src/trafficcontrol.rs b/src/trafficcontrol.rs index 05cd4fe..4d620f1 100644 --- a/src/trafficcontrol.rs +++ b/src/trafficcontrol.rs @@ -12,10 +12,9 @@ pub enum TaskWait { HardBlockDevOperation = 1 << 0, WaitForNotification = 1 << 1, WaitForTaskExit = 1 << 2, + WaitUntilAlone = 1 << 7, } -pub const STACK_SIZE_IN_BLOCKS: usize = 8; - pub const MAX_TASKS: usize = 8; pub static TC: Spinlock = Spinlock::new(TrafficControl::empty()); @@ -69,13 +68,15 @@ pub fn handle_syscall( SysCall::CreateTask => { let add = a1 as *mut TaskSetup; + let stack_size_in_blocks = unsafe { (*add).stack_block_count }; + tc.init_mem_if_not(); let sp = unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() - .alloc_n_blocks(STACK_SIZE_IN_BLOCKS) - } + (BLOCK_SIZE * STACK_SIZE_IN_BLOCKS); + .alloc_n_blocks(stack_size_in_blocks) + } + (BLOCK_SIZE * stack_size_in_blocks); #[cfg(feature = "arch_virt")] let t = Some(Task { @@ -83,6 +84,7 @@ pub fn handle_syscall( environment: unsafe { (*add).environment }, want_exit: false, sp, + stack_size_in_blocks, wait: 0, incoming_notifications: [const { None }; MAX_TASKS], ackwait: [0; MAX_TASKS], @@ -90,7 +92,7 @@ pub fn handle_syscall( ddi_mem_start: unsafe { (*add).ddi_first_addr }, - ddi_mem_blocks_count: unsafe { (*add).ddi_size / BLOCK_SIZE }, + ddi_mem_blocks_count: unsafe { (*add).ddi_size.div_ceil(BLOCK_SIZE) }, trap_frame: arch::virt::tasks::setup_task(sp), }); @@ -371,6 +373,13 @@ pub fn handle_syscall( crate::dev::framebuffer_push(&mut tc, x, y, w, h); 0 } + SysCall::WaitUntilAlone => { + let ci = tc.current; + let mut task = tc.tasks[ci].as_mut().unwrap(); + task.wait |= TaskWait::WaitForTaskExit as u32; + suspend = true; + 0 + } }, suspend, ) @@ -380,7 +389,12 @@ pub fn context_switch<'a>(tc: &'a mut TrafficControl, current: Task) -> Option<& let ci = tc.current; tc.tasks[ci] = Some(current); + let mut remaining_tasks = 0; + for i in 0..tc.tasks.len() { + if tc.tasks[i].is_some() { + remaining_tasks += 1; + } let want_exit = tc.tasks[i] .as_ref() .map(|task| task.want_exit) @@ -390,12 +404,13 @@ pub fn context_switch<'a>(tc: &'a mut TrafficControl, current: Task) -> Option<& let sp = tc.tasks[i].as_ref().map(|v| v.sp).unwrap_or(0); let ddi_start_block = tc.tasks[i].as_ref().map(|v| v.ddi_mem_start).unwrap_or(0); let ddi_blocks_count = tc.tasks[i].as_ref().map(|v| v.ddi_mem_blocks_count).unwrap_or(0); + let stack_size_in_blocks = tc.tasks[i].as_ref().map(|v| v.stack_size_in_blocks).unwrap_or(0); if sp != 0 { unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() - .free_n_blocks(sp - (BLOCK_SIZE * STACK_SIZE_IN_BLOCKS), STACK_SIZE_IN_BLOCKS) + .free_n_blocks(sp - (BLOCK_SIZE * stack_size_in_blocks), stack_size_in_blocks) }; unsafe { tc.memory_manager.as_mut().unwrap_unchecked() @@ -414,6 +429,17 @@ pub fn context_switch<'a>(tc: &'a mut TrafficControl, current: Task) -> Option<& } } + // if remaining_tasks <= 2, then we need to wake up WaitUntilAlone tasks + if remaining_tasks <= 2 { + for task in &mut tc.tasks { + if let Some(task) = task { + if task.wait & TaskWait::WaitForTaskExit as u32 != 0 { + task.wait &= !(TaskWait::WaitForTaskExit as u32); + } + } + } + } + let mut next_task = tc.current + 1; if next_task >= MAX_TASKS { next_task = 0; @@ -421,6 +447,19 @@ pub fn context_switch<'a>(tc: &'a mut TrafficControl, current: Task) -> Option<& for _ in 0..(MAX_TASKS + 1) { if let Some(task) = tc.tasks[next_task].as_ref() { + // check stack pointer and if it's oob + #[cfg(feature = "arch_virt")] + { + if task.sp != 0 { + if task.trap_frame.regs[2] < task.sp - (task.stack_size_in_blocks * BLOCK_SIZE) { + let uart = serial_port(); + if let Some(uart) = uart { + uart.putstr("stack overflow\n"); + } + rough_panic([';', '-', ';']) + } + } + } if task.wait == 0 && { let mut waiting = false; for v in &task.ackwait { @@ -464,6 +503,7 @@ pub struct Task { pub want_exit: bool, /// THE ORIGINAL STACK POINTER THAT THE TASK STARTED WITH pub sp: usize, + pub stack_size_in_blocks: usize, pub wait: u32, pub incoming_notifications: [Option; MAX_TASKS], pub ackwait: [u8; MAX_TASKS], // 3 = they have not yet received the notification, 1 = they have received the notification, 0 = we know they received the notification, or don't care diff --git a/turntable/src/main.rs b/turntable/src/main.rs index af1f3e6..f8b613a 100644 --- a/turntable/src/main.rs +++ b/turntable/src/main.rs @@ -190,6 +190,7 @@ fn attempt_run_file(env: &Environment<'_>, name: &str) { let mut task_setup = liblbos::TaskSetup { epc: 0, + stack_block_count: 8, ddi_first_addr: 0, ddi_size: 0, environment: &e as *const _ as usize, @@ -233,6 +234,10 @@ fn execute(cmd: &str, environment: &mut Environment<'_>) { if NO_EXIT.load(Ordering::Relaxed) { println("cannot exit as task 1"); } else { + if CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize != 0 { + // free + liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512)); + } liblbos::syscalls::exit(); } } @@ -260,8 +265,7 @@ fn hex(mut n: u8) -> [u8; 2] { mod arch; mod terminal; -#[unsafe(no_mangle)] -extern "C" fn main() { +fn main_wrapper() { let tid = liblbos::syscalls::current_task(); NO_EXIT.store(tid == 1, Ordering::Relaxed); @@ -341,3 +345,12 @@ extern "C" fn main() { } } } + +#[unsafe(no_mangle)] +extern "C" fn main() { + main_wrapper(); + if CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize != 0 { + // free + liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512)); + } +} \ No newline at end of file From 8bfe43427dcbe15a18e538f300a7998d4a02e10b Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 12 Sep 2025 20:51:50 -0700 Subject: [PATCH 2/2] turntbl now exits upon running a new program this should allow for users to take advantage of more system memory --- turntable/src/main.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/turntable/src/main.rs b/turntable/src/main.rs index f8b613a..3e92f80 100644 --- a/turntable/src/main.rs +++ b/turntable/src/main.rs @@ -187,14 +187,16 @@ fn attempt_run_file(env: &Environment<'_>, name: &str) { current_directory_path: CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, current_directory_path_len: CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed), }; + // fixme: turntable task needs to exit, but it owns the environment memory. + // fixme: we need to somehow pass the environment to the task, and have it free the memory let mut task_setup = liblbos::TaskSetup { epc: 0, stack_block_count: 8, ddi_first_addr: 0, ddi_size: 0, - environment: &e as *const _ as usize, - wait_for_task_exit: true, + environment: 0, + wait_for_task_exit: false, }; if liblbos::ktask::ktask_loadddifile(buf, size, &mut task_setup as *mut _ as usize) != 0 { @@ -207,11 +209,11 @@ fn attempt_run_file(env: &Environment<'_>, name: &str) { liblbos::syscalls::create_task(task_setup); - // yeah we're extending its lifetime - #[allow(clippy::drop_non_drop)] - drop(e); - - println("\ntask exited\n"); + if CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize != 0 { + // free + liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512)); + } + liblbos::syscalls::exit(); } fn execute(cmd: &str, environment: &mut Environment<'_>) {