diff --git a/nucleus/src/api.rs b/nucleus/src/api.rs index 6189a43..ef22fcb 100644 --- a/nucleus/src/api.rs +++ b/nucleus/src/api.rs @@ -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!( + "<>", + 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!( + "<>", + 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!("<>", KernelCurrentThread, KernelCurrentThread.name, KernelCurrentThread.get_restart_pc()); + }, + _ => { + panic!("<>", 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!() - } + //... } diff --git a/nucleus/src/arch/aarch64/traps.rs b/nucleus/src/arch/aarch64/traps.rs index 4f674ba..242be67 100644 --- a/nucleus/src/arch/aarch64/traps.rs +++ b/nucleus/src/arch/aarch64/traps.rs @@ -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,