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..6b0cff5 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,9 @@ cc = "1.0" [features] default = [] debug_messages = [] -arch_virt = ["dev_virtio", "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 = [] 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 ea1e4e4..c34e967 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 -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/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 417be2f..31029b4 100644 --- a/src/arch/virt/asm/linker.ld +++ b/src/arch/virt/asm/linker.ld @@ -4,8 +4,13 @@ ENTRY( _start ) MEMORY { - rom (wxa) : ORIGIN = 0x80000000, LENGTH = 64 * 1024 - ram (wxa) : ORIGIN = 0x80010000, LENGTH = 1048510 + rom (wxa) : ORIGIN = 0x80000000, LENGTH = 0x10000 + + ram (wxa) : ORIGIN = 0x80010000, LENGTH = 0x10000 + + virtqueues (wxa) : ORIGIN = 0x80020000, LENGTH = 0x20000 + + framebuffer (wxa) : ORIGIN = 0x80040000, LENGTH = 320 * 240 * 4 } PHDRS @@ -36,7 +41,7 @@ SECTIONS { PROVIDE(_bss_start = .); *(.sbss .sbss.*) *(.bss .bss.*) - . = ALIGN(4096); + . = ALIGN(512); PROVIDE(_bss_end = .); } >ram AT>ram :bss @@ -51,8 +56,13 @@ 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); + + PROVIDE(_virtio_queue_1_start = ORIGIN(virtqueues)); + 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 new file mode 100644 index 0000000..5cc7ba2 --- /dev/null +++ b/src/dev/framebuffer/console.rs @@ -0,0 +1,95 @@ +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 need_screen_clear: bool, +} + +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, + } + } + + // 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]; + 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); + } + + // 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 + self.need_screen_clear = true; + 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(&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); + } + } +} diff --git a/src/dev/framebuffer/mod.rs b/src/dev/framebuffer/mod.rs new file mode 100644 index 0000000..d651117 --- /dev/null +++ b/src/dev/framebuffer/mod.rs @@ -0,0 +1,106 @@ +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; + +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, y: usize, chars: &[char]) { + let ogx = x; + let ogy = y; + const BYTES: [u8; 3] = FB_FG_COLOR.to_bytes(); + 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; + 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; + } + } + + 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 0000000..6d2b292 Binary files /dev/null and b/src/dev/framebuffer/vapfont.data differ diff --git a/src/dev/framebuffer/vapfont.png b/src/dev/framebuffer/vapfont.png new file mode 100644 index 0000000..892116d Binary files /dev/null and b/src/dev/framebuffer/vapfont.png differ diff --git a/src/dev/mod.rs b/src/dev/mod.rs index 7c5d699..1f84f0d 100644 --- a/src/dev/mod.rs +++ b/src/dev/mod.rs @@ -1,7 +1,22 @@ +use core::sync::atomic::{AtomicUsize, Ordering}; 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 +pub static FRAMEBUFFER_ADDR: AtomicUsize = AtomicUsize::new(0); +pub static FRAMEBUFFER_BPP: AtomicUsize = AtomicUsize::new(0); + #[cfg(feature = "dev_virtio")] pub mod virtio; +#[cfg(feature = "dev_framebuffer")] +pub mod framebuffer; pub fn probe_devices(tc: &mut TrafficControl) { #[cfg(feature = "dev_virtio")] @@ -12,4 +27,18 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6 #[cfg(feature = "dev_virtio")] return virtio::read_sector(tc, buffer, size, sector); false +} + +pub fn framebuffer_address() -> usize { + FRAMEBUFFER_ADDR.load(Ordering::Relaxed) +} + +pub fn framebuffer_bpp() -> usize { + FRAMEBUFFER_BPP.load(Ordering::Relaxed) +} + + +pub fn framebuffer_push(tc: &mut TrafficControl, x: u32, y: u32, w: u32, h: u32) { + #[cfg(feature = "dev_virtio")] + virtio::framebuffer_update(tc, x, y, w, h); } \ No newline at end of file diff --git a/src/dev/virtio/block.rs b/src/dev/virtio/block.rs index e9bb612..cdf195a 100644 --- a/src/dev/virtio/block.rs +++ b/src/dev/virtio/block.rs @@ -1,9 +1,18 @@ -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" { - fn _virtio_virtqueue_start(); + fn _virtio_queue_1_start(); } + #[repr(C)] pub struct Status { pub status: u8, @@ -57,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 { @@ -96,8 +105,19 @@ 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); + 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) }; @@ -147,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, @@ -211,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 new file mode 100644 index 0000000..80f41cc --- /dev/null +++ b/src/dev/virtio/gpu.rs @@ -0,0 +1,493 @@ +use crate::arch::serial_port; +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; + +#[repr(C)] +pub struct CtrlHeader { + pub ctrl_type: u32, + pub flags: u32, + pub fence_id: u64, + pub ctx_id: 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_X8R8G8B8: u32 = 4; + +#[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 latest_response: usize, + 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 = 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(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); + 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 framebuffer_ptr = _framebuffer_start as usize; + + 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, + width: FRAMEBUFFER_WIDTH, + height: FRAMEBUFFER_HEIGHT, + }; + status |= VIRTIO_MMIO_STATUS_DRIVER_OK; + unsafe { + ((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status); + } + + let mut free_me = [0; 4]; + + // 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.ring_idx = 0; + cmd.header.padding = [0; 3]; + + cmd.resource_id = 1; + cmd.format = VIRTIO_GPU_FORMAT_X8R8G8B8; + cmd.width = FRAMEBUFFER_WIDTH as u32; + cmd.height = FRAMEBUFFER_HEIGHT as u32; + free_me[0] = unsafe { &(*cmd) as *const _ as usize }; + + 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: 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); + // 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); + 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.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.height * gpu.width * 4) as u32; + mem_entry.padding = 0; + free_me[2] = unsafe { &(*mem_entry) as *const _ as usize }; + + 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: 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); + // 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); + 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.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; + free_me[3] = unsafe { &(*cmd) as *const _ as usize }; + + 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: 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); + // 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 { + unsafe { + tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(free, 1); + } + } + + 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 + 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; + } + unsafe { + (*(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 + unsafe { + (*(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; + } + unsafe { + (*(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); + } + } + + // 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)) }; + while self.ack_used_idx != queue.used.idx { + self.ack_used_idx = self.ack_used_idx.wrapping_add(1); + } + } + + 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; + cmd.header.flags = 0; + cmd.header.fence_id = 0; + cmd.header.ctx_id = 0; + cmd.header.ring_idx = 0; + cmd.header.padding = [0; 3]; + cmd.rect.x = x; + cmd.rect.y = y; + cmd.rect.width = w; + cmd.rect.height = h; + cmd.offset = 0; + cmd.resource_id = 1; + cmd.padding = 0; + free_me[0] = unsafe { &(*cmd) as *const _ as usize }; + + 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: 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); + 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.ring_idx = 0; + cmd.header.padding = [0; 3]; + cmd.rect.x = x; + cmd.rect.y = y; + cmd.rect.width = w; + cmd.rect.height = h; + cmd.resource_id = 1; + cmd.padding = 0; + free_me[1] = unsafe { &(*cmd) as *const _ as usize }; + + 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: 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); + + // 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); + } + } + } +} \ No newline at end of file diff --git a/src/dev/virtio/mod.rs b/src/dev/virtio/mod.rs index a6ee5d7..a838e87 100644 --- a/src/dev/virtio/mod.rs +++ b/src/dev/virtio/mod.rs @@ -1,12 +1,17 @@ //! 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::rough_panic; 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; @@ -32,13 +37,15 @@ 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 = 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); +pub static VIRTIO_GPU_DEVICE: Spinlock> = Spinlock::new(None); pub enum VirtIoDevice { BlockDevice(VirtIoBlockDevice), + GPUDevice(VirtIoGpuDevice), } #[repr(C)] @@ -75,6 +82,15 @@ 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); + 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)) } +} + pub fn probe_virtio_devices(tc: &mut TrafficControl) { let serial_port = crate::arch::serial_port(); let mut devices = VIRTIO_DEVICES.lock(); @@ -117,6 +133,32 @@ 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 { + 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 { + 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,18 @@ pub fn handle_interrupt(interrupt: u32, tc: &mut TrafficControl) { } } } + 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); + } + } + } } } } @@ -156,10 +210,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"); + } + 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, w: u32, h: 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, w, h); + 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/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/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..8a29588 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")] @@ -8,25 +10,32 @@ 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 { #[cfg(feature = "arch_virt")] pub fn init() -> Self { + { + 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(_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")] @@ -38,14 +47,6 @@ impl MemoryManager { } } - 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() { @@ -64,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; @@ -78,46 +87,57 @@ 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; } } } - - 0 + + 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 || 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 5b30512..c38c386 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,6 +14,8 @@ pub enum TaskWait { WaitForTaskExit = 1 << 2, } +pub const STACK_SIZE_IN_BLOCKS: usize = 8; + pub const MAX_TASKS: usize = 8; pub static TC: Spinlock = Spinlock::new(TrafficControl::empty()); @@ -67,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(16) - }; let sp = unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() - .block_to_addr(blockalloc) - + BLOCK_SIZE - }; + .alloc_n_blocks(STACK_SIZE_IN_BLOCKS) + } + (BLOCK_SIZE * STACK_SIZE_IN_BLOCKS); #[cfg(feature = "arch_virt")] let t = Some(Task { @@ -91,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), @@ -132,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 @@ -161,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 @@ -201,6 +183,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 => { @@ -226,13 +216,47 @@ 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 { + tc.use_fb_console = true; + { + let uart = serial_port(); + if let Some(uart) = uart { + uart.putstr("using framebuffer console\n"); + } + } + } 0 } 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; @@ -249,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 { @@ -262,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; @@ -328,6 +346,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 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 + } }, suspend, ) @@ -345,24 +388,18 @@ 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) - }; unsafe { tc.memory_manager .as_mut() .unwrap_unchecked() - .free_n_blocks(stackblock, 4) + .free_n_blocks(sp - (BLOCK_SIZE * STACK_SIZE_IN_BLOCKS), 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; @@ -417,6 +454,7 @@ pub struct TrafficControl { pub inbuf_read: u8, pub inbuf_write: u8, pub hung_system: bool, + pub use_fb_console: bool, } pub struct Task { @@ -430,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, @@ -449,6 +487,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); 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]); } } }