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