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