[wip] syscalls
This commit is contained in:
parent
7e71ff1787
commit
dd6f186623
|
@ -10,19 +10,23 @@
|
|||
|
||||
// Syscalls (kernel API)
|
||||
trait API {
|
||||
// Three below (send, nb_send, call) are "invocation" syscalls.
|
||||
fn send(cap: Cap, msg_info: MessageInfo);
|
||||
fn nb_send(dest: Cap, msg_info: MessageInfo);
|
||||
fn call(cap: Cap, msg_info: MessageInfo) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
// Wait for message, when it is received,
|
||||
// return object Badge and block caller on `reply`.
|
||||
fn recv(src: Cap, reply: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
fn call(cap: Cap, msg_info: MessageInfo) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
fn reply(msg_info: MessageInfo);
|
||||
fn nb_send(dest: Cap, msg_info: MessageInfo);
|
||||
// As Recv but invoke `reply` first.
|
||||
fn reply_recv(
|
||||
src: Cap,
|
||||
reply: Cap,
|
||||
msg_info: MessageInfo,
|
||||
) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
fn nb_recv(src: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
fn r#yield();
|
||||
// -- end of default seL4 syscall list --
|
||||
// As ReplyRecv but invoke `dest` not `reply`.
|
||||
fn nb_send_recv(
|
||||
dest: Cap,
|
||||
|
@ -30,7 +34,6 @@ trait API {
|
|||
src: Cap,
|
||||
reply: Cap,
|
||||
) -> Result<(MessageInfo, Options<&Badge>)>;
|
||||
fn nb_recv(src: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
// As NBSendRecv, with no reply. Donation is not possible.
|
||||
fn nb_send_wait(
|
||||
cap: Cap,
|
||||
|
@ -39,54 +42,330 @@ trait API {
|
|||
) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
// As per Recv, but donation not possible.
|
||||
fn wait(src: Cap) -> Result<(MessageInfo, Option<&Badge>)>;
|
||||
fn r#yield();
|
||||
// Plus some debugging calls...
|
||||
}
|
||||
|
||||
// @todo This is going to be in the interface library.
|
||||
enum SysCall {
|
||||
Send,
|
||||
NBSend,
|
||||
Call,
|
||||
Recv,
|
||||
Reply,
|
||||
ReplyRecv,
|
||||
NBRecv,
|
||||
Yield,
|
||||
}
|
||||
|
||||
fn handle_syscall(syscall: SysCall) -> Result<()> {
|
||||
match syscall {
|
||||
SysCall::Send => {
|
||||
let result = handle_invocation(false, true);
|
||||
if result.is_err() {
|
||||
let irq = get_active_irq();
|
||||
if irq.is_ok() {
|
||||
handle_interrupt(irq.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
SysCall::NBSend => {
|
||||
let result = handle_invocation(false, false);
|
||||
if result.is_err() {
|
||||
let irq = get_active_irq();
|
||||
if irq.is_ok() {
|
||||
handle_interrupt(irq.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
SysCall::Call => {
|
||||
let result = handle_invocation(true, true);
|
||||
if result.is_err() {
|
||||
let irq = get_active_irq();
|
||||
if irq.is_ok() {
|
||||
handle_interrupt(irq.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
SysCall::Recv => handle_receive(true),
|
||||
SysCall::Reply => handle_reply(),
|
||||
SysCall::ReplyRecv => {
|
||||
handle_reply();
|
||||
handle_receive(true)
|
||||
}
|
||||
SysCall::NBRecv => handle_receive(false),
|
||||
SysCall::Yield => handle_yield(),
|
||||
}
|
||||
|
||||
schedule();
|
||||
activateThread();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_invocation(is_call: bool, is_blocking: bool) -> Result<()> {
|
||||
let thread: &TCB = KernelCurrentThread;
|
||||
|
||||
let infoRegister = thread.get_register(msgInfoRegister);
|
||||
let info: MessageInfo = messageInfoFromWord(infoRegister);
|
||||
let cap_ptr: CapPath = thread.get_register(capRegister);
|
||||
|
||||
result = thread.lookup_cap_and_slot(cap_ptr);
|
||||
|
||||
if result.is_err() {
|
||||
println!(
|
||||
"<<vesper[T{} \"{}\" @{}]: Invocation of invalid cap {}.>>",
|
||||
thread,
|
||||
thread.name,
|
||||
thread.get_restart_pc(),
|
||||
cap_ptr,
|
||||
);
|
||||
|
||||
if is_blocking {
|
||||
handle_fault(thread);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let buffer = thread.lookup_ipc_buffer(false);
|
||||
|
||||
let status = thread.lookup_extra_caps(buffer, info);
|
||||
|
||||
if status.is_err() {
|
||||
println!(
|
||||
"<<vesper[T{} \"{}\" @{}]: Lookup of extra caps failed.>>",
|
||||
thread,
|
||||
thread.name,
|
||||
thread.get_restart_pc(),
|
||||
);
|
||||
|
||||
if is_blocking {
|
||||
handle_fault(thread);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut length = info.length();
|
||||
if length > n_MsgRegisters && !buffer {
|
||||
length = n_MsgRegisters;
|
||||
}
|
||||
|
||||
let status = decode_invocation(
|
||||
info.label(),
|
||||
length,
|
||||
cap_ptr,
|
||||
result.slot,
|
||||
result.cap,
|
||||
current_extra_caps,
|
||||
is_blocking,
|
||||
is_call,
|
||||
buffer,
|
||||
);
|
||||
|
||||
if status.is_err() {
|
||||
return match status {
|
||||
Err(Preempted) => status,
|
||||
Err(SysCallError) => {
|
||||
if is_call {
|
||||
thread.replyFromKernel_error();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if thread.get_state() == ThreadState::Restart {
|
||||
if is_call {
|
||||
thread.replyFromKernel_success_empty();
|
||||
}
|
||||
thread.set_state(ThreadState::Running);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_receive(is_blocking: bool) {
|
||||
let endpoint_cap_ptr = KernelCurrentThread.get_register(capRegister);
|
||||
|
||||
let result = KernelCurrentThread.lookup_cap(endpoint_cap_ptr);
|
||||
|
||||
if result.is_err() {
|
||||
KernelCurrentFault = Fault_CapFault::new(endpoint_cap_ptr, true);
|
||||
handle_fault(KernelCurrentThread);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match result.cap.get_type() {
|
||||
endpoint => ,
|
||||
notification => ,
|
||||
_ => fault,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_reply() {
|
||||
let caller_slot = KernelCurrentThread.get_caller_slot();
|
||||
let caller_cap = caller_slot.capability;
|
||||
match caller_cap.get_type() {
|
||||
ReplyCap::Type.value => {
|
||||
// if (cap_reply_cap_get_capReplyMaster(callerCap)) {
|
||||
// break;
|
||||
// }
|
||||
// caller = ((tcb_t *)(cap_reply_cap_get_capTCBPtr(callerCap)));
|
||||
// if(!(caller != ksCurThread)) _assert_fail("caller must not be the current thread", "src/api/syscall.c", 313, __FUNCTION__);
|
||||
// do_reply_transfer(ksCurThread, caller, callerSlot);
|
||||
},
|
||||
NullCap::Type.value => {
|
||||
println!("<<vesper[T{} \"{}\" @{}]: Attempted reply operation when no reply capability present.>>", KernelCurrentThread, KernelCurrentThread.name, KernelCurrentThread.get_restart_pc());
|
||||
},
|
||||
_ => {
|
||||
panic!("<<vesper[T{} \"{}\" @{}]: Invalid caller capability.>>", KernelCurrentThread, KernelCurrentThread.name, KernelCurrentThread.get_restart_pc());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_reply_transfer() {}
|
||||
|
||||
fn handle_yield() {
|
||||
Scheduler::dequeue(KernelCurrentThread);
|
||||
Scheduler::append(KernelCurrentThread);
|
||||
Scheduler::reschedule_required();
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
enum Fault {
|
||||
#[snafu(display("null fault"))]
|
||||
Null,
|
||||
#[snafu(display("capability fault in {} phase at address {:x}", if in_receive_phase { "receive" } else { "send" }, address))]
|
||||
Capability {
|
||||
in_receive_phase: bool,
|
||||
address: PhysAddr,
|
||||
},
|
||||
#[snafu(display("vm fault on {} at address {:x} with status {:x}", if is_instruction_fault { "code" } else { "data" }, address, fsr))]
|
||||
VM {
|
||||
is_instruction_fault: bool,
|
||||
address: PhysAddr,
|
||||
fsr: u64, // status
|
||||
},
|
||||
#[snafu(display("unknown syscall {:x}", syscall_number))]
|
||||
UnknownSyscall {
|
||||
syscall_number: u64,
|
||||
},
|
||||
#[snafu(display("user exception {:x} code {:x}", number, code))]
|
||||
UserException {
|
||||
number: u64,
|
||||
code: u64,
|
||||
},
|
||||
}
|
||||
|
||||
fn handle_fault(thread: &TCB) {
|
||||
let fault = KernelCurrentFault;
|
||||
|
||||
let result = thread.send_fault_ipc();
|
||||
if result.is_err() {
|
||||
handle_double_fault(thread, fault);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_double_fault(thread: &TCB, fault1: Fault) {
|
||||
let fault2 = KernelCurrentFault;
|
||||
|
||||
println!("Caught {} while trying to handle {}", fault2, fault1);
|
||||
println!("in thread T{} \"{}\"", thread, thread.name);
|
||||
println!("at address {}", thread.get_restart_pc());
|
||||
println!("with stack trace:");
|
||||
arch::user_stack_trace(thread);
|
||||
|
||||
thread.set_state(ThreadState::Inactive);
|
||||
}
|
||||
|
||||
fn handle_unknown_syscall() {
|
||||
// handles
|
||||
// - SysDebugPutChar
|
||||
// - SysDebugHalt
|
||||
// - SysDebugSnapshot
|
||||
// - SysDebugCapIdentify
|
||||
// - SysDebugNameThread
|
||||
// - Fault_UnknownSyscall
|
||||
}
|
||||
|
||||
fn handle_interrupt_entry() -> Result<()> {
|
||||
let irq = get_active_irq();
|
||||
if irq.is_ok() {
|
||||
handle_interrupt(irq.unwrap());
|
||||
} else {
|
||||
handle_spurious_irq();
|
||||
}
|
||||
|
||||
schedule();
|
||||
activate_thread();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/* TCB: size 64 bytes + sizeof(arch_tcb_t) (aligned to nearest power of 2) */
|
||||
struct TCB {
|
||||
arch_tcb: arch::TCB,
|
||||
state: ThreadState, // 12 bytes?
|
||||
/* Notification that this TCB is bound to. If this is set, when this TCB waits on
|
||||
* any sync endpoint, it may receive a signal from a Notification object.
|
||||
* 4 bytes*/
|
||||
// notification_t *tcbBoundNotification;
|
||||
fault: Fault, // 8 bytes?
|
||||
lookup_failure: LookupFault, // 8 bytes
|
||||
/* Domain, 1 byte (packed to 4) */
|
||||
domain: Domain,
|
||||
/* maximum controlled priorioty, 1 byte (packed to 4) */
|
||||
mcp: Priority,
|
||||
/* Priority, 1 byte (packed to 4) */
|
||||
priority: Priority,
|
||||
/* Timeslice remaining, 4 bytes */
|
||||
time_slice: u32,
|
||||
/* Capability pointer to thread fault handler, 4 bytes */
|
||||
fault_handler: CapPath,
|
||||
/* userland virtual address of thread IPC buffer, 4 bytes */
|
||||
ipc_buffer: VirtAddr,
|
||||
/* Previous and next pointers for scheduler queues , 8 bytes */
|
||||
sched_next: *mut TCB,
|
||||
sched_prev: *mut TCB,
|
||||
/* Previous and next pointers for endpoint and notification queues, 8 bytes */
|
||||
ep_next: *mut TCB,
|
||||
ep_prev: *mut TCB,
|
||||
/* Use any remaining space for a thread name */
|
||||
name: &str,
|
||||
}
|
||||
|
||||
impl TCB {
|
||||
fn get_register(...) {}
|
||||
fn lookup_cap_and_slot() -> Result<()> {}
|
||||
fn get_restart_pc() {}
|
||||
fn lookup_ipc_buffer(some: bool) {}
|
||||
fn lookup_extra_caps() -> Result<()> {}
|
||||
fn get_state() -> ThreadState {}
|
||||
fn set_state(state: ThreadState) {}
|
||||
|
||||
fn get_caller_slot() -> Slot {}
|
||||
fn send_fault_ipc(&self) {}
|
||||
|
||||
fn replyFromKernel_success_empty() {}
|
||||
fn replyFromKernel_error() {}
|
||||
}
|
||||
//handleSyscall(syscall) in the slowpath()
|
||||
// these are expressed in terms of
|
||||
// handleInvocation(bool isCall, bool isBlocking)
|
||||
// handleRecv(block)
|
||||
// handleReply()
|
||||
// replyRecv: -- handleReply+handleRecv
|
||||
// handleYield()
|
||||
|
||||
// slowpath() called in c_handle_syscall() in abi
|
||||
// Call and ReplyRecv have fastpath handlers
|
||||
// the rest goes through slowpath
|
||||
|
||||
// c_handle_syscall called directly from SWI vector entry
|
||||
|
||||
struct Nucleus {}
|
||||
|
||||
impl API for Nucleus {
|
||||
fn send(cap: _, msg_info: _) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn recv(src: _, reply: _) -> _ {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn call(cap: _, msg_info: _) -> _ {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn reply(msg_info: _) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn nb_send(dest: _, msg_info: _) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn reply_recv(src: _, reply: _, msg_info: _) -> _ {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn nb_send_recv(dest: _, msg_info: _, src: _, reply: _) -> _ {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn nb_recv(src: _) -> _ {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn nb_send_wait(cap: _, msg_info: _, src: _) -> _ {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn wait(src: _) -> _ {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn r#yield() {
|
||||
unimplemented!()
|
||||
}
|
||||
//...
|
||||
}
|
||||
|
|
|
@ -126,6 +126,13 @@ unsafe extern "C" fn default_exception_handler() -> ! {
|
|||
/// Totally unsafe in the land of the hardware.
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn current_el0_synchronous(e: &mut ExceptionContext) {
|
||||
let cause = ESR_EL1.read(ESR_EL1::EC);
|
||||
|
||||
if cause == ESR_EL1::EC::SVC64.read(ESR_EL1::EC) {
|
||||
let syscall = ESR_EL1.read(ESR_EL1::ISS);
|
||||
return crate::api::handle_syscall(syscall);
|
||||
}
|
||||
|
||||
println!("[!] USER synchronous exception happened.");
|
||||
synchronous_common(e)
|
||||
}
|
||||
|
@ -309,8 +316,9 @@ fn iss_dfsc_to_string(iss: IssForDataAbort) -> &'static str {
|
|||
/// Helper function to 1) display current exception, 2) skip the offending asm instruction.
|
||||
/// Not for production use!
|
||||
fn synchronous_common(e: &mut ExceptionContext) {
|
||||
println!(" ESR_EL1: {:#010x} (syndrome)", ESR_EL1.get());
|
||||
let cause = ESR_EL1.read(ESR_EL1::EC);
|
||||
|
||||
println!(" ESR_EL1: {:#010x} (syndrome)", ESR_EL1.get());
|
||||
println!(
|
||||
" EC: {:#06b} (cause) -- {}",
|
||||
cause,
|
||||
|
|
Loading…
Reference in New Issue