lh-l4v/spec/sep-abstract/Syscall_SA.thy

259 lines
9.1 KiB
Plaintext

(*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: GPL-2.0-only
*)
(*
Top-level system call interface of reduced kernel API.
*)
chapter "Separation Kernel Abstract Specification"
theory Syscall_SA
imports Decode_SA
begin
section "Introduction"
text \<open>
The Separation Kernel maintains separation between processes by limiting the capabilities that are
present in the system. We call these the "restricted capabilities" in the documentation that
follows.
The specification described here, the Separation Kernel Abstract Specification (abbreviated
\texttt{sep-abstract} from here on in), is identical to the Abstract Specification
(aka. \texttt{abstract}), except that the following system calls have been overridden to
provide reduced (fully static) functionality only.
\begin{itemize}
\item{handle_fault}
\item{invoke_irq_handler}
\item{decode_invocation}
\item{perform_invocation}
\item{handle_invocation}
\item{handle_reply}
\item{handle_event}
\item{call_kernel}
\end{itemize}
The resulting kernel API is simplified significantly compared to full seL4.
The changes to the original abstract specification are minimal, except that it contains
much fewer system calls.
We achieve this by modifying the cases distinctions that determine which API call is
to by executed. The new case distinctions
on capabilities only provide code for the restricted capabilities in our reduced setup,
otherwise they fail (i.e. throw an exception).
We then prove that \texttt{sep-abstract} and \texttt{abstract} have the same behaviour under the
restricted capabilities of the separation kernel via bi-simulation. This simply requires that we
prove refinement in both directions. This proof implies that the missing (failing) code branches
in the reduced specification can never be executed.
It is clear that the behaviour will be the same for the "mostly identical" overridden
definitions. In a few cases, which are documented below, the definitions have bigger differencess.
We provide ab informal explanation at the site of the overriden definition in each of these
cases. (The bi-simulation proof provides the formal demonstration.)
\<close>
section \<open>Generic system call structure\label{s:spec_syscall}\<close>
section \<open>System call entry point\<close>
fun
perform_invocation :: "bool \<Rightarrow> bool \<Rightarrow> invocation \<Rightarrow> (data list,'z::state_ext) p_monad"
where
"perform_invocation block call (InvokeNotification ep badge) =
doE
without_preemption $ send_signal ep badge;
returnOk []
odE"
| "perform_invocation _ _ _ = fail"
definition
handle_invocation :: "bool \<Rightarrow> bool \<Rightarrow> (unit,'z::state_ext) p_monad"
where
"handle_invocation calling blocking \<equiv> doE
thread \<leftarrow> liftE $ gets cur_thread;
info \<leftarrow> without_preemption $ get_message_info thread;
ptr \<leftarrow> without_preemption $ liftM data_to_cptr $
as_user thread $ getRegister cap_register;
syscall
(doE
(cap, slot) \<leftarrow> cap_fault_on_failure (of_bl ptr) False $ lookup_cap_and_slot thread ptr;
buffer \<leftarrow> liftE $ lookup_ipc_buffer False thread;
extracaps \<leftarrow> lookup_extra_caps thread buffer info;
returnOk (slot, cap, extracaps, buffer)
odE)
(\<lambda>fault. when blocking $ handle_fault thread fault)
(\<lambda>(slot,cap,extracaps,buffer). doE
args \<leftarrow> liftE $ get_mrs thread buffer info;
decode_invocation (mi_label info) args ptr slot cap extracaps
odE)
(\<lambda>err. when calling $
reply_from_kernel thread $ msg_from_syscall_error err)
(\<lambda>oper. doE
without_preemption $ set_thread_state thread Restart;
reply \<leftarrow> perform_invocation blocking calling oper;
without_preemption $ do
state \<leftarrow> get_thread_state thread;
case state of
Restart \<Rightarrow> do
when calling $
reply_from_kernel thread (0, reply);
set_thread_state thread Running
od
| _ \<Rightarrow> return ()
od
odE)
odE"
definition
handle_yield :: "(unit,'z::state_ext) s_monad" where
"handle_yield \<equiv> do
thread \<leftarrow> gets cur_thread;
do_extended_op (tcb_sched_action (tcb_sched_dequeue) thread);
do_extended_op (tcb_sched_action (tcb_sched_append) thread);
do_extended_op reschedule_required
od"
definition
handle_send :: "bool \<Rightarrow> (unit,'z::state_ext) p_monad" where
"handle_send bl \<equiv> handle_invocation False bl"
definition
handle_call :: "(unit,'z::state_ext) p_monad" where
"handle_call \<equiv> handle_invocation True True"
text \<open>
This definition of \texttt{handle_recv} is almost identical to the abstract specification's definition
for the restricted capabilities. Also, a call to \texttt{delete_caller_cap} has been removed. They have
the same behaviour under the restricted capabilities since there are no caller capabilities in
\texttt{sep-abstract}.
\<close>
definition
handle_recv :: "bool \<Rightarrow> (unit,'z::state_ext) s_monad" where
"handle_recv is_blocking \<equiv> do
thread \<leftarrow> gets cur_thread;
ep_cptr \<leftarrow> liftM data_to_cptr $ as_user thread $
getRegister cap_register;
(cap_fault_on_failure (of_bl ep_cptr) True $ doE
ep_cap \<leftarrow> lookup_cap thread ep_cptr;
let flt = (throwError $ MissingCapability 0)
in
case ep_cap
of
NotificationCap ref badge rights \<Rightarrow>
(if AllowRecv \<in> rights
then doE
ntfn \<leftarrow> liftE $ get_notification ref;
boundTCB \<leftarrow> returnOk $ ntfn_bound_tcb ntfn;
if boundTCB = Some thread \<or> boundTCB = None
then liftE $ receive_signal thread ep_cap is_blocking
else flt
odE
else flt)
| _ \<Rightarrow> flt
odE)
<catch> handle_fault thread
od"
text \<open>
The definition here has been specialised to \texttt{return ()}. The behaviour
is identical with the abstract specification under restricted capabilities because there
are no \texttt{Reply} capabilities in \texttt{sep-abstract}.
\<close>
definition
handle_reply :: "(unit,'z::state_ext) s_monad" where
"handle_reply \<equiv> return ()"
section \<open>Top-level event handling\<close>
text \<open>
The definition here is almost identical to that of the abstract specification (for the restricted
capablities), except that a call to \texttt{handle_reply} has been removed. Since there
are no \texttt{Reply}s in the restricted capabilities the behaviour is the same.
\<close>
fun
handle_event :: "event \<Rightarrow> (unit,'z::state_ext) p_monad"
where
"handle_event (SyscallEvent call) =
(case call of
SysSend \<Rightarrow> handle_send True
| SysNBSend \<Rightarrow> handle_send False
| SysCall \<Rightarrow> handle_call
| SysRecv \<Rightarrow> without_preemption $ handle_recv True
| SysYield \<Rightarrow> without_preemption handle_yield
| SysReply \<Rightarrow> without_preemption handle_reply
| SysReplyRecv \<Rightarrow> without_preemption $ handle_recv True
| SysNBRecv \<Rightarrow> without_preemption $ handle_recv False)"
| "handle_event (UnknownSyscall n) = (without_preemption $ do
thread \<leftarrow> gets cur_thread;
handle_fault thread $ UnknownSyscallException $ of_nat n;
return ()
od)"
| "handle_event (UserLevelFault w1 w2) = (without_preemption $ do
thread \<leftarrow> gets cur_thread;
handle_fault thread $ UserException w1 (w2 && mask 28);
return ()
od)"
| "handle_event Interrupt = (without_preemption $ do
active \<leftarrow> do_machine_op (getActiveIRQ False);
case active of
Some irq \<Rightarrow> handle_interrupt irq
| None \<Rightarrow> return ()
od)"
| "handle_event (VMFaultEvent fault_type) = (without_preemption $ do
thread \<leftarrow> gets cur_thread;
handle_vm_fault thread fault_type <catch> handle_fault thread;
return ()
od)"
| "handle_event (HypervisorEvent fault_type) = (without_preemption $ do
thread \<leftarrow> gets cur_thread;
handle_hypervisor_fault thread fault_type;
return ()
od)"
section \<open>Kernel entry point\<close>
text \<open>
This function is the main kernel entry point. The main event loop of the
kernel handles events, handles a potential preemption interrupt, schedules
and switches back to the active thread.
\<close>
definition
call_kernel :: "event \<Rightarrow> (unit,'z::state_ext_sched) s_monad" where
"call_kernel ev \<equiv> do
handle_event ev <handle>
(\<lambda>_. without_preemption $ do
irq \<leftarrow> do_machine_op (getActiveIRQ True);
when (irq \<noteq> None) $ handle_interrupt (the irq)
od);
schedule;
activate_thread
od"
end