1+ #ifndef PORT_CONTRACTS_H
2+ #define PORT_CONTRACTS_H
3+
4+ /* This file defines function contracts for the macros used to invoke
5+ * synchronization mechanisms, e.g., masking interrupts and acquiring locks.
6+ * The definitions of these macros are port-specific and involve inline
7+ * assembly. VeriFast cannot reason about assembly. Hence, we have to
8+ * abstract the assembly's semantics with these contracts.
9+ *
10+ * Note that we cannot verify that the contracts' correctness. We have to treat
11+ * their correctness as a proof assumption.
12+ *
13+ * Moreover, together with the invariants defined in the proof header
14+ * `lock_predicates.h`, the below contracts define the locking discipline that
15+ * our proof relies on. The file `lock_predicates.h` contains a more detailed
16+ * explanation of the locking discipline.
17+ *
18+ * In short:
19+ * - Data that is only meant to be accessed by the a specific core is protected
20+ * by deactivating interrupts on this core. Access permissions are expressed
21+ * by `coreLocalInterruptInv_p`.
22+ * - The task lock and the ISR lock (i.e. interrupt lock) themselves protect
23+ * data and code regions irrelevant to the switch-context proof. Hence,
24+ * the respective invariants are left abstract, cf. `taskLockInv_p` and
25+ * `isrLockInv_p`.
26+ * - FreeRTOS' locking discipline demands that the task lock is acquired before
27+ * and released after the ISR lock. The contracts defined below ensure that
28+ * we follow this locking discipline.
29+ * - The ready lists and the task run states (i.e. the data most important to
30+ * the context-switch proof) is protected by a combination of the task lock
31+ * and the ISR lock. That is, this data must only be accessed when both
32+ * locks have been acquired in the right order. The invariant
33+ * `taskISRLockInv_p` expresses these access rights. `lock_predicates.h`
34+ * defines lemmas to produce and consume this invariant. The lemmas ensure
35+ * that we only produce the invariant when both locks have been acquired in
36+ * the right order.
37+ */
38+
39+ // We want our proofs to hold for an arbitrary number of cores.
40+ #undef portGET_CORE_ID
41+ #define portGET_CORE_ID () VF__get_core_num()
42+
43+ /* FreeRTOS core id is always zero based.*/
44+ static uint VF__get_core_num (void );
45+ //@ requires true;
46+ /*@ ensures 0 <= result &*& result < configNUM_CORES &*&
47+ result == coreID_f();
48+ @*/
49+
50+ /*@
51+ // This contant allows proofs to talk about the ID of the core that the
52+ // function we verify is running on. The verified function's contract must
53+ // ensure that this constant holds the value of the current core.
54+ fixpoint uint coreID_f();
55+
56+ lemma void coreID_f_range();
57+ requires true;
58+ ensures 0 <= coreID_f() &*& coreID_f() < configNUM_CORES;
59+ @*/
60+
61+
62+
63+
64+ /* In FreeRTOS interrupts are masked to protect core-local data.
65+ * The invariant `coreLocalInterruptInv_p` expresses what data the masking
66+ * of interrupts protects on a specific core, cf., `lock_predicates.h`.
67+ *
68+ * Deactivating the interrupts on the current core produces the invariant
69+ * `coreLocalInterruptInv_p()` and thereby gives us the permission to access
70+ * the protected data.
71+ */
72+ #undef portDISABLE_INTERRUPTS
73+ #define portDISABLE_INTERRUPTS VF__portDISABLE_INTERRUPTS
74+ uint32_t VF__portDISABLE_INTERRUPTS ();
75+ //@ requires interruptState_p(?coreID, ?state);
76+ /*@ ensures result == state &*&
77+ interruptState_p(coreID, ?newState) &*&
78+ interruptsDisabled_f(newState) == true &*&
79+ interruptsDisabled_f(state) == true
80+ ? newState == state
81+ : coreLocalInterruptInv_p();
82+ @*/
83+
84+
85+ /* This macro is used to restore the interrupt state (activated or deactivated)
86+ * to a specific value. When an invokation sets the state from deactivated to
87+ * activated, the invariant `coreLocalInterruptInv_p()` is consumed.
88+ * Thereby, we lose the permission to access the core-local data protected
89+ * by the deactivation of interrupts on this core.
90+ */
91+ #undef portRESTORE_INTERRUPTS
92+ #define portRESTORE_INTERRUPTS (ulState ) VF__portRESTORE_INTERRUPTS(ulState)
93+ void VF__portRESTORE_INTERRUPTS (uint32_t ulState );
94+ /*@ requires interruptState_p(?coreID, ?tmpState) &*&
95+ (interruptsDisabled_f(tmpState) == true && interruptsDisabled_f(ulState) == false)
96+ ? coreLocalInterruptInv_p()
97+ : true;
98+ @*/
99+ /*@ ensures interruptState_p(coreID, ulState);
100+ @*/
101+
102+
103+ /* This macro is used to acquire the task lock. The task lock on its own
104+ * protects data and core regions that are not relevant to the context-switch
105+ * proof. Hence, an invocation produces an abstract invariant `taskLockInv_p()`
106+ * and updates the locking history `locked_p(...)` to log that the task log
107+ * has been acquired.
108+ *
109+ * FreeRTOS' locking discipline requires that the task lock must be acquired
110+ * before the ISR lock. The precondition `locked_p(nil)` only allows
111+ * invocations of this macro when no lock has been acquired, yet.
112+ */
113+ #undef portGET_TASK_LOCK
114+ #define portGET_TASK_LOCK VF__portGET_TASK_LOCK
115+ void VF__portGET_TASK_LOCK ();
116+ //@ requires [?f]taskLock_p() &*& locked_p(nil);
117+ //@ ensures taskLockInv_p() &*& locked_p( cons( pair(f, taskLockID_f()), nil) );
118+
119+
120+ /* This macro is used to release the task lock. An invocation consumes the
121+ * task lock invariant `taskLockInv_p` and updates the locking history
122+ * `locked_p(...)` to reflect the release.
123+ *
124+ * FreeRTOS' locking discipline demands that the task lock must be acquired
125+ * before and released after the ISR lock. The precondition
126+ * `locked_p( cons( pair(?f, taskLockID_f()), nil) )` only allows calls to this
127+ * macro when we can prove that we only hold the task lock.
128+ * */
129+ #undef portRELEASE_TASK_LOCK
130+ #define portRELEASE_TASK_LOCK VF__portRELEASE_TASK_LOCK
131+ void VF__portRELEASE_TASK_LOCK ();
132+ //@ requires taskLockInv_p() &*& locked_p( cons( pair(?f, taskLockID_f()), nil) );
133+ //@ ensures [f]taskLock_p() &*& locked_p(nil);
134+
135+
136+ /* This macro is used to acquire the ISR lock (i.e. interrupt lock). An
137+ * invocation produces the abstract ISR lock invariant `isrLock_p` and
138+ * updates the locking history `locked_p(...)` to reflect that the lock has
139+ * been acquired.
140+ */
141+ #undef portGET_ISR_LOCK
142+ #define portGET_ISR_LOCK VF__portGET_ISR_LOCK
143+ void VF__portGET_ISR_LOCK ();
144+ //@ requires [?f]isrLock_p() &*& locked_p(?heldLocks);
145+ //@ ensures isrLockInv_p() &*& locked_p( cons( pair(f, isrLockID_f()), heldLocks) );
146+
147+
148+ /* This macro is used to release the ISR lock (i.e. interrupt lock). A call
149+ * consumes the ISR lock invariant and updates the locking history
150+ * `locked_p(...)` to reflect the release.
151+ */
152+ #undef portRELEASE_ISR_LOCK
153+ #define portRELEASE_ISR_LOCK VF__portRELEASE_ISR_LOCK
154+ void VF__portRELEASE_ISR_LOCK ();
155+ //@ requires isrLockInv_p() &*& locked_p( cons( pair(?f, isrLockID_f()), ?heldLocks) );
156+ //@ ensures [f]isrLock_p() &*& locked_p(heldLocks);
157+
158+
159+ #endif /* PORT_CONTRACTS_H */
0 commit comments