Sync computers

This commit is contained in:
2026-03-17 18:29:00 +01:00
parent 404a681254
commit 56a00d0403
34 changed files with 2578 additions and 257 deletions

2
src/drivers.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod keyboard;
pub mod mouse;

67
src/drivers/keyboard.rs Normal file
View File

@@ -0,0 +1,67 @@
use crate::{
keymap::{KeyType, ModifierType, map_keycode},
tty::TTY0,
virtio::{
Virtqueue,
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver},
},
virtual_fs::{FILE_SYSTEM, VirtualFileSystem},
};
#[derive(Debug, Clone, Copy, Default)]
pub struct KeyboardState {
// ctrl_modifier: bool,
pub shift_modifier: bool,
pub alt_gr_modifier: bool,
}
impl KeyboardState {
pub const fn new() -> Self {
Self {
// ctrl_modifier: false,
shift_modifier: false,
alt_gr_modifier: false,
}
}
}
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
pub static mut KBD_DRIVER: VirtioPciDriver<KeyboardState> = unsafe {
VirtioPciDriver::new(
|state, event| {
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
kbd_buffer
.write(core::mem::transmute::<
&VirtioInputEvent,
&[u8; size_of::<VirtioInputEvent>()],
>(event))
.unwrap();
if event.is_key() {
let event = event.as_key_event();
#[allow(clippy::single_match)]
match map_keycode(event.code, state) {
KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => {
let mut buf = [0; 4];
let to_send = c.encode_utf8(&mut buf);
for c in to_send.as_bytes() {
TTY0.buffer.borrow_mut().push(*c);
}
}
KeyType::Modifier(ModifierType::Shift) => {
state.shift_modifier = !state.shift_modifier;
}
KeyType::Modifier(ModifierType::AltGr) => {
state.alt_gr_modifier = !state.alt_gr_modifier;
}
_ => {}
}
// println!("event: {:#?}", event);
} else {
// println!("key pressed, {:#?}", event);
}
},
KeyboardState::new(),
&mut KBD_QUEUE,
)
};

38
src/drivers/mouse.rs Normal file
View File

@@ -0,0 +1,38 @@
use crate::{
cursor::{clear_cursor, draw_cursor},
vga::Vga,
virtio::{self, Virtqueue, input::VirtioPciDriver},
};
pub static mut MOUSE_POSITION: (u16, u16) = (0, 0);
static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe {
VirtioPciDriver::new(
|_, event| {
if event.is_relative() {
let event = event.as_relative_event();
clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
match event.code {
virtio::input::EventCodeRelative::X => {
MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16
}
virtio::input::EventCodeRelative::Y => {
MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16
}
_ => {}
}
draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
// println!("mouse moved relatively, {:#?}", event);
} else {
// println!("mouse moved, {:#?}", event);
}
},
(),
&mut MOUSE_QUEUE,
)
};

View File

@@ -12,7 +12,7 @@ use io::{IoBase, Read, Seek, Write};
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
const DISK_ADDR: *const u8 = 0x9000_0000 as *const _;
const DISK_ADDR: *const u8 = 0xA000_0000 as *const _;
#[derive(Debug)]
/// Simple disk backend that reads from a fixed memory region.

View File

@@ -9,9 +9,9 @@ use log::info;
use shared::syscall::SysCall;
use crate::{
KBD_DRIVER, MOUSE_DRIVER,
boot::sbi::{ExtensionID, TimerFunctionID},
clear_csr,
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
process::{ExecutionContext, exit_process, sleep},
read_csr,
riscv::{disable_interrupt, dump_cpu},
@@ -160,6 +160,15 @@ unsafe extern "C" fn supervisor_trap_handler(
unsafe { (*interrupt_state).a[0] = fd as u64 };
}
SysCall::Close => {
let fd = a1;
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process();
let mut vnode = current_process.fd_table[fd as usize].take().unwrap();
vnode.close();
}
SysCall::Write => {
let fd = a1;
let buf =
@@ -168,7 +177,8 @@ unsafe extern "C" fn supervisor_trap_handler(
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process();
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
vnode.write(buf).unwrap();
let res = vnode.write(buf).unwrap();
unsafe { (*interrupt_state).a[0] = res as u64 };
}
SysCall::Read => {
let fd = a1;
@@ -182,6 +192,8 @@ unsafe extern "C" fn supervisor_trap_handler(
if res == 0 && !buf.is_empty() {
loop_syscall(interrupt_state);
scheduler.schedule(&mut interrupt_state);
} else {
unsafe { (*interrupt_state).a[0] = res as u64 };
}
}
SysCall::Seek => {
@@ -197,6 +209,17 @@ unsafe extern "C" fn supervisor_trap_handler(
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
vnode.seek(seek).unwrap();
}
SysCall::Spawn => {
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
let mut scheduler = SCHEDULER.lock();
scheduler.create_process_from_file(path, 0, core::ptr::null());
}
SysCall::ExecVE => {
// let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
// let mut scheduler = SCHEDULER.lock();
// scheduler.create_process_from_file(path, &[]);
unimplemented!("ExecVE is not implemented")
}
SysCall::Alloc => {
let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap();
// Allocate memory and put the pointer in a0

View File

@@ -1,4 +1,4 @@
use crate::KeyboardState;
use crate::drivers::keyboard::KeyboardState;
#[derive(Debug, Clone, Copy)]
#[derive_const(PartialEq, Eq)]

View File

@@ -12,7 +12,8 @@
arbitrary_self_types_pointers,
derive_const,
const_cmp,
const_trait_impl
const_trait_impl,
trait_alias
)]
use core::sync::atomic::AtomicBool;
@@ -22,20 +23,15 @@ use embedded_alloc::LlffHeap as Heap;
use log::info;
use crate::{
cursor::{clear_cursor, draw_cursor},
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
io::init_log,
keymap::{KeyType, ModifierType, map_keycode},
pci::{PciDeviceIterator, scan_virtio_devices},
pci::scan_virtio_devices,
riscv::enable_supervisor_interrupt,
scheduler::{SCHEDULER, idle},
tty::TTY0,
user::{proc2, test},
vga::Vga,
virtio::{
Virtqueue,
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver, init_plic_pci},
},
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, init_file_system},
virtio::input::init_plic_pci,
virtual_fs::init_file_system,
};
extern crate alloc;
@@ -44,6 +40,7 @@ mod critical_section;
mod cursor;
mod data_structures;
mod draw;
mod drivers;
mod fs;
mod interrupt;
mod io;
@@ -75,97 +72,6 @@ const _: () = assert!(core::mem::size_of::<usize>() == core::mem::size_of::<u64>
#[cfg(not(target_endian = "little"))]
compile_error! {"This kernel implementation assume endianness is little-endian. Some memory access like PCI could not work in big-endian."}
#[derive(Debug, Clone, Copy, Default)]
pub struct KeyboardState {
// ctrl_modifier: bool,
pub shift_modifier: bool,
pub alt_gr_modifier: bool,
}
impl KeyboardState {
pub const fn new() -> Self {
Self {
// ctrl_modifier: false,
shift_modifier: false,
alt_gr_modifier: false,
}
}
}
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
pub static mut KBD_DRIVER: VirtioPciDriver<KeyboardState> = unsafe {
VirtioPciDriver::new(
|state, event| {
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
kbd_buffer
.write(core::mem::transmute::<
&VirtioInputEvent,
&[u8; size_of::<VirtioInputEvent>()],
>(event))
.unwrap();
if event.is_key() {
let event = event.as_key_event();
#[allow(clippy::single_match)]
match map_keycode(event.code, state) {
KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => {
let mut buf = [0; 4];
let to_send = c.encode_utf8(&mut buf);
for c in to_send.as_bytes() {
TTY0.buffer.borrow_mut().push(*c);
}
}
KeyType::Modifier(ModifierType::Shift) => {
state.shift_modifier = !state.shift_modifier;
}
KeyType::Modifier(ModifierType::AltGr) => {
state.alt_gr_modifier = !state.alt_gr_modifier;
}
_ => {}
}
println!("event: {:#?}", event);
} else {
// println!("key pressed, {:#?}", event);
}
},
KeyboardState::new(),
&mut KBD_QUEUE,
)
};
pub static mut MOUSE_POSITION: (u16, u16) = (0, 0);
static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe {
VirtioPciDriver::new(
|_, event| {
if event.is_relative() {
let event = event.as_relative_event();
clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
match event.code {
virtio::input::EventCodeRelative::X => {
MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16
}
virtio::input::EventCodeRelative::Y => {
MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16
}
_ => {}
}
draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
// println!("mouse moved relatively, {:#?}", event);
} else {
// println!("mouse moved, {:#?}", event);
}
},
(),
&mut MOUSE_QUEUE,
)
};
#[unsafe(no_mangle)]
pub extern "C" fn supervisor_mode_entry() {
unsafe {
@@ -180,19 +86,20 @@ pub extern "C" fn supervisor_mode_entry() {
info!("Hello World !");
// unsafe { Vga.draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
SCHEDULER.lock().create_process(Box::new(test), "proc1");
SCHEDULER.lock().create_process(Box::new(proc2), "proc2");
// let binding = Box::leak(Box::new(["coucou".as_bytes()]));
SCHEDULER
.lock()
.create_process(Box::new(test), "proc1", 0, core::ptr::null());
SCHEDULER
.lock()
.create_process(Box::new(proc2), "proc2", 0, core::ptr::null());
SCHEDULER
.lock()
.create_process_from_file("/usr/bin/test_pic");
.create_process_from_file("/usr/bin/test_pic", 0, core::ptr::null());
enable_supervisor_interrupt();
for pci in PciDeviceIterator::new() {
println!("{:x?}", pci.vendor_and_device_id())
}
unsafe {
let (pci_keyboard, pci_mouse) = scan_virtio_devices();
let (pci_keyboard, pci_mouse) = (pci_keyboard.unwrap(), pci_mouse.unwrap());
@@ -214,5 +121,5 @@ pub extern "C" fn supervisor_mode_entry() {
init_plic_pci(pci_keyboard.irq);
init_plic_pci(pci_mouse.irq);
}
idle();
idle(0, core::ptr::null());
}

View File

@@ -19,7 +19,6 @@ use crate::{
riscv::SStatus,
scheduler::{ACTIVE_PID, SCHEDULER, Scheduler},
time::elapsed_time_since_startup,
tty::TTY0,
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode},
};
@@ -66,6 +65,9 @@ pub struct ExecutionContext {
pub mstatus: u64,
}
pub trait ProcessEntryTrait = Fn(isize, *const *const u8);
pub type ProcessEntry = dyn ProcessEntryTrait;
/// Represents a process in the system.
///
/// Each process has its own execution context, stack,
@@ -78,13 +80,13 @@ pub struct Process {
/// Current state of the process.
pub state: ProcessState,
/// Optional entry point for the process code.
pub entry: Option<Box<dyn Fn()>>,
pub entry: Option<Box<ProcessEntry>>,
/// Wake time for sleeping processes.
pub wake_time: Duration,
/// Saved execution context.
pub ctx: ExecutionContext,
/// Process stack.
pub stack: [u64; STACK_SIZE],
pub stack: Box<[u64; STACK_SIZE]>,
/// File descriptor table.
pub fd_table: Vec<Option<Box<dyn VirtualNode>>>,
}
@@ -109,7 +111,7 @@ impl Default for Process {
mepc: core::ptr::null(),
mstatus: 0,
},
stack: [0; _],
stack: unsafe { Box::new_zeroed().assume_init() },
entry: None,
fd_table: Vec::new(),
}
@@ -150,7 +152,12 @@ impl Scheduler {
/// Attempts to open `path`, load its contents into memory and create a new
/// kernel process that will execute the loaded binary. Returns the PID of the
/// created process, or -1 on failure.
pub fn create_process_from_file<T: AsRef<Path>>(&mut self, path: T) -> i64 {
pub fn create_process_from_file<T: AsRef<Path>>(
&mut self,
path: T,
argc: isize,
argv: *const *const u8,
) -> i64 {
let path = path.as_ref();
let name = path.as_str();
@@ -231,25 +238,30 @@ impl Scheduler {
// Entry point
let entry_va = gelf.entry;
let entry_addr = unsafe { base.add((entry_va - min_vaddr) as usize) } as *const u8;
let entry_fn =
unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(entry_addr) };
let wrapper = Box::new(move || {
entry_fn();
});
let entry_fn = unsafe {
core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>(
entry_addr,
)
};
let wrapper = move |argc, argv| {
entry_fn(argc, argv);
};
println!("Program loaded at : {:x?}", entry_addr);
return self.create_process(wrapper, name);
return self.create_process(wrapper, name, argc, argv);
}
}
// Fallback: treat the file as a raw binary blob and execute in-place
let entry_point = unsafe {
core::mem::transmute::<*const u8, extern "C" fn()>(Vec::leak(content).as_ptr())
core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>(
Vec::leak(content).as_ptr(),
)
};
let wrapper = move |argc, argv| {
entry_point(argc, argv);
};
let wrapper = Box::new(move || {
entry_point();
});
self.create_process(wrapper, name)
self.create_process(wrapper, name, argc, argv)
}
/// Creates a new process with the specified code and name.
@@ -271,39 +283,48 @@ impl Scheduler {
///
/// The provided `code` function will be executed when the process is first
/// scheduled. Returns the new PID, or -1 if the process table is full.
pub fn create_process<T: Into<String>, F: Fn() + 'static + Send>(
pub fn create_process<T: Into<String>, F: ProcessEntryTrait + 'static + Send>(
&mut self,
code: Box<F>,
code: F,
name: T,
argc: isize,
argv: *const *const u8,
) -> i64 {
// SAFETY: Initializing process in the global table.
// Access is safe because we verified bounds and found a Dead slot.
unsafe {
self.process_table
.insert(self.next_pid, Box::new(Process::default()));
self.process_table.insert(self.next_pid, Process::default());
let process = self.process_table.get_mut(&self.next_pid).unwrap();
// Configure process metadata
process.pid = self.next_pid as i64;
process.name = name.into();
process.state = ProcessState::Activable;
process.entry = Some(code);
process.entry = Some(Box::new(code));
process.fd_table = Vec::new();
FILE_SYSTEM.mount(
format!("/proc/{}/0", process.pid).into(),
Box::new(TTY0.clone()),
);
// FILE_SYSTEM.mount(
// format!("/proc/{}/0", process.pid).into(),
// Box::new(TTY0.clone()),
// );
// FD 0
process.fd_table.push(Some(
FILE_SYSTEM
.open(format!("/proc/{}/0", process.pid).as_ref())
.unwrap(),
));
process
.fd_table
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
// FD 1
process
.fd_table
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
// FD 2
process
.fd_table
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
// Configure execution context
// a0 contains the pointer to the function to execute
process.ctx.a[0] = &raw const *process.entry.as_ref().unwrap_unchecked() as u64;
process.ctx.a[1] = argc as u64;
process.ctx.a[2] = argv as u64;
// mepc points to process_launcher which will call the function
process.ctx.mepc = process_launcher as *const _;
@@ -338,10 +359,15 @@ impl Scheduler {
/// This function is installed into the process `mepc` so that when the new
/// process is scheduled it will run this launcher which calls the user
/// function and ensures the process exits cleanly.
extern "C" fn process_launcher(code: *const Box<dyn Fn()>) {
#[allow(improper_ctypes_definitions)]
extern "C" fn process_launcher(
code: *const Box<ProcessEntry>,
argc: isize,
argv: *const *const u8,
) {
// SAFETY: The code pointer was initialized in create_process
// and points to a valid function.
unsafe { (*code)() };
unsafe { (*code)(argc, argv) };
// If user code didn't exit explicitly, call exit() to clean up the process
exit();

View File

@@ -17,7 +17,7 @@ use crate::{
#[derive(Debug)]
pub struct Scheduler {
pub next_pid: u64,
pub process_table: BTreeMap<u64, Box<Process>>,
pub process_table: BTreeMap<u64, Process>,
}
pub static ACTIVE_PID: AtomicU64 = AtomicU64::new(0);
@@ -30,7 +30,7 @@ pub static SCHEDULER: Mutex<LazyCell<Scheduler>> = Mutex::new(LazyCell::new(|| S
/// Idle loop executed when there is no runnable process.
///
/// Uses the `wfi` instruction to yield the CPU while waiting for interrupts.
pub fn idle() {
pub fn idle(_argc: isize, _argv: *const *const u8) {
loop {
// write_string_temp("idle");
// info!("idle");
@@ -47,7 +47,7 @@ impl Scheduler {
/// it as the active process.
pub fn init(&mut self) {
info!("scheduler init");
self.create_process(Box::new(idle), "idle");
self.create_process(Box::new(idle), "idle", 0, core::ptr::null());
self.process_table.get_mut(&0).unwrap().state = ProcessState::Active;
}
@@ -69,9 +69,7 @@ impl Scheduler {
}
}
let mut current_process_iter = self
.process_table
.range_mut((Bound::Excluded(prev_pid), Bound::Unbounded));
let mut current_process_iter = self.process_table.range_mut(prev_pid + 1..);
ACTIVE_PID.store(
loop {

View File

@@ -6,14 +6,15 @@ use core::time::Duration;
use shared::syscall::{sleep, write_string_temp};
pub fn test() {
pub fn test(_argc: isize, _argv: *const *const u8) {
loop {
// write_string_temp(str::from_utf8(_args[0]).unwrap());
write_string_temp("test");
sleep(Duration::new(2, 0));
}
}
pub fn proc2() {
pub fn proc2(_argc: isize, _argv: *const *const u8) {
loop {
write_string_temp("proc2");
sleep(Duration::new(3, 0));

View File

@@ -22,6 +22,7 @@ pub struct VirtioPciDriver<S, F: Fn(&mut S, &VirtioInputEvent) = fn(&mut S, &Vir
}
#[repr(u16)]
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventType {
Sync = 0,
@@ -31,6 +32,7 @@ pub enum EventType {
}
#[repr(u16)]
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventCodeRelative {
X = 0,
@@ -39,6 +41,7 @@ pub enum EventCodeRelative {
}
#[repr(u16)]
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventCodeValue {
Released = 0,

View File

@@ -3,7 +3,6 @@ use io::SeekFrom;
use crate::{
draw::{Color, Draw, FONT_HEIGHT, FONT_WIDTH},
println,
vga::{self, Vga},
virtual_fs::{self, FILE_SYSTEM, VirtualFileSystem},
};
@@ -41,7 +40,6 @@ impl VirtualConsole {
}
pub fn write_char(&mut self, c: char) {
println!("char_console: {:?}", c as u64);
let mut last_cursor = self.cursor;
match c {
'\n' => {

View File

@@ -9,6 +9,7 @@ use hashbrown::HashMap;
use io::{IoBase, Read, Seek, Write};
pub mod keyboard;
pub mod null;
pub mod stdin;
pub mod virtual_stdin;
@@ -16,10 +17,12 @@ use crate::{
fs::Disk,
tty::TTY0,
vga::Vga,
virtual_fs::{keyboard::KeyboardBuffer, virtual_stdin::VirtualStdin},
virtual_fs::{keyboard::KeyboardBuffer, null::Null, virtual_stdin::VirtualStdin},
};
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {}
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {
fn close(&mut self) {}
}
pub trait VirtualFileSystem: Debug {
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, ()>;
@@ -62,6 +65,7 @@ pub unsafe fn init_file_system() {
unsafe {
FILE_SYSTEM.mount("/dev/fb0".into(), Box::new(VGAFileSystem));
FILE_SYSTEM.mount("/dev/tty0".into(), Box::new(TTY0.clone()));
FILE_SYSTEM.mount("/dev/null".into(), Box::new(Null));
FILE_SYSTEM.mount(
"/dev/input/keyboard".into(),
Box::new(KeyboardBuffer::new()),

51
src/virtual_fs/null.rs Normal file
View File

@@ -0,0 +1,51 @@
use alloc::boxed::Box;
use io::{IoBase, Read, Seek, Write};
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
#[derive(Debug)]
pub struct Null;
#[derive(Debug)]
pub struct NullNode;
impl VirtualFileSystem for Null {
fn open(
&mut self,
path: &bffs::path::Path,
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, ()> {
if !path.is_empty() {
Err(())
} else {
Ok(Box::new(NullNode))
}
}
}
impl IoBase for NullNode {
type Error = ();
}
impl Read for NullNode {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, Self::Error> {
Ok(0)
}
}
impl Seek for NullNode {
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
todo!()
}
}
impl Write for NullNode {
fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
todo!()
}
fn flush(&mut self) -> Result<(), Self::Error> {
todo!()
}
}
impl VirtualNode for NullNode {}