Compare commits

..

9 Commits

17 changed files with 446 additions and 81 deletions

16
.vscode/launch.json vendored
View File

@ -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
View File

@ -252,6 +252,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"num-derive", "num-derive",
"num-traits", "num-traits",
"utils",
] ]
[[package]] [[package]]

View File

@ -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,54 +18,10 @@ 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) {
let scause = riscv::register::scause::read();
trace!("[Trap] cpu@{} scause: {:?}", HART_ID.get(), scause.cause());
macro_rules! panic_fatal {
($msg:expr) => {
panic!(
"[Trap] {}, scause: {:?}, bits: {:#x}, sepc: {:#x}, stval: {:#x}",
$msg,
scause.cause(),
scause.bits(),
tf.sepc,
riscv::register::stval::read()
)
};
}
match scause.cause() {
T::Interrupt(I::SupervisorTimer) => {
Timer::do_tick();
if !from_kernel {
SCHEDULER.schedule()
}
},
T::Exception(E::UserEnvCall) => {
// TODO: handle syscall
tf.sepc += 4;
},
T::Interrupt(I::SupervisorExternal) => {
// TODO: handle external interrupt, e.g. plic
},
T::Exception(E::InstructionPageFault) | T::Exception(E::LoadPageFault) | T::Exception(E::StorePageFault) => {
if from_kernel {
panic_fatal!("Page Fault in Kernel");
}
// TODO: handle page fault
},
_ => panic_fatal!("Unhandled Trap"),
}
Timer::set_next();
trace!("[Trap] exiting...");
} }
impl TrapOps for Trap { impl TrapOps for Trap {
@ -76,13 +33,97 @@ impl TrapOps for Trap {
} }
} }
#[naked]
extern "C" fn idle_thread() {
unsafe {
core::arch::asm!(
"
1: wfi
j 1b
",
options(noreturn)
)
};
}
impl TrapContextOps for TrapContext { impl TrapContextOps for TrapContext {
fn restore(&mut self) { fn restore(&mut self) {
unsafe { switch_to_user(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();
trace!("[Trap] cpu@{} scause: {:?}", HART_ID.get(), scause.cause());
macro_rules! panic_fatal {
($msg:expr) => {
panic!(
"[Trap] {}, scause: {:?}, bits: {:#x}, sepc: {:#x}, stval: {:#x}",
$msg,
scause.cause(),
scause.bits(),
self.sepc,
riscv::register::stval::read()
)
};
}
let tcb = self.as_object_mut();
match scause.cause() {
T::Interrupt(I::SupervisorTimer) => {
Timer::do_tick();
if !from_kernel {
tcb.timer_tick();
}
},
T::Exception(E::UserEnvCall) => {
tcb.handle_syscall();
self.sepc += 4;
},
T::Interrupt(I::SupervisorExternal) => {
// TODO: handle external interrupt, e.g. plic
},
T::Exception(E::InstructionPageFault) | T::Exception(E::LoadPageFault) | T::Exception(E::StorePageFault) => {
if from_kernel {
panic_fatal!("Page Fault in Kernel");
}
// TODO: handle page fault
},
_ => panic_fatal!("Unhandled Trap"),
}
Timer::set_next();
}
fn as_object(&self) -> &TcbObject {
unsafe { &*(self as *const TrapContext as *const TcbObject) }
}
fn as_object_mut(&mut self) -> &mut TcbObject {
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;
}
} }
#[derive(Debug)] #[derive(Debug, Default)]
#[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]) };
&regs[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

View File

@ -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));
} }
} }

View File

@ -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()
} }
} }

View File

@ -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();

View File

@ -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();

View File

@ -10,10 +10,10 @@ 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
pub args: [usize; 2], // TODO: use u64 rather than usize
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.
/// Kernel will not manage memory, userspace should pass PhysAddr to kernel /// Kernel will not manage memory, userspace should pass PhysAddr to kernel

View File

@ -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
View 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())
}
}
}

View File

@ -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
View 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?");
}
}

View File

@ -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");
}
}

View File

@ -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;

View File

@ -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"

View File

@ -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
View 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))
}
}