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
|
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) {
|
let img = match image::open(path) {
|
||||||
Ok(img) => img.to_luma8(),
|
Ok(img) => img.to_luma8(),
|
||||||
Err(e) => panic!("failed to open image {}: {}", path, e),
|
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 bytes = to_format(img, width, height);
|
||||||
|
|
||||||
let path = path
|
(bytes, width, height)
|
||||||
.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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ParsedArgs {
|
struct ParsedArgs {
|
||||||
@@ -69,20 +62,11 @@ impl Parse for ParsedArgs {
|
|||||||
|
|
||||||
pub fn parse_image(
|
pub fn parse_image(
|
||||||
input: TokenStream,
|
input: TokenStream,
|
||||||
) -> Result<
|
) -> Result<(u8, u8, usize, Vec<proc_macro2::TokenStream>), syn::Error> {
|
||||||
(
|
|
||||||
u8,
|
|
||||||
u8,
|
|
||||||
proc_macro2::Ident,
|
|
||||||
usize,
|
|
||||||
Vec<proc_macro2::TokenStream>,
|
|
||||||
),
|
|
||||||
syn::Error,
|
|
||||||
> {
|
|
||||||
// parse the input into a comma separated list of arguments
|
// parse the input into a comma separated list of arguments
|
||||||
let parsed_args = syn::parse::<ParsedArgs>(input)?;
|
let parsed_args = syn::parse::<ParsedArgs>(input)?;
|
||||||
// let parsed_args = parse_macro_input!(input as ParsedArgs);
|
// 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 width = width as u8;
|
||||||
let height = height as u8;
|
let height = height as u8;
|
||||||
@@ -90,14 +74,12 @@ pub fn parse_image(
|
|||||||
let byte_array = bytes.as_slice();
|
let byte_array = bytes.as_slice();
|
||||||
let byte_count = byte_array.len();
|
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<_>>();
|
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 {
|
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! {
|
let output = quote! {
|
||||||
[#(#byte_tokens),*]
|
[#(#byte_tokens),*]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy)]
|
||||||
pub struct CircularBuffer<T, const SIZE: usize> {
|
pub struct CircularBuffer<T, const SIZE: usize> {
|
||||||
buffer: [MaybeUninit<T>; SIZE],
|
buffer: [MaybeUninit<T>; SIZE],
|
||||||
head: usize,
|
head: usize,
|
||||||
@@ -8,6 +8,12 @@ pub struct CircularBuffer<T, const SIZE: usize> {
|
|||||||
len: 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> {
|
impl<T, const SIZE: usize> CircularBuffer<T, SIZE> {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -39,12 +39,6 @@ pub trait Draw {
|
|||||||
///
|
///
|
||||||
/// Uses the embedded font plate to render glyphs into the framebuffer.
|
/// 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) {
|
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
|
// Get char position within font plate
|
||||||
let char_x = (c as usize % 32) * FONT_WIDTH;
|
let char_x = (c as usize % 32) * FONT_WIDTH;
|
||||||
let char_y = (c as usize / 32) * FONT_HEIGHT;
|
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_WIDTH: usize = 6;
|
||||||
pub const FONT_HEIGHT: usize = 13;
|
pub const FONT_HEIGHT: usize = 13;
|
||||||
pub const FONTPLATE_WIDTH: usize = 32 * FONT_WIDTH;
|
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 const FONTPLATE_SIZE: usize = FONTPLATE_WIDTH * FONTPLATE_HEIGHT / 8;
|
||||||
pub static FONTPLATE: [u8; FONTPLATE_SIZE] = include_bitmap_image! {"assets/fontplate.png"};
|
pub static FONTPLATE: [u8; FONTPLATE_SIZE] = include_bitmap_image! {"assets/fontplate.png"};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use crate::{
|
|||||||
clear_csr,
|
clear_csr,
|
||||||
process::{ExecutionContext, exit_process, sleep},
|
process::{ExecutionContext, exit_process, sleep},
|
||||||
read_csr,
|
read_csr,
|
||||||
riscv::disable_interrupt,
|
riscv::{disable_interrupt, dump_cpu},
|
||||||
scheduler::SCHEDULER,
|
scheduler::SCHEDULER,
|
||||||
set_csr, syscall,
|
set_csr, syscall,
|
||||||
time::{IRQ_M_EXTERNAL, IRQ_M_TIMER, setup_next_timer_interrupt},
|
time::{IRQ_M_EXTERNAL, IRQ_M_TIMER, setup_next_timer_interrupt},
|
||||||
@@ -85,7 +85,13 @@ unsafe extern "C" fn machine_trap_handler(
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
disable_interrupt();
|
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 {
|
} else {
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match mcause & !(1 << 63) {
|
match mcause & !(1 << 63) {
|
||||||
@@ -120,6 +126,12 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
unsafe {
|
unsafe {
|
||||||
(*interrupt_state).mepc = (*interrupt_state).mepc.byte_add(4);
|
(*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
|
// Environment call from S-mode
|
||||||
let syscall_u64: u64 = unsafe { (*interrupt_state).a[0] };
|
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 mut scheduler = SCHEDULER.lock();
|
||||||
let current_process = scheduler.get_current_process();
|
let current_process = scheduler.get_current_process();
|
||||||
let fd = current_process.next_fd;
|
|
||||||
current_process.fd_table.insert(fd, virtual_node);
|
let fd = if let Some(fd) =
|
||||||
current_process.next_fd += 1;
|
current_process.fd_table.iter().position(Option::is_none)
|
||||||
unsafe { (*interrupt_state).a[0] = fd };
|
{
|
||||||
|
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 => {
|
SysCall::Write => {
|
||||||
let fd = a1;
|
let fd = a1;
|
||||||
@@ -146,7 +167,7 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
|
|
||||||
let mut scheduler = SCHEDULER.lock();
|
let mut scheduler = SCHEDULER.lock();
|
||||||
let current_process = scheduler.get_current_process();
|
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();
|
vnode.write(buf).unwrap();
|
||||||
}
|
}
|
||||||
SysCall::Read => {
|
SysCall::Read => {
|
||||||
@@ -156,8 +177,12 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
|
|
||||||
let mut scheduler = SCHEDULER.lock();
|
let mut scheduler = SCHEDULER.lock();
|
||||||
let current_process = scheduler.get_current_process();
|
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.read(buf).unwrap();
|
let res = vnode.read(buf).unwrap();
|
||||||
|
if res == 0 && !buf.is_empty() {
|
||||||
|
loop_syscall(interrupt_state);
|
||||||
|
scheduler.schedule(&mut interrupt_state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SysCall::Seek => {
|
SysCall::Seek => {
|
||||||
let fd = a1;
|
let fd = a1;
|
||||||
@@ -169,7 +194,7 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
};
|
};
|
||||||
let mut scheduler = SCHEDULER.lock();
|
let mut scheduler = SCHEDULER.lock();
|
||||||
let current_process = scheduler.get_current_process();
|
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();
|
vnode.seek(seek).unwrap();
|
||||||
}
|
}
|
||||||
SysCall::Alloc => {
|
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::{
|
use crate::{
|
||||||
cursor::{clear_cursor, draw_cursor},
|
cursor::{clear_cursor, draw_cursor},
|
||||||
io::init_log,
|
io::init_log,
|
||||||
|
keymap::map_keycode,
|
||||||
pci::{PciDeviceIterator, scan_virtio_devices},
|
pci::{PciDeviceIterator, scan_virtio_devices},
|
||||||
riscv::enable_supervisor_interrupt,
|
riscv::enable_supervisor_interrupt,
|
||||||
scheduler::{SCHEDULER, idle},
|
scheduler::{SCHEDULER, idle},
|
||||||
|
tty::TTY0,
|
||||||
user::{proc2, test},
|
user::{proc2, test},
|
||||||
vga::Vga,
|
vga::Vga,
|
||||||
virtio::{
|
virtio::{
|
||||||
Virtqueue,
|
Virtqueue,
|
||||||
input::{EventCodeValue, VirtioPciDriver, init_plic_pci},
|
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver, init_plic_pci},
|
||||||
},
|
},
|
||||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, init_file_system},
|
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, init_file_system},
|
||||||
};
|
};
|
||||||
@@ -42,6 +44,7 @@ mod draw;
|
|||||||
mod fs;
|
mod fs;
|
||||||
mod interrupt;
|
mod interrupt;
|
||||||
mod io;
|
mod io;
|
||||||
|
mod keymap;
|
||||||
mod panic_handler;
|
mod panic_handler;
|
||||||
mod pci;
|
mod pci;
|
||||||
mod process;
|
mod process;
|
||||||
@@ -69,17 +72,50 @@ const _: () = assert!(core::mem::size_of::<usize>() == core::mem::size_of::<u64>
|
|||||||
#[cfg(not(target_endian = "little"))]
|
#[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."}
|
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() };
|
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
||||||
pub static mut KBD_DRIVER: VirtioPciDriver = unsafe {
|
pub static mut KBD_DRIVER: VirtioPciDriver = unsafe {
|
||||||
VirtioPciDriver::new(
|
VirtioPciDriver::new(
|
||||||
|event| {
|
|event| {
|
||||||
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
|
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() {
|
if event.is_key() {
|
||||||
let event = event.as_key_event();
|
let event = event.as_key_event();
|
||||||
if event.value == EventCodeValue::Pressed {
|
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);
|
println!("event: {:#?}", event);
|
||||||
kbd_buffer.write(&[event.code as u8]).unwrap();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// println!("key pressed, {:#?}", event);
|
// println!("key pressed, {:#?}", event);
|
||||||
|
|||||||
@@ -12,25 +12,20 @@ use core::time::Duration;
|
|||||||
use alloc::{boxed::Box, format, string::String, vec::Vec};
|
use alloc::{boxed::Box, format, string::String, vec::Vec};
|
||||||
use bffs::path::Path;
|
use bffs::path::Path;
|
||||||
use goblin::elf::reloc::R_RISCV_RELATIVE;
|
use goblin::elf::reloc::R_RISCV_RELATIVE;
|
||||||
use hashbrown::HashMap;
|
|
||||||
use shared::syscall::exit;
|
use shared::syscall::exit;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
println,
|
println,
|
||||||
scheduler::{SCHEDULER, Scheduler},
|
riscv::SStatus,
|
||||||
|
scheduler::{ACTIVE_PID, SCHEDULER, Scheduler},
|
||||||
time::elapsed_time_since_startup,
|
time::elapsed_time_since_startup,
|
||||||
|
tty::TTY0,
|
||||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode},
|
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Size of the stack allocated to each process (in 64-bit words).
|
/// Size of the stack allocated to each process (in 64-bit words).
|
||||||
const STACK_SIZE: usize = 4096;
|
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.
|
/// Represents the state of a process in the system.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum ProcessState {
|
pub enum ProcessState {
|
||||||
@@ -91,9 +86,7 @@ pub struct Process {
|
|||||||
/// Process stack.
|
/// Process stack.
|
||||||
pub stack: [u64; STACK_SIZE],
|
pub stack: [u64; STACK_SIZE],
|
||||||
/// File descriptor table.
|
/// File descriptor table.
|
||||||
pub fd_table: HashMap<u64, Box<dyn VirtualNode>>,
|
pub fd_table: Vec<Option<Box<dyn VirtualNode>>>,
|
||||||
/// Next available file descriptor.
|
|
||||||
pub next_fd: u64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Process {}
|
unsafe impl Send for Process {}
|
||||||
@@ -118,8 +111,7 @@ impl Default for Process {
|
|||||||
},
|
},
|
||||||
stack: [0; _],
|
stack: [0; _],
|
||||||
entry: None,
|
entry: None,
|
||||||
fd_table: HashMap::new(),
|
fd_table: Vec::new(),
|
||||||
next_fd: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,10 +289,17 @@ impl Scheduler {
|
|||||||
process.state = ProcessState::Activable;
|
process.state = ProcessState::Activable;
|
||||||
process.entry = Some(code);
|
process.entry = Some(code);
|
||||||
|
|
||||||
process.fd_table = HashMap::new();
|
process.fd_table = Vec::new();
|
||||||
process
|
FILE_SYSTEM.mount(
|
||||||
.fd_table
|
format!("/proc/{}/0", process.pid).into(),
|
||||||
.insert(0, FILE_SYSTEM.open("/dev/input/keyboard".into()).unwrap());
|
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
|
// Configure execution context
|
||||||
// a0 contains the pointer to the function to execute
|
// a0 contains the pointer to the function to execute
|
||||||
@@ -310,7 +309,7 @@ impl Scheduler {
|
|||||||
process.ctx.mepc = process_launcher as *const _;
|
process.ctx.mepc = process_launcher as *const _;
|
||||||
|
|
||||||
// Configure mstatus for supervisor mode with interrupts enabled
|
// 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
|
// Initialize stack pointer at the top of the stack
|
||||||
process.ctx.sp = &raw const process.stack[STACK_SIZE - 1];
|
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) {
|
pub fn exit_process(interrupt_context: &mut *mut ExecutionContext) {
|
||||||
// SAFETY: ACTIVE_PID is maintained by the scheduler and is always valid.
|
// SAFETY: ACTIVE_PID is maintained by the scheduler and is always valid.
|
||||||
let mut scheduler = SCHEDULER.lock();
|
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();
|
scheduler.process_table.remove(&active_pid).unwrap();
|
||||||
// Transfer control to the scheduler (does not return)
|
// Transfer control to the scheduler (does not return)
|
||||||
scheduler.schedule(interrupt_context)
|
scheduler.schedule(interrupt_context)
|
||||||
|
|||||||
30
src/riscv.rs
30
src/riscv.rs
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
use core::arch::naked_asm;
|
use core::arch::naked_asm;
|
||||||
|
|
||||||
|
use alloc::fmt::format;
|
||||||
|
use alloc::format;
|
||||||
|
use alloc::string::String;
|
||||||
|
|
||||||
use crate::clear_csr;
|
use crate::clear_csr;
|
||||||
use crate::read_csr;
|
use crate::read_csr;
|
||||||
use crate::set_csr;
|
use crate::set_csr;
|
||||||
@@ -18,8 +22,9 @@ impl MStatus {
|
|||||||
pub const MPIE: usize = 1 << 7;
|
pub const MPIE: usize = 1 << 7;
|
||||||
}
|
}
|
||||||
impl SStatus {
|
impl SStatus {
|
||||||
pub const SIE: usize = 1 << 1;
|
pub const SPP: u64 = 1 << 8;
|
||||||
pub const SPIE: usize = 1 << 5;
|
pub const SIE: u64 = 1 << 1;
|
||||||
|
pub const SPIE: u64 = 1 << 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the current machine interrupt enable state.
|
/// Return the current machine interrupt enable state.
|
||||||
@@ -28,7 +33,7 @@ pub fn get_interrupt_state() -> bool {
|
|||||||
}
|
}
|
||||||
/// Return whether supervisor interrupts are currently enabled.
|
/// Return whether supervisor interrupts are currently enabled.
|
||||||
pub fn get_supervisor_interrupt_state() -> bool {
|
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.
|
/// Enable machine-level interrupts.
|
||||||
pub fn enable_interrupt() {
|
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
|
//! This module exposes the global process table, the scheduler initialization
|
||||||
//! and a simple round-robin scheduler used by the kernel.
|
//! 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 alloc::{boxed::Box, collections::BTreeMap};
|
||||||
use log::info;
|
use log::info;
|
||||||
@@ -17,13 +17,13 @@ use crate::{
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Scheduler {
|
pub struct Scheduler {
|
||||||
pub next_pid: u64,
|
pub next_pid: u64,
|
||||||
pub active_pid: u64,
|
|
||||||
pub process_table: BTreeMap<u64, Box<Process>>,
|
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 {
|
pub static SCHEDULER: Mutex<LazyCell<Scheduler>> = Mutex::new(LazyCell::new(|| Scheduler {
|
||||||
next_pid: 0,
|
next_pid: 0,
|
||||||
active_pid: 0,
|
|
||||||
process_table: BTreeMap::new(),
|
process_table: BTreeMap::new(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ impl Scheduler {
|
|||||||
pub fn schedule(&mut self, interrupt_state: &mut *mut ExecutionContext) {
|
pub fn schedule(&mut self, interrupt_state: &mut *mut ExecutionContext) {
|
||||||
// info!("scheduler");
|
// info!("scheduler");
|
||||||
unsafe {
|
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) {
|
if let Some(previous_process) = self.process_table.get_mut(&prev_pid) {
|
||||||
previous_process.ctx = **interrupt_state;
|
previous_process.ctx = **interrupt_state;
|
||||||
|
|
||||||
@@ -73,27 +73,31 @@ impl Scheduler {
|
|||||||
.process_table
|
.process_table
|
||||||
.range_mut((Bound::Excluded(prev_pid), Bound::Unbounded));
|
.range_mut((Bound::Excluded(prev_pid), Bound::Unbounded));
|
||||||
|
|
||||||
self.active_pid = loop {
|
ACTIVE_PID.store(
|
||||||
if let Some((pid, current_process)) = current_process_iter.next() {
|
loop {
|
||||||
if current_process.state == ProcessState::Asleep
|
if let Some((pid, current_process)) = current_process_iter.next() {
|
||||||
&& time::elapsed_time_since_startup() > current_process.wake_time
|
if current_process.state == ProcessState::Asleep
|
||||||
{
|
&& time::elapsed_time_since_startup() > current_process.wake_time
|
||||||
current_process.state = ProcessState::Activable;
|
{
|
||||||
|
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;
|
core::sync::atomic::Ordering::Relaxed,
|
||||||
*interrupt_state = &raw mut current_process.ctx;
|
);
|
||||||
break *pid;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
current_process_iter = self
|
|
||||||
.process_table
|
|
||||||
.range_mut((Bound::Unbounded, Bound::Included(prev_pid)))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_current_process(&mut self) -> &mut Process {
|
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).
|
/// The hardware timer frequency (Hz).
|
||||||
const TIMER_FREQUENCY: u64 = 10_000_000; // 10 MHz
|
const TIMER_FREQUENCY: u64 = 10_000_000; // 10 MHz
|
||||||
/// The frequency at which timer interrupts should occur (Hz).
|
/// 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.
|
/// Stores the instant when the kernel started.
|
||||||
static START_TIME: AtomicU64 = AtomicU64::new(0);
|
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 alloc::{boxed::Box, rc::Rc};
|
||||||
use io::{IoBase, Read, Seek, Write};
|
use io::{IoBase, Read, Seek, Write};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
data_structures::circular_buffer::CircularBuffer,
|
||||||
virtual_console::VirtualConsole,
|
virtual_console::VirtualConsole,
|
||||||
virtual_fs::{VirtualFileSystem, VirtualNode},
|
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 struct Tty {
|
||||||
|
pub buffer: Rc<RefCell<CircularBuffer<u8, TTY_BUFFER_SIZE>>>,
|
||||||
console: Rc<RefCell<VirtualConsole>>,
|
console: Rc<RefCell<VirtualConsole>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tty {
|
impl Tty {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
buffer: RefCell::new(CircularBuffer::new()).into(),
|
||||||
console: RefCell::new(VirtualConsole::new()).into(),
|
console: RefCell::new(VirtualConsole::new()).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct TtyNode {
|
struct TtyNode<'a> {
|
||||||
console: Rc<RefCell<VirtualConsole>>,
|
tty: &'a Tty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VirtualFileSystem for Tty {
|
impl VirtualFileSystem for Tty {
|
||||||
@@ -34,32 +40,36 @@ impl VirtualFileSystem for Tty {
|
|||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
Ok(Box::new(TtyNode {
|
Ok(Box::new(TtyNode { tty: self }))
|
||||||
console: self.console.clone(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoBase for TtyNode {
|
impl IoBase for TtyNode<'_> {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for TtyNode {
|
impl Read for TtyNode<'_> {
|
||||||
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, Self::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
unimplemented!()
|
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> {
|
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for TtyNode {
|
impl Write for TtyNode<'_> {
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
self.console
|
self.tty
|
||||||
|
.console
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.write_str(str::from_utf8(buf).unwrap());
|
.write_str(str::from_utf8(buf).unwrap());
|
||||||
Ok(buf.len())
|
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 keyboard;
|
||||||
pub mod stdin;
|
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 {}
|
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() {
|
pub unsafe fn init_file_system() {
|
||||||
unsafe {
|
unsafe {
|
||||||
FILE_SYSTEM.mount("/dev/fb0".into(), Box::new(VGAFileSystem));
|
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(
|
FILE_SYSTEM.mount(
|
||||||
"/dev/input/keyboard".into(),
|
"/dev/input/keyboard".into(),
|
||||||
Box::new(KeyboardBuffer::new()),
|
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::{
|
use crate::{
|
||||||
data_structures::circular_buffer::CircularBuffer,
|
data_structures::circular_buffer::CircularBuffer,
|
||||||
println,
|
|
||||||
virtual_fs::{VirtualFileSystem, VirtualNode},
|
virtual_fs::{VirtualFileSystem, VirtualNode},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,15 +54,15 @@ impl IoBase for KeyboardBufferNode<'_> {
|
|||||||
impl Read for KeyboardBufferNode<'_> {
|
impl Read for KeyboardBufferNode<'_> {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
let mut buffer = self.buffer.buffer.borrow_mut();
|
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();
|
buf[i] = buffer.pop().unwrap();
|
||||||
}
|
});
|
||||||
Ok(buf.len())
|
Ok(buf.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Seek for KeyboardBufferNode<'_> {
|
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!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,22 @@
|
|||||||
// use alloc::boxed::Box;
|
// use alloc::boxed::Box;
|
||||||
// use io::{IoBase, Read, Seek, Write};
|
// 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
|
// pub const STDIN_BUFFER_SIZE: usize = 4096; // 4Ko
|
||||||
|
|
||||||
// #[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
// pub struct Stdin {
|
// pub struct Stdin {
|
||||||
// buffer: RefCell<[u8; STDIN_BUFFER_SIZE]>,
|
// buffer: RefCell<CircularBuffer<u8, STDIN_BUFFER_SIZE>>,
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// impl Stdin {
|
// impl Stdin {
|
||||||
// pub fn new() -> Self {
|
// pub fn new() -> Self {
|
||||||
// Self {
|
// Self {
|
||||||
// buffer: RefCell::new([0; _]),
|
// buffer: RefCell::new(CircularBuffer::new()),
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
@@ -44,7 +47,11 @@
|
|||||||
|
|
||||||
// impl Read for StdinNode<'_> {
|
// impl Read for StdinNode<'_> {
|
||||||
// fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
// 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::seek(&mut file, SeekFrom::End(-3));
|
||||||
// syscall::write(&mut file, &[255; 6400 * 50]);
|
// syscall::write(&mut file, &[255; 6400 * 50]);
|
||||||
syscall::sleep(Duration::from_secs_f64(2.0));
|
syscall::sleep(Duration::from_secs_f64(2.0));
|
||||||
let mut stdin = syscall::open("/dev/input/keyboard");
|
let mut stdin = syscall::open("/dev/tty0");
|
||||||
let mut test = [0; 2];
|
|
||||||
syscall::read(&mut stdin, &mut test);
|
|
||||||
let mut file = syscall::open("/dev/tty0");
|
let mut file = syscall::open("/dev/tty0");
|
||||||
syscall::write(&mut file, b"Hi !\nnice tty\x08");
|
loop {
|
||||||
println!(
|
let mut test = [0; 2];
|
||||||
"Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! {:?}",
|
syscall::read(&mut stdin, &mut test);
|
||||||
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