Sync computers
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
target = "riscv64.json"
|
target = "riscv64.json"
|
||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
|
json-target-spec = true
|
||||||
build-std = ["core", "compiler_builtins", "alloc"]
|
build-std = ["core", "compiler_builtins", "alloc"]
|
||||||
build-std-features = ["compiler-builtins-mem"]
|
build-std-features = ["compiler-builtins-mem"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
|
use alloc::string::String;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct Path<'a> {
|
pub struct Path<'a> {
|
||||||
inner: &'a str,
|
inner: &'a str,
|
||||||
}
|
}
|
||||||
@@ -31,3 +36,13 @@ impl<'a> Path<'a> {
|
|||||||
!self.is_absolute()
|
!self.is_absolute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PathBuf {
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for PathBuf {
|
||||||
|
type Target = Path<'a>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {}
|
||||||
|
}
|
||||||
|
|||||||
2
justfile
2
justfile
@@ -1,5 +1,5 @@
|
|||||||
release := ""
|
release := ""
|
||||||
cargo_flags := "-Zjson-target-spec" + if release != "" { "--release" } else { "" }
|
cargo_flags := "" + if release != "" { "--release" } else { "" }
|
||||||
bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" }
|
bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" }
|
||||||
|
|
||||||
default: run
|
default: run
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly"
|
channel = "nightly"
|
||||||
|
components = ["rust-src"]
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
//! supervisor interrupts for short atomic regions.
|
//! supervisor interrupts for short atomic regions.
|
||||||
use critical_section::RawRestoreState;
|
use critical_section::RawRestoreState;
|
||||||
|
|
||||||
use crate::riscv::{disable_supervisor_interrupt, get_supervisor_interrupt_state, restore_supervisor_interrupt};
|
use crate::riscv::{
|
||||||
|
disable_supervisor_interrupt, get_supervisor_interrupt_state, restore_supervisor_interrupt,
|
||||||
|
};
|
||||||
|
|
||||||
struct MyCriticalSection;
|
struct MyCriticalSection;
|
||||||
critical_section::set_impl!(MyCriticalSection);
|
critical_section::set_impl!(MyCriticalSection);
|
||||||
|
|||||||
15
src/fs.rs
15
src/fs.rs
@@ -2,17 +2,12 @@
|
|||||||
//!
|
//!
|
||||||
//! Implements a minimal disk backend and exposes a global FILE_SYSTEM used by
|
//! Implements a minimal disk backend and exposes a global FILE_SYSTEM used by
|
||||||
//! the kernel to load user binaries.
|
//! the kernel to load user binaries.
|
||||||
use core::{
|
use core::{cell::UnsafeCell, ops::Deref};
|
||||||
cell::{LazyCell, UnsafeCell},
|
|
||||||
ops::Deref,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bffs::{
|
use bffs::{
|
||||||
io::{IoBase, Read, Seek},
|
io::{IoBase, Read, Seek},
|
||||||
path::Path,
|
|
||||||
Fat32FileSystem,
|
Fat32FileSystem,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
|
||||||
|
|
||||||
const DISK_ADDR: *const u8 = 0x9000_0000 as *const _;
|
const DISK_ADDR: *const u8 = 0x9000_0000 as *const _;
|
||||||
|
|
||||||
@@ -43,7 +38,7 @@ impl Deref for FSTemp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static FILE_SYSTEM: FSTemp = FSTemp(UnsafeCell::new(None));
|
pub static FAT32_FILE_SYSTEM: FSTemp = FSTemp(UnsafeCell::new(None));
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Simple disk backend that reads from a fixed memory region.
|
/// Simple disk backend that reads from a fixed memory region.
|
||||||
@@ -95,9 +90,3 @@ impl Read for Disk {
|
|||||||
Ok(size)
|
Ok(size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct KernelFDTable(LazyCell<HashMap<u64, Path<'static>>>);
|
|
||||||
|
|
||||||
unsafe impl Sync for KernelFDTable {}
|
|
||||||
|
|
||||||
pub static KERNEL_FILE_DESCRIPTOR_TABLE: KernelFDTable = KernelFDTable(LazyCell::new(HashMap::new));
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use crate::{
|
|||||||
process::{exit_process, sleep, ExecutionContext},
|
process::{exit_process, sleep, ExecutionContext},
|
||||||
read_csr,
|
read_csr,
|
||||||
riscv::disable_interrupt,
|
riscv::disable_interrupt,
|
||||||
scheduler::scheduler_without_ret,
|
scheduler::SCHEDULER,
|
||||||
set_csr, syscall,
|
set_csr, syscall,
|
||||||
time::{setup_next_timer_interrupt, IRQ_M_TIMER},
|
time::{setup_next_timer_interrupt, IRQ_M_TIMER},
|
||||||
write_csr,
|
write_csr,
|
||||||
@@ -123,8 +123,14 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
match syscall {
|
match syscall {
|
||||||
SysCall::Open => {
|
SysCall::Open => {
|
||||||
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
|
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
|
||||||
let fd = syscall::open(path, false);
|
let virtual_node = syscall::open(path, false).unwrap();
|
||||||
unsafe { (*interrupt_state).a[0] = fd.unwrap() };
|
|
||||||
|
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 };
|
||||||
}
|
}
|
||||||
SysCall::Alloc => {
|
SysCall::Alloc => {
|
||||||
let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap();
|
let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap();
|
||||||
@@ -168,7 +174,7 @@ unsafe extern "C" fn supervisor_trap_handler(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
timer_interrupt();
|
timer_interrupt();
|
||||||
scheduler_without_ret(&mut interrupt_state);
|
SCHEDULER.lock().schedule(&mut interrupt_state);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -253,7 +259,7 @@ unsafe extern "C" fn _machine_mode_trap() {
|
|||||||
/// This stub saves the full register state and forwards control to
|
/// This stub saves the full register state and forwards control to
|
||||||
/// `supervisor_trap_handler` implemented in Rust.
|
/// `supervisor_trap_handler` implemented in Rust.
|
||||||
unsafe extern "C" fn _supervisor_mode_trap() {
|
unsafe extern "C" fn _supervisor_mode_trap() {
|
||||||
naked_asm!(concat!(
|
naked_asm!(
|
||||||
"
|
"
|
||||||
// Store sp before it gets modified
|
// Store sp before it gets modified
|
||||||
sd sp, 8-264(sp)
|
sd sp, 8-264(sp)
|
||||||
@@ -304,14 +310,14 @@ unsafe extern "C" fn _supervisor_mode_trap() {
|
|||||||
|
|
||||||
# Restore registers and sret
|
# Restore registers and sret
|
||||||
jal restore_context"
|
jal restore_context"
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(naked)]
|
#[unsafe(naked)]
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
/// Restore a saved execution context and perform `sret` to return to user code.
|
/// Restore a saved execution context and perform `sret` to return to user code.
|
||||||
pub unsafe extern "C" fn restore_context(context: *const ExecutionContext) -> ! {
|
pub unsafe extern "C" fn restore_context(context: *const ExecutionContext) -> ! {
|
||||||
naked_asm!(concat!(
|
naked_asm!(
|
||||||
"
|
"
|
||||||
ld t0, 248(a0)
|
ld t0, 248(a0)
|
||||||
csrw sepc, t0
|
csrw sepc, t0
|
||||||
@@ -351,7 +357,7 @@ pub unsafe extern "C" fn restore_context(context: *const ExecutionContext) -> !
|
|||||||
ld a0, 32(a0)
|
ld a0, 32(a0)
|
||||||
|
|
||||||
sret"
|
sret"
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
//! macros for printing from kernel code.
|
//! macros for printing from kernel code.
|
||||||
use crate::println;
|
use crate::println;
|
||||||
|
|
||||||
use alloc::format;
|
|
||||||
|
|
||||||
use log::{Level, Metadata, Record};
|
use log::{Level, Metadata, Record};
|
||||||
use log::{LevelFilter, SetLoggerError};
|
use log::{LevelFilter, SetLoggerError};
|
||||||
|
|
||||||
@@ -58,7 +56,7 @@ pub fn init_log() -> Result<(), SetLoggerError> {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! print {
|
macro_rules! print {
|
||||||
($($args:expr),*) => {
|
($($args:expr),*) => {
|
||||||
$crate::io::print(format!($($args),*))
|
$crate::io::print(alloc::format!($($args),*))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|||||||
21
src/main.rs
21
src/main.rs
@@ -4,7 +4,7 @@
|
|||||||
//! scheduler, and logging), and starts initial processes.
|
//! scheduler, and logging), and starts initial processes.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![allow(static_mut_refs)]
|
// #![warn(clippy::pedantic)]
|
||||||
#![feature(
|
#![feature(
|
||||||
riscv_ext_intrinsics,
|
riscv_ext_intrinsics,
|
||||||
const_trait_impl,
|
const_trait_impl,
|
||||||
@@ -20,11 +20,10 @@ use embedded_alloc::LlffHeap as Heap;
|
|||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
fs::FILE_SYSTEM,
|
fs::FAT32_FILE_SYSTEM,
|
||||||
io::init_log,
|
io::init_log,
|
||||||
process::{create_process, create_process_from_file},
|
|
||||||
riscv::enable_supervisor_interrupt,
|
riscv::enable_supervisor_interrupt,
|
||||||
scheduler::{idle, scheduler_init},
|
scheduler::{idle, SCHEDULER},
|
||||||
user::{proc2, test},
|
user::{proc2, test},
|
||||||
vga::{Color, Vga},
|
vga::{Color, Vga},
|
||||||
};
|
};
|
||||||
@@ -39,11 +38,13 @@ mod panic_handler;
|
|||||||
mod process;
|
mod process;
|
||||||
mod riscv;
|
mod riscv;
|
||||||
mod scheduler;
|
mod scheduler;
|
||||||
|
mod sync;
|
||||||
mod syscall;
|
mod syscall;
|
||||||
mod time;
|
mod time;
|
||||||
mod uart;
|
mod uart;
|
||||||
mod user;
|
mod user;
|
||||||
mod vga;
|
mod vga;
|
||||||
|
mod virtual_fs;
|
||||||
|
|
||||||
pub const HEAP_SIZE: usize = 1024 * 1024 * 32; // 32Mo RAM
|
pub const HEAP_SIZE: usize = 1024 * 1024 * 32; // 32Mo RAM
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
@@ -58,17 +59,19 @@ pub extern "C" fn supervisor_mode_entry() {
|
|||||||
embedded_alloc::init!(HEAP, HEAP_SIZE);
|
embedded_alloc::init!(HEAP, HEAP_SIZE);
|
||||||
init_log().unwrap();
|
init_log().unwrap();
|
||||||
Vga::init();
|
Vga::init();
|
||||||
FILE_SYSTEM.init();
|
FAT32_FILE_SYSTEM.init();
|
||||||
scheduler_init();
|
SCHEDULER.lock().init();
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Hello World !");
|
info!("Hello World !");
|
||||||
unsafe { Vga::draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
|
unsafe { Vga::draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
|
||||||
|
|
||||||
create_process(Box::new(test), "proc1");
|
SCHEDULER.lock().create_process(Box::new(test), "proc1");
|
||||||
create_process(Box::new(proc2), "proc2");
|
SCHEDULER.lock().create_process(Box::new(proc2), "proc2");
|
||||||
|
|
||||||
create_process_from_file("/usr/bin/test_pic");
|
SCHEDULER
|
||||||
|
.lock()
|
||||||
|
.create_process_from_file("/usr/bin/test_pic");
|
||||||
|
|
||||||
enable_supervisor_interrupt();
|
enable_supervisor_interrupt();
|
||||||
idle();
|
idle();
|
||||||
|
|||||||
389
src/process.rs
389
src/process.rs
@@ -16,10 +16,11 @@ use hashbrown::HashMap;
|
|||||||
use shared::syscall::exit;
|
use shared::syscall::exit;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
fs::FILE_SYSTEM,
|
fs::FAT32_FILE_SYSTEM,
|
||||||
println,
|
println,
|
||||||
scheduler::{scheduler_without_ret, ACTIVE_PID, PROCESS_COUNT, PROCESS_TABLE},
|
scheduler::{Scheduler, SCHEDULER},
|
||||||
time::elapsed_time_since_startup,
|
time::elapsed_time_since_startup,
|
||||||
|
virtual_fs::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).
|
||||||
@@ -83,7 +84,7 @@ pub struct Process {
|
|||||||
/// Current state of the process.
|
/// Current state of the process.
|
||||||
pub state: ProcessState,
|
pub state: ProcessState,
|
||||||
/// Optional entry point for the process code.
|
/// Optional entry point for the process code.
|
||||||
pub entry: Option<Box<dyn Fn()>>,
|
pub entry: Option<Box<dyn Fn() + Send>>,
|
||||||
/// Wake time for sleeping processes.
|
/// Wake time for sleeping processes.
|
||||||
pub wake_time: Duration,
|
pub wake_time: Duration,
|
||||||
/// Saved execution context.
|
/// Saved execution context.
|
||||||
@@ -91,7 +92,37 @@ 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, Path<'static>>
|
pub fd_table: HashMap<u64, Box<dyn VirtualNode + Send>>,
|
||||||
|
/// Next available file descriptor.
|
||||||
|
pub next_fd: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Process {}
|
||||||
|
|
||||||
|
impl Default for Process {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pid: -1,
|
||||||
|
name: String::new(),
|
||||||
|
state: ProcessState::Dead,
|
||||||
|
wake_time: Duration::new(0, 0),
|
||||||
|
ctx: ExecutionContext {
|
||||||
|
ra: core::ptr::null(),
|
||||||
|
sp: core::ptr::null(),
|
||||||
|
gp: 0,
|
||||||
|
tp: 0,
|
||||||
|
a: [0; _],
|
||||||
|
t: [0; _],
|
||||||
|
s: [0; _],
|
||||||
|
mepc: core::ptr::null(),
|
||||||
|
mstatus: 0,
|
||||||
|
},
|
||||||
|
stack: [0; _],
|
||||||
|
entry: None,
|
||||||
|
fd_table: HashMap::new(),
|
||||||
|
next_fd: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::fmt::Debug for Process {
|
impl core::fmt::Debug for Process {
|
||||||
@@ -107,187 +138,185 @@ impl core::fmt::Debug for Process {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a process from a binary file.
|
impl Scheduler {
|
||||||
///
|
/// Creates a process from a binary file.
|
||||||
/// # Arguments
|
///
|
||||||
///
|
/// # Arguments
|
||||||
/// * `path` - Path to the executable binary file.
|
///
|
||||||
///
|
/// * `path` - Path to the executable binary file.
|
||||||
/// # Returns
|
///
|
||||||
///
|
/// # Returns
|
||||||
/// Returns the PID of the created process, or -1 on failure.
|
///
|
||||||
///
|
/// Returns the PID of the created process, or -1 on failure.
|
||||||
/// # Safety
|
///
|
||||||
///
|
/// # Safety
|
||||||
/// This function uses `unsafe` to transmute the file content into an
|
///
|
||||||
/// executable function. The binary must be in the correct format and
|
/// This function uses `unsafe` to transmute the file content into an
|
||||||
/// conform to the expected ABI.
|
/// executable function. The binary must be in the correct format and
|
||||||
/// Create a process from an executable binary located on the filesystem.
|
/// conform to the expected ABI.
|
||||||
///
|
/// Create a process from an executable binary located on the filesystem.
|
||||||
/// 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
|
/// Attempts to open `path`, load its contents into memory and create a new
|
||||||
/// created process, or -1 on failure.
|
/// kernel process that will execute the loaded binary. Returns the PID of the
|
||||||
pub fn create_process_from_file<'a, T: Into<Path<'a>>>(path: T) -> i64 {
|
/// created process, or -1 on failure.
|
||||||
let path = path.into();
|
pub fn create_process_from_file<'a, T: Into<Path<'a>>>(&mut self, path: T) -> i64 {
|
||||||
let name = path.as_str();
|
let path = path.into();
|
||||||
|
let name = path.as_str();
|
||||||
|
|
||||||
// Open and read the binary file
|
// Open and read the binary file
|
||||||
let mut bin = FILE_SYSTEM.open_file(path).unwrap();
|
let mut bin = FAT32_FILE_SYSTEM.open_file(path).unwrap();
|
||||||
println!("Creating process");
|
println!("Creating process");
|
||||||
let mut content: Vec<u8> = Vec::new();
|
let mut content: Vec<u8> = Vec::new();
|
||||||
bin.read_to_end(&mut content).unwrap();
|
bin.read_to_end(&mut content).unwrap();
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Loading binary at address: {:x?}, length: {}",
|
"Loading binary at address: {:x?}, length: {}",
|
||||||
content.as_ptr(),
|
content.as_ptr(),
|
||||||
content.len()
|
content.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
// If ELF, use goblin to load PT_LOAD segments and apply relocations
|
// If ELF, use goblin to load PT_LOAD segments and apply relocations
|
||||||
if let Ok(gelf) = goblin::elf::Elf::parse(&content) {
|
if let Ok(gelf) = goblin::elf::Elf::parse(&content) {
|
||||||
println!("Parsed");
|
println!("Parsed");
|
||||||
// Determine memory bounds from program headers
|
// Determine memory bounds from program headers
|
||||||
let mut min_vaddr = u64::MAX;
|
let mut min_vaddr = u64::MAX;
|
||||||
let mut max_vaddr = 0u64;
|
let mut max_vaddr = 0u64;
|
||||||
for ph in gelf.program_headers.iter() {
|
|
||||||
if ph.p_type == goblin::elf::program_header::PT_LOAD && ph.p_memsz > 0 {
|
|
||||||
min_vaddr = core::cmp::min(min_vaddr, ph.p_vaddr);
|
|
||||||
max_vaddr = core::cmp::max(max_vaddr, ph.p_vaddr + ph.p_memsz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if min_vaddr != u64::MAX {
|
|
||||||
let size = (max_vaddr - min_vaddr) as usize;
|
|
||||||
use alloc::alloc::{alloc_zeroed, Layout};
|
|
||||||
let layout = Layout::from_size_align(size, 0x1000).unwrap();
|
|
||||||
let base = unsafe { alloc_zeroed(layout) };
|
|
||||||
|
|
||||||
// Copy segments
|
|
||||||
for ph in gelf.program_headers.iter() {
|
for ph in gelf.program_headers.iter() {
|
||||||
if ph.p_type == goblin::elf::program_header::PT_LOAD {
|
if ph.p_type == goblin::elf::program_header::PT_LOAD && ph.p_memsz > 0 {
|
||||||
let dst = unsafe { base.add((ph.p_vaddr - min_vaddr) as usize) };
|
min_vaddr = core::cmp::min(min_vaddr, ph.p_vaddr);
|
||||||
let src_off = ph.p_offset as usize;
|
max_vaddr = core::cmp::max(max_vaddr, ph.p_vaddr + ph.p_memsz);
|
||||||
let copy_len = ph.p_filesz as usize;
|
}
|
||||||
if copy_len > 0 {
|
}
|
||||||
unsafe {
|
|
||||||
core::ptr::copy_nonoverlapping(
|
if min_vaddr != u64::MAX {
|
||||||
content.as_ptr().add(src_off),
|
let size = (max_vaddr - min_vaddr) as usize;
|
||||||
dst,
|
use alloc::alloc::{alloc_zeroed, Layout};
|
||||||
copy_len,
|
let layout = Layout::from_size_align(size, 0x1000).unwrap();
|
||||||
)
|
let base = unsafe { alloc_zeroed(layout) };
|
||||||
|
|
||||||
|
// Copy segments
|
||||||
|
for ph in gelf.program_headers.iter() {
|
||||||
|
if ph.p_type == goblin::elf::program_header::PT_LOAD {
|
||||||
|
let dst = unsafe { base.add((ph.p_vaddr - min_vaddr) as usize) };
|
||||||
|
let src_off = ph.p_offset as usize;
|
||||||
|
let copy_len = ph.p_filesz as usize;
|
||||||
|
if copy_len > 0 {
|
||||||
|
unsafe {
|
||||||
|
core::ptr::copy_nonoverlapping(
|
||||||
|
content.as_ptr().add(src_off),
|
||||||
|
dst,
|
||||||
|
copy_len,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if ph.p_memsz as usize > copy_len {
|
||||||
if ph.p_memsz as usize > copy_len {
|
unsafe {
|
||||||
unsafe {
|
core::slice::from_raw_parts_mut(
|
||||||
core::slice::from_raw_parts_mut(
|
dst.add(copy_len),
|
||||||
dst.add(copy_len),
|
ph.p_memsz as usize - copy_len,
|
||||||
ph.p_memsz as usize - copy_len,
|
)
|
||||||
)
|
.fill(0)
|
||||||
.fill(0)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
println!("Copied");
|
||||||
println!("Copied");
|
|
||||||
|
|
||||||
// Apply relocations using our parser (handles RELA entries)
|
// Apply relocations using our parser (handles RELA entries)
|
||||||
for rela in gelf.dynrelas.iter() {
|
for rela in gelf.dynrelas.iter() {
|
||||||
let r_type = rela.r_type;
|
let r_type = rela.r_type;
|
||||||
match r_type {
|
match r_type {
|
||||||
x if x == R_RISCV_RELATIVE => {
|
x if x == R_RISCV_RELATIVE => {
|
||||||
let where_off = (rela.r_offset - min_vaddr) as usize;
|
let where_off = (rela.r_offset - min_vaddr) as usize;
|
||||||
let where_ptr = unsafe { base.add(where_off) } as *mut u64;
|
let where_ptr = unsafe { base.add(where_off) } as *mut u64;
|
||||||
let val = (base as u64).wrapping_add(rela.r_addend.unwrap() as u64);
|
let val = (base as u64).wrapping_add(rela.r_addend.unwrap() as u64);
|
||||||
unsafe { core::ptr::write_unaligned(where_ptr, val) };
|
unsafe { core::ptr::write_unaligned(where_ptr, val) };
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
println!("Relocated");
|
||||||
println!("Relocated");
|
|
||||||
|
|
||||||
// Entry point
|
// Entry point
|
||||||
let entry_va = gelf.entry;
|
let entry_va = gelf.entry;
|
||||||
let entry_addr = unsafe { base.add((entry_va - min_vaddr) as usize) } as *const u8;
|
let entry_addr = unsafe { base.add((entry_va - min_vaddr) as usize) } as *const u8;
|
||||||
let entry_fn =
|
let entry_fn =
|
||||||
unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(entry_addr) };
|
unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(entry_addr) };
|
||||||
let wrapper = Box::new(move || {
|
let wrapper = Box::new(move || {
|
||||||
entry_fn();
|
entry_fn();
|
||||||
});
|
});
|
||||||
println!("Program loaded at : {:x?}", entry_addr);
|
println!("Program loaded at : {:x?}", entry_addr);
|
||||||
return create_process(wrapper, name);
|
return self.create_process(wrapper, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
};
|
||||||
|
let wrapper = Box::new(move || {
|
||||||
|
entry_point();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.create_process(wrapper, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new process with the specified code and name.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `code` - Static reference to the function to execute.
|
||||||
|
/// * `name` - Name of the process (for identification).
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns the PID of the created process, or -1 if the process table is full.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function manipulates the global process table and initializes
|
||||||
|
/// the execution context using unsafe operations.
|
||||||
|
/// Create a new process from a function pointer.
|
||||||
|
///
|
||||||
|
/// 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>(
|
||||||
|
&mut self,
|
||||||
|
code: Box<F>,
|
||||||
|
name: T,
|
||||||
|
) -> 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()));
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// mepc points to process_launcher which will call the function
|
||||||
|
process.ctx.mepc = process_launcher as *const _;
|
||||||
|
|
||||||
|
// Configure mstatus for supervisor mode with interrupts enabled
|
||||||
|
process.ctx.mstatus = MSTATUS_SPP | MSTATUS_SPIE;
|
||||||
|
|
||||||
|
// Initialize stack pointer at the top of the stack
|
||||||
|
process.ctx.sp = &raw const process.stack[STACK_SIZE - 1];
|
||||||
|
|
||||||
|
self.next_pid += 1;
|
||||||
|
|
||||||
|
(self.next_pid - 1) as i64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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()) };
|
|
||||||
let wrapper = Box::new(move || {
|
|
||||||
entry_point();
|
|
||||||
});
|
|
||||||
|
|
||||||
create_process(wrapper, name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new process with the specified code and name.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `code` - Static reference to the function to execute.
|
|
||||||
/// * `name` - Name of the process (for identification).
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// Returns the PID of the created process, or -1 if the process table is full.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This function manipulates the global process table and initializes
|
|
||||||
/// the execution context using unsafe operations.
|
|
||||||
/// Create a new process from a function pointer.
|
|
||||||
///
|
|
||||||
/// 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>(code: Box<F>, name: T) -> i64 {
|
|
||||||
// Search for a free slot in the process table
|
|
||||||
let mut next_pid = 0;
|
|
||||||
while next_pid < PROCESS_COUNT && unsafe { PROCESS_TABLE[next_pid].state != ProcessState::Dead }
|
|
||||||
{
|
|
||||||
next_pid += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a slot is available
|
|
||||||
if next_pid >= PROCESS_COUNT {
|
|
||||||
return -1; // Process table is full
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: Initializing process in the global table.
|
|
||||||
// Access is safe because we verified bounds and found a Dead slot.
|
|
||||||
unsafe {
|
|
||||||
let process = &mut PROCESS_TABLE[next_pid];
|
|
||||||
|
|
||||||
// Configure process metadata
|
|
||||||
process.pid = next_pid as i64;
|
|
||||||
process.name = name.into();
|
|
||||||
process.state = ProcessState::Activable;
|
|
||||||
process.entry = Some(code);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// mepc points to process_launcher which will call the function
|
|
||||||
process.ctx.mepc = process_launcher as *const _;
|
|
||||||
|
|
||||||
// Configure mstatus for supervisor mode with interrupts enabled
|
|
||||||
process.ctx.mstatus = MSTATUS_SPP | MSTATUS_SPIE;
|
|
||||||
|
|
||||||
// Initialize stack pointer at the top of the stack
|
|
||||||
process.ctx.sp = &raw const process.stack[STACK_SIZE - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
next_pid as i64
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Entry point to launch a new process.
|
/// Entry point to launch a new process.
|
||||||
///
|
///
|
||||||
/// This function is automatically called during the first scheduling
|
/// This function is automatically called during the first scheduling
|
||||||
@@ -334,12 +363,11 @@ extern "C" fn process_launcher(code: *const Box<dyn Fn()>) {
|
|||||||
/// to select the next runnable process. This function does not return.
|
/// to select the next runnable process. This function does not return.
|
||||||
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.
|
||||||
unsafe {
|
let mut scheduler = SCHEDULER.lock();
|
||||||
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Dead;
|
let active_pid = scheduler.active_pid;
|
||||||
}
|
scheduler.process_table.remove(&active_pid).unwrap();
|
||||||
|
|
||||||
// Transfer control to the scheduler (does not return)
|
// Transfer control to the scheduler (does not return)
|
||||||
scheduler_without_ret(interrupt_context)
|
scheduler.schedule(interrupt_context)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Puts the active process to sleep for a specified duration.
|
/// Puts the active process to sleep for a specified duration.
|
||||||
@@ -362,12 +390,11 @@ pub fn exit_process(interrupt_context: &mut *mut ExecutionContext) {
|
|||||||
/// reactivate the process when the wake time is reached.
|
/// reactivate the process when the wake time is reached.
|
||||||
pub fn sleep(duration: Duration, interrupt_context: &mut *mut ExecutionContext) {
|
pub fn sleep(duration: Duration, 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.
|
||||||
unsafe {
|
let mut scheduler = SCHEDULER.lock();
|
||||||
let process = &mut PROCESS_TABLE[ACTIVE_PID];
|
let process = scheduler.get_current_process();
|
||||||
process.wake_time = elapsed_time_since_startup() + duration;
|
process.wake_time = elapsed_time_since_startup() + duration;
|
||||||
process.state = ProcessState::Asleep;
|
process.state = ProcessState::Asleep;
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer control to the scheduler (does not return)
|
// Transfer control to the scheduler (does not return)
|
||||||
scheduler_without_ret(interrupt_context)
|
scheduler.schedule(interrupt_context)
|
||||||
}
|
}
|
||||||
|
|||||||
148
src/scheduler.rs
148
src/scheduler.rs
@@ -3,50 +3,29 @@
|
|||||||
//!
|
//!
|
||||||
//! 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, array, cell::LazyCell, time::Duration};
|
use core::{arch::riscv64::wfi, cell::LazyCell, ops::Bound};
|
||||||
|
|
||||||
use alloc::{boxed::Box, string::String};
|
use alloc::{boxed::Box, collections::BTreeMap};
|
||||||
use hashbrown::HashMap;
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
process::{create_process, ExecutionContext, Process, ProcessState},
|
process::{ExecutionContext, Process, ProcessState},
|
||||||
|
sync::Mutex,
|
||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Maximum number of simultaneous processes supported by the kernel.
|
#[derive(Debug)]
|
||||||
pub const PROCESS_COUNT: usize = 16;
|
pub struct Scheduler {
|
||||||
|
pub next_pid: u64,
|
||||||
|
pub active_pid: u64,
|
||||||
|
pub process_table: BTreeMap<u64, Box<Process>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Currently active PID.
|
pub static SCHEDULER: Mutex<LazyCell<Scheduler>> = Mutex::new(LazyCell::new(|| Scheduler {
|
||||||
///
|
next_pid: 0,
|
||||||
/// Updated by the scheduler when switching contexts.
|
active_pid: 0,
|
||||||
pub static mut ACTIVE_PID: usize = 0;
|
process_table: BTreeMap::new(),
|
||||||
/// Global process table stored in a lazily-initialized container.
|
}));
|
||||||
///
|
|
||||||
/// Each entry represents a process slot which may be `Dead`, `Activable`,
|
|
||||||
/// `Active` or `Asleep`.
|
|
||||||
pub static mut PROCESS_TABLE: LazyCell<[Process; PROCESS_COUNT]> = LazyCell::new(|| {
|
|
||||||
array::from_fn(|_| Process {
|
|
||||||
pid: -1,
|
|
||||||
name: String::new(),
|
|
||||||
state: ProcessState::Dead,
|
|
||||||
wake_time: Duration::new(0, 0),
|
|
||||||
ctx: ExecutionContext {
|
|
||||||
ra: core::ptr::null(),
|
|
||||||
sp: core::ptr::null(),
|
|
||||||
gp: 0,
|
|
||||||
tp: 0,
|
|
||||||
a: [0; _],
|
|
||||||
t: [0; _],
|
|
||||||
s: [0; _],
|
|
||||||
mepc: core::ptr::null(),
|
|
||||||
mstatus: 0,
|
|
||||||
},
|
|
||||||
stack: [0; _],
|
|
||||||
entry: None,
|
|
||||||
fd_table: HashMap::new()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Idle loop executed when there is no runnable process.
|
/// Idle loop executed when there is no runnable process.
|
||||||
///
|
///
|
||||||
@@ -61,53 +40,60 @@ pub fn idle() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the scheduler and create the idle process.
|
impl Scheduler {
|
||||||
///
|
/// Initialize the scheduler and create the idle process.
|
||||||
/// Marks all process slots as `Dead` then creates the idle process and sets
|
///
|
||||||
/// it as the active process.
|
/// Marks all process slots as `Dead` then creates the idle process and sets
|
||||||
pub fn scheduler_init() {
|
/// it as the active process.
|
||||||
info!("scheduler init");
|
pub fn init(&mut self) {
|
||||||
for pid in 0..PROCESS_COUNT {
|
info!("scheduler init");
|
||||||
|
self.create_process(Box::new(idle), "idle");
|
||||||
|
self.process_table.get_mut(&0).unwrap().state = ProcessState::Active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Round-robin scheduler used to select the next runnable process.
|
||||||
|
///
|
||||||
|
/// Saves the provided interrupt context into the previous process slot and
|
||||||
|
/// updates `ACTIVE_PID` to point to the chosen process. This function does
|
||||||
|
/// not return but instead updates `interrupt_state` to the context of the
|
||||||
|
/// next process to run.
|
||||||
|
pub fn schedule(&mut self, interrupt_state: &mut *mut ExecutionContext) {
|
||||||
|
// info!("scheduler");
|
||||||
unsafe {
|
unsafe {
|
||||||
PROCESS_TABLE[pid].state = ProcessState::Dead;
|
let prev_pid = self.active_pid;
|
||||||
|
if let Some(previous_process) = self.process_table.get_mut(&prev_pid) {
|
||||||
|
previous_process.ctx = **interrupt_state;
|
||||||
|
|
||||||
|
if previous_process.state == ProcessState::Active {
|
||||||
|
previous_process.state = ProcessState::Activable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_process_iter = self
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn get_current_process(&mut self) -> &mut Process {
|
||||||
create_process(Box::new(idle), "idle");
|
self.process_table.get_mut(&self.active_pid).unwrap()
|
||||||
unsafe {
|
|
||||||
PROCESS_TABLE[0].state = ProcessState::Active;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Round-robin scheduler used to select the next runnable process.
|
|
||||||
///
|
|
||||||
/// Saves the provided interrupt context into the previous process slot and
|
|
||||||
/// updates `ACTIVE_PID` to point to the chosen process. This function does
|
|
||||||
/// not return but instead updates `interrupt_state` to the context of the
|
|
||||||
/// next process to run.
|
|
||||||
pub fn scheduler_without_ret(interrupt_state: &mut *mut ExecutionContext) {
|
|
||||||
// info!("scheduler");
|
|
||||||
unsafe {
|
|
||||||
let prev_pid = ACTIVE_PID;
|
|
||||||
PROCESS_TABLE[prev_pid].ctx = **interrupt_state;
|
|
||||||
|
|
||||||
if PROCESS_TABLE[prev_pid].state == ProcessState::Active {
|
|
||||||
PROCESS_TABLE[prev_pid].state = ProcessState::Activable;
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Asleep
|
|
||||||
&& time::elapsed_time_since_startup() > PROCESS_TABLE[ACTIVE_PID].wake_time
|
|
||||||
{
|
|
||||||
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Activable;
|
|
||||||
}
|
|
||||||
ACTIVE_PID = (ACTIVE_PID + 1) % PROCESS_COUNT;
|
|
||||||
if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Activable {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Active;
|
|
||||||
|
|
||||||
*interrupt_state = &raw mut PROCESS_TABLE[ACTIVE_PID].ctx
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
114
src/sync.rs
Normal file
114
src/sync.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
use core::{
|
||||||
|
arch::riscv64::wfi,
|
||||||
|
cell::UnsafeCell,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Mutex<T> {
|
||||||
|
locked: AtomicBool,
|
||||||
|
value: UnsafeCell<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Mutex<T> {
|
||||||
|
pub const fn new(value: T) -> Self {
|
||||||
|
Self {
|
||||||
|
locked: AtomicBool::new(false),
|
||||||
|
value: UnsafeCell::new(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lock(&self) -> MutexGuard<'_, T> {
|
||||||
|
// Lock
|
||||||
|
while self
|
||||||
|
.locked
|
||||||
|
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
unsafe { wfi() };
|
||||||
|
}
|
||||||
|
MutexGuard { mutex: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: Send> Send for Mutex<T> {}
|
||||||
|
unsafe impl<T: Send> Sync for Mutex<T> {}
|
||||||
|
|
||||||
|
pub struct MutexGuard<'a, T> {
|
||||||
|
mutex: &'a Mutex<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for MutexGuard<'_, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.mutex.locked.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Deref for MutexGuard<'a, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { &*self.mutex.value.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DerefMut for MutexGuard<'a, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
unsafe { &mut *self.mutex.value.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// union Data<T, F> {
|
||||||
|
// value: ManuallyDrop<T>,
|
||||||
|
// f: ManuallyDrop<F>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub struct LazyLock<T, F = fn() -> T> {
|
||||||
|
// init: UnsafeCell<bool>,
|
||||||
|
// data: UnsafeCell<Data<T, F>>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<T, F: Fn() -> T> LazyLock<T, F> {
|
||||||
|
// pub const fn new(f: F) -> Self {
|
||||||
|
// Self {
|
||||||
|
// init: UnsafeCell::new(false),
|
||||||
|
// data: UnsafeCell::new(Data {
|
||||||
|
// f: ManuallyDrop::new(f),
|
||||||
|
// }),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn force(&self) -> &T {
|
||||||
|
// unsafe {
|
||||||
|
// critical_section::with(|_| {
|
||||||
|
// if !*self.init.get() {
|
||||||
|
// *(*self.data.get()).value = ((*self.data.get()).f)();
|
||||||
|
// *self.init.get() = true;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// &(*self.data.get()).value
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// pub fn force_mut(&mut self) -> &mut T {
|
||||||
|
// self.force();
|
||||||
|
|
||||||
|
// unsafe { &mut self.data.get_mut().value }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl<T, F: Fn() -> T> Deref for LazyLock<T, F> {
|
||||||
|
// type Target = T;
|
||||||
|
|
||||||
|
// fn deref(&self) -> &Self::Target {
|
||||||
|
// self.force()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// impl<T, F: Fn() -> T> DerefMut for LazyLock<T, F> {
|
||||||
|
// fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
// self.force_mut()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {}
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
use core::alloc::Layout;
|
use core::alloc::Layout;
|
||||||
|
|
||||||
|
use alloc::boxed::Box;
|
||||||
use bffs::{error::Error, path::Path};
|
use bffs::{error::Error, path::Path};
|
||||||
|
|
||||||
use crate::fs::{Disk, FILE_SYSTEM};
|
use crate::{
|
||||||
|
fs::{Disk, FAT32_FILE_SYSTEM},
|
||||||
|
virtual_fs::VirtualNode,
|
||||||
|
};
|
||||||
|
|
||||||
pub unsafe fn alloc(layout: Layout) -> *mut u8 {
|
pub unsafe fn alloc(layout: Layout) -> *mut u8 {
|
||||||
unsafe { alloc::alloc::alloc(layout) }
|
unsafe { alloc::alloc::alloc(layout) }
|
||||||
@@ -15,13 +19,13 @@ pub unsafe fn dealloc(ptr: *mut u8, layout: core::alloc::Layout) {
|
|||||||
pub fn open<'a, P: Into<Path<'a>>>(
|
pub fn open<'a, P: Into<Path<'a>>>(
|
||||||
path: P,
|
path: P,
|
||||||
in_kernel: bool,
|
in_kernel: bool,
|
||||||
) -> Result<u64, Error<<Disk as bffs::io::IoBase>::Error>> {
|
) -> Result<Box<dyn VirtualNode + Send>, Error<<Disk as bffs::io::IoBase>::Error>> {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let file = match path.split_path() {
|
let file = match path.split_path() {
|
||||||
("dev", path) => {
|
("dev", path) => {
|
||||||
unimplemented!()
|
todo!()
|
||||||
}
|
}
|
||||||
_ => FILE_SYSTEM.open_file(path)?,
|
_ => FAT32_FILE_SYSTEM.open_file(path)?,
|
||||||
};
|
};
|
||||||
Ok(42)
|
todo!()
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/time.rs
14
src/time.rs
@@ -1,7 +1,10 @@
|
|||||||
//! Time and timer interrupt management for the kernel.
|
//! Time and timer interrupt management for the kernel.
|
||||||
|
|
||||||
use core::time::Duration;
|
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
|
use core::{
|
||||||
|
sync::atomic::{AtomicU64, Ordering},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
set_csr,
|
set_csr,
|
||||||
@@ -21,16 +24,16 @@ 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 = 20; // 20 Hz
|
||||||
|
|
||||||
/// Stores the instant when the kernel started.
|
/// Stores the instant when the kernel started.
|
||||||
static mut START_TIME: Instant = Instant(0);
|
static START_TIME: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
/// Initializes the timer interrupt system and records the kernel start time.
|
/// Initializes the timer interrupt system and records the kernel start time.
|
||||||
///
|
///
|
||||||
/// This should be called once during kernel initialization.
|
/// This should be called once during kernel initialization.
|
||||||
pub fn setup_timer_interrupt() {
|
pub fn setup_timer_interrupt() {
|
||||||
unsafe { START_TIME = Instant::now(); }
|
START_TIME.store(Instant::now().0, Ordering::Relaxed);
|
||||||
set_csr!(sie, IRQ_S_TIMER);
|
set_csr!(sie, IRQ_S_TIMER);
|
||||||
setup_next_timer_interrupt();
|
setup_next_timer_interrupt();
|
||||||
}
|
}
|
||||||
@@ -69,7 +72,8 @@ pub fn timer_interrupt() {
|
|||||||
|
|
||||||
/// Returns the duration since the kernel was started.
|
/// Returns the duration since the kernel was started.
|
||||||
pub fn elapsed_time_since_startup() -> Duration {
|
pub fn elapsed_time_since_startup() -> Duration {
|
||||||
unsafe { START_TIME.elapsed() }
|
let start_instant = Instant(START_TIME.load(Ordering::Relaxed));
|
||||||
|
start_instant.elapsed()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a point in time, based on the hardware timer.
|
/// Represents a point in time, based on the hardware timer.
|
||||||
|
|||||||
@@ -4,16 +4,9 @@
|
|||||||
//! and the scheduler.
|
//! and the scheduler.
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
|
|
||||||
use shared::syscall::{sleep, write_int_temp, write_string_temp};
|
use shared::syscall::{sleep, write_string_temp};
|
||||||
|
|
||||||
#[repr(align(32))]
|
|
||||||
struct Alignement([u8; include_bytes!("../user/test_pic/test_pic.mem").len()]);
|
|
||||||
|
|
||||||
static PROG: Alignement = Alignement(*include_bytes!("../user/test_pic/test_pic.mem"));
|
|
||||||
|
|
||||||
pub fn test() {
|
pub fn test() {
|
||||||
write_int_temp(PROG.0.as_ptr() as u64);
|
|
||||||
// unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(PROG.0.as_ptr())() }
|
|
||||||
loop {
|
loop {
|
||||||
write_string_temp("test");
|
write_string_temp("test");
|
||||||
sleep(Duration::new(2, 0));
|
sleep(Duration::new(2, 0));
|
||||||
|
|||||||
21
src/virtual_fs.rs
Normal file
21
src/virtual_fs.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use alloc::boxed::Box;
|
||||||
|
use bffs::path::Path;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
pub trait VirtualNode {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait VirtualFileSystem {
|
||||||
|
fn open<'a, P: Into<Path<'a>>>(path: P) -> Result<Box<dyn VirtualNode + Send>, ()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MainFileSystem {
|
||||||
|
mounts: HashMap<PathBuf>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtualFileSystem for MainFileSystem {
|
||||||
|
fn open<'a, P: Into<Path<'a>>>(path: P) -> Result<Box<dyn VirtualNode + Send>, ()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user