1 Introduction
Partitioning Operating Systems (POSs) [
33] can provide hard guarantees of temporal and spatial partitioning and isolation. These guarantees can ensure that the effects of any misbehavior are confined to their own components. The SAE-ITC promotes the ARINC 653 standard [
5], which standardizes the interface between POSs and application software as well as the system functionality of POSs. ARINC 653 is actually the de-factor standard of POSs. Many ARINC 653-compliant productions, such as the commercial VxWorks653 [
37], INTEGRITY-178B [
21], LynxOS-178 [
38], PikeOS [
39], and the open-source production, e.g., POK [
13], have been widely applied in many domains from aerospace to automotive.
The ARINC 653 Part 1-4 (Version 4) [
5] standardizes APIs for multiprocessor platforms. However, the standard document is described using informal textual language and diagrams, which makes it hard to analyze the consistency and correctness of the standard itself.
A rigorous and machine-checkable specification and analysis of ARINC 653 can help to ensure the correctness of this standard. For concurrency [
8] on multi-core platforms, the formal proof of seL4 [
9,
16,
25,
40] using Isabelle/HOL [
29] is based on the coarse-grained locking [
32]. The CertiKOS [
18,
19] using Coq [
15] builds certified, fine-grained locking concurrent OS kernels that support interleaved execution of kernel/user modules across different layers of abstraction. Nonetheless, CertiKOS doesn’t support the POS policy.
There are some works on the single-core ARINC 653 [
10,
31,
36,
42]. We have utilized Event-B to construct formal specifications and perform analysis on ARINC 653 [
44,
46]. Zhao et al. [
46] propose a specification method with seven refinement layers, provide a comprehensive model, and then perform analysis using Rodin [
4]. Nevertheless, this article presents the first work that provides a comprehensive, stepwise refinement-based formalization for the multi-core ARINC 653.
The reasons we select Event-B to conduct formalization are as follows: First, the “guard-action” programming of Event-B is appropriate to represent the nested “if-else” frames of ARINC 653. Its structural grammar utilizes many nested “if-else” frames, each of which defines one or several service actions. We can naturally split a service into several atomic events in Event-B. Furthermore, the service actions after an “if-else” judge can be organized as one event, and the “if-else” judgment can be represented as a guard condition. Second, Rodin [
4], the IDE of Event-B, supports proof obligations to be automatically generated and provides excellent automatic proof function associated with set theory, which can markedly reduce our proof burden. Lastly, the inductive verification approach avoids the state-space explosion of automatic approaches (e.g., model checking) when verifying complex POSs. Therefore, Event-B has been successfully applied in some industry cases [
23,
28].
There are three main challenges in our work. First, a generic method needs to be developed for specifying process concurrencies on a multi-core platform using atomic Event-B events. Second, a specification method needs to be constructed that can model the comprehensive ARINC 653 system, including its various components, communication channels, and a suitable refinement structure for its different modules. Lastly, constructing a comprehensive and complicated model that conforms to the highly complicated ARINC 653 P1-4 standard, which uses over 110 pages of textual descriptions to describe 67 services, will require a significant amount of manpower.
This article presents the following technical contributions:
(1)
We propose a generic method to specify process concurrencies on multi-core platforms using atomic Event-B events. We define the core configuration, recognize racy [
30] processes that concurrently execute to compete for shared resources, split an ARINC 653 service into several sub-procedures, model each sub-procedure using an Event-B event, and give each event an execution location to denote the execution sequence in a service.
(2)
We present a comprehensive, fine-grained, refinement-based Event-B specification for the multi-core ARINC 653 P1-4, which contains seven refinement layers, 300 lines of context code, and more than 8,760 lines of model code in the last-layer machine. This specification covers the system functionality and all the 67 services. As far as we know, this specification is the most comprehensive and complicated formal specification in the literature.
1(3)
We formally verify a substantial number of safety properties that are specified as 157 invariants and 3,814 proof obligations. We verify that the critical errors disclosed in the single-core ARINC 653 P1-3 (presented in the literature [
44]) also exist in ARINC 653 P1-4.
The rest of this article is organized as follows: Section
2 introduces ARINC 653, Event-B, and RODIN. Section
3 proposes a specification method for concurrencies on a multi-core platform for the first challenge, Section
4 proposes the refinement structure and method for the second challenge, Section
5 introduces the Event-B specification of ARINC 653 P1-4 for the third challenge, Section
6 describes project evaluation, proof obligations, and verification results, and Section
7 introduces the current research status. Finally, Section
8 gives the conclusion and the future work. Additionally, we present a notation list and a operator list in Event-B in the appendix.
3 A Specification Method for Concurrency On Multiple CORES
3.1 Basic Elements
Services in ARINC 653 consist of components, attributes, constraints, and actions. These basic elements are represented in Event-B as sets, variables, constants, events, and invariants, respectively. The policy described in this section is similar to that for single-core ARINC 653 [
44,
46].
Components are specified as sets . The main components in ARINC 653 are processor cores, partitions, processes, communicating components (ports and channels for interpartition communications; buffer, blackboard, semaphore, event, and mutex for intrapartition communications), waiting queues, error handlers, and so on. Partitions, the health monitor (a set of configured tables, e.g., MultiPartitionHMTable, PartitionHMTable), and interpartition communication components (e.g., Port, Channel) are statically configured and initialized during the system/partition initialization phase. Processes and intrapartition communication components (e.g., buffer, semaphore, blackboard, event) are created at system compile time. All these components can be specified using sets in the Event-B context.
Attributes of components are specified as axioms . Attributes are restrictions on components, and they are specified as one or several axioms on their components. For example, partitions and cores in ARINC 653 are specified as sets PARTITIONS and CORES, respectively, each of which contains one or multiple partitions and cores. Meanwhile, there are some constraints on these two sets. For instance, the set PARTITIONS should be finite and contains partitions more than 1 and less than 256. This constraint is specified using two axioms: finite(PARTITIONS), and card(PARTITIONS) \(\gt\) 0 \(\wedge\) card(PARTITIONS) \(\lt\) 256.
Relations between components are specified as functions, which may be partial or total functions. For example, a process’s current execution state is specified as a partial function: process_state \(⇸\) PROCESS_STATES, in which the \(⇸\) indicates a partial function, and PROCESS_STATES is a set representing process execution states of processes. This partial function indicates that some processes may not have a current execution state because they have not been created yet when the containing partition is in initialization mode.
The relation that each partition has at most one error handler process is specified as a partial function errorhandler_ of_partition \(\in\) PARTITIONS \(⤔\) processes; the \(⤔\) is a partial injection relation.
Actions of components are specified as relations of Event-B sets. In Event-B, a relation is a set of ordered pairs representing a many-to-many mapping between sets. Actions of components describe the behaviors conducted on the sets of elements they operate on, and these actions can be specified as a set of relations.
For example, the action of sending a queuing message msg from a queuing port with a message capacity can be specified as a union operation on the set of messages in the port. This can be represented as msgs_of_queuports(port) \(:= {}\) msgs_of_queuports(port) \(\cup\) {msg \(\mapsto\) t}. Here, the natural number t is the length of the msg, and msgs_of_queuports is a function of type: msgs_of_queuingports \(\in {}\) queuing_ports \(\rightarrow {}\) (MESSAGES \(⇸ {}\) \(\mathbb {N}\) ), and queuing_ports is of type queuing_ports \(\in {}\) \(\mathbb {P}\) (QueuingPorts).
3.2 Core Configurations
The core configuration consists of the core assignment for partitions and the core affinity for processes.
Core assignment for partitions. In the ARINC 653 standard, a partition can be assigned a set of processor cores and is provided exclusive access to the cores assigned to this partition.
One or more processor cores can be assigned to a partition that represents an application and contains one or multiple services. This can be modeled in Event-B as an axiom axm_ assigned_cores_of_part:
Cores_of_Partition \(\in\) PARTITIONS \(\rightarrow \mathbb {P}_1\) (cores), where \(\mathbb {P}_1(cores)\) represents non-empty subsets of cores. Essentially, this specification indicates that each partition must own at least one processor core assigned to it. By assigning multiple cores to a partition, processes within the partition can execute concurrently on different cores. This allows for better utilization of resources and improved performance in multi-core systems.
This article complies with the ARINC 653 Part 1–4, which only considers the “use of multiple processes within a partition scheduled to execute concurrently on different processor cores.” However, ARINC 653 Part 1–4 does not provide the “definition of scheduling behaviors associated with multiple partitions scheduled to execute concurrently on different processor cores.” This means that there is only one currently executing partition at a moment and only processes within this currently executing partition can run concurrently on different processor cores assigned to this partition.
To achieve this characteristic, we introduce the variable currently executing partition represented by current_partition \(\in\) PARTITIONS. This variable ensures that only one partition is currently executing at a given time, and only processes within this partition can run concurrently on different processor cores assigned to the partition. In essence, the variable current_partition specifies that the current running partition has exclusive access to the cores assigned to it.
Core affinity for processes. A partition can contain multiple processes that run concurrently to provide application functions. When multiple processor cores are assigned to a partition, the partition should determine on which processor core each specific process can run. This is known as the Process Core Affinity (PCA) in the standard.
The assignment of cores to processes (PCA) is defined as processes_of_cores \(\in\) processes \(⇸\) CORES, where processes and CORES are sets of processes and processor cores, respectively. The variable current_processes_flag of type CORES \(\times\) BOOL determines if a core can execute a specific process.
The PCA identifies the processor core a process can run on. The PCA for a specific processor core is used to restrict processes to be eligible for scheduling only on their assigned processor cores. The cores assigned to processes should only be the ones assigned to the partition that the processes belong to. All the processes in a partition should run only on the cores assigned to their partition, which is constrained by an invariant inv_cores_imply_procandpart as follows. The processes_of_partition \(\in\) processes \(⇸\) PARTITIONS represents the partition to which a process belongs.
\(\forall proc \cdot\)
\(proc\in processes \wedge proc \in dom(processes\_of\_cores) \wedge proc \in dom(processes\_of\_partition\) \(\Rightarrow\)
\(processes\_of\_cores(proc) \in {Cores\_of\_Partition(processes\_of\_partition(proc)))}\) .
3.3 Execution Locations of Services
In order to specify the current execution phase, i.e., the location, of a service on a given processor core, we introduce the concept of the execution locations for services.
First, we divide a service into several sub-procedures and model each sub-procedure as an event. In the ARINC 653 standard, one service consists of several actions defined textually; each action is described with one clause or adjacent actions with several clauses organized by if-else or other structures. These can be specified as one Event-B event, each of which defines one or several actions described by sentences in natural language.
Second, we identify the execution location of each sub-procedure in a service. We need two parameters: the execution location of a sub-procedure on its assigned core and the execution status of a core for a service.
This execution location is used to denote which sub-procedure of a service is currently executing and defined in Event-B as location_of_service \(\in\) CORES \(⇸\) (Services \(\times\) Location). The Location is a set in Event-B that denotes the current execution location of the service. It is defined as partition (Location, {loc_i}, {loc_1}, {loc_2}, {loc_3} \(, \ldots ,\) {loc_r}), in which loc_i represents the initialization and loc_r represents the return action (i.e., the last action) of a service. The loc_1, loc_2, loc_3, and so on up to loc_n is a sequence of intermediate procedures of service, and the number n is determined by the number of sub-procedures.
The execution status is defined as finished_core \(\in\) CORES \(\rightarrow\) BOOL. It denotes whether a core is running a service. finished_core(core) = False indicates that the core is currently running a sub-procedure of a service, and it cannot run another service until the return event (identified by loc_r) of the current service is completed.
3.4 Criteria of the Specification Method
A general structure of a service in the APEX grammar is illustrated in the left part of Figure
1. The
error part describes error handling due to incorrect values of actual input parameters, which are robust handling. ARINC 653 uses textual language associated with diagrams to explain the service actions. These textual descriptions use compound statements “if-else” and “SEQUENCE” to describe complex structures, and simple actions are described in natural language.
Then, we propose a criterion of the specification method that derives from our previous work for the single-core ARINC 653 [
42,
44,
46].
First, an Event-B event is used to describe a single action in the ARINC 653 service. A description in an ARINC 653 service can serve as a single action. For example, in Table
2, the description “
set the process state to DORMANT” can be modeled by the Event-B event “
create_process_dormant,” illustrated in Table
4. Similarly, each of the other sentences in the ARINC 653 service can also be modeled by an event.
Second, the “guard-action” structure of Event-B is used to represent the nested “if-else” frames of the ARINC 653 services. The structural grammar of the ARINC 653 uses many nested “if-else” frames, each of which defines one or several service actions. For example, in Figure
1, only if the error conditions are negative and the judgments
\(\lnot\) cond_1 and
cond_10 hold will the action
act_10 be triggered. The triggering conditions of an event correspond to the “if-else” frames of the ARINC 653 services.
Third, a service is split into several atomic Event-B events with the above two policies. The service action after an “if-else” judgment can be organized as one event, and the “if-else” judgment can be represented as a guard condition.
Lastly, an APEX service with multiple actions is decomposed into a set of events with non-intersect guards.
For the essence of the “if-else” semantics, one “if-else” clause uses one location because only one guard will hold and only one action will be enabled. This policy is present in the example in Figure
1; we can see that the actions
act_10,
act_11, and
act_2 share one execution location
loc_0. Only one action (e.g.,
act_10,
act_11, or
act_2) can execute when its corresponding guards hold. So we can use an event to represent an action in an “if-else” clause. The parameters as service inputs are packed into the
any part. Conditions (
grd_0,
grd_1, etc.) in the error part indicate incorrect inputs of parameters, and the negations these conditions are represented as guards in Event-B.
3.5 Fine-grained Concurrency
The specification method in our work demonstrates the fine-grained concurrency.
First, services of a partition can execute
in parallel with each other across all processor cores if they do not compete for shared resources. This is made possible through the core assignment mechanism for partitions and the core affinity mechanism for processes, as described in Section
3.2. The core configuration allows a partition to be assigned a set of cores. Although there is only one currently executing partition at a moment, different processes within a partition can run in parallel on their respective assigned cores.
Second, racy [
30] services attempting to compete for shared resources execute
concurrently. Processes are considered racy if they attempt to access the same shared resources at the same time. Otherwise they are race-free. When a running service attempts to access an unavailable shared resource, this service will be suspended and the OS will invoke the scheduler to allow another eligible service to preempt execution. Once the shared resource becomes available, the suspended service can resume its execution.
For example, let us consider the service SEND_BUFFER. If the message buffer is full and the wait-time argument is not zero, a process (named
P0) that calls this service will be blocked. The OS will insert this process in the buffer’s process queue. Once another process receives and removes some messages from this buffer, thereby freeing up space, the blocked process
P0 will be resumed and execute the remaining actions. This mode is described in detail in Section
4.4. Another example is the service SUSPEND_SELF outlined in Table
1. In this case, the current process will be suspended until either the timeout value expires or another process resumes this suspended process. Subsequently, the OS asks for rescheduling to allow another eligible process to preempt execution.
Based on the execution semantics, services such as SEND_BUFFER, SUSPEND_SELF, and similar ones should be split into at least two parts at the point of scheduling: the first part implements the actions before scheduling, including the scheduling itself, while the second part implements the actions after scheduling, excluding the scheduling.
Lastly, in fact, we employ a more fine-grained approach to split and model all ARINC 653 services.
We utilize atomic Event-B events to describe individual statements or actions within an ARINC 653 service, as explained in Sections
3.3 and
3.4. For example, the SUSPEND_SELF shown in Table
1 is modeled by 5 combined Event-B events, while the SEND_BUFFER utilizes 10 combined Event-B events as illustrated in Figure
8 and described in Section
4.4.
3.6 An Example
We illustrate an example using the service CREATE_PROCESS in Table
2. In this example, we focus on the concurrency-related elements only and ignore the functionality of a service.
(1) Construct an initialization event for the first clause to identify the entry of a service with the suffix
_init and located at
loc_i. For example, the initialization event
create_process_init, illustrated in Table
3, serves two main purposes:
First, it checks whether the core can execute this service by verifying if
finished_core(core) = True holds (
3.grd006). If not, it means the core is currently running another service and cannot run this one.
Second, it sets the status of the core to indicate that this core is running this service. This consists of two actions. It first sets
finished_core(core
) (Table
3.act001) to False to denote that the core is running a service and can’t run other services. Then, it sets the execution location of the current service to
loc_i (Table
3.act002). The other actions (act003 and act004 in Table
3) are related to the functionality of the service CREATE_PROCESS.
(2) To model the intermediate actions of a service, we use an event and identify its execution location with a sequence number such as
loc_1,
loc_2,
loc_3, and so on. For example, the intermediate functional event
create_process_dormant in Table
4 models the second-line description “set the process state to DORMANT” in the example service in Table
2.
To handle these intermediate actions, we need to address two issues:
First, verify whether the core can execute this intermediate service by checking the
finished_core(core) and
location_of_service. Unlike an initialization event with
finished_core(core) = True, on the contrary, the
finished_core(core) for an intermediate event should be False (Table
4.grd004). Additionally, the execution location,
location_of_service, should indicate whether the core is running the previous event. For example, if the candidate event is the first intermediate event, the current location should be
loc_i (Table
4.grd005), and if the candidate event is the second intermediate event, the current location should be
loc_1.
Second, set the status of the core to indicate that it is running an intermediate event. In this example, we should set the execution location to
loc_1 (Table
4.act001) to indicate that the core is running the first intermediate event of the service.
(3) Model the return action of a service by an event whose name is suffixed with
_return and whose location is identified as
loc_r. For example, the return event
create_process_return illustrated in Table
5 corresponds to the fifth and sixth lines in the example service CREATE_PROCESS.
The return action needs to tackle two affairs: (1) it should check whether the core can run this return action, i.e., check that whether the
finished_core(core) is false (grd004) and whether
location_of_service denotes the last intermediate event (in this example, the last intermediate event’s location is
loc_2 in grd005), and (2) it needs to set the core’s status to a value denoting the completion of all actions of the current service and allows it to run other services. This needs to set the execution location to
loc_r (Table
5.act001).
3.7 Translating Methods from ARINC 653 to Event-B
We designed an algorithm to guide translations from APEX services to Event-B models as shown in Figure
2. We first give a simple abstract syntax for APEX service requirements in the above expression, where “
ACT act” is a simple action, and “
\(c; c\) ” is a sequential composition statement:
In this algorithm, the function
\(\mathrm{TRANSLATE\_STMT}\) is from our prior work [
44,
46].
The function CHECK_CORE_AFFINITY_STATUS is to check out the core affinity for processes and the running status of cores.
The function SET_CORE_STATUS is to set the core’s status at a corresponding value after executing a service.
An event is a tuple \(\langle \iota ,p,\sigma ,\alpha \rangle\) , in which \(\iota\) is the name of the event, p is a list of parameters, \(\sigma\) is a list of guards, and \(\alpha\) is a list of actions. We duplicate each event into \(evts^{\prime }\) for each “IF” statement and translate the “IF” conditions as guards.
The function \(\mathrm{TRANSLATE\_SERVICE}\) translates a service requirement spec into a final set of events in Event-B by invoking the above sub-functions. A service requirement can be abstracted into a tuple spec: \(\lt\) \(\zeta\) , P, E, S \(\gt\) , where \(\zeta\) is the service name, P is the input parameter list of this service, E represents error conditions in the error part, and S is a statement of the service’s normal action part. The \(\mathrm{TRANSLATE\_SERVICE}\) assigns the parameters of a service to the parameter list of each event and adds the negation of the conditions of the error part to the guard list in the Event-B machine.
4 A Refinement Structure
The ARINC 653 Part 1–4 standard uses more than 110 pages of textual descriptions to describe its 67 services; it should be formalized with refinement-based methods.
4.1 Refinement Structure for the Entire System
We first introduce the refinement structure in our work, illustrated in Figure
3 (which derives from [
44,
46]). This refinement structure is built according to the structure of the standard document.
In the standard, Section
1 and Sections
2.1,
2.2, and
3.1 provide an overview of ARINC 653 from different perspectives. Sections 2.3 and 2.4 describe the functionality of the ARINC 653 and its interface toward application software. Section
3 specifies service requests corresponding to the functionality. Sections 2.5 and
5 define the information and data format of the system configuration for integration and deployment.
The first machine M_Part_Trans specifies the partition operating modes, namely COLD_START, WARM_START, NORMAL, and IDLE. The abstract mode transitions are demonstrated in Section 2.3.1.4 and Figure 2.3.1.4 in the standard.
The second machine
M_PartProc_Trans models process states, namely
Dormant,
Ready,
Waiting,
Suspend,
WaitandSuspend,
Running, and
Faulted. Note that the states
Waiting,
Suspend, and
WaitandSuspend are refined from the state
Waiting in the standard. The reasonability of the introduction of these three states is described in Section
5.2.1. Moreover, this layer specifies state transitions under certain partition modes as described in Section 2.3.1.4.2.2 in the standard. For example, a process at state
Ready in a partition at
Normal mode can progress to
Dormant or
Suspend.
The third machine M_PartProc_withEvents introduces process periods and specifies period-related process actions. Meanwhile, this machine specifies start, stop, resume, and so forth, multiple services for processes. This layer mainly describes (i.e., refines) the abstract state transitions defined in the second layer. For example, the service stop_self in the third layer models only that this service will turn the current running process in a normal partition into dormant.
The fourth machine M_PartProc_Manage introduces process priorities along with the specific service (set_priority), a concrete two-level scheduling policy for partitions and processes, timing management services, and so on.
The fifth machine M_IPC_Conds introduces sampling and queueing modes for interpartition communications. Meanwhile, this layer introduces the blackboard and buffer for intrapartition communications.
The sixth machine M_IPC supplements semaphore, event, and Mutex for control-flow communications, which are used for process synchronization or process concurrencies. Furthermore, it introduces the refresh periodic for sampling ports, queuing discipline for interpartition communication ports and events and buffers.
The last machine M_HM introduces health monitoring services. Then, the refinement structure is completed.
4.2 Refinement Structure for Partition Managements
Partition management services affect not only partition modes but also processes, inter- and intrapartition communications, and other resources in the operated partition. Therefore, we regard the partition management as the core module and construct the partition module first.
The refinement of the partition module through the top three layers is illustrated in Figure
4. Once the third layer has been reached, we do not introduce new events for partition management services. We only refine existing partition elements gradually, such as partition time windows, partition core assignments, and processes in partitions.
The first layer uses an event partition_mode_transition to specify abstract partition mode transitions described on page 10 of ARINC 653 P1–4. In the second layer, nine events are introduced to describe concrete partition mode transitions with partition core assignments and process core affinities. The third layer introduces events to model the service SET_PARTITION_MODE with periodic and aperiodic processes. Subsequent layers refine the partition service gradually by adding processes, inter- and intrapartition communications, health monitoring, and other factors.
4.3 Refinement Structure for Process Managements
The second layer introduces the process. This layer consists of events that are used to model process scheduling and process state transitions. This layer also models the service SET_PRIORITY using the specification method for process concurrencies described in Section
3.3. The third layer models some other process management services, such as STOP, SUSPEND, RESUME, and so forth. All these services also refine the process-state-transition event. The third layer has not yet introduced the process priority, and we will introduce the service SET_PRIORITY in the fourth layer.
For example, the service SUSPEND, illustrated in Figure
5, is refined by four events. These four events model the entry of the service, setting the process to DORMANT, rescheduling the core, and returning the service, respectively. Another service START in Figure
5 is refined by 16 events.
This service takes into consideration the periodic and aperiodic processes started in a NORMAL partition or an initialization mode partition, set priority and release points, and so forth. In the subsequent layers, each layer will refine the process management services and introduce new elements for processes.
4.4 Refinement Structure for Inter- and Intrapartition Communications
Regardless of the type of communication (namely queueing or sampling ports, as well as buffers, blackboards, semaphores, mutexes, or events), there is a general characteristic for communication behaviors, the requesting-waiting-scheduling procedures for currently unavailable resources.
The general procedures for communications are based on the following scenarios:
(1)
The requested resource is available and no processes are waiting for this resource. When a process issues a request in this situation, such as the service SEND_BUFFER, the service will perform the sending action immediately and the message will be sent out successfully.
(2)
The requested resource is available but there exist processes waiting for this resource.
(3)
The current resource is unavailable. In this situation, the application will wait for this resource and request to reschedule.
Based on the communication characteristics, we can abstract communication actions as three types of events in the third layer:
(1)
The requested resource is currently available and then performs communication actions immediately, such as sending or receiving actions.
(2)
The requested resource is current busy, namely the event
req_busy_resource in Figure
6.
(3)
The requested resource becomes available after rescheduling (the event
resource_become_ available in Figure
7). In Figures
6 and
7, we list concrete event refinement of the abstract resource-requesting events
req_busy_resource and
resource_become_available.
The fifth layer refines each abstract event into concrete communication events, namely the queuing and sampling, blackboards, buffers, and mutexes.
We use the service SEND_BUFFER in Figure
8 as an example to describe the refinement process.
(1) In the first situation, where the requested resource is available and no processes are waiting for this resource, the service will perform the sending event send_buffer immediately.
(2) In the second situation, the requested action where the event is req_busy_resource will be refined by five sub-procedures:
•
The event 2.1 removes the first waiting process from the resource’s process queue.
•
The event 2.2 handles the timeout value of the waiting process.
•
The event 2.3 wakes up this process, removes this process from the WAITING list, and sets the process at READY, unless another process has suspended it.
•
The event 2.4 performs rescheduling.
•
The event 2.5 executes the remaining actions.
(3) In the third situation, the requested event
req_become_available will be divided into five sub-procedures: The event
3.1 initializes the context. The event
3.2 sets the timeout type, namely
timed-wait,
wait forever, or
no-wait. The event
3.3 sets the requesting process at WAITING and then inserts this requesting process into the requested port’s waiting list. The event
3.4 performs rescheduling. The event
3.5 resumes the suspended process and continues to perform its remaining actions. The refinement relations are illustrated in Figure
8.
We can see from Figures
6 and
7 that these refinement events lack two sets of events: the waking-up action of waiting processes that are blocked on blackboards or on events. The reason we don’t classify these two types of events into the above refinement structure in Figure
7 is that we should wake up all the processes waiting for blackboards or events, whereas we only wake up the first waiting process blocked on queueing ports, mutexes, or semaphores. Therefore, another abstract event
resource_become_available2 was designed to wake up all of the waiting processes. This abstract event is different from the abstract one
resource_become_available that wakes up only the first waiting processes, which is illustrated in Figure
9.
Then, we construct the refinement structure for all the inter- and intrapartition communication services.
5 A Comprehensive Event-B Specification
This section introduces the comprehensive Event-B specification.
5.1 Partition Managements
A partition is a program unit of an application designed to satisfy these partitioning constraints.
5.1.1 Partition Mode Transitions.
We first specify the abstract partition-mode transition paths in the top machine
M_Part_Trans, which is defined in the event
partition_mode_transition in Table
6.
We introduce process state transitions under certain partition modes in the second machine. However, these events about partitions are still abstract. In the third machine, we start to specify concrete partition management services associated with periodic/aperiodic processes.
The partition’s operating mode is the current execution state of a partition. There exist four types of partition modes:
IDLE,
NORMAL,
COLD_START, and
WARM_START. The partition mode transition paths specified in Table
6 correspond to Figure 2.3.1.4 in the ARINC 653 standard.
5.1.2 Partition Scheduling.
The scheduling of partitions in a module is strictly static and deterministic over time, which is a round-robin scheduling policy. The module generates the time-based scheduling based on the predefined module configuration. Each partition is then scheduled according to its respective partition time windows.
The static scheduling configuration is defined in the context
C_Part_Proc_Manage, as listed in Table
7. In this context, the constant
partition2num in the axiom
axm_partitionID: \(partition2num \in {} PARTITIONS ⤖ {} \mathbb {N}{}\) first assigns an ID to each partition using a total bijection. Then, the constant
part_sched_list in the axiom
axm_part_sched_list: \(part\_sched\_list \in {} \mathbb {N}{} \rightarrow {} (\mathbb {N}{} \times {} \mathbb {N}{})\) defines the static scheduling list. In the axiom
axm_part_sched_list, the first parameter with the nature type is the partition ID; the second and the third one are the offsets and durations of a partition, respectively. Axioms
axm_part_sched_list1 and
axm_part_sched_list2 present the constraints on the partition configuration; the former shows that each partition has an offset and a time capacity (duration), while the latter shows that each partition’s offset is more than or equal to the prior partition’s offset plus its duration. Axiom
axm_major_time_window_value presents the value of a module’s major frame time, which is the last partition’s offset plus its duration in the configuration list. Axioms
axm_periodicStartPoint and
axm_periodicStartPoint1 present the release point of periodic processes.
The action of the partition scheduling is specified by an event
partition_schedule, as listed in Table
8. In Table
8, the
grd002 represents that partitions in NORMAL mode and initialization mode (COLD_START or WARM_START) can be scheduled. The
Grd101 is a flag for partition scheduling. The
grd102 is the core algorithm that determines the current partition ID based on the current clock time, and the
act102 sets this partition ID as the current partition. The algorithm in the
Grd102 guarantees that the current clock time is within the voted partition’s time window; i.e., the value of the current clock time should be between the value of the current partition’s offset and the value of its offset plus its duration. The system should perform process scheduling following the partition scheduling. The action
act103 sets the process rescheduling flag to TRUE for the cores assigned to the new partition ID. Then the system will perform process scheduling in the event
partition_schedule().
5.1.3 Partition Services.
In Figure
4, we can see that the partition service SET_PARTITION_ MODE is specified as nine refined events. We present only the event
set_part_mode2Normal_ready as illustrated in Table
9. This event turns the partition to NORMAL mode, sets all previously started aperiodic processes to READY, and sets all previously delay-started aperiodic processes to WAITING. In this event,
stperprocs represents
started
periodic
processe
s,
dstperprocs represents
delayed
started
periodic
processe
s,
staperprocs represents
started
aperiodic
processe
s, and
dstaperprocs represents
delayed
started
aperiodic
processe
s.
5.2 Process Managements
5.2.1 Process State Transitions.
A partition consists of one or multiple processes that combine dynamically and execute concurrently to provide functions associated with that partition’s resources.
We first introduce the concept of processes in the second-layer machine
M_PartProc_Trans. This layer introduces process states, the allocation of processes to partitions, and the core configuration for partitions and processes. Additionally, we specify the service CREATE_PROCESS and define the key event
process_state_transition, as illustrated in Table
10. The key event specifies process state transition paths under certain partition modes.
The process state Waiting in ARINC 653 implies three situations. To distinguish between these three situations, we introduce three state modes to represent Waiting: Suspended, Waiting, and WaitandSuspend. These state modes are described as follows:
(1) If a running process is suspended by the service SUSPEND or SUSPEND_SELF, the state transition of this process is from running to suspended.
(2) If a running process requests a currently unavailable resource, such as events or mutexes occupied by other processes, the state transition is from running to waiting.
(3) If a process waiting for a resource is suspended by other processes, the state transition is from suspended to WaitandSuspend.
Based on the above analysis, the process states are defined as partition(PROCESS_STATES, PS_Dormant, PS_Ready, PS_Waiting, PS_suspended, PS_Waitandsuspended, PS_Running, PS_Faulted) in the context C_Part_Proc_Trans. Similar to the partition-mode-transition event partition_mode_transition at the top machine, the state transitions defined in the event process_state_transition are not concrete transition events or actions; they are only the possible state transition paths. The concrete services in the following machines, such as SET_PRIORITY, SUSPEND, RESUME, and STOP, will refine the concrete state transition paths.
5.2.2 Process Scheduling.
One of the most important activities of the OS is to arbitrate the competition within a partition when multiple processes of a partition concurrently issue requests of processor cores.
The event of process scheduling in the bottom machine is listed in Table
11. In Table
11, the
grd005 determines whether the operating
core is assigned to the partition
part, and the
grd006 determines whether the core has been assigned to the process
proc in the PCA. The
grd007 and the
grd008 confirm the current partition mode is NORMAL and the scheduled process is READY or RUNNING. The
grd009 examines whether the current processor
core has finished the executions of prior services. The
grd204 can ensure the scheduled process
proc is a process with the highest priority.
In the action part in Table
11, the
act002 and the
act003 assign the processor core
core to the process
proc and enable the process execution on this core. Meanwhile, the
act001 updates states for all processes in the partition.
5.2.3 Process Services.
Processes may be designed as periodic or aperiodic executions. The periodic types are introduced in the third machine M_Part_Proc_With_Events and defined as partition(PROC_PERIOD_TYPE, PERIOD_PROC, APERIOD_PROC). Three types of priority (current, base, and retained) are represented as variables basepriority_of_process, currentpriority_of_process, retainedpriority_of_process in the fourth machine, respectively.
We have listed an example of process service, CREATE_PROCESS, in Section
3.6. This service is divided into four subprocedures, and each procedure is specified as one event that models the entry, the intermediate, and the last action behaviors, respectively.
5.3 Inter- and Intrapartition Communications
ARINC 653 defines two types of communication paradigms: interpartition communications and intrapartition communications. Interpartition communications refer to communications between partitions, while intrapartition communications refer to communications between constituent processes within a partition.
Interpartition communications are composed of two modes: the queuing and sampling. The queuing involves storing messages in a queue until they are read by the receiving partition, while the sampling involves periodically checking for the availability of a new message.
Intrapartition communications can take place in several modes: blackboards, buffers, mutexes, events, and semaphores. Blackboards and buffers provide general interprocess message communications and synchronization, while semaphores, events, and mutexes provide only process synchronizations.
In ARINC 653, messages are atomic entities. We use the set MESSAGES in Ctr_IPC to represent the set of messages, and the variable used_messages to represent the set of already sent (used) messages. When sending messages in interpartition communications, the messages to be sent should be in the set MESSAGES, and they are stored in used_messages after being sent.
5.3.1 Interpartition Communications.
The significant difference between inter- and intrapartition communications is the channel mechanism for interpartition communications. A channel serves as a link between a source port and one or more destination ports, illustrated in Figure
10. Ports allow a partition to send or receive messages on a specific channel. We use two types of channel events,
transfer_sampling_msg and
transfer_queuing_msg, to model channel behaviors for sampling and queueing modes, respectively.
In sampling channels, “a message remains in the source port until it is transmitted by the channel or it is overwritten by a new occurrence of the message, whichever occurs first.” On the other hand, in queuing channels, “a message sent by the source partition is stored in the message queue of the source port until it is transmitted by the channel. When the message reaches the destination port, it is stored in a message queue until it is received by the destination partition.”
We use partition (PORTS, SamplingPorts, QueuingPorts) to represent interpartition communication ports, and Queuing_Channels \(\in\) Source_QueuingPorts \(⤖\) Dest_QueuingPorts to represent queuing channels. A port has a message space to store the message(s) to be sent/received. Sampling ports have a single message storage, which is specified as a variable msgspace_of_samplingports \(\in\) sampling_ports \(⇸\) (MESSAGES \(\times\) \(\mathbb {N}\) ). Queuing ports keep a message queue with a queuing length, which is specified as a variable queue_of_queuingports \(\in\) queuing_ports \(\rightarrow\) (MESSAGES \(\times\) \(\mathbb {N}\) ). The pair (MESSAGES \(\times\) \(\mathbb {N}_1\) ) represents a message sent/received at a specific time. For example, writing a sampling message msg on the port pt at time t is represented as pt \(\mapsto\) (msg \(\mapsto\) t) \(\in\) msgspace_of_samplingports. The queuing mode is queued in the FIFO or priority order. We use a variable processes_waitingfor_queuingports \(\in\) queuing_ports \(\rightarrow\) (processes \(⇸\) (MESSAGES \(\times\) \(\mathbb {N}\) )) to store waiting processes of a queuing port.
The event
send_queuing_message specifies the service
SEND_QUEUING_MESSAGE, which is illustrated in Table
12. This event involves sending a message
msg via a queuing port
port when the port is not full and no other processes are waiting for it. The guard
grd05 checks whether the current number of the queue port is less than the queue size. The guard
grd06 checks whether other processes are waiting for this port. The sending behavior is defined as
act01, and the message is set to
used state after being sent by the action
act02.
5.3.2 Intrapartition Communications.
Intrapartition communications provide provisions for processes within a partition to communicate with each other.
Buffers can store multiple messages in a message queue. When using buffers, no messages will be lost, and the sender will be blocked if the queue is full. The size of a message queue is specified as \(\forall\) buf.(buf \(\in\) buffers \(\wedge\) finite(queue_of_buffers(buf)) \(\Rightarrow\) card(queue_of_buffers(buf)) \(\le\) MaxMsgNum_of_Buffers(buf)).
No message queuing occurs in blackboards. Any message written to a blackboard remains invariant until the message is either cleared or overwritten. Processes waiting for blackboards are specified as processes_waitingfor_blackboards \(\in\) blackboards \(\rightarrow\) \(\mathbb {P}\) (processes).
Semaphores provide controlled accesses to partition resources, and they can be counted. Processes waiting for semaphores are specified as processes_waitingfor_semaphores \(\in\) semaphores \(\rightarrow\) (processes \(⇸\) \(\mathbb {N}\) ).
An event contains a bool-state variable, namely “up” and “down,” and a waiting-process queue. The bool-state variable of an event is specified as partition(EVENT_STATE, {EVENT_UP}, {EVENT_DOWN}), and the processes waiting for events are specified as processes_waitingfor_events \(\in\) events \(\rightarrow\) \(\mathbb {P}\) (processes).
A mutex is a synchronization object to control access to partition resources. Only one process at a time can own a mutex, which is specified as partition(MUTEX_STATE, MUTEX_AVAILABLE, MUTEX_OWNED). The process owning a mutex is specified as mutex_of_process \(\in\) mutexs \(⇸\) processes. A mutex contains a defined priority value that a process’s current priority is raised to when the mutex is obtained by this process. Processor cores that request mutexes are specified as acquire_mutex \(\in\) CORES \(⇸\) mutexs.
The event
display_blackboard_needwakeuprdprocs_insert in the fifth-layer machine, as illustrated in Table
13, specifies the action “
remove the processes from the blackboard’s process queue and move them from the WAITING to the READY state” in the
DISPLAY_BLACKBOARD service. This event means that if the partition is in NORMAL (
grd006), the processes
procs are waiting for the blackboard
bb (
grd012), and the service has executed the procedure 1 (
grd014), then the service will transfer the message
msg on the blackboard
bb (
act002, act003, act004), set the service execution location (
act001), and set the WAITING processes to READY and insert them into the READY queue (
act005).
5.4 Health Monitoring
Health monitoring (HM) functions respond to and report hardware, application, and/or OS software errors and failures. The HM mechanism is introduced in the last machine M_HM. The ARINC 653 OS will provide HM configuration tables and an application-level error handler process for each partition. The error handler is a special process in a partition with the highest priority but no process identifiers. It defines a variable shutdown \(\in\) BOOL to control a module and refines each event in the machine M_IPC by adding a guard shutdown = FALSE. This means that if a module is shut down, no event can be triggered. Each partition having at most one error handler process is specified as a partial function errorhandler_ of_partition \(\in\) PARTITIONS \(⤔\) processes.
The event
create_error_handler in Table
14 in the
M_HM specifies the action “
Create a special process” in the service
CREATE_ERROR_HANDLER. This event means that if the service has completed the initialization (grd004) and the partition state is in
COLD_START or
WARM_START (
grd009), the event will set the current process to
Dormant (
act002) and the location at
loc_1 (
act001).
5.5 Timing Managements
The event ticktock is used to represent the clock ticks. The variable clock_tick specifies the system time clock and the variable need_reschedule is a flag used to denote whether the system can trigger scheduling. Both clock_tick and need_reschedule will be used in process or partition events.
The timing management services provide a means to control periodic and aperiodic processes. At the end of each processing cycle, a periodic process can call the PERIODIC_WAIT service to get a new deadline. The TIMED_WAIT service allows the process to suspend itself for a minimum amount of elapsed time. After the wait time has elapsed, the process becomes available to be scheduled. These two services are first introduced as period_wait and time_wait in the third machine M_PartProc_With_Events.
We define a variable timeout_trigger \(\in\) processes \(⇸\) PROCESS_STATES \(\times\) \(\mathbb {N}_{1}\) to store the time counter for processes. The ordered pair (proc \(\mapsto\) (PS_Ready \(\mapsto\) t)) \(\in\) timeout_trigger means that the process proc is blocked and is waiting for some unavailable resource until time t, and then this process will be set to READY. When the system initiates a time counter with a duration t, a tuple (process_state \(\times\) t) will be inserted into the timeout trigger. When the time t comes, the event time_out triggers a blocked process to the state process_state.
7 Related Work
The seL4 [
24,
25,
2] is the first and most comprehensive OS kernel that uses formal methods to ensure safety and security properties of a high-performance L4-family microkernel [
20]. However, seL4 does not support multicore concurrency with fine-grained interleaving and locking. For this issue, von Tessin [
41] and Peters et al. [
32] argue that concurrent behaviors executing on multicores can be reduced to a minimum, so they believe a single
big-kernel-lock (BLK) might be good enough to achieve good performance on multicore platforms. Von Tessin further shows how to convert single-core seL4 proofs into proofs of a multicore OS kernel with a BKL framework.
Zhong Shao et al. present a compositional approach and build a clean-slate and certified concurrent OS kernel—CertikOS (Certified Kit Operating System) [
1,
17,
18,
19]. Their concurrency [
18] framework allows interleaved executions of kernel/user modules across different abstraction layers, and each such layer can have a different set of observable events and features [
19]. They introduce the hypervisor architecture of the CertikOS that leverages formal certification to ensure information leakage in cloud computing [
17]. CertiKOS can isolate applications from each other as well as provider-controlled resource management mechanisms.
Nevertheless, the well-known contributions of the above literatures focus on traditional OSs but not ARINC 653-compliant OSs.
Formal specification of the ARINC 653 architecture and components has been developed using
Circus language [
31],
Architecture Analysis and Design Language (AADL) [
14,
36,
43], and PROMELA of the SPIN [
22] model checker [
12]. However, they only specify a small part of services and analysis of few critical properties. Zhao et al. [
34,
45] specify and verify interpartition communication of ARINC 653 in sequential and concurrent settings, respectively. They also redevelop and automatically verify ARINC 653 models using Python [
35]. In [
44], the system functionalities and all service requirements of ARINC 653 have been formalized in Event-B, and some errors have been found in the standard.
About the validation of the Event-B model, Ait-Ameur et al. [
7] claim that the ontology is a good candidate for handling explicit domain knowledge, Mendil et al. [
27] present a framework that facilitates the formalization of standard concepts and rules as an ontology using Event-B consisting of data types and a collection of operators and properties. At-Ameur et al. propose [
6] to define transitions explicitly as partial functions in an Event-B theory. Zhao et al. [
34,
45] also propose an ontology methodology based on Event-B as an intermediate model between informal descriptions of ARINC 653 and the formal specification in Event-B. However, all the above contributions support only the single-core ARINC 653.