mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45:19 +08:00
Compare commits
9 Commits
7d5875cb0c
...
3f6f0ac18c
Author | SHA1 | Date | |
---|---|---|---|
3f6f0ac18c | |||
b37fef3e26 | |||
32e21b3468 | |||
5d5c18592f | |||
8897e6467c | |||
f005c8254b | |||
001550b387 | |||
0d0a5471af | |||
cdcc709d70 |
16
.vscode/launch.json
vendored
16
.vscode/launch.json
vendored
@ -19,7 +19,13 @@
|
|||||||
{
|
{
|
||||||
"text": "set output-radix 16"
|
"text": "set output-radix 16"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"sourceFileMap": {
|
||||||
|
"${workspaceFolder}": {
|
||||||
|
"editorPath": "${workspaceFolder}",
|
||||||
|
"useForBreakpoints": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Debug Kernel",
|
"name": "Debug Kernel",
|
||||||
@ -39,7 +45,13 @@
|
|||||||
{
|
{
|
||||||
"text": "set output-radix 16"
|
"text": "set output-radix 16"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"sourceFileMap": {
|
||||||
|
"${workspaceFolder}": {
|
||||||
|
"editorPath": "${workspaceFolder}",
|
||||||
|
"useForBreakpoints": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -252,6 +252,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use crate::entry::HART_ID;
|
use crate::entry::HART_ID;
|
||||||
|
use crate::objects::tcb::TcbObject;
|
||||||
use crate::plat::console::CONSOLE;
|
use crate::plat::console::CONSOLE;
|
||||||
use crate::plat::timer::{Timer, TimerOps};
|
use crate::plat::timer::{Timer, TimerOps};
|
||||||
use crate::plat::trap::{Trap, TrapContextOps, TrapOps};
|
use crate::plat::trap::{Trap, TrapContextOps, TrapOps};
|
||||||
use crate::scheduler::SCHEDULER;
|
use core::ops::{Index, IndexMut};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use riscv::register::scause::{Exception as E, Interrupt as I, Trap as T};
|
use riscv::register::scause::{Exception as E, Interrupt as I, Trap as T};
|
||||||
use riscv::register::stvec::TrapMode;
|
use riscv::register::stvec::TrapMode;
|
||||||
@ -17,12 +18,45 @@ extern "C" {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn kernel_trap_handler(tf: &mut TrapContext) {
|
extern "C" fn kernel_trap_handler(tf: &mut TrapContext) {
|
||||||
|
// No trap in kernel, except from IDLE thread
|
||||||
// TODO: Replace CONSOLE Mutex to ReentrantLock
|
// TODO: Replace CONSOLE Mutex to ReentrantLock
|
||||||
unsafe { CONSOLE.force_unlock() }
|
unsafe { CONSOLE.force_unlock() }
|
||||||
trap_handler(tf, true)
|
tf.handle_trap(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trap_handler(tf: &mut TrapContext, from_kernel: bool) {
|
impl TrapOps for Trap {
|
||||||
|
fn init() {
|
||||||
|
// anyway, clear sscratch
|
||||||
|
riscv::register::sscratch::write(0);
|
||||||
|
// set trap_entry
|
||||||
|
unsafe { riscv::register::stvec::write(trap_entry as usize, TrapMode::Direct) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[naked]
|
||||||
|
extern "C" fn idle_thread() {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"
|
||||||
|
1: wfi
|
||||||
|
j 1b
|
||||||
|
",
|
||||||
|
options(noreturn)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrapContextOps for TrapContext {
|
||||||
|
fn restore(&mut self) {
|
||||||
|
unsafe { switch_to_user(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_idle_thread(&mut self) {
|
||||||
|
self.sepc = idle_thread as usize;
|
||||||
|
self.sstatus = (1 << 5) | (1 << 8); // SPIE + SPP
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_trap(&mut self, from_kernel: bool) {
|
||||||
let scause = riscv::register::scause::read();
|
let scause = riscv::register::scause::read();
|
||||||
trace!("[Trap] cpu@{} scause: {:?}", HART_ID.get(), scause.cause());
|
trace!("[Trap] cpu@{} scause: {:?}", HART_ID.get(), scause.cause());
|
||||||
|
|
||||||
@ -33,23 +67,24 @@ fn trap_handler(tf: &mut TrapContext, from_kernel: bool) {
|
|||||||
$msg,
|
$msg,
|
||||||
scause.cause(),
|
scause.cause(),
|
||||||
scause.bits(),
|
scause.bits(),
|
||||||
tf.sepc,
|
self.sepc,
|
||||||
riscv::register::stval::read()
|
riscv::register::stval::read()
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tcb = self.as_object_mut();
|
||||||
|
|
||||||
match scause.cause() {
|
match scause.cause() {
|
||||||
T::Interrupt(I::SupervisorTimer) => {
|
T::Interrupt(I::SupervisorTimer) => {
|
||||||
Timer::do_tick();
|
Timer::do_tick();
|
||||||
|
|
||||||
if !from_kernel {
|
if !from_kernel {
|
||||||
SCHEDULER.schedule()
|
tcb.timer_tick();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
T::Exception(E::UserEnvCall) => {
|
T::Exception(E::UserEnvCall) => {
|
||||||
// TODO: handle syscall
|
tcb.handle_syscall();
|
||||||
tf.sepc += 4;
|
self.sepc += 4;
|
||||||
},
|
},
|
||||||
T::Interrupt(I::SupervisorExternal) => {
|
T::Interrupt(I::SupervisorExternal) => {
|
||||||
// TODO: handle external interrupt, e.g. plic
|
// TODO: handle external interrupt, e.g. plic
|
||||||
@ -64,25 +99,31 @@ fn trap_handler(tf: &mut TrapContext, from_kernel: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Timer::set_next();
|
Timer::set_next();
|
||||||
trace!("[Trap] exiting...");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl TrapOps for Trap {
|
fn as_object(&self) -> &TcbObject {
|
||||||
fn init() {
|
unsafe { &*(self as *const TrapContext as *const TcbObject) }
|
||||||
// anyway, clear sscratch
|
}
|
||||||
riscv::register::sscratch::write(0);
|
|
||||||
// set trap_entry
|
fn as_object_mut(&mut self) -> &mut TcbObject {
|
||||||
unsafe { riscv::register::stvec::write(trap_entry as usize, TrapMode::Direct) }
|
unsafe { &mut *(self as *mut TrapContext as *mut TcbObject) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_reg(&self, index: usize) -> usize {
|
||||||
|
// x10 ~ x17: Function arguments
|
||||||
|
assert!(index < 8, "TrapContext get_reg index out of range");
|
||||||
|
self.gprs[index + 10]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_reg(&mut self, index: usize, value: usize) {
|
||||||
|
// x10 ~ x17: Function arguments
|
||||||
|
// x10 ~ x11: Function return values
|
||||||
|
assert!(index < 8, "TrapContext set_reg index out of range");
|
||||||
|
self.gprs[index + 10] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrapContextOps for TrapContext {
|
#[derive(Debug, Default)]
|
||||||
fn restore(&mut self) {
|
|
||||||
unsafe { switch_to_user(self) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct GeneralRegs {
|
pub struct GeneralRegs {
|
||||||
pub ra: usize,
|
pub ra: usize,
|
||||||
@ -118,7 +159,25 @@ pub struct GeneralRegs {
|
|||||||
pub t6: usize,
|
pub t6: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl Index<usize> for GeneralRegs {
|
||||||
|
type Output = usize;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
assert!(index < 32, "GeneralRegs index out of range");
|
||||||
|
let regs = unsafe { &*(self as *const _ as *const [usize; 32]) };
|
||||||
|
®s[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<usize> for GeneralRegs {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
assert!(index < 32, "GeneralRegs index out of range");
|
||||||
|
let regs = unsafe { &mut *(self as *mut _ as *mut [usize; 32]) };
|
||||||
|
&mut regs[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct TrapContext {
|
pub struct TrapContext {
|
||||||
// [0:30] GPRs exclude zero
|
// [0:30] GPRs exclude zero
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
use utils::{
|
use crate::{arch::layout::*, vspace::TableLevel};
|
||||||
addr::{AddressOps, VirtAddr},
|
use utils::{addr::*, MASK};
|
||||||
MASK,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::vspace::TableLevel;
|
|
||||||
|
|
||||||
#[cfg(feature = "riscv.pagetable.sv39")]
|
#[cfg(feature = "riscv.pagetable.sv39")]
|
||||||
mod sv39;
|
mod sv39;
|
||||||
@ -27,6 +23,13 @@ trait GenericPhysAddrPage: AddressOps {
|
|||||||
const PA_PPN_MASK: usize = MASK!(Self::PPN_BITS) << Self::PG_OFFSET;
|
const PA_PPN_MASK: usize = MASK!(Self::PPN_BITS) << Self::PG_OFFSET;
|
||||||
const PTE_PPN_MASK: usize = MASK!(Self::PPN_BITS) << Self::PPN_OFFSET;
|
const PTE_PPN_MASK: usize = MASK!(Self::PPN_BITS) << Self::PPN_OFFSET;
|
||||||
|
|
||||||
|
fn is_kernel(&self) -> bool {
|
||||||
|
let kernel_start = unsafe { kernel_virt_to_phys(KERNEL_START.as_virt_addr()) };
|
||||||
|
let kernel_end = unsafe { kernel_virt_to_phys(KERNEL_END.as_virt_addr()).align_up(PAGE_SIZE) };
|
||||||
|
|
||||||
|
self.as_usize() >= kernel_start.as_usize() && self.as_usize() < kernel_end.as_usize()
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_ppn(&self) -> usize {
|
fn extract_ppn(&self) -> usize {
|
||||||
(self.as_usize() & Self::PA_PPN_MASK) >> Self::PG_OFFSET
|
(self.as_usize() & Self::PA_PPN_MASK) >> Self::PG_OFFSET
|
||||||
}
|
}
|
||||||
@ -51,16 +54,45 @@ trait GenericVirtAddrPage: AddressOps {
|
|||||||
const PG_OFFSET: usize;
|
const PG_OFFSET: usize;
|
||||||
const MAX_LEVEL: usize;
|
const MAX_LEVEL: usize;
|
||||||
|
|
||||||
|
fn is_kernel(&self, bits: usize) -> bool {
|
||||||
|
let mask = !MASK!(bits + 1);
|
||||||
|
(self.as_usize() & mask) != 0
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_vpn<T: TableLevel>(&self) -> usize {
|
fn extract_vpn<T: TableLevel>(&self) -> usize {
|
||||||
let mask = MASK!(T::LEVEL_BITS);
|
let mask = MASK!(T::LEVEL_BITS);
|
||||||
(self.as_usize() >> (Self::PG_OFFSET + T::LEVEL_BITS * (Self::MAX_LEVEL - T::LEVEL))) & mask
|
(self.as_usize() >> (Self::PG_OFFSET + T::LEVEL_BITS * (Self::MAX_LEVEL - T::LEVEL))) & mask
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_vpn<T: TableLevel>(&self, vpn: usize) -> VirtAddr {
|
fn merge_vpn<T: TableLevel>(&self, vpn: usize, bits: usize) -> VirtAddr {
|
||||||
let shift = Self::PG_OFFSET + T::LEVEL_BITS * (Self::MAX_LEVEL - T::LEVEL);
|
let shift = Self::PG_OFFSET + T::LEVEL_BITS * (Self::MAX_LEVEL - T::LEVEL);
|
||||||
let mask = MASK!(T::LEVEL_BITS);
|
let mask = MASK!(T::LEVEL_BITS);
|
||||||
let addr = (self.as_usize() & !(mask << shift)) | ((vpn & mask) << shift);
|
let addr = (self.as_usize() & !(mask << shift)) | ((vpn & mask) << shift);
|
||||||
|
|
||||||
VirtAddr(sign_extend(addr, 39 - 1))
|
VirtAddr(sign_extend(addr, bits))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn test_sign_extend() {
|
||||||
|
assert_eq!(sign_extend(0b01010, 4), 0b01010);
|
||||||
|
assert_eq!(sign_extend(0b01010, 3), !0b101);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn test_is_kernel() {
|
||||||
|
let addr = PhysAddr(0x80200000);
|
||||||
|
assert!(addr.is_kernel());
|
||||||
|
let addr = PhysAddr(0x80000000);
|
||||||
|
assert!(!addr.is_kernel());
|
||||||
|
|
||||||
|
let addr = VirtAddr(0x0000_0080_0000_0000);
|
||||||
|
assert!(addr.is_kernel(39 - 1));
|
||||||
|
let addr = VirtAddr(0x0000_007f_ffff_ffff);
|
||||||
|
assert!(!addr.is_kernel(39 - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,10 @@ impl GenericPhysAddrPage for PhysAddr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PhysAddrPage for PhysAddr {
|
impl PhysAddrPage for PhysAddr {
|
||||||
|
fn is_kerenel(&self) -> bool {
|
||||||
|
GenericPhysAddrPage::is_kernel(self)
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_ppn(&self) -> usize {
|
fn extract_ppn(&self) -> usize {
|
||||||
GenericPhysAddrPage::extract_ppn(self)
|
GenericPhysAddrPage::extract_ppn(self)
|
||||||
}
|
}
|
||||||
@ -32,11 +36,15 @@ impl GenericVirtAddrPage for VirtAddr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VirtAddrPage for VirtAddr {
|
impl VirtAddrPage for VirtAddr {
|
||||||
|
fn is_kerenel(&self) -> bool {
|
||||||
|
GenericVirtAddrPage::is_kernel(self, 39 - 1)
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_vpn<T: TableLevel>(&self) -> usize {
|
fn extract_vpn<T: TableLevel>(&self) -> usize {
|
||||||
GenericVirtAddrPage::extract_vpn::<T>(self)
|
GenericVirtAddrPage::extract_vpn::<T>(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_vpn<T: TableLevel>(&self, vpn: usize) -> usize {
|
fn merge_vpn<T: TableLevel>(&self, vpn: usize) -> usize {
|
||||||
GenericVirtAddrPage::merge_vpn::<T>(self, vpn).into()
|
GenericVirtAddrPage::merge_vpn::<T>(self, vpn, 39 - 1).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,23 @@ use crate::plat::console::{set_console, ConsoleDevice, ConsoleDriver, CONSOLE};
|
|||||||
use crate::plat::lowlevel::{Hardware, LowLevel};
|
use crate::plat::lowlevel::{Hardware, LowLevel};
|
||||||
use crate::plat::timer::{Timer, TimerOps};
|
use crate::plat::timer::{Timer, TimerOps};
|
||||||
use crate::plat::trap::{Trap, TrapOps};
|
use crate::plat::trap::{Trap, TrapOps};
|
||||||
|
use crate::scheduler::SCHEDULER;
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use fdt::Fdt;
|
use fdt::Fdt;
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
|
|
||||||
#[thread_local]
|
#[thread_local]
|
||||||
pub static HART_ID: Cell<usize> = Cell::new(0);
|
pub static HART_ID: Cell<usize> = Cell::new(0);
|
||||||
|
|
||||||
|
pub static N_CPUS: AtomicUsize = AtomicUsize::new(1);
|
||||||
|
|
||||||
pub fn rust_main() -> ! {
|
pub fn rust_main() -> ! {
|
||||||
let fdt_addr = crate::arch::FDT.load(core::sync::atomic::Ordering::Acquire);
|
let fdt_addr = crate::arch::FDT.load(Ordering::Acquire);
|
||||||
let fdt = unsafe { Fdt::from_ptr(fdt_addr).unwrap() };
|
let fdt = unsafe { Fdt::from_ptr(fdt_addr).unwrap() };
|
||||||
|
|
||||||
|
N_CPUS.store(fdt.cpus().count(), Ordering::Release);
|
||||||
|
|
||||||
setup_console(&fdt);
|
setup_console(&fdt);
|
||||||
info!("Kernel Started");
|
info!("Kernel Started");
|
||||||
|
|
||||||
@ -28,7 +34,8 @@ pub fn rust_main() -> ! {
|
|||||||
|
|
||||||
Trap::init();
|
Trap::init();
|
||||||
Timer::init();
|
Timer::init();
|
||||||
Hardware::enable_interrupt();
|
|
||||||
|
SCHEDULER.schedule();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let data = CONSOLE.lock().try_read();
|
let data = CONSOLE.lock().try_read();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::entry::HART_ID;
|
|
||||||
use crate::plat::console::CONSOLE;
|
use crate::plat::console::CONSOLE;
|
||||||
|
use crate::{entry::HART_ID, plat::timer::CURRENT_TICK};
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
use core::sync::atomic::Ordering;
|
||||||
use log::{LevelFilter, Log, Metadata, Record};
|
use log::{LevelFilter, Log, Metadata, Record};
|
||||||
|
|
||||||
struct SimpleLogger;
|
struct SimpleLogger;
|
||||||
@ -28,9 +29,10 @@ impl Log for SimpleLogger {
|
|||||||
CONSOLE
|
CONSOLE
|
||||||
.lock()
|
.lock()
|
||||||
.write_fmt(format_args!(
|
.write_fmt(format_args!(
|
||||||
"{color_prefix}[{}][HART{}] {}{color_reset}\n",
|
"{color_prefix}[{}][HART{}][{}] {}{color_reset}\n",
|
||||||
record.level(),
|
record.level(),
|
||||||
HART_ID.get(),
|
HART_ID.get(),
|
||||||
|
CURRENT_TICK.load(Ordering::Acquire),
|
||||||
record.args(),
|
record.args(),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -10,9 +10,9 @@ use utils::{
|
|||||||
/// RawCap is the specific implementation of capability which stores in CNode
|
/// RawCap is the specific implementation of capability which stores in CNode
|
||||||
#[derive(Copy, Clone, Default, PartialEq, Eq)]
|
#[derive(Copy, Clone, Default, PartialEq, Eq)]
|
||||||
pub struct RawCap {
|
pub struct RawCap {
|
||||||
// TODO: this could be an enum, figure out a way to do this
|
|
||||||
/// args: in vanilla seL4 implementation, a cap use two 64-bit words to store all information,
|
/// args: in vanilla seL4 implementation, a cap use two 64-bit words to store all information,
|
||||||
/// but we'll waste some space rather than using bitfield to simplify the implementation
|
/// but we'll waste some space rather than using bitfield to simplify the implementation
|
||||||
|
// TODO: use u64 rather than usize
|
||||||
pub args: [usize; 2],
|
pub args: [usize; 2],
|
||||||
/// ptr: vanilla seL4 stores the pointer with either higher bits or lower bits cut off,
|
/// ptr: vanilla seL4 stores the pointer with either higher bits or lower bits cut off,
|
||||||
/// which limits the address space. Use an independent field to store the pointer.
|
/// which limits the address space. Use an independent field to store the pointer.
|
||||||
|
@ -26,6 +26,7 @@ pub mod cnode;
|
|||||||
pub mod frame;
|
pub mod frame;
|
||||||
pub mod null;
|
pub mod null;
|
||||||
pub mod table;
|
pub mod table;
|
||||||
|
pub mod tcb;
|
||||||
pub mod untyped;
|
pub mod untyped;
|
||||||
|
|
||||||
/// Cap is the high-level wrapper of RawCap, it's a typed reference to RawCap (which is untyped in Rust)
|
/// Cap is the high-level wrapper of RawCap, it's a typed reference to RawCap (which is untyped in Rust)
|
||||||
|
110
kernel/src/objects/tcb.rs
Normal file
110
kernel/src/objects/tcb.rs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
use super::{
|
||||||
|
cap::{CapEntry, RawCap},
|
||||||
|
null::NullCap,
|
||||||
|
Cap, KernelObject,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
arch::{layout::mmap_phys_to_virt, trap::TrapContext},
|
||||||
|
plat::trap::TrapContextOps,
|
||||||
|
};
|
||||||
|
use uapi::cap::ObjectType;
|
||||||
|
use utils::{
|
||||||
|
addr::*,
|
||||||
|
linked_list::{Link, LinkHelper},
|
||||||
|
LinkHelperImpl,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ThreadState {
|
||||||
|
Inactive,
|
||||||
|
Running,
|
||||||
|
Restart,
|
||||||
|
Blocked,
|
||||||
|
Idle,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TcbObject {
|
||||||
|
pub trapframe: TrapContext, // must be the first field
|
||||||
|
|
||||||
|
cspace: CapEntry,
|
||||||
|
vspace: CapEntry,
|
||||||
|
|
||||||
|
// TODO: add reply, buffer, fault, caller ... priority, dom
|
||||||
|
state: ThreadState,
|
||||||
|
time_tick: usize,
|
||||||
|
pub link: Link<Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkHelperImpl!(TcbObject:link);
|
||||||
|
|
||||||
|
impl TcbObject {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
trapframe: TrapContext::default(),
|
||||||
|
cspace: CapEntry::new(NullCap::mint()),
|
||||||
|
vspace: CapEntry::new(NullCap::mint()),
|
||||||
|
state: ThreadState::Inactive,
|
||||||
|
time_tick: 0,
|
||||||
|
link: Link::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_idle_thread(&mut self) {
|
||||||
|
self.trapframe.configure_idle_thread();
|
||||||
|
self.state = ThreadState::Idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timetick(&self) -> usize {
|
||||||
|
self.time_tick
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_timetick(&mut self, timeslice: usize) {
|
||||||
|
self.time_tick = timeslice;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timer_tick(&mut self) {
|
||||||
|
self.time_tick = self.time_tick.saturating_sub(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install_vspace(&self) {
|
||||||
|
// setup pagetable
|
||||||
|
todo!("TcbObject::install_vspace");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate(&mut self) {
|
||||||
|
self.install_vspace();
|
||||||
|
self.trapframe.restore();
|
||||||
|
self.trapframe.handle_trap(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_syscall(&mut self) {
|
||||||
|
todo!("TcbObject::handle_syscall");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KernelObject for TcbObject {
|
||||||
|
const OBJ_TYPE: ObjectType = ObjectType::TCB;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type TcbCap<'a> = Cap<'a, TcbObject>;
|
||||||
|
|
||||||
|
impl<'a> TcbCap<'a> {
|
||||||
|
pub fn mint(ptr: PhysAddr) -> RawCap {
|
||||||
|
RawCap::new(0, 0, ptr, ObjectType::TCB)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_object(&self) -> &TcbObject {
|
||||||
|
unsafe {
|
||||||
|
let virt = mmap_phys_to_virt(self.cte.cap.get().ptr);
|
||||||
|
&*(virt.as_const_ptr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_object_mut(&mut self) -> &mut TcbObject {
|
||||||
|
unsafe {
|
||||||
|
let virt = mmap_phys_to_virt(self.cte.cap.get().ptr);
|
||||||
|
&mut *(virt.as_mut_ptr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
use crate::objects::tcb::TcbObject;
|
||||||
|
|
||||||
pub struct Trap;
|
pub struct Trap;
|
||||||
|
|
||||||
pub trait TrapOps {
|
pub trait TrapOps {
|
||||||
@ -6,4 +8,12 @@ pub trait TrapOps {
|
|||||||
|
|
||||||
pub trait TrapContextOps {
|
pub trait TrapContextOps {
|
||||||
fn restore(&mut self);
|
fn restore(&mut self);
|
||||||
|
fn configure_idle_thread(&mut self);
|
||||||
|
fn handle_trap(&mut self, from_kernel: bool);
|
||||||
|
|
||||||
|
fn as_object(&self) -> &TcbObject;
|
||||||
|
fn as_object_mut(&mut self) -> &mut TcbObject;
|
||||||
|
|
||||||
|
fn get_reg(&self, index: usize) -> usize;
|
||||||
|
fn set_reg(&mut self, index: usize, value: usize);
|
||||||
}
|
}
|
||||||
|
59
kernel/src/scheduler.rs
Normal file
59
kernel/src/scheduler.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use crate::objects::tcb::TcbObject;
|
||||||
|
use core::ptr::NonNull;
|
||||||
|
use log::error;
|
||||||
|
use spin::lazy::Lazy;
|
||||||
|
use utils::linked_list::Link;
|
||||||
|
|
||||||
|
#[thread_local]
|
||||||
|
static IDLE_THREAD: Lazy<TcbObject> = Lazy::new(|| {
|
||||||
|
let mut idle_thread = TcbObject::new();
|
||||||
|
idle_thread.configure_idle_thread();
|
||||||
|
idle_thread
|
||||||
|
});
|
||||||
|
|
||||||
|
#[thread_local]
|
||||||
|
pub static SCHEDULER: Lazy<Scheduler> = Lazy::new(|| Scheduler::new(&IDLE_THREAD));
|
||||||
|
|
||||||
|
// TODO: add a shared buffer to transfer TCB between cores
|
||||||
|
|
||||||
|
// one process can run for 10 ticks
|
||||||
|
pub const TIME_SLICE: usize = 10;
|
||||||
|
|
||||||
|
pub struct Scheduler {
|
||||||
|
head: Link<TcbObject>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scheduler {
|
||||||
|
pub fn new(idle_thread: &TcbObject) -> Self {
|
||||||
|
// link idle_thread
|
||||||
|
idle_thread.link.set_prev(Some(NonNull::from(idle_thread)));
|
||||||
|
idle_thread.link.set_next(Some(NonNull::from(idle_thread)));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
head: idle_thread.link.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&self, tcb: &TcbObject) {
|
||||||
|
self.head.prepend(tcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn schedule(&self) {
|
||||||
|
while let Some(next) = self.head.next_mut() {
|
||||||
|
// TODO: also need to check whether it is schedulable
|
||||||
|
if next.timetick() > 0 {
|
||||||
|
// Available to run, activate it
|
||||||
|
next.activate();
|
||||||
|
} else if next.timetick() == 0 {
|
||||||
|
// No time left, refill time tick and move to the end of the queue
|
||||||
|
next.set_timetick(TIME_SLICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// put to the end of the queue
|
||||||
|
next.link.detach();
|
||||||
|
self.head.prepend(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
error!("[Scheduler] No thread to schedule! Where is IDLE thread?");
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
pub static SCHEDULER: Scheduler = Scheduler::new();
|
|
||||||
|
|
||||||
pub struct Scheduler {}
|
|
||||||
|
|
||||||
impl Scheduler {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
// TODO: implement Scheduler::new
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(never)]
|
|
||||||
pub fn schedule(&self) {
|
|
||||||
todo!("Scheduler::schedule");
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,6 +13,9 @@ use super::TableLevel;
|
|||||||
use utils::addr::{AddressOps, PhysAddr, VirtAddr};
|
use utils::addr::{AddressOps, PhysAddr, VirtAddr};
|
||||||
|
|
||||||
pub trait PhysAddrPage: AddressOps {
|
pub trait PhysAddrPage: AddressOps {
|
||||||
|
/// Checks if the address is a kernel address.
|
||||||
|
fn is_kerenel(&self) -> bool;
|
||||||
|
|
||||||
/// Extracts the Physical Page Number (PPN).
|
/// Extracts the Physical Page Number (PPN).
|
||||||
fn extract_ppn(&self) -> usize;
|
fn extract_ppn(&self) -> usize;
|
||||||
|
|
||||||
@ -27,6 +30,9 @@ pub trait PhysAddrPage: AddressOps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait VirtAddrPage: AddressOps {
|
pub trait VirtAddrPage: AddressOps {
|
||||||
|
/// Checks if the address is a kernel address.
|
||||||
|
fn is_kerenel(&self) -> bool;
|
||||||
|
|
||||||
/// Extracts the Virtual Page Number (VPN).
|
/// Extracts the Virtual Page Number (VPN).
|
||||||
fn extract_vpn<T: TableLevel>(&self) -> usize;
|
fn extract_vpn<T: TableLevel>(&self) -> usize;
|
||||||
|
|
||||||
|
@ -4,5 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
utils = { path = "../lib/utils" }
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
num-derive = "0.4"
|
num-derive = "0.4"
|
||||||
|
@ -6,4 +6,5 @@ extern crate num_derive;
|
|||||||
|
|
||||||
pub mod cap;
|
pub mod cap;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod syscall;
|
||||||
pub mod vspace;
|
pub mod vspace;
|
||||||
|
71
uapi/src/syscall.rs
Normal file
71
uapi/src/syscall.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
|
use utils::MASK;
|
||||||
|
|
||||||
|
use crate::error::SysError;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
||||||
|
pub enum Syscall {
|
||||||
|
Invalid = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* in vanilla seL4, MessageInfo layout:
|
||||||
|
* > | label | caps | extra_caps | length |
|
||||||
|
* > | [63:12] | [11:9] | [8:7] | [6:0] |
|
||||||
|
*
|
||||||
|
* in out implementation, MessageInfo layout:
|
||||||
|
* > label: Syscall
|
||||||
|
* > length: args.len()
|
||||||
|
*
|
||||||
|
* Syscall convention:
|
||||||
|
* > Call:
|
||||||
|
* > > [a0]: MessageInfo
|
||||||
|
* > > [a1]: Capability Pointer
|
||||||
|
* > > [a3..a7]: extra arguments
|
||||||
|
* > > length = (a0..a?).len()
|
||||||
|
* > Reply:
|
||||||
|
* > TODO: figure out how vanilla seL4 handle reply
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct MessageInfo(usize);
|
||||||
|
|
||||||
|
impl MessageInfo {
|
||||||
|
const LABEL_MASK: usize = MASK!(63 - 12 + 1);
|
||||||
|
|
||||||
|
pub fn new(label: Syscall, length: usize) -> Self {
|
||||||
|
Self((label.to_usize().unwrap_or(0)) << 12 | (length & 0x7f))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn label(&self) -> Syscall {
|
||||||
|
Syscall::from_usize((self.0 >> 12) & Self::LABEL_MASK).unwrap_or(Syscall::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length(&self) -> usize {
|
||||||
|
self.0 & 0x7F
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_usize(&self) -> usize {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for MessageInfo {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("MessageInfo")
|
||||||
|
.field("label", &self.label())
|
||||||
|
.field("length", &self.length())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<usize> for MessageInfo {
|
||||||
|
type Error = SysError;
|
||||||
|
|
||||||
|
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||||
|
let syscall = Syscall::from_usize((value >> 12) & MessageInfo::LABEL_MASK).ok_or(SysError::InvalidArgument)?;
|
||||||
|
let length = value & 0x7F;
|
||||||
|
Ok(Self::new(syscall, length))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user