1(* 2 * Copyright 2014, NICTA 3 * 4 * This software may be distributed and modified according to the terms of 5 * the GNU General Public License version 2. Note that NO WARRANTY is provided. 6 * See "LICENSE_GPLv2.txt" for details. 7 * 8 * @TAG(NICTA_GPL) 9 *) 10 11chapter {* \label{sec:examples}Example Systems *} 12 13(*<*) 14theory Examples_CAMKES 15imports 16 Types_CAMKES 17 Library_CAMKES 18 Wellformed_CAMKES 19begin 20(*>*) 21 22subsection {* \label{subsec:echo}Echo *} 23text {* 24 The following ADL and IDL describe an example system involving two 25 components, client and echo. There is a single connection between them, from 26 a procedure @{text client.s} that client requires to a procedure 27 @{text echo.s} that echo provides. The system is depicted in Figure 28 \ref{fig:echo}. 29 30 \begin{figure}[h] 31 \begin{center} 32 \caption{\label{fig:echo}Echo example} 33 \includegraphics[width=0.6\textwidth]{imgs/echo} 34 \end{center} 35 \end{figure} 36*} 37 38text {* 39 The procedure used in this system is expressed by the following IDL: 40 41\begin{verbatim} 42 procedure Simple { 43 string echo_string(in string s); 44 int echo_int(in int i); 45 void echo_parameter(in int pin, out int pout); 46 }; 47\end{verbatim} 48 49 The representation of this in Isabelle is quite similar:\footnote{The 50 procedure parameter type @{text int} 51 is a synonym for 52 @{term integer} and is 53 therefore not modelled in Isabelle.} 54*} 55definition 56 simple :: procedure (* Sourced from Simple.idl4 *) 57where 58 "simple \<equiv> [ 59 \<lparr> m_return_type = Some (Primitive (Textual String)), m_name = ''echo_string'', 60 m_parameters = [ 61 \<lparr> p_type = Primitive (Textual String), 62 p_direction = InParameter, 63 p_name = ''s'' \<rparr> 64 ] \<rparr>, 65 \<lparr> m_return_type = Some (Primitive (Numerical Integer)), m_name = ''echo_int'', 66 m_parameters = [ 67 \<lparr> p_type = Primitive (Numerical Integer), 68 p_direction = InParameter, 69 p_name = ''i'' \<rparr> 70 ] \<rparr>, 71 \<lparr> m_return_type = None, m_name = ''echo_parameter'', m_parameters = [ 72 \<lparr> p_type = Primitive (Numerical Integer), 73 p_direction = InParameter, 74 p_name = ''pin'' \<rparr>, 75 \<lparr> p_type = Primitive (Numerical Integer), 76 p_direction = InParameter, 77 p_name = ''pout'' \<rparr> 78 ] \<rparr> 79 ]" 80 81text {* 82 Each component of the system is described by a separate IDL representation: 83 84\begin{verbatim} 85 component Client { 86 control; 87 uses Simple s; 88 } 89\end{verbatim} 90 91\begin{verbatim} 92 component Echo { 93 provides Simple s; 94 } 95\end{verbatim} 96 97 These generate the following formal representations in Isabelle: 98*} 99definition 100 client :: component (* Sourced from Client.camkes *) 101where 102 "client \<equiv> \<lparr> 103 control = True, 104 requires = [(''s'', simple)], 105 provides = [], 106 dataports = [], 107 emits = [], 108 consumes = [], 109 attributes = [] 110 \<rparr>" 111 112definition 113 echo :: component (* Sourced from Echo.camkes *) 114where 115 "echo \<equiv> \<lparr> 116 control = False, 117 requires = [], 118 provides = [(''s'', simple)], 119 dataports = [], 120 emits = [], 121 consumes = [], 122 attributes = [] 123 \<rparr>" 124 125text {* 126 A @{term composition} block is used to combine these elements into the 127 complete system. There are no attributes in this simple system so the 128 @{term configuration} block of the @{term assembly} can be omitted. The two 129 components are connected via a seL4RPC connection. Note that the underlying 130 implementation mechanism of this seL4RPC connection is abstracted. 131 132\begin{verbatim} 133 assembly { 134 composition { 135 component Echo echo; 136 component Client client; 137 connection seL4RPC simple(from client.s, to echo.s); 138 } 139 } 140\end{verbatim} 141 142 Once again the generated Isabelle formalism looks similar: 143*} 144definition 145 system :: assembly (* Sourced from simple.camkes *) 146where 147 "system \<equiv> \<lparr> 148 composition = \<lparr> 149 components = [(''echo'', echo),(''client'', client)], 150 connections = [(''simple'', \<lparr> 151 conn_type = seL4RPC, 152 conn_from = [(''client'', ''s'')], 153 conn_to = [(''echo'', ''s'')] 154 \<rparr>)] 155 \<rparr>, 156 configuration = None 157 \<rparr>" 158 159text {* 160 Since our wellformedness conditions are executable, we can now prove that 161 this example is a wellformed assembly by evaluation. 162*} 163lemma "wellformed_assembly system" by eval 164 165subsection {* \label{subsec:events}Events *} 166text {* 167 \begin{figure}[h] 168 \begin{center} 169 \caption{\label{fig:event}Event example} 170 \includegraphics[width=0.6\textwidth]{imgs/event} 171 \end{center} 172 \end{figure} 173 174 The following example shows a system using a single event to provide 175 asynchronous communication between two components. The identifier assigned 176 to the event, @{text 1}, is unimportant in this example as there is only 177 one event in use. 178*} 179definition 180 signal :: event 181where 182 "signal \<equiv> 1" 183 184text {* 185 The active component @{text emitter} generates events of the type 186 @{term signal}. 187*} 188definition 189 emitter :: component 190where 191 "emitter \<equiv> \<lparr> 192 control = True, 193 requires = [], 194 provides = [], 195 dataports = [], 196 emits = [(''event'', signal)], 197 consumes = [], 198 attributes = [] 199 \<rparr>" 200 201text {* 202 The component @{text consumer} expects to receive these events. When a 203 component is defined to consume an event, a function for registering a 204 callback for this event is made available to the component. The component 205 is initialised at runtime with no callback registered to allow it to do 206 any necessary setup without needing to guard against concurrency. Thus, 207 even when consuming components are conceptually passive they are usually 208 specified as active (@{text "control = True"}) with an entry function that 209 performs some initialisation and then registers an event handler. 210*} 211definition 212 consumer :: component 213where 214 "consumer \<equiv> \<lparr> 215 control = True, 216 requires = [], 217 provides = [], 218 dataports = [], 219 emits = [], 220 consumes = [(''event'', signal)], 221 attributes = [] 222 \<rparr>" 223 224text {* 225 The system assembly looks similar to that shown in Section 226 \ref{subsec:echo}, but an asynchronous connector is used between the 227 components. 228*} 229definition 230 event_system :: assembly 231where 232 "event_system \<equiv> \<lparr> 233 composition = \<lparr> 234 components = [(''source'', emitter), (''sink'', consumer)], 235 connections = [(''simpleEvent1'', \<lparr> 236 conn_type = seL4Asynch, 237 conn_from = [(''source'', ''event'')], 238 conn_to = [(''sink'', ''event'')] 239 \<rparr>)] 240 \<rparr>, 241 configuration = None 242 \<rparr>" 243 244text {* 245 Again, wellformedness is proved easily by evaluation. 246*} 247lemma "wellformed_assembly event_system" by eval 248 249subsection {* \label{subsec:dataport}Dataport Usage *} 250text {* 251 \begin{figure}[h] 252 \begin{center} 253 \caption{\label{fig:dataport}Dataport example} 254 \includegraphics[width=0.6\textwidth]{imgs/dataport} 255 \end{center} 256 \end{figure} 257 258 The following example demonstrates the use of a shared memory region, referred 259 to as a dataport in CAmkES. It also uses one of the key aspects of a component 260 platform, component re-use. First the definition of a simple component that 261 uses two dataports: 262*} 263definition 264 data_client :: component 265where 266 "data_client \<equiv> \<lparr> 267 control = True, 268 requires = [], 269 provides = [], 270 dataports = [(''d1'', None), (''d2'', None)], 271 emits = [], 272 consumes = [], 273 attributes = [] 274 \<rparr>" 275 276text {* 277 By instantiating this component twice (once as @{text comp1} and once as 278 @{text comp2}) 279 the system contains two identical components. The assembly below connects the 280 first dataport of @{text comp1} to the second dataport of 281 @{text comp2} and vice versa. 282 It is possible to specify a system that instantiates @{term data_client} once and 283 connects one of the instance's dataports to the other, but there is nothing to 284 be gained from a component communicating with itself via shared memory. 285*} 286definition 287 data_system :: assembly 288where 289 "data_system \<equiv> \<lparr> 290 composition = \<lparr> 291 components = [(''comp1'', data_client), (''comp2'', data_client)], 292 connections = [(''simple1'', \<lparr> 293 conn_type = seL4SharedData, 294 conn_from = [(''comp1'', ''d1'')], 295 conn_to = [(''comp2'', ''d2'')] 296 \<rparr>), (''simple2'', \<lparr> 297 conn_type = seL4SharedData, 298 conn_from = [(''comp2'', ''d1'')], 299 conn_to = [(''comp1'', ''d2'')] 300 \<rparr>)] 301 \<rparr>, 302 configuration = None 303 \<rparr>" 304 305text {* The data port example is wellformed: *} 306lemma "wellformed_assembly data_system" by eval 307 308subsection {* \label{subsec:terminal}Secure Terminal *} 309text {* 310 This section presents a more realistic component system as a prototype of a 311 secure terminal. Two components are each given a separate region of a text 312 terminal to which they can write character data. They accomplish this by using 313 a connection to a third, trusted component that manages the terminal. 314 315 \begin{figure}[h] 316 \begin{center} 317 \caption{\label{fig:terminal}Terminal example} 318 \includegraphics[width=0.8\textwidth]{imgs/terminal} 319 \end{center} 320 \end{figure} 321 322 The interface for writing to the terminal takes coordinates to write to and a 323 single character to write. The coordinates are relative to the caller's 324 dedicated region. That is, (0, 0) represents the upper left corner of the 325 caller's region, not the terminal as a whole. The method @{text put_char} 326 returns 0 on success and non-zero if the coordinates are out of range. 327*} 328definition 329 display :: procedure 330where 331 "display \<equiv> [ 332 \<lparr> m_return_type = Some (Primitive (Numerical uint32_t)), m_name = ''put_char'', 333 m_parameters = [ 334 \<lparr> p_type = Primitive (Numerical uint32_t), 335 p_direction = InParameter, 336 p_name = ''x'' \<rparr>, 337 \<lparr> p_type = Primitive (Numerical uint32_t), 338 p_direction = InParameter, 339 p_name = ''y'' \<rparr>, 340 \<lparr> p_type = Primitive (Numerical uint32_t), 341 p_direction = InParameter, 342 p_name = ''data'' \<rparr> 343 ] \<rparr> ]" 344 345text {* 346 The trusted component that manages the terminal is passive and executes only 347 in response to @{text put_char} calls from its clients. The component 348 described below supports exactly two components. This is a case where a more 349 flexible definition would be possible using interface arrays as described in 350 Section \ref{subsubsec:iarrays}. 351*} 352definition 353 manager :: component 354where 355 "manager \<equiv> \<lparr> 356 control = False, 357 requires = [], 358 provides = [(''domain1'', display), (''domain2'', display)], 359 dataports = [], 360 emits = [], 361 consumes = [], 362 attributes = [] 363 \<rparr>" 364 365text {* 366 The definition of the client adds an attribute so the execution can branch 367 based on which instance of the component is running, but the instances could 368 equally well execute exactly the same code and have their (identical) output 369 written to the two distinct regions by the manager. 370*} 371definition 372 terminal_client :: component 373where 374 "terminal_client \<equiv> \<lparr> 375 control = True, 376 requires = [(''d'', display)], 377 provides = [], 378 dataports = [], 379 emits = [], 380 consumes = [], 381 attributes = [(''ID'', Primitive (Numerical Integer))] 382 \<rparr>" 383 384text {* 385 Each client is connected to a single interface of the manager. 386*} 387definition 388 channel1 :: connection 389where 390 "channel1 \<equiv> \<lparr> 391 conn_type = seL4RPC, 392 conn_from = [(''client1'', ''d'')], 393 conn_to = [(''manager'', ''domain1'')] 394 \<rparr>" 395definition 396 channel2 :: connection 397where 398 "channel2 \<equiv> \<lparr> 399 conn_type = seL4RPC, 400 conn_from = [(''client2'', ''d'')], 401 conn_to = [(''manager'', ''domain2'')] 402 \<rparr>" 403 404definition 405 comp :: composition 406where 407 "comp \<equiv> \<lparr> 408 components = [(''manager'', manager), 409 (''client1'', terminal_client), 410 (''client2'', terminal_client)], 411 connections = [(''channel1'', channel1), 412 (''channel2'', channel2)] 413 \<rparr>" 414 415text {* 416 Each client is given a unique identifier so it can distinguish itself. As 417 noted above, this is not necessarily required in all systems with multiple 418 instantiations of a single component. 419*} 420definition 421 conf :: configuration 422where 423 "conf \<equiv> [(''client1'', ''ID'', ''1''), 424 (''client2'', ''ID'', ''2'')]" 425 426definition 427 terminal :: assembly 428where 429 "terminal \<equiv> \<lparr> 430 composition = comp, 431 configuration = Some conf 432 \<rparr>" 433 434text {* 435 Wellformedness for this more complex example is easy as well. 436*} 437lemma "wellformed_assembly terminal" by eval 438 439(* An example with an unsatisfied required interface. This should be provable 440 * to be not wellformed. 441 *) 442(*<*)locale FAIL_MissingRequires begin(*>*) 443(*<*) 444(* This example is just here as a sanity check and not 445 * particularly relevant for the docs. 446 *) 447definition 448 x :: component 449where 450 "x \<equiv> \<lparr> 451 control = undefined, 452 requires = [(undefined, undefined)], (* 1 required interface...*) 453 provides = undefined, 454 dataports = undefined, 455 emits = undefined, 456 consumes = undefined, 457 attributes = undefined 458 \<rparr>" 459 460definition 461 broken_assembly :: assembly 462where 463 "broken_assembly \<equiv> \<lparr>composition = \<lparr> 464 components = [(undefined, x)], 465 connections = [] (*... that is unsatisfied. *) 466 \<rparr>, configuration = undefined\<rparr>" 467 468lemma "\<not> wellformed_assembly broken_assembly" 469 apply (unfold wellformed_assembly_def) 470 apply (subst de_Morgan_conj) 471 apply (rule disjI1) 472 apply (unfold wellformed_composition_def broken_assembly_def, simp) 473 apply (unfold refs_valid_composition_def 474 refs_valid_components_def 475 refs_valid_procedures_def 476 x_def ex_one_def) 477 apply fastforce 478 done 479(*>*) 480(*<*)end(*>*) 481 482(*<*)locale FAIL_Empty begin(*>*) 483(*<*) 484(* This example is just here as a sanity check and not 485 * particularly relevant for the docs. 486 *) 487definition 488 broken_assembly :: assembly 489where 490 "broken_assembly \<equiv> \<lparr> composition = \<lparr> 491 components = [], 492 connections = undefined 493 \<rparr>, configuration = undefined \<rparr>" 494 495lemma "\<not>wellformed_assembly broken_assembly" 496 apply (unfold wellformed_assembly_def) 497 apply (case_tac "wellformed_composition (composition broken_assembly)") 498 apply (unfold broken_assembly_def) 499 apply (frule wellformed_composition_is_nonempty) 500 apply simp+ 501 done 502(*>*) 503(*<*)end(*>*) 504 505(*<*) 506lemma "\<not>wellformed_assembly \<lparr> composition = \<lparr> 507 components = [(''foo'', undefined), (''foo'', undefined)], 508 connections = undefined 509 \<rparr>, configuration = undefined \<rparr>" 510 by (simp add:wellformed_assembly_def wellformed_composition_def) 511 512lemma "\<not>wellformed_assembly \<lparr> composition = \<lparr> 513 components = undefined, 514 connections = [(''foo'', undefined), (''foo'', undefined)] 515 \<rparr>, configuration = undefined \<rparr>" 516 by (simp add:wellformed_assembly_def wellformed_composition_def) 517 518lemma "\<not>wellformed_assembly \<lparr> composition = \<lparr> 519 components = [(''foo'', undefined)], 520 connections = [(''foo'', undefined)] 521 \<rparr>, configuration = undefined \<rparr>" 522 by (simp add:wellformed_assembly_def wellformed_composition_def) 523 524(* Catch previous issue (\<exists>! x \<in> xs::set \<noteq> \<exists>1 x \<in> xs::list) *) 525lemma "\<not>wellformed_assembly \<lparr> composition = \<lparr> 526 components = [(''foo'', \<lparr> 527 control = undefined, 528 requires = [(''bar'', undefined)], 529 provides = undefined, 530 dataports = undefined, 531 emits = undefined, 532 consumes = undefined, 533 attributes = undefined \<rparr>)], 534 connections = [(''bar'', \<lparr> 535 conn_type = undefined, 536 conn_from = [(''foo'', ''bar'')], 537 conn_to = undefined \<rparr>), 538 (''baz'', \<lparr> 539 conn_type = undefined, 540 conn_from = [(''foo'', ''bar'')], 541 conn_to = undefined \<rparr>)] 542 \<rparr>, configuration = undefined \<rparr>" 543 by (simp add:wellformed_assembly_def wellformed_composition_def refs_valid_components_def 544 refs_valid_composition_def refs_valid_procedures_def ex_one_def) 545 546lemma "\<not>wellformed_procedure []" 547 by (simp add:wellformed_procedure_def) 548(*>*) 549 550(*<*)end(*>*) 551