mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45:19 +08:00
Compare commits
16 Commits
c352a0b8c6
...
3a7517f8fc
Author | SHA1 | Date | |
---|---|---|---|
3a7517f8fc | |||
c758e69946 | |||
ef45061230 | |||
8918dac00c | |||
13180fe9f4 | |||
55547d336e | |||
db7fa2ba58 | |||
163813fbde | |||
5b40dabb7b | |||
0ed0720ba8 | |||
66386aa00b | |||
60d9b10300 | |||
3475bf36cd | |||
27fdf26cc1 | |||
9f4a804e30 | |||
7b54abcc13 |
211
kernel/src/objects/endpoint.rs
Normal file
211
kernel/src/objects/endpoint.rs
Normal 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");
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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<'_> {
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
17
lib/utils/src/array.rs
Normal 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) }
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
#![feature(step_trait)]
|
||||
|
||||
pub mod addr;
|
||||
pub mod array;
|
||||
pub mod assert;
|
||||
pub mod atomic;
|
||||
pub mod bin;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
12
root/root.S
12
root/root.S
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user