feature/nikocs/framebuffer #1

Merged
husky merged 9 commits from feature/nikocs/framebuffer into develop 2025-09-11 16:54:17 -07:00
20 changed files with 1073 additions and 130 deletions

View file

@ -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",
]

View file

@ -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 = []

View file

@ -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);
}

View file

@ -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
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

View file

@ -1,3 +1,5 @@
// fixme: port this to the device-agnostic framebuffer console
pub mod colors;
pub mod extrachars;
pub mod terminal;

View file

@ -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));
}

View file

@ -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<FramebufferConsole> = 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);
}
}
}

106
src/dev/framebuffer/mod.rs Normal file
View file

@ -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,
);
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -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);
}

View file

@ -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::<VirtQueue>().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::<VirtQueue>() {
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::<Request>() 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) {

493
src/dev/virtio/gpu.rs Normal file
View file

@ -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<Self, VirtIoGpuDeviceError> {
// 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::<VirtQueue>().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::<VirtQueue>() {
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::<CtrlHeader>(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::<ResourceCreate2D>(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::<ResourceCreate2D>() as u32,
flags: VIRTIO_DESC_F_NEXT,
next: 0,
};
let desc_resp = Descriptor {
addr: gpu.latest_response as u64,
len: size_of::<CtrlHeader>() 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::<ResourceAttachBacking>(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::<GPUMemEntry>(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::<ResourceAttachBacking>() 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::<GPUMemEntry>() as u32,
flags: VIRTIO_DESC_F_NEXT,
next: 0,
};
let desc_resp = Descriptor {
addr: gpu.latest_response as u64,
len: size_of::<CtrlHeader>() 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::<SetScanout>(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::<SetScanout>() as u32,
flags: VIRTIO_DESC_F_NEXT,
next: 0,
};
let desc_resp = Descriptor {
addr: gpu.latest_response as u64,
len: size_of::<CtrlHeader>() 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<T: Sized + IntoIterator<Item = Descriptor>>(&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::<TransferToHost2D>(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::<TransferToHost2D>() as u32,
flags: VIRTIO_DESC_F_NEXT,
next: 0,
};
let desc_resp = Descriptor {
addr: self.latest_response as u64,
len: size_of::<CtrlHeader>() 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::<ResourceFlush>(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::<ResourceFlush>() as u32,
flags: VIRTIO_DESC_F_NEXT,
next: 0,
};
let desc_resp = Descriptor {
addr: self.latest_response as u64,
len: size_of::<CtrlHeader>() 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);
}
}
}
}

View file

@ -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<VirtIoDevice>; VIRTIO_MMIO_DEVCOUNT]> = Spinlock::new([const { None }; VIRTIO_MMIO_DEVCOUNT]);
pub static VIRTIO_HARD_BLOCK_DEVICE: Spinlock<Option<u8>> = Spinlock::new(None);
pub static VIRTIO_GPU_DEVICE: Spinlock<Option<u8>> = 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<T: Sized>(tc: &mut TrafficControl) -> &'static mut T {
let num_blocks = size_of::<T>().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 {

View file

@ -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 {

View file

@ -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();

View file

@ -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

View file

@ -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<TrafficControl> = 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<usize>; 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,
}
}

View file

@ -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);

View file

@ -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]);
}
}
}