Compare commits

..

16 Commits

Author SHA1 Message Date
3a7517f8fc feat: kernel/plat/irq: add inital dispatch support 2024-09-03 20:38:07 +08:00
c758e69946 feat: kernel/objects: add inital endpoint cap support 2024-09-03 20:36:36 +08:00
ef45061230 fix: root: sync with latest uapi 2024-09-03 20:32:41 +08:00
8918dac00c feat: kernel/objects/tcb: add get_message_info 2024-09-03 19:58:06 +08:00
13180fe9f4 feat: kernel/objects/null: add override_cap 2024-09-03 19:39:08 +08:00
55547d336e feat: kernel/objects/tcb: support for endpoint 2024-09-03 19:38:34 +08:00
db7fa2ba58 chore: kernel/objects: extract as_object* 2024-09-03 19:35:17 +08:00
163813fbde chore: kernel/objects/tcb: make clippy happy 2024-09-03 19:34:06 +08:00
5b40dabb7b feat: utils: add utils for casting array 2024-09-03 19:29:42 +08:00
0ed0720ba8 feat: uapi: update spec 2024-09-03 19:27:37 +08:00
66386aa00b feat: kernel/objects/tcb: rewrite schedulable and add schedule_next 2024-09-02 13:58:30 +08:00
60d9b10300 fix: kernel/root: only idle thread could me marked as ThreadState::Idle 2024-09-02 13:55:37 +08:00
3475bf36cd feat: kernel/syscall: refactor 2024-09-02 13:55:00 +08:00
27fdf26cc1 feat: utils/linked_list: add is_empty 2024-08-29 20:41:52 +08:00
9f4a804e30 feat: kernel/scheduler: skip idle thread if there are any other threads available to run 2024-08-29 20:41:34 +08:00
7b54abcc13 feat: uapi/syscall: update message info, add cap n_bits field 2024-08-29 19:45:56 +08:00
15 changed files with 508 additions and 118 deletions

View File

@ -0,0 +1,211 @@
use crate::{objects::*, plat::trap::TrapContextOps};
use core::cmp::min;
use tcb::{ThreadState, EP_QUEUE_LINK_ID};
use uapi::{cap::ObjectType, fault::Fault, syscall::*};
use utils::{addr::*, array::*, linked_list::Link};
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum EndpointState {
Idle,
Sending,
Receiving,
}
#[repr(C)]
pub struct EndpointObject {
pub queue: Link<TcbObject, EP_QUEUE_LINK_ID>,
pub state: EndpointState,
}
impl KernelObject for EndpointObject {
const OBJ_TYPE: ObjectType = ObjectType::Endpoint;
}
/*
* in vanilla seL4, EndpointCap layout:
* > args[0]: | cap_tag | can_grant_reply | can_grant | can_recv | can_send | base_ptr |
* > | [63:59] | [58:58] | [57:57] | [56:56] | [55:55] | [54:0] |
* > args[1]: | badge |
* > | [63::0] |
*
* in our implementation, EndpointCap layout:
* > args[0]: badge
* > args[1]: none
* > ptr: base_ptr
* > cap_type: cap_tag
*/
pub type EndpointCap<'a> = Cap<'a, EndpointObject>;
impl<'a> EndpointCap<'a> {
pub fn mint(ptr: PhysAddr, badge: usize) -> RawCap {
RawCap::new(badge, 0, ptr, ObjectType::Endpoint)
}
pub fn can_send(&self) -> bool {
self.cte.cap.args[0] & 0x2 != 0
}
pub fn can_recv(&self) -> bool {
self.cte.cap.args[0] & 0x1 != 0
}
pub fn badge(&self) -> usize {
self.cte.cap.args[1]
}
pub fn state(&self) -> EndpointState {
self.as_object().state
}
pub fn set_state(&mut self, state: EndpointState) {
self.as_object_mut().state = state;
}
pub fn do_send(&mut self, send: &mut TcbObject) -> SysResult<()> {
match self.state() {
EndpointState::Idle | EndpointState::Sending => {
send.set_state(ThreadState::Sending);
send.set_badge(self.badge());
self.set_state(EndpointState::Sending);
self.as_object_mut().queue.append(send);
},
EndpointState::Receiving => {
assert!(!self.as_object().queue.is_empty(), "Receive endpoint queue must not be empty");
let recv = {
let recv = self.as_object_mut().queue.next_mut().unwrap();
recv.ep_queue.detach();
// SAFETY: `recv` is detached from the queue, no longer related to `self`
unsafe { &mut *(recv as *mut TcbObject) }
};
if self.as_object().queue.is_empty() {
self.set_state(EndpointState::Idle);
}
do_ipc_transfer(send, send.get_message_info()?, recv, recv.get_message_info()?, self.badge())?;
recv.set_state(ThreadState::Running);
recv.schedule_next();
},
};
Ok(())
}
pub fn do_recv(&mut self, recv: &mut TcbObject) -> SysResult<()> {
match self.state() {
EndpointState::Idle | EndpointState::Receiving => {
recv.set_state(ThreadState::Receiving);
self.set_state(EndpointState::Receiving);
self.as_object_mut().queue.append(recv);
},
EndpointState::Sending => {
assert!(!self.as_object().queue.is_empty(), "Send endpoint queue must not be empty");
let send = {
let send = self.as_object_mut().queue.next_mut().unwrap();
send.ep_queue.detach();
// SAFETY: `send` is detached from the queue, no longer related to `self`
unsafe { &mut *(send as *mut TcbObject) }
};
if self.as_object().queue.is_empty() {
self.set_state(EndpointState::Idle);
}
do_ipc_transfer(send, send.get_message_info()?, recv, recv.get_message_info()?, self.badge())?;
send.set_state(ThreadState::Running);
send.schedule_next();
},
};
Ok(())
}
}
fn copy_message(send: &mut TcbObject, send_msg: MessageInfo, recv: &mut TcbObject, recv_msg: MessageInfo) -> SysResult<()> {
let send_len = send_msg.length() - if send_msg.transfer_cap() { 2 } else { 0 };
let recv_len = recv_msg.length() - if recv_msg.transfer_cap() { 2 } else { 0 };
let msg_len = min(send_len, recv_len);
// get ipc buffer
let mut recv_cap = recv.buffer()?;
let recv_buf: &mut [usize] = cast_arr_mut(recv_cap.as_object_mut());
let send_cap = send.buffer()?;
let send_buf: &[usize] = cast_arr(send_cap.as_object());
// copy ipc buffer
let bgn = REG_ARG_MAX + 1;
recv_buf[..(msg_len - bgn)].copy_from_slice(&send_buf[..(msg_len - bgn)]);
// copy register
for i in REG_ARG_0..min(REG_ARG_MAX + 1, msg_len) {
recv.trapframe.set_reg(i, send.trapframe.get_reg(i));
}
Ok(())
}
fn ipc_get_args(tcb: &TcbObject, idx: usize) -> SysResult<usize> {
let ret = if idx <= REG_ARG_MAX {
tcb.trapframe.get_reg(idx)
} else {
let buf = tcb.buffer()?;
let buf: &[usize] = cast_arr(buf.as_object());
buf[idx]
};
Ok(ret)
}
fn transfer_cap(send: &mut TcbObject, send_msg: MessageInfo, recv: &mut TcbObject, recv_msg: MessageInfo) -> SysResult<()> {
if !send_msg.transfer_cap() || !recv_msg.transfer_cap() {
return Ok(());
}
let send_cspace = send.cspace()?;
let recv_cspace = recv.cspace()?;
let send_cptr = ipc_get_args(send, send_msg.length() - LEN_ARG_OFFSET_CPTR)?;
let send_bits = ipc_get_args(send, send_msg.length() - LEN_ARG_OFFSET_BITS)?;
let recv_cptr = ipc_get_args(recv, recv_msg.length() - LEN_ARG_OFFSET_CPTR)?;
let recv_bits = ipc_get_args(recv, recv_msg.length() - LEN_ARG_OFFSET_BITS)?;
let send_cap = send_cspace.resolve_address_bits(send_cptr, send_bits)?;
let recv_cap = recv_cspace.resolve_address_bits(recv_cptr, recv_bits)?;
let dest = NullCap::try_from(recv_cap)?;
dest.override_cap(send_cap.cap);
unsafe { send_cap.cap.update(|cap| *cap = NullCap::mint()) };
Ok(())
}
fn do_ipc_transfer(
send: &mut TcbObject,
send_msg: MessageInfo,
recv: &mut TcbObject,
recv_msg: MessageInfo,
_badge: usize,
) -> SysResult<()> {
if send.fault() == Fault::Null {
// do normal transfer
copy_message(send, send_msg, recv, recv_msg)?;
transfer_cap(send, send_msg, recv, recv_msg)?;
todo!("do_ipc_transfer: normal transfer: uapi/reply");
} else {
// do fault transfer
todo!("do_ipc_transfer: fault transfer");
}
}

View File

@ -14,14 +14,17 @@
*/
use crate::arch::layout::mmap_phys_to_virt;
use core::marker::PhantomData;
use uapi::{
cap::ObjectType,
error::{SysError, SysResult},
};
use utils::addr::AddressOps;
pub mod cap;
pub mod cnode;
pub mod endpoint;
pub mod frame;
pub mod null;
pub mod table;
@ -30,6 +33,7 @@ pub mod untyped;
pub use cap::{CapEntry, RawCap};
pub use cnode::{CNodeCap, CNodeObject};
pub use endpoint::{EndpointCap, EndpointObject};
pub use frame::{FrameCap, FrameObject};
pub use null::NullCap;
pub use table::{TableCap, TableObject};
@ -65,6 +69,22 @@ impl<'a, T: KernelObject + ?Sized> Cap<'a, T> {
}
}
impl<'a, T: KernelObject> Cap<'a, T> {
pub fn as_object(&self) -> &T {
unsafe {
let virt = mmap_phys_to_virt(self.cte.cap.ptr);
&*(virt.as_const_ptr())
}
}
pub fn as_object_mut(&mut self) -> &mut T {
unsafe {
let virt = mmap_phys_to_virt(self.cte.cap.ptr);
&mut *(virt.as_mut_ptr())
}
}
}
/// KernelObject is the base trait for all kernel objects
pub trait KernelObject {
const OBJ_TYPE: ObjectType;

View File

@ -14,6 +14,10 @@ impl<'a> NullCap<'a> {
pub fn mint() -> RawCap {
RawCap::new(0, 0, PhysAddr(0), ObjectType::Null)
}
pub fn override_cap(&self, new: RawCap) {
unsafe { self.cte.cap.update(|cap| *cap = new) };
}
}
impl Debug for NullCap<'_> {

View File

@ -1,9 +1,12 @@
use crate::arch::{layout::mmap_phys_to_virt, trap::TrapContext, vspace::*};
use crate::arch::{trap::TrapContext, vspace::*};
use crate::scheduler::SCHEDULER;
use crate::syscall::handle_syscall;
use crate::{objects::*, plat::trap::TrapContextOps, vspace::*};
use core::fmt::Debug;
use core::sync::atomic::{AtomicUsize, Ordering};
use uapi::{cap::ObjectType, fault::*};
use uapi::cap::ObjectType;
use uapi::fault::Fault;
use uapi::syscall::*;
use utils::{
addr::*,
linked_list::{Link, LinkHelper},
@ -20,31 +23,36 @@ pub enum ThreadState {
Inactive,
Running,
Restart,
Blocked,
Sending,
Receiving,
Idle,
}
pub const SCHED_QUEUE_LINK_ID: usize = 1;
pub const EP_QUEUE_LINK_ID: usize = 2;
#[repr(C)]
pub struct TcbObject {
pub trapframe: TrapContext, // must be the first field
cspace: CapEntry,
vspace: CapEntry,
notification: CapEntry,
buffer: CapEntry, // CapFrame
fault: Fault,
fault_handler: CapEntry,
cspace: CapEntry,
vspace: CapEntry,
// endpoint
badge: usize,
buffer: CapEntry,
fault: Fault,
state: ThreadState,
time_tick: usize,
tid: usize,
pub sched_queue: Link<Self, SCHED_QUEUE_LINK_ID>,
pub ep_queue: Link<Self, EP_QUEUE_LINK_ID>,
}
LinkHelperImpl!(TcbObject { sched_queue } => SCHED_QUEUE_LINK_ID);
LinkHelperImpl!(TcbObject { ep_queue } => EP_QUEUE_LINK_ID);
const_assert_eq!(ObjectType::TCB.size(0), ObjectType::TCB.size(0x42));
const_assert!(core::mem::size_of::<TcbObject>() <= ObjectType::TCB.size(0));
@ -52,22 +60,28 @@ const_assert!(core::mem::size_of::<TcbObject>() <= ObjectType::TCB.size(0));
impl TcbObject {
pub fn new() -> Self {
Self {
trapframe: TrapContext::default(),
cspace: CapEntry::new(NullCap::mint()),
vspace: CapEntry::new(NullCap::mint()),
notification: CapEntry::new(NullCap::mint()),
buffer: CapEntry::new(NullCap::mint()),
fault: Fault::Null,
fault_handler: CapEntry::new(NullCap::mint()),
state: ThreadState::Inactive,
time_tick: 0,
tid: TID_ALLOCATOR.fetch_add(1, Ordering::SeqCst),
sched_queue: Link::default(),
trapframe: TrapContext::default(),
cspace: CapEntry::new(NullCap::mint()),
vspace: CapEntry::new(NullCap::mint()),
badge: 0,
buffer: CapEntry::new(NullCap::mint()),
fault: Fault::Null,
state: ThreadState::Inactive,
time_tick: 0,
tid: TID_ALLOCATOR.fetch_add(1, Ordering::SeqCst),
sched_queue: Link::default(),
ep_queue: Link::default(),
}
}
pub fn cspace(&self) -> &CapEntry {
&self.cspace
pub fn configure_idle_thread(&mut self) {
self.trapframe.configure_idle_thread();
self.vspace = CapEntry::new(TableCap::mint(unsafe { get_current_pagetable().paddr() }));
self.state = ThreadState::Idle;
}
pub fn cspace(&self) -> SysResult<CNodeCap> {
CNodeCap::try_from(&self.cspace)
}
pub fn set_cspace(&mut self, cspace: CapEntry) {
@ -83,24 +97,34 @@ impl TcbObject {
self.vspace = vspace;
}
pub fn configure_idle_thread(&mut self) {
self.trapframe.configure_idle_thread();
self.vspace = CapEntry::new(TableCap::mint(unsafe { get_current_pagetable().paddr() }));
self.state = ThreadState::Idle;
pub fn badge(&self) -> usize {
self.badge
}
pub fn tid(&self) -> usize {
self.tid
pub fn set_badge(&mut self, badge: usize) {
self.badge = badge;
}
pub fn buffer(&self) -> SysResult<FrameCap> {
FrameCap::try_from(&self.buffer)
}
pub fn set_buffer(&mut self, buffer: CapEntry) {
self.buffer = buffer;
}
pub fn fault(&self) -> Fault {
self.fault
}
pub fn set_fault(&mut self, fault: Fault) {
self.fault = fault;
}
pub fn state(&self) -> ThreadState {
self.state
}
pub fn schedulable(&self) -> bool {
self.state != ThreadState::Blocked && self.state != ThreadState::Inactive
}
pub fn set_state(&mut self, state: ThreadState) {
self.state = state;
}
@ -121,6 +145,19 @@ impl TcbObject {
self.time_tick = self.time_tick.saturating_sub(1);
}
pub fn tid(&self) -> usize {
self.tid
}
pub fn schedulable(&self) -> bool {
matches!(self.state, ThreadState::Idle | ThreadState::Running)
}
pub fn schedule_next(&self) {
self.sched_queue.detach();
SCHEDULER.add_next(self);
}
pub fn activate_vspace(&self) {
let cap = TableCap::try_from(&self.vspace).expect("activate_vspace: invalid cap");
let paddr = cap.as_object::<Level0>().paddr();
@ -133,6 +170,10 @@ impl TcbObject {
self.trapframe.handle_trap(false);
}
pub fn get_message_info(&self) -> SysResult<MessageInfo> {
MessageInfo::try_from(self.trapframe.get_reg(REG_MESSAGE_INFO))
}
pub fn handle_syscall(&mut self) {
// TODO: don't panic here, return error to user instead, create SysRespInfo
handle_syscall(self).unwrap();
@ -149,20 +190,6 @@ 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.ptr);
&*(virt.as_const_ptr())
}
}
pub fn as_object_mut(&mut self) -> &mut TcbObject {
unsafe {
let virt = mmap_phys_to_virt(self.cte.cap.ptr);
&mut *(virt.as_mut_ptr())
}
}
}
impl Debug for TcbCap<'_> {
@ -171,10 +198,9 @@ impl Debug for TcbCap<'_> {
f.debug_struct("TcbCap")
.field("cspace", &obj.cspace)
.field("vspace", &obj.vspace)
.field("notification", &obj.notification)
.field("badge", &obj.badge)
.field("buffer", &obj.buffer)
.field("fault", &obj.fault)
.field("fault_handler", &obj.fault_handler)
.field("state", &obj.state)
.field("time_tick", &obj.time_tick)
.finish()

View File

@ -1,6 +1,7 @@
use super::utils::generate_driver;
use crate::drivers::irq::IrqPlic;
use crate::objects::*;
use log::{error, warn};
use spin::{lazy::Lazy, Mutex};
const IRQ_NUM: usize = 32;
@ -63,6 +64,36 @@ impl IrqManager {
}
pub fn dispatch(&mut self) {
todo!("forward to endpoint cap");
let irq = IRQ_DRIVER.lock().claim();
if irq.is_none() {
warn!("[IrqManager] No pending IRQ found");
return;
}
let irq = irq.unwrap();
if irq >= IRQ_NUM {
error!("[IrqManager] Invalid IRQ number: {}", irq);
IRQ_DRIVER.lock().disable(irq);
IRQ_DRIVER.lock().complete(irq);
return;
}
match self.state[irq] {
IrqState::Inactive => {
warn!("[IrqManager] Received disabled IRQ: {}", irq);
IRQ_DRIVER.lock().disable(irq);
},
IrqState::Signal => {
// TODO: send signal via endpoint
warn!("[IrqManager] Undelivered IRQ: {}", irq);
IRQ_DRIVER.lock().disable(irq);
},
IrqState::Ipi => {
todo!("Kernel: NO SMP support now");
},
IrqState::Reserved => error!("[IrqManager] Received unhandled reserved IRQ: {}", irq),
}
IRQ_DRIVER.lock().complete(irq);
}
}

View File

@ -221,7 +221,7 @@ fn create_objects(cnode_obj: &mut CNodeObject, free_idx: &mut usize, cnode_entry
tcb.set_cspace(cnode_entry.clone());
tcb.set_vspace(vspace.clone());
tcb.set_state(tcb::ThreadState::Idle);
tcb.set_state(tcb::ThreadState::Running);
}
}

View File

@ -2,7 +2,7 @@ use crate::objects::*;
use core::sync::atomic::AtomicPtr;
use log::{error, trace};
use spin::lazy::Lazy;
use tcb::SCHED_QUEUE_LINK_ID;
use tcb::{ThreadState, SCHED_QUEUE_LINK_ID};
use utils::{container_of_mut, linked_list::Link};
#[thread_local]
@ -38,10 +38,29 @@ impl Scheduler {
self.head.prepend(tcb);
}
pub fn add_next(&self, tcb: &TcbObject) {
self.head.append(tcb);
}
pub fn schedule(&self) {
let mut idle_found = false;
while let Some(next) = self.head.next_mut() {
if next.timetick() > 0 && next.schedulable() {
trace!("Scheduling thread {}", next.tid());
if next.state() == ThreadState::Idle {
debug_assert!(next.tid() == 0, "IDLE thread should have TID 0");
if !idle_found {
// we are meeting the idle thread for the first time, bypass it
idle_found = true;
} else {
// no other thread could be scheduled
trace!("[Scheduler] Scheduling IDLE thread");
next.refill_timetick();
next.activate();
}
} else if next.timetick() > 0 && next.schedulable() {
trace!("[Scheduler] Scheduling thread {}", next.tid());
idle_found = false;
next.activate();
} else if next.timetick() == 0 {
next.refill_timetick();

View File

@ -2,32 +2,43 @@ use crate::{objects::*, plat::trap::TrapContextOps};
use log::trace;
use uapi::{error::*, syscall::*};
pub fn handle_syscall(tcb: &mut TcbObject) -> SysResult {
let info = MessageInfo::try_from(tcb.trapframe.get_reg(0))?;
let cspace = CNodeCap::try_from(tcb.cspace())?;
macro_rules! assert_length {
($info:expr, $len:expr) => {
if $info.length() != $len {
return Err(SysError::InvalidArgument);
}
};
}
let cap_ptr = tcb.trapframe.get_reg(1);
let cap = cspace
.resolve_address_bits(cap_ptr, usize::BITS as usize)
.map_err(SysError::from)?;
pub fn handle_syscall(tcb: &mut TcbObject) -> SysResult {
let info = tcb.get_message_info()?;
// Invalid, Debug*, Yield will bypass the capability check
match info.label() {
Syscall::Invalid => return Ok(()),
Syscall::DebugPutChar => {
assert_length!(info, 3);
let c = tcb.trapframe.get_reg(2) as u8 as char;
crate::print!("{}", c);
return Ok(());
},
Syscall::Yield => {
tcb.clear_timetick();
return Ok(());
},
_ => (),
};
let cspace = tcb.cspace()?;
let cap_ptr = tcb.trapframe.get_reg(REG_CAP_PTR);
let cap = cspace.resolve_address_bits(cap_ptr, info.bits()).map_err(SysError::from)?;
trace!("handle_syscall: info={:?} cap={:?}", info, cap);
match info.label() {
Syscall::Invalid => Ok(()),
Syscall::DebugPutChar => {
if info.length() != 3 {
return Err(SysError::InvalidArgument);
}
let c = tcb.trapframe.get_reg(2) as u8 as char;
crate::print!("{}", c);
Ok(())
},
Syscall::Yield => {
tcb.clear_timetick();
Ok(())
Syscall::InvokeNtfn => {
assert_length!(info, 2);
unimplemented!("[Syscall] InvokeNtfn: {:?}", info.label());
},
_ => unimplemented!("handle_syscall: {:?}, {:?}", info.label(), cap),
}

17
lib/utils/src/array.rs Normal file
View File

@ -0,0 +1,17 @@
pub fn cast_arr_mut<T, U>(slice: &mut [T]) -> &mut [U] {
assert_eq!(slice.len() % core::mem::align_of::<U>(), 0);
let len = core::mem::size_of_val(slice) / core::mem::size_of::<U>();
let ptr = slice.as_mut_ptr() as *mut U;
unsafe { core::slice::from_raw_parts_mut(ptr, len) }
}
pub fn cast_arr<T, U>(slice: &[T]) -> &[U] {
assert_eq!(slice.len() % core::mem::align_of::<U>(), 0);
let len = core::mem::size_of_val(slice) / core::mem::size_of::<U>();
let ptr = slice.as_ptr() as *const U;
unsafe { core::slice::from_raw_parts(ptr, len) }
}

View File

@ -3,6 +3,7 @@
#![feature(step_trait)]
pub mod addr;
pub mod array;
pub mod assert;
pub mod atomic;
pub mod bin;

View File

@ -65,6 +65,12 @@ impl<T: LinkHelper<ID>, const ID: usize> Link<T, ID> {
&*(container_of_offset!(self, T, T::LINK_OFFSET))
}
pub fn is_empty(&self) -> bool {
debug_assert!(!(self.prev.is_none() && self.next.is_some()));
debug_assert!(!(self.prev.is_some() && self.next.is_none()));
self.prev.is_none() && self.next.is_none()
}
pub fn prev_raw(&self) -> &Option<AtomicPtr<T>> {
&self.prev
}

View File

@ -14,7 +14,7 @@ root: root.o
$(LD) -Tlink.ld $^ -o $@
init.cpio: root
find . -type f -not -name "*.cpio" | cpio -ov -H crc > init.cpio
find . -type f -executable | cpio -ov -H crc > init.cpio
clean:
rm -f *.o root

View File

@ -11,15 +11,15 @@ _start:
.loop:
lb a2, 0(t0)
beqz a2, .yield
# msg: label = DebugPutChar(1), length = 3
# ptr: self-referential to cspace, just to create a valid cptr
syscall (1<<12 | 3), 0x0202020202020202
# msg: label = DebugPutChar(1), bits = 1, length = 3, transfer_cap = false
# ptr: (any)
syscall (1<<14 | 0<<8 | 3<<1 | 0), 0
addi t0, t0, 1
j .loop
.yield:
# msg: label = Yield(2), length = 2
# ptr: self-referential to cspace, just to create a valid cptr
syscall (2<<12 | 2), 0x0202020202020202
# msg: label = Yield(2), bits = 1, length = 2, transfer_cap = false
# ptr: (any)
syscall (2<<14 | 0<<8 | 2<<1 | 0), 0
j _start
.data

View File

@ -1,7 +1,7 @@
use crate::error::SysError;
use utils::addr::VirtAddr;
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LookupFailure {
InvalidRoot,
MissingCapability {
@ -29,32 +29,32 @@ impl From<LookupFailure> for SysError {
}
}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CapFault {
address: usize,
in_receive_phase: bool,
lookup_failure: LookupFailure,
}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct UnknownSyscall {
fault_ip: VirtAddr,
syscall: usize,
}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct UserException {
fault_ip: VirtAddr,
number: usize,
code: usize,
}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct VMFault {
ip: VirtAddr,
}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Fault {
Null,
CapFault(CapFault),

View File

@ -1,56 +1,99 @@
use core::fmt::Debug;
use num_traits::{FromPrimitive, ToPrimitive};
use utils::MASK;
use crate::error::SysError;
use core::fmt::Debug;
use num_traits::{FromPrimitive, ToPrimitive};
use utils::{then::Then, MASK};
#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum Syscall {
Invalid = 0,
DebugPutChar = 1,
Yield = 2,
Identify = 3,
Send = 4,
Recv = 5,
Call = 6,
Reply = 7,
InvokeNtfn = 3,
// Send = 4,
// Recv = 5,
// Call = 6,
// Reply = 7,
}
/* 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:
/* Syscall convention:
* > Call:
* > > [a0]: MessageInfo
* > > [a1]: Capability Pointer
* > > [a2..a7]: extra arguments
* > > length = (a0..a?).len()
* > > +----------+-------------------------+--------------------+
* > > | [a0] | REG_MESSAGE_INFO | MessageInfo |
* > > | [a1] | REG_CAP_PTR | Capability Pointer |
* > > | [a2..a7] | REG_ARG_0..=REG_ARG_MAX | Arguments |
* > > | buffer | REG_ARG_MAX+1.. | Arguments |
* > > +----------+-------------------------+--------------------+
* > > length = (a0..).len(), extra arguments are stored on ipc buffer
* > > if info.transfer_cap { Arguments[-2] = cap_ptr; Arguments[-1] = n_bits; }
* >
* > Reply:
* > TODO: figure out how vanilla seL4 handle reply
*/
pub const REG_MESSAGE_INFO: usize = 0;
pub const REG_CAP_PTR: usize = 1;
pub const REG_ARG_0: usize = 2;
pub const REG_ARG_MAX: usize = 7;
pub const LEN_ARG_OFFSET_CPTR: usize = 2;
pub const LEN_ARG_OFFSET_BITS: usize = 1;
/* MessageInfo layout:
* > +---------+--------+--------+--------------+
* > | label | bits | length | transfer_cap |
* > | [63:14] | [13:8] | [7:1] | [0] |
* > +---------+--------+--------+--------------+
* > label: syscall
* > bits: cap_ptr n_bits
* > length: args.len()
* > transfer_cap: whether to transfer cap
*/
#[derive(Clone, Copy)]
pub struct MessageInfo(usize);
impl MessageInfo {
const LABEL_MASK: usize = MASK!(63 - 12 + 1);
const LABEL_MASK: usize = MASK!(63 - Self::LABEL_OFFSET + 1);
const LABEL_OFFSET: usize = 14;
pub fn new(label: Syscall, length: usize) -> Self {
Self((label.to_usize().unwrap_or(0)) << 12 | (length & 0x7f))
const BITS_MASK: usize = MASK!(6);
const BITS_OFFSET: usize = 8;
const LENGTH_MASK: usize = MASK!(7);
const LENGTH_OFFSET: usize = 1;
const TRANSFER_CAP_MASK: usize = MASK!(1);
const TRANSFER_CAP_OFFSET: usize = 0;
pub fn new(label: Syscall, bits: usize, length: usize, transfer_cap: bool) -> Self {
let label = label.to_usize().unwrap_or(0);
let bits = bits - 1; // stored as n_bits - 1
let transfer_cap = transfer_cap as usize;
assert!(label <= Self::LABEL_MASK);
assert!(bits <= Self::BITS_MASK);
assert!(length <= Self::LENGTH_MASK);
let info = ((label & Self::LABEL_MASK) << Self::LABEL_OFFSET)
| ((bits & Self::BITS_MASK) << Self::BITS_OFFSET)
| ((length & Self::LENGTH_MASK) << Self::LENGTH_OFFSET)
| ((transfer_cap & Self::TRANSFER_CAP_MASK) << Self::TRANSFER_CAP_OFFSET);
Self(info)
}
pub fn label(&self) -> Syscall {
Syscall::from_usize((self.0 >> 12) & Self::LABEL_MASK).unwrap_or(Syscall::Invalid)
Syscall::from_usize((self.0 >> Self::LABEL_OFFSET) & Self::LABEL_MASK).unwrap_or(Syscall::Invalid)
}
pub fn bits(&self) -> usize {
((self.0 >> Self::BITS_OFFSET) & Self::BITS_MASK) + 1
}
pub fn length(&self) -> usize {
self.0 & 0x7F
(self.0 >> Self::LENGTH_OFFSET) & Self::LENGTH_MASK
}
pub fn transfer_cap(&self) -> bool {
(self.0 >> Self::TRANSFER_CAP_OFFSET) & Self::TRANSFER_CAP_MASK != 0
}
pub fn as_usize(&self) -> usize {
@ -62,7 +105,9 @@ impl Debug for MessageInfo {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MessageInfo")
.field("label", &self.label())
.field("bits", &self.bits())
.field("length", &self.length())
.field("transfer_cap", &self.transfer_cap())
.finish()
}
}
@ -71,8 +116,7 @@ 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))
let info = Self(value);
(info.label() != Syscall::Invalid).then_ok(info, SysError::InvalidArgument)
}
}