208 lines
9.0 KiB
Plaintext
208 lines
9.0 KiB
Plaintext
(*
|
|
* Copyright 2014, General Dynamics C4 Systems
|
|
* Copyright 2022, Proofcraft Pty Ltd
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*)
|
|
|
|
chapter \<open>VCPU\<close>
|
|
|
|
theory VCPU_A
|
|
imports
|
|
TcbAcc_A
|
|
InvocationLabels_A
|
|
begin
|
|
|
|
text \<open>This is used by some decode functions. VCPU decode functions are the first that need to bounds
|
|
check IRQs from the user.\<close>
|
|
definition arch_check_irq :: "data \<Rightarrow> (unit,'z::state_ext) se_monad" where
|
|
"arch_check_irq irq \<equiv> whenE (irq > ucast maxIRQ \<or> irq < ucast minIRQ)
|
|
$ throwError (RangeError (ucast minIRQ) (ucast maxIRQ))"
|
|
|
|
context Arch begin global_naming AARCH64_A
|
|
|
|
section "VCPU"
|
|
|
|
subsection "VCPU: Set TCB"
|
|
|
|
definition decode_vcpu_set_tcb ::
|
|
"arch_cap \<Rightarrow> (cap \<times> cslot_ptr) list \<Rightarrow> (arch_invocation,'z::state_ext) se_monad"
|
|
where
|
|
"decode_vcpu_set_tcb cap extras \<equiv> case (cap, extras) of
|
|
(VCPUCap v, fs#_) \<Rightarrow> (case fs of
|
|
(ThreadCap t, _) \<Rightarrow> returnOk $ InvokeVCPU $ VCPUSetTCB v t
|
|
| _ \<Rightarrow> throwError IllegalOperation)
|
|
| (VCPUCap v, _) \<Rightarrow> throwError TruncatedMessage
|
|
| _ \<Rightarrow> throwError IllegalOperation"
|
|
|
|
text \<open>VCPU objects can be associated with and dissociated from TCBs.
|
|
It is not possible to dissociate a VCPU and a TCB by using setTCB.
|
|
Final outcome has to be an associated TCB and VCPU.
|
|
The only way to get lasting dissociation is to delete the TCB or the VCPU. See ArchVSpace\_A.\<close>
|
|
|
|
|
|
subsection "VCPU: Read/Write Registers"
|
|
|
|
definition read_vcpu_register :: "obj_ref \<Rightarrow> vcpureg \<Rightarrow> (machine_word,'z::state_ext) s_monad" where
|
|
"read_vcpu_register vcpu_ptr reg \<equiv> do
|
|
cur_vcpu \<leftarrow> gets (arm_current_vcpu \<circ> arch_state);
|
|
(on_cur_vcpu, active) \<leftarrow> return (case cur_vcpu of
|
|
Some (vcpu_ptr', a) \<Rightarrow> (vcpu_ptr' = vcpu_ptr, a)
|
|
| _ \<Rightarrow> (False, False));
|
|
|
|
if on_cur_vcpu
|
|
then if vcpuRegSavedWhenDisabled reg \<and> \<not>active
|
|
then vcpu_read_reg vcpu_ptr reg
|
|
else do_machine_op $ readVCPUHardwareReg reg
|
|
else vcpu_read_reg vcpu_ptr reg
|
|
od"
|
|
|
|
definition
|
|
write_vcpu_register :: "obj_ref \<Rightarrow> vcpureg \<Rightarrow> machine_word \<Rightarrow> (unit,'z::state_ext) s_monad"
|
|
where
|
|
"write_vcpu_register vcpu_ptr reg val \<equiv>
|
|
do
|
|
cur_vcpu \<leftarrow> gets (arm_current_vcpu o arch_state);
|
|
(on_cur_vcpu, active) \<leftarrow> return (case cur_vcpu of
|
|
Some (cv, a) \<Rightarrow> (cv = vcpu_ptr, a)
|
|
| _ \<Rightarrow> (False, False));
|
|
|
|
if on_cur_vcpu
|
|
then if vcpuRegSavedWhenDisabled reg \<and> \<not>active
|
|
then vcpu_write_reg vcpu_ptr reg val
|
|
else do_machine_op $ writeVCPUHardwareReg reg val
|
|
else vcpu_write_reg vcpu_ptr reg val
|
|
od"
|
|
|
|
definition decode_vcpu_read_register ::
|
|
"machine_word list \<Rightarrow> arch_cap \<Rightarrow> (arch_invocation,'z::state_ext) se_monad"
|
|
where
|
|
"decode_vcpu_read_register args cap \<equiv> case (args, cap) of
|
|
(reg#_, VCPUCap p) \<Rightarrow> if fromEnum (maxBound::vcpureg) < unat reg
|
|
then throwError (InvalidArgument 1)
|
|
else returnOk $ InvokeVCPU $ VCPUReadRegister p $ toEnum (unat reg)
|
|
| (_, _) \<Rightarrow> throwError TruncatedMessage"
|
|
|
|
definition decode_vcpu_write_register :: "machine_word list \<Rightarrow> arch_cap \<Rightarrow> (arch_invocation,'z::state_ext) se_monad"
|
|
where
|
|
"decode_vcpu_write_register args cap \<equiv> case (args, cap) of
|
|
(reg#val#_, VCPUCap p) \<Rightarrow> if fromEnum (maxBound::vcpureg) < unat reg
|
|
then throwError (InvalidArgument 1)
|
|
else returnOk $ InvokeVCPU $ VCPUWriteRegister p (toEnum (unat reg)) val
|
|
| (_, _) \<Rightarrow> throwError TruncatedMessage"
|
|
|
|
definition invoke_vcpu_read_register :: "obj_ref \<Rightarrow> vcpureg \<Rightarrow> (data list, 'z::state_ext) s_monad"
|
|
where
|
|
"invoke_vcpu_read_register v reg \<equiv> do
|
|
val \<leftarrow> read_vcpu_register v reg;
|
|
return [val]
|
|
od"
|
|
|
|
definition
|
|
invoke_vcpu_write_register :: "obj_ref \<Rightarrow> vcpureg \<Rightarrow> machine_word \<Rightarrow> (unit,'z::state_ext) s_monad"
|
|
where
|
|
"invoke_vcpu_write_register v reg val \<equiv> write_vcpu_register v reg val"
|
|
|
|
text \<open>VCPU : inject IRQ\<close>
|
|
|
|
(* This following function does not correspond to exactly what the C does, but
|
|
it is the value that is stored inside of lr in the vgic *)
|
|
definition make_virq :: "obj_ref \<Rightarrow> obj_ref \<Rightarrow> obj_ref \<Rightarrow> virq" where
|
|
"make_virq grp prio irq \<equiv>
|
|
let
|
|
groupShift = 30;
|
|
prioShift = 23;
|
|
irqPending = 1 << 28;
|
|
eoiirqen = 1 << 19
|
|
in ((grp && 1) << groupShift) || ((prio && 0x1F) << prioShift) || (irq && 0x3FF)
|
|
|| irqPending || eoiirqen"
|
|
|
|
definition virq_type :: "virq \<Rightarrow> nat" where
|
|
"virq_type virq \<equiv> unat ((virq >> 28) && 3)"
|
|
|
|
definition is_virq_active :: "virq \<Rightarrow> bool" where
|
|
"is_virq_active virq \<equiv> virq_type virq = 2"
|
|
|
|
definition decode_vcpu_inject_irq ::
|
|
"obj_ref list \<Rightarrow> arch_cap \<Rightarrow> (arch_invocation,'z::state_ext) se_monad"
|
|
where
|
|
"decode_vcpu_inject_irq ptrs cap \<equiv> case (ptrs, cap) of
|
|
(mr0 # _, VCPUCap p) \<Rightarrow> doE
|
|
vid \<leftarrow> returnOk (mr0 && 0xFFFF);
|
|
priority \<leftarrow> returnOk ((mr0 >> 16) && 0xFF);
|
|
group \<leftarrow> returnOk ((mr0 >> 24) && 0xFF);
|
|
index \<leftarrow> returnOk ((mr0 >> 32) && 0xFF);
|
|
range_check vid 0 ((1 << 10) - 1);
|
|
range_check priority 0 31;
|
|
range_check group 0 1;
|
|
num_list_regs \<leftarrow> liftE $ gets (arm_gicvcpu_numlistregs \<circ> arch_state);
|
|
whenE (index \<ge> of_nat num_list_regs) $
|
|
(throwError $ RangeError 0 (of_nat num_list_regs - 1));
|
|
|
|
vcpu \<leftarrow> liftE $ get_vcpu p;
|
|
vcpuLR \<leftarrow> returnOk (vgic_lr $ vcpu_vgic $ vcpu);
|
|
|
|
whenE (is_virq_active (vcpuLR (unat index))) $ throwError DeleteFirst;
|
|
|
|
virq \<leftarrow> returnOk (make_virq group priority vid);
|
|
returnOk $ InvokeVCPU $ VCPUInjectIRQ p (unat index) virq
|
|
odE
|
|
| _ \<Rightarrow> throwError TruncatedMessage"
|
|
|
|
definition invoke_vcpu_inject_irq :: "obj_ref \<Rightarrow> nat \<Rightarrow> virq \<Rightarrow> (unit,'z::state_ext) s_monad" where
|
|
"invoke_vcpu_inject_irq vr index virq \<equiv> do
|
|
cur_v \<leftarrow> gets (arm_current_vcpu \<circ> arch_state);
|
|
if (cur_v \<noteq> None \<and> fst (the cur_v) = vr)
|
|
then do_machine_op $ set_gic_vcpu_ctrl_lr (of_nat index) virq
|
|
else vgic_update_lr vr index virq
|
|
od"
|
|
|
|
text \<open>VCPU : acknowledge VPPI\<close>
|
|
|
|
definition decode_vcpu_ack_vppi ::
|
|
"obj_ref list \<Rightarrow> arch_cap \<Rightarrow> (arch_invocation,'z::state_ext) se_monad"
|
|
where
|
|
"decode_vcpu_ack_vppi mrs cap \<equiv>
|
|
case (mrs, cap)
|
|
of (mr0 # _, VCPUCap vcpu_ptr) \<Rightarrow> doE
|
|
arch_check_irq mr0;
|
|
(case irq_vppi_event_index (ucast mr0)
|
|
of None \<Rightarrow> throwError $ InvalidArgument 0
|
|
| Some vppi \<Rightarrow> returnOk $ InvokeVCPU $ VCPUAckVPPI vcpu_ptr vppi)
|
|
odE
|
|
| _ \<Rightarrow> throwError TruncatedMessage"
|
|
|
|
definition invoke_vcpu_ack_vppi :: "obj_ref \<Rightarrow> vppievent_irq \<Rightarrow> (unit,'z::state_ext) s_monad" where
|
|
"invoke_vcpu_ack_vppi vcpu_ptr vppi =
|
|
vcpu_update vcpu_ptr
|
|
(\<lambda>vcpu. vcpu\<lparr> vcpu_vppi_masked := (vcpu_vppi_masked vcpu)(vppi := False) \<rparr>)"
|
|
|
|
text \<open>VCPU perform and decode main functions\<close>
|
|
|
|
definition
|
|
perform_vcpu_invocation :: "vcpu_invocation \<Rightarrow> (data list,'z::state_ext) s_monad" where
|
|
"perform_vcpu_invocation iv \<equiv> case iv of
|
|
VCPUSetTCB vcpu tcb \<Rightarrow> do associate_vcpu_tcb vcpu tcb; return [] od
|
|
| VCPUReadRegister vcpu reg \<Rightarrow> invoke_vcpu_read_register vcpu reg
|
|
| VCPUWriteRegister vcpu reg val \<Rightarrow> do invoke_vcpu_write_register vcpu reg val; return [] od
|
|
| VCPUInjectIRQ vcpu index vir \<Rightarrow> do invoke_vcpu_inject_irq vcpu index vir; return [] od
|
|
| VCPUAckVPPI vcpu vppi \<Rightarrow> do invoke_vcpu_ack_vppi vcpu vppi; return [] od"
|
|
|
|
definition decode_vcpu_invocation ::
|
|
"machine_word \<Rightarrow> machine_word list \<Rightarrow> arch_cap \<Rightarrow> (cap \<times> cslot_ptr) list \<Rightarrow>
|
|
(arch_invocation,'z::state_ext) se_monad"
|
|
where
|
|
"decode_vcpu_invocation label args cap extras \<equiv> case cap of
|
|
VCPUCap _ \<Rightarrow> (case invocation_type label of
|
|
ArchInvocationLabel ARMVCPUSetTCB \<Rightarrow> decode_vcpu_set_tcb cap extras
|
|
| ArchInvocationLabel ARMVCPUReadReg \<Rightarrow> decode_vcpu_read_register args cap
|
|
| ArchInvocationLabel ARMVCPUWriteReg \<Rightarrow> decode_vcpu_write_register args cap
|
|
| ArchInvocationLabel ARMVCPUInjectIRQ \<Rightarrow> decode_vcpu_inject_irq args cap
|
|
| ArchInvocationLabel ARMVCPUAckVPPI \<Rightarrow> decode_vcpu_ack_vppi args cap
|
|
| _ \<Rightarrow> throwError IllegalOperation)
|
|
| _ \<Rightarrow> throwError IllegalOperation"
|
|
|
|
end
|
|
|
|
end
|