blob: 38de40de704b9cbd7d4fd075f7c79b430e9ebbec [file] [log] [blame] [edit]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
/* The following SPIN model attempts to represent the semantics of the
* seL4Notification connector code and its relevant correctness properties. It is not
* intended to be exhaustive, but is intended to give some confidence of the
* concurrency correctness of the connector's glue code. To execute:
*
* # Verify safety
* spin -a sel4notification.pml
* gcc -o pan-safety -O3 -DREACH -DSAFETY pan.c
* ./pan-safety | tee /dev/stderr | grep -q 'errors: 0'
*
* # Verify liveness
* spin -a sel4notification.pml
* gcc -o pan-liveness -O3 -DNP -DNOREDUCE pan.c
* ./pan-liveness -l -m100000 | tee /dev/stderr | grep -q 'errors: 0'
*/
#define NULL 0
/* The NOTIFICATION underlying the connection itself (`notification` in the template). */
chan connection = [1] of {bool};
/* Models of seL4 NOTIFICATION functions. */
inline NOTIFICATION_WAIT(notification) {
notification ? 1;
}
inline NOTIFICATION_NOTIFY(notification) {
atomic {
if
:: notification ? [1] -> skip;
:: !(notification ? [1]) -> notification ! 1;
fi;
}
}
/* Models of sync_sem_bare_*. We don't attempt to model the internals of the
* semaphore with any accuracy, and just assume we have some external evidence
* of the correctness of the libsel4sync-provided implementation.
*/
byte handoff = 0;
inline SEM_WAIT(result, sem) {
atomic {
if
:: sem > 0 ->
sem--;
result = 0;
/* We over approximate `wait` and assume it can fail at any time
* for no reason.
*/
:: result = 1;
fi;
}
}
inline SEM_TRYWAIT(result, sem) {
atomic {
if
:: sem > 0 ->
sem--;
result = 1;
:: else ->
result = 0;
fi;
}
}
inline SEM_POST(sem) {
sem++;
}
/* Model of a binary semaphore. Similarly, we don't attempt to accurately model
* its internals.
*/
int lock_count = 1;
inline lock() {
atomic {
lock_count == 1 ->
lock_count--;
}
}
inline unlock() {
assert(lock_count == 0);
lock_count++;
}
/* The slot for registered callbacks. */
bool callback = NULL;
/* A process representing the glue code thread. This code is intended to model
* the glue code execution as closely as possible.
*/
active [1] proctype inf_run() {
do
::
NOTIFICATION_WAIT(connection);
lock();
bool cb = callback;
callback = NULL;
if
:: cb == NULL ->
if
/* We cap the semaphore at 10, though the
* implementation caps it at `INT_MAX`. It is unlikely
* we mask any bugs with this under approximation, but
* we could increase this value to reduce the risk in
* future.
*/
:: handoff < 10 ->
SEM_POST(handoff);
:: else ->
skip;
fi;
:: else ->
skip;
fi;
/* A claim of the correctness of the connector code is that, at
* this point, no one else can be concurrently modifying the
* callback slot, so we ought to know it is empty.
*/
assert(callback == NULL);
unlock();
if
:: cb != NULL ->
/* Here is where we invoke the callback function. We don't
* model execution of the callback function.
*/
skip;
:: else ->
skip;
fi;
progress:
od;
}
/* The various API functions from the 'to' side of the connector follow. */
inline inf_poll() {
/* For the purposes of this model, we don't care whether the polled
* endpoint contained a message or not.
*/
bool ignored;
SEM_TRYWAIT(ignored, handoff);
}
inline inf_wait() {
/* Similarly, we don't care whether a `wait` failed or not. */
bool ignored;
SEM_WAIT(ignored, handoff);
}
inline inf_reg_callback() {
bool result;
SEM_TRYWAIT(result, handoff);
if
:: result -> goto done;
:: else -> skip;
fi;
lock();
SEM_TRYWAIT(result, handoff);
if
:: result ->
unlock();
/* At this point in the implementation, we invoke the callback
* function. We don't model the actual execution of the callback
* function here.
*/
:: !result && callback ->
unlock();
:: else ->
/* The following captures a desired correctness property of the
* connector code. Namely, that we are not overwriting an existing
* registered callback and there is no pending message.
*/
assert(callback == NULL && handoff == 0);
/* The value of the callback pointer itself is irrelevant, so we
* just use 1 to indicate that the slot is occupied.
*/
callback = 1;
unlock();
fi;
done:;
}
/* A process to represent the user making calls into glue code on the 'to'
* side. The model is processed in a reasonable time with 4 processes, but we
* may wish to increase this limit to be more thorough in future.
*/
active [4] proctype user() {
if
:: inf_poll();
:: inf_wait();
:: inf_reg_callback();
fi;
progress:
}
/* A process to represent the user making calls into glue code on the 'from'
* side. Note that we only ever need 1 process for this to capture all possible
* interleavings.
*/
active [1] proctype inf_emit() {
do
::
NOTIFICATION_NOTIFY(connection);
progress:
od;
}