From e004212c9c71a4173584e62b4cd9022cc0b56614 Mon Sep 17 00:00:00 2001 From: husky Date: Mon, 8 Sep 2025 22:30:10 -0700 Subject: [PATCH 01/20] support reloc type 28 and allow turntbl to exit --- ddi/src/arch/riscv32/mod.rs | 20 ++++++++++++++++++++ turntable/src/main.rs | 13 ++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/ddi/src/arch/riscv32/mod.rs b/ddi/src/arch/riscv32/mod.rs index e9ee3cd..ceace48 100644 --- a/ddi/src/arch/riscv32/mod.rs +++ b/ddi/src/arch/riscv32/mod.rs @@ -76,6 +76,19 @@ fn patch_lo12_iins(mut instruction: u32, pointer: u32) -> u32 { instruction } +fn patch_lo12_sins(mut instruction: u32, pointer: u32) -> u32 { + // bits 0..5 are shifted seven left + // bits 5..12 are shifted 25 left + const MASK_1: u32 = 0x1F; + const TARGET_1: u32 = 0xF80; + const MASK_2: u32 = 0xFE0; + const TARGET_2: u32 = 0xFE000000; + let lo_1 = ((pointer & MASK_1) << 7) & TARGET_1; + let lo_2 = ((pointer & MASK_2) << 20) & TARGET_2; + instruction = (instruction & !(TARGET_1 | TARGET_2)) | lo_1 | lo_2; + instruction +} + fn sap_addr(target_pointer: u32, target_segment_base: u32, relocation_pointer: u32, current_segment_base: u32) -> u32 { (target_pointer).wrapping_sub(relocation_pointer) } @@ -102,6 +115,13 @@ pub fn apply_relocation(segment_buffer: &mut [u8], current_segment_base: u32, ta i_ins = patch_lo12_iins(i_ins, addr); segment_buffer[i_ins_ptr..i_ins_ptr+4].copy_from_slice(&i_ins.to_le_bytes()); } + x if x == RiscVRelocationType::Lo12S as u16 => { + let mut s_ins_ptr = relocation_header.relocation_pointer as usize; + let mut s_ins = u32::from_le_bytes(segment_buffer[s_ins_ptr..s_ins_ptr+4].try_into().unwrap()); + let addr = relocation_header.target_pointer as u32 + target_segment_base; + s_ins = patch_lo12_sins(s_ins, addr); + segment_buffer[s_ins_ptr..s_ins_ptr+4].copy_from_slice(&s_ins.to_le_bytes()); + } x => { unhandled_callback(x); } diff --git a/turntable/src/main.rs b/turntable/src/main.rs index c00372c..d670ae9 100644 --- a/turntable/src/main.rs +++ b/turntable/src/main.rs @@ -1,10 +1,11 @@ #![no_std] #![no_main] +use core::sync::atomic::{AtomicBool, Ordering}; use crate::terminal::{print, println}; use liblbos::fs::{DirectoryReader, FileSystem}; -static TEST: &[u8] = b"test"; +static NO_EXIT: AtomicBool = AtomicBool::new(false); #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { @@ -131,6 +132,13 @@ fn execute(cmd: &str) { "ls" => { lsdir(); } + "exit" => { + if NO_EXIT.load(Ordering::Relaxed) { + println("cannot exit as task 1"); + } else { + liblbos::syscalls::exit(); + } + } _ => { attempt_run_file(cmd); } @@ -157,6 +165,9 @@ mod terminal; #[unsafe(no_mangle)] extern "C" fn main() { + let tid = liblbos::syscalls::current_task(); + NO_EXIT.store(tid == 1, Ordering::Relaxed); + print("\n\n"); print("turntable v"); const VERSION: &str = env!("CARGO_PKG_VERSION"); From 960de83bbc73b4bb3e7c90cf94fb5212486aab4a Mon Sep 17 00:00:00 2001 From: husky Date: Tue, 9 Sep 2025 09:17:42 -0700 Subject: [PATCH 02/20] handle directories --- liblbos/src/lib.rs | 7 +++ liblbos/src/syscalls.rs | 7 +++ src/arch/virt/mod.rs | 1 + src/fs/fat32.rs | 4 ++ src/fs/mod.rs | 90 +++++++++++++++++++++------ src/main.rs | 1 + src/trafficcontrol.rs | 10 +++ turntable/src/main.rs | 132 +++++++++++++++++++++++++++++++++++++--- 8 files changed, 225 insertions(+), 27 deletions(-) diff --git a/liblbos/src/lib.rs b/liblbos/src/lib.rs index 4ec169c..13c5a0d 100644 --- a/liblbos/src/lib.rs +++ b/liblbos/src/lib.rs @@ -10,5 +10,12 @@ pub struct TaskSetup { pub epc: usize, pub ddi_first_addr: usize, pub ddi_size: usize, + pub environment: usize, pub wait_for_task_exit: bool, } + +#[repr(C)] +pub struct Environment { + pub current_directory_path: usize, + pub current_directory_path_len: usize, +} \ No newline at end of file diff --git a/liblbos/src/syscalls.rs b/liblbos/src/syscalls.rs index 9c9f772..c8c3210 100644 --- a/liblbos/src/syscalls.rs +++ b/liblbos/src/syscalls.rs @@ -65,6 +65,8 @@ pub enum SysCall { /// waits for a sent notification to be received /// (taskid) -> 0 WaitForNotifAck = 13, + /// returns the environment pointer for the given task + EnvironmentPointer = 14, /// initializes kernel, ONLY CALL ONCE! IN FACT, YOU PROBABLY NEVER NEED TO CALL THIS InitKernel = 666 @@ -90,6 +92,7 @@ pub fn usize2sc(u: usize) -> SysCall { 11 => SysCall::WaitForNotification, 12 => SysCall::PendingNotifications, 13 => SysCall::WaitForNotifAck, + 14 => SysCall::EnvironmentPointer, 666 => SysCall::InitKernel, _ => SysCall::NoAction, } @@ -159,6 +162,10 @@ pub fn wait_for_notif_ack(taskid: u8) { syscall(SysCall::WaitForNotifAck, taskid as usize, 0, 0, 0, 0, 0); } +pub fn environment_pointer() -> usize { + syscall(SysCall::EnvironmentPointer, 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 d007fd9..2c694d1 100644 --- a/src/arch/virt/mod.rs +++ b/src/arch/virt/mod.rs @@ -85,6 +85,7 @@ extern "C" fn _virt_init() -> ! { epc: ktask as usize, ddi_first_addr: 0, ddi_size: 0, + environment: 0, wait_for_task_exit: false, }); diff --git a/src/fs/fat32.rs b/src/fs/fat32.rs index 986356c..507e1e0 100644 --- a/src/fs/fat32.rs +++ b/src/fs/fat32.rs @@ -176,6 +176,10 @@ pub fn reader_from_cluster(bpb: &BPBUseful, cluster: usize) -> Fat32FileReader { } } +pub fn root_target(bpb: &BPBUseful) -> u32 { + bpb.root_cluster as u32 +} + pub fn root_reader(bpb: &BPBUseful) -> Fat32DirectoryReader { let cluster = bpb.root_cluster as usize; Fat32DirectoryReader { diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 72850c9..b0a2c68 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -38,6 +38,13 @@ pub fn open_fs(fs: &mut FileSystem) -> bool { } } +pub fn root_target(fs: &FileSystem) -> u32 { + #[cfg(feature = "fs_fat32")] + { + fat32::root_target(fs) + } +} + pub fn root_reader(fs: &FileSystem) -> DirectoryReader { #[cfg(feature = "fs_fat32")] { @@ -94,8 +101,8 @@ pub fn read_one_record(fs: &FileSystem, reader: &mut DirectoryReader, record: &m } } -pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryReader) -> usize { - path.make_ascii_uppercase(); +pub fn open_directory(fs: &FileSystem, path: &[u8], reader: &mut DirectoryReader) -> usize { + //path.make_ascii_uppercase(); if path[0] != b'/' { return 3; } @@ -104,6 +111,10 @@ pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryRe *reader = root_reader; 0 } else { + let mut target_stack = [0u32; 128]; // todo: make this actually dynamic + target_stack[0] = root_target(fs); + let mut target_stack_idx = 1; + let mut record = Record { name: [0; 12], record_type: 0, @@ -111,28 +122,67 @@ pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryRe total_size_bytes: 0, }; let mut current_reader = root_reader; - let mut next_path_start = path[1..].iter().position(|&x| x == b'/').unwrap_or(path.len()); + let mut next_path_start = path[1..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + 1; let mut search_for = &path[1..next_path_start]; - loop { - if !read_one_record(fs, &mut current_reader, &mut record) { - return 1; + 'into_next_dir: loop { + if search_for == b"." { + // do nothing and skip this part of the path + let new_path_start = next_path_start + 1; + if new_path_start >= path.len() { + // we're at the end of the path + *reader = current_reader; + return 0; + } + next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start; + search_for = &path[new_path_start..next_path_start]; + target_stack[target_stack_idx] = record.target; + target_stack_idx += 1; + continue 'into_next_dir; + } else if search_for == b".." { + // go back up + if target_stack_idx < 2 { + directory_reader(fs, &mut current_reader, target_stack[0]); + *reader = current_reader; + return 0; + } + target_stack_idx -= 2; // skip the current directory and the one before that + let prev_target = target_stack[target_stack_idx]; + let new_path_start = next_path_start + 1; + if new_path_start >= path.len() { + // we're at the end of the path + directory_reader(fs, &mut current_reader, prev_target); + *reader = current_reader; + return 0; + } + next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start; + search_for = &path[new_path_start..next_path_start]; + directory_reader(fs, &mut current_reader, prev_target); + continue 'into_next_dir; } - let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)]; - if record_name == search_for { - // this is the next record we want - if record.record_type == RecordType::Directory as u8 { - let new_path_start = next_path_start + 1; // skip the slash - if new_path_start >= path.len() { - // we're at the end of the path + loop { + if !read_one_record(fs, &mut current_reader, &mut record) { + return 1; + } + let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)]; + if record_name == search_for { + // this is the next record we want + if record.record_type == RecordType::Directory as u8 { + let new_path_start = next_path_start + 1; // skip the slash + if new_path_start >= path.len() { + // we're at the end of the path + directory_reader(fs, &mut current_reader, record.target); + *reader = current_reader; + return 0; + } + next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start; + search_for = &path[new_path_start..next_path_start]; directory_reader(fs, &mut current_reader, record.target); - *reader = current_reader; - return 0; + target_stack[target_stack_idx] = record.target; + target_stack_idx += 1; + continue 'into_next_dir; + } else { + return 2; } - next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start; - search_for = &path[next_path_start..next_path_start]; - directory_reader(fs, &mut current_reader, record.target); - } else { - return 2; } } } diff --git a/src/main.rs b/src/main.rs index 4129030..0ddbee8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -110,6 +110,7 @@ pub extern "C" fn ktask() -> ! { epc: entrypoint, ddi_first_addr: first_addr, ddi_size: size, + environment: 0, wait_for_task_exit: false, }); } else { diff --git a/src/trafficcontrol.rs b/src/trafficcontrol.rs index a9719cd..5b30512 100644 --- a/src/trafficcontrol.rs +++ b/src/trafficcontrol.rs @@ -84,6 +84,7 @@ pub fn handle_syscall( #[cfg(feature = "arch_virt")] let t = Some(Task { epc: unsafe { (*add).epc }, + environment: unsafe { (*add).environment }, want_exit: false, sp, wait: 0, @@ -319,6 +320,14 @@ pub fn handle_syscall( } 0 } + SysCall::EnvironmentPointer => { + let ci = tc.current; + if let Some(task) = tc.tasks[ci].as_ref() { + task.environment + } else { + 0 + } + } }, suspend, ) @@ -413,6 +422,7 @@ pub struct TrafficControl { pub struct Task { #[cfg(feature = "arch_virt")] pub epc: usize, + pub environment: usize, pub want_exit: bool, /// THE ORIGINAL STACK POINTER THAT THE TASK STARTED WITH pub sp: usize, diff --git a/turntable/src/main.rs b/turntable/src/main.rs index d670ae9..e0b6fb2 100644 --- a/turntable/src/main.rs +++ b/turntable/src/main.rs @@ -1,11 +1,17 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::terminal::{print, println}; use liblbos::fs::{DirectoryReader, FileSystem}; static NO_EXIT: AtomicBool = AtomicBool::new(false); +static CURRENT_DIR_PATH: AtomicPtr = AtomicPtr::new(0 as *mut _); +static CURRENT_DIR_PATH_LEN: AtomicUsize = AtomicUsize::new(0); + +struct Environment<'a> { + pub current_directory_path: &'a [u8], +} #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { @@ -13,14 +19,83 @@ fn panic(info: &core::panic::PanicInfo) -> ! { loop {} } -fn lsdir() { +fn cd(new_path: &str, environment: &mut Environment<'_>) { + let old_path = environment.current_directory_path; + let abs_new_path = if new_path.starts_with('/') { + // absolute path, replaces old path + let new_path_buf = liblbos::syscalls::alloc_blocks(new_path.len().div_ceil(512)); + let new_path_buf = unsafe { core::slice::from_raw_parts_mut(new_path_buf as *mut _, new_path.len()) }; + // copy + { + for (i, &x) in new_path.as_bytes().iter().enumerate() { + new_path_buf[i] = x; + } + } + new_path_buf + } else { + // todo: ".." is currently handled on the ktask level, however... + // todo: we should evaluate this and instead modify the absolute path to remove the ".." + // todo: otherwise, we're bound to end up with a path like "/a/b/../../c" which is not nice + // relative path, append + let new_path_len = old_path.len() + new_path.len() + 1; + let new_path_buf = liblbos::syscalls::alloc_blocks(new_path_len.div_ceil(512)); + let new_path_buf = unsafe { core::slice::from_raw_parts_mut(new_path_buf as *mut _, new_path_len) }; + // copy old path + { + for (i, &x) in old_path.iter().enumerate() { + new_path_buf[i] = x; + } + } + // copy new path + { + for (i, &x) in new_path.as_bytes().iter().enumerate() { + new_path_buf[old_path.len() + i] = x; + } + } + // add separator + new_path_buf[old_path.len() + new_path.len()] = b'/'; + + new_path_buf + }; + + // check to make sure the new path is valid + { + let mut fs = FileSystem::empty(); + if liblbos::ktask::ktask_fsopen(&mut fs) != 0 { + println("failed to open fs"); + // free + liblbos::syscalls::free_blocks(abs_new_path.as_ptr() as usize, abs_new_path.len().div_ceil(512)); + return; + } + let mut dir = DirectoryReader::empty(); + if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, abs_new_path) != 0 { + println("invalid dir"); + // free + liblbos::syscalls::free_blocks(abs_new_path.as_ptr() as usize, abs_new_path.len().div_ceil(512)); + return; + } + } + + // it is valid, update everything + { + // free old path + liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512)); + } + CURRENT_DIR_PATH.store(abs_new_path.as_ptr() as *mut _, Ordering::Relaxed); + CURRENT_DIR_PATH_LEN.store(abs_new_path.len(), Ordering::Relaxed); + environment.current_directory_path = unsafe { + core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut _, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed)) + }; +} + +fn lsdir(env: &Environment<'_>) { let mut fs = FileSystem::empty(); if liblbos::ktask::ktask_fsopen(&mut fs) != 0 { println("failed to open fs"); return; } let mut dir = DirectoryReader::empty(); - if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, b"/") != 0 { + if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, env.current_directory_path) != 0 { println("failed to open dir"); return; } @@ -107,6 +182,7 @@ fn attempt_run_file(name: &str) { epc: 0, ddi_first_addr: 0, ddi_size: 0, + environment: 0, wait_for_task_exit: true, }; @@ -118,19 +194,26 @@ fn attempt_run_file(name: &str) { liblbos::syscalls::free_blocks(buf, size.div_ceil(512)); - let task_id = liblbos::syscalls::create_task(task_setup); + liblbos::syscalls::create_task(task_setup); println("\ntask exited\n"); } -fn execute(cmd: &str) { +fn execute(cmd: &str, environment: &mut Environment<'_>) { print("\n"); let mut split = cmd.split_ascii_whitespace(); if let Some(cmd) = split.next() { match cmd { "ls" => { - lsdir(); + lsdir(environment); + } + "cd" => { + if let Some(path) = split.next() { + cd(path, environment); + } else { + println("cd requires a path"); + } } "exit" => { if NO_EXIT.load(Ordering::Relaxed) { @@ -168,6 +251,40 @@ extern "C" fn main() { let tid = liblbos::syscalls::current_task(); NO_EXIT.store(tid == 1, Ordering::Relaxed); + let environment = liblbos::syscalls::environment_pointer(); + let mut environment = if environment == 0 { + // no environment, construct it ourself + static DEFAULT_CURRENT_DIRECTORY: &[u8] = b"/"; + let environment = Environment { + current_directory_path: DEFAULT_CURRENT_DIRECTORY, + }; + environment + } else { + let env = unsafe { &mut *(environment as *mut liblbos::Environment) }; + let current_directory_path = unsafe { core::slice::from_raw_parts(env.current_directory_path as *const u8, env.current_directory_path_len) }; + let environment = Environment { + current_directory_path, + }; + environment + }; + // replace the current path with one that we allocate ourself, so that we can modify it later + { + let path = environment.current_directory_path; + let len = environment.current_directory_path.len(); + CURRENT_DIR_PATH.store(liblbos::syscalls::alloc_blocks(len.div_ceil(512)) as *mut u8, Ordering::Relaxed); + CURRENT_DIR_PATH_LEN.store(len, Ordering::Relaxed); + { + // copy + let dest = unsafe { + core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut _, len) + }; + for (i, &x) in path.iter().enumerate() { + dest[i] = x; + } + } + environment.current_directory_path = unsafe { core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut u8, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed)) }; + } + print("\n\n"); print("turntable v"); const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -198,7 +315,8 @@ extern "C" fn main() { if *c == b'\r' { execute(unsafe { core::str::from_utf8_unchecked(&cmdbuf[0..cmdbuf_len - 1]) - }); + }, + &mut environment); cmdbuf_len = 0; print("> "); break; From 0b0a2afd6d605857e38916032c5467a15eb16169 Mon Sep 17 00:00:00 2001 From: husky Date: Tue, 9 Sep 2025 10:14:50 -0700 Subject: [PATCH 03/20] run file in env dir and pass env to new task --- turntable/src/main.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/turntable/src/main.rs b/turntable/src/main.rs index e0b6fb2..ef457bc 100644 --- a/turntable/src/main.rs +++ b/turntable/src/main.rs @@ -124,7 +124,7 @@ fn lsdir(env: &Environment<'_>) { print("\n"); } -fn attempt_run_file(name: &str) { +fn attempt_run_file(env: &Environment<'_>, name: &str) { print("\n"); if name.len() > 11 { println("filename too long"); @@ -137,7 +137,7 @@ fn attempt_run_file(name: &str) { return; } let mut dir = DirectoryReader::empty(); - if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, b"/") != 0 { + if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, env.current_directory_path) != 0 { println("failed to open dir"); return; } @@ -178,11 +178,17 @@ fn attempt_run_file(name: &str) { if liblbos::ktask::ktask_fsreadfile(&fs, &mut reader, unsafe { core::slice::from_raw_parts_mut(buf as *mut _, size) }) != 0 { println("failed to read file"); } + + let e = liblbos::Environment { + current_directory_path: CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, + current_directory_path_len: CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed), + }; + let mut task_setup = liblbos::TaskSetup { epc: 0, ddi_first_addr: 0, ddi_size: 0, - environment: 0, + environment: &e as *const _ as usize, wait_for_task_exit: true, }; @@ -196,6 +202,10 @@ fn attempt_run_file(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"); } @@ -223,7 +233,7 @@ fn execute(cmd: &str, environment: &mut Environment<'_>) { } } _ => { - attempt_run_file(cmd); + attempt_run_file(environment, cmd); } } } From e090331c235c4d232f70795eb2ae241125bf6ab1 Mon Sep 17 00:00:00 2001 From: husky Date: Wed, 10 Sep 2025 12:38:06 -0700 Subject: [PATCH 04/20] runtime alloc for virtio + fix some stack stuff --- src/arch/virt/asm/linker.ld | 4 +--- src/dev/virtio/block.rs | 11 ++++++----- src/trafficcontrol.rs | 12 +++++++----- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/arch/virt/asm/linker.ld b/src/arch/virt/asm/linker.ld index 417be2f..56add6c 100644 --- a/src/arch/virt/asm/linker.ld +++ b/src/arch/virt/asm/linker.ld @@ -51,8 +51,6 @@ SECTIONS { PROVIDE(_stack_end = _stack_start + 16384); PROVIDE(_tstack_start = _stack_end); PROVIDE(_tstack_end = _tstack_start + 16384); - PROVIDE(_virtio_virtqueue_start = _tstack_end); - PROVIDE(_virtio_virtqueue_end = _virtio_virtqueue_start + 32768); - PROVIDE(_heap_start = _virtio_virtqueue_end); + PROVIDE(_heap_start = _tstack_end); PROVIDE(_heap_size = _MEM_END - _heap_start); } \ No newline at end of file diff --git a/src/dev/virtio/block.rs b/src/dev/virtio/block.rs index e9bb612..20d9744 100644 --- a/src/dev/virtio/block.rs +++ b/src/dev/virtio/block.rs @@ -1,9 +1,6 @@ use crate::dev::virtio::{Descriptor, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE, VirtQueue, Used}; use crate::trafficcontrol::{TaskWait, TrafficControl}; -unsafe extern "C" { - fn _virtio_virtqueue_start(); -} #[repr(C)] pub struct Status { pub status: u8, @@ -96,8 +93,12 @@ impl VirtIoBlockDevice { unsafe { ((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0); } - let queue_ptr= _virtio_virtqueue_start as usize; - unsafe { ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096) }; // who knows if this actually works :p + let num_blocks = size_of::().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes + let queue_block= unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; + let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) }; + // align up to 4096 + let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095); + unsafe { ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096) }; unsafe { ((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(queue_ptr as u32 / 4096) }; diff --git a/src/trafficcontrol.rs b/src/trafficcontrol.rs index 5b30512..345b4de 100644 --- a/src/trafficcontrol.rs +++ b/src/trafficcontrol.rs @@ -13,6 +13,8 @@ pub enum TaskWait { WaitForTaskExit = 1 << 2, } +pub const STACK_SIZE_IN_BLOCKS: usize = 16; + pub const MAX_TASKS: usize = 8; pub static TC: Spinlock = Spinlock::new(TrafficControl::empty()); @@ -71,14 +73,14 @@ pub fn handle_syscall( tc.memory_manager .as_mut() .unwrap_unchecked() - .alloc_n_blocks(16) + .alloc_n_blocks(STACK_SIZE_IN_BLOCKS) }; let sp = unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() .block_to_addr(blockalloc) - + BLOCK_SIZE + + (BLOCK_SIZE * STACK_SIZE_IN_BLOCKS) }; #[cfg(feature = "arch_virt")] @@ -352,17 +354,17 @@ pub fn context_switch<'a>(tc: &'a mut TrafficControl, current: Task) -> Option<& tc.memory_manager .as_mut() .unwrap_unchecked() - .addr_to_block(sp - BLOCK_SIZE) + .addr_to_block(sp - (BLOCK_SIZE * STACK_SIZE_IN_BLOCKS)) }; unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() - .free_n_blocks(stackblock, 4) + .free_n_blocks(stackblock, STACK_SIZE_IN_BLOCKS) }; unsafe { tc.memory_manager.as_mut().unwrap_unchecked() - .free_n_blocks(ddi_start_block as usize, ddi_blocks_count as usize) + .free_n_blocks(ddi_start_block, ddi_blocks_count) } } tc.tasks[i] = None; From c9d116c46287c7f16f839920938041ef406e4327 Mon Sep 17 00:00:00 2001 From: husky Date: Wed, 10 Sep 2025 12:41:32 -0700 Subject: [PATCH 05/20] add gpu to qemu, also make qemurun script dynamic --- qemurun.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qemurun.sh b/qemurun.sh index ea1e4e4..4817fce 100755 --- a/qemurun.sh +++ b/qemurun.sh @@ -1,2 +1,5 @@ #!/usr/bin/env bash -qemu-system-riscv32 -machine virt -bios none -display gtk -drive if=none,format=raw,file=/dev/loop0,id=disk1 -device virtio-blk-device,drive=disk1 -serial mon:stdio -d guest_errors,unimp -m 5M -kernel target/riscv32imac-unknown-none-elf/release/lbos \ No newline at end of file + +storage_device="${1}" + +qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -device virtio-gpu -serial mon:stdio -m 5M -kernel target/riscv32imac-unknown-none-elf/release/lbos \ No newline at end of file From f238ac7b3b744c6d5b3e84b06e2e9c71a811078b Mon Sep 17 00:00:00 2001 From: husky Date: Wed, 10 Sep 2025 14:06:00 -0700 Subject: [PATCH 06/20] virtio gpu support --- qemurun.sh | 2 +- src/dev/mod.rs | 3 + src/dev/virtio/block.rs | 2 +- src/dev/virtio/gpu.rs | 430 ++++++++++++++++++++++++++++++++++++++++ src/dev/virtio/mod.rs | 51 +++++ 5 files changed, 486 insertions(+), 2 deletions(-) create mode 100644 src/dev/virtio/gpu.rs diff --git a/qemurun.sh b/qemurun.sh index 4817fce..5f30ce7 100755 --- a/qemurun.sh +++ b/qemurun.sh @@ -2,4 +2,4 @@ storage_device="${1}" -qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -device virtio-gpu -serial mon:stdio -m 5M -kernel target/riscv32imac-unknown-none-elf/release/lbos \ No newline at end of file +qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -device virtio-gpu-device -serial mon:stdio -m 5M -kernel target/riscv32imac-unknown-none-elf/release/lbos \ No newline at end of file diff --git a/src/dev/mod.rs b/src/dev/mod.rs index 7c5d699..439851f 100644 --- a/src/dev/mod.rs +++ b/src/dev/mod.rs @@ -1,5 +1,8 @@ use crate::trafficcontrol::TrafficControl; +pub const FRAMEBUFFER_WIDTH: usize = 320; +pub const FRAMEBUFFER_HEIGHT: usize = 240; + #[cfg(feature = "dev_virtio")] pub mod virtio; diff --git a/src/dev/virtio/block.rs b/src/dev/virtio/block.rs index 20d9744..ac891a7 100644 --- a/src/dev/virtio/block.rs +++ b/src/dev/virtio/block.rs @@ -94,7 +94,7 @@ impl VirtIoBlockDevice { ((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0); } let num_blocks = size_of::().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes - let queue_block= unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; + let queue_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) }; // align up to 4096 let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095); diff --git a/src/dev/virtio/gpu.rs b/src/dev/virtio/gpu.rs new file mode 100644 index 0000000..eb9a1ab --- /dev/null +++ b/src/dev/virtio/gpu.rs @@ -0,0 +1,430 @@ +use crate::dev::{FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH}; +use crate::dev::virtio::{free_type, heap_allocate_type, Descriptor, VirtQueue, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE}; +use crate::trafficcontrol::TrafficControl; + +pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100; + +#[repr(C)] +pub struct CtrlHeader { + pub ctrl_type: u32, + pub flags: u32, + pub fence_id: u32, + pub ctx_id: u32, + pub padding: u32, +} + +pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0100 + 1; +pub const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; + +#[repr(C)] +pub struct ResourceCreate2D { + pub header: CtrlHeader, + pub resource_id: u32, + pub format: u32, + pub width: u32, + pub height: u32, +} + +pub const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0100 + 6; +#[repr(C)] +pub struct ResourceAttachBacking { + pub header: CtrlHeader, + pub resource_id: u32, + pub nr_entries: u32, +} + +#[repr(C)] +pub struct GPUMemEntry { + pub addr: u64, + pub length: u32, + pub padding: u32, +} + +pub const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0100 + 3; + +#[repr(C)] +pub struct SetScanout { + pub header: CtrlHeader, + pub rect: VirtIoGpuRect, + pub scanout_id: u32, + pub resource_id: u32, +} + +#[repr(C)] +pub struct VirtIoGpuRect { + pub x: u32, + pub y: u32, + pub width: u32, + pub height: u32, +} + +pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0100 + 5; + +#[repr(C)] +pub struct TransferToHost2D { + pub header: CtrlHeader, + pub rect: VirtIoGpuRect, + pub offset: u64, + pub resource_id: u32, + pub padding: u32, +} + +pub const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0100 + 4; + +#[repr(C)] +pub struct ResourceFlush { + pub header: CtrlHeader, + pub rect: VirtIoGpuRect, + pub resource_id: u32, + pub padding: u32, +} + +pub struct VirtIoGpuDevice { + pub addr: usize, + pub queue: usize, + pub idx: u16, + pub ack_used_idx: u16, + pub framebuffer: usize, + pub width: usize, + pub height: usize, +} + +pub enum VirtIoGpuDeviceError { + FeatureSetMismatch, + QueueSetupFailed, +} + +impl VirtIoGpuDevice { + pub fn new_and_init( + tc: &mut TrafficControl, + addr: usize, + ) -> Result { + // reset device (write 0 to status) + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(0); + } + // set ack bit + let mut status = VIRTIO_MMIO_STATUS_ACKNOWLEDGE; + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); + } + // set driver bit + status |= VIRTIO_MMIO_STATUS_DRIVER; + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); + } + // read host features + let host_features = + unsafe { ((addr + VIRTIO_MMIO_HOST_FEATURES) as *const u32).read_volatile() }; + let guest_features = host_features; // todo: configure properly + + unsafe { + ((addr + VIRTIO_MMIO_GUEST_FEATURES) as *mut u32).write_volatile(guest_features); + } + status |= VIRTIO_MMIO_STATUS_FEATURES_OK; + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); + } + // make sure features ok is still set, otherwise failed + if unsafe { ((addr + VIRTIO_MMIO_STATUS) as *const u32).read_volatile() } + & VIRTIO_MMIO_STATUS_FEATURES_OK + == 0 + { + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(VIRTIO_MMIO_STATUS_FAILED); + } + return Err(VirtIoGpuDeviceError::FeatureSetMismatch); + } + + // setup queue + let queue_max_by_device = + unsafe { ((addr + VIRTIO_MMIO_QUEUE_NUM_MAX) as *const u32).read_volatile() }; + if queue_max_by_device < VIRTIO_QUEUE_SIZE as _ { + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(VIRTIO_MMIO_STATUS_FAILED); + } + return Err(VirtIoGpuDeviceError::QueueSetupFailed); + } + unsafe { + ((addr + VIRTIO_MMIO_QUEUE_NUM) as *mut u32).write_volatile(VIRTIO_QUEUE_SIZE as _); + } + unsafe { + ((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0); + } + + let num_blocks = size_of::().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes + let queue_block= unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; + let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) }; + // align up to 4096 + let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095); + + // allocate memory for framebuffer + let num_blocks = (FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4).div_ceil(512); // no alignment requirements afaik + let framebuffer_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; + let framebuffer_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(framebuffer_block) }; + + let mut gpu = Self { + addr, + queue: queue_ptr, + idx: 0, + ack_used_idx: 0, + framebuffer: framebuffer_ptr, + width: FRAMEBUFFER_WIDTH, + height: FRAMEBUFFER_HEIGHT, + }; + status |= VIRTIO_MMIO_STATUS_DRIVER_OK; + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); + } + + // note: we heap allocate a lot of types here, they will be freed by the self.pending(tc) call + + // create host resource 1, ResourceCreate2D + let cmd = heap_allocate_type::(tc); + cmd.header.ctrl_type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D; + cmd.header.flags = 0; + cmd.header.fence_id = 0; + cmd.header.ctx_id = 0; + cmd.header.padding = 0; + + cmd.resource_id = 1; + cmd.format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; + cmd.width = FRAMEBUFFER_WIDTH as u32; + cmd.height = FRAMEBUFFER_HEIGHT as u32; + + let resp = heap_allocate_type::(tc); + + let desc_rq = Descriptor { + addr: unsafe { &(*cmd) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_NEXT, + next: 0, + }; + let desc_resp = Descriptor { + addr: unsafe { &(*resp) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_WRITE, + next: 0, + }; + + gpu.send_rq_rsp(desc_rq, desc_resp); + gpu.pending(tc); + + // attach backing, ResourceAttachBacking + let cmd = heap_allocate_type::(tc); + cmd.header.ctrl_type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING; + cmd.header.flags = 0; + cmd.header.fence_id = 0; + cmd.header.ctx_id = 0; + cmd.header.padding = 0; + cmd.resource_id = 1; + cmd.nr_entries = 1; + let mem_entry = heap_allocate_type::(tc); + mem_entry.addr = gpu.framebuffer as u64; + mem_entry.length = (gpu.width * gpu.height * 4) as u32; + mem_entry.padding = 0; + let resp = heap_allocate_type::(tc); + + let desc_rq = Descriptor { + addr: unsafe { &(*cmd) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_NEXT, + next: 0, + }; + let desc_mem_entry = Descriptor { + addr: unsafe { &(*mem_entry) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_NEXT, + next: 0, + }; + let desc_resp = Descriptor { + addr: unsafe { &(*resp) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_WRITE, + next: 0, + }; + + gpu.send_rq_next_rsp([desc_rq, desc_mem_entry], desc_resp); + gpu.pending(tc); + + // set scanout + let cmd = heap_allocate_type::(tc); + cmd.header.ctrl_type = VIRTIO_GPU_CMD_SET_SCANOUT; + cmd.header.flags = 0; + cmd.header.fence_id = 0; + cmd.header.ctx_id = 0; + cmd.header.padding = 0; + cmd.rect.x = 0; + cmd.rect.y = 0; + cmd.rect.width = gpu.width as u32; + cmd.rect.height = gpu.height as u32; + cmd.scanout_id = 0; + cmd.resource_id = 1; + let resp = heap_allocate_type::(tc); + + let desc_rq = Descriptor { + addr: unsafe { &(*cmd) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_NEXT, + next: 0, + }; + let desc_resp = Descriptor { + addr: unsafe { &(*resp) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_WRITE, + next: 0, + }; + gpu.send_rq_rsp(desc_rq, desc_resp); + gpu.pending(tc); + + Ok(gpu) + } + + pub fn send_rq_rsp(&mut self, desc_rq: Descriptor, desc_resp: Descriptor) { + self.idx = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; + let head = self.idx; + unsafe { + (*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc_rq; + } + + // assume next is always set, in fact set it if it isn't + unsafe { + (*(self.queue as *mut VirtQueue)).desc[self.idx as usize].flags |= VIRTIO_DESC_F_NEXT; + + (*(self.queue as *mut VirtQueue)).desc[self.idx as usize].next = + (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; + } + + self.idx = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; + unsafe { + (*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc_resp; + } + + unsafe { + (*(self.queue as *mut VirtQueue)).avail.ring[(*(self.queue as *mut VirtQueue)).avail.idx as usize % VIRTIO_QUEUE_SIZE] = head; + (*(self.queue as *mut VirtQueue)).avail.idx = (*(self.queue as *mut VirtQueue)).avail.idx.wrapping_add(1); + } + + // notify + unsafe { + ((self.addr + VIRTIO_MMIO_QUEUE_NOTIFY) as *mut u32).write_volatile(0); + } + } + + pub fn send_rq_next_rsp>(&mut self, descs: T, desc_resp: Descriptor) { + let head = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; + for desc in descs { + self.idx = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; + unsafe { + (*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc; + } + + // assume next is always set, in fact set it if it isn't + unsafe { + (*(self.queue as *mut VirtQueue)).desc[self.idx as usize].flags |= VIRTIO_DESC_F_NEXT; + + (*(self.queue as *mut VirtQueue)).desc[self.idx as usize].next = + (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; + } + } + + self.idx = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; + unsafe { + (*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc_resp; + } + + unsafe { + (*(self.queue as *mut VirtQueue)).avail.ring[(*(self.queue as *mut VirtQueue)).avail.idx as usize % VIRTIO_QUEUE_SIZE] = head; + (*(self.queue as *mut VirtQueue)).avail.idx = (*(self.queue as *mut VirtQueue)).avail.idx.wrapping_add(1); + } + + // notify + unsafe { + ((self.addr + VIRTIO_MMIO_QUEUE_NOTIFY) as *mut u32).write_volatile(0); + } + } + + pub fn pending(&mut self, tc: &mut TrafficControl) { + let queue = unsafe { &(*(self.queue as *mut VirtQueue)) }; + while self.ack_used_idx != queue.used.idx { + let elem = &queue.used.ring[self.ack_used_idx as usize % VIRTIO_QUEUE_SIZE]; + let desc = &queue.desc[elem.id as usize]; + let addr = desc.addr as usize; + let num_blocks = (desc.len as usize).div_ceil(512); + let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(addr) }; + + // free + unsafe { + tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, num_blocks); + } + self.ack_used_idx = self.ack_used_idx.wrapping_add(1); + } + } + + pub fn transfer(&mut self, tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) { + // we have enough queue space to send 2 commands (4 descriptors), so lets send + // both the TransferToHost2D and the ResourceFlush commands in one go + // i.e. don't call pending until the flush + + let cmd = heap_allocate_type::(tc); + cmd.header.ctrl_type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D; + cmd.header.flags = 0; + cmd.header.fence_id = 0; + cmd.header.ctx_id = 0; + cmd.header.padding = 0; + cmd.rect.x = x; + cmd.rect.y = y; + cmd.rect.width = width; + cmd.rect.height = height; + cmd.offset = 0; + cmd.resource_id = 1; + cmd.padding = 0; + let resp = heap_allocate_type::(tc); + + let desc_rq = Descriptor { + addr: unsafe { &(*cmd) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_NEXT, + next: 0, + }; + let desc_resp = Descriptor { + addr: unsafe { &(*resp) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_WRITE, + next: 0, + }; + + self.send_rq_rsp(desc_rq, desc_resp); + + // resource flush + let cmd = heap_allocate_type::(tc); + cmd.header.ctrl_type = VIRTIO_GPU_CMD_RESOURCE_FLUSH; + cmd.header.flags = 0; + cmd.header.fence_id = 0; + cmd.header.ctx_id = 0; + cmd.header.padding = 0; + cmd.rect.x = x; + cmd.rect.y = y; + cmd.rect.width = width; + cmd.rect.height = height; + cmd.resource_id = 1; + cmd.padding = 0; + let resp = heap_allocate_type::(tc); + + let desc_rq = Descriptor { + addr: unsafe { &(*cmd) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_NEXT, + next: 0, + }; + let desc_resp = Descriptor { + addr: unsafe { &(*resp) as *const _ as u64 }, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_WRITE, + next: 0, + }; + + self.send_rq_rsp(desc_rq, desc_resp); + self.pending(tc); + } +} \ No newline at end of file diff --git a/src/dev/virtio/mod.rs b/src/dev/virtio/mod.rs index a6ee5d7..e3eecaa 100644 --- a/src/dev/virtio/mod.rs +++ b/src/dev/virtio/mod.rs @@ -2,11 +2,13 @@ //! WARNING: virtio is currently completely broken! don't use it! use crate::dev::virtio::block::{VirtIoBlockDevice, VirtIoBlockDeviceError}; +use crate::dev::virtio::gpu::{VirtIoGpuDevice, VirtIoGpuDeviceError}; use crate::spinlock::Spinlock; use crate::strprint::twodigit; use crate::trafficcontrol::{TrafficControl}; mod block; +mod gpu; pub const VIRTIO_MMIO_START: usize = 0x1000_1000; pub const VIRTIO_MMIO_END: usize = 0x1000_8000; @@ -36,9 +38,11 @@ pub const VIRTIO_QUEUE_SIZE: usize = 4; pub static VIRTIO_DEVICES: Spinlock<[Option; VIRTIO_MMIO_DEVCOUNT]> = Spinlock::new([const { None }; VIRTIO_MMIO_DEVCOUNT]); pub static VIRTIO_HARD_BLOCK_DEVICE: Spinlock> = Spinlock::new(None); +pub static VIRTIO_GPU_DEVICE: Spinlock> = Spinlock::new(None); pub enum VirtIoDevice { BlockDevice(VirtIoBlockDevice), + GPUDevice(VirtIoGpuDevice), } #[repr(C)] @@ -75,6 +79,20 @@ pub struct VirtQueue { pub used: Used, } +pub fn heap_allocate_type(tc: &mut TrafficControl) -> &'static mut T { + let num_blocks = size_of::().div_ceil(512); + let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; + let addr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(block) }; + unsafe { &mut (*(addr as *mut T)) } +} + +/// WARNING: ptr will be invalid after this function returns! +pub fn free_type(tc: &mut TrafficControl, ptr: &'static mut T) { + let num_blocks = size_of::().div_ceil(512); + let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(ptr as *const T as usize) }; + unsafe { tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, num_blocks) }; +} + pub fn probe_virtio_devices(tc: &mut TrafficControl) { let serial_port = crate::arch::serial_port(); let mut devices = VIRTIO_DEVICES.lock(); @@ -117,6 +135,30 @@ pub fn probe_virtio_devices(tc: &mut TrafficControl) { } } } + 16 => { + // gpu device + let gpu_device = VirtIoGpuDevice::new_and_init(tc, addr); + if let Ok(gpu_device) = gpu_device { + devices[i] = Some(VirtIoDevice::GPUDevice(gpu_device)); + *VIRTIO_GPU_DEVICE.lock() = Some(i as u8); + if let Some(serial_port) = &serial_port { + serial_port.putstr("virtio gpu device found\n"); + } + } else if let Err(e) = gpu_device { + match e { + VirtIoGpuDeviceError::FeatureSetMismatch => { + if let Some(serial_port) = &serial_port { + serial_port.putstr("virtio gpu device feature mismatch\n"); + } + } + VirtIoGpuDeviceError::QueueSetupFailed => { + if let Some(serial_port) = &serial_port { + serial_port.putstr("virtio gpu device queue setup failed\n"); + } + } + } + } + } x => { if let Some(serial_port) = &serial_port { serial_port.putstr("unsupported device type "); @@ -144,6 +186,9 @@ pub fn handle_interrupt(interrupt: u32, tc: &mut TrafficControl) { } } } + VirtIoDevice::GPUDevice(_) => { + // todo: handle gpu interrupts + } } } } @@ -161,6 +206,12 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6 device.operation(tc, buffer, size, sector, false); return true; } + _ => { + if let Some(serial_port) = &crate::arch::serial_port() { + serial_port.putstr("unexpected device type (not block)\n"); + } + return false; + } } } else { let uart = crate::uart::UART::new(0x1000_0000); From af901cdfb3888ef9e5259abca2fe3ddc2a9bda4e Mon Sep 17 00:00:00 2001 From: husky Date: Wed, 10 Sep 2025 23:49:17 -0700 Subject: [PATCH 07/20] framebuffer working, only cost me 8 hours! --- .cargo/config | 2 +- Cargo.toml | 6 +- liblbos/src/syscalls.rs | 33 +++++ qemurun.sh | 2 +- src/arch/ofw/ofw_framebuffer/mod.rs | 2 + src/arch/virt/asm/linker.ld | 9 +- src/dev/framebuffer/console.rs | 90 ++++++++++++++ src/dev/framebuffer/mod.rs | 105 ++++++++++++++++ src/dev/framebuffer/vapfont.data | Bin 0 -> 3584 bytes src/dev/framebuffer/vapfont.png | Bin 0 -> 2316 bytes src/dev/mod.rs | 23 ++++ src/dev/virtio/block.rs | 19 ++- src/dev/virtio/gpu.rs | 186 ++++++++++++++++++++-------- src/dev/virtio/mod.rs | 51 ++++++-- src/main.rs | 11 +- src/memory.rs | 21 +++- src/trafficcontrol.rs | 44 +++++++ src/uart.rs | 2 + 18 files changed, 523 insertions(+), 83 deletions(-) create mode 100644 src/dev/framebuffer/console.rs create mode 100644 src/dev/framebuffer/mod.rs create mode 100644 src/dev/framebuffer/vapfont.data create mode 100644 src/dev/framebuffer/vapfont.png diff --git a/.cargo/config b/.cargo/config index 5198a7d..6d3b8a3 100644 --- a/.cargo/config +++ b/.cargo/config @@ -8,5 +8,5 @@ rustflags = [ [tarcet.riscv32i-unknown-none-elf] rustflags = [ - "-C", "link-args=-nostdlib -ffreestanding -fPIC", + "-C", "link-args=-nostdlib -ffreestanding -relocation-model=static", ] \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 30dfd61..bf8ecdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ panic = "abort" opt-level = "z" debug-assertions = false overflow-checks = false -strip = false +strip = true lto = true codegen-units = 1 @@ -34,8 +34,10 @@ cc = "1.0" [features] default = [] debug_messages = [] -arch_virt = ["dev_virtio", "fs_fat32", "liblbos/arch_riscv32", "ddi/arch_riscv32"] +arch_virt = ["dev_virtio", "dev_qemu_ramfb", "dev_framebuffer", "fs_fat32", "liblbos/arch_riscv32", "ddi/arch_riscv32"] arch_ofw = [] arch_ppc32 = ["compiler_builtins", "arch_ofw", "fs_fat32"] dev_virtio = [] +dev_framebuffer = [] +dev_qemu_ramfb = [] fs_fat32 = [] \ No newline at end of file diff --git a/liblbos/src/syscalls.rs b/liblbos/src/syscalls.rs index c8c3210..795c655 100644 --- a/liblbos/src/syscalls.rs +++ b/liblbos/src/syscalls.rs @@ -67,6 +67,19 @@ pub enum SysCall { WaitForNotifAck = 13, /// returns the environment pointer for the given task EnvironmentPointer = 14, + /// disables the kernel framebuffer console, allowing you to use it for other purposes. + /// if there is no device providing a gpu, this will not do anything. + DisableFramebufferConsole = 15, + /// enables the kernel framebuffer console, this will result in an immediate framebuffer console reset. + /// if the framebuffer console is already enabled, then it will be reset upon calling this. + /// if there is no device providing a gpu, this will not do anything. + EnableFramebufferConsole = 16, + /// gets a pointer to the framebuffer + /// if there is no device providing a gpu, this will return a null pointer + FramebufferPointer = 17, + /// flushes changes done within the given rectangle to the framebuffer + /// if there is no device providing a gpu, this will not do anything + FlushFramebufferRect = 18, /// initializes kernel, ONLY CALL ONCE! IN FACT, YOU PROBABLY NEVER NEED TO CALL THIS InitKernel = 666 @@ -93,6 +106,10 @@ pub fn usize2sc(u: usize) -> SysCall { 12 => SysCall::PendingNotifications, 13 => SysCall::WaitForNotifAck, 14 => SysCall::EnvironmentPointer, + 15 => SysCall::DisableFramebufferConsole, + 16 => SysCall::EnableFramebufferConsole, + 17 => SysCall::FramebufferPointer, + 18 => SysCall::FlushFramebufferRect, 666 => SysCall::InitKernel, _ => SysCall::NoAction, } @@ -166,6 +183,22 @@ pub fn environment_pointer() -> usize { syscall(SysCall::EnvironmentPointer, 0, 0, 0, 0, 0, 0) } +pub fn disable_framebuffer_console() { + syscall(SysCall::DisableFramebufferConsole, 0, 0, 0, 0, 0, 0); +} + +pub fn enable_framebuffer_console() { + syscall(SysCall::EnableFramebufferConsole, 0, 0, 0, 0, 0, 0); +} + +pub fn framebuffer_pointer() -> usize { + syscall(SysCall::FramebufferPointer, 0, 0, 0, 0, 0, 0) +} + +pub fn flush_framebuffer_rect(x: usize, y: usize, w: usize, h: usize) { + syscall(SysCall::FlushFramebufferRect, x, y, w, h, 0, 0); +} + pub fn init_kernel() { syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0); } \ No newline at end of file diff --git a/qemurun.sh b/qemurun.sh index 5f30ce7..e1edece 100755 --- a/qemurun.sh +++ b/qemurun.sh @@ -2,4 +2,4 @@ storage_device="${1}" -qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -device virtio-gpu-device -serial mon:stdio -m 5M -kernel target/riscv32imac-unknown-none-elf/release/lbos \ No newline at end of file +qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -display sdl -device virtio-gpu-device -serial stdio -m 5M -device loader,cpu-num=0,file=target/riscv32imac-unknown-none-elf/release/lbos -monitor telnet:127.0.0.1:1235,server,nowait -d guest_errors,unimp \ No newline at end of file diff --git a/src/arch/ofw/ofw_framebuffer/mod.rs b/src/arch/ofw/ofw_framebuffer/mod.rs index 2449b88..9616d7a 100644 --- a/src/arch/ofw/ofw_framebuffer/mod.rs +++ b/src/arch/ofw/ofw_framebuffer/mod.rs @@ -1,3 +1,5 @@ +// fixme: port this to the device-agnostic framebuffer console + pub mod colors; pub mod extrachars; pub mod terminal; diff --git a/src/arch/virt/asm/linker.ld b/src/arch/virt/asm/linker.ld index 56add6c..8face3b 100644 --- a/src/arch/virt/asm/linker.ld +++ b/src/arch/virt/asm/linker.ld @@ -5,7 +5,9 @@ ENTRY( _start ) MEMORY { rom (wxa) : ORIGIN = 0x80000000, LENGTH = 64 * 1024 - ram (wxa) : ORIGIN = 0x80010000, LENGTH = 1048510 + ram (wxa) : ORIGIN = ORIGIN(rom) + LENGTH(rom), LENGTH = 64 * 1024 + virtqueues (wxa) : ORIGIN = ORIGIN(ram) + LENGTH(ram), LENGTH = 16 * 1024 * 2 + framebuffer (wxa) : ORIGIN = ORIGIN(virtqueues) + LENGTH(virtqueues), LENGTH = 320 * 240 * 3 } PHDRS @@ -53,4 +55,9 @@ SECTIONS { PROVIDE(_tstack_end = _tstack_start + 16384); PROVIDE(_heap_start = _tstack_end); PROVIDE(_heap_size = _MEM_END - _heap_start); + PROVIDE(_virtio_queue_1_start = ORIGIN(virtqueues)); + PROVIDE(_virtio_queue_1_end = _virtio_queue_1_start + 16384); + PROVIDE(_virtio_queue_2_start = _virtio_queue_1_end); + PROVIDE(_virtio_queue_2_end = _virtio_queue_2_start + 16384); + PROVIDE(_framebuffer_start = ORIGIN(framebuffer)); } \ No newline at end of file diff --git a/src/dev/framebuffer/console.rs b/src/dev/framebuffer/console.rs new file mode 100644 index 0000000..6214267 --- /dev/null +++ b/src/dev/framebuffer/console.rs @@ -0,0 +1,90 @@ +use crate::dev::{framebuffer_update, FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH}; +use crate::dev::framebuffer::{fb_clearscreen, fb_write_char_array}; +use crate::spinlock::Spinlock; +use crate::trafficcontrol::TrafficControl; + +pub struct FramebufferConsole { + pub buffer: [[char; 20]; 15], // our font is 16x16, 320x240 is the standard res, so 20x15 + pub cursor: (usize, usize), // (x, y) +} + +pub static FBCONSOLE: Spinlock = Spinlock::new( + FramebufferConsole::empty() +); + +impl FramebufferConsole { + pub const fn empty() -> Self { + Self { + buffer: [[' '; 20]; 15], + cursor: (0, 0), + } + } + + // does NOT send a framebuffer update! that's up to the caller + pub fn scroll_terminal(&mut self) { + for line_num in 0..(self.buffer.len() - 1) { + let copy_from = self.buffer[line_num + 1]; + self.buffer[line_num] = copy_from; + } + self.buffer[self.buffer.len() - 1] = [' '; 20]; + } + + // DOES send a framebuffer update! + pub fn clear_terminal(&mut self, tc: &mut TrafficControl) { + self.buffer = [[' '; 20]; 15]; + self.cursor = (0, 0); + self.render(tc); + } + + // DOES send a framebuffer update! + pub fn printstr(&mut self, tc: &mut TrafficControl, str: &str) { + for c in str.chars() { + let mut was_special_char = false; + if c == '\n' || c == '\r' { + was_special_char = true; + self.cursor.0 = 0; + self.cursor.1 += 1; + } + if self.cursor.1 >= self.buffer.len() { + self.cursor.0 = 0; + self.cursor.1 = self.buffer.len() - 1; + self.scroll_terminal(); + } + if c == '\x08' { + was_special_char = true; + // we don't clear the character, that's up to the terminal + if self.cursor.0 > 0 { + self.cursor.0 -= 1; + } else { + self.cursor.0 = 0; + if self.cursor.1 > 0 { + self.cursor.1 -= 1; + self.cursor.0 = self.buffer[self.cursor.1].len() - 1; + } + } + } + if !was_special_char { + self.buffer[self.cursor.1][self.cursor.0] = c; + self.cursor.0 += 1; + if self.cursor.0 >= self.buffer[self.cursor.1].len() { + self.cursor.0 = 0; + self.cursor.1 += 1; + if self.cursor.1 >= self.buffer.len() { + self.cursor.1 = self.buffer.len() - 1; + self.scroll_terminal(); + } + } + } + } + + self.render(tc); + } + + pub fn render(&self, tc: &mut TrafficControl) { + fb_clearscreen(tc); + + for (y, line) in self.buffer.iter().enumerate() { + fb_write_char_array(tc, 0, y * 16, line); + } + } +} \ No newline at end of file diff --git a/src/dev/framebuffer/mod.rs b/src/dev/framebuffer/mod.rs new file mode 100644 index 0000000..f548170 --- /dev/null +++ b/src/dev/framebuffer/mod.rs @@ -0,0 +1,105 @@ +use crate::dev::{ + FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP, FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH, framebuffer_update, +}; +use crate::trafficcontrol::TrafficControl; +use core::sync::atomic::Ordering; + +pub mod console; + +pub const VAPFONT: &[u8] = include_bytes!("./vapfont.data"); +pub const VAPFONT_W: usize = 256; +pub const VAPFONT_H: usize = 112; + +pub struct FBColor { + pub red: u8, + pub green: u8, + pub blue: u8, +} + +pub const FB_BG_COLOR: FBColor = FBColor { + red: 216, + green: 216, + blue: 216, +}; + +pub const FB_FG_COLOR: FBColor = FBColor { + red: 0, + green: 0, + blue: 0, +}; + +impl FBColor { + #[inline] + pub const fn to_bytes(&self) -> [u8; 3] { + [self.red, self.green, self.blue] + } +} + +pub fn fb_write_char_array(tc: &mut TrafficControl, mut x: usize, mut y: usize, chars: &[char]) { + const BYTES: [u8; 3] = FB_FG_COLOR.to_bytes(); + let ogx = x; + let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed); + if fbaddr == 0 { + return; + } + let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH; + const CHAR_SIZE: usize = 16; + let mut drew_anything = false; + for c in chars { + let c = *c; + //if c == '\n' { + // y += CHAR_SIZE; + // x = ogx; + // if y * CHAR_SIZE > FRAMEBUFFER_HEIGHT { + // break; + // } + //} else if c == ' ' { + if c == ' ' { + x += CHAR_SIZE; + //if x * CHAR_SIZE > FRAMEBUFFER_WIDTH { + // x = ogx; + // y += CHAR_SIZE; + // if y * CHAR_SIZE > FRAMEBUFFER_HEIGHT { + // break; + // } + //} + } else if c as u8 > 32 { + let c = c as u8 - 32; + let cx = (c % 16) as usize * CHAR_SIZE; + let cy = (c / 16) as usize * CHAR_SIZE; + for row in 0..CHAR_SIZE { + for col in 0..CHAR_SIZE { + let coff = (VAPFONT_W * (cy + row)) + (cx + col); + let draw = VAPFONT[coff / 8] & (0x80 >> (coff % 8)); + if draw != 0 { + unsafe { + let fb = (fbaddr as *mut u8).add(((y+row)*fbstride) + ((x+col)*4)) as *mut u32; + fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]])); + drew_anything = true; + } + } + } + } + x += CHAR_SIZE; + } + } + framebuffer_update(tc, 0, 0, FRAMEBUFFER_WIDTH as u32, FRAMEBUFFER_HEIGHT as u32); +} + +pub fn fb_clearscreen(tc: &mut TrafficControl) { + const BYTES: [u8; 3] = FB_BG_COLOR.to_bytes(); + let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed); + if fbaddr == 0 { + return; + } + let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH; + for y in 0..FRAMEBUFFER_HEIGHT { + for x in 0..FRAMEBUFFER_WIDTH { + unsafe { + let fb = (fbaddr as *mut u8).add(((y)*fbstride) + ((x)*4)) as *mut u32; + fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]])); + } + } + } + framebuffer_update(tc, 0, 0, FRAMEBUFFER_WIDTH as u32, FRAMEBUFFER_HEIGHT as u32); +} diff --git a/src/dev/framebuffer/vapfont.data b/src/dev/framebuffer/vapfont.data new file mode 100644 index 0000000000000000000000000000000000000000..6d2b29209919ff07143a48576f796f57fc65b4bd GIT binary patch literal 3584 zcmcJRzmMER6vv;}j%O~JT|37i%_S>)xNO3O0!DN_5s3&{S&63piG$pAq!EXEmtT7-#1?G-dzv@BF3`UJ0HIv^WK|h6TN99KDC)i zNSDHsB{#Srka#i6j0h8>9L>llXL%q>eBNYcXdW_eKz*`#CUWWPNTF3OQih}Pck2bi}IOCJ$bOm6JNAMe6nuBDP7_c630F7<>Aem z5A@c&<0+o)M&!7aZfFJ*-FAFv6H=Jf#GGv$Ew-BMq5g(=Ca!M;GKqZPn2P}Z08hgn zOR%q#cx4X%NKPc?`}nd4I&yxRd8q~FUe6Ct{`bLgOIxv^P%OowSfn9sH`y-!X${Xr zNRb6ZlrJWs$USMf;%b&WW7NqfbPe|5i>{Xv@`CgfX~BrQ1cW*6H3eXu!$=<2pTNI_ z61r^2XHuTw$|61k%z7)255GP1~GAE819P!~=TX zaujXi>o%R&p
bbdG$K3P_?il)caR0Lfn9)8VK!Cbnck)Fj!0QFuFQRt{{4t-ZoFEFZbtwh~-t;5IigzNmNRqXz#pxRs_@P7jfv!1<{tZ8<+_ zsF15qj>pK)Tc)4tRs@_cjuzFlntuOteSA2pm#h~%1ce&r3&jw@H%jL_elbQ_LUm$}hTQ8|tpR5mzatmN^(>a^5A&# zJiqoxH8HClw<*?sR{rwAD(l!uxs%K#x=ZNoi$Bo03}#Kwl}7vn=b&CzMC0o4oq$~# zm{-Tb_xa)Yz3r%>pzDR`Isy5J3KhpE4uRj;@EUX*oMYlKroQvAOfR1rmgo3xsiM_*KSsmE_Ze#>zhj`}hv<|TX8^piiBPH#) z`zC&bbN3$d*1)9}oH~xavF7^6j;E&MH7vtsJN;;@%n$=#TtU>N)!_=GS@sbAkR=gfa48QPrm5v)`))umArTDf}1b6k}JN83OC? zE1;V`&vWp;g8mslZ*3!gJrr&j3SS3h4%_d3!JBDTv>ne&&b|&+qg_g4qn$lLHjG3S z!Fnj3bzFg9|3;4Q2ScIbyNea48o7g{-~9^T4~{~oUp76D{UvhVgE-ggLrxrOHuUo7 zFP=L9Q8a12vR+O|A6&@XIF5qKzjDPLx`8kuE ze|4Ksfv>*t$(_fgETUwMXlw?)J7p>NPHsD`>p*n(rSZlyM?KRBw_UybLhA{7CJt%#?j0?~a~H;a>9nJ;k}*Fdt?zH&-`;!5T+Xug^rEFWuCDo2 y`TlPlpPToz=H1+l4c~jx#h1BgSC~fa(JQu3^!SG^KR1w{o_4>yzVkDG_3uxDddxZi literal 0 HcmV?d00001 diff --git a/src/dev/framebuffer/vapfont.png b/src/dev/framebuffer/vapfont.png new file mode 100644 index 0000000000000000000000000000000000000000..892116de41136dbed0118bbe8b354fbaa4c831ba GIT binary patch literal 2316 zcmV+n3G?=eP)?jN*9lrmS zb3V!oS`Y%`?w)w%h*>DxfP|DS^dQ8U*Z?_ZuLA>f8G$y#;ez!V@GFB zEQJMU1BLkf_qvU`>Cz}~LqENKg^&4e$>(wW)9egjOFE)HefmoQLvdLrA}I-=TtHq_ zq9TPQT2i;qkRCB0#Bd7*ASVIg@}J8ERO^bN<@acVWD0&41I*kIEHONlf3$qKaOH0> zz@@pGpIuEpojn8t4@^dc*^i}$y?fe-1xBrq^i5#{;_PpWLGbQwdq=ZAI>M{q!RC^uf%CzY`37 zuYdi*C35HnD~Mbz+S~U7berl8w8vW>3g5%;UbyFII1XzpqO+`_pfrMP_)Tbyx1N5; z>C^bK619%T?b%tN6fe*PP0fHBBfYjrL9vU`>@T_3{Z^xPr5Q+vvRR3{yTs)muXet3 zZU$=pZ393tGCaw!JqlHb+dXYWGr6#*I)KNB$ekjj3d=Ut`{2`fpYQ~}b|%FWkf|FU z8HX@Qr;}n5#G$N!NMW(qlooYL$eI-9buGiZMuujaVln_Ah;5)E9Nl*47rkM?#3bog zT5uuIHb2Ns&{KQG0tjR(V;YePh8i7iNfM$5_<=Z*Sx?^aG71LF&;$os1ndih3`(do z>w%C8gRkrWu)sJE1e*lx)yU}4fA;eBXPeA-0Jb@R!~#O706bUNdUod9Ap8UZ)O|S5 zJ75wKZ2W*REePBcG|Z3~r4ihqwC$pIADw(x+ssa+bTZ+<_xfHJf~UfJDblDty-;Il zRkwbhmqRL4tgR$xn!WBHM!cz>*ZtQ7(#h-DhKf$9!dDZ#2H~x88|?UWB5D&px1K(S z*C|QrSmdw*G?|*ixo>-Z1s*W7gVeHMV$jujDCcg{wswU_!c}^S`pl#;_Wq*)AnU7a zC7qq#$v=~Mz^X0{B4>(Y0x?OHH&`HGAb&)LnLO3oIUnYwhQYt8*7dCof^()%J zkO_XmV8~;Rx*Y{WW<0PNZ1EK6%DOoHe-i@V%iw=2qt&`k_~E{ezh(Ot;9A!znKt3O z%_sCkm{EG8X=dog%fWrt!Mzv%`|Ue>K>zvH>v97I13PK%w-Iu4u(|PlL*G}QyVrq= zF)~gL7-9ITZhz`5U-Jaf_4Oo0S2j!AQG^48S@YNgMiuKT4a5RHPow_O zvDlSMQ2dsoz0bfZ3K!2uMXTUX3BVN#bWug}2V=pLTwxh;^_~E@4Dhq|k7Qm(+9*UFvga#K*Mq?B zW3q)QM`saZ`Z^$|irU=>J1?Rlwm{GuO>}a!;e3y@A2WlkkrX0vee7)H0~Q2B-|KsI z^8o_|Ni$D{H7Vm?-LT9~Y^TZNETp8KVg%C*Hb~7IsO_@)eCVTK`aTkls4OF0=tk{7 z=ZO7HRNjAWbiZUZXJ219%8Pd1^yC526aAKD>&G7#l`Dzj4+LnGq{IF`sagU)sYlukYXc6KdC#C(0#^#t8~%J=%$ z@&W(Di}60*fgLf6>IBK(rQa?qf0hA%O>*;sz5VMLu&olw|Le7TcK%~H#Wp_%y8_L& zM;Y&`Jx+JCDiCxwL9uxT%WU$c?LDhaPj(L!!#zvI?6FdNlv+(TIyCK4!V^VGCy7W+ z!JrCYu(loJh{iC!dw8NKKS^J|hpo)eHMW69(RVae$%OiN+F=NDLVr&jttFfRK>(|9 z4V9WKs1wL>dIu(aYUUUHY&F%2DW?crfDYe~$gsJ&USw{vXexC*J4B5VwC!-RWQks&oFvD^|dVb9H#kNw6+}I^fJyeX$iQc=Ym0 zDpGRMw8vQ(aqz4a)|gyTrrV=-kC$aY%df+yb2iML`BVrkUs9ln#=51Sb?6cl_gpgPeAk^PSjaLb?q3$i$wQ@%+K!dL;!urWik$sxG0)~a}P-V;1s zJDd*5Ra usize { + FRAMEBUFFER_ADDR.load(Ordering::Relaxed) +} + +pub fn framebuffer_stride() -> usize { + FRAMEBUFFER_BPP.load(Ordering::Relaxed) +} + + +pub fn framebuffer_update(tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) { + #[cfg(feature = "dev_virtio")] + virtio::framebuffer_update(tc, x, y, width, height); } \ No newline at end of file diff --git a/src/dev/virtio/block.rs b/src/dev/virtio/block.rs index ac891a7..b667e3c 100644 --- a/src/dev/virtio/block.rs +++ b/src/dev/virtio/block.rs @@ -1,6 +1,10 @@ use crate::dev::virtio::{Descriptor, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE, VirtQueue, Used}; use crate::trafficcontrol::{TaskWait, TrafficControl}; +unsafe extern "C" { + fn _virtio_queue_1_start(); +} + #[repr(C)] pub struct Status { pub status: u8, @@ -94,10 +98,17 @@ impl VirtIoBlockDevice { ((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0); } let num_blocks = size_of::().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes - let queue_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; - let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) }; - // align up to 4096 - let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095); + //let queue_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; + //let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) }; + //// align up to 4096 + //let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095); + let queue_ptr = _virtio_queue_1_start as usize; + // zero out queue + for i in 0..size_of::() { + unsafe { + ((queue_ptr + i) as *mut u8).write_volatile(0); + } + } unsafe { ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096) }; unsafe { ((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(queue_ptr as u32 / 4096) diff --git a/src/dev/virtio/gpu.rs b/src/dev/virtio/gpu.rs index eb9a1ab..cef1382 100644 --- a/src/dev/virtio/gpu.rs +++ b/src/dev/virtio/gpu.rs @@ -1,20 +1,28 @@ +use crate::arch::serial_port; use crate::dev::{FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH}; -use crate::dev::virtio::{free_type, heap_allocate_type, Descriptor, VirtQueue, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE}; +use crate::dev::virtio::{heap_allocate_type, Descriptor, VirtQueue, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE}; +use crate::strprint::u32_hex; use crate::trafficcontrol::TrafficControl; +unsafe extern "C" { + fn _virtio_queue_2_start(); + fn _framebuffer_start(); +} + pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100; #[repr(C)] pub struct CtrlHeader { pub ctrl_type: u32, pub flags: u32, - pub fence_id: u32, + pub fence_id: u64, pub ctx_id: u32, - pub padding: u32, + pub ring_idx: u8, + pub padding: [u8; 3], } pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0100 + 1; -pub const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134; +pub const VIRTIO_GPU_FORMAT_X8R8G8B8: u32 = 4; #[repr(C)] pub struct ResourceCreate2D { @@ -80,6 +88,7 @@ pub struct ResourceFlush { } pub struct VirtIoGpuDevice { + pub latest_response: usize, pub addr: usize, pub queue: usize, pub idx: u16, @@ -116,7 +125,7 @@ impl VirtIoGpuDevice { // read host features let host_features = unsafe { ((addr + VIRTIO_MMIO_HOST_FEATURES) as *const u32).read_volatile() }; - let guest_features = host_features; // todo: configure properly + let guest_features = 0; // todo: configure properly unsafe { ((addr + VIRTIO_MMIO_GUEST_FEATURES) as *mut u32).write_volatile(guest_features); @@ -152,23 +161,36 @@ impl VirtIoGpuDevice { ((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0); } - let num_blocks = size_of::().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes - let queue_block= unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; - let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) }; - // align up to 4096 - let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095); + //let num_blocks = size_of::().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes + //let queue_block= unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; + //let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) }; + //// align up to 4096 + //let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095); + let queue_ptr = _virtio_queue_2_start as usize; + // zero out queue + for i in 0..size_of::() { + unsafe { + ((queue_ptr + i) as *mut u8).write_volatile(0); + } + } + unsafe { + ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096); + } + unsafe { + ((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(queue_ptr as u32 / 4096); + } + // allocate memory for framebuffer - let num_blocks = (FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4).div_ceil(512); // no alignment requirements afaik - let framebuffer_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; - let framebuffer_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(framebuffer_block) }; + let latest_response = heap_allocate_type::(tc) as *const _ as usize; let mut gpu = Self { + latest_response, addr, queue: queue_ptr, idx: 0, ack_used_idx: 0, - framebuffer: framebuffer_ptr, + framebuffer: _framebuffer_start as usize, width: FRAMEBUFFER_WIDTH, height: FRAMEBUFFER_HEIGHT, }; @@ -177,7 +199,7 @@ impl VirtIoGpuDevice { ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); } - // note: we heap allocate a lot of types here, they will be freed by the self.pending(tc) call + let mut free_me = [0; 4]; // create host resource 1, ResourceCreate2D let cmd = heap_allocate_type::(tc); @@ -185,14 +207,14 @@ impl VirtIoGpuDevice { cmd.header.flags = 0; cmd.header.fence_id = 0; cmd.header.ctx_id = 0; - cmd.header.padding = 0; + cmd.header.ring_idx = 0; + cmd.header.padding = [0; 3]; cmd.resource_id = 1; - cmd.format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; + cmd.format = VIRTIO_GPU_FORMAT_X8R8G8B8; cmd.width = FRAMEBUFFER_WIDTH as u32; cmd.height = FRAMEBUFFER_HEIGHT as u32; - - let resp = heap_allocate_type::(tc); + free_me[0] = unsafe { &(*cmd) as *const _ as usize }; let desc_rq = Descriptor { addr: unsafe { &(*cmd) as *const _ as u64 }, @@ -201,14 +223,20 @@ impl VirtIoGpuDevice { next: 0, }; let desc_resp = Descriptor { - addr: unsafe { &(*resp) as *const _ as u64 }, + addr: gpu.latest_response as u64, len: size_of::() as u32, flags: VIRTIO_DESC_F_WRITE, next: 0, }; gpu.send_rq_rsp(desc_rq, desc_resp); - gpu.pending(tc); + // awful hack so that we don't overload the queue + while { + gpu.ack_used_idx == unsafe { (gpu.queue as *const VirtQueue).read_volatile().used.idx } + } { + + } + //gpu.pending(tc); // attach backing, ResourceAttachBacking let cmd = heap_allocate_type::(tc); @@ -216,14 +244,16 @@ impl VirtIoGpuDevice { cmd.header.flags = 0; cmd.header.fence_id = 0; cmd.header.ctx_id = 0; - cmd.header.padding = 0; + cmd.header.ring_idx = 0; + cmd.header.padding = [0; 3]; cmd.resource_id = 1; cmd.nr_entries = 1; + free_me[1] = unsafe { &(*cmd) as *const _ as usize }; let mem_entry = heap_allocate_type::(tc); mem_entry.addr = gpu.framebuffer as u64; - mem_entry.length = (gpu.width * gpu.height * 4) as u32; + mem_entry.length = (gpu.height * gpu.width * 4) as u32; mem_entry.padding = 0; - let resp = heap_allocate_type::(tc); + free_me[2] = unsafe { &(*mem_entry) as *const _ as usize }; let desc_rq = Descriptor { addr: unsafe { &(*cmd) as *const _ as u64 }, @@ -238,14 +268,20 @@ impl VirtIoGpuDevice { next: 0, }; let desc_resp = Descriptor { - addr: unsafe { &(*resp) as *const _ as u64 }, + addr: gpu.latest_response as u64, len: size_of::() as u32, flags: VIRTIO_DESC_F_WRITE, next: 0, }; gpu.send_rq_next_rsp([desc_rq, desc_mem_entry], desc_resp); - gpu.pending(tc); + // awful hack so that we don't overload the queue + while { + gpu.ack_used_idx == unsafe { (gpu.queue as *const VirtQueue).read_volatile().used.idx } + } { + + } + //gpu.pending(tc); // set scanout let cmd = heap_allocate_type::(tc); @@ -253,14 +289,15 @@ impl VirtIoGpuDevice { cmd.header.flags = 0; cmd.header.fence_id = 0; cmd.header.ctx_id = 0; - cmd.header.padding = 0; + cmd.header.ring_idx = 0; + cmd.header.padding = [0; 3]; cmd.rect.x = 0; cmd.rect.y = 0; cmd.rect.width = gpu.width as u32; cmd.rect.height = gpu.height as u32; cmd.scanout_id = 0; cmd.resource_id = 1; - let resp = heap_allocate_type::(tc); + free_me[3] = unsafe { &(*cmd) as *const _ as usize }; let desc_rq = Descriptor { addr: unsafe { &(*cmd) as *const _ as u64 }, @@ -269,13 +306,28 @@ impl VirtIoGpuDevice { next: 0, }; let desc_resp = Descriptor { - addr: unsafe { &(*resp) as *const _ as u64 }, + addr: gpu.latest_response as u64, len: size_of::() as u32, flags: VIRTIO_DESC_F_WRITE, next: 0, }; gpu.send_rq_rsp(desc_rq, desc_resp); - gpu.pending(tc); + // awful hack so that we don't overload the queue + while { + gpu.ack_used_idx == unsafe { (gpu.queue as *const VirtQueue).read_volatile().used.idx } + } { + + } + //gpu.pending(tc); + + for free in free_me { + let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(free) }; + unsafe { + tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, 1); + } + } + + gpu.transfer(tc, 0, 0, gpu.width as u32, gpu.height as u32); Ok(gpu) } @@ -287,9 +339,9 @@ impl VirtIoGpuDevice { (*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc_rq; } - // assume next is always set, in fact set it if it isn't + // assume next is always set unsafe { - (*(self.queue as *mut VirtQueue)).desc[self.idx as usize].flags |= VIRTIO_DESC_F_NEXT; + //(*(self.queue as *mut VirtQueue)).desc[self.idx as usize].flags |= VIRTIO_DESC_F_NEXT; (*(self.queue as *mut VirtQueue)).desc[self.idx as usize].next = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; @@ -319,10 +371,8 @@ impl VirtIoGpuDevice { (*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc; } - // assume next is always set, in fact set it if it isn't + // assume next is always set unsafe { - (*(self.queue as *mut VirtQueue)).desc[self.idx as usize].flags |= VIRTIO_DESC_F_NEXT; - (*(self.queue as *mut VirtQueue)).desc[self.idx as usize].next = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; } @@ -344,34 +394,54 @@ impl VirtIoGpuDevice { } } + // WARNING: THIS VERSION OF PENDING DOES NOT FREE ANYTHING pub fn pending(&mut self, tc: &mut TrafficControl) { let queue = unsafe { &(*(self.queue as *mut VirtQueue)) }; + let mut pended = false; while self.ack_used_idx != queue.used.idx { + pended = true; + let uart = serial_port(); + if let Some(uart) = uart { + //uart.putstr("pended\n"); + } let elem = &queue.used.ring[self.ack_used_idx as usize % VIRTIO_QUEUE_SIZE]; let desc = &queue.desc[elem.id as usize]; let addr = desc.addr as usize; - let num_blocks = (desc.len as usize).div_ceil(512); - let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(addr) }; + //let num_blocks = (desc.len as usize).div_ceil(512); + //let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(addr) }; - // free - unsafe { - tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, num_blocks); - } + //// free + //unsafe { + // tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, num_blocks); + //} self.ack_used_idx = self.ack_used_idx.wrapping_add(1); + if self.ack_used_idx >= VIRTIO_QUEUE_SIZE as u16 { + self.ack_used_idx = 0; + } + + } + if !pended { + let uart = serial_port(); + if let Some(uart) = uart { + uart.putstr("no pended\n"); + } } } pub fn transfer(&mut self, tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) { - // we have enough queue space to send 2 commands (4 descriptors), so lets send - // both the TransferToHost2D and the ResourceFlush commands in one go - // i.e. don't call pending until the flush + let uart = serial_port(); + if let Some(uart) = uart { + //uart.putstr("transfer\n"); + } + let mut free_me = [0; 4]; let cmd = heap_allocate_type::(tc); cmd.header.ctrl_type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D; cmd.header.flags = 0; cmd.header.fence_id = 0; cmd.header.ctx_id = 0; - cmd.header.padding = 0; + cmd.header.ring_idx = 0; + cmd.header.padding = [0; 3]; cmd.rect.x = x; cmd.rect.y = y; cmd.rect.width = width; @@ -379,7 +449,7 @@ impl VirtIoGpuDevice { cmd.offset = 0; cmd.resource_id = 1; cmd.padding = 0; - let resp = heap_allocate_type::(tc); + free_me[1] = unsafe { &(*cmd) as *const _ as usize }; let desc_rq = Descriptor { addr: unsafe { &(*cmd) as *const _ as u64 }, @@ -388,13 +458,14 @@ impl VirtIoGpuDevice { next: 0, }; let desc_resp = Descriptor { - addr: unsafe { &(*resp) as *const _ as u64 }, + addr: self.latest_response as u64, len: size_of::() as u32, flags: VIRTIO_DESC_F_WRITE, next: 0, }; self.send_rq_rsp(desc_rq, desc_resp); + //self.pending(tc); // resource flush let cmd = heap_allocate_type::(tc); @@ -402,14 +473,15 @@ impl VirtIoGpuDevice { cmd.header.flags = 0; cmd.header.fence_id = 0; cmd.header.ctx_id = 0; - cmd.header.padding = 0; + cmd.header.ring_idx = 0; + cmd.header.padding = [0; 3]; cmd.rect.x = x; cmd.rect.y = y; cmd.rect.width = width; cmd.rect.height = height; cmd.resource_id = 1; cmd.padding = 0; - let resp = heap_allocate_type::(tc); + free_me[2] = unsafe { &(*cmd) as *const _ as usize }; let desc_rq = Descriptor { addr: unsafe { &(*cmd) as *const _ as u64 }, @@ -418,13 +490,25 @@ impl VirtIoGpuDevice { next: 0, }; let desc_resp = Descriptor { - addr: unsafe { &(*resp) as *const _ as u64 }, + addr: self.latest_response as u64, len: size_of::() as u32, flags: VIRTIO_DESC_F_WRITE, next: 0, }; self.send_rq_rsp(desc_rq, desc_resp); - self.pending(tc); + // awful hack so that we don't overload the queue + while { + self.ack_used_idx == unsafe { (self.queue as *const VirtQueue).read_volatile().used.idx } + } { + + } + //self.pending(tc); + for free in free_me.into_iter().skip(1) { + let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(free) }; + unsafe { + tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, 1); + } + } } } \ No newline at end of file diff --git a/src/dev/virtio/mod.rs b/src/dev/virtio/mod.rs index e3eecaa..e717903 100644 --- a/src/dev/virtio/mod.rs +++ b/src/dev/virtio/mod.rs @@ -1,6 +1,8 @@ //! virtio devices //! WARNING: virtio is currently completely broken! don't use it! +use core::sync::atomic::Ordering; +use crate::dev::{FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP}; use crate::dev::virtio::block::{VirtIoBlockDevice, VirtIoBlockDeviceError}; use crate::dev::virtio::gpu::{VirtIoGpuDevice, VirtIoGpuDeviceError}; use crate::spinlock::Spinlock; @@ -34,7 +36,7 @@ pub const VIRTIO_MMIO_STATUS_FAILED: u32 = 1 << 7; pub const VIRTIO_DESC_F_NEXT: u16 = 1 << 0; pub const VIRTIO_DESC_F_WRITE: u16 = 1 << 1; -pub const VIRTIO_QUEUE_SIZE: usize = 4; +pub const VIRTIO_QUEUE_SIZE: usize = 128; pub static VIRTIO_DEVICES: Spinlock<[Option; VIRTIO_MMIO_DEVCOUNT]> = Spinlock::new([const { None }; VIRTIO_MMIO_DEVCOUNT]); pub static VIRTIO_HARD_BLOCK_DEVICE: Spinlock> = Spinlock::new(None); @@ -86,13 +88,6 @@ pub fn heap_allocate_type(tc: &mut TrafficControl) -> &'static mut T { unsafe { &mut (*(addr as *mut T)) } } -/// WARNING: ptr will be invalid after this function returns! -pub fn free_type(tc: &mut TrafficControl, ptr: &'static mut T) { - let num_blocks = size_of::().div_ceil(512); - let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(ptr as *const T as usize) }; - unsafe { tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, num_blocks) }; -} - pub fn probe_virtio_devices(tc: &mut TrafficControl) { let serial_port = crate::arch::serial_port(); let mut devices = VIRTIO_DEVICES.lock(); @@ -139,6 +134,8 @@ pub fn probe_virtio_devices(tc: &mut TrafficControl) { // gpu device let gpu_device = VirtIoGpuDevice::new_and_init(tc, addr); if let Ok(gpu_device) = gpu_device { + FRAMEBUFFER_ADDR.store(gpu_device.framebuffer, Ordering::Relaxed); + FRAMEBUFFER_BPP.store(4, Ordering::Relaxed); // virtio always uses 4 byte stride devices[i] = Some(VirtIoDevice::GPUDevice(gpu_device)); *VIRTIO_GPU_DEVICE.lock() = Some(i as u8); if let Some(serial_port) = &serial_port { @@ -201,16 +198,48 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6 let mut devices = VIRTIO_DEVICES.lock(); if let Some(idx) = idx { if let Some(device) = devices[idx as usize].as_mut() { - match device { + return match device { VirtIoDevice::BlockDevice(device) => { device.operation(tc, buffer, size, sector, false); - return true; + true } _ => { if let Some(serial_port) = &crate::arch::serial_port() { serial_port.putstr("unexpected device type (not block)\n"); } - return false; + false + } + }; + } else { + let uart = crate::uart::UART::new(0x1000_0000); + uart.putstr("no device") + } + } else { + let uart = crate::uart::UART::new(0x1000_0000); + uart.putstr("no idx") + } + + false +} + +pub fn framebuffer_update(tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) -> bool { + let idx = { + let lock = VIRTIO_GPU_DEVICE.lock(); + *lock + }; + let mut devices = VIRTIO_DEVICES.lock(); + if let Some(idx) = idx { + if let Some(device) = devices[idx as usize].as_mut() { + return match device { + VirtIoDevice::GPUDevice(device) => { + device.transfer(tc, x, y, width, height); + true + } + _ => { + if let Some(serial_port) = &crate::arch::serial_port() { + serial_port.putstr("unexpected device type (not gpu)\n"); + } + false } } } else { diff --git a/src/main.rs b/src/main.rs index 0ddbee8..9de0f95 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,16 +32,6 @@ pub extern "C" fn ktask() -> ! { syscalls::init_kernel(); print(b"kernel ready!\n"); - //let kinfo = kinfo(); - //print(b"\nkinfo\n"); - //print(b"task count "); - //print(&u32_hex(kinfo.current_process_count as u32)); - //print(b"\ntotal mem "); - //print(&u32_hex(kinfo.total_mem_blocks as u32)); - //print(b"\nfree mem "); - //print(&u32_hex(kinfo.free_mem_blocks as u32)); - //print(b"\n"); - // load TURNTBL.DDI { @@ -50,6 +40,7 @@ pub extern "C" fn ktask() -> ! { rough_panic(['f', 's', 'e']); } + let mut fs = liblbos::fs::FileSystem::empty(); if !fs::open_fs(unsafe { &mut *(&mut fs as *mut _ as *mut _) }) { fserr(); diff --git a/src/memory.rs b/src/memory.rs index 7550743..ecfdfa8 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,4 +1,6 @@ +use crate::arch::serial_port; use crate::rough_panic; +use crate::strprint::u32_hex; #[cfg(feature = "arch_virt")] @@ -23,6 +25,14 @@ pub struct MemoryManager { impl MemoryManager { #[cfg(feature = "arch_virt")] pub fn init() -> Self { + { + let uart = serial_port(); + if let Some(uart) = uart { + uart.putstr("totalmem: "); + uart.put_bytes(&u32_hex(MEM_BLOCKS as u32)); + uart.putstr("\n"); + } + } Self { heap_start: _heap_start as _, heap_size: _heap_size as _, @@ -37,6 +47,13 @@ impl MemoryManager { blockmap: [0; (MEM_BLOCKS+7) / 8], } } + + pub fn is_block_free(&self, block: usize) -> bool { + let block = block % 8; + let block = block / 8; + let val = self.blockmap[block]; + (val & (1 << block)) == 0 + } pub fn block_to_addr(&self, block: usize) -> usize { block * BLOCK_SIZE + self.heap_start @@ -101,8 +118,8 @@ impl MemoryManager { } } } - - 0 + + rough_panic(['o', 'o', 'm']) } pub fn free_n_blocks(&mut self, block: usize, n: usize) { diff --git a/src/trafficcontrol.rs b/src/trafficcontrol.rs index 345b4de..2e7ed96 100644 --- a/src/trafficcontrol.rs +++ b/src/trafficcontrol.rs @@ -203,6 +203,14 @@ pub fn handle_syscall( core::slice::from_raw_parts(addr as *const u8, count) }); } + if tc.use_fb_console { + let mut fbcons = crate::dev::framebuffer::console::FBCONSOLE.lock(); + fbcons.printstr(&mut tc, unsafe { + core::str::from_utf8_unchecked(unsafe { + core::slice::from_raw_parts(addr as *const u8, count) + }) + }); + } 0 } SysCall::ReadHBD => { @@ -229,6 +237,15 @@ pub fn handle_syscall( SysCall::InitKernel => { tc.init_mem_if_not(); crate::dev::probe_devices(&mut tc); + if crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { + tc.use_fb_console = true; + { + let uart = serial_port(); + if let Some(uart) = uart { + uart.putstr("using framebuffer console\n"); + } + } + } 0 } SysCall::SendNotification => { @@ -330,6 +347,31 @@ pub fn handle_syscall( 0 } } + SysCall::DisableFramebufferConsole => { + tc.use_fb_console = false; + 0 + } + SysCall::EnableFramebufferConsole => { + if crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { + tc.use_fb_console = true; + // clear the screen + let mut fbcons = crate::dev::framebuffer::console::FBCONSOLE.lock(); + fbcons.clear_terminal(&mut tc); + } + + 0 + } + SysCall::FramebufferPointer => { + crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) + } + SysCall::FlushFramebufferRect => { + let x = a1; + let y = a2; + let w = a3; + let h = a4; + crate::dev::framebuffer_update(&mut tc, x as u32, y as u32, w as u32, h as u32); + 0 + } }, suspend, ) @@ -419,6 +461,7 @@ pub struct TrafficControl { pub inbuf_read: u8, pub inbuf_write: u8, pub hung_system: bool, + pub use_fb_console: bool, } pub struct Task { @@ -451,6 +494,7 @@ impl TrafficControl { inbuf_read: 0, inbuf_write: 0, hung_system: false, + use_fb_console: false, } } diff --git a/src/uart.rs b/src/uart.rs index 2430656..d30921c 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -1,5 +1,7 @@ use core::fmt::Write; +// todo: this should probably be in the dev module + pub trait Serial { fn put(&self, c: u8); fn putstr(&self, s: &str); From f13cb2eb121fc5cbb60e5eb8bdd7f0ee3623dfac Mon Sep 17 00:00:00 2001 From: husky Date: Thu, 11 Sep 2025 16:00:04 -0700 Subject: [PATCH 08/20] fix a ton of memory related bugs --- qemurun.sh | 2 +- src/arch/virt/asm/linker.ld | 17 +++--- src/dev/framebuffer/console.rs | 2 +- src/dev/framebuffer/mod.rs | 76 ++++++++++++-------------- src/dev/mod.rs | 23 ++++---- src/dev/virtio/block.rs | 33 +++++++++--- src/dev/virtio/gpu.rs | 86 ++++++++++------------------- src/dev/virtio/mod.rs | 30 +++++++---- src/fs/fat32.rs | 6 +-- src/memory.rs | 71 ++++++++++++------------ src/trafficcontrol.rs | 99 ++++++++++++++++------------------ 11 files changed, 215 insertions(+), 230 deletions(-) diff --git a/qemurun.sh b/qemurun.sh index e1edece..c34e967 100755 --- a/qemurun.sh +++ b/qemurun.sh @@ -2,4 +2,4 @@ storage_device="${1}" -qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -display sdl -device virtio-gpu-device -serial stdio -m 5M -device loader,cpu-num=0,file=target/riscv32imac-unknown-none-elf/release/lbos -monitor telnet:127.0.0.1:1235,server,nowait -d guest_errors,unimp \ No newline at end of file +qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -display sdl -device virtio-gpu-device -serial stdio -m 10M -device loader,cpu-num=0,file=target/riscv32imac-unknown-none-elf/release/lbos -monitor telnet:127.0.0.1:1235,server,nowait -d guest_errors,unimp \ No newline at end of file diff --git a/src/arch/virt/asm/linker.ld b/src/arch/virt/asm/linker.ld index 8face3b..5ac0cc5 100644 --- a/src/arch/virt/asm/linker.ld +++ b/src/arch/virt/asm/linker.ld @@ -4,10 +4,11 @@ ENTRY( _start ) MEMORY { - rom (wxa) : ORIGIN = 0x80000000, LENGTH = 64 * 1024 - ram (wxa) : ORIGIN = ORIGIN(rom) + LENGTH(rom), LENGTH = 64 * 1024 - virtqueues (wxa) : ORIGIN = ORIGIN(ram) + LENGTH(ram), LENGTH = 16 * 1024 * 2 - framebuffer (wxa) : ORIGIN = ORIGIN(virtqueues) + LENGTH(virtqueues), LENGTH = 320 * 240 * 3 + rom (wxa) : ORIGIN = 0x80000000, LENGTH = 0x10000 + + ram (wxa) : ORIGIN = 0x80010000, LENGTH = 0x10000 + + virtqueues (wxa) : ORIGIN = 0x80020000, LENGTH = 0x20000 } PHDRS @@ -38,7 +39,7 @@ SECTIONS { PROVIDE(_bss_start = .); *(.sbss .sbss.*) *(.bss .bss.*) - . = ALIGN(4096); + . = ALIGN(512); PROVIDE(_bss_end = .); } >ram AT>ram :bss @@ -55,9 +56,9 @@ SECTIONS { PROVIDE(_tstack_end = _tstack_start + 16384); PROVIDE(_heap_start = _tstack_end); PROVIDE(_heap_size = _MEM_END - _heap_start); + PROVIDE(_virtio_queue_1_start = ORIGIN(virtqueues)); - PROVIDE(_virtio_queue_1_end = _virtio_queue_1_start + 16384); + PROVIDE(_virtio_queue_1_end = _virtio_queue_1_start + 0x10000); PROVIDE(_virtio_queue_2_start = _virtio_queue_1_end); - PROVIDE(_virtio_queue_2_end = _virtio_queue_2_start + 16384); - PROVIDE(_framebuffer_start = ORIGIN(framebuffer)); + PROVIDE(_virtio_queue_2_end = _virtio_queue_2_start + 0x10000); } \ No newline at end of file diff --git a/src/dev/framebuffer/console.rs b/src/dev/framebuffer/console.rs index 6214267..bd98b90 100644 --- a/src/dev/framebuffer/console.rs +++ b/src/dev/framebuffer/console.rs @@ -1,4 +1,3 @@ -use crate::dev::{framebuffer_update, FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH}; use crate::dev::framebuffer::{fb_clearscreen, fb_write_char_array}; use crate::spinlock::Spinlock; use crate::trafficcontrol::TrafficControl; @@ -38,6 +37,7 @@ impl FramebufferConsole { // DOES send a framebuffer update! pub fn printstr(&mut self, tc: &mut TrafficControl, str: &str) { + return; for c in str.chars() { let mut was_special_char = false; if c == '\n' || c == '\r' { diff --git a/src/dev/framebuffer/mod.rs b/src/dev/framebuffer/mod.rs index f548170..2e99749 100644 --- a/src/dev/framebuffer/mod.rs +++ b/src/dev/framebuffer/mod.rs @@ -1,6 +1,4 @@ -use crate::dev::{ - FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP, FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH, framebuffer_update, -}; +use crate::dev::{linebuffer_push, FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH, LINEBUFFER_ADDR, LINEBUFFER_BPP}; use crate::trafficcontrol::TrafficControl; use core::sync::atomic::Ordering; @@ -37,69 +35,61 @@ impl FBColor { pub fn fb_write_char_array(tc: &mut TrafficControl, mut x: usize, mut y: usize, chars: &[char]) { const BYTES: [u8; 3] = FB_FG_COLOR.to_bytes(); - let ogx = x; - let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed); + let fbaddr = LINEBUFFER_ADDR.load(Ordering::Relaxed); if fbaddr == 0 { return; } - let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH; const CHAR_SIZE: usize = 16; let mut drew_anything = false; - for c in chars { - let c = *c; - //if c == '\n' { - // y += CHAR_SIZE; - // x = ogx; - // if y * CHAR_SIZE > FRAMEBUFFER_HEIGHT { - // break; - // } - //} else if c == ' ' { - if c == ' ' { - x += CHAR_SIZE; - //if x * CHAR_SIZE > FRAMEBUFFER_WIDTH { - // x = ogx; - // y += CHAR_SIZE; - // if y * CHAR_SIZE > FRAMEBUFFER_HEIGHT { - // break; - // } - //} - } else if c as u8 > 32 { - let c = c as u8 - 32; - let cx = (c % 16) as usize * CHAR_SIZE; - let cy = (c / 16) as usize * CHAR_SIZE; - for row in 0..CHAR_SIZE { - for col in 0..CHAR_SIZE { - let coff = (VAPFONT_W * (cy + row)) + (cx + col); - let draw = VAPFONT[coff / 8] & (0x80 >> (coff % 8)); - if draw != 0 { - unsafe { - let fb = (fbaddr as *mut u8).add(((y+row)*fbstride) + ((x+col)*4)) as *mut u32; - fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]])); - drew_anything = true; + for line in 0..FRAMEBUFFER_HEIGHT { + for c in chars { + let c = *c; + if c == ' ' { + x += CHAR_SIZE; + } else if c as u8 > 32 { + let c = c as u8 - 32; + let cx = (c % 16) as usize * CHAR_SIZE; + let cy = (c / 16) as usize * CHAR_SIZE; + for row in 0..CHAR_SIZE { + if (y+row) != line { + continue; + } + for col in 0..CHAR_SIZE { + let coff = (VAPFONT_W * (cy + row)) + (cx + col); + let draw = VAPFONT[coff / 8] & (0x80 >> (coff % 8)); + if draw != 0 { + unsafe { + //let fb = (fbaddr as *mut u8).add(((y + row) * fbstride) + ((x + col) * 4)) as *mut u32; + let fb = (fbaddr as *mut u8).add((x + col) * 4) as *mut u32; + fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]])); + drew_anything = true; + } } } } + x += CHAR_SIZE; } - x += CHAR_SIZE; + } + if drew_anything { + linebuffer_push(tc, line as u32); } } - framebuffer_update(tc, 0, 0, FRAMEBUFFER_WIDTH as u32, FRAMEBUFFER_HEIGHT as u32); } pub fn fb_clearscreen(tc: &mut TrafficControl) { const BYTES: [u8; 3] = FB_BG_COLOR.to_bytes(); - let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed); + let fbaddr = LINEBUFFER_ADDR.load(Ordering::Relaxed); if fbaddr == 0 { return; } - let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH; for y in 0..FRAMEBUFFER_HEIGHT { for x in 0..FRAMEBUFFER_WIDTH { unsafe { - let fb = (fbaddr as *mut u8).add(((y)*fbstride) + ((x)*4)) as *mut u32; + //let fb = (fbaddr as *mut u8).add(((y)*fbstride) + ((x)*4)) as *mut u32; + let fb = (fbaddr as *mut u8).add(x*4) as *mut u32; fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]])); } } + linebuffer_push(tc, y as u32); } - framebuffer_update(tc, 0, 0, FRAMEBUFFER_WIDTH as u32, FRAMEBUFFER_HEIGHT as u32); } diff --git a/src/dev/mod.rs b/src/dev/mod.rs index 2d0c5b7..2e1bd7a 100644 --- a/src/dev/mod.rs +++ b/src/dev/mod.rs @@ -4,11 +4,14 @@ use crate::trafficcontrol::TrafficControl; pub const FRAMEBUFFER_WIDTH: usize = 320; pub const FRAMEBUFFER_HEIGHT: usize = 240; +pub const LINEBUFFER_BLOCKS: usize = 3; +pub const LINEBUFFER_HEIGHT: usize = 1; + // NOTE: -// FRAMEBUFFER_ADDR should always be 0 if no framebuffer exists -// if FRAMEBUFFER_ADDR is NOT 0, then FRAMEBUFFER_STRIDE should also be NOT 0 -pub static FRAMEBUFFER_ADDR: AtomicUsize = AtomicUsize::new(0); -pub static FRAMEBUFFER_BPP: AtomicUsize = AtomicUsize::new(0); +// LINEBUFFER_ADDR should always be 0 if no framebuffer exists +// if LINEBUFFER_ADDR is NOT 0, then LINEBUFFER_BPP should also be NOT 0 +pub static LINEBUFFER_ADDR: AtomicUsize = AtomicUsize::new(0); +pub static LINEBUFFER_BPP: AtomicUsize = AtomicUsize::new(0); #[cfg(feature = "dev_virtio")] pub mod virtio; @@ -26,16 +29,16 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6 false } -pub fn framebuffer_address() -> usize { - FRAMEBUFFER_ADDR.load(Ordering::Relaxed) +pub fn linebuffer_address() -> usize { + LINEBUFFER_ADDR.load(Ordering::Relaxed) } -pub fn framebuffer_stride() -> usize { - FRAMEBUFFER_BPP.load(Ordering::Relaxed) +pub fn linebuffer_bpp() -> usize { + LINEBUFFER_BPP.load(Ordering::Relaxed) } -pub fn framebuffer_update(tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) { +pub fn linebuffer_push(tc: &mut TrafficControl, line: u32) { #[cfg(feature = "dev_virtio")] - virtio::framebuffer_update(tc, x, y, width, height); + virtio::framebuffer_update(tc, line); } \ No newline at end of file diff --git a/src/dev/virtio/block.rs b/src/dev/virtio/block.rs index b667e3c..cdf195a 100644 --- a/src/dev/virtio/block.rs +++ b/src/dev/virtio/block.rs @@ -1,4 +1,12 @@ -use crate::dev::virtio::{Descriptor, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE, VirtQueue, Used}; +use crate::dev::virtio::{ + Descriptor, Used, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, + VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, + VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, + VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, + VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, + VIRTIO_QUEUE_SIZE, VirtQueue, +}; +use crate::strprint::u32_hex; use crate::trafficcontrol::{TaskWait, TrafficControl}; unsafe extern "C" { @@ -58,7 +66,7 @@ impl VirtIoBlockDevice { // read host features let host_features = unsafe { ((addr + VIRTIO_MMIO_HOST_FEATURES) as *const u32).read_volatile() }; - let guest_features = host_features & !(1 << 5); + let guest_features = 0; let read_only = host_features & (1 << 5) != 0; unsafe { @@ -159,10 +167,9 @@ impl VirtIoBlockDevice { return; } - let blk_request = { - let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked() }.alloc_one_block(); - unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(block) } - } as *mut Request; + let blk_request = + { unsafe { tc.memory_manager.as_mut().unwrap_unchecked() }.alloc_one_block() } + as *mut Request; let desc = Descriptor { addr: unsafe { &(*blk_request) as *const _ as u64 }, len: size_of::() as u32, @@ -223,8 +230,18 @@ impl VirtIoBlockDevice { let tid = unsafe { (*rq).tid }; - let rq_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked() }.addr_to_block(rq as usize); - unsafe { tc.memory_manager.as_mut().unwrap_unchecked().free_one_block(rq_block); } + unsafe { + tc.memory_manager + .as_mut() + .unwrap_unchecked() + .free_n_blocks(rq as usize, 1); + } + let actually_freed = unsafe { + tc.memory_manager + .as_mut() + .unwrap_unchecked() + .is_addr_free(rq as usize) + }; // awaken if let Some(Some(task)) = tc.tasks.get_mut(tid as usize) { diff --git a/src/dev/virtio/gpu.rs b/src/dev/virtio/gpu.rs index cef1382..ec68811 100644 --- a/src/dev/virtio/gpu.rs +++ b/src/dev/virtio/gpu.rs @@ -1,12 +1,11 @@ use crate::arch::serial_port; -use crate::dev::{FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH}; +use crate::dev::{FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH, LINEBUFFER_BLOCKS}; use crate::dev::virtio::{heap_allocate_type, Descriptor, VirtQueue, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE}; use crate::strprint::u32_hex; use crate::trafficcontrol::TrafficControl; unsafe extern "C" { fn _virtio_queue_2_start(); - fn _framebuffer_start(); } pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100; @@ -181,7 +180,9 @@ impl VirtIoGpuDevice { } - // allocate memory for framebuffer + // allocate memory for linebuffer + let linebuffer_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(LINEBUFFER_BLOCKS) }; + let latest_response = heap_allocate_type::(tc) as *const _ as usize; let mut gpu = Self { @@ -190,7 +191,7 @@ impl VirtIoGpuDevice { queue: queue_ptr, idx: 0, ack_used_idx: 0, - framebuffer: _framebuffer_start as usize, + framebuffer: linebuffer_ptr as usize, width: FRAMEBUFFER_WIDTH, height: FRAMEBUFFER_HEIGHT, }; @@ -251,7 +252,7 @@ impl VirtIoGpuDevice { free_me[1] = unsafe { &(*cmd) as *const _ as usize }; let mem_entry = heap_allocate_type::(tc); mem_entry.addr = gpu.framebuffer as u64; - mem_entry.length = (gpu.height * gpu.width * 4) as u32; + mem_entry.length = (gpu.width * 4) as u32; mem_entry.padding = 0; free_me[2] = unsafe { &(*mem_entry) as *const _ as usize }; @@ -321,14 +322,11 @@ impl VirtIoGpuDevice { //gpu.pending(tc); for free in free_me { - let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(free) }; unsafe { - tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, 1); + tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(free, 1); } } - gpu.transfer(tc, 0, 0, gpu.width as u32, gpu.height as u32); - Ok(gpu) } @@ -353,8 +351,12 @@ impl VirtIoGpuDevice { } unsafe { - (*(self.queue as *mut VirtQueue)).avail.ring[(*(self.queue as *mut VirtQueue)).avail.idx as usize % VIRTIO_QUEUE_SIZE] = head; - (*(self.queue as *mut VirtQueue)).avail.idx = (*(self.queue as *mut VirtQueue)).avail.idx.wrapping_add(1); + (*(self.queue as *mut VirtQueue)).avail.ring + [(*(self.queue as *mut VirtQueue)).avail.idx as usize % VIRTIO_QUEUE_SIZE] = head; + } + unsafe { + (*(self.queue as *mut VirtQueue)).avail.idx = + (*(self.queue as *mut VirtQueue)).avail.idx.wrapping_add(1); } // notify @@ -384,8 +386,12 @@ impl VirtIoGpuDevice { } unsafe { - (*(self.queue as *mut VirtQueue)).avail.ring[(*(self.queue as *mut VirtQueue)).avail.idx as usize % VIRTIO_QUEUE_SIZE] = head; - (*(self.queue as *mut VirtQueue)).avail.idx = (*(self.queue as *mut VirtQueue)).avail.idx.wrapping_add(1); + (*(self.queue as *mut VirtQueue)).avail.ring + [(*(self.queue as *mut VirtQueue)).avail.idx as usize % VIRTIO_QUEUE_SIZE] = head; + } + unsafe { + (*(self.queue as *mut VirtQueue)).avail.idx = + (*(self.queue as *mut VirtQueue)).avail.idx.wrapping_add(1); } // notify @@ -397,42 +403,12 @@ impl VirtIoGpuDevice { // WARNING: THIS VERSION OF PENDING DOES NOT FREE ANYTHING pub fn pending(&mut self, tc: &mut TrafficControl) { let queue = unsafe { &(*(self.queue as *mut VirtQueue)) }; - let mut pended = false; while self.ack_used_idx != queue.used.idx { - pended = true; - let uart = serial_port(); - if let Some(uart) = uart { - //uart.putstr("pended\n"); - } - let elem = &queue.used.ring[self.ack_used_idx as usize % VIRTIO_QUEUE_SIZE]; - let desc = &queue.desc[elem.id as usize]; - let addr = desc.addr as usize; - //let num_blocks = (desc.len as usize).div_ceil(512); - //let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(addr) }; - - //// free - //unsafe { - // tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, num_blocks); - //} self.ack_used_idx = self.ack_used_idx.wrapping_add(1); - if self.ack_used_idx >= VIRTIO_QUEUE_SIZE as u16 { - self.ack_used_idx = 0; - } - - } - if !pended { - let uart = serial_port(); - if let Some(uart) = uart { - uart.putstr("no pended\n"); - } } } - pub fn transfer(&mut self, tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) { - let uart = serial_port(); - if let Some(uart) = uart { - //uart.putstr("transfer\n"); - } + pub fn transfer(&mut self, tc: &mut TrafficControl, y: u32) { let mut free_me = [0; 4]; let cmd = heap_allocate_type::(tc); @@ -442,10 +418,10 @@ impl VirtIoGpuDevice { cmd.header.ctx_id = 0; cmd.header.ring_idx = 0; cmd.header.padding = [0; 3]; - cmd.rect.x = x; + cmd.rect.x = 0; cmd.rect.y = y; - cmd.rect.width = width; - cmd.rect.height = height; + cmd.rect.width = self.width as u32; + cmd.rect.height = 1; cmd.offset = 0; cmd.resource_id = 1; cmd.padding = 0; @@ -475,10 +451,10 @@ impl VirtIoGpuDevice { cmd.header.ctx_id = 0; cmd.header.ring_idx = 0; cmd.header.padding = [0; 3]; - cmd.rect.x = x; + cmd.rect.x = 0; cmd.rect.y = y; - cmd.rect.width = width; - cmd.rect.height = height; + cmd.rect.width = self.width as u32; + cmd.rect.height = 1; cmd.resource_id = 1; cmd.padding = 0; free_me[2] = unsafe { &(*cmd) as *const _ as usize }; @@ -497,17 +473,9 @@ impl VirtIoGpuDevice { }; self.send_rq_rsp(desc_rq, desc_resp); - // awful hack so that we don't overload the queue - while { - self.ack_used_idx == unsafe { (self.queue as *const VirtQueue).read_volatile().used.idx } - } { - - } - //self.pending(tc); for free in free_me.into_iter().skip(1) { - let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(free) }; unsafe { - tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, 1); + tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(free, 1); } } } diff --git a/src/dev/virtio/mod.rs b/src/dev/virtio/mod.rs index e717903..ed95305 100644 --- a/src/dev/virtio/mod.rs +++ b/src/dev/virtio/mod.rs @@ -2,7 +2,7 @@ //! WARNING: virtio is currently completely broken! don't use it! use core::sync::atomic::Ordering; -use crate::dev::{FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP}; +use crate::dev::{LINEBUFFER_ADDR, LINEBUFFER_BPP}; use crate::dev::virtio::block::{VirtIoBlockDevice, VirtIoBlockDeviceError}; use crate::dev::virtio::gpu::{VirtIoGpuDevice, VirtIoGpuDeviceError}; use crate::spinlock::Spinlock; @@ -36,7 +36,7 @@ pub const VIRTIO_MMIO_STATUS_FAILED: u32 = 1 << 7; pub const VIRTIO_DESC_F_NEXT: u16 = 1 << 0; pub const VIRTIO_DESC_F_WRITE: u16 = 1 << 1; -pub const VIRTIO_QUEUE_SIZE: usize = 128; +pub const VIRTIO_QUEUE_SIZE: usize = 16; pub static VIRTIO_DEVICES: Spinlock<[Option; VIRTIO_MMIO_DEVCOUNT]> = Spinlock::new([const { None }; VIRTIO_MMIO_DEVCOUNT]); pub static VIRTIO_HARD_BLOCK_DEVICE: Spinlock> = Spinlock::new(None); @@ -83,8 +83,7 @@ pub struct VirtQueue { pub fn heap_allocate_type(tc: &mut TrafficControl) -> &'static mut T { let num_blocks = size_of::().div_ceil(512); - let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; - let addr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(block) }; + let addr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; unsafe { &mut (*(addr as *mut T)) } } @@ -131,11 +130,12 @@ pub fn probe_virtio_devices(tc: &mut TrafficControl) { } } 16 => { + continue; // gpu device let gpu_device = VirtIoGpuDevice::new_and_init(tc, addr); if let Ok(gpu_device) = gpu_device { - FRAMEBUFFER_ADDR.store(gpu_device.framebuffer, Ordering::Relaxed); - FRAMEBUFFER_BPP.store(4, Ordering::Relaxed); // virtio always uses 4 byte stride + LINEBUFFER_ADDR.store(gpu_device.framebuffer, Ordering::Relaxed); + LINEBUFFER_BPP.store(4, Ordering::Relaxed); // virtio always uses 4 byte stride devices[i] = Some(VirtIoDevice::GPUDevice(gpu_device)); *VIRTIO_GPU_DEVICE.lock() = Some(i as u8); if let Some(serial_port) = &serial_port { @@ -183,8 +183,17 @@ pub fn handle_interrupt(interrupt: u32, tc: &mut TrafficControl) { } } } - VirtIoDevice::GPUDevice(_) => { - // todo: handle gpu interrupts + VirtIoDevice::GPUDevice(gpudev) => { + return; + let gpu = { + let lock = VIRTIO_GPU_DEVICE.lock(); + *lock + }; + if let Some(gpu) = gpu { + if gpu as usize == idx { + gpudev.pending(tc); + } + } } } } @@ -222,7 +231,8 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6 false } -pub fn framebuffer_update(tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) -> bool { +pub fn framebuffer_update(tc: &mut TrafficControl, y: u32) -> bool { + return true; let idx = { let lock = VIRTIO_GPU_DEVICE.lock(); *lock @@ -232,7 +242,7 @@ pub fn framebuffer_update(tc: &mut TrafficControl, x: u32, y: u32, width: u32, h if let Some(device) = devices[idx as usize].as_mut() { return match device { VirtIoDevice::GPUDevice(device) => { - device.transfer(tc, x, y, width, height); + device.transfer(tc, y); true } _ => { diff --git a/src/fs/fat32.rs b/src/fs/fat32.rs index 507e1e0..71df609 100644 --- a/src/fs/fat32.rs +++ b/src/fs/fat32.rs @@ -1,4 +1,5 @@ use crate::fs::{Record, RecordType}; +use crate::strprint::u32_hex; #[repr(packed(1), C)] pub struct BPB { @@ -286,9 +287,9 @@ pub fn seek_forward_one_sector(bpb: &BPBUseful, reader: &mut Fat32FileReader) -> } let table_value = unsafe { ((fat_addr+(ent_offset)) as *const u32).read_volatile() } & 0x0FFFFFFF; + crate::syscalls::free_blocks(fat_addr, 1); if table_value >= 0x0FFFFFF8 { // no more clusters, whole file has been read - crate::syscalls::free_blocks(fat_addr, 1); reader.eof = 1; return false; } else if table_value >= 0x00000002 { @@ -298,12 +299,10 @@ pub fn seek_forward_one_sector(bpb: &BPBUseful, reader: &mut Fat32FileReader) -> ((reader.cluster - 2) * bpb.sectors_per_cluster as usize) + bpb.first_data_sector(); reader.sector_offset = 0; if table_value == 0x00000000 || table_value == 0x00000001 { - crate::syscalls::free_blocks(fat_addr, 1); reader.eof = 1; return false; } else if table_value == 0x0FFFFFF7 { // bad cluster, stop reading - crate::syscalls::free_blocks(fat_addr, 1); crate::syscalls::write_terminal(b"badcluster"); reader.eof = 1; return false; @@ -338,6 +337,7 @@ pub fn read_file(bpb: &BPBUseful, reader: &mut Fat32FileReader, buffer: &mut [u8 let mut left_in_sector = 512 - reader.sector_offset; if left_in_sector == 0 { crate::syscalls::write_terminal(b"SECTORNULL?"); + crate::syscalls::free_blocks(sector_addr, 1); return false; } while buffer_idx < buffer.len() && left_in_sector > 0 { diff --git a/src/memory.rs b/src/memory.rs index ecfdfa8..4c6a1e3 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -10,16 +10,11 @@ unsafe extern "C" { } pub const BLOCK_SIZE: usize = 512; -#[cfg(feature = "arch_virt")] -pub const TOTAL_MEMORY: usize = 1048510; -#[cfg(feature = "arch_ppc32")] -pub const TOTAL_MEMORY: usize = 1024 * 1024; // 1MiB; -pub const MEM_BLOCKS: usize = TOTAL_MEMORY / BLOCK_SIZE; pub struct MemoryManager { pub heap_start: usize, pub heap_size: usize, - pub blockmap: [u8; (MEM_BLOCKS+7) / 8], + pub blockmap: [u8; 128/8] } impl MemoryManager { @@ -28,15 +23,19 @@ impl MemoryManager { { let uart = serial_port(); if let Some(uart) = uart { + uart.putstr("heap_start: "); + uart.put_bytes(&u32_hex(_heap_start as u32)); uart.putstr("totalmem: "); - uart.put_bytes(&u32_hex(MEM_BLOCKS as u32)); + uart.put_bytes(&u32_hex(_heap_size as u32)); uart.putstr("\n"); } } + Self { heap_start: _heap_start as _, heap_size: _heap_size as _, - blockmap: [0; (MEM_BLOCKS+7) / 8], + + blockmap: [0; 16], } } #[cfg(feature = "arch_ppc32")] @@ -47,22 +46,7 @@ impl MemoryManager { blockmap: [0; (MEM_BLOCKS+7) / 8], } } - - pub fn is_block_free(&self, block: usize) -> bool { - let block = block % 8; - let block = block / 8; - let val = self.blockmap[block]; - (val & (1 << block)) == 0 - } - pub fn block_to_addr(&self, block: usize) -> usize { - block * BLOCK_SIZE + self.heap_start - } - - pub fn addr_to_block(&self, addr: usize) -> usize { - (addr - self.heap_start) / BLOCK_SIZE - } - pub fn alloc_one_block(&mut self) -> usize { /* for (i, v) in self.blockmap.iter_mut().enumerate() { @@ -81,11 +65,19 @@ impl MemoryManager { */ self.alloc_n_blocks(1) } - pub fn free_one_block(&mut self, block: usize) { + pub fn free_one_block(&mut self, addr: usize) { + let block = (addr - self.heap_start) / BLOCK_SIZE; self.blockmap[block / 8] &= !(1 << (block % 8)); } - + + pub fn is_addr_free(&self, addr: usize) -> bool { + let block = (addr - self.heap_start) / BLOCK_SIZE; + let val = (1 << (block % 8)) & self.blockmap[block / 8]; + val == 0 + } + // can easily fail if too many blocks are requested, will return 0 on failure + // RETURNS ADDRESS pub fn alloc_n_blocks(&mut self, n: usize) -> usize { if n == 0 { return 0; @@ -95,23 +87,29 @@ impl MemoryManager { for i in 0..self.blockmap.len() { for j in 0..8 { let block = i * 8 + j; + let valid = block < (self.heap_size / BLOCK_SIZE); let val = (1 << j) & self.blockmap[i]; - if val == 0 { + if val == 0 && valid { // this is free self.blockmap[i] |= 1 << j; if first_block.is_none() { first_block = Some(block); } found += 1; - if found >= n { - return first_block.unwrap(); + if found == n { + let addr = (first_block.unwrap() * BLOCK_SIZE) + self.heap_start; + return addr; + } + if found > n { + rough_panic(['h', 'm', '?']) } } else { // used, restart search let mut i = 0; while found > 0 { found -= 1; - self.free_one_block(first_block.unwrap() + i); + let addr = ((first_block.unwrap() * BLOCK_SIZE) + self.heap_start) + (i * BLOCK_SIZE); + self.free_one_block(addr); i += 1; } first_block = None; @@ -122,19 +120,24 @@ impl MemoryManager { rough_panic(['o', 'o', 'm']) } - pub fn free_n_blocks(&mut self, block: usize, n: usize) { - if n == 0 || block >= MEM_BLOCKS || block + n > MEM_BLOCKS { - return; + pub fn free_n_blocks(&mut self, addr: usize, n: usize) { + if n == 0 || addr >= self.heap_start + self.heap_size || addr + n * BLOCK_SIZE > self.heap_start + self.heap_size { + rough_panic(['b', 'm', 'f']) } for i in 0..n { - self.free_one_block(block + i); + self.free_one_block(addr + (i * BLOCK_SIZE)); } } pub fn used_blocks(&self) -> usize { let mut used_blocks = 0; - for v in self.blockmap.iter() { + for (i, v) in self.blockmap.iter().enumerate() { for j in 0..8 { + let block = i * 8 + j; + let valid = block < (self.heap_size / BLOCK_SIZE); + if !valid { + continue; + } let val = (1 << j) & *v; if val != 0 { // used diff --git a/src/trafficcontrol.rs b/src/trafficcontrol.rs index 2e7ed96..0a3a8a7 100644 --- a/src/trafficcontrol.rs +++ b/src/trafficcontrol.rs @@ -4,7 +4,8 @@ use crate::arch::serial_port; use crate::memory::{BLOCK_SIZE, MemoryManager}; use crate::spinlock::Spinlock; use crate::syscalls::SysCall; -use crate::{arch}; +use crate::{arch, rough_panic}; +use crate::strprint::u32_hex; #[repr(u32)] pub enum TaskWait { @@ -13,7 +14,7 @@ pub enum TaskWait { WaitForTaskExit = 1 << 2, } -pub const STACK_SIZE_IN_BLOCKS: usize = 16; +pub const STACK_SIZE_IN_BLOCKS: usize = 8; pub const MAX_TASKS: usize = 8; @@ -69,19 +70,12 @@ pub fn handle_syscall( let add = a1 as *mut TaskSetup; tc.init_mem_if_not(); - let blockalloc = unsafe { - tc.memory_manager - .as_mut() - .unwrap_unchecked() - .alloc_n_blocks(STACK_SIZE_IN_BLOCKS) - }; let sp = unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() - .block_to_addr(blockalloc) - + (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 { @@ -93,10 +87,8 @@ pub fn handle_syscall( incoming_notifications: [const { None }; MAX_TASKS], ackwait: [0; MAX_TASKS], task_wait: 0, - ddi_mem_start_block: unsafe { - tc.memory_manager - .as_mut().unwrap_unchecked() - .addr_to_block((*add).ddi_first_addr) + ddi_mem_start: unsafe { + (*add).ddi_first_addr }, ddi_mem_blocks_count: unsafe { (*add).ddi_size / BLOCK_SIZE }, trap_frame: arch::virt::tasks::setup_task(sp), @@ -134,7 +126,7 @@ pub fn handle_syscall( tc.memory_manager .as_mut() .unwrap_unchecked() - .free_one_block(blockalloc); + .free_n_blocks(sp, 1); } MAX_TASKS + 1 @@ -163,34 +155,22 @@ pub fn handle_syscall( } SysCall::AllocBlocks => { let count = a1; - let block = unsafe { - tc.memory_manager - .as_mut() - .unwrap_unchecked() - .alloc_n_blocks(count) - }; let addr = unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() - .block_to_addr(block) + .alloc_n_blocks(count) }; addr } SysCall::FreeBlocks => { let addr = a1; let count = a2; - let block = unsafe { - tc.memory_manager - .as_mut() - .unwrap_unchecked() - .addr_to_block(addr) - }; unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() - .free_n_blocks(block, count) + .free_n_blocks(addr, count) }; 0 @@ -236,8 +216,34 @@ pub fn handle_syscall( } SysCall::InitKernel => { tc.init_mem_if_not(); + { + // sanity check + const FAILURE: &str = "memory manager sanity check failed"; + let mem = unsafe { + tc.memory_manager.as_mut().unwrap_unchecked() + }; + let addr = mem.alloc_one_block(); + let addr2 = mem.alloc_n_blocks(2); + if mem.is_addr_free(addr) || mem.is_addr_free(addr2) || mem.is_addr_free(addr2 + 512) { + let uart = serial_port(); + if let Some(uart) = uart { + uart.putstr(FAILURE); + } + rough_panic([';', '-', ';']) + } + mem.free_one_block(addr); + mem.free_n_blocks(addr2, 2); + + if !(mem.is_addr_free(addr) || mem.is_addr_free(addr2) || mem.is_addr_free(addr2 + 512)) { + let uart = serial_port(); + if let Some(uart) = uart { + uart.putstr(FAILURE); + } + rough_panic([';', '-', ';']) + } + } crate::dev::probe_devices(&mut tc); - if crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { + if crate::dev::LINEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { tc.use_fb_console = true; { let uart = serial_port(); @@ -251,7 +257,6 @@ pub fn handle_syscall( SysCall::SendNotification => { let taskid = a1; let addr = a2; - let addr_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(addr) }; let ci = tc.current; if let Some(Some(task)) = tc.tasks.get_mut(taskid).as_mut() { let waiting = task.wait & TaskWait::WaitForNotification as u32 != 0; @@ -268,7 +273,7 @@ pub fn handle_syscall( current_task.ackwait[taskid] = 1; } } else { - task.incoming_notifications[ci] = Some(addr_block); // queue it for them + task.incoming_notifications[ci] = Some(addr); // queue it for them } 0 } else { @@ -281,15 +286,9 @@ pub fn handle_syscall( suspend = true; if let Some(task) = tc.tasks[ci].as_mut() { // is there already a pending notification? - for (i, block) in task.incoming_notifications.iter_mut().enumerate() { - if let Some(block) = block.take() { + for (i, addr) in task.incoming_notifications.iter_mut().enumerate() { + if let Some(addr) = addr.take() { // yes, there is - let addr = unsafe { - tc.memory_manager - .as_mut() - .unwrap_unchecked() - .block_to_addr(block as usize) - }; if let Some(sender_task) = tc.tasks[i].as_mut() { if sender_task.ackwait[ci] == 3 { // they are waiting for us to ack sender_task.ackwait[ci] = 0; @@ -352,7 +351,7 @@ pub fn handle_syscall( 0 } SysCall::EnableFramebufferConsole => { - if crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { + if crate::dev::LINEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { tc.use_fb_console = true; // clear the screen let mut fbcons = crate::dev::framebuffer::console::FBCONSOLE.lock(); @@ -362,14 +361,14 @@ pub fn handle_syscall( 0 } SysCall::FramebufferPointer => { - crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) + crate::dev::LINEBUFFER_ADDR.load(Ordering::Relaxed) } SysCall::FlushFramebufferRect => { let x = a1; let y = a2; let w = a3; let h = a4; - crate::dev::framebuffer_update(&mut tc, x as u32, y as u32, w as u32, h as u32); + crate::dev::linebuffer_push(&mut tc, y as u32); 0 } }, @@ -389,20 +388,14 @@ pub fn context_switch<'a>(tc: &'a mut TrafficControl, current: Task) -> Option<& if want_exit { tc.init_mem_if_not(); 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_block).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); if sp != 0 { - let stackblock = unsafe { - tc.memory_manager - .as_mut() - .unwrap_unchecked() - .addr_to_block(sp - (BLOCK_SIZE * STACK_SIZE_IN_BLOCKS)) - }; unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() - .free_n_blocks(stackblock, 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() @@ -475,7 +468,7 @@ pub struct Task { 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 pub task_wait: u8, - pub ddi_mem_start_block: usize, + pub ddi_mem_start: usize, pub ddi_mem_blocks_count: usize, #[cfg(feature = "arch_virt")] pub trap_frame: arch::virt::trap::TrapFrame, From b3af2a79672ec3050d350226d56147aa71c80b0e Mon Sep 17 00:00:00 2001 From: husky Date: Thu, 11 Sep 2025 16:28:11 -0700 Subject: [PATCH 09/20] working again --- src/arch/virt/asm/linker.ld | 4 ++ src/dev/framebuffer/console.rs | 1 - src/dev/framebuffer/mod.rs | 81 +++++++++++++++++++--------------- src/dev/mod.rs | 20 ++++----- src/dev/virtio/gpu.rs | 43 +++++++++++------- src/dev/virtio/mod.rs | 16 ++++--- src/memory.rs | 2 +- src/trafficcontrol.rs | 16 +++---- 8 files changed, 105 insertions(+), 78 deletions(-) diff --git a/src/arch/virt/asm/linker.ld b/src/arch/virt/asm/linker.ld index 5ac0cc5..31029b4 100644 --- a/src/arch/virt/asm/linker.ld +++ b/src/arch/virt/asm/linker.ld @@ -9,6 +9,8 @@ MEMORY ram (wxa) : ORIGIN = 0x80010000, LENGTH = 0x10000 virtqueues (wxa) : ORIGIN = 0x80020000, LENGTH = 0x20000 + + framebuffer (wxa) : ORIGIN = 0x80040000, LENGTH = 320 * 240 * 4 } PHDRS @@ -61,4 +63,6 @@ SECTIONS { PROVIDE(_virtio_queue_1_end = _virtio_queue_1_start + 0x10000); PROVIDE(_virtio_queue_2_start = _virtio_queue_1_end); PROVIDE(_virtio_queue_2_end = _virtio_queue_2_start + 0x10000); + + PROVIDE(_framebuffer_start = ORIGIN(framebuffer)); } \ No newline at end of file diff --git a/src/dev/framebuffer/console.rs b/src/dev/framebuffer/console.rs index bd98b90..d9d0120 100644 --- a/src/dev/framebuffer/console.rs +++ b/src/dev/framebuffer/console.rs @@ -37,7 +37,6 @@ impl FramebufferConsole { // DOES send a framebuffer update! pub fn printstr(&mut self, tc: &mut TrafficControl, str: &str) { - return; for c in str.chars() { let mut was_special_char = false; if c == '\n' || c == '\r' { diff --git a/src/dev/framebuffer/mod.rs b/src/dev/framebuffer/mod.rs index 2e99749..d651117 100644 --- a/src/dev/framebuffer/mod.rs +++ b/src/dev/framebuffer/mod.rs @@ -1,4 +1,5 @@ -use crate::dev::{linebuffer_push, FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH, LINEBUFFER_ADDR, LINEBUFFER_BPP}; +use crate::dev::virtio::framebuffer_update; +use crate::dev::{FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP, FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH}; use crate::trafficcontrol::TrafficControl; use core::sync::atomic::Ordering; @@ -33,63 +34,73 @@ impl FBColor { } } -pub fn fb_write_char_array(tc: &mut TrafficControl, mut x: usize, mut y: usize, chars: &[char]) { +pub fn fb_write_char_array(tc: &mut TrafficControl, mut x: usize, y: usize, chars: &[char]) { + let ogx = x; + let ogy = y; const BYTES: [u8; 3] = FB_FG_COLOR.to_bytes(); - let fbaddr = LINEBUFFER_ADDR.load(Ordering::Relaxed); + let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed); if fbaddr == 0 { return; } + let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH; const CHAR_SIZE: usize = 16; - let mut drew_anything = false; - for line in 0..FRAMEBUFFER_HEIGHT { - for c in chars { - let c = *c; - if c == ' ' { - x += CHAR_SIZE; - } else if c as u8 > 32 { - let c = c as u8 - 32; - let cx = (c % 16) as usize * CHAR_SIZE; - let cy = (c / 16) as usize * CHAR_SIZE; - for row in 0..CHAR_SIZE { - if (y+row) != line { - continue; - } - for col in 0..CHAR_SIZE { - let coff = (VAPFONT_W * (cy + row)) + (cx + col); - let draw = VAPFONT[coff / 8] & (0x80 >> (coff % 8)); - if draw != 0 { - unsafe { - //let fb = (fbaddr as *mut u8).add(((y + row) * fbstride) + ((x + col) * 4)) as *mut u32; - let fb = (fbaddr as *mut u8).add((x + col) * 4) as *mut u32; - fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]])); - drew_anything = true; - } + for c in chars { + let c = *c; + if c == ' ' { + x += CHAR_SIZE; + } else if c as u8 > 32 { + let c = c as u8 - 32; + let cx = (c % 16) as usize * CHAR_SIZE; + let cy = (c / 16) as usize * CHAR_SIZE; + for row in 0..CHAR_SIZE { + for col in 0..CHAR_SIZE { + let coff = (VAPFONT_W * (cy + row)) + (cx + col); + let draw = VAPFONT[coff / 8] & (0x80 >> (coff % 8)); + if draw != 0 { + unsafe { + let fb = (fbaddr as *mut u8) + .add(((y + row) * fbstride) + ((x + col) * 4)) + as *mut u32; + fb.write_volatile(u32::from_ne_bytes([ + 0, BYTES[0], BYTES[1], BYTES[2], + ])); } } } - x += CHAR_SIZE; } - } - if drew_anything { - linebuffer_push(tc, line as u32); + x += CHAR_SIZE; } } + + framebuffer_update( + tc, + 0, + 0, + FRAMEBUFFER_WIDTH as u32, + FRAMEBUFFER_HEIGHT as u32, + ); } pub fn fb_clearscreen(tc: &mut TrafficControl) { const BYTES: [u8; 3] = FB_BG_COLOR.to_bytes(); - let fbaddr = LINEBUFFER_ADDR.load(Ordering::Relaxed); + let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed); if fbaddr == 0 { return; } + let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH; for y in 0..FRAMEBUFFER_HEIGHT { for x in 0..FRAMEBUFFER_WIDTH { unsafe { - //let fb = (fbaddr as *mut u8).add(((y)*fbstride) + ((x)*4)) as *mut u32; - let fb = (fbaddr as *mut u8).add(x*4) as *mut u32; + let fb = (fbaddr as *mut u8).add(((y) * fbstride) + ((x) * 4)) as *mut u32; fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]])); } } - linebuffer_push(tc, y as u32); } + framebuffer_update( + tc, + 0, + 0, + FRAMEBUFFER_WIDTH as u32, + FRAMEBUFFER_HEIGHT as u32, + ); } diff --git a/src/dev/mod.rs b/src/dev/mod.rs index 2e1bd7a..1f84f0d 100644 --- a/src/dev/mod.rs +++ b/src/dev/mod.rs @@ -8,10 +8,10 @@ pub const LINEBUFFER_BLOCKS: usize = 3; pub const LINEBUFFER_HEIGHT: usize = 1; // NOTE: -// LINEBUFFER_ADDR should always be 0 if no framebuffer exists -// if LINEBUFFER_ADDR is NOT 0, then LINEBUFFER_BPP should also be NOT 0 -pub static LINEBUFFER_ADDR: AtomicUsize = AtomicUsize::new(0); -pub static LINEBUFFER_BPP: AtomicUsize = AtomicUsize::new(0); +// FRAMEBUFFER_ADDR should always be 0 if no framebuffer exists +// if FRAMEBUFFER_ADDR is NOT 0, then FRAMEBUFFER_BPP should also be NOT 0 +pub static FRAMEBUFFER_ADDR: AtomicUsize = AtomicUsize::new(0); +pub static FRAMEBUFFER_BPP: AtomicUsize = AtomicUsize::new(0); #[cfg(feature = "dev_virtio")] pub mod virtio; @@ -29,16 +29,16 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6 false } -pub fn linebuffer_address() -> usize { - LINEBUFFER_ADDR.load(Ordering::Relaxed) +pub fn framebuffer_address() -> usize { + FRAMEBUFFER_ADDR.load(Ordering::Relaxed) } -pub fn linebuffer_bpp() -> usize { - LINEBUFFER_BPP.load(Ordering::Relaxed) +pub fn framebuffer_bpp() -> usize { + FRAMEBUFFER_BPP.load(Ordering::Relaxed) } -pub fn linebuffer_push(tc: &mut TrafficControl, line: u32) { +pub fn framebuffer_push(tc: &mut TrafficControl, x: u32, y: u32, w: u32, h: u32) { #[cfg(feature = "dev_virtio")] - virtio::framebuffer_update(tc, line); + virtio::framebuffer_update(tc, x, y, w, h); } \ No newline at end of file diff --git a/src/dev/virtio/gpu.rs b/src/dev/virtio/gpu.rs index ec68811..80f41cc 100644 --- a/src/dev/virtio/gpu.rs +++ b/src/dev/virtio/gpu.rs @@ -6,6 +6,7 @@ use crate::trafficcontrol::TrafficControl; unsafe extern "C" { fn _virtio_queue_2_start(); + fn _framebuffer_start(); } pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100; @@ -180,8 +181,8 @@ impl VirtIoGpuDevice { } - // allocate memory for linebuffer - let linebuffer_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(LINEBUFFER_BLOCKS) }; + // allocate memory for framebuffer + let framebuffer_ptr = _framebuffer_start as usize; let latest_response = heap_allocate_type::(tc) as *const _ as usize; @@ -191,7 +192,7 @@ impl VirtIoGpuDevice { queue: queue_ptr, idx: 0, ack_used_idx: 0, - framebuffer: linebuffer_ptr as usize, + framebuffer: framebuffer_ptr, width: FRAMEBUFFER_WIDTH, height: FRAMEBUFFER_HEIGHT, }; @@ -252,7 +253,7 @@ impl VirtIoGpuDevice { free_me[1] = unsafe { &(*cmd) as *const _ as usize }; let mem_entry = heap_allocate_type::(tc); mem_entry.addr = gpu.framebuffer as u64; - mem_entry.length = (gpu.width * 4) as u32; + mem_entry.length = (gpu.height * gpu.width * 4) as u32; mem_entry.padding = 0; free_me[2] = unsafe { &(*mem_entry) as *const _ as usize }; @@ -319,7 +320,7 @@ impl VirtIoGpuDevice { } { } - //gpu.pending(tc); + gpu.pending(tc); for free in free_me { unsafe { @@ -408,8 +409,8 @@ impl VirtIoGpuDevice { } } - pub fn transfer(&mut self, tc: &mut TrafficControl, y: u32) { - let mut free_me = [0; 4]; + pub fn transfer(&mut self, tc: &mut TrafficControl, x: u32, y: u32, w: u32, h: u32) { + let mut free_me = [0; 2]; let cmd = heap_allocate_type::(tc); cmd.header.ctrl_type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D; @@ -418,14 +419,14 @@ impl VirtIoGpuDevice { cmd.header.ctx_id = 0; cmd.header.ring_idx = 0; cmd.header.padding = [0; 3]; - cmd.rect.x = 0; + cmd.rect.x = x; cmd.rect.y = y; - cmd.rect.width = self.width as u32; - cmd.rect.height = 1; + cmd.rect.width = w; + cmd.rect.height = h; cmd.offset = 0; cmd.resource_id = 1; cmd.padding = 0; - free_me[1] = unsafe { &(*cmd) as *const _ as usize }; + free_me[0] = unsafe { &(*cmd) as *const _ as usize }; let desc_rq = Descriptor { addr: unsafe { &(*cmd) as *const _ as u64 }, @@ -451,13 +452,13 @@ impl VirtIoGpuDevice { cmd.header.ctx_id = 0; cmd.header.ring_idx = 0; cmd.header.padding = [0; 3]; - cmd.rect.x = 0; + cmd.rect.x = x; cmd.rect.y = y; - cmd.rect.width = self.width as u32; - cmd.rect.height = 1; + cmd.rect.width = w; + cmd.rect.height = h; cmd.resource_id = 1; cmd.padding = 0; - free_me[2] = unsafe { &(*cmd) as *const _ as usize }; + free_me[1] = unsafe { &(*cmd) as *const _ as usize }; let desc_rq = Descriptor { addr: unsafe { &(*cmd) as *const _ as u64 }, @@ -473,7 +474,17 @@ impl VirtIoGpuDevice { }; self.send_rq_rsp(desc_rq, desc_resp); - for free in free_me.into_iter().skip(1) { + + // hack to make sure the queue doesn't get overloaded + while { + let queue = unsafe { (self.queue as *mut VirtQueue).read_volatile() }; + self.ack_used_idx == queue.used.idx + } { + + } + self.pending(tc); + + for free in free_me.into_iter() { unsafe { tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(free, 1); } diff --git a/src/dev/virtio/mod.rs b/src/dev/virtio/mod.rs index ed95305..a838e87 100644 --- a/src/dev/virtio/mod.rs +++ b/src/dev/virtio/mod.rs @@ -2,9 +2,10 @@ //! WARNING: virtio is currently completely broken! don't use it! use core::sync::atomic::Ordering; -use crate::dev::{LINEBUFFER_ADDR, LINEBUFFER_BPP}; +use crate::dev::{FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP}; use crate::dev::virtio::block::{VirtIoBlockDevice, VirtIoBlockDeviceError}; use crate::dev::virtio::gpu::{VirtIoGpuDevice, VirtIoGpuDeviceError}; +use crate::rough_panic; use crate::spinlock::Spinlock; use crate::strprint::twodigit; use crate::trafficcontrol::{TrafficControl}; @@ -83,6 +84,9 @@ pub struct VirtQueue { pub fn heap_allocate_type(tc: &mut TrafficControl) -> &'static mut T { let num_blocks = size_of::().div_ceil(512); + if num_blocks != 1 { + rough_panic(['f', 'm', 'l']); + } let addr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) }; unsafe { &mut (*(addr as *mut T)) } } @@ -130,12 +134,11 @@ pub fn probe_virtio_devices(tc: &mut TrafficControl) { } } 16 => { - continue; // gpu device let gpu_device = VirtIoGpuDevice::new_and_init(tc, addr); if let Ok(gpu_device) = gpu_device { - LINEBUFFER_ADDR.store(gpu_device.framebuffer, Ordering::Relaxed); - LINEBUFFER_BPP.store(4, Ordering::Relaxed); // virtio always uses 4 byte stride + FRAMEBUFFER_ADDR.store(gpu_device.framebuffer, Ordering::Relaxed); + FRAMEBUFFER_BPP.store(4, Ordering::Relaxed); // virtio always uses 4 byte stride devices[i] = Some(VirtIoDevice::GPUDevice(gpu_device)); *VIRTIO_GPU_DEVICE.lock() = Some(i as u8); if let Some(serial_port) = &serial_port { @@ -231,8 +234,7 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6 false } -pub fn framebuffer_update(tc: &mut TrafficControl, y: u32) -> bool { - return true; +pub fn framebuffer_update(tc: &mut TrafficControl, x: u32, y: u32, w: u32, h: u32) -> bool { let idx = { let lock = VIRTIO_GPU_DEVICE.lock(); *lock @@ -242,7 +244,7 @@ pub fn framebuffer_update(tc: &mut TrafficControl, y: u32) -> bool { if let Some(device) = devices[idx as usize].as_mut() { return match device { VirtIoDevice::GPUDevice(device) => { - device.transfer(tc, y); + device.transfer(tc, x, y, w, h); true } _ => { diff --git a/src/memory.rs b/src/memory.rs index 4c6a1e3..8a29588 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -121,7 +121,7 @@ impl MemoryManager { } pub fn free_n_blocks(&mut self, addr: usize, n: usize) { - if n == 0 || addr >= self.heap_start + self.heap_size || addr + n * BLOCK_SIZE > self.heap_start + self.heap_size { + if n == 0 || addr < self.heap_start || addr >= self.heap_start + self.heap_size || addr + n * BLOCK_SIZE > self.heap_start + self.heap_size { rough_panic(['b', 'm', 'f']) } for i in 0..n { diff --git a/src/trafficcontrol.rs b/src/trafficcontrol.rs index 0a3a8a7..c38c386 100644 --- a/src/trafficcontrol.rs +++ b/src/trafficcontrol.rs @@ -243,7 +243,7 @@ pub fn handle_syscall( } } crate::dev::probe_devices(&mut tc); - if crate::dev::LINEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { + if crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { tc.use_fb_console = true; { let uart = serial_port(); @@ -351,7 +351,7 @@ pub fn handle_syscall( 0 } SysCall::EnableFramebufferConsole => { - if crate::dev::LINEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { + if crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) != 0 { tc.use_fb_console = true; // clear the screen let mut fbcons = crate::dev::framebuffer::console::FBCONSOLE.lock(); @@ -361,14 +361,14 @@ pub fn handle_syscall( 0 } SysCall::FramebufferPointer => { - crate::dev::LINEBUFFER_ADDR.load(Ordering::Relaxed) + crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) } SysCall::FlushFramebufferRect => { - let x = a1; - let y = a2; - let w = a3; - let h = a4; - crate::dev::linebuffer_push(&mut tc, y as u32); + let x = a1 as u32; + let y = a2 as u32; + let w = a3 as u32; + let h = a4 as u32; + crate::dev::framebuffer_push(&mut tc, x, y, w, h); 0 } }, From 776b7d131d632a50dab033587a6bb4def646343f Mon Sep 17 00:00:00 2001 From: husky Date: Thu, 11 Sep 2025 16:37:15 -0700 Subject: [PATCH 10/20] good enough --- src/dev/framebuffer/console.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/dev/framebuffer/console.rs b/src/dev/framebuffer/console.rs index d9d0120..5cc7ba2 100644 --- a/src/dev/framebuffer/console.rs +++ b/src/dev/framebuffer/console.rs @@ -4,18 +4,18 @@ use crate::trafficcontrol::TrafficControl; pub struct FramebufferConsole { pub buffer: [[char; 20]; 15], // our font is 16x16, 320x240 is the standard res, so 20x15 - pub cursor: (usize, usize), // (x, y) + pub cursor: (usize, usize), // (x, y) + pub need_screen_clear: bool, } -pub static FBCONSOLE: Spinlock = Spinlock::new( - FramebufferConsole::empty() -); +pub static FBCONSOLE: Spinlock = Spinlock::new(FramebufferConsole::empty()); impl FramebufferConsole { pub const fn empty() -> Self { Self { buffer: [[' '; 20]; 15], cursor: (0, 0), + need_screen_clear: true, } } @@ -26,10 +26,12 @@ impl FramebufferConsole { self.buffer[line_num] = copy_from; } self.buffer[self.buffer.len() - 1] = [' '; 20]; + self.need_screen_clear = true; } // DOES send a framebuffer update! pub fn clear_terminal(&mut self, tc: &mut TrafficControl) { + self.need_screen_clear = false; self.buffer = [[' '; 20]; 15]; self.cursor = (0, 0); self.render(tc); @@ -52,6 +54,7 @@ impl FramebufferConsole { if c == '\x08' { was_special_char = true; // we don't clear the character, that's up to the terminal + self.need_screen_clear = true; if self.cursor.0 > 0 { self.cursor.0 -= 1; } else { @@ -79,11 +82,14 @@ impl FramebufferConsole { self.render(tc); } - pub fn render(&self, tc: &mut TrafficControl) { - fb_clearscreen(tc); + pub fn render(&mut self, tc: &mut TrafficControl) { + if self.need_screen_clear { + fb_clearscreen(tc); + self.need_screen_clear = false; + } for (y, line) in self.buffer.iter().enumerate() { fb_write_char_array(tc, 0, y * 16, line); } } -} \ No newline at end of file +} From aef7420a9b545e79c1d1f28afcf82bbe3cd3269e Mon Sep 17 00:00:00 2001 From: husky Date: Thu, 11 Sep 2025 16:47:22 -0700 Subject: [PATCH 11/20] turntable works nicer now --- turntable/src/main.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/turntable/src/main.rs b/turntable/src/main.rs index ef457bc..1d7277c 100644 --- a/turntable/src/main.rs +++ b/turntable/src/main.rs @@ -111,14 +111,14 @@ fn lsdir(env: &Environment<'_>) { println("unexpected eod"); break; } - print(" - "); + print("- "); liblbos::syscalls::write_terminal( &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)], ); if record.record_type == liblbos::fs::RecordType::Directory as u8 { - print(" (dir)"); + print("(dir)"); } else { - print(" (file)"); + print("(file)"); } } print("\n"); @@ -301,7 +301,7 @@ extern "C" fn main() { print(VERSION); print("\n"); - println("(c) 2025 Real Microsoft, LLC"); + println("(c) 2025\nReal Microsoft, LLC"); print("> "); @@ -319,20 +319,18 @@ extern "C" fn main() { if *c == 0x7f && cmdbuf_len > 0 { cmdbuf_len -= 1; print("\x08 \x08"); - } else { + } else if *c == b'\r' { + execute(unsafe { + core::str::from_utf8_unchecked(&cmdbuf[0..cmdbuf_len]) + }, + &mut environment); + cmdbuf_len = 0; + print("> "); + break; + } else if !c.is_ascii_control() { cmdbuf[cmdbuf_len] = *c; cmdbuf_len += 1; - if *c == b'\r' { - execute(unsafe { - core::str::from_utf8_unchecked(&cmdbuf[0..cmdbuf_len - 1]) - }, - &mut environment); - cmdbuf_len = 0; - print("> "); - break; - } else { - liblbos::syscalls::write_terminal(&[*c]); - } + liblbos::syscalls::write_terminal(&[*c]); } } } From ce76f5cbfb6889b4004aa3b4893d42151600d58c Mon Sep 17 00:00:00 2001 From: husky Date: Thu, 11 Sep 2025 16:50:08 -0700 Subject: [PATCH 12/20] remove pointless qemu ramfb feature --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bf8ecdd..6b0cff5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,10 +34,9 @@ cc = "1.0" [features] default = [] debug_messages = [] -arch_virt = ["dev_virtio", "dev_qemu_ramfb", "dev_framebuffer", "fs_fat32", "liblbos/arch_riscv32", "ddi/arch_riscv32"] +arch_virt = ["dev_virtio", "dev_framebuffer", "fs_fat32", "liblbos/arch_riscv32", "ddi/arch_riscv32"] arch_ofw = [] arch_ppc32 = ["compiler_builtins", "arch_ofw", "fs_fat32"] dev_virtio = [] dev_framebuffer = [] -dev_qemu_ramfb = [] fs_fat32 = [] \ No newline at end of file From 3889036590b481c385e11fe4fa0d40cd003adc1f Mon Sep 17 00:00:00 2001 From: husky Date: Thu, 11 Sep 2025 17:48:22 -0700 Subject: [PATCH 13/20] initial driver --- qemurun.sh | 2 +- src/arch/virt/asm/linker.ld | 8 +- src/dev/virtio/input.rs | 294 ++++++++++++++++++++++++++++++++++++ src/dev/virtio/mod.rs | 34 ++++- 4 files changed, 334 insertions(+), 4 deletions(-) create mode 100644 src/dev/virtio/input.rs diff --git a/qemurun.sh b/qemurun.sh index c34e967..0a864cc 100755 --- a/qemurun.sh +++ b/qemurun.sh @@ -2,4 +2,4 @@ storage_device="${1}" -qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -display sdl -device virtio-gpu-device -serial stdio -m 10M -device loader,cpu-num=0,file=target/riscv32imac-unknown-none-elf/release/lbos -monitor telnet:127.0.0.1:1235,server,nowait -d guest_errors,unimp \ No newline at end of file +qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -display gtk -device virtio-gpu-device -device virtio-keyboard-device -serial stdio -m 10M -device loader,cpu-num=0,file=target/riscv32imac-unknown-none-elf/release/lbos -monitor telnet:127.0.0.1:1235,server,nowait -d guest_errors,unimp \ No newline at end of file diff --git a/src/arch/virt/asm/linker.ld b/src/arch/virt/asm/linker.ld index 31029b4..bd14a1d 100644 --- a/src/arch/virt/asm/linker.ld +++ b/src/arch/virt/asm/linker.ld @@ -60,9 +60,13 @@ SECTIONS { PROVIDE(_heap_size = _MEM_END - _heap_start); PROVIDE(_virtio_queue_1_start = ORIGIN(virtqueues)); - PROVIDE(_virtio_queue_1_end = _virtio_queue_1_start + 0x10000); + PROVIDE(_virtio_queue_1_end = _virtio_queue_1_start + 0x2000); PROVIDE(_virtio_queue_2_start = _virtio_queue_1_end); - PROVIDE(_virtio_queue_2_end = _virtio_queue_2_start + 0x10000); + PROVIDE(_virtio_queue_2_end = _virtio_queue_2_start + 0x2000); + PROVIDE(_virtio_queue_3_start = _virtio_queue_2_end); + PROVIDE(_virtio_queue_3_end = _virtio_queue_3_start + 0x2000); + PROVIDE(_virtio_queue_4_start = _virtio_queue_3_end); + PROVIDE(_virtio_queue_4_end = _virtio_queue_4_start + 0x2000); PROVIDE(_framebuffer_start = ORIGIN(framebuffer)); } \ No newline at end of file diff --git a/src/dev/virtio/input.rs b/src/dev/virtio/input.rs new file mode 100644 index 0000000..fe06bc0 --- /dev/null +++ b/src/dev/virtio/input.rs @@ -0,0 +1,294 @@ +use crate::dev::virtio::{Descriptor, VirtQueue, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE}; +use crate::trafficcontrol::TrafficControl; + +unsafe extern "C" { + fn _virtio_queue_3_start(); + fn _virtio_queue_4_start(); +} + +pub const VIRTIO_INPUT_EVENT_TYPE_KEYBOARD: u16 = 1; + +#[repr(C)] +pub struct Event { + pub event_type: u16, + pub code: u16, + pub value: u32, +} + +pub const VIRTIO_INPUT_EVENTBUFFER_SIZE: usize = 8; + +pub struct VirtIoInputDevice { + pub addr: usize, + pub queue: usize, + pub idx: u16, + pub ack_used_idx: u16, + pub status_queue: usize, + pub status_queue_idx: u16, + pub status_queue_ack_used_idx: u16, + pub event_buffer: usize, +} + +pub enum VirtIoInputDeviceError { + FeatureSetMismatch, + QueueSetupFailed, +} + +impl VirtIoInputDevice { + pub fn new_and_init( + tc: &mut TrafficControl, + addr: usize, + ) -> Result { + // reset device (write 0 to status) + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(0); + } + // set ack bit + let mut status = VIRTIO_MMIO_STATUS_ACKNOWLEDGE; + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); + } + // set driver bit + status |= VIRTIO_MMIO_STATUS_DRIVER; + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); + } + // send feature set + let guest_features = 0; // todo: configure properly + unsafe { + ((addr + VIRTIO_MMIO_GUEST_FEATURES) as *mut u32).write_volatile(guest_features); + } + status |= VIRTIO_MMIO_STATUS_FEATURES_OK; + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); + } + // make sure features ok is still set, otherwise failed + if unsafe { ((addr + VIRTIO_MMIO_STATUS) as *const u32).read_volatile() } + & VIRTIO_MMIO_STATUS_FEATURES_OK + == 0 + { + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(VIRTIO_MMIO_STATUS_FAILED); + } + return Err(VirtIoInputDeviceError::FeatureSetMismatch); + } + + // setup event queue + let queue_max_by_device = + unsafe { ((addr + VIRTIO_MMIO_QUEUE_NUM_MAX) as *const u32).read_volatile() }; + if queue_max_by_device < VIRTIO_QUEUE_SIZE as _ { + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(VIRTIO_MMIO_STATUS_FAILED); + } + return Err(VirtIoInputDeviceError::QueueSetupFailed); + } + unsafe { + ((addr + VIRTIO_MMIO_QUEUE_NUM) as *mut u32).write_volatile(VIRTIO_QUEUE_SIZE as _); + } + unsafe { + ((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0); + } + let event_queue_ptr = _virtio_queue_3_start as usize; + // zero out queue + for i in 0..size_of::() { + unsafe { + ((event_queue_ptr + i) as *mut u8).write_volatile(0); + } + } + unsafe { + ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile( + 4096 + ); + } + unsafe { + ((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(event_queue_ptr as u32 / 4096); + } + + // setup status queue + unsafe { + ((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(1); + } + let status_queue_ptr = _virtio_queue_4_start as usize; + // zero out queue + for i in 0..size_of::() { + unsafe { + ((status_queue_ptr + i) as *mut u8).write_volatile(0); + } + } + unsafe { + ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile( + 4096 + ); + } + unsafe { + ((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(status_queue_ptr as u32 / 4096); + } + + // all done! + status |= VIRTIO_MMIO_STATUS_DRIVER_OK; + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); + } + + let event_buffer_ptr = unsafe { + tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks((size_of::() * VIRTIO_INPUT_EVENTBUFFER_SIZE).div_ceil(512)) + }; + + let mut input = VirtIoInputDevice { + addr, + queue: event_queue_ptr, + idx: 0, + ack_used_idx: 0, + status_queue: status_queue_ptr, + status_queue_idx: 0, + status_queue_ack_used_idx: 0, + event_buffer: event_buffer_ptr as usize, + }; + for i in 0..VIRTIO_INPUT_EVENTBUFFER_SIZE { + input.fill_event(i); + } + + + Ok(input) + } + + fn fill_event(&mut self, eventi: usize) { + let desc = Descriptor { + addr: self.event_buffer as u64 + (eventi * size_of::()) as u64, + len: size_of::() as u32, + flags: VIRTIO_DESC_F_WRITE, + next: 0, + }; + let head = self.idx; + let queue = unsafe { &mut (*(self.queue as *mut VirtQueue)) }; + queue.desc[self.idx as usize] = desc; + self.idx = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16; + queue.avail.ring[queue.avail.idx as usize % VIRTIO_QUEUE_SIZE] = head; + queue.avail.idx = queue.avail.idx.wrapping_add(1); + } + + pub fn pending(&mut self, tc: &mut TrafficControl) { + // event queue + let queue = unsafe { &(*(self.queue as *mut VirtQueue)) }; + while self.ack_used_idx != queue.used.idx { + let elem = &queue.used.ring[self.ack_used_idx as usize % VIRTIO_QUEUE_SIZE]; + self.ack_used_idx = self.ack_used_idx.wrapping_add(1); + let desc = &queue.desc[elem.id as usize]; + let event = unsafe { &*(desc.addr as *const Event) }; + self.fill_event(elem.id as usize); + + match event.event_type { + x if x == VIRTIO_INPUT_EVENT_TYPE_KEYBOARD => { + // todo: handle key events + { + let uart = crate::arch::serial_port(); + if let Some(uart) = uart { + uart.putstr("keybd "); + uart.put_bytes(&crate::strprint::u32_hex(event.code as u32)); + uart.putstr("\n"); + } + } + } + _ => { + } + } + } + + // status queue + let queue = unsafe { &(*(self.status_queue as *mut VirtQueue)) }; + while self.status_queue_ack_used_idx != queue.used.idx { + let elem = &queue.used.ring[self.status_queue_ack_used_idx as usize % VIRTIO_QUEUE_SIZE]; + let desc = &queue.desc[elem.id as usize]; + let event = unsafe { &*(desc.addr as *const Event) }; + self.status_queue_ack_used_idx = self.status_queue_ack_used_idx.wrapping_add(1); + } + } +} + + +// linux keycodes +#[repr(u32)] +pub enum LinuxKeycode { + ESC = 1, + Key1 = 2, + Key2 = 3, + Key3 = 4, + Key4 = 5, + Key5 = 6, + Key6 = 7, + Key7 = 8, + Key8 = 9, + Key9 = 10, + Key0 = 11, + Minus = 12, + Equal = 13, + Backspace = 14, + Tab = 15, + Q = 16, + W = 17, + E = 18, + R = 19, + T = 20, + Y = 21, + U = 22, + I = 23, + O = 24, + P = 25, + LeftBracket = 26, + RightBracket = 27, + Enter = 28, + LeftControl = 29, + A = 30, + S = 31, + D = 32, + F = 33, + G = 34, + H = 35, + J = 36, + K = 37, + L = 38, + Semicolon = 39, + Apostrophe = 40, + Grave = 41, + LeftShift = 42, + Backslash = 43, + Z = 44, + X = 45, + C = 46, + V = 47, + B = 48, + N = 49, + M = 50, + Comma = 51, + Dot = 52, + Slash = 53, + RightShift = 54, + KPAsterisk = 55, + LeftAlt = 56, + Space = 57, + CapsLock = 58, + F1 = 59, + F2 = 60, + F3 = 61, + F4 = 62, + F5 = 63, + F6 = 64, + F7 = 65, + F8 = 66, + F9 = 67, + F10 = 68, + NumLock = 69, + ScrollLock = 70, + KP7 = 71, + KP8 = 72, + KP9 = 73, + KPMinus = 74, + KP4 = 75, + KP5 = 76, + KP6 = 77, + KPPlus = 78, + KP1 = 79, + KP2 = 80, + KP3 = 81, + KP0 = 82, + KPDot = 83, +} \ No newline at end of file diff --git a/src/dev/virtio/mod.rs b/src/dev/virtio/mod.rs index a838e87..515322d 100644 --- a/src/dev/virtio/mod.rs +++ b/src/dev/virtio/mod.rs @@ -5,6 +5,7 @@ use core::sync::atomic::Ordering; use crate::dev::{FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP}; use crate::dev::virtio::block::{VirtIoBlockDevice, VirtIoBlockDeviceError}; use crate::dev::virtio::gpu::{VirtIoGpuDevice, VirtIoGpuDeviceError}; +use crate::dev::virtio::input::{VirtIoInputDevice, VirtIoInputDeviceError}; use crate::rough_panic; use crate::spinlock::Spinlock; use crate::strprint::twodigit; @@ -12,6 +13,7 @@ use crate::trafficcontrol::{TrafficControl}; mod block; mod gpu; +mod input; pub const VIRTIO_MMIO_START: usize = 0x1000_1000; pub const VIRTIO_MMIO_END: usize = 0x1000_8000; @@ -46,6 +48,7 @@ pub static VIRTIO_GPU_DEVICE: Spinlock> = Spinlock::new(None); pub enum VirtIoDevice { BlockDevice(VirtIoBlockDevice), GPUDevice(VirtIoGpuDevice), + InputDevice(VirtIoInputDevice), } #[repr(C)] @@ -159,6 +162,29 @@ pub fn probe_virtio_devices(tc: &mut TrafficControl) { } } } + 18 => { + // input device + let input_device = VirtIoInputDevice::new_and_init(tc, addr); + if let Ok(input_device) = input_device { + devices[i] = Some(VirtIoDevice::InputDevice(input_device)); + if let Some(serial_port) = &serial_port { + serial_port.putstr("virtio input device found\n"); + } + } else if let Err(e) = input_device { + match e { + VirtIoInputDeviceError::FeatureSetMismatch => { + if let Some(serial_port) = &serial_port { + serial_port.putstr("virtio input device feature mismatch\n"); + } + } + VirtIoInputDeviceError::QueueSetupFailed => { + if let Some(serial_port) = &serial_port { + serial_port.putstr("virtio input device queue setup failed\n"); + } + } + } + } + } x => { if let Some(serial_port) = &serial_port { serial_port.putstr("unsupported device type "); @@ -187,7 +213,6 @@ pub fn handle_interrupt(interrupt: u32, tc: &mut TrafficControl) { } } VirtIoDevice::GPUDevice(gpudev) => { - return; let gpu = { let lock = VIRTIO_GPU_DEVICE.lock(); *lock @@ -198,6 +223,13 @@ pub fn handle_interrupt(interrupt: u32, tc: &mut TrafficControl) { } } } + VirtIoDevice::InputDevice(inputdev) => { + { + let uart = crate::uart::UART::new(0x1000_0000); + uart.putstr("input device interrupt\n"); + } + inputdev.pending(tc); + } } } } From a65ba1e487c9da942f46c1565710b3105d34ad53 Mon Sep 17 00:00:00 2001 From: husky Date: Thu, 11 Sep 2025 18:06:24 -0700 Subject: [PATCH 14/20] keyboards are working --- src/dev/virtio/input.rs | 134 +++++++++++++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 24 deletions(-) diff --git a/src/dev/virtio/input.rs b/src/dev/virtio/input.rs index fe06bc0..afdea47 100644 --- a/src/dev/virtio/input.rs +++ b/src/dev/virtio/input.rs @@ -1,4 +1,11 @@ -use crate::dev::virtio::{Descriptor, VirtQueue, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE}; +use core::sync::atomic::{AtomicBool, Ordering}; +use crate::dev::virtio::{ + Descriptor, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, + VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, + VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, + VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, + VIRTIO_QUEUE_SIZE, VirtQueue, +}; use crate::trafficcontrol::TrafficControl; unsafe extern "C" { @@ -74,7 +81,7 @@ impl VirtIoInputDevice { // setup event queue let queue_max_by_device = - unsafe { ((addr + VIRTIO_MMIO_QUEUE_NUM_MAX) as *const u32).read_volatile() }; + unsafe { ((addr + VIRTIO_MMIO_QUEUE_NUM_MAX) as *const u32).read_volatile() }; if queue_max_by_device < VIRTIO_QUEUE_SIZE as _ { unsafe { ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(VIRTIO_MMIO_STATUS_FAILED); @@ -95,12 +102,11 @@ impl VirtIoInputDevice { } } unsafe { - ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile( - 4096 - ); + ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096); } unsafe { - ((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(event_queue_ptr as u32 / 4096); + ((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32) + .write_volatile(event_queue_ptr as u32 / 4096); } // setup status queue @@ -115,12 +121,11 @@ impl VirtIoInputDevice { } } unsafe { - ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile( - 4096 - ); + ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096); } unsafe { - ((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(status_queue_ptr as u32 / 4096); + ((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32) + .write_volatile(status_queue_ptr as u32 / 4096); } // all done! @@ -130,7 +135,10 @@ impl VirtIoInputDevice { } let event_buffer_ptr = unsafe { - tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks((size_of::() * VIRTIO_INPUT_EVENTBUFFER_SIZE).div_ceil(512)) + tc.memory_manager + .as_mut() + .unwrap_unchecked() + .alloc_n_blocks((size_of::() * VIRTIO_INPUT_EVENTBUFFER_SIZE).div_ceil(512)) }; let mut input = VirtIoInputDevice { @@ -147,7 +155,6 @@ impl VirtIoInputDevice { input.fill_event(i); } - Ok(input) } @@ -178,25 +185,44 @@ impl VirtIoInputDevice { match event.event_type { x if x == VIRTIO_INPUT_EVENT_TYPE_KEYBOARD => { - // todo: handle key events - { - let uart = crate::arch::serial_port(); - if let Some(uart) = uart { - uart.putstr("keybd "); - uart.put_bytes(&crate::strprint::u32_hex(event.code as u32)); - uart.putstr("\n"); + let keycode = event.code; + let down = event.value == 1; + + // first, handle shift todo: handle more control characters + if keycode == LinuxKeycode::LeftShift as u16 || keycode == LinuxKeycode::RightShift as u16 { + UPPERCASE.store(down, Ordering::Relaxed); + } + + // next, handle ascii characters + if !down { // write on UP + let ascii = KEYMAP_ASCII.iter().find_map(|(a, b)| { + if keycode == *a as u16 { + Some(*b) + } else { + None + } + }); + if let Some(mut ascii) = ascii { + if UPPERCASE.load(Ordering::Relaxed) { + if ascii.is_ascii_alphabetic() { + ascii = ascii.to_ascii_uppercase(); + } else { + // todo: handle other characters + } + } + tc.write_inbuf(ascii); } } } - _ => { - } + _ => {} } } // status queue let queue = unsafe { &(*(self.status_queue as *mut VirtQueue)) }; while self.status_queue_ack_used_idx != queue.used.idx { - let elem = &queue.used.ring[self.status_queue_ack_used_idx as usize % VIRTIO_QUEUE_SIZE]; + let elem = + &queue.used.ring[self.status_queue_ack_used_idx as usize % VIRTIO_QUEUE_SIZE]; let desc = &queue.desc[elem.id as usize]; let event = unsafe { &*(desc.addr as *const Event) }; self.status_queue_ack_used_idx = self.status_queue_ack_used_idx.wrapping_add(1); @@ -204,9 +230,11 @@ impl VirtIoInputDevice { } } +static UPPERCASE: AtomicBool = AtomicBool::new(false); // linux keycodes -#[repr(u32)] +#[repr(u16)] +#[derive(Copy, Clone)] pub enum LinuxKeycode { ESC = 1, Key1 = 2, @@ -291,4 +319,62 @@ pub enum LinuxKeycode { KP3 = 81, KP0 = 82, KPDot = 83, -} \ No newline at end of file +} + +// keys that do not map directly are handled outside of this +const KEYMAP_ASCII: &[(LinuxKeycode, u8)] = &[ + (LinuxKeycode::ESC, 0x1B), + (LinuxKeycode::Key1, b'1'), + (LinuxKeycode::Key2, b'2'), + (LinuxKeycode::Key3, b'3'), + (LinuxKeycode::Key4, b'4'), + (LinuxKeycode::Key5, b'5'), + (LinuxKeycode::Key6, b'6'), + (LinuxKeycode::Key7, b'7'), + (LinuxKeycode::Key8, b'8'), + (LinuxKeycode::Key9, b'9'), + (LinuxKeycode::Key0, b'0'), + (LinuxKeycode::Minus, b'-'), + (LinuxKeycode::Equal, b'='), + (LinuxKeycode::Backspace, 0x7F), + (LinuxKeycode::Tab, 0x9), + (LinuxKeycode::Q, b'q'), + (LinuxKeycode::W, b'w'), + (LinuxKeycode::E, b'e'), + (LinuxKeycode::R, b'r'), + (LinuxKeycode::T, b't'), + (LinuxKeycode::Y, b'y'), + (LinuxKeycode::U, b'u'), + (LinuxKeycode::I, b'i'), + (LinuxKeycode::O, b'o'), + (LinuxKeycode::P, b'p'), + (LinuxKeycode::LeftBracket, b'['), + (LinuxKeycode::RightBracket, b']'), + (LinuxKeycode::Enter, b'\r'), + (LinuxKeycode::A, b'a'), + (LinuxKeycode::S, b's'), + (LinuxKeycode::D, b'd'), + (LinuxKeycode::F, b'f'), + (LinuxKeycode::G, b'g'), + (LinuxKeycode::H, b'h'), + (LinuxKeycode::J, b'j'), + (LinuxKeycode::K, b'k'), + (LinuxKeycode::L, b'l'), + (LinuxKeycode::Semicolon, b';'), + (LinuxKeycode::Apostrophe, b'\''), + (LinuxKeycode::Grave, b'`'), + (LinuxKeycode::LeftShift, b'/'), + (LinuxKeycode::Backslash, b'\\'), + (LinuxKeycode::Z, b'z'), + (LinuxKeycode::X, b'x'), + (LinuxKeycode::C, b'c'), + (LinuxKeycode::V, b'v'), + (LinuxKeycode::B, b'b'), + (LinuxKeycode::N, b'n'), + (LinuxKeycode::M, b'm'), + (LinuxKeycode::Comma, b','), + (LinuxKeycode::Dot, b'.'), + (LinuxKeycode::Slash, b'/'), + (LinuxKeycode::KPAsterisk, b'*'), + (LinuxKeycode::Space, b' '), +]; From 4d9c24c9b40b3eb22dde2a30e62b481bc11c479b Mon Sep 17 00:00:00 2001 From: husky Date: Thu, 11 Sep 2025 18:20:27 -0700 Subject: [PATCH 15/20] remove some of those old print statements --- qemurun.sh | 2 +- src/dev/virtio/input.rs | 1 - src/dev/virtio/mod.rs | 4 ---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/qemurun.sh b/qemurun.sh index 0a864cc..11fd054 100755 --- a/qemurun.sh +++ b/qemurun.sh @@ -2,4 +2,4 @@ storage_device="${1}" -qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -display gtk -device virtio-gpu-device -device virtio-keyboard-device -serial stdio -m 10M -device loader,cpu-num=0,file=target/riscv32imac-unknown-none-elf/release/lbos -monitor telnet:127.0.0.1:1235,server,nowait -d guest_errors,unimp \ No newline at end of file +qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -display sdl -device virtio-gpu-device -device virtio-keyboard-device -serial stdio -m 10M -device loader,cpu-num=0,file=target/riscv32imac-unknown-none-elf/release/lbos -monitor telnet:127.0.0.1:1235,server,nowait -d guest_errors,unimp \ No newline at end of file diff --git a/src/dev/virtio/input.rs b/src/dev/virtio/input.rs index afdea47..1036262 100644 --- a/src/dev/virtio/input.rs +++ b/src/dev/virtio/input.rs @@ -363,7 +363,6 @@ const KEYMAP_ASCII: &[(LinuxKeycode, u8)] = &[ (LinuxKeycode::Semicolon, b';'), (LinuxKeycode::Apostrophe, b'\''), (LinuxKeycode::Grave, b'`'), - (LinuxKeycode::LeftShift, b'/'), (LinuxKeycode::Backslash, b'\\'), (LinuxKeycode::Z, b'z'), (LinuxKeycode::X, b'x'), diff --git a/src/dev/virtio/mod.rs b/src/dev/virtio/mod.rs index 515322d..2ebf89a 100644 --- a/src/dev/virtio/mod.rs +++ b/src/dev/virtio/mod.rs @@ -224,10 +224,6 @@ pub fn handle_interrupt(interrupt: u32, tc: &mut TrafficControl) { } } VirtIoDevice::InputDevice(inputdev) => { - { - let uart = crate::uart::UART::new(0x1000_0000); - uart.putstr("input device interrupt\n"); - } inputdev.pending(tc); } } From c64f8d4de241ca91ffa896a5e0fc8c7b353fc2ab Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 12 Sep 2025 13:08:57 -0700 Subject: [PATCH 16/20] dvorak support for freaks --- Cargo.toml | 4 ++- src/dev/mod.rs | 3 --- src/dev/virtio/gpu.rs | 2 +- src/dev/virtio/input.rs | 58 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6b0cff5..46bd48f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,11 +32,13 @@ codegen-units = 1 cc = "1.0" [features] -default = [] +default = ["keyboard_en_us"] debug_messages = [] arch_virt = ["dev_virtio", "dev_framebuffer", "fs_fat32", "liblbos/arch_riscv32", "ddi/arch_riscv32"] arch_ofw = [] arch_ppc32 = ["compiler_builtins", "arch_ofw", "fs_fat32"] dev_virtio = [] dev_framebuffer = [] +keyboard_en_us = [] +keyboard_dvorak = [] fs_fat32 = [] \ No newline at end of file diff --git a/src/dev/mod.rs b/src/dev/mod.rs index 1f84f0d..cab2a03 100644 --- a/src/dev/mod.rs +++ b/src/dev/mod.rs @@ -4,9 +4,6 @@ use crate::trafficcontrol::TrafficControl; pub const FRAMEBUFFER_WIDTH: usize = 320; pub const FRAMEBUFFER_HEIGHT: usize = 240; -pub const LINEBUFFER_BLOCKS: usize = 3; -pub const LINEBUFFER_HEIGHT: usize = 1; - // NOTE: // FRAMEBUFFER_ADDR should always be 0 if no framebuffer exists // if FRAMEBUFFER_ADDR is NOT 0, then FRAMEBUFFER_BPP should also be NOT 0 diff --git a/src/dev/virtio/gpu.rs b/src/dev/virtio/gpu.rs index 80f41cc..bb3360d 100644 --- a/src/dev/virtio/gpu.rs +++ b/src/dev/virtio/gpu.rs @@ -1,5 +1,5 @@ use crate::arch::serial_port; -use crate::dev::{FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH, LINEBUFFER_BLOCKS}; +use crate::dev::{FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH}; use crate::dev::virtio::{heap_allocate_type, Descriptor, VirtQueue, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE}; use crate::strprint::u32_hex; use crate::trafficcontrol::TrafficControl; diff --git a/src/dev/virtio/input.rs b/src/dev/virtio/input.rs index 1036262..7b788f1 100644 --- a/src/dev/virtio/input.rs +++ b/src/dev/virtio/input.rs @@ -322,6 +322,7 @@ pub enum LinuxKeycode { } // keys that do not map directly are handled outside of this +#[cfg(feature = "keyboard_en_us")] const KEYMAP_ASCII: &[(LinuxKeycode, u8)] = &[ (LinuxKeycode::ESC, 0x1B), (LinuxKeycode::Key1, b'1'), @@ -377,3 +378,60 @@ const KEYMAP_ASCII: &[(LinuxKeycode, u8)] = &[ (LinuxKeycode::KPAsterisk, b'*'), (LinuxKeycode::Space, b' '), ]; + +#[cfg(feature = "keyboard_dvorak")] +const KEYMAP_ASCII: &[(LinuxKeycode, u8)] = &[ + (LinuxKeycode::ESC, 0x1B), + (LinuxKeycode::Key1, b'1'), + (LinuxKeycode::Key2, b'2'), + (LinuxKeycode::Key3, b'3'), + (LinuxKeycode::Key4, b'4'), + (LinuxKeycode::Key5, b'5'), + (LinuxKeycode::Key6, b'6'), + (LinuxKeycode::Key7, b'7'), + (LinuxKeycode::Key8, b'8'), + (LinuxKeycode::Key9, b'9'), + (LinuxKeycode::Key0, b'0'), + (LinuxKeycode::Minus, b'-'), + (LinuxKeycode::Equal, b'='), + (LinuxKeycode::Backspace, 0x7F), + (LinuxKeycode::Tab, 0x9), + (LinuxKeycode::Q, b'\''), + (LinuxKeycode::W, b','), + (LinuxKeycode::E, b'.'), + (LinuxKeycode::R, b'p'), + (LinuxKeycode::T, b'y'), + (LinuxKeycode::Y, b'f'), + (LinuxKeycode::U, b'g'), + (LinuxKeycode::I, b'c'), + (LinuxKeycode::O, b'r'), + (LinuxKeycode::P, b'l'), + (LinuxKeycode::LeftBracket, b'/'), + (LinuxKeycode::RightBracket, b'='), + (LinuxKeycode::Enter, b'\r'), + (LinuxKeycode::A, b'a'), + (LinuxKeycode::S, b'o'), + (LinuxKeycode::D, b'e'), + (LinuxKeycode::F, b'u'), + (LinuxKeycode::G, b'i'), + (LinuxKeycode::H, b'd'), + (LinuxKeycode::J, b'h'), + (LinuxKeycode::K, b't'), + (LinuxKeycode::L, b'n'), + (LinuxKeycode::Semicolon, b's'), + (LinuxKeycode::Apostrophe, b'-'), + (LinuxKeycode::Grave, b'`'), + (LinuxKeycode::Backslash, b'\\'), + (LinuxKeycode::Z, b';'), + (LinuxKeycode::X, b'q'), + (LinuxKeycode::C, b'j'), + (LinuxKeycode::V, b'k'), + (LinuxKeycode::B, b'x'), + (LinuxKeycode::N, b'b'), + (LinuxKeycode::M, b'm'), + (LinuxKeycode::Comma, b'w'), + (LinuxKeycode::Dot, b'v'), + (LinuxKeycode::Slash, b'z'), + (LinuxKeycode::KPAsterisk, b'*'), + (LinuxKeycode::Space, b' '), +]; From d7b75cf4627dcfe8b0e7daf6154c75fffd572fbc Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 12 Sep 2025 13:22:15 -0700 Subject: [PATCH 17/20] improve screen flickering on fbcons --- src/dev/framebuffer/console.rs | 20 +++++++++++++------- src/dev/framebuffer/mod.rs | 32 ++++++++++++++++++++++++++++++-- src/trafficcontrol.rs | 2 ++ 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/dev/framebuffer/console.rs b/src/dev/framebuffer/console.rs index 5cc7ba2..77cc606 100644 --- a/src/dev/framebuffer/console.rs +++ b/src/dev/framebuffer/console.rs @@ -1,4 +1,4 @@ -use crate::dev::framebuffer::{fb_clearscreen, fb_write_char_array}; +use crate::dev::framebuffer::{fb_clear_area, fb_clearscreen, fb_write_char_array}; use crate::spinlock::Spinlock; use crate::trafficcontrol::TrafficControl; @@ -6,6 +6,7 @@ pub struct FramebufferConsole { pub buffer: [[char; 20]; 15], // our font is 16x16, 320x240 is the standard res, so 20x15 pub cursor: (usize, usize), // (x, y) pub need_screen_clear: bool, + pub need_line_clear: bool, } pub static FBCONSOLE: Spinlock = Spinlock::new(FramebufferConsole::empty()); @@ -16,6 +17,7 @@ impl FramebufferConsole { buffer: [[' '; 20]; 15], cursor: (0, 0), need_screen_clear: true, + need_line_clear: false, } } @@ -31,10 +33,10 @@ impl FramebufferConsole { // DOES send a framebuffer update! pub fn clear_terminal(&mut self, tc: &mut TrafficControl) { - self.need_screen_clear = false; + self.need_screen_clear = true; self.buffer = [[' '; 20]; 15]; self.cursor = (0, 0); - self.render(tc); + self.render(tc, true); } // DOES send a framebuffer update! @@ -54,7 +56,7 @@ impl FramebufferConsole { if c == '\x08' { was_special_char = true; // we don't clear the character, that's up to the terminal - self.need_screen_clear = true; + self.need_line_clear = true; if self.cursor.0 > 0 { self.cursor.0 -= 1; } else { @@ -79,14 +81,18 @@ impl FramebufferConsole { } } - self.render(tc); + self.render(tc, false); } - pub fn render(&mut self, tc: &mut TrafficControl) { + pub fn render(&mut self, tc: &mut TrafficControl, refresh_whole_screen: bool) { if self.need_screen_clear { - fb_clearscreen(tc); + fb_clearscreen(tc, refresh_whole_screen); self.need_screen_clear = false; } + if self.need_line_clear { + fb_clear_area(tc, 0, self.cursor.1 * 16, 320, 16); + self.need_line_clear = false; + } for (y, line) in self.buffer.iter().enumerate() { fb_write_char_array(tc, 0, y * 16, line); diff --git a/src/dev/framebuffer/mod.rs b/src/dev/framebuffer/mod.rs index d651117..576d7db 100644 --- a/src/dev/framebuffer/mod.rs +++ b/src/dev/framebuffer/mod.rs @@ -81,7 +81,7 @@ pub fn fb_write_char_array(tc: &mut TrafficControl, mut x: usize, y: usize, char ); } -pub fn fb_clearscreen(tc: &mut TrafficControl) { +pub fn fb_clearscreen(tc: &mut TrafficControl, send_refresh: bool) { const BYTES: [u8; 3] = FB_BG_COLOR.to_bytes(); let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed); if fbaddr == 0 { @@ -96,6 +96,34 @@ pub fn fb_clearscreen(tc: &mut TrafficControl) { } } } + if send_refresh { + framebuffer_update( + tc, + 0, + 0, + FRAMEBUFFER_WIDTH as u32, + FRAMEBUFFER_HEIGHT as u32, + ); + } +} + +pub fn fb_clear_area(tc: &mut TrafficControl, x: usize, y: usize, w: usize, h: usize) { + const BYTES: [u8; 3] = FB_BG_COLOR.to_bytes(); + let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed); + if fbaddr == 0 { + return; + } + let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH; + for row in 0..h { + for col in 0..w { + unsafe { + unsafe { + let fb = (fbaddr as *mut u8).add(((y+row) * fbstride) + ((x+col) * 4)) as *mut u32; + fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]])); + } + } + } + } framebuffer_update( tc, 0, @@ -103,4 +131,4 @@ pub fn fb_clearscreen(tc: &mut TrafficControl) { FRAMEBUFFER_WIDTH as u32, FRAMEBUFFER_HEIGHT as u32, ); -} +} \ No newline at end of file diff --git a/src/trafficcontrol.rs b/src/trafficcontrol.rs index c38c386..4f63715 100644 --- a/src/trafficcontrol.rs +++ b/src/trafficcontrol.rs @@ -251,6 +251,8 @@ pub fn handle_syscall( uart.putstr("using framebuffer console\n"); } } + let mut fbcons = crate::dev::framebuffer::console::FBCONSOLE.lock(); + fbcons.clear_terminal(&mut tc); } 0 } From 2c0be2ccd717f24404bffc13709d9697bf20c1ea Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 12 Sep 2025 13:25:57 -0700 Subject: [PATCH 18/20] improvements to previous commit --- src/dev/framebuffer/console.rs | 2 +- src/dev/framebuffer/mod.rs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/dev/framebuffer/console.rs b/src/dev/framebuffer/console.rs index 77cc606..b7903aa 100644 --- a/src/dev/framebuffer/console.rs +++ b/src/dev/framebuffer/console.rs @@ -90,7 +90,7 @@ impl FramebufferConsole { self.need_screen_clear = false; } if self.need_line_clear { - fb_clear_area(tc, 0, self.cursor.1 * 16, 320, 16); + fb_clear_area(tc, 0, self.cursor.1 * 16, 320, 16, refresh_whole_screen); self.need_line_clear = false; } diff --git a/src/dev/framebuffer/mod.rs b/src/dev/framebuffer/mod.rs index 576d7db..1717f24 100644 --- a/src/dev/framebuffer/mod.rs +++ b/src/dev/framebuffer/mod.rs @@ -107,7 +107,7 @@ pub fn fb_clearscreen(tc: &mut TrafficControl, send_refresh: bool) { } } -pub fn fb_clear_area(tc: &mut TrafficControl, x: usize, y: usize, w: usize, h: usize) { +pub fn fb_clear_area(tc: &mut TrafficControl, x: usize, y: usize, w: usize, h: usize, send_refresh: bool) { const BYTES: [u8; 3] = FB_BG_COLOR.to_bytes(); let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed); if fbaddr == 0 { @@ -124,11 +124,13 @@ pub fn fb_clear_area(tc: &mut TrafficControl, x: usize, y: usize, w: usize, h: u } } } - framebuffer_update( - tc, - 0, - 0, - FRAMEBUFFER_WIDTH as u32, - FRAMEBUFFER_HEIGHT as u32, - ); + if send_refresh { + framebuffer_update( + tc, + 0, + 0, + FRAMEBUFFER_WIDTH as u32, + FRAMEBUFFER_HEIGHT as u32, + ); + } } \ No newline at end of file From ee4c5241daea1217053a01ef18ad417ac9408001 Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 12 Sep 2025 13:28:19 -0700 Subject: [PATCH 19/20] increment version numbers --- Cargo.lock | 2 +- Cargo.toml | 2 +- turntable/Cargo.lock | 2 +- turntable/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 610fa20..8dabe4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,7 +29,7 @@ checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" [[package]] name = "lbos" -version = "0.1.0" +version = "0.1.1" dependencies = [ "cc", "compiler_builtins", diff --git a/Cargo.toml b/Cargo.toml index 46bd48f..5987ecc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["turntable", "makeddi", "ddi", "example"] [package] name = "lbos" -version = "0.1.0" +version = "0.1.1" edition = "2024" [dependencies] diff --git a/turntable/Cargo.lock b/turntable/Cargo.lock index c01326d..04e02c2 100644 --- a/turntable/Cargo.lock +++ b/turntable/Cargo.lock @@ -8,7 +8,7 @@ version = "0.1.0" [[package]] name = "turntable" -version = "0.1.0" +version = "0.1.1" dependencies = [ "liblbos", ] diff --git a/turntable/Cargo.toml b/turntable/Cargo.toml index 3f564ba..559c755 100644 --- a/turntable/Cargo.toml +++ b/turntable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "turntable" -version = "0.1.0" +version = "0.1.1" edition = "2024" [dependencies] From ba6f93e25ee850e11155591be6d4e78fd8815315 Mon Sep 17 00:00:00 2001 From: husky Date: Fri, 12 Sep 2025 13:28:52 -0700 Subject: [PATCH 20/20] increment liblbos version number --- Cargo.lock | 2 +- example/Cargo.lock | 2 +- liblbos/Cargo.toml | 2 +- turntable/Cargo.lock | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8dabe4c..777ff66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,7 +39,7 @@ dependencies = [ [[package]] name = "liblbos" -version = "0.1.0" +version = "0.1.1" [[package]] name = "shlex" diff --git a/example/Cargo.lock b/example/Cargo.lock index 479c496..30b2f56 100644 --- a/example/Cargo.lock +++ b/example/Cargo.lock @@ -11,4 +11,4 @@ dependencies = [ [[package]] name = "liblbos" -version = "0.1.0" +version = "0.1.1" diff --git a/liblbos/Cargo.toml b/liblbos/Cargo.toml index 538941d..3767eee 100644 --- a/liblbos/Cargo.toml +++ b/liblbos/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "liblbos" -version = "0.1.0" +version = "0.1.1" edition = "2024" [dependencies] diff --git a/turntable/Cargo.lock b/turntable/Cargo.lock index 04e02c2..7ecd685 100644 --- a/turntable/Cargo.lock +++ b/turntable/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "liblbos" -version = "0.1.0" +version = "0.1.1" [[package]] name = "turntable"