Compare commits
2 Commits
de6ef959ce
...
baeea20aa7
| Author | SHA1 | Date | |
|---|---|---|---|
| baeea20aa7 | |||
| b1aac20b57 |
BIN
assets/cozette.otb
Normal file
BIN
assets/cozette.otb
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 7.0 KiB |
42
assets/fontplate.py
Executable file
42
assets/fontplate.py
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/python
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
COLUMNS, ROWS = 32, 42
|
||||
CHAR_WIDTH, CHAR_HEIGHT = 6, 13
|
||||
|
||||
font_path = "assets/cozette.otb"
|
||||
canvas = Image.new("1", (COLUMNS * CHAR_WIDTH, ROWS * CHAR_HEIGHT), color=0)
|
||||
|
||||
try:
|
||||
font = ImageFont.truetype(font_path, 13)
|
||||
ttfont = TTFont(font_path)
|
||||
cmap = ttfont.getBestCmap()
|
||||
if cmap is None:
|
||||
raise Exception()
|
||||
except Exception:
|
||||
print("The font can't be loaded")
|
||||
exit()
|
||||
|
||||
ranges = [
|
||||
range(ROWS * COLUMNS),
|
||||
]
|
||||
|
||||
for range in ranges:
|
||||
for i in range:
|
||||
pos = i
|
||||
x = (pos % COLUMNS) * CHAR_WIDTH
|
||||
y = (pos // COLUMNS) * CHAR_HEIGHT
|
||||
|
||||
char_to_draw = chr(i)
|
||||
if i < 32 or i not in cmap:
|
||||
char_to_draw = chr(0xFFFD)
|
||||
|
||||
cell = Image.new("1", (CHAR_WIDTH, CHAR_HEIGHT), color=0)
|
||||
cell_draw = ImageDraw.Draw(cell)
|
||||
|
||||
cell_draw.text((0, 0), char_to_draw, font=font, fill=1)
|
||||
canvas.paste(cell, (x, y))
|
||||
|
||||
canvas.save("assets/fontplate.png")
|
||||
@@ -142,3 +142,10 @@ impl From<&str> for PathBuf {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<String> for PathBuf {
|
||||
fn from(val: String) -> Self {
|
||||
PathBuf { inner: val }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ fn to_format(img: ImageBuffer<Luma<u8>, Vec<u8>>, width: usize, height: usize) -
|
||||
output
|
||||
}
|
||||
|
||||
fn path_to_image(path: &str) -> (Vec<u8>, String, usize, usize) {
|
||||
fn path_to_image(path: &str) -> (Vec<u8>, usize, usize) {
|
||||
let img = match image::open(path) {
|
||||
Ok(img) => img.to_luma8(),
|
||||
Err(e) => panic!("failed to open image {}: {}", path, e),
|
||||
@@ -45,14 +45,7 @@ fn path_to_image(path: &str) -> (Vec<u8>, String, usize, usize) {
|
||||
|
||||
let bytes = to_format(img, width, height);
|
||||
|
||||
let path = path
|
||||
.split('/')
|
||||
.next_back()
|
||||
.expect("failed to get last part of path");
|
||||
let split: Vec<_> = path.split('.').collect();
|
||||
let name = remove_non_alphanumeric(&split[0..split.len() - 1].join(".")).to_uppercase();
|
||||
|
||||
(bytes, name, width, height)
|
||||
(bytes, width, height)
|
||||
}
|
||||
|
||||
struct ParsedArgs {
|
||||
@@ -69,20 +62,11 @@ impl Parse for ParsedArgs {
|
||||
|
||||
pub fn parse_image(
|
||||
input: TokenStream,
|
||||
) -> Result<
|
||||
(
|
||||
u8,
|
||||
u8,
|
||||
proc_macro2::Ident,
|
||||
usize,
|
||||
Vec<proc_macro2::TokenStream>,
|
||||
),
|
||||
syn::Error,
|
||||
> {
|
||||
) -> Result<(u8, u8, usize, Vec<proc_macro2::TokenStream>), syn::Error> {
|
||||
// parse the input into a comma separated list of arguments
|
||||
let parsed_args = syn::parse::<ParsedArgs>(input)?;
|
||||
// let parsed_args = parse_macro_input!(input as ParsedArgs);
|
||||
let (bytes, name, width, height) = path_to_image(&parsed_args.path);
|
||||
let (bytes, width, height) = path_to_image(&parsed_args.path);
|
||||
|
||||
let width = width as u8;
|
||||
let height = height as u8;
|
||||
@@ -90,14 +74,12 @@ pub fn parse_image(
|
||||
let byte_array = bytes.as_slice();
|
||||
let byte_count = byte_array.len();
|
||||
|
||||
let name_ident = syn::Ident::new(&name, Span::call_site().into());
|
||||
|
||||
let byte_tokens = bytes.iter().map(|b| quote! { #b }).collect::<Vec<_>>();
|
||||
Ok((width, height, name_ident, byte_count, byte_tokens))
|
||||
Ok((width, height, byte_count, byte_tokens))
|
||||
}
|
||||
|
||||
pub fn include_bitmap_image_impl(input: TokenStream) -> TokenStream {
|
||||
let (_, _, _, _, byte_tokens) = parse_image(input).unwrap();
|
||||
let (_, _, _, byte_tokens) = parse_image(input).unwrap();
|
||||
|
||||
let output = quote! {
|
||||
[#(#byte_tokens),*]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Copy)]
|
||||
pub struct CircularBuffer<T, const SIZE: usize> {
|
||||
buffer: [MaybeUninit<T>; SIZE],
|
||||
head: usize,
|
||||
@@ -8,6 +8,12 @@ pub struct CircularBuffer<T, const SIZE: usize> {
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<T: Copy, const SIZE: usize> Clone for CircularBuffer<T, SIZE> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const SIZE: usize> CircularBuffer<T, SIZE> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -39,12 +39,6 @@ pub trait Draw {
|
||||
///
|
||||
/// Uses the embedded font plate to render glyphs into the framebuffer.
|
||||
unsafe fn draw_char_bg(&mut self, x: u16, y: u16, c: char, color: Color, bg_color: Color) {
|
||||
let c = if (c as u8 > b'~') || ((c as u8) < b' ') {
|
||||
b'/' - b' '
|
||||
} else {
|
||||
c as u8 - b' '
|
||||
};
|
||||
|
||||
// Get char position within font plate
|
||||
let char_x = (c as usize % 32) * FONT_WIDTH;
|
||||
let char_y = (c as usize / 32) * FONT_HEIGHT;
|
||||
@@ -119,6 +113,6 @@ pub trait Draw {
|
||||
pub const FONT_WIDTH: usize = 6;
|
||||
pub const FONT_HEIGHT: usize = 13;
|
||||
pub const FONTPLATE_WIDTH: usize = 32 * FONT_WIDTH;
|
||||
pub const FONTPLATE_HEIGHT: usize = 3 * FONT_HEIGHT;
|
||||
pub const FONTPLATE_HEIGHT: usize = 42 * FONT_HEIGHT;
|
||||
pub const FONTPLATE_SIZE: usize = FONTPLATE_WIDTH * FONTPLATE_HEIGHT / 8;
|
||||
pub static FONTPLATE: [u8; FONTPLATE_SIZE] = include_bitmap_image! {"assets/fontplate.png"};
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
clear_csr,
|
||||
process::{ExecutionContext, exit_process, sleep},
|
||||
read_csr,
|
||||
riscv::disable_interrupt,
|
||||
riscv::{disable_interrupt, dump_cpu},
|
||||
scheduler::SCHEDULER,
|
||||
set_csr, syscall,
|
||||
time::{IRQ_M_EXTERNAL, IRQ_M_TIMER, setup_next_timer_interrupt},
|
||||
@@ -85,7 +85,13 @@ unsafe extern "C" fn machine_trap_handler(
|
||||
),
|
||||
};
|
||||
disable_interrupt();
|
||||
panic!("{} at PC=0x{:x}, mtval={}", message, mepc, mtval);
|
||||
panic!(
|
||||
"{} at PC=0x{:x}, mtval={}\n\n{}",
|
||||
message,
|
||||
mepc,
|
||||
mtval,
|
||||
dump_cpu()
|
||||
);
|
||||
} else {
|
||||
#[allow(clippy::single_match)]
|
||||
match mcause & !(1 << 63) {
|
||||
@@ -120,6 +126,12 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
unsafe {
|
||||
(*interrupt_state).mepc = (*interrupt_state).mepc.byte_add(4);
|
||||
}
|
||||
// Get back to run the syscall again
|
||||
fn loop_syscall(interrupt_state: *mut ExecutionContext) {
|
||||
unsafe {
|
||||
(*interrupt_state).mepc = (*interrupt_state).mepc.byte_sub(4);
|
||||
}
|
||||
}
|
||||
|
||||
// Environment call from S-mode
|
||||
let syscall_u64: u64 = unsafe { (*interrupt_state).a[0] };
|
||||
@@ -134,10 +146,19 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let current_process = scheduler.get_current_process();
|
||||
let fd = current_process.next_fd;
|
||||
current_process.fd_table.insert(fd, virtual_node);
|
||||
current_process.next_fd += 1;
|
||||
unsafe { (*interrupt_state).a[0] = fd };
|
||||
|
||||
let fd = if let Some(fd) =
|
||||
current_process.fd_table.iter().position(Option::is_none)
|
||||
{
|
||||
current_process.fd_table[fd] = Some(virtual_node);
|
||||
fd
|
||||
} else {
|
||||
let fd = current_process.fd_table.len();
|
||||
current_process.fd_table.push(Some(virtual_node));
|
||||
fd
|
||||
};
|
||||
|
||||
unsafe { (*interrupt_state).a[0] = fd as u64 };
|
||||
}
|
||||
SysCall::Write => {
|
||||
let fd = a1;
|
||||
@@ -146,7 +167,7 @@ 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.get_mut(&fd).unwrap();
|
||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||
vnode.write(buf).unwrap();
|
||||
}
|
||||
SysCall::Read => {
|
||||
@@ -156,8 +177,12 @@ 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.get_mut(&fd).unwrap();
|
||||
vnode.read(buf).unwrap();
|
||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||
let res = vnode.read(buf).unwrap();
|
||||
if res == 0 && !buf.is_empty() {
|
||||
loop_syscall(interrupt_state);
|
||||
scheduler.schedule(&mut interrupt_state);
|
||||
}
|
||||
}
|
||||
SysCall::Seek => {
|
||||
let fd = a1;
|
||||
@@ -169,7 +194,7 @@ 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.get_mut(&fd).unwrap();
|
||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||
vnode.seek(seek).unwrap();
|
||||
}
|
||||
SysCall::Alloc => {
|
||||
|
||||
70
src/keyboard_forwarder.rs
Normal file
70
src/keyboard_forwarder.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::virtio::input::{EventCodeValue, VirtioKeyEvent};
|
||||
use crate::{virtual_fs::FILE_SYSTEM, scheduler::SCHEDULER};
|
||||
|
||||
// Map some common linux input keycodes (evdev) to ASCII
|
||||
fn keycode_to_ascii(code: u16) -> Option<char> {
|
||||
match code as u32 {
|
||||
// letters
|
||||
30 => Some('a'), // KEY_A
|
||||
48 => Some('b'), // KEY_B
|
||||
46 => Some('c'),
|
||||
32 => Some('d'),
|
||||
18 => Some('e'),
|
||||
33 => Some('f'),
|
||||
34 => Some('g'),
|
||||
35 => Some('h'),
|
||||
23 => Some('i'),
|
||||
36 => Some('j'),
|
||||
37 => Some('k'),
|
||||
38 => Some('l'),
|
||||
50 => Some('m'),
|
||||
49 => Some('n'),
|
||||
24 => Some('o'),
|
||||
25 => Some('p'),
|
||||
16 => Some('q'),
|
||||
19 => Some('r'),
|
||||
31 => Some('s'),
|
||||
20 => Some('t'),
|
||||
22 => Some('u'),
|
||||
47 => Some('v'),
|
||||
17 => Some('w'),
|
||||
45 => Some('x'),
|
||||
21 => Some('y'),
|
||||
44 => Some('z'),
|
||||
// digits
|
||||
2 => Some('1'),
|
||||
3 => Some('2'),
|
||||
4 => Some('3'),
|
||||
5 => Some('4'),
|
||||
6 => Some('5'),
|
||||
7 => Some('6'),
|
||||
8 => Some('7'),
|
||||
9 => Some('8'),
|
||||
10 => Some('9'),
|
||||
11 => Some('0'),
|
||||
// space, enter, backspace
|
||||
57 => Some(' '),
|
||||
28 => Some('\n'),
|
||||
14 => Some('\x08'),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keyboard_forwarder() {
|
||||
// Open keyboard device and stdin device
|
||||
let mut kbd = unsafe { FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap() };
|
||||
let mut stdin = unsafe { FILE_SYSTEM.open("/dev/stdin".as_ref()).unwrap() };
|
||||
|
||||
let mut buf = [0u8; core::mem::size_of::<VirtioKeyEvent>()];
|
||||
loop {
|
||||
// Read full virtio key event
|
||||
if let Ok(_) = kbd.read(&mut buf) {
|
||||
let ev = unsafe { &*(buf.as_ptr() as *const VirtioKeyEvent) };
|
||||
if ev.value == EventCodeValue::Pressed {
|
||||
if let Some(c) = keycode_to_ascii(ev.code) {
|
||||
let _ = stdin.write(&[c as u8]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/keymap.rs
Normal file
64
src/keymap.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
pub enum KeyType {
|
||||
Ascii(char),
|
||||
// Special, // F1, Home, etc.
|
||||
Modifier, // Shift, Ctrl
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub const fn map_keycode(code: u16, shift: bool) -> KeyType {
|
||||
match code {
|
||||
// Numbers row
|
||||
2..=11 => {
|
||||
let val = if shift {
|
||||
[')', '!', '@', '#', '$', '%', '^', '&', '*', '('][(code - 2) as usize]
|
||||
} else {
|
||||
(code as u8 - 2 + b'1') as char
|
||||
};
|
||||
if code == 11 && !shift {
|
||||
KeyType::Ascii('0')
|
||||
} else {
|
||||
KeyType::Ascii(val)
|
||||
}
|
||||
}
|
||||
|
||||
// Letters (Simplified QWERTY)
|
||||
16 => KeyType::Ascii(if shift { 'Q' } else { 'q' }),
|
||||
17 => KeyType::Ascii(if shift { 'W' } else { 'w' }),
|
||||
18 => KeyType::Ascii(if shift { 'E' } else { 'e' }),
|
||||
19 => KeyType::Ascii(if shift { 'R' } else { 'r' }),
|
||||
20 => KeyType::Ascii(if shift { 'T' } else { 't' }),
|
||||
21 => KeyType::Ascii(if shift { 'Y' } else { 'y' }),
|
||||
22 => KeyType::Ascii(if shift { 'U' } else { 'u' }),
|
||||
23 => KeyType::Ascii(if shift { 'I' } else { 'i' }),
|
||||
24 => KeyType::Ascii(if shift { 'O' } else { 'o' }),
|
||||
25 => KeyType::Ascii(if shift { 'P' } else { 'p' }),
|
||||
30 => KeyType::Ascii(if shift { 'A' } else { 'a' }),
|
||||
31 => KeyType::Ascii(if shift { 'S' } else { 's' }),
|
||||
32 => KeyType::Ascii(if shift { 'D' } else { 'd' }),
|
||||
33 => KeyType::Ascii(if shift { 'F' } else { 'f' }),
|
||||
34 => KeyType::Ascii(if shift { 'G' } else { 'g' }),
|
||||
35 => KeyType::Ascii(if shift { 'H' } else { 'h' }),
|
||||
36 => KeyType::Ascii(if shift { 'J' } else { 'j' }),
|
||||
37 => KeyType::Ascii(if shift { 'K' } else { 'k' }),
|
||||
38 => KeyType::Ascii(if shift { 'L' } else { 'l' }),
|
||||
44 => KeyType::Ascii(if shift { 'Z' } else { 'z' }),
|
||||
45 => KeyType::Ascii(if shift { 'X' } else { 'x' }),
|
||||
46 => KeyType::Ascii(if shift { 'C' } else { 'c' }),
|
||||
47 => KeyType::Ascii(if shift { 'V' } else { 'v' }),
|
||||
48 => KeyType::Ascii(if shift { 'B' } else { 'b' }),
|
||||
49 => KeyType::Ascii(if shift { 'N' } else { 'n' }),
|
||||
50 => KeyType::Ascii(if shift { 'M' } else { 'm' }),
|
||||
|
||||
// Control
|
||||
28 => KeyType::Ascii('\n'),
|
||||
57 => KeyType::Ascii(' '),
|
||||
14 => KeyType::Ascii('\x08'), // Backspace
|
||||
1 => KeyType::Ascii('\x1b'), // Escape
|
||||
|
||||
// Modifiers
|
||||
42 | 54 => KeyType::Modifier, // LShift, RShift
|
||||
29 | 97 => KeyType::Modifier, // LCtrl, RCtrl
|
||||
|
||||
_ => KeyType::Unknown,
|
||||
}
|
||||
}
|
||||
42
src/main.rs
42
src/main.rs
@@ -21,14 +21,16 @@ use log::info;
|
||||
use crate::{
|
||||
cursor::{clear_cursor, draw_cursor},
|
||||
io::init_log,
|
||||
keymap::map_keycode,
|
||||
pci::{PciDeviceIterator, scan_virtio_devices},
|
||||
riscv::enable_supervisor_interrupt,
|
||||
scheduler::{SCHEDULER, idle},
|
||||
tty::TTY0,
|
||||
user::{proc2, test},
|
||||
vga::Vga,
|
||||
virtio::{
|
||||
Virtqueue,
|
||||
input::{EventCodeValue, VirtioPciDriver, init_plic_pci},
|
||||
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver, init_plic_pci},
|
||||
},
|
||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, init_file_system},
|
||||
};
|
||||
@@ -42,6 +44,7 @@ mod draw;
|
||||
mod fs;
|
||||
mod interrupt;
|
||||
mod io;
|
||||
mod keymap;
|
||||
mod panic_handler;
|
||||
mod pci;
|
||||
mod process;
|
||||
@@ -69,17 +72,50 @@ 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."}
|
||||
|
||||
// 1. Allouer de la mémoire statique alignée pour la queue
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct KeyboardState {
|
||||
// ctrl_modifier: bool,
|
||||
maj_modifier: bool,
|
||||
}
|
||||
|
||||
impl KeyboardState {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
// ctrl_modifier: false,
|
||||
maj_modifier: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut KBD_STATE: KeyboardState = KeyboardState::new();
|
||||
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
||||
pub static mut KBD_DRIVER: VirtioPciDriver = unsafe {
|
||||
VirtioPciDriver::new(
|
||||
|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();
|
||||
if event.value == EventCodeValue::Pressed {
|
||||
#[allow(clippy::single_match)]
|
||||
match map_keycode(event.code, KBD_STATE.maj_modifier) {
|
||||
keymap::KeyType::Ascii(c) => {
|
||||
let mut buf = [0; 4];
|
||||
let to_send = c.encode_utf8(&mut buf);
|
||||
for c in to_send.as_bytes() {
|
||||
println!("key: {}", c);
|
||||
TTY0.buffer.borrow_mut().push(*c);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
println!("event: {:#?}", event);
|
||||
kbd_buffer.write(&[event.code as u8]).unwrap();
|
||||
}
|
||||
} else {
|
||||
// println!("key pressed, {:#?}", event);
|
||||
|
||||
@@ -12,25 +12,20 @@ use core::time::Duration;
|
||||
use alloc::{boxed::Box, format, string::String, vec::Vec};
|
||||
use bffs::path::Path;
|
||||
use goblin::elf::reloc::R_RISCV_RELATIVE;
|
||||
use hashbrown::HashMap;
|
||||
use shared::syscall::exit;
|
||||
|
||||
use crate::{
|
||||
println,
|
||||
scheduler::{SCHEDULER, Scheduler},
|
||||
riscv::SStatus,
|
||||
scheduler::{ACTIVE_PID, SCHEDULER, Scheduler},
|
||||
time::elapsed_time_since_startup,
|
||||
tty::TTY0,
|
||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode},
|
||||
};
|
||||
|
||||
/// Size of the stack allocated to each process (in 64-bit words).
|
||||
const STACK_SIZE: usize = 4096;
|
||||
|
||||
/// MSTATUS bit to enable supervisor mode interrupts.
|
||||
const MSTATUS_SPIE: u64 = 1 << 5;
|
||||
|
||||
/// MSTATUS bit to set previous privilege mode to supervisor.
|
||||
const MSTATUS_SPP: u64 = 1 << 1;
|
||||
|
||||
/// Represents the state of a process in the system.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ProcessState {
|
||||
@@ -91,9 +86,7 @@ pub struct Process {
|
||||
/// Process stack.
|
||||
pub stack: [u64; STACK_SIZE],
|
||||
/// File descriptor table.
|
||||
pub fd_table: HashMap<u64, Box<dyn VirtualNode>>,
|
||||
/// Next available file descriptor.
|
||||
pub next_fd: u64,
|
||||
pub fd_table: Vec<Option<Box<dyn VirtualNode>>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Process {}
|
||||
@@ -118,8 +111,7 @@ impl Default for Process {
|
||||
},
|
||||
stack: [0; _],
|
||||
entry: None,
|
||||
fd_table: HashMap::new(),
|
||||
next_fd: 0,
|
||||
fd_table: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,10 +289,17 @@ impl Scheduler {
|
||||
process.state = ProcessState::Activable;
|
||||
process.entry = Some(code);
|
||||
|
||||
process.fd_table = HashMap::new();
|
||||
process
|
||||
.fd_table
|
||||
.insert(0, FILE_SYSTEM.open("/dev/input/keyboard".into()).unwrap());
|
||||
process.fd_table = Vec::new();
|
||||
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(),
|
||||
));
|
||||
|
||||
// Configure execution context
|
||||
// a0 contains the pointer to the function to execute
|
||||
@@ -310,7 +309,7 @@ impl Scheduler {
|
||||
process.ctx.mepc = process_launcher as *const _;
|
||||
|
||||
// Configure mstatus for supervisor mode with interrupts enabled
|
||||
process.ctx.mstatus = MSTATUS_SPP | MSTATUS_SPIE;
|
||||
process.ctx.mstatus = SStatus::SPIE;
|
||||
|
||||
// Initialize stack pointer at the top of the stack
|
||||
process.ctx.sp = &raw const process.stack[STACK_SIZE - 1];
|
||||
@@ -368,7 +367,7 @@ extern "C" fn process_launcher(code: *const Box<dyn Fn()>) {
|
||||
pub fn exit_process(interrupt_context: &mut *mut ExecutionContext) {
|
||||
// SAFETY: ACTIVE_PID is maintained by the scheduler and is always valid.
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let active_pid = scheduler.active_pid;
|
||||
let active_pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
scheduler.process_table.remove(&active_pid).unwrap();
|
||||
// Transfer control to the scheduler (does not return)
|
||||
scheduler.schedule(interrupt_context)
|
||||
|
||||
30
src/riscv.rs
30
src/riscv.rs
@@ -6,6 +6,10 @@
|
||||
|
||||
use core::arch::naked_asm;
|
||||
|
||||
use alloc::fmt::format;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
|
||||
use crate::clear_csr;
|
||||
use crate::read_csr;
|
||||
use crate::set_csr;
|
||||
@@ -18,8 +22,9 @@ impl MStatus {
|
||||
pub const MPIE: usize = 1 << 7;
|
||||
}
|
||||
impl SStatus {
|
||||
pub const SIE: usize = 1 << 1;
|
||||
pub const SPIE: usize = 1 << 5;
|
||||
pub const SPP: u64 = 1 << 8;
|
||||
pub const SIE: u64 = 1 << 1;
|
||||
pub const SPIE: u64 = 1 << 5;
|
||||
}
|
||||
|
||||
/// Return the current machine interrupt enable state.
|
||||
@@ -28,7 +33,7 @@ pub fn get_interrupt_state() -> bool {
|
||||
}
|
||||
/// Return whether supervisor interrupts are currently enabled.
|
||||
pub fn get_supervisor_interrupt_state() -> bool {
|
||||
(read_csr!(sstatus) & SStatus::SIE as u64) != 0
|
||||
(read_csr!(sstatus) & SStatus::SIE) != 0
|
||||
}
|
||||
/// Enable machine-level interrupts.
|
||||
pub fn enable_interrupt() {
|
||||
@@ -115,3 +120,22 @@ pub extern "C" fn exit_qemu() -> ! {
|
||||
"
|
||||
)
|
||||
}
|
||||
|
||||
pub fn dump_cpu() -> String {
|
||||
let mstatus: u64 = read_csr!(mstatus);
|
||||
let mepc: u64 = read_csr!(mepc);
|
||||
let sstatus: u64 = read_csr!(sstatus);
|
||||
let sepc: u64 = read_csr!(sepc);
|
||||
let stval: u64 = read_csr!(stval);
|
||||
|
||||
format!(
|
||||
"-------- CPU DUMP --------
|
||||
mstatus: {:#016x}
|
||||
mepc: {:#016x}
|
||||
sstatus: {:#016x}
|
||||
sepc: {:#016x}
|
||||
stval: {:#016x}
|
||||
--------------------------",
|
||||
mstatus, mepc, sstatus, sepc, stval
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//!
|
||||
//! This module exposes the global process table, the scheduler initialization
|
||||
//! and a simple round-robin scheduler used by the kernel.
|
||||
use core::{arch::riscv64::wfi, cell::LazyCell, ops::Bound};
|
||||
use core::{arch::riscv64::wfi, cell::LazyCell, ops::Bound, sync::atomic::AtomicU64};
|
||||
|
||||
use alloc::{boxed::Box, collections::BTreeMap};
|
||||
use log::info;
|
||||
@@ -17,13 +17,13 @@ use crate::{
|
||||
#[derive(Debug)]
|
||||
pub struct Scheduler {
|
||||
pub next_pid: u64,
|
||||
pub active_pid: u64,
|
||||
pub process_table: BTreeMap<u64, Box<Process>>,
|
||||
}
|
||||
|
||||
pub static ACTIVE_PID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
pub static SCHEDULER: Mutex<LazyCell<Scheduler>> = Mutex::new(LazyCell::new(|| Scheduler {
|
||||
next_pid: 0,
|
||||
active_pid: 0,
|
||||
process_table: BTreeMap::new(),
|
||||
}));
|
||||
|
||||
@@ -60,7 +60,7 @@ impl Scheduler {
|
||||
pub fn schedule(&mut self, interrupt_state: &mut *mut ExecutionContext) {
|
||||
// info!("scheduler");
|
||||
unsafe {
|
||||
let prev_pid = self.active_pid;
|
||||
let prev_pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
if let Some(previous_process) = self.process_table.get_mut(&prev_pid) {
|
||||
previous_process.ctx = **interrupt_state;
|
||||
|
||||
@@ -73,27 +73,31 @@ impl Scheduler {
|
||||
.process_table
|
||||
.range_mut((Bound::Excluded(prev_pid), Bound::Unbounded));
|
||||
|
||||
self.active_pid = loop {
|
||||
if let Some((pid, current_process)) = current_process_iter.next() {
|
||||
if current_process.state == ProcessState::Asleep
|
||||
&& time::elapsed_time_since_startup() > current_process.wake_time
|
||||
{
|
||||
current_process.state = ProcessState::Activable;
|
||||
ACTIVE_PID.store(
|
||||
loop {
|
||||
if let Some((pid, current_process)) = current_process_iter.next() {
|
||||
if current_process.state == ProcessState::Asleep
|
||||
&& time::elapsed_time_since_startup() > current_process.wake_time
|
||||
{
|
||||
current_process.state = ProcessState::Activable;
|
||||
}
|
||||
if current_process.state == ProcessState::Activable {
|
||||
current_process.state = ProcessState::Active;
|
||||
*interrupt_state = &raw mut current_process.ctx;
|
||||
break *pid;
|
||||
};
|
||||
} else {
|
||||
current_process_iter = self
|
||||
.process_table
|
||||
.range_mut((Bound::Unbounded, Bound::Included(prev_pid)))
|
||||
}
|
||||
if current_process.state == ProcessState::Activable {
|
||||
current_process.state = ProcessState::Active;
|
||||
*interrupt_state = &raw mut current_process.ctx;
|
||||
break *pid;
|
||||
};
|
||||
} else {
|
||||
current_process_iter = self
|
||||
.process_table
|
||||
.range_mut((Bound::Unbounded, Bound::Included(prev_pid)))
|
||||
}
|
||||
};
|
||||
},
|
||||
core::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn get_current_process(&mut self) -> &mut Process {
|
||||
self.process_table.get_mut(&self.active_pid).unwrap()
|
||||
let active_pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
self.process_table.get_mut(&active_pid).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const CLINT_TIMER: *const u64 = 0x0200_bff8 as *const u64;
|
||||
/// The hardware timer frequency (Hz).
|
||||
const TIMER_FREQUENCY: u64 = 10_000_000; // 10 MHz
|
||||
/// The frequency at which timer interrupts should occur (Hz).
|
||||
const INTERRUPT_FREQUENCY: u64 = 20; // 20 Hz
|
||||
const INTERRUPT_FREQUENCY: u64 = 100; // 100 Hz
|
||||
|
||||
/// Stores the instant when the kernel started.
|
||||
static START_TIME: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
40
src/tty.rs
40
src/tty.rs
@@ -1,29 +1,35 @@
|
||||
use core::cell::RefCell;
|
||||
use core::cell::{LazyCell, RefCell};
|
||||
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
|
||||
use crate::{
|
||||
data_structures::circular_buffer::CircularBuffer,
|
||||
virtual_console::VirtualConsole,
|
||||
virtual_fs::{VirtualFileSystem, VirtualNode},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub const TTY_BUFFER_SIZE: usize = 4096; // 4Ko
|
||||
pub static mut TTY0: LazyCell<Tty> = LazyCell::new(Tty::new);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Tty {
|
||||
pub buffer: Rc<RefCell<CircularBuffer<u8, TTY_BUFFER_SIZE>>>,
|
||||
console: Rc<RefCell<VirtualConsole>>,
|
||||
}
|
||||
|
||||
impl Tty {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
buffer: RefCell::new(CircularBuffer::new()).into(),
|
||||
console: RefCell::new(VirtualConsole::new()).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TtyNode {
|
||||
console: Rc<RefCell<VirtualConsole>>,
|
||||
struct TtyNode<'a> {
|
||||
tty: &'a Tty,
|
||||
}
|
||||
|
||||
impl VirtualFileSystem for Tty {
|
||||
@@ -34,32 +40,36 @@ impl VirtualFileSystem for Tty {
|
||||
if !path.is_empty() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(Box::new(TtyNode {
|
||||
console: self.console.clone(),
|
||||
}))
|
||||
Ok(Box::new(TtyNode { tty: self }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for TtyNode {
|
||||
impl IoBase for TtyNode<'_> {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for TtyNode {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
unimplemented!()
|
||||
impl Read for TtyNode<'_> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
let mut buffer = self.tty.buffer.borrow_mut();
|
||||
let max_len = buffer.len();
|
||||
(0..buf.len().min(max_len)).for_each(|i| {
|
||||
buf[i] = buffer.pop().unwrap();
|
||||
});
|
||||
Ok(buf.len().min(max_len))
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for TtyNode {
|
||||
impl Seek for TtyNode<'_> {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for TtyNode {
|
||||
impl Write for TtyNode<'_> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.console
|
||||
self.tty
|
||||
.console
|
||||
.borrow_mut()
|
||||
.write_str(str::from_utf8(buf).unwrap());
|
||||
Ok(buf.len())
|
||||
@@ -70,4 +80,4 @@ impl Write for TtyNode {
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualNode for TtyNode {}
|
||||
impl VirtualNode for TtyNode<'_> {}
|
||||
|
||||
@@ -10,8 +10,14 @@ use io::{IoBase, Read, Seek, Write};
|
||||
|
||||
pub mod keyboard;
|
||||
pub mod stdin;
|
||||
pub mod virtual_stdin;
|
||||
|
||||
use crate::{fs::Disk, tty::Tty, vga::Vga, virtual_fs::keyboard::KeyboardBuffer};
|
||||
use crate::{
|
||||
fs::Disk,
|
||||
tty::TTY0,
|
||||
vga::Vga,
|
||||
virtual_fs::{keyboard::KeyboardBuffer, virtual_stdin::VirtualStdin},
|
||||
};
|
||||
|
||||
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {}
|
||||
|
||||
@@ -55,12 +61,12 @@ pub static mut FILE_SYSTEM: LazyCell<MainFileSystem> = LazyCell::new(|| MainFile
|
||||
pub unsafe fn init_file_system() {
|
||||
unsafe {
|
||||
FILE_SYSTEM.mount("/dev/fb0".into(), Box::new(VGAFileSystem));
|
||||
FILE_SYSTEM.mount("/dev/tty0".into(), Box::new(Tty::new()));
|
||||
FILE_SYSTEM.mount("/dev/tty0".into(), Box::new(TTY0.clone()));
|
||||
FILE_SYSTEM.mount(
|
||||
"/dev/input/keyboard".into(),
|
||||
Box::new(KeyboardBuffer::new()),
|
||||
);
|
||||
// FILE_SYSTEM.mount("/dev/stdin".into(), Box::new(Stdin::new()));
|
||||
FILE_SYSTEM.mount("/dev/stdin".into(), Box::new(VirtualStdin::new()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ use io::{IoBase, Read, Seek, Write};
|
||||
|
||||
use crate::{
|
||||
data_structures::circular_buffer::CircularBuffer,
|
||||
println,
|
||||
virtual_fs::{VirtualFileSystem, VirtualNode},
|
||||
};
|
||||
|
||||
@@ -55,15 +54,15 @@ impl IoBase for KeyboardBufferNode<'_> {
|
||||
impl Read for KeyboardBufferNode<'_> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
let mut buffer = self.buffer.buffer.borrow_mut();
|
||||
for i in 0..buf.len() {
|
||||
(0..buf.len()).for_each(|i| {
|
||||
buf[i] = buffer.pop().unwrap();
|
||||
}
|
||||
});
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for KeyboardBufferNode<'_> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,22 @@
|
||||
// use alloc::boxed::Box;
|
||||
// use io::{IoBase, Read, Seek, Write};
|
||||
|
||||
// use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
|
||||
// use crate::{
|
||||
// data_structures::circular_buffer::CircularBuffer,
|
||||
// virtual_fs::{VirtualFileSystem, VirtualNode},
|
||||
// };
|
||||
|
||||
// pub const STDIN_BUFFER_SIZE: usize = 4096; // 4Ko
|
||||
|
||||
// #[derive(Debug)]
|
||||
// pub struct Stdin {
|
||||
// buffer: RefCell<[u8; STDIN_BUFFER_SIZE]>,
|
||||
// buffer: RefCell<CircularBuffer<u8, STDIN_BUFFER_SIZE>>,
|
||||
// }
|
||||
|
||||
// impl Stdin {
|
||||
// pub fn new() -> Self {
|
||||
// Self {
|
||||
// buffer: RefCell::new([0; _]),
|
||||
// buffer: RefCell::new(CircularBuffer::new()),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -44,7 +47,11 @@
|
||||
|
||||
// impl Read for StdinNode<'_> {
|
||||
// fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
// todo!()
|
||||
// let mut buffer = self.stdin.buffer.borrow_mut();
|
||||
// for i in 0..buf.len() {
|
||||
// buf[i] = buffer.pop().unwrap();
|
||||
// }
|
||||
// Ok(buf.len())
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
1
src/virtual_fs/symbolic_link.rs
Normal file
1
src/virtual_fs/symbolic_link.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub struct SymbolicLink {}
|
||||
84
src/virtual_fs/virtual_stdin.rs
Normal file
84
src/virtual_fs/virtual_stdin.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use alloc::{boxed::Box, format};
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
|
||||
use crate::{
|
||||
scheduler::ACTIVE_PID,
|
||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VirtualStdin {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VirtualStdinNode {}
|
||||
|
||||
impl VirtualStdin {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualFileSystem for VirtualStdin {
|
||||
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(VirtualStdinNode {}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for VirtualStdinNode {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for VirtualStdinNode {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
let pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
unsafe {
|
||||
FILE_SYSTEM
|
||||
.open(format!("/proc/{pid}/0").as_ref())
|
||||
.unwrap()
|
||||
.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for VirtualStdinNode {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
let pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
unsafe {
|
||||
FILE_SYSTEM
|
||||
.open(format!("/proc/{pid}/0").as_ref())
|
||||
.unwrap()
|
||||
.seek(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for VirtualStdinNode {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
let pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
unsafe {
|
||||
FILE_SYSTEM
|
||||
.open(format!("/proc/{pid}/0").as_ref())
|
||||
.unwrap()
|
||||
.write(buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
let pid = ACTIVE_PID.load(core::sync::atomic::Ordering::Relaxed);
|
||||
unsafe {
|
||||
FILE_SYSTEM
|
||||
.open(format!("/proc/{pid}/0").as_ref())
|
||||
.unwrap()
|
||||
.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualNode for VirtualStdinNode {}
|
||||
@@ -12,13 +12,19 @@ fn main() {
|
||||
// syscall::seek(&mut file, SeekFrom::End(-3));
|
||||
// syscall::write(&mut file, &[255; 6400 * 50]);
|
||||
syscall::sleep(Duration::from_secs_f64(2.0));
|
||||
let mut stdin = syscall::open("/dev/input/keyboard");
|
||||
let mut test = [0; 2];
|
||||
syscall::read(&mut stdin, &mut test);
|
||||
let mut stdin = syscall::open("/dev/tty0");
|
||||
let mut file = syscall::open("/dev/tty0");
|
||||
syscall::write(&mut file, b"Hi !\nnice tty\x08");
|
||||
println!(
|
||||
"Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! {:?}",
|
||||
test
|
||||
);
|
||||
loop {
|
||||
let mut test = [0; 2];
|
||||
syscall::read(&mut stdin, &mut test);
|
||||
let len = *test.iter().find(|x| **x == 0).unwrap_or(&1) + 1;
|
||||
syscall::write(
|
||||
&mut file,
|
||||
str::from_utf8(&test[..len as usize]).unwrap().as_bytes(),
|
||||
);
|
||||
}
|
||||
// println!(
|
||||
// "Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! {:?}",
|
||||
// str::from_utf8(&test)
|
||||
// );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user