Merge pull request 'lifeblood os v0.1.1' (#3) from develop into mommy
Reviewed-on: #3
This commit is contained in:
commit
1272089784
30 changed files with 1855 additions and 167 deletions
|
|
@ -8,5 +8,5 @@ rustflags = [
|
||||||
|
|
||||||
[tarcet.riscv32i-unknown-none-elf]
|
[tarcet.riscv32i-unknown-none-elf]
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C", "link-args=-nostdlib -ffreestanding -fPIC",
|
"-C", "link-args=-nostdlib -ffreestanding -relocation-model=static",
|
||||||
]
|
]
|
||||||
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -29,7 +29,7 @@ checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lbos"
|
name = "lbos"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"compiler_builtins",
|
"compiler_builtins",
|
||||||
|
|
@ -39,7 +39,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "liblbos"
|
name = "liblbos"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
|
|
|
||||||
11
Cargo.toml
11
Cargo.toml
|
|
@ -4,7 +4,7 @@ exclude = ["turntable", "makeddi", "ddi", "example"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lbos"
|
name = "lbos"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
@ -24,7 +24,7 @@ panic = "abort"
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
debug-assertions = false
|
debug-assertions = false
|
||||||
overflow-checks = false
|
overflow-checks = false
|
||||||
strip = false
|
strip = true
|
||||||
lto = true
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
|
|
@ -32,10 +32,13 @@ codegen-units = 1
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["keyboard_en_us"]
|
||||||
debug_messages = []
|
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_ofw = []
|
||||||
arch_ppc32 = ["compiler_builtins", "arch_ofw", "fs_fat32"]
|
arch_ppc32 = ["compiler_builtins", "arch_ofw", "fs_fat32"]
|
||||||
dev_virtio = []
|
dev_virtio = []
|
||||||
|
dev_framebuffer = []
|
||||||
|
keyboard_en_us = []
|
||||||
|
keyboard_dvorak = []
|
||||||
fs_fat32 = []
|
fs_fat32 = []
|
||||||
|
|
@ -76,6 +76,19 @@ fn patch_lo12_iins(mut instruction: u32, pointer: u32) -> u32 {
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn patch_lo12_sins(mut instruction: u32, pointer: u32) -> u32 {
|
||||||
|
// bits 0..5 are shifted seven left
|
||||||
|
// bits 5..12 are shifted 25 left
|
||||||
|
const MASK_1: u32 = 0x1F;
|
||||||
|
const TARGET_1: u32 = 0xF80;
|
||||||
|
const MASK_2: u32 = 0xFE0;
|
||||||
|
const TARGET_2: u32 = 0xFE000000;
|
||||||
|
let lo_1 = ((pointer & MASK_1) << 7) & TARGET_1;
|
||||||
|
let lo_2 = ((pointer & MASK_2) << 20) & TARGET_2;
|
||||||
|
instruction = (instruction & !(TARGET_1 | TARGET_2)) | lo_1 | lo_2;
|
||||||
|
instruction
|
||||||
|
}
|
||||||
|
|
||||||
fn sap_addr(target_pointer: u32, target_segment_base: u32, relocation_pointer: u32, current_segment_base: u32) -> u32 {
|
fn sap_addr(target_pointer: u32, target_segment_base: u32, relocation_pointer: u32, current_segment_base: u32) -> u32 {
|
||||||
(target_pointer).wrapping_sub(relocation_pointer)
|
(target_pointer).wrapping_sub(relocation_pointer)
|
||||||
}
|
}
|
||||||
|
|
@ -102,6 +115,13 @@ pub fn apply_relocation(segment_buffer: &mut [u8], current_segment_base: u32, ta
|
||||||
i_ins = patch_lo12_iins(i_ins, addr);
|
i_ins = patch_lo12_iins(i_ins, addr);
|
||||||
segment_buffer[i_ins_ptr..i_ins_ptr+4].copy_from_slice(&i_ins.to_le_bytes());
|
segment_buffer[i_ins_ptr..i_ins_ptr+4].copy_from_slice(&i_ins.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
x if x == RiscVRelocationType::Lo12S as u16 => {
|
||||||
|
let mut s_ins_ptr = relocation_header.relocation_pointer as usize;
|
||||||
|
let mut s_ins = u32::from_le_bytes(segment_buffer[s_ins_ptr..s_ins_ptr+4].try_into().unwrap());
|
||||||
|
let addr = relocation_header.target_pointer as u32 + target_segment_base;
|
||||||
|
s_ins = patch_lo12_sins(s_ins, addr);
|
||||||
|
segment_buffer[s_ins_ptr..s_ins_ptr+4].copy_from_slice(&s_ins.to_le_bytes());
|
||||||
|
}
|
||||||
x => {
|
x => {
|
||||||
unhandled_callback(x);
|
unhandled_callback(x);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
example/Cargo.lock
generated
2
example/Cargo.lock
generated
|
|
@ -11,4 +11,4 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "liblbos"
|
name = "liblbos"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "liblbos"
|
name = "liblbos"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,12 @@ pub struct TaskSetup {
|
||||||
pub epc: usize,
|
pub epc: usize,
|
||||||
pub ddi_first_addr: usize,
|
pub ddi_first_addr: usize,
|
||||||
pub ddi_size: usize,
|
pub ddi_size: usize,
|
||||||
|
pub environment: usize,
|
||||||
pub wait_for_task_exit: bool,
|
pub wait_for_task_exit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Environment {
|
||||||
|
pub current_directory_path: usize,
|
||||||
|
pub current_directory_path_len: usize,
|
||||||
|
}
|
||||||
|
|
@ -65,6 +65,21 @@ pub enum SysCall {
|
||||||
/// waits for a sent notification to be received
|
/// waits for a sent notification to be received
|
||||||
/// (taskid) -> 0
|
/// (taskid) -> 0
|
||||||
WaitForNotifAck = 13,
|
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
|
/// initializes kernel, ONLY CALL ONCE! IN FACT, YOU PROBABLY NEVER NEED TO CALL THIS
|
||||||
InitKernel = 666
|
InitKernel = 666
|
||||||
|
|
@ -90,6 +105,11 @@ pub fn usize2sc(u: usize) -> SysCall {
|
||||||
11 => SysCall::WaitForNotification,
|
11 => SysCall::WaitForNotification,
|
||||||
12 => SysCall::PendingNotifications,
|
12 => SysCall::PendingNotifications,
|
||||||
13 => SysCall::WaitForNotifAck,
|
13 => SysCall::WaitForNotifAck,
|
||||||
|
14 => SysCall::EnvironmentPointer,
|
||||||
|
15 => SysCall::DisableFramebufferConsole,
|
||||||
|
16 => SysCall::EnableFramebufferConsole,
|
||||||
|
17 => SysCall::FramebufferPointer,
|
||||||
|
18 => SysCall::FlushFramebufferRect,
|
||||||
666 => SysCall::InitKernel,
|
666 => SysCall::InitKernel,
|
||||||
_ => SysCall::NoAction,
|
_ => SysCall::NoAction,
|
||||||
}
|
}
|
||||||
|
|
@ -159,6 +179,26 @@ pub fn wait_for_notif_ack(taskid: u8) {
|
||||||
syscall(SysCall::WaitForNotifAck, taskid as usize, 0, 0, 0, 0, 0);
|
syscall(SysCall::WaitForNotifAck, taskid as usize, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn environment_pointer() -> usize {
|
||||||
|
syscall(SysCall::EnvironmentPointer, 0, 0, 0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn 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() {
|
pub fn init_kernel() {
|
||||||
syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0);
|
syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -1,2 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/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 -device virtio-keyboard-device -serial stdio -m 10M -device loader,cpu-num=0,file=target/riscv32imac-unknown-none-elf/release/lbos -monitor telnet:127.0.0.1:1235,server,nowait -d guest_errors,unimp
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// fixme: port this to the device-agnostic framebuffer console
|
||||||
|
|
||||||
pub mod colors;
|
pub mod colors;
|
||||||
pub mod extrachars;
|
pub mod extrachars;
|
||||||
pub mod terminal;
|
pub mod terminal;
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,13 @@ ENTRY( _start )
|
||||||
|
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
rom (wxa) : ORIGIN = 0x80000000, LENGTH = 64 * 1024
|
rom (wxa) : ORIGIN = 0x80000000, LENGTH = 0x10000
|
||||||
ram (wxa) : ORIGIN = 0x80010000, LENGTH = 1048510
|
|
||||||
|
ram (wxa) : ORIGIN = 0x80010000, LENGTH = 0x10000
|
||||||
|
|
||||||
|
virtqueues (wxa) : ORIGIN = 0x80020000, LENGTH = 0x20000
|
||||||
|
|
||||||
|
framebuffer (wxa) : ORIGIN = 0x80040000, LENGTH = 320 * 240 * 4
|
||||||
}
|
}
|
||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
|
|
@ -36,7 +41,7 @@ SECTIONS {
|
||||||
PROVIDE(_bss_start = .);
|
PROVIDE(_bss_start = .);
|
||||||
*(.sbss .sbss.*)
|
*(.sbss .sbss.*)
|
||||||
*(.bss .bss.*)
|
*(.bss .bss.*)
|
||||||
. = ALIGN(4096);
|
. = ALIGN(512);
|
||||||
PROVIDE(_bss_end = .);
|
PROVIDE(_bss_end = .);
|
||||||
} >ram AT>ram :bss
|
} >ram AT>ram :bss
|
||||||
|
|
||||||
|
|
@ -51,8 +56,17 @@ SECTIONS {
|
||||||
PROVIDE(_stack_end = _stack_start + 16384);
|
PROVIDE(_stack_end = _stack_start + 16384);
|
||||||
PROVIDE(_tstack_start = _stack_end);
|
PROVIDE(_tstack_start = _stack_end);
|
||||||
PROVIDE(_tstack_end = _tstack_start + 16384);
|
PROVIDE(_tstack_end = _tstack_start + 16384);
|
||||||
PROVIDE(_virtio_virtqueue_start = _tstack_end);
|
PROVIDE(_heap_start = _tstack_end);
|
||||||
PROVIDE(_virtio_virtqueue_end = _virtio_virtqueue_start + 32768);
|
|
||||||
PROVIDE(_heap_start = _virtio_virtqueue_end);
|
|
||||||
PROVIDE(_heap_size = _MEM_END - _heap_start);
|
PROVIDE(_heap_size = _MEM_END - _heap_start);
|
||||||
|
|
||||||
|
PROVIDE(_virtio_queue_1_start = ORIGIN(virtqueues));
|
||||||
|
PROVIDE(_virtio_queue_1_end = _virtio_queue_1_start + 0x2000);
|
||||||
|
PROVIDE(_virtio_queue_2_start = _virtio_queue_1_end);
|
||||||
|
PROVIDE(_virtio_queue_2_end = _virtio_queue_2_start + 0x2000);
|
||||||
|
PROVIDE(_virtio_queue_3_start = _virtio_queue_2_end);
|
||||||
|
PROVIDE(_virtio_queue_3_end = _virtio_queue_3_start + 0x2000);
|
||||||
|
PROVIDE(_virtio_queue_4_start = _virtio_queue_3_end);
|
||||||
|
PROVIDE(_virtio_queue_4_end = _virtio_queue_4_start + 0x2000);
|
||||||
|
|
||||||
|
PROVIDE(_framebuffer_start = ORIGIN(framebuffer));
|
||||||
}
|
}
|
||||||
|
|
@ -85,6 +85,7 @@ extern "C" fn _virt_init() -> ! {
|
||||||
epc: ktask as usize,
|
epc: ktask as usize,
|
||||||
ddi_first_addr: 0,
|
ddi_first_addr: 0,
|
||||||
ddi_size: 0,
|
ddi_size: 0,
|
||||||
|
environment: 0,
|
||||||
wait_for_task_exit: false,
|
wait_for_task_exit: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
101
src/dev/framebuffer/console.rs
Normal file
101
src/dev/framebuffer/console.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
use crate::dev::framebuffer::{fb_clear_area, 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 need_line_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,
|
||||||
|
need_line_clear: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = true;
|
||||||
|
self.buffer = [[' '; 20]; 15];
|
||||||
|
self.cursor = (0, 0);
|
||||||
|
self.render(tc, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_line_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, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, tc: &mut TrafficControl, refresh_whole_screen: bool) {
|
||||||
|
if self.need_screen_clear {
|
||||||
|
fb_clearscreen(tc, refresh_whole_screen);
|
||||||
|
self.need_screen_clear = false;
|
||||||
|
}
|
||||||
|
if self.need_line_clear {
|
||||||
|
fb_clear_area(tc, 0, self.cursor.1 * 16, 320, 16, refresh_whole_screen);
|
||||||
|
self.need_line_clear = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (y, line) in self.buffer.iter().enumerate() {
|
||||||
|
fb_write_char_array(tc, 0, y * 16, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
136
src/dev/framebuffer/mod.rs
Normal file
136
src/dev/framebuffer/mod.rs
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
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, send_refresh: bool) {
|
||||||
|
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]]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if send_refresh {
|
||||||
|
framebuffer_update(
|
||||||
|
tc,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
FRAMEBUFFER_WIDTH as u32,
|
||||||
|
FRAMEBUFFER_HEIGHT as u32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fb_clear_area(tc: &mut TrafficControl, x: usize, y: usize, w: usize, h: usize, send_refresh: bool) {
|
||||||
|
const BYTES: [u8; 3] = FB_BG_COLOR.to_bytes();
|
||||||
|
let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed);
|
||||||
|
if fbaddr == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH;
|
||||||
|
for row in 0..h {
|
||||||
|
for col in 0..w {
|
||||||
|
unsafe {
|
||||||
|
unsafe {
|
||||||
|
let fb = (fbaddr as *mut u8).add(((y+row) * fbstride) + ((x+col) * 4)) as *mut u32;
|
||||||
|
fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if send_refresh {
|
||||||
|
framebuffer_update(
|
||||||
|
tc,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
FRAMEBUFFER_WIDTH as u32,
|
||||||
|
FRAMEBUFFER_HEIGHT as u32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/dev/framebuffer/vapfont.data
Normal file
BIN
src/dev/framebuffer/vapfont.data
Normal file
Binary file not shown.
BIN
src/dev/framebuffer/vapfont.png
Normal file
BIN
src/dev/framebuffer/vapfont.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -1,7 +1,19 @@
|
||||||
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use crate::trafficcontrol::TrafficControl;
|
use crate::trafficcontrol::TrafficControl;
|
||||||
|
|
||||||
|
pub const FRAMEBUFFER_WIDTH: usize = 320;
|
||||||
|
pub const FRAMEBUFFER_HEIGHT: usize = 240;
|
||||||
|
|
||||||
|
// 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")]
|
#[cfg(feature = "dev_virtio")]
|
||||||
pub mod virtio;
|
pub mod virtio;
|
||||||
|
#[cfg(feature = "dev_framebuffer")]
|
||||||
|
pub mod framebuffer;
|
||||||
|
|
||||||
pub fn probe_devices(tc: &mut TrafficControl) {
|
pub fn probe_devices(tc: &mut TrafficControl) {
|
||||||
#[cfg(feature = "dev_virtio")]
|
#[cfg(feature = "dev_virtio")]
|
||||||
|
|
@ -12,4 +24,18 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6
|
||||||
#[cfg(feature = "dev_virtio")]
|
#[cfg(feature = "dev_virtio")]
|
||||||
return virtio::read_sector(tc, buffer, size, sector);
|
return virtio::read_sector(tc, buffer, size, sector);
|
||||||
false
|
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);
|
||||||
}
|
}
|
||||||
|
|
@ -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};
|
use crate::trafficcontrol::{TaskWait, TrafficControl};
|
||||||
|
|
||||||
unsafe extern "C" {
|
unsafe extern "C" {
|
||||||
fn _virtio_virtqueue_start();
|
fn _virtio_queue_1_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
pub status: u8,
|
pub status: u8,
|
||||||
|
|
@ -57,7 +66,7 @@ impl VirtIoBlockDevice {
|
||||||
// read host features
|
// read host features
|
||||||
let host_features =
|
let host_features =
|
||||||
unsafe { ((addr + VIRTIO_MMIO_HOST_FEATURES) as *const u32).read_volatile() };
|
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;
|
let read_only = host_features & (1 << 5) != 0;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -96,8 +105,19 @@ impl VirtIoBlockDevice {
|
||||||
unsafe {
|
unsafe {
|
||||||
((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0);
|
((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0);
|
||||||
}
|
}
|
||||||
let queue_ptr= _virtio_virtqueue_start as usize;
|
let num_blocks = size_of::<VirtQueue>().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes
|
||||||
unsafe { ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096) }; // who knows if this actually works :p
|
//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 {
|
unsafe {
|
||||||
((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(queue_ptr as u32 / 4096)
|
((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(queue_ptr as u32 / 4096)
|
||||||
};
|
};
|
||||||
|
|
@ -147,10 +167,9 @@ impl VirtIoBlockDevice {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let blk_request = {
|
let blk_request =
|
||||||
let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked() }.alloc_one_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;
|
||||||
} as *mut Request;
|
|
||||||
let desc = Descriptor {
|
let desc = Descriptor {
|
||||||
addr: unsafe { &(*blk_request) as *const _ as u64 },
|
addr: unsafe { &(*blk_request) as *const _ as u64 },
|
||||||
len: size_of::<Request>() as u32,
|
len: size_of::<Request>() as u32,
|
||||||
|
|
@ -211,8 +230,18 @@ impl VirtIoBlockDevice {
|
||||||
|
|
||||||
let tid = unsafe { (*rq).tid };
|
let tid = unsafe { (*rq).tid };
|
||||||
|
|
||||||
let rq_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked() }.addr_to_block(rq as usize);
|
unsafe {
|
||||||
unsafe { tc.memory_manager.as_mut().unwrap_unchecked().free_one_block(rq_block); }
|
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
|
// awaken
|
||||||
if let Some(Some(task)) = tc.tasks.get_mut(tid as usize) {
|
if let Some(Some(task)) = tc.tasks.get_mut(tid as usize) {
|
||||||
|
|
|
||||||
493
src/dev/virtio/gpu.rs
Normal file
493
src/dev/virtio/gpu.rs
Normal file
|
|
@ -0,0 +1,493 @@
|
||||||
|
use crate::arch::serial_port;
|
||||||
|
use crate::dev::{FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH};
|
||||||
|
use crate::dev::virtio::{heap_allocate_type, Descriptor, VirtQueue, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE};
|
||||||
|
use crate::strprint::u32_hex;
|
||||||
|
use crate::trafficcontrol::TrafficControl;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
437
src/dev/virtio/input.rs
Normal file
437
src/dev/virtio/input.rs
Normal file
|
|
@ -0,0 +1,437 @@
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use crate::dev::virtio::{
|
||||||
|
Descriptor, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE,
|
||||||
|
VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL,
|
||||||
|
VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER,
|
||||||
|
VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK,
|
||||||
|
VIRTIO_QUEUE_SIZE, VirtQueue,
|
||||||
|
};
|
||||||
|
use crate::trafficcontrol::TrafficControl;
|
||||||
|
|
||||||
|
unsafe extern "C" {
|
||||||
|
fn _virtio_queue_3_start();
|
||||||
|
fn _virtio_queue_4_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const VIRTIO_INPUT_EVENT_TYPE_KEYBOARD: u16 = 1;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Event {
|
||||||
|
pub event_type: u16,
|
||||||
|
pub code: u16,
|
||||||
|
pub value: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const VIRTIO_INPUT_EVENTBUFFER_SIZE: usize = 8;
|
||||||
|
|
||||||
|
pub struct VirtIoInputDevice {
|
||||||
|
pub addr: usize,
|
||||||
|
pub queue: usize,
|
||||||
|
pub idx: u16,
|
||||||
|
pub ack_used_idx: u16,
|
||||||
|
pub status_queue: usize,
|
||||||
|
pub status_queue_idx: u16,
|
||||||
|
pub status_queue_ack_used_idx: u16,
|
||||||
|
pub event_buffer: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum VirtIoInputDeviceError {
|
||||||
|
FeatureSetMismatch,
|
||||||
|
QueueSetupFailed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtIoInputDevice {
|
||||||
|
pub fn new_and_init(
|
||||||
|
tc: &mut TrafficControl,
|
||||||
|
addr: usize,
|
||||||
|
) -> Result<Self, VirtIoInputDeviceError> {
|
||||||
|
// reset device (write 0 to status)
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(0);
|
||||||
|
}
|
||||||
|
// set ack bit
|
||||||
|
let mut status = VIRTIO_MMIO_STATUS_ACKNOWLEDGE;
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
||||||
|
}
|
||||||
|
// set driver bit
|
||||||
|
status |= VIRTIO_MMIO_STATUS_DRIVER;
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
||||||
|
}
|
||||||
|
// send feature set
|
||||||
|
let guest_features = 0; // todo: configure properly
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_GUEST_FEATURES) as *mut u32).write_volatile(guest_features);
|
||||||
|
}
|
||||||
|
status |= VIRTIO_MMIO_STATUS_FEATURES_OK;
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
||||||
|
}
|
||||||
|
// make sure features ok is still set, otherwise failed
|
||||||
|
if unsafe { ((addr + VIRTIO_MMIO_STATUS) as *const u32).read_volatile() }
|
||||||
|
& VIRTIO_MMIO_STATUS_FEATURES_OK
|
||||||
|
== 0
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(VIRTIO_MMIO_STATUS_FAILED);
|
||||||
|
}
|
||||||
|
return Err(VirtIoInputDeviceError::FeatureSetMismatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup event queue
|
||||||
|
let queue_max_by_device =
|
||||||
|
unsafe { ((addr + VIRTIO_MMIO_QUEUE_NUM_MAX) as *const u32).read_volatile() };
|
||||||
|
if queue_max_by_device < VIRTIO_QUEUE_SIZE as _ {
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(VIRTIO_MMIO_STATUS_FAILED);
|
||||||
|
}
|
||||||
|
return Err(VirtIoInputDeviceError::QueueSetupFailed);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_QUEUE_NUM) as *mut u32).write_volatile(VIRTIO_QUEUE_SIZE as _);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0);
|
||||||
|
}
|
||||||
|
let event_queue_ptr = _virtio_queue_3_start as usize;
|
||||||
|
// zero out queue
|
||||||
|
for i in 0..size_of::<VirtQueue>() {
|
||||||
|
unsafe {
|
||||||
|
((event_queue_ptr + i) as *mut u8).write_volatile(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32)
|
||||||
|
.write_volatile(event_queue_ptr as u32 / 4096);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup status queue
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(1);
|
||||||
|
}
|
||||||
|
let status_queue_ptr = _virtio_queue_4_start as usize;
|
||||||
|
// zero out queue
|
||||||
|
for i in 0..size_of::<VirtQueue>() {
|
||||||
|
unsafe {
|
||||||
|
((status_queue_ptr + i) as *mut u8).write_volatile(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32)
|
||||||
|
.write_volatile(status_queue_ptr as u32 / 4096);
|
||||||
|
}
|
||||||
|
|
||||||
|
// all done!
|
||||||
|
status |= VIRTIO_MMIO_STATUS_DRIVER_OK;
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
let event_buffer_ptr = unsafe {
|
||||||
|
tc.memory_manager
|
||||||
|
.as_mut()
|
||||||
|
.unwrap_unchecked()
|
||||||
|
.alloc_n_blocks((size_of::<Event>() * VIRTIO_INPUT_EVENTBUFFER_SIZE).div_ceil(512))
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut input = VirtIoInputDevice {
|
||||||
|
addr,
|
||||||
|
queue: event_queue_ptr,
|
||||||
|
idx: 0,
|
||||||
|
ack_used_idx: 0,
|
||||||
|
status_queue: status_queue_ptr,
|
||||||
|
status_queue_idx: 0,
|
||||||
|
status_queue_ack_used_idx: 0,
|
||||||
|
event_buffer: event_buffer_ptr as usize,
|
||||||
|
};
|
||||||
|
for i in 0..VIRTIO_INPUT_EVENTBUFFER_SIZE {
|
||||||
|
input.fill_event(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_event(&mut self, eventi: usize) {
|
||||||
|
let desc = Descriptor {
|
||||||
|
addr: self.event_buffer as u64 + (eventi * size_of::<Event>()) as u64,
|
||||||
|
len: size_of::<Event>() as u32,
|
||||||
|
flags: VIRTIO_DESC_F_WRITE,
|
||||||
|
next: 0,
|
||||||
|
};
|
||||||
|
let head = self.idx;
|
||||||
|
let queue = unsafe { &mut (*(self.queue as *mut VirtQueue)) };
|
||||||
|
queue.desc[self.idx as usize] = desc;
|
||||||
|
self.idx = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16;
|
||||||
|
queue.avail.ring[queue.avail.idx as usize % VIRTIO_QUEUE_SIZE] = head;
|
||||||
|
queue.avail.idx = queue.avail.idx.wrapping_add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pending(&mut self, tc: &mut TrafficControl) {
|
||||||
|
// event queue
|
||||||
|
let queue = unsafe { &(*(self.queue as *mut VirtQueue)) };
|
||||||
|
while self.ack_used_idx != queue.used.idx {
|
||||||
|
let elem = &queue.used.ring[self.ack_used_idx as usize % VIRTIO_QUEUE_SIZE];
|
||||||
|
self.ack_used_idx = self.ack_used_idx.wrapping_add(1);
|
||||||
|
let desc = &queue.desc[elem.id as usize];
|
||||||
|
let event = unsafe { &*(desc.addr as *const Event) };
|
||||||
|
self.fill_event(elem.id as usize);
|
||||||
|
|
||||||
|
match event.event_type {
|
||||||
|
x if x == VIRTIO_INPUT_EVENT_TYPE_KEYBOARD => {
|
||||||
|
let keycode = event.code;
|
||||||
|
let down = event.value == 1;
|
||||||
|
|
||||||
|
// first, handle shift todo: handle more control characters
|
||||||
|
if keycode == LinuxKeycode::LeftShift as u16 || keycode == LinuxKeycode::RightShift as u16 {
|
||||||
|
UPPERCASE.store(down, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// next, handle ascii characters
|
||||||
|
if !down { // write on UP
|
||||||
|
let ascii = KEYMAP_ASCII.iter().find_map(|(a, b)| {
|
||||||
|
if keycode == *a as u16 {
|
||||||
|
Some(*b)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some(mut ascii) = ascii {
|
||||||
|
if UPPERCASE.load(Ordering::Relaxed) {
|
||||||
|
if ascii.is_ascii_alphabetic() {
|
||||||
|
ascii = ascii.to_ascii_uppercase();
|
||||||
|
} else {
|
||||||
|
// todo: handle other characters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tc.write_inbuf(ascii);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// status queue
|
||||||
|
let queue = unsafe { &(*(self.status_queue as *mut VirtQueue)) };
|
||||||
|
while self.status_queue_ack_used_idx != queue.used.idx {
|
||||||
|
let elem =
|
||||||
|
&queue.used.ring[self.status_queue_ack_used_idx as usize % VIRTIO_QUEUE_SIZE];
|
||||||
|
let desc = &queue.desc[elem.id as usize];
|
||||||
|
let event = unsafe { &*(desc.addr as *const Event) };
|
||||||
|
self.status_queue_ack_used_idx = self.status_queue_ack_used_idx.wrapping_add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static UPPERCASE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
// linux keycodes
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum LinuxKeycode {
|
||||||
|
ESC = 1,
|
||||||
|
Key1 = 2,
|
||||||
|
Key2 = 3,
|
||||||
|
Key3 = 4,
|
||||||
|
Key4 = 5,
|
||||||
|
Key5 = 6,
|
||||||
|
Key6 = 7,
|
||||||
|
Key7 = 8,
|
||||||
|
Key8 = 9,
|
||||||
|
Key9 = 10,
|
||||||
|
Key0 = 11,
|
||||||
|
Minus = 12,
|
||||||
|
Equal = 13,
|
||||||
|
Backspace = 14,
|
||||||
|
Tab = 15,
|
||||||
|
Q = 16,
|
||||||
|
W = 17,
|
||||||
|
E = 18,
|
||||||
|
R = 19,
|
||||||
|
T = 20,
|
||||||
|
Y = 21,
|
||||||
|
U = 22,
|
||||||
|
I = 23,
|
||||||
|
O = 24,
|
||||||
|
P = 25,
|
||||||
|
LeftBracket = 26,
|
||||||
|
RightBracket = 27,
|
||||||
|
Enter = 28,
|
||||||
|
LeftControl = 29,
|
||||||
|
A = 30,
|
||||||
|
S = 31,
|
||||||
|
D = 32,
|
||||||
|
F = 33,
|
||||||
|
G = 34,
|
||||||
|
H = 35,
|
||||||
|
J = 36,
|
||||||
|
K = 37,
|
||||||
|
L = 38,
|
||||||
|
Semicolon = 39,
|
||||||
|
Apostrophe = 40,
|
||||||
|
Grave = 41,
|
||||||
|
LeftShift = 42,
|
||||||
|
Backslash = 43,
|
||||||
|
Z = 44,
|
||||||
|
X = 45,
|
||||||
|
C = 46,
|
||||||
|
V = 47,
|
||||||
|
B = 48,
|
||||||
|
N = 49,
|
||||||
|
M = 50,
|
||||||
|
Comma = 51,
|
||||||
|
Dot = 52,
|
||||||
|
Slash = 53,
|
||||||
|
RightShift = 54,
|
||||||
|
KPAsterisk = 55,
|
||||||
|
LeftAlt = 56,
|
||||||
|
Space = 57,
|
||||||
|
CapsLock = 58,
|
||||||
|
F1 = 59,
|
||||||
|
F2 = 60,
|
||||||
|
F3 = 61,
|
||||||
|
F4 = 62,
|
||||||
|
F5 = 63,
|
||||||
|
F6 = 64,
|
||||||
|
F7 = 65,
|
||||||
|
F8 = 66,
|
||||||
|
F9 = 67,
|
||||||
|
F10 = 68,
|
||||||
|
NumLock = 69,
|
||||||
|
ScrollLock = 70,
|
||||||
|
KP7 = 71,
|
||||||
|
KP8 = 72,
|
||||||
|
KP9 = 73,
|
||||||
|
KPMinus = 74,
|
||||||
|
KP4 = 75,
|
||||||
|
KP5 = 76,
|
||||||
|
KP6 = 77,
|
||||||
|
KPPlus = 78,
|
||||||
|
KP1 = 79,
|
||||||
|
KP2 = 80,
|
||||||
|
KP3 = 81,
|
||||||
|
KP0 = 82,
|
||||||
|
KPDot = 83,
|
||||||
|
}
|
||||||
|
|
||||||
|
// keys that do not map directly are handled outside of this
|
||||||
|
#[cfg(feature = "keyboard_en_us")]
|
||||||
|
const KEYMAP_ASCII: &[(LinuxKeycode, u8)] = &[
|
||||||
|
(LinuxKeycode::ESC, 0x1B),
|
||||||
|
(LinuxKeycode::Key1, b'1'),
|
||||||
|
(LinuxKeycode::Key2, b'2'),
|
||||||
|
(LinuxKeycode::Key3, b'3'),
|
||||||
|
(LinuxKeycode::Key4, b'4'),
|
||||||
|
(LinuxKeycode::Key5, b'5'),
|
||||||
|
(LinuxKeycode::Key6, b'6'),
|
||||||
|
(LinuxKeycode::Key7, b'7'),
|
||||||
|
(LinuxKeycode::Key8, b'8'),
|
||||||
|
(LinuxKeycode::Key9, b'9'),
|
||||||
|
(LinuxKeycode::Key0, b'0'),
|
||||||
|
(LinuxKeycode::Minus, b'-'),
|
||||||
|
(LinuxKeycode::Equal, b'='),
|
||||||
|
(LinuxKeycode::Backspace, 0x7F),
|
||||||
|
(LinuxKeycode::Tab, 0x9),
|
||||||
|
(LinuxKeycode::Q, b'q'),
|
||||||
|
(LinuxKeycode::W, b'w'),
|
||||||
|
(LinuxKeycode::E, b'e'),
|
||||||
|
(LinuxKeycode::R, b'r'),
|
||||||
|
(LinuxKeycode::T, b't'),
|
||||||
|
(LinuxKeycode::Y, b'y'),
|
||||||
|
(LinuxKeycode::U, b'u'),
|
||||||
|
(LinuxKeycode::I, b'i'),
|
||||||
|
(LinuxKeycode::O, b'o'),
|
||||||
|
(LinuxKeycode::P, b'p'),
|
||||||
|
(LinuxKeycode::LeftBracket, b'['),
|
||||||
|
(LinuxKeycode::RightBracket, b']'),
|
||||||
|
(LinuxKeycode::Enter, b'\r'),
|
||||||
|
(LinuxKeycode::A, b'a'),
|
||||||
|
(LinuxKeycode::S, b's'),
|
||||||
|
(LinuxKeycode::D, b'd'),
|
||||||
|
(LinuxKeycode::F, b'f'),
|
||||||
|
(LinuxKeycode::G, b'g'),
|
||||||
|
(LinuxKeycode::H, b'h'),
|
||||||
|
(LinuxKeycode::J, b'j'),
|
||||||
|
(LinuxKeycode::K, b'k'),
|
||||||
|
(LinuxKeycode::L, b'l'),
|
||||||
|
(LinuxKeycode::Semicolon, b';'),
|
||||||
|
(LinuxKeycode::Apostrophe, b'\''),
|
||||||
|
(LinuxKeycode::Grave, b'`'),
|
||||||
|
(LinuxKeycode::Backslash, b'\\'),
|
||||||
|
(LinuxKeycode::Z, b'z'),
|
||||||
|
(LinuxKeycode::X, b'x'),
|
||||||
|
(LinuxKeycode::C, b'c'),
|
||||||
|
(LinuxKeycode::V, b'v'),
|
||||||
|
(LinuxKeycode::B, b'b'),
|
||||||
|
(LinuxKeycode::N, b'n'),
|
||||||
|
(LinuxKeycode::M, b'm'),
|
||||||
|
(LinuxKeycode::Comma, b','),
|
||||||
|
(LinuxKeycode::Dot, b'.'),
|
||||||
|
(LinuxKeycode::Slash, b'/'),
|
||||||
|
(LinuxKeycode::KPAsterisk, b'*'),
|
||||||
|
(LinuxKeycode::Space, b' '),
|
||||||
|
];
|
||||||
|
|
||||||
|
#[cfg(feature = "keyboard_dvorak")]
|
||||||
|
const KEYMAP_ASCII: &[(LinuxKeycode, u8)] = &[
|
||||||
|
(LinuxKeycode::ESC, 0x1B),
|
||||||
|
(LinuxKeycode::Key1, b'1'),
|
||||||
|
(LinuxKeycode::Key2, b'2'),
|
||||||
|
(LinuxKeycode::Key3, b'3'),
|
||||||
|
(LinuxKeycode::Key4, b'4'),
|
||||||
|
(LinuxKeycode::Key5, b'5'),
|
||||||
|
(LinuxKeycode::Key6, b'6'),
|
||||||
|
(LinuxKeycode::Key7, b'7'),
|
||||||
|
(LinuxKeycode::Key8, b'8'),
|
||||||
|
(LinuxKeycode::Key9, b'9'),
|
||||||
|
(LinuxKeycode::Key0, b'0'),
|
||||||
|
(LinuxKeycode::Minus, b'-'),
|
||||||
|
(LinuxKeycode::Equal, b'='),
|
||||||
|
(LinuxKeycode::Backspace, 0x7F),
|
||||||
|
(LinuxKeycode::Tab, 0x9),
|
||||||
|
(LinuxKeycode::Q, b'\''),
|
||||||
|
(LinuxKeycode::W, b','),
|
||||||
|
(LinuxKeycode::E, b'.'),
|
||||||
|
(LinuxKeycode::R, b'p'),
|
||||||
|
(LinuxKeycode::T, b'y'),
|
||||||
|
(LinuxKeycode::Y, b'f'),
|
||||||
|
(LinuxKeycode::U, b'g'),
|
||||||
|
(LinuxKeycode::I, b'c'),
|
||||||
|
(LinuxKeycode::O, b'r'),
|
||||||
|
(LinuxKeycode::P, b'l'),
|
||||||
|
(LinuxKeycode::LeftBracket, b'/'),
|
||||||
|
(LinuxKeycode::RightBracket, b'='),
|
||||||
|
(LinuxKeycode::Enter, b'\r'),
|
||||||
|
(LinuxKeycode::A, b'a'),
|
||||||
|
(LinuxKeycode::S, b'o'),
|
||||||
|
(LinuxKeycode::D, b'e'),
|
||||||
|
(LinuxKeycode::F, b'u'),
|
||||||
|
(LinuxKeycode::G, b'i'),
|
||||||
|
(LinuxKeycode::H, b'd'),
|
||||||
|
(LinuxKeycode::J, b'h'),
|
||||||
|
(LinuxKeycode::K, b't'),
|
||||||
|
(LinuxKeycode::L, b'n'),
|
||||||
|
(LinuxKeycode::Semicolon, b's'),
|
||||||
|
(LinuxKeycode::Apostrophe, b'-'),
|
||||||
|
(LinuxKeycode::Grave, b'`'),
|
||||||
|
(LinuxKeycode::Backslash, b'\\'),
|
||||||
|
(LinuxKeycode::Z, b';'),
|
||||||
|
(LinuxKeycode::X, b'q'),
|
||||||
|
(LinuxKeycode::C, b'j'),
|
||||||
|
(LinuxKeycode::V, b'k'),
|
||||||
|
(LinuxKeycode::B, b'x'),
|
||||||
|
(LinuxKeycode::N, b'b'),
|
||||||
|
(LinuxKeycode::M, b'm'),
|
||||||
|
(LinuxKeycode::Comma, b'w'),
|
||||||
|
(LinuxKeycode::Dot, b'v'),
|
||||||
|
(LinuxKeycode::Slash, b'z'),
|
||||||
|
(LinuxKeycode::KPAsterisk, b'*'),
|
||||||
|
(LinuxKeycode::Space, b' '),
|
||||||
|
];
|
||||||
|
|
@ -1,12 +1,19 @@
|
||||||
//! virtio devices
|
//! virtio devices
|
||||||
//! WARNING: virtio is currently completely broken! don't use it!
|
//! 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::block::{VirtIoBlockDevice, VirtIoBlockDeviceError};
|
||||||
|
use crate::dev::virtio::gpu::{VirtIoGpuDevice, VirtIoGpuDeviceError};
|
||||||
|
use crate::dev::virtio::input::{VirtIoInputDevice, VirtIoInputDeviceError};
|
||||||
|
use crate::rough_panic;
|
||||||
use crate::spinlock::Spinlock;
|
use crate::spinlock::Spinlock;
|
||||||
use crate::strprint::twodigit;
|
use crate::strprint::twodigit;
|
||||||
use crate::trafficcontrol::{TrafficControl};
|
use crate::trafficcontrol::{TrafficControl};
|
||||||
|
|
||||||
mod block;
|
mod block;
|
||||||
|
mod gpu;
|
||||||
|
mod input;
|
||||||
|
|
||||||
pub const VIRTIO_MMIO_START: usize = 0x1000_1000;
|
pub const VIRTIO_MMIO_START: usize = 0x1000_1000;
|
||||||
pub const VIRTIO_MMIO_END: usize = 0x1000_8000;
|
pub const VIRTIO_MMIO_END: usize = 0x1000_8000;
|
||||||
|
|
@ -32,13 +39,16 @@ pub const VIRTIO_MMIO_STATUS_FAILED: u32 = 1 << 7;
|
||||||
pub const VIRTIO_DESC_F_NEXT: u16 = 1 << 0;
|
pub const VIRTIO_DESC_F_NEXT: u16 = 1 << 0;
|
||||||
pub const VIRTIO_DESC_F_WRITE: u16 = 1 << 1;
|
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_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_HARD_BLOCK_DEVICE: Spinlock<Option<u8>> = Spinlock::new(None);
|
||||||
|
pub static VIRTIO_GPU_DEVICE: Spinlock<Option<u8>> = Spinlock::new(None);
|
||||||
|
|
||||||
pub enum VirtIoDevice {
|
pub enum VirtIoDevice {
|
||||||
BlockDevice(VirtIoBlockDevice),
|
BlockDevice(VirtIoBlockDevice),
|
||||||
|
GPUDevice(VirtIoGpuDevice),
|
||||||
|
InputDevice(VirtIoInputDevice),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -75,6 +85,15 @@ pub struct VirtQueue {
|
||||||
pub used: Used,
|
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) {
|
pub fn probe_virtio_devices(tc: &mut TrafficControl) {
|
||||||
let serial_port = crate::arch::serial_port();
|
let serial_port = crate::arch::serial_port();
|
||||||
let mut devices = VIRTIO_DEVICES.lock();
|
let mut devices = VIRTIO_DEVICES.lock();
|
||||||
|
|
@ -117,6 +136,55 @@ 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
18 => {
|
||||||
|
// input device
|
||||||
|
let input_device = VirtIoInputDevice::new_and_init(tc, addr);
|
||||||
|
if let Ok(input_device) = input_device {
|
||||||
|
devices[i] = Some(VirtIoDevice::InputDevice(input_device));
|
||||||
|
if let Some(serial_port) = &serial_port {
|
||||||
|
serial_port.putstr("virtio input device found\n");
|
||||||
|
}
|
||||||
|
} else if let Err(e) = input_device {
|
||||||
|
match e {
|
||||||
|
VirtIoInputDeviceError::FeatureSetMismatch => {
|
||||||
|
if let Some(serial_port) = &serial_port {
|
||||||
|
serial_port.putstr("virtio input device feature mismatch\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VirtIoInputDeviceError::QueueSetupFailed => {
|
||||||
|
if let Some(serial_port) = &serial_port {
|
||||||
|
serial_port.putstr("virtio input device queue setup failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
x => {
|
x => {
|
||||||
if let Some(serial_port) = &serial_port {
|
if let Some(serial_port) = &serial_port {
|
||||||
serial_port.putstr("unsupported device type ");
|
serial_port.putstr("unsupported device type ");
|
||||||
|
|
@ -144,6 +212,20 @@ pub fn handle_interrupt(interrupt: u32, tc: &mut TrafficControl) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
VirtIoDevice::GPUDevice(gpudev) => {
|
||||||
|
let gpu = {
|
||||||
|
let lock = VIRTIO_GPU_DEVICE.lock();
|
||||||
|
*lock
|
||||||
|
};
|
||||||
|
if let Some(gpu) = gpu {
|
||||||
|
if gpu as usize == idx {
|
||||||
|
gpudev.pending(tc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VirtIoDevice::InputDevice(inputdev) => {
|
||||||
|
inputdev.pending(tc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -156,10 +238,48 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6
|
||||||
let mut devices = VIRTIO_DEVICES.lock();
|
let mut devices = VIRTIO_DEVICES.lock();
|
||||||
if let Some(idx) = idx {
|
if let Some(idx) = idx {
|
||||||
if let Some(device) = devices[idx as usize].as_mut() {
|
if let Some(device) = devices[idx as usize].as_mut() {
|
||||||
match device {
|
return match device {
|
||||||
VirtIoDevice::BlockDevice(device) => {
|
VirtIoDevice::BlockDevice(device) => {
|
||||||
device.operation(tc, buffer, size, sector, false);
|
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 {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::fs::{Record, RecordType};
|
use crate::fs::{Record, RecordType};
|
||||||
|
use crate::strprint::u32_hex;
|
||||||
|
|
||||||
#[repr(packed(1), C)]
|
#[repr(packed(1), C)]
|
||||||
pub struct BPB {
|
pub struct BPB {
|
||||||
|
|
@ -176,6 +177,10 @@ pub fn reader_from_cluster(bpb: &BPBUseful, cluster: usize) -> Fat32FileReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn root_target(bpb: &BPBUseful) -> u32 {
|
||||||
|
bpb.root_cluster as u32
|
||||||
|
}
|
||||||
|
|
||||||
pub fn root_reader(bpb: &BPBUseful) -> Fat32DirectoryReader {
|
pub fn root_reader(bpb: &BPBUseful) -> Fat32DirectoryReader {
|
||||||
let cluster = bpb.root_cluster as usize;
|
let cluster = bpb.root_cluster as usize;
|
||||||
Fat32DirectoryReader {
|
Fat32DirectoryReader {
|
||||||
|
|
@ -282,9 +287,9 @@ pub fn seek_forward_one_sector(bpb: &BPBUseful, reader: &mut Fat32FileReader) ->
|
||||||
}
|
}
|
||||||
let table_value =
|
let table_value =
|
||||||
unsafe { ((fat_addr+(ent_offset)) as *const u32).read_volatile() } & 0x0FFFFFFF;
|
unsafe { ((fat_addr+(ent_offset)) as *const u32).read_volatile() } & 0x0FFFFFFF;
|
||||||
|
crate::syscalls::free_blocks(fat_addr, 1);
|
||||||
if table_value >= 0x0FFFFFF8 {
|
if table_value >= 0x0FFFFFF8 {
|
||||||
// no more clusters, whole file has been read
|
// no more clusters, whole file has been read
|
||||||
crate::syscalls::free_blocks(fat_addr, 1);
|
|
||||||
reader.eof = 1;
|
reader.eof = 1;
|
||||||
return false;
|
return false;
|
||||||
} else if table_value >= 0x00000002 {
|
} else if table_value >= 0x00000002 {
|
||||||
|
|
@ -294,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.cluster - 2) * bpb.sectors_per_cluster as usize) + bpb.first_data_sector();
|
||||||
reader.sector_offset = 0;
|
reader.sector_offset = 0;
|
||||||
if table_value == 0x00000000 || table_value == 0x00000001 {
|
if table_value == 0x00000000 || table_value == 0x00000001 {
|
||||||
crate::syscalls::free_blocks(fat_addr, 1);
|
|
||||||
reader.eof = 1;
|
reader.eof = 1;
|
||||||
return false;
|
return false;
|
||||||
} else if table_value == 0x0FFFFFF7 {
|
} else if table_value == 0x0FFFFFF7 {
|
||||||
// bad cluster, stop reading
|
// bad cluster, stop reading
|
||||||
crate::syscalls::free_blocks(fat_addr, 1);
|
|
||||||
crate::syscalls::write_terminal(b"badcluster");
|
crate::syscalls::write_terminal(b"badcluster");
|
||||||
reader.eof = 1;
|
reader.eof = 1;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -334,6 +337,7 @@ pub fn read_file(bpb: &BPBUseful, reader: &mut Fat32FileReader, buffer: &mut [u8
|
||||||
let mut left_in_sector = 512 - reader.sector_offset;
|
let mut left_in_sector = 512 - reader.sector_offset;
|
||||||
if left_in_sector == 0 {
|
if left_in_sector == 0 {
|
||||||
crate::syscalls::write_terminal(b"SECTORNULL?");
|
crate::syscalls::write_terminal(b"SECTORNULL?");
|
||||||
|
crate::syscalls::free_blocks(sector_addr, 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
while buffer_idx < buffer.len() && left_in_sector > 0 {
|
while buffer_idx < buffer.len() && left_in_sector > 0 {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,13 @@ pub fn open_fs(fs: &mut FileSystem) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn root_target(fs: &FileSystem) -> u32 {
|
||||||
|
#[cfg(feature = "fs_fat32")]
|
||||||
|
{
|
||||||
|
fat32::root_target(fs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn root_reader(fs: &FileSystem) -> DirectoryReader {
|
pub fn root_reader(fs: &FileSystem) -> DirectoryReader {
|
||||||
#[cfg(feature = "fs_fat32")]
|
#[cfg(feature = "fs_fat32")]
|
||||||
{
|
{
|
||||||
|
|
@ -94,8 +101,8 @@ pub fn read_one_record(fs: &FileSystem, reader: &mut DirectoryReader, record: &m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryReader) -> usize {
|
pub fn open_directory(fs: &FileSystem, path: &[u8], reader: &mut DirectoryReader) -> usize {
|
||||||
path.make_ascii_uppercase();
|
//path.make_ascii_uppercase();
|
||||||
if path[0] != b'/' {
|
if path[0] != b'/' {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
@ -104,6 +111,10 @@ pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryRe
|
||||||
*reader = root_reader;
|
*reader = root_reader;
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
|
let mut target_stack = [0u32; 128]; // todo: make this actually dynamic
|
||||||
|
target_stack[0] = root_target(fs);
|
||||||
|
let mut target_stack_idx = 1;
|
||||||
|
|
||||||
let mut record = Record {
|
let mut record = Record {
|
||||||
name: [0; 12],
|
name: [0; 12],
|
||||||
record_type: 0,
|
record_type: 0,
|
||||||
|
|
@ -111,28 +122,67 @@ pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryRe
|
||||||
total_size_bytes: 0,
|
total_size_bytes: 0,
|
||||||
};
|
};
|
||||||
let mut current_reader = root_reader;
|
let mut current_reader = root_reader;
|
||||||
let mut next_path_start = path[1..].iter().position(|&x| x == b'/').unwrap_or(path.len());
|
let mut next_path_start = path[1..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + 1;
|
||||||
let mut search_for = &path[1..next_path_start];
|
let mut search_for = &path[1..next_path_start];
|
||||||
loop {
|
'into_next_dir: loop {
|
||||||
if !read_one_record(fs, &mut current_reader, &mut record) {
|
if search_for == b"." {
|
||||||
return 1;
|
// do nothing and skip this part of the path
|
||||||
|
let new_path_start = next_path_start + 1;
|
||||||
|
if new_path_start >= path.len() {
|
||||||
|
// we're at the end of the path
|
||||||
|
*reader = current_reader;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start;
|
||||||
|
search_for = &path[new_path_start..next_path_start];
|
||||||
|
target_stack[target_stack_idx] = record.target;
|
||||||
|
target_stack_idx += 1;
|
||||||
|
continue 'into_next_dir;
|
||||||
|
} else if search_for == b".." {
|
||||||
|
// go back up
|
||||||
|
if target_stack_idx < 2 {
|
||||||
|
directory_reader(fs, &mut current_reader, target_stack[0]);
|
||||||
|
*reader = current_reader;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
target_stack_idx -= 2; // skip the current directory and the one before that
|
||||||
|
let prev_target = target_stack[target_stack_idx];
|
||||||
|
let new_path_start = next_path_start + 1;
|
||||||
|
if new_path_start >= path.len() {
|
||||||
|
// we're at the end of the path
|
||||||
|
directory_reader(fs, &mut current_reader, prev_target);
|
||||||
|
*reader = current_reader;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start;
|
||||||
|
search_for = &path[new_path_start..next_path_start];
|
||||||
|
directory_reader(fs, &mut current_reader, prev_target);
|
||||||
|
continue 'into_next_dir;
|
||||||
}
|
}
|
||||||
let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)];
|
loop {
|
||||||
if record_name == search_for {
|
if !read_one_record(fs, &mut current_reader, &mut record) {
|
||||||
// this is the next record we want
|
return 1;
|
||||||
if record.record_type == RecordType::Directory as u8 {
|
}
|
||||||
let new_path_start = next_path_start + 1; // skip the slash
|
let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)];
|
||||||
if new_path_start >= path.len() {
|
if record_name == search_for {
|
||||||
// we're at the end of the path
|
// this is the next record we want
|
||||||
|
if record.record_type == RecordType::Directory as u8 {
|
||||||
|
let new_path_start = next_path_start + 1; // skip the slash
|
||||||
|
if new_path_start >= path.len() {
|
||||||
|
// we're at the end of the path
|
||||||
|
directory_reader(fs, &mut current_reader, record.target);
|
||||||
|
*reader = current_reader;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start;
|
||||||
|
search_for = &path[new_path_start..next_path_start];
|
||||||
directory_reader(fs, &mut current_reader, record.target);
|
directory_reader(fs, &mut current_reader, record.target);
|
||||||
*reader = current_reader;
|
target_stack[target_stack_idx] = record.target;
|
||||||
return 0;
|
target_stack_idx += 1;
|
||||||
|
continue 'into_next_dir;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start;
|
|
||||||
search_for = &path[next_path_start..next_path_start];
|
|
||||||
directory_reader(fs, &mut current_reader, record.target);
|
|
||||||
} else {
|
|
||||||
return 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/main.rs
12
src/main.rs
|
|
@ -32,16 +32,6 @@ pub extern "C" fn ktask() -> ! {
|
||||||
syscalls::init_kernel();
|
syscalls::init_kernel();
|
||||||
print(b"kernel ready!\n");
|
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
|
// load TURNTBL.DDI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -50,6 +40,7 @@ pub extern "C" fn ktask() -> ! {
|
||||||
rough_panic(['f', 's', 'e']);
|
rough_panic(['f', 's', 'e']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let mut fs = liblbos::fs::FileSystem::empty();
|
let mut fs = liblbos::fs::FileSystem::empty();
|
||||||
if !fs::open_fs(unsafe { &mut *(&mut fs as *mut _ as *mut _) }) {
|
if !fs::open_fs(unsafe { &mut *(&mut fs as *mut _ as *mut _) }) {
|
||||||
fserr();
|
fserr();
|
||||||
|
|
@ -110,6 +101,7 @@ pub extern "C" fn ktask() -> ! {
|
||||||
epc: entrypoint,
|
epc: entrypoint,
|
||||||
ddi_first_addr: first_addr,
|
ddi_first_addr: first_addr,
|
||||||
ddi_size: size,
|
ddi_size: size,
|
||||||
|
environment: 0,
|
||||||
wait_for_task_exit: false,
|
wait_for_task_exit: false,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
use crate::arch::serial_port;
|
||||||
use crate::rough_panic;
|
use crate::rough_panic;
|
||||||
|
use crate::strprint::u32_hex;
|
||||||
|
|
||||||
#[cfg(feature = "arch_virt")]
|
#[cfg(feature = "arch_virt")]
|
||||||
|
|
||||||
|
|
@ -8,25 +10,32 @@ unsafe extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const BLOCK_SIZE: usize = 512;
|
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 struct MemoryManager {
|
||||||
pub heap_start: usize,
|
pub heap_start: usize,
|
||||||
pub heap_size: usize,
|
pub heap_size: usize,
|
||||||
pub blockmap: [u8; (MEM_BLOCKS+7) / 8],
|
pub blockmap: [u8; 128/8]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemoryManager {
|
impl MemoryManager {
|
||||||
#[cfg(feature = "arch_virt")]
|
#[cfg(feature = "arch_virt")]
|
||||||
pub fn init() -> Self {
|
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 {
|
Self {
|
||||||
heap_start: _heap_start as _,
|
heap_start: _heap_start as _,
|
||||||
heap_size: _heap_size as _,
|
heap_size: _heap_size as _,
|
||||||
blockmap: [0; (MEM_BLOCKS+7) / 8],
|
|
||||||
|
blockmap: [0; 16],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "arch_ppc32")]
|
#[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 {
|
pub fn alloc_one_block(&mut self) -> usize {
|
||||||
/*
|
/*
|
||||||
for (i, v) in self.blockmap.iter_mut().enumerate() {
|
for (i, v) in self.blockmap.iter_mut().enumerate() {
|
||||||
|
|
@ -64,11 +65,19 @@ impl MemoryManager {
|
||||||
*/
|
*/
|
||||||
self.alloc_n_blocks(1)
|
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));
|
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
|
// 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 {
|
pub fn alloc_n_blocks(&mut self, n: usize) -> usize {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -78,46 +87,57 @@ impl MemoryManager {
|
||||||
for i in 0..self.blockmap.len() {
|
for i in 0..self.blockmap.len() {
|
||||||
for j in 0..8 {
|
for j in 0..8 {
|
||||||
let block = i * 8 + j;
|
let block = i * 8 + j;
|
||||||
|
let valid = block < (self.heap_size / BLOCK_SIZE);
|
||||||
let val = (1 << j) & self.blockmap[i];
|
let val = (1 << j) & self.blockmap[i];
|
||||||
if val == 0 {
|
if val == 0 && valid {
|
||||||
// this is free
|
// this is free
|
||||||
self.blockmap[i] |= 1 << j;
|
self.blockmap[i] |= 1 << j;
|
||||||
if first_block.is_none() {
|
if first_block.is_none() {
|
||||||
first_block = Some(block);
|
first_block = Some(block);
|
||||||
}
|
}
|
||||||
found += 1;
|
found += 1;
|
||||||
if found >= n {
|
if found == n {
|
||||||
return first_block.unwrap();
|
let addr = (first_block.unwrap() * BLOCK_SIZE) + self.heap_start;
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
if found > n {
|
||||||
|
rough_panic(['h', 'm', '?'])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// used, restart search
|
// used, restart search
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while found > 0 {
|
while found > 0 {
|
||||||
found -= 1;
|
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;
|
i += 1;
|
||||||
}
|
}
|
||||||
first_block = None;
|
first_block = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
0
|
rough_panic(['o', 'o', 'm'])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free_n_blocks(&mut self, block: usize, n: usize) {
|
pub fn free_n_blocks(&mut self, addr: usize, n: usize) {
|
||||||
if n == 0 || block >= MEM_BLOCKS || block + n > MEM_BLOCKS {
|
if n == 0 || addr < self.heap_start || addr >= self.heap_start + self.heap_size || addr + n * BLOCK_SIZE > self.heap_start + self.heap_size {
|
||||||
return;
|
rough_panic(['b', 'm', 'f'])
|
||||||
}
|
}
|
||||||
for i in 0..n {
|
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 {
|
pub fn used_blocks(&self) -> usize {
|
||||||
let mut used_blocks = 0;
|
let mut used_blocks = 0;
|
||||||
for v in self.blockmap.iter() {
|
for (i, v) in self.blockmap.iter().enumerate() {
|
||||||
for j in 0..8 {
|
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;
|
let val = (1 << j) & *v;
|
||||||
if val != 0 {
|
if val != 0 {
|
||||||
// used
|
// used
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ use crate::arch::serial_port;
|
||||||
use crate::memory::{BLOCK_SIZE, MemoryManager};
|
use crate::memory::{BLOCK_SIZE, MemoryManager};
|
||||||
use crate::spinlock::Spinlock;
|
use crate::spinlock::Spinlock;
|
||||||
use crate::syscalls::SysCall;
|
use crate::syscalls::SysCall;
|
||||||
use crate::{arch};
|
use crate::{arch, rough_panic};
|
||||||
|
use crate::strprint::u32_hex;
|
||||||
|
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub enum TaskWait {
|
pub enum TaskWait {
|
||||||
|
|
@ -13,6 +14,8 @@ pub enum TaskWait {
|
||||||
WaitForTaskExit = 1 << 2,
|
WaitForTaskExit = 1 << 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const STACK_SIZE_IN_BLOCKS: usize = 8;
|
||||||
|
|
||||||
pub const MAX_TASKS: usize = 8;
|
pub const MAX_TASKS: usize = 8;
|
||||||
|
|
||||||
pub static TC: Spinlock<TrafficControl> = Spinlock::new(TrafficControl::empty());
|
pub static TC: Spinlock<TrafficControl> = Spinlock::new(TrafficControl::empty());
|
||||||
|
|
@ -67,33 +70,25 @@ pub fn handle_syscall(
|
||||||
let add = a1 as *mut TaskSetup;
|
let add = a1 as *mut TaskSetup;
|
||||||
|
|
||||||
tc.init_mem_if_not();
|
tc.init_mem_if_not();
|
||||||
let blockalloc = unsafe {
|
|
||||||
tc.memory_manager
|
|
||||||
.as_mut()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
.alloc_n_blocks(16)
|
|
||||||
};
|
|
||||||
let sp = unsafe {
|
let sp = unsafe {
|
||||||
tc.memory_manager
|
tc.memory_manager
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap_unchecked()
|
.unwrap_unchecked()
|
||||||
.block_to_addr(blockalloc)
|
.alloc_n_blocks(STACK_SIZE_IN_BLOCKS)
|
||||||
+ BLOCK_SIZE
|
} + (BLOCK_SIZE * STACK_SIZE_IN_BLOCKS);
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "arch_virt")]
|
#[cfg(feature = "arch_virt")]
|
||||||
let t = Some(Task {
|
let t = Some(Task {
|
||||||
epc: unsafe { (*add).epc },
|
epc: unsafe { (*add).epc },
|
||||||
|
environment: unsafe { (*add).environment },
|
||||||
want_exit: false,
|
want_exit: false,
|
||||||
sp,
|
sp,
|
||||||
wait: 0,
|
wait: 0,
|
||||||
incoming_notifications: [const { None }; MAX_TASKS],
|
incoming_notifications: [const { None }; MAX_TASKS],
|
||||||
ackwait: [0; MAX_TASKS],
|
ackwait: [0; MAX_TASKS],
|
||||||
task_wait: 0,
|
task_wait: 0,
|
||||||
ddi_mem_start_block: unsafe {
|
ddi_mem_start: unsafe {
|
||||||
tc.memory_manager
|
(*add).ddi_first_addr
|
||||||
.as_mut().unwrap_unchecked()
|
|
||||||
.addr_to_block((*add).ddi_first_addr)
|
|
||||||
},
|
},
|
||||||
ddi_mem_blocks_count: unsafe { (*add).ddi_size / BLOCK_SIZE },
|
ddi_mem_blocks_count: unsafe { (*add).ddi_size / BLOCK_SIZE },
|
||||||
trap_frame: arch::virt::tasks::setup_task(sp),
|
trap_frame: arch::virt::tasks::setup_task(sp),
|
||||||
|
|
@ -131,7 +126,7 @@ pub fn handle_syscall(
|
||||||
tc.memory_manager
|
tc.memory_manager
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap_unchecked()
|
.unwrap_unchecked()
|
||||||
.free_one_block(blockalloc);
|
.free_n_blocks(sp, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
MAX_TASKS + 1
|
MAX_TASKS + 1
|
||||||
|
|
@ -160,34 +155,22 @@ pub fn handle_syscall(
|
||||||
}
|
}
|
||||||
SysCall::AllocBlocks => {
|
SysCall::AllocBlocks => {
|
||||||
let count = a1;
|
let count = a1;
|
||||||
let block = unsafe {
|
|
||||||
tc.memory_manager
|
|
||||||
.as_mut()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
.alloc_n_blocks(count)
|
|
||||||
};
|
|
||||||
let addr = unsafe {
|
let addr = unsafe {
|
||||||
tc.memory_manager
|
tc.memory_manager
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap_unchecked()
|
.unwrap_unchecked()
|
||||||
.block_to_addr(block)
|
.alloc_n_blocks(count)
|
||||||
};
|
};
|
||||||
addr
|
addr
|
||||||
}
|
}
|
||||||
SysCall::FreeBlocks => {
|
SysCall::FreeBlocks => {
|
||||||
let addr = a1;
|
let addr = a1;
|
||||||
let count = a2;
|
let count = a2;
|
||||||
let block = unsafe {
|
|
||||||
tc.memory_manager
|
|
||||||
.as_mut()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
.addr_to_block(addr)
|
|
||||||
};
|
|
||||||
unsafe {
|
unsafe {
|
||||||
tc.memory_manager
|
tc.memory_manager
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap_unchecked()
|
.unwrap_unchecked()
|
||||||
.free_n_blocks(block, count)
|
.free_n_blocks(addr, count)
|
||||||
};
|
};
|
||||||
|
|
||||||
0
|
0
|
||||||
|
|
@ -200,6 +183,14 @@ pub fn handle_syscall(
|
||||||
core::slice::from_raw_parts(addr as *const u8, count)
|
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
|
0
|
||||||
}
|
}
|
||||||
SysCall::ReadHBD => {
|
SysCall::ReadHBD => {
|
||||||
|
|
@ -225,13 +216,49 @@ pub fn handle_syscall(
|
||||||
}
|
}
|
||||||
SysCall::InitKernel => {
|
SysCall::InitKernel => {
|
||||||
tc.init_mem_if_not();
|
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);
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut fbcons = crate::dev::framebuffer::console::FBCONSOLE.lock();
|
||||||
|
fbcons.clear_terminal(&mut tc);
|
||||||
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
SysCall::SendNotification => {
|
SysCall::SendNotification => {
|
||||||
let taskid = a1;
|
let taskid = a1;
|
||||||
let addr = a2;
|
let addr = a2;
|
||||||
let addr_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(addr) };
|
|
||||||
let ci = tc.current;
|
let ci = tc.current;
|
||||||
if let Some(Some(task)) = tc.tasks.get_mut(taskid).as_mut() {
|
if let Some(Some(task)) = tc.tasks.get_mut(taskid).as_mut() {
|
||||||
let waiting = task.wait & TaskWait::WaitForNotification as u32 != 0;
|
let waiting = task.wait & TaskWait::WaitForNotification as u32 != 0;
|
||||||
|
|
@ -248,7 +275,7 @@ pub fn handle_syscall(
|
||||||
current_task.ackwait[taskid] = 1;
|
current_task.ackwait[taskid] = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
task.incoming_notifications[ci] = Some(addr_block); // queue it for them
|
task.incoming_notifications[ci] = Some(addr); // queue it for them
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -261,15 +288,9 @@ pub fn handle_syscall(
|
||||||
suspend = true;
|
suspend = true;
|
||||||
if let Some(task) = tc.tasks[ci].as_mut() {
|
if let Some(task) = tc.tasks[ci].as_mut() {
|
||||||
// is there already a pending notification?
|
// is there already a pending notification?
|
||||||
for (i, block) in task.incoming_notifications.iter_mut().enumerate() {
|
for (i, addr) in task.incoming_notifications.iter_mut().enumerate() {
|
||||||
if let Some(block) = block.take() {
|
if let Some(addr) = addr.take() {
|
||||||
// yes, there is
|
// 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 let Some(sender_task) = tc.tasks[i].as_mut() {
|
||||||
if sender_task.ackwait[ci] == 3 { // they are waiting for us to ack
|
if sender_task.ackwait[ci] == 3 { // they are waiting for us to ack
|
||||||
sender_task.ackwait[ci] = 0;
|
sender_task.ackwait[ci] = 0;
|
||||||
|
|
@ -319,6 +340,39 @@ pub fn handle_syscall(
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
SysCall::EnvironmentPointer => {
|
||||||
|
let ci = tc.current;
|
||||||
|
if let Some(task) = tc.tasks[ci].as_ref() {
|
||||||
|
task.environment
|
||||||
|
} else {
|
||||||
|
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,
|
suspend,
|
||||||
)
|
)
|
||||||
|
|
@ -336,24 +390,18 @@ pub fn context_switch<'a>(tc: &'a mut TrafficControl, current: Task) -> Option<&
|
||||||
if want_exit {
|
if want_exit {
|
||||||
tc.init_mem_if_not();
|
tc.init_mem_if_not();
|
||||||
let sp = tc.tasks[i].as_ref().map(|v| v.sp).unwrap_or(0);
|
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);
|
let ddi_blocks_count = tc.tasks[i].as_ref().map(|v| v.ddi_mem_blocks_count).unwrap_or(0);
|
||||||
if sp != 0 {
|
if sp != 0 {
|
||||||
let stackblock = unsafe {
|
|
||||||
tc.memory_manager
|
|
||||||
.as_mut()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
.addr_to_block(sp - BLOCK_SIZE)
|
|
||||||
};
|
|
||||||
unsafe {
|
unsafe {
|
||||||
tc.memory_manager
|
tc.memory_manager
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap_unchecked()
|
.unwrap_unchecked()
|
||||||
.free_n_blocks(stackblock, 4)
|
.free_n_blocks(sp - (BLOCK_SIZE * STACK_SIZE_IN_BLOCKS), STACK_SIZE_IN_BLOCKS)
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
tc.memory_manager.as_mut().unwrap_unchecked()
|
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;
|
tc.tasks[i] = None;
|
||||||
|
|
@ -408,11 +456,13 @@ pub struct TrafficControl {
|
||||||
pub inbuf_read: u8,
|
pub inbuf_read: u8,
|
||||||
pub inbuf_write: u8,
|
pub inbuf_write: u8,
|
||||||
pub hung_system: bool,
|
pub hung_system: bool,
|
||||||
|
pub use_fb_console: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
#[cfg(feature = "arch_virt")]
|
#[cfg(feature = "arch_virt")]
|
||||||
pub epc: usize,
|
pub epc: usize,
|
||||||
|
pub environment: usize,
|
||||||
pub want_exit: bool,
|
pub want_exit: bool,
|
||||||
/// THE ORIGINAL STACK POINTER THAT THE TASK STARTED WITH
|
/// THE ORIGINAL STACK POINTER THAT THE TASK STARTED WITH
|
||||||
pub sp: usize,
|
pub sp: usize,
|
||||||
|
|
@ -420,7 +470,7 @@ pub struct Task {
|
||||||
pub incoming_notifications: [Option<usize>; MAX_TASKS],
|
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 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 task_wait: u8,
|
||||||
pub ddi_mem_start_block: usize,
|
pub ddi_mem_start: usize,
|
||||||
pub ddi_mem_blocks_count: usize,
|
pub ddi_mem_blocks_count: usize,
|
||||||
#[cfg(feature = "arch_virt")]
|
#[cfg(feature = "arch_virt")]
|
||||||
pub trap_frame: arch::virt::trap::TrapFrame,
|
pub trap_frame: arch::virt::trap::TrapFrame,
|
||||||
|
|
@ -439,6 +489,7 @@ impl TrafficControl {
|
||||||
inbuf_read: 0,
|
inbuf_read: 0,
|
||||||
inbuf_write: 0,
|
inbuf_write: 0,
|
||||||
hung_system: false,
|
hung_system: false,
|
||||||
|
use_fb_console: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
// todo: this should probably be in the dev module
|
||||||
|
|
||||||
pub trait Serial {
|
pub trait Serial {
|
||||||
fn put(&self, c: u8);
|
fn put(&self, c: u8);
|
||||||
fn putstr(&self, s: &str);
|
fn putstr(&self, s: &str);
|
||||||
|
|
|
||||||
4
turntable/Cargo.lock
generated
4
turntable/Cargo.lock
generated
|
|
@ -4,11 +4,11 @@ version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "liblbos"
|
name = "liblbos"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "turntable"
|
name = "turntable"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"liblbos",
|
"liblbos",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "turntable"
|
name = "turntable"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
|
||||||
use crate::terminal::{print, println};
|
use crate::terminal::{print, println};
|
||||||
use liblbos::fs::{DirectoryReader, FileSystem};
|
use liblbos::fs::{DirectoryReader, FileSystem};
|
||||||
|
|
||||||
static TEST: &[u8] = b"test";
|
static NO_EXIT: AtomicBool = AtomicBool::new(false);
|
||||||
|
static CURRENT_DIR_PATH: AtomicPtr<u8> = AtomicPtr::new(0 as *mut _);
|
||||||
|
static CURRENT_DIR_PATH_LEN: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
struct Environment<'a> {
|
||||||
|
pub current_directory_path: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
|
@ -12,14 +19,83 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lsdir() {
|
fn cd(new_path: &str, environment: &mut Environment<'_>) {
|
||||||
|
let old_path = environment.current_directory_path;
|
||||||
|
let abs_new_path = if new_path.starts_with('/') {
|
||||||
|
// absolute path, replaces old path
|
||||||
|
let new_path_buf = liblbos::syscalls::alloc_blocks(new_path.len().div_ceil(512));
|
||||||
|
let new_path_buf = unsafe { core::slice::from_raw_parts_mut(new_path_buf as *mut _, new_path.len()) };
|
||||||
|
// copy
|
||||||
|
{
|
||||||
|
for (i, &x) in new_path.as_bytes().iter().enumerate() {
|
||||||
|
new_path_buf[i] = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_path_buf
|
||||||
|
} else {
|
||||||
|
// todo: ".." is currently handled on the ktask level, however...
|
||||||
|
// todo: we should evaluate this and instead modify the absolute path to remove the ".."
|
||||||
|
// todo: otherwise, we're bound to end up with a path like "/a/b/../../c" which is not nice
|
||||||
|
// relative path, append
|
||||||
|
let new_path_len = old_path.len() + new_path.len() + 1;
|
||||||
|
let new_path_buf = liblbos::syscalls::alloc_blocks(new_path_len.div_ceil(512));
|
||||||
|
let new_path_buf = unsafe { core::slice::from_raw_parts_mut(new_path_buf as *mut _, new_path_len) };
|
||||||
|
// copy old path
|
||||||
|
{
|
||||||
|
for (i, &x) in old_path.iter().enumerate() {
|
||||||
|
new_path_buf[i] = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// copy new path
|
||||||
|
{
|
||||||
|
for (i, &x) in new_path.as_bytes().iter().enumerate() {
|
||||||
|
new_path_buf[old_path.len() + i] = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add separator
|
||||||
|
new_path_buf[old_path.len() + new_path.len()] = b'/';
|
||||||
|
|
||||||
|
new_path_buf
|
||||||
|
};
|
||||||
|
|
||||||
|
// check to make sure the new path is valid
|
||||||
|
{
|
||||||
|
let mut fs = FileSystem::empty();
|
||||||
|
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
|
||||||
|
println("failed to open fs");
|
||||||
|
// free
|
||||||
|
liblbos::syscalls::free_blocks(abs_new_path.as_ptr() as usize, abs_new_path.len().div_ceil(512));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut dir = DirectoryReader::empty();
|
||||||
|
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, abs_new_path) != 0 {
|
||||||
|
println("invalid dir");
|
||||||
|
// free
|
||||||
|
liblbos::syscalls::free_blocks(abs_new_path.as_ptr() as usize, abs_new_path.len().div_ceil(512));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// it is valid, update everything
|
||||||
|
{
|
||||||
|
// free old path
|
||||||
|
liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512));
|
||||||
|
}
|
||||||
|
CURRENT_DIR_PATH.store(abs_new_path.as_ptr() as *mut _, Ordering::Relaxed);
|
||||||
|
CURRENT_DIR_PATH_LEN.store(abs_new_path.len(), Ordering::Relaxed);
|
||||||
|
environment.current_directory_path = unsafe {
|
||||||
|
core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut _, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lsdir(env: &Environment<'_>) {
|
||||||
let mut fs = FileSystem::empty();
|
let mut fs = FileSystem::empty();
|
||||||
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
|
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
|
||||||
println("failed to open fs");
|
println("failed to open fs");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut dir = DirectoryReader::empty();
|
let mut dir = DirectoryReader::empty();
|
||||||
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, b"/") != 0 {
|
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, env.current_directory_path) != 0 {
|
||||||
println("failed to open dir");
|
println("failed to open dir");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -35,20 +111,20 @@ fn lsdir() {
|
||||||
println("unexpected eod");
|
println("unexpected eod");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
print(" - ");
|
print("- ");
|
||||||
liblbos::syscalls::write_terminal(
|
liblbos::syscalls::write_terminal(
|
||||||
&record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)],
|
&record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)],
|
||||||
);
|
);
|
||||||
if record.record_type == liblbos::fs::RecordType::Directory as u8 {
|
if record.record_type == liblbos::fs::RecordType::Directory as u8 {
|
||||||
print(" (dir)");
|
print("(dir)");
|
||||||
} else {
|
} else {
|
||||||
print(" (file)");
|
print("(file)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print("\n");
|
print("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attempt_run_file(name: &str) {
|
fn attempt_run_file(env: &Environment<'_>, name: &str) {
|
||||||
print("\n");
|
print("\n");
|
||||||
if name.len() > 11 {
|
if name.len() > 11 {
|
||||||
println("filename too long");
|
println("filename too long");
|
||||||
|
|
@ -61,7 +137,7 @@ fn attempt_run_file(name: &str) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut dir = DirectoryReader::empty();
|
let mut dir = DirectoryReader::empty();
|
||||||
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, b"/") != 0 {
|
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, env.current_directory_path) != 0 {
|
||||||
println("failed to open dir");
|
println("failed to open dir");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -102,10 +178,17 @@ fn attempt_run_file(name: &str) {
|
||||||
if liblbos::ktask::ktask_fsreadfile(&fs, &mut reader, unsafe { core::slice::from_raw_parts_mut(buf as *mut _, size) }) != 0 {
|
if liblbos::ktask::ktask_fsreadfile(&fs, &mut reader, unsafe { core::slice::from_raw_parts_mut(buf as *mut _, size) }) != 0 {
|
||||||
println("failed to read file");
|
println("failed to read file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let e = liblbos::Environment {
|
||||||
|
current_directory_path: CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize,
|
||||||
|
current_directory_path_len: CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed),
|
||||||
|
};
|
||||||
|
|
||||||
let mut task_setup = liblbos::TaskSetup {
|
let mut task_setup = liblbos::TaskSetup {
|
||||||
epc: 0,
|
epc: 0,
|
||||||
ddi_first_addr: 0,
|
ddi_first_addr: 0,
|
||||||
ddi_size: 0,
|
ddi_size: 0,
|
||||||
|
environment: &e as *const _ as usize,
|
||||||
wait_for_task_exit: true,
|
wait_for_task_exit: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -117,22 +200,40 @@ fn attempt_run_file(name: &str) {
|
||||||
|
|
||||||
liblbos::syscalls::free_blocks(buf, size.div_ceil(512));
|
liblbos::syscalls::free_blocks(buf, size.div_ceil(512));
|
||||||
|
|
||||||
let task_id = liblbos::syscalls::create_task(task_setup);
|
liblbos::syscalls::create_task(task_setup);
|
||||||
|
|
||||||
|
// yeah we're extending its lifetime
|
||||||
|
#[allow(clippy::drop_non_drop)]
|
||||||
|
drop(e);
|
||||||
|
|
||||||
println("\ntask exited\n");
|
println("\ntask exited\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(cmd: &str) {
|
fn execute(cmd: &str, environment: &mut Environment<'_>) {
|
||||||
print("\n");
|
print("\n");
|
||||||
let mut split = cmd.split_ascii_whitespace();
|
let mut split = cmd.split_ascii_whitespace();
|
||||||
|
|
||||||
if let Some(cmd) = split.next() {
|
if let Some(cmd) = split.next() {
|
||||||
match cmd {
|
match cmd {
|
||||||
"ls" => {
|
"ls" => {
|
||||||
lsdir();
|
lsdir(environment);
|
||||||
|
}
|
||||||
|
"cd" => {
|
||||||
|
if let Some(path) = split.next() {
|
||||||
|
cd(path, environment);
|
||||||
|
} else {
|
||||||
|
println("cd requires a path");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"exit" => {
|
||||||
|
if NO_EXIT.load(Ordering::Relaxed) {
|
||||||
|
println("cannot exit as task 1");
|
||||||
|
} else {
|
||||||
|
liblbos::syscalls::exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
attempt_run_file(cmd);
|
attempt_run_file(environment, cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,13 +258,50 @@ mod terminal;
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn main() {
|
extern "C" fn main() {
|
||||||
|
let tid = liblbos::syscalls::current_task();
|
||||||
|
NO_EXIT.store(tid == 1, Ordering::Relaxed);
|
||||||
|
|
||||||
|
let environment = liblbos::syscalls::environment_pointer();
|
||||||
|
let mut environment = if environment == 0 {
|
||||||
|
// no environment, construct it ourself
|
||||||
|
static DEFAULT_CURRENT_DIRECTORY: &[u8] = b"/";
|
||||||
|
let environment = Environment {
|
||||||
|
current_directory_path: DEFAULT_CURRENT_DIRECTORY,
|
||||||
|
};
|
||||||
|
environment
|
||||||
|
} else {
|
||||||
|
let env = unsafe { &mut *(environment as *mut liblbos::Environment) };
|
||||||
|
let current_directory_path = unsafe { core::slice::from_raw_parts(env.current_directory_path as *const u8, env.current_directory_path_len) };
|
||||||
|
let environment = Environment {
|
||||||
|
current_directory_path,
|
||||||
|
};
|
||||||
|
environment
|
||||||
|
};
|
||||||
|
// replace the current path with one that we allocate ourself, so that we can modify it later
|
||||||
|
{
|
||||||
|
let path = environment.current_directory_path;
|
||||||
|
let len = environment.current_directory_path.len();
|
||||||
|
CURRENT_DIR_PATH.store(liblbos::syscalls::alloc_blocks(len.div_ceil(512)) as *mut u8, Ordering::Relaxed);
|
||||||
|
CURRENT_DIR_PATH_LEN.store(len, Ordering::Relaxed);
|
||||||
|
{
|
||||||
|
// copy
|
||||||
|
let dest = unsafe {
|
||||||
|
core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut _, len)
|
||||||
|
};
|
||||||
|
for (i, &x) in path.iter().enumerate() {
|
||||||
|
dest[i] = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
environment.current_directory_path = unsafe { core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut u8, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed)) };
|
||||||
|
}
|
||||||
|
|
||||||
print("\n\n");
|
print("\n\n");
|
||||||
print("turntable v");
|
print("turntable v");
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
print(VERSION);
|
print(VERSION);
|
||||||
print("\n");
|
print("\n");
|
||||||
|
|
||||||
println("(c) 2025 Real Microsoft, LLC");
|
println("(c) 2025\nReal Microsoft, LLC");
|
||||||
|
|
||||||
print("> ");
|
print("> ");
|
||||||
|
|
||||||
|
|
@ -181,19 +319,18 @@ extern "C" fn main() {
|
||||||
if *c == 0x7f && cmdbuf_len > 0 {
|
if *c == 0x7f && cmdbuf_len > 0 {
|
||||||
cmdbuf_len -= 1;
|
cmdbuf_len -= 1;
|
||||||
print("\x08 \x08");
|
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[cmdbuf_len] = *c;
|
||||||
cmdbuf_len += 1;
|
cmdbuf_len += 1;
|
||||||
if *c == b'\r' {
|
liblbos::syscalls::write_terminal(&[*c]);
|
||||||
execute(unsafe {
|
|
||||||
core::str::from_utf8_unchecked(&cmdbuf[0..cmdbuf_len - 1])
|
|
||||||
});
|
|
||||||
cmdbuf_len = 0;
|
|
||||||
print("> ");
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
liblbos::syscalls::write_terminal(&[*c]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue