-
Notifications
You must be signed in to change notification settings - Fork 108
/
Syscall_SA.thy
258 lines (212 loc) · 9.14 KB
/
Syscall_SA.thy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
(*
* 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