blob: 6bb32e23c4b52a46134b3f99fdbec2569177b63e [file] [log] [blame]
/*#
*# Copyright 2018, Data61, CSIRO (ABN 41 687 119 230)
*#
*# SPDX-License-Identifier: BSD-2-Clause
#*/
(* /*? macros.generated_file_notice() ?*/ *)
/*? macros.check_isabelle_outfile(
'%s_CDL_Refine' % options.verification_base_name, outfile_name) ?*/
/*- set cdl_thy = '%s_CDL' % options.verification_base_name -*/
/*- set arch_spec_thy = '%s_Arch_Spec' % options.verification_base_name -*/
/*- set group_labels = macros.integrity_group_labels(me.composition, me.configuration) -*/
theory "/*? options.verification_base_name ?*/_CDL_Refine"
imports
/*? arch_spec_thy ?*/ (* generated arch spec *)
/*? cdl_thy ?*/ (* generated CDL spec *)
"CamkesCdlRefine.Policy_CAMKES_CDL"
"CamkesCdlRefine.Eval_CAMKES_CDL"
"DPolicy.Dpolicy"
"Lib.FastMap"
"Lib.RangeMap"
"Lib.FP_Eval"
"Lib.TermPatternAntiquote"
begin
(* FIXME: MOVE *)
setup \<open>
let
fun check thm = let
val eta_thm = Conv.fconv_rule Thm.eta_conversion thm;
fun which_thm thm = if Thm.derivation_name thm <> "" then Thm.derivation_name thm
else "\n" ^ @{make_string} thm;
in if Thm.eq_thm_prop (thm, eta_thm) then thm
else (warning ("check_eta_norm: fixing thm: " ^ which_thm thm);
eta_thm)
end
in
Attrib.setup \<^binding>\<open>check_eta_norm\<close>
(pair (fn (_, t) => (NONE, SOME (check t))))
"eta normalise theorems, and emit warnings"
end
\<close>
context begin interpretation Arch . (* FIXME: needed to talk about ASIDs *)
section \<open>System-specific policy definitions\<close>
text \<open>
We need to label objects in a way that matches the architecture spec
and also allows the cap layout to satisfy the access policy.
\<close>
section \<open>Expand policy rules\<close>
/*# FIXME: naming scheme duplicated from arch-definitions #*/
/*- if hasattr(me, 'name') and me.name is not none -*/
/*- set assembly_name = '%s.%s' % (arch_spec_thy, me.name) -*/
/*- else -*/
/*- set assembly_name = '%s.assembly\'' % arch_spec_thy -*/
/*- endif -*/
/*- if hasattr(me.composition, 'name') and me.composition.name is not none -*/
/*- set composition_name = '%s.%s' % (arch_spec_thy, me.composition.name) -*/
/*- else -*/
/*- set composition_name = '%s.composition\'' % arch_spec_thy -*/
/*- endif -*/
definition /*? options.verification_base_name ?*/_policy
where
"/*? options.verification_base_name ?*/_policy \<equiv> policy_of /*? assembly_name ?*/"
schematic_goal /*? options.verification_base_name ?*/_component_names:
"components (composition /*? assembly_name ?*/) = ?comps"
apply (clarsimp simp: /*? assembly_name ?*/_def
/*? composition_name ?*/_def)
apply (rule refl)
done
schematic_goal /*? options.verification_base_name ?*/_connections:
"connections (composition /*? assembly_name ?*/) = ?spec"
apply (clarsimp simp: /*? assembly_name ?*/_def
/*? composition_name ?*/_def
/*- for c in me.composition.instances -*/
/*? arch_spec_thy ?*/./*? isabelle_component(c.name) ?*/_def
/*- endfor -*/
/*- for c in me.composition.connections -*/
/*? arch_spec_thy ?*/./*? isabelle_connection(c.name) ?*/_def
/*- endfor -*/
/*- for c in uniq(map(lambda('x: x.type.name'), me.composition.connections)) -*/
/*? arch_spec_thy ?*/./*? isabelle_connector(c) ?*/_def
/*- endfor -*/
\<comment> \<open>Built-in connector defs\<close>
seL4RPC_def
seL4Notification_def
seL4SharedData_def
seL4HardwareMMIO_def
seL4HardwareInterrupt_def
)
apply (rule refl)
done
schematic_goal /*? options.verification_base_name ?*/_groups:
"group_labels (composition /*? assembly_name ?*/) = ?map"
apply (clarsimp simp: /*? assembly_name ?*/_def
/*? composition_name ?*/_def
/*- for c in me.composition.connections -*/
/*? arch_spec_thy ?*/./*? isabelle_connection(c.name) ?*/_def
/*- endfor -*/
/*- for c in uniq(map(lambda('x: x.type.name'), me.composition.connections)) -*/
/*? arch_spec_thy ?*/./*? isabelle_connector(c) ?*/_def
/*- endfor -*/
)
apply (rule refl)
done
(* Optimise group label lookups *)
lemma /*? options.verification_base_name ?*/_group_label_lookups:
/*- for x in me.composition.instances + me.composition.connections: -*/
"get_group_label (composition /*? assembly_name ?*/) ''/*? x.name ?*/'' = ''/*? group_labels.get(x.name, x.name) ?*/''"
/*- endfor -*/
by (simp add: get_group_label_def /*? options.verification_base_name ?*/_groups)+
/*- if get_policy_extra() -*/
lemma /*? options.verification_base_name ?*/_policy_extra:
"(\<lambda>(subj, auth, obj). (get_group_label (composition /*? assembly_name ?*/) subj, auth,
get_group_label (composition /*? assembly_name ?*/) obj)) `
policy_extra /*? assembly_name ?*/ =
{(subj, auth, obj).
/*- for i, (subj, auth, obj) in enumerate(get_policy_extra()) -*/
/*? '\\<or> ' if i else ' ' ?*/subj = ''/*? subj ?*/'' \<and> auth = /*? auth ?*/ \<and> obj = ''/*? obj ?*/''
/*- endfor -*/
}"
apply (rule_tac t="policy_extra /*? assembly_name ?*/" in subst)
apply (simp add: /*? assembly_name ?*/_def)
apply (simp add: /*? options.verification_base_name ?*/_group_label_lookups)
by fastforce
/*- else -*/
lemma /*? options.verification_base_name ?*/_policy_extra:
"(\<lambda>(subj, auth, obj). (get_group_label (composition /*? assembly_name ?*/) subj, auth,
get_group_label (composition /*? assembly_name ?*/) obj)) `
policy_extra /*? assembly_name ?*/ =
{}"
by (simp add: /*? assembly_name ?*/_def)
/*- endif -*/
schematic_goal /*? options.verification_base_name ?*/_policy_def':
"/*? options.verification_base_name ?*/_policy = ?PAS"
(* FIXME: this should be more systematic *)
apply (clarsimp
simp: policy_of_def
/*? options.verification_base_name ?*/_policy_def
/*? options.verification_base_name ?*/_component_names
/*? options.verification_base_name ?*/_connections
/*? options.verification_base_name ?*/_policy_extra
/*? options.verification_base_name ?*/_group_label_lookups
Collect_Int_pred_eq Collect_union
simp del: Collect_case_prod
)
apply (subst split_Collect_graph_edge)
apply (rule Collect_graph_cong_helper)
apply (clarsimp simp: Groebner_Basis.dnf) \<comment> \<open>normalise\<close>
apply ((clarsimp cong: conj_cong)?, (clarsimp cong: rev_conj_cong)?) \<comment> \<open>normalise some more\<close>
apply assign_schematic_dnf
done
(*
* From /*? options.verification_base_name ?*/_policy_def', generate a list of rules of the form
* "(''subj'', auth, ''obj'') \<in> /*? options.verification_base_name ?*/_policy"
*)
schematic_goal /*? options.verification_base_name ?*/_policy_gen_cases_:
"((subj, auth, obj) \<in> /*? options.verification_base_name ?*/_policy) = ?cases"
apply (clarsimp simp only: /*? options.verification_base_name ?*/_policy_def' mem_Collect_eq)
by assign_schematic_dnf
local_setup \<open>fn ctxt => let
fun try_repeat f x = case try f x of SOME x' => try_repeat f x' | NONE => x;
(* convert "(a = x \<and> b = y \<and> \<dots>) \<longrightarrow> foo a b \<dots>" to "foo x y \<dots>" *)
val subst_values =
REPEAT_ALL_NEW (resolve_tac @{context} @{thms conjI}) 1
#> Seq.hd
#> Simplifier.rewrite_rule @{context} @{thms atomize_imp}
#> try_repeat (fn t => t RS @{thm subst_eqn_helper});
fun process thm =
case try subst_values thm of
SOME eqn => [eqn]
| NONE => process (@{thm disjI1} RS thm) @ process (@{thm disjI2} RS thm);
val /*? options.verification_base_name ?*/_policy_intros =
process @{thm /*? options.verification_base_name ?*/_policy_gen_cases_[THEN iffD2]}
|> distinct Thm.eq_thm_prop; (* remove dups *)
in
ctxt
|> Local_Theory.notes [((Binding.name "/*? options.verification_base_name ?*/_policy_intros", []),
[(/*? options.verification_base_name ?*/_policy_intros, [])])]
|> snd
end
\<close>
thm /*? options.verification_base_name ?*/_policy_intros
section \<open>Object label mappings\<close>
text \<open>
Construct mapping of integrity labels for each object.
\<close>
ML \<open>
(* The RangeMap package expects the input in sorted order,
we need to sort the object IDs by numeric value. *)
fun /*? options.verification_base_name ?*/_id_value ctxt obj_name =
Proof_Context.get_thm ctxt ("/*? cdl_thy ?*/." ^ obj_name ^ "_id_def")
|> Thm.prop_of
|> Logic.dest_equals |> snd
|> HOLogic.dest_number |> snd;
val /*? options.verification_base_name ?*/_obj_labels =
(* object name, size bits, policy label *)
/*- set delim = namespace(value='[') -*//*# need this nonsense to modify variable -- see jinja2 docs #*/
/*- for obj, label in sorted(object_label_mapping(), key=lambda('x: x[0].name')) -*/
/*- if not obj.name.startswith('root_untyped_') -*//*# Exclude root untypeds because they overlap other objects and have no policy. FIXME: better way to detect these #*/
/*? delim.value ?*/ ("/*? isabelle_capdl_identifier(obj.name) ?*/", /*? obj.get_size_bits() if obj.get_size_bits() != None else '0' ?*/, "/*? group_labels.get(label, label) ?*/")
/*- set delim.value = ',' -*/
/*- endif -*/
/*- endfor -*/
]
|> sort (apply2 (#1 #> /*? options.verification_base_name ?*/_id_value @{context}) #> int_ord);
val id_value : string -> int =
fn id =>
Proof_Context.get_thm @{context} (id ^ "_def")
|> Thm.rhs_of |> Thm.term_of |> HOLogic.dest_number |> snd;
(* Now construct our mapping from (ptr, ptr + 2^sz) to labels *)
val /*? options.verification_base_name ?*/_label_entries =
/*? options.verification_base_name ?*/_obj_labels
|> map (fn (obj_name, sz, label) => ("/*? cdl_thy ?*/." ^ obj_name ^ "_id", sz, label))
|> map (fn (id, sz, label) =>
((Const (id, @{typ cdl_object_id}),
@{term "(+) :: cdl_object_id \<Rightarrow> _ \<Rightarrow> _"} $
Const (id, @{typ cdl_object_id}) $
(@{term "(^) 2 :: _ \<Rightarrow> cdl_object_id"} $ HOLogic.mk_number @{typ nat} sz)),
HOLogic.mk_string label))
|> map (fn (range, label) =>
(apply2 (Thm.cterm_of @{context}) range, Thm.cterm_of @{context} label));
\<close>
local_setup \<open>
RangeMap.define_map (RangeMap.name_opts_default "/*? options.verification_base_name ?*/_label")
/*? options.verification_base_name ?*/_label_entries @{typ cdl_object_id} @{typ label}
(FP_Eval.eval_conv @{context}
(FP_Eval.make_rules @{thms word_rel_simps_small word_pow_arith_simps /*? cdl_thy ?*/.ids} []))
\<close>
(* we will primarily use these *)
lemmas /*? options.verification_base_name ?*/_label_lookups =
/*? options.verification_base_name ?*/_label.start_lookups
[unfolded /*? options.verification_base_name ?*/_label.tree_list_lookup_eq]
text \<open>
Define admissible labelling functions to be those that are consistent
with the mapping we defined above.
\<close>
definition /*? options.verification_base_name ?*/_labelling :: "cdl_object_id \<Rightarrow> string option"
where
"/*? options.verification_base_name ?*/_labelling x \<equiv> map_option snd (RangeMap.range_map_of /*? options.verification_base_name ?*/_label.list x)"
definition /*? options.verification_base_name ?*/_admissible_labelling :: "label agent_map \<Rightarrow> bool"
where
"/*? options.verification_base_name ?*/_admissible_labelling label_of \<equiv>
(\<forall>i l. /*? options.verification_base_name ?*/_labelling i = Some l \<longrightarrow> label_of i = l)"
lemma /*? options.verification_base_name ?*/_admissible_labelling_default:
"/*? options.verification_base_name ?*/_admissible_labelling (the o /*? options.verification_base_name ?*/_labelling)"
by (simp add: /*? options.verification_base_name ?*/_admissible_labelling_def)
text \<open>Make sure that an admissible labelling exists.\<close>
corollary /*? options.verification_base_name ?*/_admissible_labelling_exists:
"\<exists>label_of. /*? options.verification_base_name ?*/_admissible_labelling label_of"
by (blast intro: /*? options.verification_base_name ?*/_admissible_labelling_default)
subsection \<open>Sanity checks for object labelling\<close>
text \<open>
Check that we always label certain important objects with the obvious
component name. We check this for each component's:
\begin{itemize}
\item Control TCB
\item Root CNode (CAmkES only generates one CNode level for each component,
so this suffices for now)
\item PD and its PTs
\item TCB root caps (this also covers the CNode and PD, but includes
additional things like the IPC buffer frame)
\end{itemize}
\<close>
lemma /*? options.verification_base_name ?*/_admissible_labelling__tcbs_correct:
"/*? options.verification_base_name ?*/_admissible_labelling label_of \<Longrightarrow>
(
/*- set delim = namespace(value=' ') -*//*# need this nonsense to modify variable -- see jinja2 docs #*/
/*- for c in me.composition.instances -*/
/*- if c.type.control -*/
/*- set group = group_labels.get(c.name, c.name) -*/
/*? delim.value ?*/ label_of /*? isabelle_capdl_identifier(group) ?*/_cnode_id = ''/*? group ?*/''
\<and> label_of /*? isabelle_capdl_identifier('%s_%s_0_control_tcb_id' % (c.name, c.name.replace('.', '_'))) ?*/ = ''/*? group ?*/''/*#
XXX: the extra 'replace' in the second name component duplicates what the camkes tool does internally #*/
\<and> label_of /*? isabelle_capdl_identifier('%s_group_bin_pd_id' % group) ?*/ = ''/*? group ?*/''
\<and> (\<forall>cap \<in> ran /*? isabelle_capdl_identifier('%s_%s_0_control_tcb_caps' % (c.name, c.name.replace('.', '_'))) ?*/. \<forall>i \<in> cap_objects cap. label_of i = ''/*? group ?*/'')/*# XXX: ditto here #*/
\<and> (\<forall>pt_i \<in> mapped_pts_of /*? cdl_thy ?*/.objects /*? isabelle_capdl_identifier('%s_group_bin_pd_caps' % group) ?*/. label_of pt_i = ''/*? group ?*/'')
/*- set delim.value = '\\<and>' -*/
/*- endif -*/
/*- endfor -*/
)"
(* FIXME: cleanup *)
apply (simp only: rel_simps arith_simps simp_thms if_False if_True conj_assoc imp_disjL
option.map prod.sel
ball_simps bex_simps fun_upd_apply ran_map_upd ran_empty
finite_set_simps
cdl_cap.case cap_objects.simps mapped_pts_of_def object_slots_def
/*? options.verification_base_name ?*/_CDL.cap_defs
/*? options.verification_base_name ?*/_CDL.obj_defs
/*? options.verification_base_name ?*/_CDL.objects_lookups
/*? options.verification_base_name ?*/_admissible_labelling_def
/*? options.verification_base_name ?*/_labelling_def
/*? options.verification_base_name ?*/_label_lookups
cong: imp_cong)
done
(*
text \<open>
Potential check that all labels are inhabited. It is currently allowed for connections
without any objects which would fail this check. However this check could be useful
as a sanity check.
\<close>
lemma /*? options.verification_base_name ?*/_admissible_labelling__all_labels_inhabited:
"/*? options.verification_base_name ?*/_admissible_labelling label_of \<Longrightarrow>
/*- set delim = namespace(value=' ') -*//*# need this nonsense to modify variable -- see jinja2 docs #*/
/*- set seen = set() -*/
/*- for c in composition.instances + me.composition.connections -*/
/*- set name = group_labels.get(c.name, c.name) -*/
/*- if name not in seen -*/
/*? delim.value ?*/ (\<exists>obj. label_of obj = ''/*? name ?*/'')
/*- do seen.add(name) -*/
/*- set delim.value = '\\<and>' -*/
/*- endif -*/
/*- endfor -*/
"
apply (simp add: /*? options.verification_base_name ?*/_admissible_labelling_def
/*? options.verification_base_name ?*/_labelling_def)
apply (fastforce intro: /*? options.verification_base_name ?*/_label_lookups)
done
*)
(* FIXME: more sanity checks *)
subsection \<open>Admissible ASID labels\<close>
/*- set have_ASIDPools = namespace(value=False) -*//*# need this nonsense to modify variable -- see jinja2 docs #*/
/*- for asid_pool, label in object_label_mapping() -*/
/*- if is_ASIDPool_object(asid_pool) and not (asid_pool.asid_high is none) -*/
/*- set have_ASIDPools.value = True -*/
/*- endif -*/
/*- endfor -*/
definition /*? options.verification_base_name ?*/_asids :: "asid_high \<Rightarrow> label option"
where
/*- if have_ASIDPools.value -*/
"/*? options.verification_base_name ?*/_asids \<equiv> [
/*- set delim = namespace(value=' ') -*//*# need this nonsense to modify variable -- see jinja2 docs #*/
/*- for asid_pool, label in object_label_mapping() -*/
/*- if is_ASIDPool_object(asid_pool) and not (asid_pool.asid_high is none) -*/
/*? delim.value ?*/ /*? '0x%x' % asid_pool.asid_high ?*/ \<mapsto> ''/*? label ?*/''
/*- set delim.value = ',' -*/
/*- endif -*/
/*- endfor -*/
]"
/*- else -*/
"/*? options.verification_base_name ?*/_asids \<equiv> map_of []"
/*- endif -*/
definition /*? options.verification_base_name ?*/_admissible_asids :: "label agent_asid_map \<Rightarrow> bool"
where
"/*? options.verification_base_name ?*/_admissible_asids asidAbs \<equiv>
(\<forall>asid label. /*? options.verification_base_name ?*/_asids (asid_high_bits_of asid) = Some label \<longrightarrow> asidAbs asid = label)"
(* Expanded (FIXME: assumes exactly one ASID slot) *)
schematic_goal /*? options.verification_base_name ?*/_admissible_asids_cases:
"/*? options.verification_base_name ?*/_admissible_asids irqAbs = ?cases"
apply (clarsimp simp: /*? options.verification_base_name ?*/_admissible_asids_def /*? options.verification_base_name ?*/_asids_def)
/*- if have_ASIDPools.value -*/
apply (rule refl)
/*- else -*/
apply (rule TrueI)
/*- endif -*/
done
subsection \<open>Admissible IRQ labels\<close>
/*# FIXME: maybe should be generated from components' .irq configuration
rather than blindly scraping the objects list #*/
/*- set have_IRQs = namespace(value=False) -*//*# need this nonsense to modify variable -- see jinja2 docs #*/
/*- for obj, label in object_label_mapping() -*/
/*- if is_IRQ_object(obj) -*/
/*- set have_IRQs.value = True -*/
/*- endif -*/
/*- endfor -*/
/*# FIXME: workaround because FastMap currently doesn't support empty maps #*/
/*- if have_IRQs.value -*/
local_setup \<open>
let
val entries =
/*- for obj, label in object_label_mapping() -*/
/*- if is_IRQ_object(obj) -*/
(/*? obj.number ?*/, "/*? group_labels.get(label, label) ?*/") ::
/*- endif -*/
/*- endfor -*/
[]
|> sort (prod_ord int_ord string_ord);
in
FastMap.define_map
(FastMap.name_opts_default "/*? options.verification_base_name ?*/_irqs")
(map (fn (irq, l) => (HOLogic.mk_number @{typ cdl_irq} irq, HOLogic.mk_string l))
entries)
@{term "id :: cdl_irq \<Rightarrow> _"}
[]
false
end
\<close>
/*- else -*/
definition /*? options.verification_base_name ?*/_irqs :: "label option agent_irq_map"
where
"/*? options.verification_base_name ?*/_irqs \<equiv> map_of []"
/*# would have been generated by FastMap #*/
lemmas /*? options.verification_base_name ?*/_irqs_lookups = refl
/*- endif -*/
definition /*? options.verification_base_name ?*/_admissible_irqs :: "label agent_irq_map \<Rightarrow> bool"
where
"/*? options.verification_base_name ?*/_admissible_irqs irqAbs \<equiv>
(\<forall>irq label. /*? options.verification_base_name ?*/_irqs irq = Some label \<longrightarrow> irqAbs irq = label)"
(* Expanded *)
schematic_goal /*? options.verification_base_name ?*/_admissible_irqs_cases:
"/*? options.verification_base_name ?*/_admissible_irqs irqAbs = ?cases"
apply (clarsimp simp only: /*? options.verification_base_name ?*/_admissible_irqs_def)
/*- if have_IRQs.value -*/
apply (subst iterate_labelling_helper)
apply (rule /*? options.verification_base_name ?*/_irqs_to_lookup_list)
apply (rule /*? options.verification_base_name ?*/_irqs_keys_distinct)
apply (clarsimp simp only: FastMap.list_all_dest)
apply (rule refl)
/*- else -*//*# no FastMap theorems #*/
apply (clarsimp simp: /*? options.verification_base_name ?*/_irqs_def)
apply (rule TrueI)
/*- endif -*/
done
section \<open>Admissible PAS\<close>
text \<open>
This defines a set of policies that fit our arch spec and cap layout.
\<close>
definition /*? options.verification_base_name ?*/_admissible_pas :: "label PAS \<Rightarrow> bool"
where
"/*? options.verification_base_name ?*/_admissible_pas pas \<equiv>
/*? options.verification_base_name ?*/_admissible_labelling (pasObjectAbs pas) \<and>
/*? options.verification_base_name ?*/_admissible_asids (pasASIDAbs pas) \<and>
/*? options.verification_base_name ?*/_admissible_irqs (pasIRQAbs pas) \<and>
pasSubject pas \<in> fst ` set (components (composition /*? assembly_name ?*/)) \<and>
/*? options.verification_base_name ?*/_policy \<subseteq> pasPolicy pas"
text \<open>Again, ensure that admissible policies exist.\<close>
lemma /*? options.verification_base_name ?*/_admissible_pas_exists:
"\<exists>pas. /*? options.verification_base_name ?*/_admissible_pas pas"
apply (insert /*? options.verification_base_name ?*/_admissible_labelling_exists)
apply (erule exE, rename_tac poa)
(* For now, just fill in the fields we need. *)
apply (rule_tac x = "undefined\<lparr>
pasObjectAbs := poa,
pasPolicy := /*? options.verification_base_name ?*/_policy,
pasSubject := fst (hd (components (composition /*? assembly_name ?*/))),
pasASIDAbs := the o /*? options.verification_base_name ?*/_asids o asid_high_bits_of,
pasIRQAbs := the o /*? options.verification_base_name ?*/_irqs
\<rparr>"
in exI)
apply (simp add: /*? options.verification_base_name ?*/_admissible_pas_def)
apply (intro conjI)
apply (simp add: /*? options.verification_base_name ?*/_admissible_asids_def)
apply (simp add: /*? options.verification_base_name ?*/_admissible_irqs_def)
apply (simp add: /*? options.verification_base_name ?*/_admissible_pas_def /*? options.verification_base_name ?*/_connections /*? options.verification_base_name ?*/_component_names)
done
text \<open>
Checking the transitivity conditions for @{const policy_wellformed} is quadratic in
the size of our policy; here we extract relevant subsets of the policy cases to
make things a bit faster.
Ultimately, we would like to prove a generic @{const policy_wellformed} theorem for
all @{const policy_of} outputs, but the current messiness of
@{const policy_of} and @{const wellformed_assembly} are not conducive for that.
\<close>
lemmas /*? options.verification_base_name ?*/_policy_cases_Control =
/*? options.verification_base_name ?*/_policy_gen_cases_[where auth = "Control", simplified]
lemmas /*? options.verification_base_name ?*/_policy_cases_Receive =
/*? options.verification_base_name ?*/_policy_gen_cases_[where auth = "Receive", simplified]
lemmas /*? options.verification_base_name ?*/_policy_cases_Reply =
/*? options.verification_base_name ?*/_policy_gen_cases_[where auth = "Reply", simplified]
lemmas /*? options.verification_base_name ?*/_policy_cases_Grant =
/*? options.verification_base_name ?*/_policy_gen_cases_[where auth = "auth.Grant", simplified]
lemmas /*? options.verification_base_name ?*/_policy_cases_Call =
/*? options.verification_base_name ?*/_policy_gen_cases_[where auth = "Call", simplified]
lemmas /*? options.verification_base_name ?*/_policy_cases_DeleteDerived =
/*? options.verification_base_name ?*/_policy_gen_cases_[where auth = "DeleteDerived", simplified]
lemmas /*? options.verification_base_name ?*/_policy_cases_DeleteDerived2 =
/*- for c in me.composition.instances -*/
/*? options.verification_base_name ?*/_policy_cases_DeleteDerived[
where subj = "''/*? group_labels.get(c.name, c.name) ?*/''", simplified simp_thms list.distinct list.inject char.inject]
/*- endfor -*/
text \<open>
Ensure that our base access policy is wellformed.
This lets us extend it to other wellformed policies.
\<close>
(* Expand a policy rule of the form
* "(a, auth, b) \<in> policy \<Longrightarrow> (a = ''a'' \<and> b = ''b'' \<or> ...)"
* and simplify string label comparisons *)
method unfold_policy uses expand =
(drule expand;
elim conjE disjE;
(simp only: simp_thms list.distinct list.inject char.inject cong: disj_cong)?)
lemma /*? options.verification_base_name ?*/_policy_wellformed:
"\<lbrakk> pasPolicy aag = /*? options.verification_base_name ?*/_policy;
pasSubject aag \<in> get_group_label (composition /*? assembly_name ?*/)
` fst ` set (components (composition /*? assembly_name ?*/));
\<not> pasMaySendIrqs aag \<comment> \<open>ignore IRQs for now\<close>
\<rbrakk> \<Longrightarrow> pas_wellformed aag"
apply clarsimp
apply (rule camkes_policy_wellformedI)
(* Components may not send IRQs *)
subgoal
apply blast
done
(* Components are agents *)
subgoal
apply (fastforce simp: /*? options.verification_base_name ?*/_connections
/*? options.verification_base_name ?*/_component_names
/*? options.verification_base_name ?*/_group_label_lookups
intro: /*? options.verification_base_name ?*/_policy_intros)
done
(* All subjects have self Control *)
subgoal
apply (unfold_policy expand: /*? options.verification_base_name ?*/_policy_gen_cases_[THEN iffD1];
(intro /*? options.verification_base_name ?*/_policy_intros)?)
done
(* Grant confinement *)
subgoal
apply (unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_Grant[THEN iffD1];
(intro /*? options.verification_base_name ?*/_policy_intros)?)
done
(* Control confinement *)
subgoal
apply (fastforce dest: /*? options.verification_base_name ?*/_policy_cases_Control[THEN iffD1]
intro: /*? options.verification_base_name ?*/_policy_intros)
done
(* Control implies all rights *)
subgoal
apply (fastforce dest: /*? options.verification_base_name ?*/_policy_cases_Control[THEN iffD1]
intro: /*? options.verification_base_name ?*/_policy_intros)
done
(* Components are not Receive targets *)
subgoal
apply (unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_Receive[THEN iffD1];
unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_Control[THEN iffD1];
(intro /*? options.verification_base_name ?*/_policy_intros)?)
done
(* Components are not Call targets *)
subgoal
apply (unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_Call[THEN iffD1];
unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_Control[THEN iffD1];
(intro /*? options.verification_base_name ?*/_policy_intros)?)
done
(* Call implies SyncSend *)
subgoal
apply (unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_Call[THEN iffD1];
(intro /*? options.verification_base_name ?*/_policy_intros)?)
done
(* Reply implies DeleteDerived *)
subgoal
apply (unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_Reply[THEN iffD1];
(intro /*? options.verification_base_name ?*/_policy_intros)?)
done
(* Call + Receive implies Reply *)
subgoal
apply (unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_Call[THEN iffD1];
unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_Receive[THEN iffD1];
(intro /*? options.verification_base_name ?*/_policy_intros)?)
done
(* DeleteDerived is transitive (see also VER-1030) *)
subgoal
apply (erule(4) complete_graph_is_transitive')
apply (subst let_weak_cong)
apply (simp only: /*? options.verification_base_name ?*/_policy_cases_DeleteDerived Collect_case_prod_dnf)
apply (simp only: Let_def finite_set_simps prod.sel
image_insert image_empty
(* performance note: insert_commute sorts labels in the simplifier,
then insert_absorb2 removes duplicates *)
insert_commute insert_absorb2)
apply (simp; intro conjI /*? options.verification_base_name ?*/_policy_intros)
(* slow version, O(#labels ^ 4) cases*)
(*
apply (unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_DeleteDerived[THEN iffD1];
unfold_policy expand: /*? options.verification_base_name ?*/_policy_cases_DeleteDerived2[THEN iffD1];
(intro /*? options.verification_base_name ?*/_policy_intros)?)
*)
done
done
section \<open>CAmkES-capDL refinement proof\<close>
(* specialise ptr_range rewrite rule *)
lemmas /*? options.verification_base_name ?*/_label_over_ptr_range =
label_over_ptr_range[
(* FP_Eval only supports first-order matching, so instantiate P *)
where P = "\<lambda>l. generic_tag type tag ((_, _, l) \<in> _)"
and label_spec = /*? options.verification_base_name ?*/_labelling
for type tag,
OF /*? options.verification_base_name ?*/_label.list_monotonic,
(* expand labelling into tree lookup *)
unfolded /*? options.verification_base_name ?*/_labelling_def, OF refl]
lemmas /*? options.verification_base_name ?*/_label_over_ptr_range_cases =
/*? options.verification_base_name ?*/_label_lookups[THEN /*? options.verification_base_name ?*/_label_over_ptr_range]
lemma /*? options.verification_base_name ?*/_asid_policy:
assumes admissible_pas:
"/*? options.verification_base_name ?*/_admissible_pas pas"
shows
"cdl_state_asids_to_policy pas /*? options.verification_base_name ?*/_CDL.state \<subseteq> pasPolicy pas"
proof -
(* everything needed to evaluate asid integrity... *)
note obj_policy_eval_simps' =
/*? options.verification_base_name ?*/_label_lookups
/*? options.verification_base_name ?*/_CDL.obj_defs
/*? options.verification_base_name ?*/_CDL.objects_lookups
(* Map.empty is actually an abbrev for a lambda, which fp_eval
doesn't allow as a rule LHS. Rewrite eagerly first *)
(* FIXME: generate these with map_of in the first place.
Also fix "rev ..." in graph_of_map_of__sorted_eval *)
/*? options.verification_base_name ?*/_CDL.cap_defs[simplified fun_upds_to_map_of]
/*? options.verification_base_name ?*/_label_over_ptr_range_cases
\<comment> \<open>FIXME: Isabelle2019 simplifier doesn't eta-normalise??\<close>
Collect_asid_high__eval_helper
[simplified asid_high_bits_def, simplified, check_eta_norm]
/*? options.verification_base_name ?*/_asids_def[simplified fun_upds_to_map_of] map_of_Cons_code
/*? options.verification_base_name ?*/_CDL.asid_table_def[simplified fun_upds_to_map_of]
obj_policy_eval_simps
show ?thesis
apply (rule cdl_state_asids_to_policy__eval[where
policy_spec=/*? options.verification_base_name ?*/_policy and
obj_label_spec=/*? options.verification_base_name ?*/_labelling and
asid_label_spec=/*? options.verification_base_name ?*/_asids
])
using admissible_pas /*? options.verification_base_name ?*/_admissible_pas_def apply blast
using admissible_pas /*? options.verification_base_name ?*/_admissible_pas_def /*? options.verification_base_name ?*/_admissible_labelling_def apply blast
using admissible_pas /*? options.verification_base_name ?*/_admissible_pas_def /*? options.verification_base_name ?*/_admissible_asids_def apply blast
(* ASIDs in objects *)
subgoal
apply (simp only: /*? options.verification_base_name ?*/_labelling_def)
apply (tactic \<open>let
val rules = @{thms /*? options.verification_base_name ?*/_CDL.state_def cdl_state.simps
/*? options.verification_base_name ?*/_CDL.objects_to_lookup_list
graph_of_map_of__distinct_eval if_True
/*? options.verification_base_name ?*/_CDL.objects_keys_distinct};
val congs = @{thms obj_policy_eval_congs};
in SUBGOAL (fn _ => FP_Eval.eval_tac @{context} (FP_Eval.make_rules rules congs) 1)
1 end\<close>)
(* this is a separate step for now, because graph_of_map_of__sorted_eval
won't work for objects list *)
apply (tactic \<open>let
val rules = @{thms obj_policy_eval_simps'};
val congs = @{thms obj_policy_eval_congs};
val conv = rpair FP_Eval.skel0
#> FP_Eval.eval @{context} (FP_Eval.make_rules rules congs)
#> tap (fn (_, c) => tracing ("fp_eval counters: " ^ @{make_string} c))
#> fst #> fst;
in SUBGOAL (fn _ => Conv.gconv_rule conv 1 #> Seq.succeed) 1 end\<close>;
intro TrueI conjI /*? options.verification_base_name ?*/_policy_intros[THEN generic_tagP_I])
done
(* ASID pools *)
apply (simp only: /*? options.verification_base_name ?*/_labelling_def /*? options.verification_base_name ?*/_CDL.state_def cdl_state.simps)
apply (tactic \<open>let
val rules = @{thms /*? options.verification_base_name ?*/_CDL.state_def cdl_state.simps
/*? options.verification_base_name ?*/_CDL.objects_to_lookup_list
graph_of_map_of__distinct_eval if_True
/*? options.verification_base_name ?*/_CDL.objects_keys_distinct};
val congs = @{thms obj_policy_eval_congs};
in SUBGOAL (fn _ => FP_Eval.eval_tac @{context} (FP_Eval.make_rules rules congs) 1)
1 end\<close>)
(* this is a separate step for now, because graph_of_map_of__sorted_eval
won't work for objects list *)
apply (tactic \<open>let
val rules = @{thms obj_policy_eval_simps'};
val congs = @{thms obj_policy_eval_congs};
val conv = rpair FP_Eval.skel0
#> FP_Eval.eval @{context} (FP_Eval.make_rules rules congs)
#> tap (fn (_, c) => tracing ("fp_eval counters: " ^ @{make_string} c))
#> fst #> fst;
in SUBGOAL (fn _ => Conv.gconv_rule conv 1 #> Seq.succeed) 1 end\<close>;
intro TrueI conjI /*? options.verification_base_name ?*/_policy_intros[THEN generic_tagP_I])
done
qed
lemma /*? options.verification_base_name ?*/_irq_policy:
assumes admissible_pas:
"/*? options.verification_base_name ?*/_admissible_pas pas"
shows
"cdl_state_irqs_to_policy pas /*? options.verification_base_name ?*/_CDL.state \<subseteq> pasPolicy pas"
proof -
(* everything needed to evaluate asid integrity... *)
note obj_policy_eval_simps' =
/*? options.verification_base_name ?*/_label_lookups
/*? options.verification_base_name ?*/_CDL.obj_defs
(* Map.empty is actually an abbrev for a lambda, which fp_eval
doesn't allow as a rule LHS. Rewrite eagerly first *)
(* FIXME: generate these with map_of in the first place.
Also fix "rev ..." in graph_of_map_of__sorted_eval *)
/*? options.verification_base_name ?*/_CDL.cap_defs[simplified fun_upds_to_map_of]
/*? options.verification_base_name ?*/_label_over_ptr_range_cases
/*? options.verification_base_name ?*/_irqs_lookups
obj_policy_eval_simps
show ?thesis
apply (rule cdl_state_irqs_to_policy__eval[where
policy_spec=/*? options.verification_base_name ?*/_policy and
obj_label_spec=/*? options.verification_base_name ?*/_labelling and
irq_label_spec=/*? options.verification_base_name ?*/_irqs
])
using admissible_pas /*? options.verification_base_name ?*/_admissible_pas_def apply blast
using admissible_pas /*? options.verification_base_name ?*/_admissible_pas_def /*? options.verification_base_name ?*/_admissible_labelling_def apply blast
using admissible_pas /*? options.verification_base_name ?*/_admissible_pas_def /*? options.verification_base_name ?*/_admissible_irqs_def apply blast
apply (simp only: /*? options.verification_base_name ?*/_labelling_def)
apply (tactic \<open>let
val rules = @{thms /*? options.verification_base_name ?*/_CDL.state_def cdl_state.simps
/*? options.verification_base_name ?*/_CDL.objects_to_lookup_list
graph_of_map_of__distinct_eval if_True
/*? options.verification_base_name ?*/_CDL.objects_keys_distinct};
val congs = @{thms obj_policy_eval_congs};
in SUBGOAL (fn _ => FP_Eval.eval_tac @{context} (FP_Eval.make_rules rules congs) 1)
1 end\<close>)
(* this is a separate step for now, because graph_of_map_of__sorted_eval
won't work for objects list *)
apply (tactic \<open>let
val rules = @{thms obj_policy_eval_simps'};
val congs = @{thms obj_policy_eval_congs};
val conv = rpair FP_Eval.skel0
#> FP_Eval.eval @{context} (FP_Eval.make_rules rules congs)
#> tap (fn (_, c) => tracing ("fp_eval counters: " ^ @{make_string} c))
#> fst #> fst;
in SUBGOAL (fn _ => Conv.gconv_rule conv 1 #> Seq.succeed) 1 end\<close>;
intro TrueI conjI /*? options.verification_base_name ?*/_policy_intros[THEN generic_tagP_I])
done
qed
text \<open>Main integrity proof\<close>
theorem /*? options.verification_base_name ?*/_pcs_refined:
assumes other_assms:
"pas_wellformed pas"
(* TODO *) "cdl_irq_map_wellformed pas /*? options.verification_base_name ?*/_CDL.state"
(* TODO *) "cdl_tcb_domain_map_wellformed pas /*? options.verification_base_name ?*/_CDL.state"
assumes admissible_pas:
"/*? options.verification_base_name ?*/_admissible_pas pas"
shows
"pcs_refined pas /*? options.verification_base_name ?*/_CDL.state"
proof -
from admissible_pas have /*? options.verification_base_name ?*/_policy:
"/*? options.verification_base_name ?*/_policy \<subseteq> pasPolicy pas"
by (simp add: /*? options.verification_base_name ?*/_admissible_pas_def)
(* everything needed to evaluate object integrity... *)
note obj_policy_eval_simps' =
/*? options.verification_base_name ?*/_label_lookups
/*? options.verification_base_name ?*/_CDL.obj_defs
(* Map.empty is actually an abbrev for a lambda, which fp_eval
doesn't allow as a rule LHS. Rewrite eagerly first *)
(* FIXME: generate these with map_of in the first place.
Also fix "rev ..." in graph_of_map_of__sorted_eval *)
/*? options.verification_base_name ?*/_CDL.cap_defs[simplified fun_upds_to_map_of]
/*? options.verification_base_name ?*/_label_over_ptr_range_cases
obj_policy_eval_simps
show ?thesis
apply (clarsimp simp only: simp_thms
pcs_refined_def other_assms
/*? options.verification_base_name ?*/_asid_policy[OF admissible_pas]
/*? options.verification_base_name ?*/_irq_policy[OF admissible_pas]
del: subsetI)
apply (rule helper_pcs_refined_policy__eval[
where policy_spec = /*? options.verification_base_name ?*/_policy
and label_spec = /*? options.verification_base_name ?*/_labelling])
(* instantiate policy specs *)
apply (rule /*? options.verification_base_name ?*/_policy)
using admissible_pas /*? options.verification_base_name ?*/_admissible_pas_def /*? options.verification_base_name ?*/_admissible_labelling_def
apply blast
text \<open>
CDT properties.
FIXME: Our capDL assigns an empty CDT, so there's not much to do here right now.
\<close>
apply ((fastforce simp: /*? options.verification_base_name ?*/_CDL.state_def /*? options.verification_base_name ?*/_CDL.cdt_def)+)[2]
text \<open>Object case.\<close>
apply (simp only: /*? options.verification_base_name ?*/_labelling_def)
(* this is a separate step for now, because graph_of_map_of__sorted_eval
won't work for objects list *)
apply (tactic \<open>let
val rules = @{thms /*? options.verification_base_name ?*/_CDL.state_def cdl_state.simps
/*? options.verification_base_name ?*/_CDL.objects_to_lookup_list
graph_of_map_of__distinct_eval if_True
/*? options.verification_base_name ?*/_CDL.objects_keys_distinct};
val congs = @{thms obj_policy_eval_congs};
in SUBGOAL (fn _ => FP_Eval.eval_tac @{context} (FP_Eval.make_rules rules congs) 1)
1 end\<close>)
(* this is a separate step for now, because graph_of_map_of__sorted_eval
won't work for objects list *)
apply (tactic \<open>let
val rules = @{thms obj_policy_eval_simps'};
val congs = @{thms obj_policy_eval_congs};
val conv = rpair FP_Eval.skel0
#> FP_Eval.eval @{context} (FP_Eval.make_rules rules congs)
#> tap (fn (_, c) => tracing ("fp_eval counters: " ^ @{make_string} c))
#> fst #> fst;
in SUBGOAL (fn _ => Conv.gconv_rule conv 1 #> Seq.succeed) 1 end\<close>;
intro TrueI conjI /*? options.verification_base_name ?*/_policy_intros[THEN generic_tagP_I])
done
qed
end
end