blob: b65ecd1cb98c11bfd5411f7475011c5475f99668 [file] [log] [blame]
------------------------------ MODULE fpsimd ---------------------------------
EXTENDS Naturals, Sequences, FiniteSets, TLC
CONSTANTS CPUS, \* set of available CPUs
THREADS, \* set of threads
VALS \* set of register values
\* Generic helpers
Range(func) ==
{func[e] : e \in DOMAIN func}
(* --algorithm fpsimd {
variables
\* OS and hardware state
\* Select distinct init threads for each CPU
cpu \in {c \in [THREADS -> CPUS \cup {"none"}] :
/\ Cardinality(Range(c) \ {"none"}) = Cardinality(CPUS)
/\ \A t1, t2 \in THREADS :
t1 # t2 => c[t1] = "none" \/ c[t1] # c[t2]};
\* Remove the init threads from the runqueue
runqueue = THREADS \ {t \in THREADS : cpu[t] # "none"};
interrupts = [p \in CPUS |-> "off"];
in_interrupt = [t \in THREADS |-> FALSE];
mode = [t \in THREADS |-> "user"];
preempt_count = [t \in THREADS |-> 0];
\* Thread state
thread = [t \in THREADS |->
[fpsimd_state |-> "zero",
fpsimd_cpu |-> "none",
sve_state |-> << "zero", "zero" >>,
flags |-> {"TIF_FOREIGN_FPSTATE"}]];
\* FPSIMD state tracking
fpsimd_last_state = [p \in CPUS |-> "null"];
fpsimd_context_busy = [p \in CPUS |-> FALSE];
\* Hardware << FPSIMD, SVE >> vectors
hwfpsimd = [p \in CPUS |-> << "zero", "zero" >>];
sve_user_trap = [p \in CPUS |-> TRUE];
define {
\*
\* Type invariants
\*
ThreadFlags == {"TIF_FOREIGN_FPSTATE", "TIF_SVE"}
FPSIMDType == VALS \cup {"zero"}
SVEType == FPSIMDType \X FPSIMDType
ThreadStateType ==
[fpsimd_state: FPSIMDType,
fpsimd_cpu: CPUS \cup {"none"},
sve_state: SVEType,
flags: SUBSET ThreadFlags]
TypeInv ==
/\ thread \in [THREADS -> ThreadStateType]
/\ runqueue \subseteq THREADS
/\ interrupts \in [CPUS -> {"on", "off"}]
/\ in_interrupt \in [THREADS -> BOOLEAN]
/\ cpu \in [THREADS -> CPUS \cup {"none"}]
/\ preempt_count \in [THREADS -> Nat]
/\ mode \in [THREADS -> {"user", "kernel"}]
/\ hwfpsimd \in [CPUS -> SVEType]
/\ sve_user_trap \in [CPUS -> BOOLEAN]
/\ fpsimd_last_state \in [CPUS -> THREADS \cup {"null"}]
/\ fpsimd_context_busy \in [CPUS -> BOOLEAN]
\*
\* Scheduler invariants
\*
SchedInv ==
/\ Cardinality(Range(cpu) \ {"none"}) = Cardinality(CPUS)
/\ \A t1, t2 \in THREADS :
t1 # t2 => cpu[t1] = "none" \/ cpu[t1] # cpu[t2]
/\ \A t \in runqueue : cpu[t] = "none"
\* Symmetry optimisations
Perms == Permutations(CPUS) \cup Permutations(THREADS) \cup
Permutations(VALS)
}
\* Interrupts enabling/disabling
macro interrupts_enable() {
interrupts[cpu[self]] := "on";
}
macro interrupts_disable() {
interrupts[cpu[self]] := "off";
}
macro preempt_disable() {
preempt_count[self] := preempt_count[self] + 1;
}
macro preempt_enable() {
preempt_count[self] := preempt_count[self] - 1;
}
\* Thread flag manipulation
macro set_thread_flag(thr, flag) {
thread[thr].flags := thread[thr].flags \cup {flag};
}
macro clear_thread_flag(thr, flag) {
thread[thr].flags := thread[thr].flags \ {flag};
}
\* FPSIMD/SVE register reading/writing with the expected value
\* stored in the local variable "val"
macro write_fpsimd(v, val) {
val := << v, "zero" >>;
hwfpsimd[cpu[self]] := val;
}
macro read_fpsimd(val) {
assert hwfpsimd[cpu[self]][1] = val[1];
}
macro write_sve(vv, val) {
val := vv;
hwfpsimd[cpu[self]] := val;
}
macro read_sve(val) {
assert hwfpsimd[cpu[self]] = val;
}
\* Update current thread for the CPU
macro cpu_switch_to(next)
{
cpu[next] := cpu[self] ||
cpu[self] := "none";
}
macro sleep()
{
await cpu[self] # "none";
}
macro __get_cpu_fpsimd_context()
{
assert ~fpsimd_context_busy[cpu[self]];
fpsimd_context_busy[cpu[self]] := TRUE;
}
macro __put_cpu_fpsimd_context()
{
assert fpsimd_context_busy[cpu[self]];
fpsimd_context_busy[cpu[self]] := FALSE;
}
macro get_cpu_fpsimd_context()
{
preempt_disable();
__get_cpu_fpsimd_context();
}
macro put_cpu_fpsimd_context()
{
__put_cpu_fpsimd_context();
preempt_enable();
}
macro fpsimd_save_state() {
thread[self].fpsimd_state := hwfpsimd[cpu[self]][1];
}
macro fpsimd_load_state() {
hwfpsimd[cpu[self]] := << thread[self].fpsimd_state, "zero" >>;
}
macro sve_save_state() {
thread[self].sve_state := hwfpsimd[cpu[self]];
}
macro sve_load_state() {
hwfpsimd[cpu[self]] := thread[self].sve_state;
}
macro sve_user_enable() {
sve_user_trap[cpu[self]] := FALSE;
}
macro sve_user_disable() {
sve_user_trap[cpu[self]] := TRUE;
}
macro fpsimd_save() {
assert fpsimd_context_busy[cpu[self]];
if ("TIF_FOREIGN_FPSTATE" \notin thread[self].flags) {
if ("TIF_SVE" \in thread[self].flags)
sve_save_state();
else
fpsimd_save_state();
}
}
macro task_fpsimd_load() {
assert fpsimd_context_busy[cpu[self]];
if ("TIF_SVE" \in thread[self].flags)
sve_load_state();
else
fpsimd_load_state();
}
macro fpsimd_to_sve() {
thread[self].sve_state := << thread[self].fpsimd_state, "zero" >>;
}
macro fpsimd_bind_task_to_cpu() {
fpsimd_last_state[cpu[self]] := self;
thread[self].fpsimd_cpu := cpu[self];
if ("TIF_SVE" \in thread[self].flags)
sve_user_enable();
else
sve_user_disable();
}
macro fpsimd_flush_task_state() {
\* set_thread_flag() does not work with the || operator
thread[self].fpsimd_cpu := "none" ||
thread[self].flags := thread[self].flags \cup {"TIF_FOREIGN_FPSTATE"};
}
macro fpsimd_flush_cpu_state() {
fpsimd_last_state[cpu[self]] := "null";
set_thread_flag(self, "TIF_FOREIGN_FPSTATE");
}
macro sve_user_discard() {
clear_thread_flag(self, "TIF_SVE");
sve_user_disable();
}
procedure kernel_neon_begin()
{
knb1: get_cpu_fpsimd_context();
knb2: fpsimd_save();
knb3: fpsimd_flush_cpu_state();
return;
}
procedure kernel_neon_end()
{
kne1: put_cpu_fpsimd_context();
return;
}
procedure fpsimd_restore_current_state()
{
frcs1: get_cpu_fpsimd_context();
frcs2: if ("TIF_FOREIGN_FPSTATE" \in thread[self].flags) {
clear_thread_flag(self, "TIF_FOREIGN_FPSTATE");
frcs3: task_fpsimd_load();
fpsimd_bind_task_to_cpu();
};
frcs4: put_cpu_fpsimd_context();
return;
}
\* FPSIMD/SVE state switching
procedure fpsimd_thread_switch(next)
{
fts1: __get_cpu_fpsimd_context();
fpsimd_save();
fts2: \* wrong_task or wrong_cpu
if (fpsimd_last_state[cpu[self]] # next \/
thread[next].fpsimd_cpu # cpu[self])
set_thread_flag(next, "TIF_FOREIGN_FPSTATE");
else
clear_thread_flag(next, "TIF_FOREIGN_FPSTATE");
fts3: __put_cpu_fpsimd_context();
return;
}
procedure schedule()
variables next;
{
sch1: assert preempt_count[self] = 0;
assert interrupts[cpu[self]] = "off";
with (n \in runqueue \cup {"none"}) {
if (n = "none") {
\* Return if no new thread to schedule
interrupts_enable();
return;
} else {
\* Remove the next thread from the runqueue and schedule
runqueue := runqueue \ {n};
next := n;
};
};
sch2: call fpsimd_thread_switch(next);
sch3: \* Add the previous thread to the runqueue and switch to next
runqueue := runqueue \cup {self};
cpu_switch_to(next);
sch4: \* Context switching done, wait to be rescheduled
sleep();
sch5: interrupts_enable();
return;
}
procedure do_notify_resume()
{
dnr1: assert interrupts[cpu[self]] = "off";
call schedule();
dnr2: if ("TIF_FOREIGN_FPSTATE" \in thread[self].flags)
call fpsimd_restore_current_state();
dnr3: interrupts_disable();
\* repeat if more work needed
if ("TIF_FOREIGN_FPSTATE" \in thread[self].flags)
goto dnr1;
else
return;
}
\* Interrupt handler
procedure interrupt_handler()
{
handle_int:
interrupts_disable();
in_interrupt[self] := TRUE;
if (mode[self] = "user")
call do_notify_resume();
else if (preempt_count[self] = 0)
call schedule();
int1: in_interrupt[self] := FALSE;
interrupts_enable();
return;
}
procedure do_sve_acc()
{
dsa1: mode[self] := "kernel";
dsa2: get_cpu_fpsimd_context();
fpsimd_save();
dsa3: fpsimd_flush_task_state();
dsa4: fpsimd_to_sve();
dsa5: set_thread_flag(self, "TIF_SVE");
put_cpu_fpsimd_context();
\* ret_to_user
dsa6: interrupts_disable();
call do_notify_resume();
dsa7: mode[self] := "user";
interrupts_enable();
return;
}
procedure kernel_neon()
variable kval;
{
kn1: call kernel_neon_begin();
kn2: with (v \in VALS) write_fpsimd(v, kval);
kn3: read_fpsimd(kval);
call kernel_neon_end();
return;
}
procedure syscall()
{
sys_entry:
mode[self] := "kernel";
sve_user_discard();
\* exercise in-kernel FPSIMD
sys1: while (TRUE) {
either call kernel_neon();
or goto ret_to_user;
};
ret_to_user:
interrupts_disable();
call do_notify_resume();
sys2: mode[self] := "user";
interrupts_enable();
return;
}
\* User threads
process (thread \in THREADS)
variable val = << "zero", "zero" >>;
{
thr1: \* wait to be scheduled (simulate thread creation)
sleep();
\* FPSIMD initialisation (normally done by do_notify_resume())
call fpsimd_restore_current_state();
thr2: interrupts_enable();
thr3: \* user thread actions
while (TRUE) {
\* FPSIMD read/write
either { with (v \in VALS) write_fpsimd(v, val); }
or { read_fpsimd(val); }
\* SVE read/write/trap
or { when ~sve_user_trap[cpu[self]]; with (v \in VALS \X VALS) write_sve(v, val); }
or { when ~sve_user_trap[cpu[self]]; read_sve(val); }
or { when sve_user_trap[cpu[self]]; call do_sve_acc(); }
\* syscall; discard the SVE state
or { val[2] := "zero"; call syscall(); }
}
}
} *)
------------------------------------------------------------------------------
\* BEGIN TRANSLATION
\* Process thread at line 378 col 1 changed to thread_
\* Procedure variable next of procedure schedule at line 276 col 19 changed to next_
CONSTANT defaultInitValue
VARIABLES cpu, runqueue, interrupts, in_interrupt, mode, preempt_count,
thread, fpsimd_last_state, fpsimd_context_busy, hwfpsimd,
sve_user_trap, pc, stack
(* define statement *)
ThreadFlags == {"TIF_FOREIGN_FPSTATE", "TIF_SVE"}
FPSIMDType == VALS \cup {"zero"}
SVEType == FPSIMDType \X FPSIMDType
ThreadStateType ==
[fpsimd_state: FPSIMDType,
fpsimd_cpu: CPUS \cup {"none"},
sve_state: SVEType,
flags: SUBSET ThreadFlags]
TypeInv ==
/\ thread \in [THREADS -> ThreadStateType]
/\ runqueue \subseteq THREADS
/\ interrupts \in [CPUS -> {"on", "off"}]
/\ in_interrupt \in [THREADS -> BOOLEAN]
/\ cpu \in [THREADS -> CPUS \cup {"none"}]
/\ preempt_count \in [THREADS -> Nat]
/\ mode \in [THREADS -> {"user", "kernel"}]
/\ hwfpsimd \in [CPUS -> SVEType]
/\ sve_user_trap \in [CPUS -> BOOLEAN]
/\ fpsimd_last_state \in [CPUS -> THREADS \cup {"null"}]
/\ fpsimd_context_busy \in [CPUS -> BOOLEAN]
SchedInv ==
/\ Cardinality(Range(cpu) \ {"none"}) = Cardinality(CPUS)
/\ \A t1, t2 \in THREADS :
t1 # t2 => cpu[t1] = "none" \/ cpu[t1] # cpu[t2]
/\ \A t \in runqueue : cpu[t] = "none"
Perms == Permutations(CPUS) \cup Permutations(THREADS) \cup
Permutations(VALS)
VARIABLES next, next_, kval, val
global_vars == << sve_user_trap, hwfpsimd, mode, fpsimd_last_state, preempt_count, fpsimd_context_busy, thread, cpu, in_interrupt, interrupts, runqueue >>
local_vars == << next, val, next_, kval >>
vars == << global_vars, local_vars, pc, stack >>
ProcSet == (THREADS)
Init == (* Global variables *)
/\ cpu \in {c \in [THREADS -> CPUS \cup {"none"}] :
/\ Cardinality(Range(c) \ {"none"}) = Cardinality(CPUS)
/\ \A t1, t2 \in THREADS :
t1 # t2 => c[t1] = "none" \/ c[t1] # c[t2]}
/\ runqueue = THREADS \ {t \in THREADS : cpu[t] # "none"}
/\ interrupts = [p \in CPUS |-> "off"]
/\ in_interrupt = [t \in THREADS |-> FALSE]
/\ mode = [t \in THREADS |-> "user"]
/\ preempt_count = [t \in THREADS |-> 0]
/\ thread = [t \in THREADS |->
[fpsimd_state |-> "zero",
fpsimd_cpu |-> "none",
sve_state |-> << "zero", "zero" >>,
flags |-> {"TIF_FOREIGN_FPSTATE"}]]
/\ fpsimd_last_state = [p \in CPUS |-> "null"]
/\ fpsimd_context_busy = [p \in CPUS |-> FALSE]
/\ hwfpsimd = [p \in CPUS |-> << "zero", "zero" >>]
/\ sve_user_trap = [p \in CPUS |-> TRUE]
(* Procedure fpsimd_thread_switch *)
/\ next = [ self \in ProcSet |-> defaultInitValue]
(* Procedure schedule *)
/\ next_ = [ self \in ProcSet |-> defaultInitValue]
(* Procedure kernel_neon *)
/\ kval = [ self \in ProcSet |-> defaultInitValue]
(* Process thread_ *)
/\ val = [self \in THREADS |-> << "zero", "zero" >>]
/\ stack = [self \in ProcSet |-> << >>]
/\ pc = [self \in ProcSet |-> "thr1"]
knb1(self) == /\ pc[self] = "knb1"
/\ preempt_count' = [preempt_count EXCEPT ![self] = preempt_count[self] + 1]
/\ Assert(~fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 141, column 9 of macro called at line 236, column 9.")
/\ fpsimd_context_busy' = [fpsimd_context_busy EXCEPT ![cpu[self]] = TRUE]
/\ pc' = [pc EXCEPT ![self] = "knb2"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
thread, fpsimd_last_state, hwfpsimd,
sve_user_trap, stack, next, next_, kval, val >>
knb2(self) == /\ pc[self] = "knb2"
/\ Assert(fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 188, column 9 of macro called at line 237, column 9.")
/\ IF "TIF_FOREIGN_FPSTATE" \notin thread[self].flags
THEN /\ IF "TIF_SVE" \in thread[self].flags
THEN /\ thread' = [thread EXCEPT ![self].sve_state = hwfpsimd[cpu[self]]]
ELSE /\ thread' = [thread EXCEPT ![self].fpsimd_state = hwfpsimd[cpu[self]][1]]
ELSE /\ TRUE
/\ UNCHANGED thread
/\ pc' = [pc EXCEPT ![self] = "knb3"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
stack, next, next_, kval, val >>
knb3(self) == /\ pc[self] = "knb3"
/\ fpsimd_last_state' = [fpsimd_last_state EXCEPT ![cpu[self]] = "null"]
/\ thread' = [thread EXCEPT ![self].flags = thread[self].flags \cup {"TIF_FOREIGN_FPSTATE"}]
/\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, fpsimd_context_busy, hwfpsimd,
sve_user_trap, next, next_, kval, val >>
kernel_neon_begin(self) == knb1(self) \/ knb2(self) \/ knb3(self)
kne1(self) == /\ pc[self] = "kne1"
/\ Assert(fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 147, column 9 of macro called at line 244, column 9.")
/\ fpsimd_context_busy' = [fpsimd_context_busy EXCEPT ![cpu[self]] = FALSE]
/\ preempt_count' = [preempt_count EXCEPT ![self] = preempt_count[self] - 1]
/\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
thread, fpsimd_last_state, hwfpsimd,
sve_user_trap, next, next_, kval, val >>
kernel_neon_end(self) == kne1(self)
frcs1(self) == /\ pc[self] = "frcs1"
/\ preempt_count' = [preempt_count EXCEPT ![self] = preempt_count[self] + 1]
/\ Assert(~fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 141, column 9 of macro called at line 250, column 9.")
/\ fpsimd_context_busy' = [fpsimd_context_busy EXCEPT ![cpu[self]] = TRUE]
/\ pc' = [pc EXCEPT ![self] = "frcs2"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
thread, fpsimd_last_state, hwfpsimd,
sve_user_trap, stack, next, next_, kval, val >>
frcs2(self) == /\ pc[self] = "frcs2"
/\ IF "TIF_FOREIGN_FPSTATE" \in thread[self].flags
THEN /\ thread' = [thread EXCEPT ![self].flags = thread[self].flags \ {"TIF_FOREIGN_FPSTATE"}]
/\ pc' = [pc EXCEPT ![self] = "frcs3"]
ELSE /\ pc' = [pc EXCEPT ![self] = "frcs4"]
/\ UNCHANGED thread
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
stack, next, next_, kval, val >>
frcs3(self) == /\ pc[self] = "frcs3"
/\ Assert(fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 198, column 9 of macro called at line 253, column 17.")
/\ IF "TIF_SVE" \in thread[self].flags
THEN /\ hwfpsimd' = [hwfpsimd EXCEPT ![cpu[self]] = thread[self].sve_state]
ELSE /\ hwfpsimd' = [hwfpsimd EXCEPT ![cpu[self]] = << thread[self].fpsimd_state, "zero" >>]
/\ fpsimd_last_state' = [fpsimd_last_state EXCEPT ![cpu[self]] = self]
/\ thread' = [thread EXCEPT ![self].fpsimd_cpu = cpu[self]]
/\ IF "TIF_SVE" \in thread'[self].flags
THEN /\ sve_user_trap' = [sve_user_trap EXCEPT ![cpu[self]] = FALSE]
ELSE /\ sve_user_trap' = [sve_user_trap EXCEPT ![cpu[self]] = TRUE]
/\ pc' = [pc EXCEPT ![self] = "frcs4"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, fpsimd_context_busy, stack, next,
next_, kval, val >>
frcs4(self) == /\ pc[self] = "frcs4"
/\ Assert(fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 147, column 9 of macro called at line 256, column 9.")
/\ fpsimd_context_busy' = [fpsimd_context_busy EXCEPT ![cpu[self]] = FALSE]
/\ preempt_count' = [preempt_count EXCEPT ![self] = preempt_count[self] - 1]
/\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
thread, fpsimd_last_state, hwfpsimd,
sve_user_trap, next, next_, kval, val >>
fpsimd_restore_current_state(self) == frcs1(self) \/ frcs2(self)
\/ frcs3(self) \/ frcs4(self)
fts1(self) == /\ pc[self] = "fts1"
/\ Assert(~fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 141, column 9 of macro called at line 263, column 9.")
/\ fpsimd_context_busy' = [fpsimd_context_busy EXCEPT ![cpu[self]] = TRUE]
/\ Assert(fpsimd_context_busy'[cpu[self]],
"Failure of assertion at line 188, column 9 of macro called at line 264, column 9.")
/\ IF "TIF_FOREIGN_FPSTATE" \notin thread[self].flags
THEN /\ IF "TIF_SVE" \in thread[self].flags
THEN /\ thread' = [thread EXCEPT ![self].sve_state = hwfpsimd[cpu[self]]]
ELSE /\ thread' = [thread EXCEPT ![self].fpsimd_state = hwfpsimd[cpu[self]][1]]
ELSE /\ TRUE
/\ UNCHANGED thread
/\ pc' = [pc EXCEPT ![self] = "fts2"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, fpsimd_last_state, hwfpsimd,
sve_user_trap, stack, next, next_, kval, val >>
fts2(self) == /\ pc[self] = "fts2"
/\ IF fpsimd_last_state[cpu[self]] # next[self] \/
thread[next[self]].fpsimd_cpu # cpu[self]
THEN /\ thread' = [thread EXCEPT ![next[self]].flags = thread[next[self]].flags \cup {"TIF_FOREIGN_FPSTATE"}]
ELSE /\ thread' = [thread EXCEPT ![next[self]].flags = thread[next[self]].flags \ {"TIF_FOREIGN_FPSTATE"}]
/\ pc' = [pc EXCEPT ![self] = "fts3"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
stack, next, next_, kval, val >>
fts3(self) == /\ pc[self] = "fts3"
/\ Assert(fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 147, column 9 of macro called at line 271, column 9.")
/\ fpsimd_context_busy' = [fpsimd_context_busy EXCEPT ![cpu[self]] = FALSE]
/\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ next' = [next EXCEPT ![self] = Head(stack[self]).next]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
hwfpsimd, sve_user_trap, next_, kval, val >>
fpsimd_thread_switch(self) == fts1(self) \/ fts2(self) \/ fts3(self)
sch1(self) == /\ pc[self] = "sch1"
/\ Assert(preempt_count[self] = 0,
"Failure of assertion at line 278, column 9.")
/\ Assert(interrupts[cpu[self]] = "off",
"Failure of assertion at line 279, column 9.")
/\ \E n \in runqueue \cup {"none"}:
IF n = "none"
THEN /\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "on"]
/\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ next_' = [next_ EXCEPT ![self] = Head(stack[self]).next_]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED runqueue
ELSE /\ runqueue' = runqueue \ {n}
/\ next_' = [next_ EXCEPT ![self] = n]
/\ pc' = [pc EXCEPT ![self] = "sch2"]
/\ UNCHANGED << interrupts, stack >>
/\ UNCHANGED << cpu, in_interrupt, mode, preempt_count, thread,
fpsimd_last_state, fpsimd_context_busy, hwfpsimd,
sve_user_trap, next, kval, val >>
sch2(self) == /\ pc[self] = "sch2"
/\ /\ next' = [next EXCEPT ![self] = next_[self]]
/\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "fpsimd_thread_switch",
pc |-> "sch3",
next |-> next[self] ] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "fts1"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
next_, kval, val >>
sch3(self) == /\ pc[self] = "sch3"
/\ runqueue' = (runqueue \cup {self})
/\ cpu' = [cpu EXCEPT ![next_[self]] = cpu[self],
![self] = "none"]
/\ pc' = [pc EXCEPT ![self] = "sch4"]
/\ UNCHANGED << interrupts, in_interrupt, mode, preempt_count,
thread, fpsimd_last_state, fpsimd_context_busy,
hwfpsimd, sve_user_trap, stack, next, next_,
kval, val >>
sch4(self) == /\ pc[self] = "sch4"
/\ cpu[self] # "none"
/\ pc' = [pc EXCEPT ![self] = "sch5"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
stack, next, next_, kval, val >>
sch5(self) == /\ pc[self] = "sch5"
/\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "on"]
/\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ next_' = [next_ EXCEPT ![self] = Head(stack[self]).next_]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED << cpu, runqueue, in_interrupt, mode, preempt_count,
thread, fpsimd_last_state, fpsimd_context_busy,
hwfpsimd, sve_user_trap, next, kval, val >>
schedule(self) == sch1(self) \/ sch2(self) \/ sch3(self) \/ sch4(self)
\/ sch5(self)
dnr1(self) == /\ pc[self] = "dnr1"
/\ Assert(interrupts[cpu[self]] = "off",
"Failure of assertion at line 303, column 9.")
/\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "schedule",
pc |-> "dnr2",
next_ |-> next_[self] ] >>
\o stack[self]]
/\ next_' = [next_ EXCEPT ![self] = defaultInitValue]
/\ pc' = [pc EXCEPT ![self] = "sch1"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
next, kval, val >>
dnr2(self) == /\ pc[self] = "dnr2"
/\ IF "TIF_FOREIGN_FPSTATE" \in thread[self].flags
THEN /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "fpsimd_restore_current_state",
pc |-> "dnr3" ] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "frcs1"]
ELSE /\ pc' = [pc EXCEPT ![self] = "dnr3"]
/\ stack' = stack
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
next, next_, kval, val >>
dnr3(self) == /\ pc[self] = "dnr3"
/\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "off"]
/\ IF "TIF_FOREIGN_FPSTATE" \in thread[self].flags
THEN /\ pc' = [pc EXCEPT ![self] = "dnr1"]
/\ stack' = stack
ELSE /\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED << cpu, runqueue, in_interrupt, mode, preempt_count,
thread, fpsimd_last_state, fpsimd_context_busy,
hwfpsimd, sve_user_trap, next, next_, kval, val >>
do_notify_resume(self) == dnr1(self) \/ dnr2(self) \/ dnr3(self)
handle_int(self) == /\ pc[self] = "handle_int"
/\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "off"]
/\ in_interrupt' = [in_interrupt EXCEPT ![self] = TRUE]
/\ IF mode[self] = "user"
THEN /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "do_notify_resume",
pc |-> "int1" ] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "dnr1"]
/\ next_' = next_
ELSE /\ IF preempt_count[self] = 0
THEN /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "schedule",
pc |-> "int1",
next_ |-> next_[self] ] >>
\o stack[self]]
/\ next_' = [next_ EXCEPT ![self] = defaultInitValue]
/\ pc' = [pc EXCEPT ![self] = "sch1"]
ELSE /\ pc' = [pc EXCEPT ![self] = "int1"]
/\ UNCHANGED << stack, next_ >>
/\ UNCHANGED << cpu, runqueue, mode, preempt_count, thread,
fpsimd_last_state, fpsimd_context_busy,
hwfpsimd, sve_user_trap, next, kval, val >>
int1(self) == /\ pc[self] = "int1"
/\ in_interrupt' = [in_interrupt EXCEPT ![self] = FALSE]
/\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "on"]
/\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED << cpu, runqueue, mode, preempt_count, thread,
fpsimd_last_state, fpsimd_context_busy, hwfpsimd,
sve_user_trap, next, next_, kval, val >>
interrupt_handler(self) == handle_int(self) \/ int1(self)
dsa1(self) == /\ pc[self] = "dsa1"
/\ mode' = [mode EXCEPT ![self] = "kernel"]
/\ pc' = [pc EXCEPT ![self] = "dsa2"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
stack, next, next_, kval, val >>
dsa2(self) == /\ pc[self] = "dsa2"
/\ preempt_count' = [preempt_count EXCEPT ![self] = preempt_count[self] + 1]
/\ Assert(~fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 141, column 9 of macro called at line 334, column 9.")
/\ fpsimd_context_busy' = [fpsimd_context_busy EXCEPT ![cpu[self]] = TRUE]
/\ Assert(fpsimd_context_busy'[cpu[self]],
"Failure of assertion at line 188, column 9 of macro called at line 335, column 9.")
/\ IF "TIF_FOREIGN_FPSTATE" \notin thread[self].flags
THEN /\ IF "TIF_SVE" \in thread[self].flags
THEN /\ thread' = [thread EXCEPT ![self].sve_state = hwfpsimd[cpu[self]]]
ELSE /\ thread' = [thread EXCEPT ![self].fpsimd_state = hwfpsimd[cpu[self]][1]]
ELSE /\ TRUE
/\ UNCHANGED thread
/\ pc' = [pc EXCEPT ![self] = "dsa3"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
fpsimd_last_state, hwfpsimd, sve_user_trap,
stack, next, next_, kval, val >>
dsa3(self) == /\ pc[self] = "dsa3"
/\ thread' = [thread EXCEPT ![self].fpsimd_cpu = "none",
![self].flags = thread[self].flags \cup {"TIF_FOREIGN_FPSTATE"}]
/\ pc' = [pc EXCEPT ![self] = "dsa4"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
stack, next, next_, kval, val >>
dsa4(self) == /\ pc[self] = "dsa4"
/\ thread' = [thread EXCEPT ![self].sve_state = << thread[self].fpsimd_state, "zero" >>]
/\ pc' = [pc EXCEPT ![self] = "dsa5"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
stack, next, next_, kval, val >>
dsa5(self) == /\ pc[self] = "dsa5"
/\ thread' = [thread EXCEPT ![self].flags = thread[self].flags \cup {"TIF_SVE"}]
/\ Assert(fpsimd_context_busy[cpu[self]],
"Failure of assertion at line 147, column 9 of macro called at line 339, column 9.")
/\ fpsimd_context_busy' = [fpsimd_context_busy EXCEPT ![cpu[self]] = FALSE]
/\ preempt_count' = [preempt_count EXCEPT ![self] = preempt_count[self] - 1]
/\ pc' = [pc EXCEPT ![self] = "dsa6"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
fpsimd_last_state, hwfpsimd, sve_user_trap,
stack, next, next_, kval, val >>
dsa6(self) == /\ pc[self] = "dsa6"
/\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "off"]
/\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "do_notify_resume",
pc |-> "dsa7" ] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "dnr1"]
/\ UNCHANGED << cpu, runqueue, in_interrupt, mode, preempt_count,
thread, fpsimd_last_state, fpsimd_context_busy,
hwfpsimd, sve_user_trap, next, next_, kval, val >>
dsa7(self) == /\ pc[self] = "dsa7"
/\ mode' = [mode EXCEPT ![self] = "user"]
/\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "on"]
/\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED << cpu, runqueue, in_interrupt, preempt_count,
thread, fpsimd_last_state, fpsimd_context_busy,
hwfpsimd, sve_user_trap, next, next_, kval, val >>
do_sve_acc(self) == dsa1(self) \/ dsa2(self) \/ dsa3(self) \/ dsa4(self)
\/ dsa5(self) \/ dsa6(self) \/ dsa7(self)
kn1(self) == /\ pc[self] = "kn1"
/\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "kernel_neon_begin",
pc |-> "kn2" ] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "knb1"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
next, next_, kval, val >>
kn2(self) == /\ pc[self] = "kn2"
/\ \E v \in VALS:
/\ kval' = [kval EXCEPT ![self] = << v, "zero" >>]
/\ hwfpsimd' = [hwfpsimd EXCEPT ![cpu[self]] = kval'[self]]
/\ pc' = [pc EXCEPT ![self] = "kn3"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, sve_user_trap, stack, next,
next_, val >>
kn3(self) == /\ pc[self] = "kn3"
/\ Assert(hwfpsimd[cpu[self]][1] = kval[self][1],
"Failure of assertion at line 115, column 9 of macro called at line 354, column 9.")
/\ /\ kval' = [kval EXCEPT ![self] = Head(stack[self]).kval]
/\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "kernel_neon_end",
pc |-> Head(stack[self]).pc ] >>
\o Tail(stack[self])]
/\ pc' = [pc EXCEPT ![self] = "kne1"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
next, next_, val >>
kernel_neon(self) == kn1(self) \/ kn2(self) \/ kn3(self)
sys_entry(self) == /\ pc[self] = "sys_entry"
/\ mode' = [mode EXCEPT ![self] = "kernel"]
/\ thread' = [thread EXCEPT ![self].flags = thread[self].flags \ {"TIF_SVE"}]
/\ sve_user_trap' = [sve_user_trap EXCEPT ![cpu[self]] = TRUE]
/\ pc' = [pc EXCEPT ![self] = "sys1"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt,
preempt_count, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, stack, next,
next_, kval, val >>
sys1(self) == /\ pc[self] = "sys1"
/\ \/ /\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "kernel_neon",
pc |-> "sys1",
kval |-> kval[self] ] >>
\o stack[self]]
/\ kval' = [kval EXCEPT ![self] = defaultInitValue]
/\ pc' = [pc EXCEPT ![self] = "kn1"]
\/ /\ pc' = [pc EXCEPT ![self] = "ret_to_user"]
/\ UNCHANGED <<stack, kval>>
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
next, next_, val >>
ret_to_user(self) == /\ pc[self] = "ret_to_user"
/\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "off"]
/\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "do_notify_resume",
pc |-> "sys2" ] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "dnr1"]
/\ UNCHANGED << cpu, runqueue, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd,
sve_user_trap, next, next_, kval, val >>
sys2(self) == /\ pc[self] = "sys2"
/\ mode' = [mode EXCEPT ![self] = "user"]
/\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "on"]
/\ pc' = [pc EXCEPT ![self] = Head(stack[self]).pc]
/\ stack' = [stack EXCEPT ![self] = Tail(stack[self])]
/\ UNCHANGED << cpu, runqueue, in_interrupt, preempt_count,
thread, fpsimd_last_state, fpsimd_context_busy,
hwfpsimd, sve_user_trap, next, next_, kval, val >>
syscall(self) == sys_entry(self) \/ sys1(self) \/ ret_to_user(self)
\/ sys2(self)
thr1(self) == /\ pc[self] = "thr1"
/\ cpu[self] # "none"
/\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "fpsimd_restore_current_state",
pc |-> "thr2" ] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "frcs1"]
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, hwfpsimd, sve_user_trap,
next, next_, kval, val >>
thr2(self) == /\ pc[self] = "thr2"
/\ interrupts' = [interrupts EXCEPT ![cpu[self]] = "on"]
/\ pc' = [pc EXCEPT ![self] = "thr3"]
/\ UNCHANGED << cpu, runqueue, in_interrupt, mode, preempt_count,
thread, fpsimd_last_state, fpsimd_context_busy,
hwfpsimd, sve_user_trap, stack, next, next_,
kval, val >>
thr3(self) == /\ pc[self] = "thr3"
/\ \/ /\ \E v \in VALS:
/\ val' = [val EXCEPT ![self] = << v, "zero" >>]
/\ hwfpsimd' = [hwfpsimd EXCEPT ![cpu[self]] = val'[self]]
/\ pc' = [pc EXCEPT ![self] = "thr3"]
/\ stack' = stack
\/ /\ Assert(hwfpsimd[cpu[self]][1] = val[self][1],
"Failure of assertion at line 115, column 9 of macro called at line 390, column 27.")
/\ pc' = [pc EXCEPT ![self] = "thr3"]
/\ UNCHANGED <<hwfpsimd, stack, val>>
\/ /\ ~sve_user_trap[cpu[self]]
/\ \E v \in VALS \X VALS:
/\ val' = [val EXCEPT ![self] = v]
/\ hwfpsimd' = [hwfpsimd EXCEPT ![cpu[self]] = val'[self]]
/\ pc' = [pc EXCEPT ![self] = "thr3"]
/\ stack' = stack
\/ /\ ~sve_user_trap[cpu[self]]
/\ Assert(hwfpsimd[cpu[self]] = val[self],
"Failure of assertion at line 124, column 9 of macro called at line 393, column 59.")
/\ pc' = [pc EXCEPT ![self] = "thr3"]
/\ UNCHANGED <<hwfpsimd, stack, val>>
\/ /\ sve_user_trap[cpu[self]]
/\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "do_sve_acc",
pc |-> "thr3" ] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "dsa1"]
/\ UNCHANGED <<hwfpsimd, val>>
\/ /\ val' = [val EXCEPT ![self][2] = "zero"]
/\ stack' = [stack EXCEPT ![self] = << [ procedure |-> "syscall",
pc |-> "thr3" ] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "sys_entry"]
/\ UNCHANGED hwfpsimd
/\ UNCHANGED << cpu, runqueue, interrupts, in_interrupt, mode,
preempt_count, thread, fpsimd_last_state,
fpsimd_context_busy, sve_user_trap, next, next_,
kval >>
thread_(self) == thr1(self) \/ thr2(self) \/ thr3(self)
(* Allow infinite stuttering to prevent deadlock on termination. *)
Terminating == /\ \A self \in ProcSet: pc[self] = "Done"
/\ UNCHANGED vars
Next == (\E self \in ProcSet: \/ kernel_neon_begin(self)
\/ kernel_neon_end(self)
\/ fpsimd_restore_current_state(self)
\/ fpsimd_thread_switch(self)
\/ schedule(self) \/ do_notify_resume(self)
\/ interrupt_handler(self)
\/ do_sve_acc(self) \/ kernel_neon(self)
\/ syscall(self))
\/ (\E self \in THREADS: thread_(self))
\/ Terminating
Spec == Init /\ [][Next]_vars
Termination == <>(\A self \in ProcSet: pc[self] = "Done")
\* END TRANSLATION
------------------------------------------------------------------------------
\* Inject the interrupt_handler call into the call path
Interrupt(self) ==
\* Only interrupt running threads
/\ cpu[self] # "none"
/\ interrupts[cpu[self]] = "on"
\* Non-reentrant interrupt handler
/\ ~in_interrupt[self]
\* Save the PC of the interrupted context on the stack.
/\ stack' = [stack EXCEPT ![self] = << [procedure |-> "interrupt_handler",
pc |-> pc[self]] >>
\o stack[self]]
/\ pc' = [pc EXCEPT ![self] = "handle_int"]
/\ UNCHANGED << global_vars, local_vars >>
OSNext == (\E self \in THREADS : Interrupt(self)) \/ Next
OSSpec == Init /\ [][OSNext]_vars
==============================================================================