An Event-based Compositional Reasoning Approach for Concurrent Reactive Systems

10/18/2018 ∙ by Yongwang Zhao, et al. ∙ Beihang University 0

Reactive systems are composed of a well defined set of input events that the system reacts with by executing an associated handler to each event. In concurrent environments, event handlers can interact with the execution of other programs such as hardware interruptions in preemptive systems, or other instances of the reactive system in multicore architectures. State of the art rely-guarantee based verification frameworks only focus on imperative programs, being difficult to capture in the rely and guarantee relations interactions with possible infinite sequences of event handlers, and the input arguments to event handlers. In this paper, we propose the formalisation in Isabelle/HOL of an event-based rely-guarantee approach for concurrent reactive systems. We develop a Pi-Core language which incorporates a concurrent imperative and system specification language by `events', and we build a rely-guarantee proof system for Pi-Core and prove its soundness. Our approach can deal with multicore and interruptible concurrency. We use two case studies to show this: an interruptible controller for stepper motors and an ARINC 653 multicore kernel, and prove the functional correctness and preservation of invariants of them in Isabelle/HOL.

READ FULL TEXT VIEW PDF
POST COMMENT

Comments

There are no comments yet.

Authors

page 1

page 2

page 3

page 4

This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

1 Introduction

Nowadays high-assurance systems are often designed as concurrent reactive systems [2]. One of their key roles is how they behave in their computing environment, i.e., the sequence of commands the system executes under an input event. We call this behaviour reaction services. Examples of such systems are operating systems (OS), control systems, and communication systems. In this kind of system, how and when environment interactions happen are key aspects of their specification.

The rely-guarantee technique [12, 7, 22] represents a fundamental approach to compositional reasoning of concurrent programs with shared variables. However, concurrent languages used in existing rely-guarantee methods (e.g. [22, 19, 15]) do not provide a straightforward way to specify and verify the temporal aspect of reactive systems: the how and when. For instance, if we consider calls to the services offered by an operating system as input events, we can provide the specification handler for each service and model the OS handler as a case covering all the services; but this complicates the guarantee relation. Also, input arguments may be part of the state and sometimes they must not change during the execution of the event. Therefore, the relations must reflect this condition. Nevertheless, in a language not considering events, when having sequential composition of events it is not straightforward to state in the rely and guarantee that an argument does not change during the execution of a event. Then, the specification and verification for such systems become more difficult when not having a proper framework able to deal with these features.

Actually, the concept of event has been implicitly applied in formal verification. In seL4 [14], an event is defined to wrap all system calls of the kernel, while not considering in-kernel concurrency. In the deep specification approach [9], a finite map is used to represent events of an interface or module. In formal verification of multicore or preemptive OS kernels [10, 6, 21], rely conditions are defined as invariants to represent the environment behaviour. These studies apply the rely-guarantee technique for specific systems, rather than propose a generic framework for concurrent reactive systems. Event-B [1] provides a refinement-based formal method for system-level modeling and analysis, in which the execution of events is not in an interleaved manner. In [11], Event-B is extended to mimic rely-guarantee style reasoning for concurrent programs, but not to provide a rely-guarantee framework for Event-B. Compositional reasoning about critical properties (e.g., safety and security) for concurrent programs has attracted many research efforts (e.g., [8, 16, 18]). However, rely-guarantee-based compositional reasoning about them at system level deserves further study. For instance, noninterference of OS kernels [17] concerns the whole trace of events rather than the program states.

In this paper, we propose an event-based rely-guarantee reasoning approach for concurrent reactive systems. The approach supports compositional verification of functional correctness and safety as well as dealing with multicore concurrency and interruptible concurrency together. We introduce “events” [4, 1] into the rely-guarantee method to specify and verify reactive services. Developers could focus on specifying and verifying reactive services. Instead, compositional specification and verification are kindly supported in our framework. This can offer a flexible rely-guarantee framework for modeling and verification at system level [13]. We extend the imperative language in [22, 19] to specify imperative statements in events. Other richer imperative languages, such as CSimpl [20], can also be wrapped by events for specification and verification at implementation level. This work is the first effort to study the rely-guarantee method for system-level concurrency in the literature.

We focus on multicore concurrency and interruptible concurrency of reactive systems. First, recent multicore OS kernels such as XtratuM [5] and CertiKOS [10] are shared-variable concurrent reactive systems. Kernel instances may be executed simultaneously on different cores of a processor sharing the same memory. Interleaving may happen at arbitrary fine-grained program locations in interrupt handlers. Second, in interruptable systems, the execution of functions may be interrupted and jumps to the interrupt handler. During the execution of the handler, the function is blocked at the break point. Upon return from the handler, the system state could be substantially changed.

In detail, the technical contributions of this work are as follows.

  1. We propose an event-based language – -Core, which incorporates concurrent programming and system specification languages. We define a modular composition and a parallel composition of events as well as semantics of non-deterministic occurrence and interleaving of events. (Section 2)

  2. We build a rely-guarantee proof system for -Core and prove its soundness. We provide rules to prove properties with coarse and fine granularity. We can prove functional partial correctness of systems by rely-guarantee conditions of each event providing coarse granularity of properties, i.e. granularity at event level. We provide a compositional reasoning approach for safety properties defined as invariants providing fine granularity, i.e., granularity at internal steps of events. Invariant proof of systems can be discharged by local proof on each event. (Section 3)

  3. We develop two case studies: an interruptible controller for stepper motors and inter-partition communication (IPC) of ARINC 653 [3] multicore kernels. We prove the functional correctness and preservation of invariants of them. (Section 4)

  4. We develop the -Core language and the two case studies in Isabelle/HOL using 13,000 lines of new specification and proof based on those in [19]. The Isabelle sources are available at https://lvpgroup.github.io/picore_doc/.

2 The -Core Language

This section introduces the -Core language including its abstract syntax, operational semantics, and computations. We also create a concrete syntax for -Core, which is illustrated in our case studies.

2.1 Abstract Syntax

The abstract syntax of -Core is shown in Fig. 1. The syntax of programs representing the body of an event extends the syntax in [22, 19] with the command, which models nondeterminism through a state relation . The command executes the body atomically whenever the boolean condition holds. In the case study of multicore OS kernels, we use this command to model the synchronized access to shared resources from multiple partitions. As illustrated in the case study of interruptible controller, multi-level interrupts can also be modeled by this command via an interrupts stack. Other commands of programs are standard.

Program: Event:
Event System: Parallel Event System:
Figure 1: Abstract Syntax of -Core

Reaction services are modeled as events which are parameterized programs with a guard condition. The parameters indicate how a reaction service is triggered. They are the context when starting the execution of reaction services and decided by application programs, such as the parameters of an invocation to a system call. The guard condition, e.g. the interrupt flag in x86 is enabled, indicates when a reaction service can be triggered. The syntax for events distinguishes non-triggered events , called basic event, from triggered events that are being executed. A basic event is a tuple where defines the name, the guard condition, and the body of the event. is triggered when holds in the current state. Then, its body begins to be executed and the event is substituted by . We implement the parameterization of events in concrete syntax of -Core.

We define two categories of event composition in this paper: modular and parallel composition. An event system is a modular composition of events representing the behaviour of a single-processing system. It has two forms that we call event sequence and event set. The execution of an event set consists of a continuous evaluation of the guards of the events in the set. When there is an event in the set where holds in the current state, the event is triggered and its body executed. After finishes, the evaluation of the guards starts again looking for the next event to be executed. The event sequence models the sequential execution of events. In an event sequence , when the guard condition of holds in the current state, is triggered and the event sequence transits to , with being the body of . The event sequence behaves as the event system when the execution of finishes. This form of event sequences is able to specify the initialization of an event system. A concurrent reactive system is modeled by a parallel composition of event systems with shared states. It is a function from to event systems, where indicates the identifiers of event systems. This design is more general and could be applied to track executing events. For instance, we use to represent the core identifier in multicore systems.

2.2 Operational Semantics

The semantics of -Core is defined via transition rules between configurations. We define a configuration in -Core as a triple where is a specification, is a state, and is an event context. The event context indicates which event is currently executed in an event system .

A system can perform two kinds of transitions: action transitions and environment transitions. The former are performed by the system itself at a parallel event system or an event system; the latter by an arbitrary environment in a parallel event system, or by an event system when computing an event system with . Transition rules for actions in events, event systems, and parallel event systems have the form , where is a label indicating the kind of transition. Here can be a constant to indicate a program action or an occurrence of an event . means that the action occurs in event system . Environment transition rules have the form . Intuitively, a transition made by the environment may change the state and the event context but not the specification.

[Basic]
(Basic  f, s) ⟶c (⟂, f  s)-
[Seq1]
(P_1;;P_2, s) ⟶c (P_2, s’)(P_1,s) ⟶c (⟂,s’)
[Seq2]
(P_1;;P_2, s) ⟶c (P_1’;;P_2, s’)(P_1,s) ⟶c (P_1’,s’)  P_1’ ≠⟂
[CondF]
(Cond  b  P_1  P_2, s) ⟶c (P_2, s)s∉b
[CondT]
(Cond  b  P_1  P_2, s) ⟶c (P_1, s)s∈b
[WhileT]
(While  b  P, s) ⟶c (P;;(While  b  P), s)s∈b
[WhileF]
(While  b  P), s) ⟶c (⟂, s)s∉b
[Nondt]
(Nondt  r, s) ⟶c (⟂, s’)(s,s’) ∈r
[Await]
(Await  b  P, s) ⟶c (⟂, s’)s∈b (P, s) ⟶c* (⟂, s’)
[InnerEvt]
(⌊P⌋,s,x) ⟶c@k (⌊P’⌋,s’,x)(P,s) ⟶c (P’,s’)
[BasicEvt]
(Event  α, s, x) ⟶Event  α@κ (⌊P⌋, s, x’) P= body(α)  s∈guard(α)  x’ = x(k ↦Event  α)
[EvtSet]
({E_0,  … , E_n}, s, x) ⟶Ei@κ (E_i’⊕{E_0,  … , E_n},s, x’)i ≤n (E_i, s, x) ⟶Ei@κ (E_i’,s, x’)
[EvtSeq1]
(ES, s, x) ⟶t@κ (E’⊕S, s’, x’)(E, s, x) ⟶t@κ (E’, s’, x’)  E’ ≠⌊⟂⌋
[EvtSeq2]
(ES, s, x) ⟶t@κ (S, s’, x’) (E, s, x) ⟶t@κ (⌊⟂⌋, s’, x’)
[Par]
(PS, s, x) ⟶t@κ (PS’, s’, x’) (PS(κ), s, x) ⟶t@κ (S’, s’, x’)  PS’ = PS(κS’)
Figure 2: Operational Semantics of -Core

Transition rules of -Core are shown in Fig. 2. Transition rules of programs follow the traditional form of , since the execution of programs does not change the event context. in the Await rule is the reflexive transitive closure of . Nondt transits from a state to state if and it blocks otherwise. Other transition rules of programs are standard.

The execution of mimics program . The BasicEvt rule shows the occurrence of an event when its guard is true in the current state. It updates the specification and context of the current state to the program bounded to the triggered event and the event itself, respectively. The EvtSet, EvtSeq1, and EvtSeq2 rules specify the occurrence and execution of events in an event set. After the execution of the event, the event system behaves as the original event set.

The Par rule shows that the execution of a parallel event system is modeled by a non-deterministic interleaving of event systems. updates the function using to replace the mapping of . The parallel composition of event systems is fine-grained since small steps in events are interleaved in the semantics of -Core. This design relaxes the atomicity of events in other approaches (e.g., Event-B [1]).

Our framework in this paper tackles with partial correctness and therefore we are assuming program termination. In the semantics, state transformation of is atomic as well as guard evaluation of Cond, While, and Await statements. It is reasonable for specification for two reasons. First, complicated guard conditions in programming languages may be decomposed and specified by introducing local variables in -Core. Second, shared variables in concurrent programs, such as multicore OS kernels, are usually controlled by mutex.

2.3 Computation

A computation of -Core is a sequence of transitions. We define the set of computations of parallel event systems as , which is a set of lists of configurations inductively defined as follows. The singleton list is always a computation. Two consecutive configurations are part of a computation if they are the initial and final configurations of an environment or action transition. The operator in represents the insertion of element in list .

The computations of programs, events, and event systems are defined in a similar way. We use to denote the set of computations of a parallel event system . The function denotes the computations of executing from an initial state and event context . The computations of programs, events, and event systems are also denoted as the function. We say that a parallel event system is a closed system when there is no environment transition in computations of .

3 Compositional Verification of Functional Correctness and Safety

For the purpose of compositional reasoning, we propose a rely-guarantee proof system for -Core in this section. We first introduce the rely-guarantee specification and its validity. Then, we present a set of proof rules and their soundness for compositionality, and compositional reasoning about safety properties.

Formal specifications and proofs in existing rely-guarantee methods only consider the state of programs and they focus on traditional imperative sequential languages. At event-system level, events are basic components for the specification and compositional reasoning is conducted using the rely-guarantee conditions of events. This decomposition allows us to ease the specification in the rely and the guarantee of local variables to events, as well as the reasoning on sequences of events and properties involving local variables. We consider the verification of two different kinds of properties in the rely-guarantee proof system for reactive systems: pre- and post-conditions of events and invariants. We use the former for the verification of functional correctness, whilst we use the latter on the verification of safety properties concerning the internal steps of events. For instance, in the case of the interruptible controller, a safety property is that collisions must not happen even during internal steps of the forward and backward system calls. Other critical properties can also be defined considering the execution trace of events, e.g. information-flow security [16, 18].

3.1 Rely-Guarantee Specification

A rely-guarantee specification for a system is a quadruple , where is the pre-condition, is the rely condition, is the guarantee condition, and is the post-condition. The assumption and commitment functions are denoted by and respectively. For each computation , we use to denote the configuration at index . For convenience, we use to denote computations of programs, events, and event systems. , , and represent the elements of .

We define the validity of a rely-guarantee specification in a parallel event system as follows.

Intuitively, validity represents that the set of computations starting at configuration , with and any environment transition belonging to the rely relation , is a subset of the set of computations where action transitions belongs to the guarantee relation and where if a system terminates, then the final states belongs to . Validity for programs, events, and event systems are defined in a similar way.

3.2 Compositional Proof Rules

We present the proof rules in Fig. 3, which gives us a relational proof method for concurrent systems. is the universal set. We first define . Thus, means that the pre-condition is stable when the rely condition holds.

[Basic]
⊢(Basic  f)  sat  ⟨pre, R, G, pst ⟩
[Cond]
⊢(Cond  b  P_1  P_2)  sat  ⟨pre, R, G, pst ⟩
[Seq]
⊢(P;;Q)  sat  ⟨pre, R, G, pst ⟩
[While]
⊢(While  b  P)  sat  ⟨pre, R, G, pst ⟩
[Await]
⊢(Await  b  P)  sat  ⟨pre, R, G, pst ⟩
[Nondt]
⊢(Nondt  r)  sat  ⟨pre, R, G, pst ⟩
[Conseq]
⊢♯  sat  ⟨pre, R, G, pst ⟩
[BasicEvt]
Event  α  sat  ⟨pre, R, G, pst ⟩
[Inner]
⊢(⌊P⌋)  sat  ⟨pre, R, G, pst ⟩ ⊢P  sat  ⟨pre, R, G, pst ⟩
[EvtSeq]
⊢(ES)  sat  ⟨pre, R, G, pst ⟩
[EvtSet]
⊢({E_0,  … , E_n})  sat  ⟨pre, R, G, pst ⟩
[Par]
PS  sat  ⟨pre, R, G, pst ⟩
Figure 3: Rely-guarantee Proof Rules for -Core

For , by the semantics of the command, the evaluation of the condition and the execution of the body are done atomically. Thus, the state transition of the command must satisfy the guarantee condition. This is presented in the pre- and post-conditions of in the assumptions of the Await rule. We use an universally quantified variable to relate the state before and after the transformation. The intermediate state changes during the execution of must not guarantee anything, thus the guarantee condition is . Since is executed atomically, the environment cannot change the state, i.e. the rely condition is the identity relation . To ensure that both the pre- and post-conditions are preserved after environment transitions we request stability of and .

For , any state change in requires that holds immediately after the action transition and the transition should be in relation. Before and after the action transition there may be a number of environment transitions that can modify the state. To ensure that both the pre- and post-conditions are preserved after environment transitions we request stability of and . Other proof rules for programs are standard, and we reuse them from [19].

For an inner of events, it is just a wrapper of a program, and they have the same state and event context in their computations according to the InnerEvt transition rule in Fig. 2. Therefore, satisfies the rely-guarantee specification iff the program satisfies the specification. For a basic event, it satisfies the rely-guarantee specification, if its body satisfies the rely-guarantee condition with an augmented pre-condition with the guard condition of the event. Since the occurrence of an event does not change the state (BasicEvt rule in Fig. 2), we require that , i.e. . As illustrated in our case studies, it is reasonable since the set is usually a subset of the rely condition of other event systems. Moreover, there may be a number of environment transitions before the event occurs. ensures that holds during the environment transitions.

Regarding the proof rules for event systems, sequential composition of events is modeled by rule EvtSeq, which is similar to the rule for sequential statement. In order to prove that an event set satisfies its rely-guarantee specification, we have to prove eight premises (EvtSet rule in Fig. 3). It is necessary that each event together with its specification is derivable in the system (Premise 1). Since the event set behaves as itself after an event finishes, then the post-condition of each event should imply the pre-condition of each event (Premise 2), and the pre-condition for the event set has to imply the pre-conditions of all events (Premise 3). An environment transition for event corresponds to a transition from the environment of the event set (Premise 4). The guarantee condition of each event must be in the guarantee condition of the event set, since an action transition of the event set is performed by one of its events (Premise 5). The post-condition of each event must be in the overall post-condition (Premise 6). The last two refer to stability of the pre-condition and identity of the guarantee relation.

The Conseq rule can be applied to programs, events, and event systems, where the specification is denoted as , allowing us to strengthen the assumptions and weaken the commitments.

We now introduce the proof rule Par for parallel composition of event systems. In order to prove that a concurrent reactive system satisfies its rely-guarantee specification, we have to prove six premises (Par rule in Fig. 3). A concurrent system in -Core is modeled as a function from to event systems. It is necessary that each event system satisfies its specification (Premise 1). The pre-condition for the parallel composition imply all the event system’s pre-conditions (Premise 2). The overall post-condition must be a logical consequence of all post-conditions of event systems (Premise 3). Since an action transition of the concurrent system is performed by one of its event system, the guarantee condition of each event system must be a subset of the overall guarantee condition (Premise 4). An environment transition for the event system corresponds to a transition from the overall environment (Premise 5). An action transition of an event system should be defined in the rely condition of another event system , where (Premise 6).

Besides the proof rule for each language constructor of -Core (Fig. 3), we also define a set of auxiliary proof rules for programs to ease complicated proof of large programs, as shown in Fig. 4. The first two rules shows the union of pre-conditions and intersection of post-conditions. The UnivPre rule is usefull for command, due to the first premise of the Await rule in Fig. 3. The last rule EmptyPre means that any program satisfies a rely-guarantee specification with an empty pre-condition. It is usually applied for conditional statements when the condition is .

[UnPre]
⊢P  sat  ⟨pre ∪pre’, R, G, pst ⟩
[IntPost]
⊢P  sat  ⟨pre, R, G, pst ∩pst’ ⟩
[UnivPre]
⊢P  sat  ⟨pre, R, G, pst ⟩
[EmptyPre]
⊢P  sat  ⟨{}, R, G, pst ⟩ -
Figure 4: Auxiliary Rely-guarantee Proof Rules for Programs

3.3 Soundness of Proof System

Finally, the soundness theorem for being a specification of programs, events, event systems, or parallel event systems, relates specifications proven on the proof system with its validity.

[Soundness] If  , then .

The soundness of rules for programs is discussed in detail in [22] and we reuse the Isabelle/HOL sources of [19]. The soundness of auxiliary proof rules for programs are straightforward and proved by induction on rules of programs constructors. The soundness of rules for events is obvious and proved by the rules for programs.

To prove soundness of rules for event systems, we first show how to decompose a computation of event systems into computations of its events. We define an equivalent relation on computations as follows. Here, we concern the state, event context, and transitions, but not the specification of a configuration.

[Simulation of Computations] A computation is a simulation of , denoted as , if and .

In order to decompose computations of event systems to those of events, we define serialization of events based on the simulation of computations.

[Serialization of Events] A computation of event systems is a serialization of a set of events , denoted by , iff there exist a set of computations , where for there exists that , such that .

Then, we can decompose a computation of an event system into a set of computation of its events as follows.

For any computation of an event system , .

The soundness of the EvtSeq rule is proved by two cases. For any computation of “”, the first case is that the execution of event does not finish in . In such a case, . By the first premise of this rule, we can prove the soundness. In the second case, the execution of event finishes in . In such a case, we have , where and . By the two premises of this rule, we can prove the soundness. The soundness of the EvtSet rule is complicated. From Lemma 3.3, we have that for any computation of the event set, , for there exists that . When is in , from , , and , we have that there is one for each that is in . By the first premise in the EvtSet rule, we have is in . Finally, with and , we have that is in .

To prove the soundness of the PAR rule, we first use conjoin in Definition 3.3 to decompose a computation of parallel event systems into computations of its event systems. Computations of a set of event systems can be combined into a computation of the parallel composition of them, iff they have the same state and event context sequences, as well as not having the action transition at the same time. The resulting computation of also has the same state and event context sequences. Furthermore, in this computation a transition is an action transition on core if this is the action in the computation of event system at the corresponding position; a transition is an environment transition if this is the case in all computations of event systems at the corresponding position. By the definition, we have that the semantics is compositional as shown in Lemma 3.3. Then, the soundness of the Par rule is proved by a similar way in [22, 19].

A computation of a parallel event system and a set of computations conjoin, denoted by , iff

  • .

  • .

  • .

  • for any , one of the following two cases holds:

    • , and .

    • , , and .

The semantics of -Core is compositional, i.e., .

3.4 Safety Verification

This subsection discusses formal verification of safety properties defined as invariants preserved in each internal transition in an event. We use a set of states to describe the possible initial states of a parallel event system , and we say that a set of states is an invariant of with respect to if for each reachable state from an initial state in , . We regard a parallel event system as a closed system for safety, i.e., it has no environment transition ().

A set of states is an invariant of w.r.t. , iff

To show that is an invariant of , it suffices to show that (1) initially holds in , and (2) is preserved by each transition of . First, we prove that each event satisfies its rely-guarantee specification. Then, by the EvtSet and EvtSeq proof rules in Fig. 3, we can get the rely-guarantee specification for each event system of , i.e., . To show the premise (2), it suffices to show that (2.1) each action transition of an event preserves the guarantee condition of the event, and (2.2) the guarantee condition of all events preserves . To show the premise (2.1), it suffices to show that rely-guarantee conditions of event systems are compatible, i.e., for any such that , is a subset of . Moreover, we only consider computations of with an initial state in and without environment transitions. These premises are those of the Par proof rule by reduction of . We specify the guarantee and post-conditions as to automatically ensure the premises of the Par proof rule, that is for any such that is a subset of and is a subset of . Therefore, we have the following theorem for invariant verification:

[Invariant Verification] For any , , and , if

  • .

  • .

  • The guarantee condition of each event in is stable for , i.e.,

then is an invariant of w.r.t. .

We give the rely-guarantee specification for each event in by a function . is the rely-guarantee specification of the event , and is the guarantee condition in its specification.

4 Case Studies

We develop formal specifications as well as their correctness and invariant proof in Isabelle/HOL for the two case studies. The architectures of them are shown in Fig. 5.

Figure 5: Architectures of Case Studies

The first case study is a demo of an unmanned vehicle using a stepper motor. The application software and the controller are deployed on an Arduino development board. The controller provides bigstep system calls to applications, such as moving 10 meters forward. In the system calls, the controller drives the stepper motor in microstep mode. Since obstacles may appear at any time and be detected by a radar during movement of the vehicle, the execution of system calls in the controller has to be interruptible to avoid collision.

ARINC 653 [3] is the premier safety standard of partitioning OSs targeted at multicore processing environments and has been complied with by mainstream industrial implementations. A kernel instance executes on each physical core to manage and schedule the deployed partitions on it. System calls are invoked by programs in partitions by triggering a syscall interrupt. The scheduling is triggered by a timer interrupt. A multicore kernel is thus considered as a reactive system to these interrupts. Interrupt handlers are executed in parallel on processor cores and may access shared resources (e.g., communication channels). Note that device drivers are deployed in partitions and execute by invoking system calls of ARINC 653.

We create the concrete syntax of -Core in Isabelle/HOL to ease the development of system specification. In the concrete syntax, the statement “ATOM  c  END” denotes an atomic program, which is syntactically “AWAIT  True  THEN  c  END”. “” is denoted as “” . When there is no “” part, it means that the guard condition is . A parameterized event is defined as “”, where is an identifid label and is a list of parameters. , and are thus variables in the event declaration of . In the two case studies, we instantiate the event name to a tuple which is syntactically represented as “elabel [ plist ] @ ”.

4.1 Interruptible Controller for Stepper Motor

EVENT forward  [Natural v] @ C  THEN  C i := 0;;    C pos_aux := car_pos;;                      WHILE i int  v collide (car_pos + 1)  obstacle_pos DO      C ATOM    IF collide (car_pos + 1)  obstacle_pos THEN       car_pos := car_pos + 1                                    FI  END;;   C i := i + 1                     OD;;  C iret   END
Figure 6: Definition of Event and its Proof Sketch

In the case study, we apply our approach to preemption and multi-level interrupts. We prove functional correctness and safety properties of an unmanned vehicle with a controller for a stepper motor and a radar to detect obstacles. The architecture of the case is illustrated in Fig. 5 (a). We assume that the vehicle can move forward or backward. Thus, we consider two system calls of the controller, i.e. forward(nat v) and backward(nat v), where v is the distance to move. When the controller received a system call, it drives the stepper motor in the microstep mode until moving over the distance. When the vehicle is moving, obstacles may appear and be detected by the radar. To avoid collision, the radar then sends an IRQ to the controller to interrupt the movement.

We design three concurrent modules: a radar (R), a controller (C), and a programmable interrupt controller (PIC). The two system calls and the reaction to radar are interrupt handlers. System calls from applications and detections from radar will send IRQs to the PIC. Then the PIC blocks the current handler and jumps to a new one. In order to represent the multi-level interrupts, we define a stack to save the IRQs and use a guarded statement to represent that an internel step of a handler can be executed only when the handler is the top element of the stack, i.e. the currently executing handler. The system state is defined by a car_pos variable showing the current position of the vehicle; an obstacle_pos saving the positions of all obstacles detected; and auxiliary variables i, pos_aux, and obst_pos_aux locally used in the events.

We define a set of events to specify the interrupt handlers for system calls, detected obstacle, and IRQ to PIC. The event is the handler of the system call “forward(nat distance)” shown in Fig. 6 in black color. The event is the handler of the system call “backward(nat distance)”, which is similar to and not presented here. The two handlers drive the stepper motor to move one step forward or backward each time until it moves over steps. During the movement, if the handler finds that there is an obstacle in the next position, it stops immediately. In the end of the handler, the iret statement pops the IRQ stack. The event shown as follows is the handler of IRQs from the radar. The event will insert the position of the appeared obstacle into obstacle_pos. Here, we assume that an obstacle will not appear at the current position of the vehicle as well as one step before and after the current position. Otherwise, the vehicle cannot avoid collision in time. The event simulates the receiving of IRQs in the PIC. It pushes the new IRQ to the stack. We assume that if an IRQ from one device is being handled, no IRQ from the same device will come. The two events are shown as follows. In this case, we permit that events (or ) and could be preempted by each other.

EVENT obstacle  [Integer v] @ R THEN  R obst_pos_aux := obstacle_pos;;  R IF  v car_pos v car_pos + 1 v car_pos - 1  THEN     obstacle_pos := v # obstacle_pos    FI;;  R iret END EVENT IRQs  [Irq d] @ PIC THEN  ATOM     IF hd  stack d  THEN    push  d   FI    END END

The system is the parallel composition of the controller, the radar, and the PIC, shown as follows. Since the events can be triggered with any parameter, we use the union of events w.r.t. the parameters.

The functional correctness of the system is specified and verified by concerning the rely-guarantee conditions of their events. We define the rely-guarantee conditions of each event. As an example, the condition of is defined as follows. The expression is a concrete syntax for a set of states satisfying . We present the value of a variable in the state by . We present the value of a variable in the state before and after a transition by and respectively. The pre-condition is relaxed to the universal set . The rely condition shows that and the two local variables ( and ) are not changed by the environment. Moreover, during handling radar IRQs (), the rely condition includes state changes in the events and . In first case, remains unchanged during stack operation () and assignment of local variables (). In second case, new obstacles may occur and thus . As we assume that obstacles will not appear at one step forward the vehicle, the rely condition also requires that collision at is the same for before and after obstacles occurring. If no new obstacles are detected, i.e., the controller is currently running (), the environment does not change the variables and . The PIC may receive new IRQs from the Radar (), and then is interrupted. The post-condition defines the correctness of the event. It means that the vehicle will be moved steps forward. If no obstacle appears among the distance , then . Otherwise, the vehicle stops before the obstacle, i.e. .

Functional correctness of the event is proven by induction of rely-guarantee proof rules. We use and to denote the rely and guarantee conditions of respectively. Fig. 6 shows the proof sketch of satisfies its rely-guarantee condition. In the figure, pre- and post-conditions of each statement are shown in blue colour, and verification conditions produced by proof rules in green colour. We omit general proof obligations in rely-guarantee, i.e., stability of pre- and post-conditions w.r.t. the rely relation, and that rely and guarantee are reflexive. We use a loop invariant for the WHILE statement. Then, the functional correctness of Ctrl, Radar, and PIC could be proved using the EvtSet proof rule in Fig. 3. Finally, we define the rely-guarantee condition of the whole system as and prove the correctness using the Par proof rule. We release the pre-condition to the universal set and consider the system as a closed one, i.e. the rely condition is an empty set.

We verify a safety property defined as of the system, which means that the vehicle will not collide with any obstacle at any time. By the functional correctness of the system and the Conseq proof rule, it is straightforward that , where is the initial state of the system. Then we prove that is an invariant of using Theorem 3.4 by showing that and the guarantee condition of each event is stable for .

In the case study, we have only one external device, i.e. the radar. It is straightforward to support multiple devices which could be specified as event systems in -Core.

4.2 IPC in ARINC 653 Multicore OS Kernels

This case study concerns multicore concurrency and invariant verification. Since device drivers run in special partitions, we do not consider multi-level interrupts in the kernel. As shown in Fig. 5 (b), IPC in ARINC 653 is conducted via messages on channels configured among partitions. Partitions have access to channels via ports which are the endpoints of channels. A queuing mode channel has a source port, a destination port, and a bounded FIFO message queue.

The kernel configuration is split into static and dynamic components in Isabelle. We create a constant to be used in the specification defining static components of the state. In , is the mapping from cores to schedulers and is bijective. is the deployment of partitions to schedulers. indicates the partition a port belongs to. and indicate the source port and destination port of a queuing channel. Finally, defines the maximum capacity of a channel. Our specification is created based on abstract data types for these elements: , , , , . It means that we conduct formal verification on arbitrary system configurations rather than a concrete instance.

The dynamic component of the kernel state concerns states of schedulers, channels, and partitions. The state of a scheduler shows the current partition under execution. The state of a channel keeps the information of the messages in the FIFO queue. The state of a partition is defined as IDLE, READY or RUN. We define as the initial state of the system.

record Config  c2s  Core   Sched     p2s  Part   Sched     p2p   Port    Part                                 chsrc   QChannel    Port     chdest   QChannel    Port                                 chmax   QChannel    nat

record State  cur   Sched  Part  option     qbuf   QChannel    Message list                               qbufsize   QChannel    nat     partst   Part    PartMode

We define a set of events to specify the scheduling and communication services. These events are parametrized by their input parameters and the core identifier . The and events are shown below. A partition can be scheduled on processor core when was deployed on and the state of is not IDLE. The event first sets the state of the currently running partition to READY and current partition on to . Then it sets as the current partition and its state to RUN. The event can happen in the current partition on processor core when the source port is configured in the current partition. The event will be blocked until the message queue of the operated channel has available spaces. Then it inserts the message into the tail of the message queue and increases the size of the queue.

EVENT Schedule  [Part p] @ WHEN p2s conf p = c2s conf partst p IDLE THEN  IF cur((c2s conf) ) None THEN   ATOM     partst := partst(cur ((c2s conf) ) := READY);;    cur := cur((c2s conf) := None)     END  FI;;  ATOM    cur := cur((c2s conf) := Some p);;   partst := partst(p := RUN)     END END EVENT Send_QMsg  [Port p, Msg m] @ WHEN is_src_port conf p cur (c2s conf )