(* * Copyright 2014, NICTA * * This software may be distributed and modified according to the terms of * the GNU General Public License version 2. Note that NO WARRANTY is provided. * See "LICENSE_GPLv2.txt" for details. * * @TAG(NICTA_GPL) *) (* * System calls *) theory Syscall_D imports Schedule_D Decode_D "ExecSpec.Event_H" begin (* * Perform system calls. * * Each system call is broken into three stages: * * (1) Cap validation, where we ensure that all caps passed * into the system call are valid; * * (2) Argument validation, where we ensure that the requested * operation is valid and permitted; and * * (3) Syscall execution, where we carry out the actual * operation. * * For this function, the user passes us in 5 functions: * * (1, 2) A cap validation function, and an error handler; * (3, 4) A argument validation function, and an error handler; * (5) A syscall execution function. * * This function returns an item of type "'c", and may also * return an exception if operation (3) above was preempted * by an interrupt. *) definition syscall :: " ('a fault_monad) \ (unit k_monad) \ ('a \ 'b except_monad) \ (unit k_monad) \ ('b \ unit preempt_monad) \ unit preempt_monad" where "syscall cap_decoder_fn decode_error_handler_fn arg_decode_fn arg_error_handler_fn perform_syscall_fn \ cap_decoder_fn (\ _. liftE $ decode_error_handler_fn) (\ a. ((arg_decode_fn a) (\ _. liftE $ arg_error_handler_fn) perform_syscall_fn)) " fun perform_invocation :: "bool \ bool \ cdl_invocation \ unit preempt_monad" where "perform_invocation is_call can_block (InvokeUntyped untyped_params) = (invoke_untyped untyped_params)" | "perform_invocation is_call can_block (InvokeEndpoint endpoint_params) = liftE (invoke_endpoint is_call can_block endpoint_params)" | "perform_invocation is_call can_block (InvokeNotification ntfn_params) = liftE (invoke_notification ntfn_params)" | "perform_invocation is_call can_block (InvokeReply reply_params) = liftE (invoke_reply reply_params)" | "perform_invocation is_call can_block (InvokeTcb tcb_params) = (invoke_tcb tcb_params)" | "perform_invocation is_call can_block (InvokeDomain domain_params) = (invoke_domain domain_params)" | "perform_invocation is_call can_block (InvokeCNode cnode_params) = invoke_cnode cnode_params" | "perform_invocation is_call can_block (InvokeIrqControl irq_params) = liftE (invoke_irq_control irq_params)" | "perform_invocation is_call can_block (InvokeIrqHandler handler_params) = liftE (invoke_irq_handler handler_params)" | "perform_invocation is_call can_block (InvokePageTable page_table_params) = liftE (invoke_page_table page_table_params)" | "perform_invocation is_call can_block (InvokePage page_params) = liftE (invoke_page page_params)" | "perform_invocation is_call can_block (InvokeAsidControl asid_control_params) = liftE (invoke_asid_control asid_control_params)" | "perform_invocation is_call can_block (InvokeAsidPool asid_pool_params) = liftE (invoke_asid_pool asid_pool_params)" | "perform_invocation is_call can_block (InvokePageDirectory page_dir_params) = liftE (invoke_page_directory page_dir_params) " definition ep_related_cap :: "cdl_cap \ bool" where "ep_related_cap cap \ case cap of cdl_cap.EndpointCap o_id badge rights \ True | cdl_cap.NotificationCap o_id badge rights \ True | cdl_cap.ReplyCap o_id \ True | _ \ False" definition "has_restart_cap \ \tcb_id. do t \ get_thread tcb_id; return ((cdl_tcb_caps t) tcb_pending_op_slot = Some cdl_cap.RestartCap) od" definition handle_invocation :: "bool \ bool \ unit preempt_monad" where "handle_invocation is_call can_block \ doE thread_ptr \ liftE $ gets_the cdl_current_thread; thread \ liftE $ get_thread thread_ptr; full_intent \ returnOk $ cdl_tcb_intent thread; intent \ returnOk $ cdl_intent_op full_intent; invoked_cptr \ returnOk $ cdl_intent_cap full_intent; extra_cap_cptrs \ returnOk $ cdl_intent_extras full_intent; syscall (* Lookup all caps presented. *) (doE (cap, cap_ref) \ lookup_cap_and_slot thread_ptr invoked_cptr; extra_caps \ lookup_extra_caps thread_ptr extra_cap_cptrs; returnOk (cap, cap_ref, extra_caps) odE) (* If that failed, send off a fault IPC (if we did a blocking operation). *) (when can_block $ handle_fault) (* Decode the user's intent. *) (\ (cap, cap_ref, extra_caps). case intent of None \ (if ep_related_cap cap then decode_invocation cap cap_ref extra_caps undefined else throw) | Some intent' \ decode_invocation cap cap_ref extra_caps intent') (* If that stuffed up, we do nothing more than corrupt the frames. *) (do corrupt_ipc_buffer thread_ptr True; when is_call (mark_tcb_intent_error thread_ptr True) od) (* Invoke the system call. *) (\ inv. doE liftE $ set_cap (thread_ptr,tcb_pending_op_slot) RestartCap; perform_invocation is_call can_block inv; restart \ liftE $ has_restart_cap thread_ptr; whenE restart $ liftE (do corrupt_ipc_buffer thread_ptr True; when (is_call) (mark_tcb_intent_error thread_ptr False); set_cap (thread_ptr,tcb_pending_op_slot) RunningCap od) odE) odE " definition handle_recv :: "unit k_monad" where "handle_recv \ do (* Get the current thread. *) tcb_id \ gets_the cdl_current_thread; tcb \ get_thread tcb_id; (* Get the endpoint it is trying to receive from. *) (doE ep_cptr \ returnOk $ cdl_intent_cap (cdl_tcb_intent tcb); ep_cap \ lookup_cap tcb_id ep_cptr; (case ep_cap of EndpointCap o_id badge rights \ if Read \ rights then (liftE $ do delete_cap_simple (tcb_id, tcb_caller_slot); receive_ipc tcb_id (cap_object ep_cap) od) \ throw else throw | NotificationCap o_id badge rights \ if Read \ rights then (liftE $ recv_signal tcb_id ep_cap) \ throw else throw | _ \ throw) odE) (\ _. handle_fault) od " definition handle_reply :: "unit k_monad" where "handle_reply \ do tcb_id \ gets_the cdl_current_thread; caller_cap \ get_cap (tcb_id, tcb_caller_slot); case caller_cap of ReplyCap target \ do_reply_transfer tcb_id target (tcb_id, tcb_caller_slot) | NullCap \ return () | _ \ fail od " definition handle_hypervisor_fault :: "unit k_monad" where "handle_hypervisor_fault \ return ()" definition handle_syscall :: "syscall \ unit preempt_monad" where "handle_syscall sys \ case sys of SysSend \ handle_invocation False True | SysNBSend \ handle_invocation False False | SysCall \ handle_invocation True True | SysRecv \ liftE $ handle_recv | SysYield \ returnOk () | SysReply \ liftE $ handle_reply | SysReplyRecv \ liftE $ do handle_reply; handle_recv od | SysNBRecv \ liftE $ handle_recv" definition handle_event :: "event \ unit preempt_monad" where "handle_event ev \ case ev of SyscallEvent sys \ handle_syscall sys | UnknownSyscall n \ liftE $ handle_fault | UserLevelFault a b \ liftE $ handle_fault | VMFaultEvent c \ liftE $ handle_fault | Interrupt \ liftE $ handle_pending_interrupts | HypervisorEvent w \ liftE $ handle_hypervisor_fault " definition call_kernel :: "event \ unit k_monad" where "call_kernel ev \ do (* Deal with the event. *) handle_event ev (\ _. liftE handle_pending_interrupts); schedule; t \ gets cdl_current_thread; case t of Some thread \ do restart \ has_restart_cap thread; when restart $ set_cap (thread, tcb_pending_op_slot) RunningCap od | None \ return () od" end