(*
 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
 *
 * SPDX-License-Identifier: GPL-2.0-only
 *)

theory CNode_IF
imports ArchFinalCaps
begin

lemma cap_fault_on_failure_rev:
  "reads_equiv_valid_inv A aag P m
   \<Longrightarrow> reads_equiv_valid_inv A aag P (cap_fault_on_failure cptr rp m)"
  unfolding cap_fault_on_failure_def handleE'_def
  by (wp | wpc | simp add: o_def)+

lemma cap_fault_on_failure_rev_g:
  "reads_respects_g aag l P m \<Longrightarrow> reads_respects_g aag l P (cap_fault_on_failure cptr rp m)"
  unfolding cap_fault_on_failure_def handleE'_def
  by (wp | wpc | simp add: o_def)+

definition gets_apply where
  "gets_apply f x \<equiv>
     do s \<leftarrow> get;
        return ((f s) x)
     od"

lemma gets_apply_ev:
  "equiv_valid I A A (K (\<forall>s t. I s t \<and> A s t \<longrightarrow> (f s) x = (f t) x)) (gets_apply f x)"
  apply (simp add: gets_apply_def get_def bind_def return_def)
  apply (clarsimp simp: equiv_valid_def2 equiv_valid_2_def)
  done

lemma gets_apply:
  "gets f >>= (\<lambda>f. g (f x)) = gets_apply f x >>= g"
  by (simp add: gets_apply_def gets_def)

lemma get_object_rev:
  "reads_equiv_valid_inv A aag (\<lambda>s. aag_can_read aag oref) (get_object oref)"
  apply (unfold get_object_def fun_app_def)
  apply (subst gets_apply)
  apply (wp gets_apply_ev | wp (once) hoare_drop_imps)+
  apply (fastforce elim: reads_equivE equiv_forE)
  done

lemma get_cap_rev:
  "reads_equiv_valid_inv A aag (K (aag_can_read aag (fst slot))) (get_cap slot)"
  unfolding get_cap_def
  by (wp get_object_rev | wpc | simp add: split_def)+

declare if_weak_cong[cong]

lemma resolve_address_bits_spec_rev:
  "reads_spec_equiv_valid_inv s A aag
     (pas_refined aag and K (is_cnode_cap (fst ref) \<longrightarrow> is_subject aag (obj_ref_of (fst ref))))
     (resolve_address_bits ref)"
proof(unfold resolve_address_bits_def, induct ref arbitrary: s rule: resolve_address_bits'.induct)
  case (1 z cap' cref' s') show ?case
    apply (subst resolve_address_bits'.simps)
    apply (cases cap')
               apply (simp_all add: drop_spec_ev throwError_ev_pre
                              cong: if_cong
                         split del: if_split)
    apply (wp "1.hyps")
                   apply (assumption | simp add: in_monad | rule conjI)+
           apply (wp get_cap_rev get_cap_wp whenE_throwError_wp)+
    apply (auto simp: cte_wp_at_caps_of_state is_cap_simps cap_auth_conferred_def
                dest: caps_of_state_pasObjectAbs_eq)
    done
qed

lemma resolve_address_bits_rev:
  "reads_equiv_valid_inv A aag
     (pas_refined aag and K (is_cnode_cap (fst ref) \<longrightarrow> is_subject aag (obj_ref_of (fst ref))))
     (resolve_address_bits ref)"
  by (rule use_spec_ev[OF resolve_address_bits_spec_rev])

lemma lookup_slot_for_thread_rev:
  "reads_equiv_valid_inv A aag (pas_refined aag and K (is_subject aag thread))
                         (lookup_slot_for_thread thread cptr)"
  unfolding lookup_slot_for_thread_def fun_app_def
  apply (rule gen_asm_ev)
  apply (wp resolve_address_bits_rev gets_the_ev | simp)+
  apply (rule conjI)
   apply blast
  apply (clarsimp simp: tcb.splits)
  apply (erule (2) owns_thread_owns_cspace)
   defer
   apply (case_tac tcb_ctablea, simp_all)
  done

lemma lookup_cap_and_slot_rev[wp]:
  "reads_equiv_valid_inv A aag (pas_refined aag and K (is_subject aag thread))
                         (lookup_cap_and_slot thread cptr)"
  unfolding lookup_cap_and_slot_def
  by (wp lookup_slot_for_thread_rev lookup_slot_for_thread_authorised get_cap_rev
      | simp add: split_def
      | strengthen aag_can_read_self)+

lemmas lookup_cap_and_slot_reads_respects_g =
  reads_respects_g_from_inv[OF lookup_cap_and_slot_rev lookup_cap_and_slot_inv]

lemma set_cap_reads_respects:
  "reads_respects aag l (K (aag_can_read aag (fst slot))) (set_cap cap slot)"
  by (wpsimp wp: set_object_reads_respects get_object_rev hoare_vcg_all_lift
           simp: set_cap_def split_def)

lemma set_original_reads_respects:
  "reads_respects aag l \<top> (set_original slot v)"
  unfolding set_original_def
  apply (unfold equiv_valid_def2)
  apply (rule_tac Q="\<top>\<top>" in equiv_valid_rv_bind)
    apply (rule gets_is_original_cap_revrv)
   apply (rule modify_ev2)
   apply (clarsimp simp: equiv_for_or or_comp_dist)
   apply (safe)
    apply (erule reads_equiv_is_original_cap_update)
    apply (erule equiv_for_id_update)
   apply (erule affects_equiv_is_original_cap_update)
   apply (erule equiv_for_id_update)
  apply wp
  done

lemma set_cdt_reads_respects:
  "reads_respects aag l \<top> (set_cdt c)"
  unfolding set_cdt_def
  apply (unfold equiv_valid_def2)
  apply (rule get_bind_ev2)
  apply (unfold fun_app_def, rule put_ev2)
  apply (fastforce intro: reads_equiv_cdt_update affects_equiv_cdt_update equiv_for_refl)
  done

lemma set_cdt_ev2:
  "equiv_for (((aag_can_read aag) or (aag_can_affect aag l)) \<circ> fst) id c c'
   \<Longrightarrow> equiv_valid_2 (reads_equiv aag) (affects_equiv aag l) (affects_equiv aag l)
                     (=) \<top> \<top> (set_cdt c) (set_cdt c')"
  unfolding set_cdt_def
  apply (rule get_bind_ev2)
  apply (unfold fun_app_def, rule put_ev2)
  apply (fastforce simp: equiv_for_or or_comp_dist reads_equiv_cdt_update affects_equiv_cdt_update)
  done

lemma set_cdt_list_ev2:
  "equiv_for (((aag_can_read aag) or (aag_can_affect aag l)) \<circ> fst) id c c'
   \<Longrightarrow> equiv_valid_2 (reads_equiv aag) (affects_equiv aag l) (affects_equiv aag l)
                     (=) \<top> \<top> (set_cdt_list c) (set_cdt_list c')"
  unfolding set_cdt_list_def
  apply (rule get_bind_ev2)
  apply (unfold fun_app_def, rule put_ev2)
  apply (fastforce simp: equiv_for_or or_comp_dist reads_equiv_cdt_list_update affects_equiv_cdt_list_update)
  done

lemma kheap_get_tcb_eq: "kheap s ref = kheap t ref \<Longrightarrow> get_tcb ref s = get_tcb ref t"
  by (simp add: get_tcb_def)

lemma thread_get_rev:
  "reads_equiv_valid_inv A aag (K (aag_can_read aag thread)) (thread_get f thread)"
  unfolding thread_get_def fun_app_def
  by (wp gets_the_ev) (fastforce intro: kheap_get_tcb_eq elim: reads_equivE equiv_forD)

lemma update_cdt_reads_respects:
  "reads_respects aag l (K (\<forall>rv rv'.
      equiv_for ((aag_can_read aag or aag_can_affect aag l) \<circ> fst) id rv rv' \<longrightarrow>
      equiv_for ((aag_can_read aag or aag_can_affect aag l) \<circ> fst) f rv rv'))
     (update_cdt f)"
  unfolding update_cdt_def
  apply (rule gen_asm_ev)
  apply (unfold equiv_valid_def2)
  apply (rule equiv_valid_rv_bind)
    apply (rule equiv_valid_rv_guard_imp[OF gets_cdt_revrv])
    apply (rule TrueI)
   apply (rule set_cdt_ev2)
   apply (simp add: equiv_for_comp[symmetric])
  apply wp
  done

lemma update_cdt_list_reads_respects:
  "reads_respects aag l (K  (\<forall>rv rv'.
      equiv_for ((aag_can_read aag or aag_can_affect aag l) \<circ> fst) id rv rv' \<longrightarrow>
      equiv_for ((aag_can_read aag or aag_can_affect aag l) \<circ> fst) f rv rv'))
     (update_cdt_list f)"
  unfolding update_cdt_list_def
  apply (rule gen_asm_ev)
  apply (unfold equiv_valid_def2)
  apply (rule equiv_valid_rv_bind)
    apply (rule equiv_valid_rv_guard_imp[OF gets_cdt_list_revrv])
    apply (rule TrueI)
   apply (rule set_cdt_list_ev2)
   apply (simp add: equiv_for_comp[symmetric])
  apply wp
  done

lemma gen_asm_ev2':
  assumes "Q \<Longrightarrow> Q' \<Longrightarrow> equiv_valid_2 I A B R P P' f f'"
  shows "equiv_valid_2 I A B R (P and K Q) (P' and K Q') f f'"
  using assms by (fastforce simp: equiv_valid_def2 equiv_valid_2_def)

lemmas gen_asm_ev2 = gen_asm_ev2'[where P=\<top> and P'=\<top>, simplified]

lemma set_untyped_cap_as_full_reads_respects:
  "reads_respects aag l (K (aag_can_read aag (fst src_slot)))
     (set_untyped_cap_as_full src_cap new_cap src_slot)"
  unfolding set_untyped_cap_as_full_def
  apply (wp set_cap_reads_respects)
  apply auto
  done

lemma gets_apply_wp[wp]:
  "\<lbrace>\<lambda>s. P (f s x) s\<rbrace> gets_apply f x \<lbrace>P\<rbrace>"
  by (wpsimp simp: gets_apply_def)

lemma cap_insert_reads_respects:
  notes split_paired_All[simp del]
  shows
  "reads_respects aag l (K (aag_can_read aag (fst src_slot) \<and> aag_can_read aag (fst dest_slot)))
                  (cap_insert new_cap src_slot dest_slot)"
  unfolding cap_insert_def
  apply (rule gen_asm_ev)
  apply (subst gets_apply)
  apply (simp only: cap_insert_ext_extended.dxo_eq)
  apply (simp only: cap_insert_ext_def)
  apply (wp set_original_reads_respects update_cdt_reads_respects set_cap_reads_respects
            gets_apply_ev update_cdt_list_reads_respects set_untyped_cap_as_full_reads_respects
            get_cap_wp get_cap_rev
         | simp split del: if_split
         | clarsimp simp: equiv_for_def split: option.splits)+
  by (fastforce simp: reads_equiv_def2 equiv_for_def
                elim: states_equiv_forE_is_original_cap states_equiv_forE_cdt
                dest: aag_can_read_self
               split: option.splits)

lemma cap_move_reads_respects:
  notes split_paired_All[simp del]
  shows
  "reads_respects aag l (K (is_subject aag (fst src_slot) \<and> is_subject aag (fst dest_slot)))
                  (cap_move new_cap src_slot dest_slot)"
  unfolding cap_move_def
  apply (subst gets_apply)
  apply (simp add: bind_assoc[symmetric])
  apply (fold update_cdt_def)
  apply (simp add: bind_assoc cap_move_ext_def)
  apply (rule gen_asm_ev)
  apply (elim conjE)
  apply (wp set_original_reads_respects gets_apply_ev update_cdt_reads_respects
            set_cap_reads_respects update_cdt_list_reads_respects
         | simp split del: if_split | fastforce simp: equiv_for_def split: option.splits)+
  apply (intro impI conjI allI)
  apply (fastforce simp: reads_equiv_def2 equiv_for_def
                   elim: states_equiv_forE_is_original_cap states_equiv_forE_cdt
                   dest: aag_can_read_self
                  split: option.splits)+
  done

lemma get_idemp:
  "do s1 \<leftarrow> get; s2 \<leftarrow> get; f s1 s2 od =
   do s1 \<leftarrow> get; f s1 s1 od"
  by (simp add: get_def bind_def)

lemma gets_apply2:
  "gets f >>= (\<lambda> f. g (f x) (f y)) =
   gets_apply f x >>= (\<lambda> x. gets_apply f y >>= (\<lambda> y. g x y))"
  by (simp add: gets_def gets_apply_def get_idemp)

lemma cap_swap_reads_respects:
  notes split_paired_All[simp del]
  shows "reads_respects aag l (K (is_subject aag (fst slot1) \<and> is_subject aag (fst slot2)))
                        (cap_swap cap1 slot1 cap2 slot2)"
  unfolding cap_swap_def
  apply (subst gets_apply2)
  apply (simp add: bind_assoc[symmetric])
  apply (fold update_cdt_def)
  apply (simp add: bind_assoc cap_swap_ext_def)
  apply (rule gen_asm_ev)
  apply (wp set_original_reads_respects update_cdt_reads_respects gets_apply_ev
            set_cap_reads_respects update_cdt_list_reads_respects
         | simp split del: if_split | fastforce simp: equiv_for_def split: option.splits)+
  apply (intro impI conjI allI)
      apply ((fastforce simp: reads_equiv_def2 equiv_for_def
                        elim: states_equiv_forE_is_original_cap states_equiv_forE_cdt
                        dest: aag_can_read_self
                       split: option.splits)+)[2]
    apply (frule_tac x = slot1 in equiv_forD, elim conjE,drule aag_can_read_self,simp)
    apply (frule_tac x = slot2 in equiv_forD, elim conjE)
     apply (drule aag_can_read_self)+
     apply clarsimp
    apply clarsimp
    apply (erule equiv_forE)
    apply (fastforce intro: equiv_forI)
   apply (fastforce simp: equiv_for_def dest: aag_can_read_self
                    elim: reads_equivE equiv_forE[where f="is_original_cap"] split: option.splits)+
  done

lemma tcb_at_def2: "tcb_at ptr s = (\<exists>tcb. kheap s ptr = Some (TCB tcb))"
  by (clarsimp simp: tcb_at_def get_tcb_def split: kernel_object.splits option.splits)

lemma set_cdt_globals_equiv:
  "set_cdt c \<lbrace>globals_equiv s\<rbrace>"
  unfolding set_cdt_def
  by (wpsimp simp: globals_equiv_def idle_equiv_def)

lemma update_cdt_globals_equiv:
  "update_cdt f \<lbrace>globals_equiv s\<rbrace>"
  unfolding update_cdt_def
  by (wp set_cdt_globals_equiv)

declare set_original_wp[wp del]

lemma set_original_globals_equiv:
  "set_original slot v \<lbrace>globals_equiv s\<rbrace>"
  unfolding set_original_def
  by (wpsimp simp: globals_equiv_def idle_equiv_def)

lemma globals_equiv_exst_update[simp]:
  "globals_equiv st (trans_state f s) =
   globals_equiv st s"
  by (simp add: globals_equiv_def idle_equiv_def)

lemma (in is_extended') globals_equiv: "I (globals_equiv st)" by (rule lift_inv,simp)

lemma domain_sep_inv_refl:
  "domain_sep_inv irqs st s \<Longrightarrow> domain_sep_inv irqs s s"
  by (fastforce simp: domain_sep_inv_def)


locale CNode_IF_1 =
  fixes state_ext_t :: "'s :: state_ext itself"
  and irq_at :: "nat \<Rightarrow> (irq \<Rightarrow> bool) \<Rightarrow> irq option"
  assumes set_cap_globals_equiv:
    "\<lbrace>globals_equiv s and valid_global_objs and valid_arch_state\<rbrace>
     set_cap cap p
     \<lbrace>\<lambda>_. globals_equiv s\<rbrace>"
  and dmo_getActiveIRQ_wp:
    "\<lbrace>\<lambda>s. P (irq_at (irq_state (machine_state s) + 1) (irq_masks (machine_state s)))
            (s\<lparr>machine_state := machine_state s\<lparr>irq_state := irq_state (machine_state s) + 1\<rparr>\<rparr>)\<rbrace>
     do_machine_op (getActiveIRQ in_kernel)
     \<lbrace>\<lambda>rv s :: 's state. P rv s\<rbrace>"
  and arch_globals_equiv_irq_state_update[simp]:
    "arch_globals_equiv ct it kh kh' as as' ms (irq_state_update f ms') =
     arch_globals_equiv ct it kh kh' as as' ms ms'"
    "arch_globals_equiv ct it kh kh' as as' (irq_state_update f ms) ms' =
     arch_globals_equiv ct it kh kh' as as' ms ms'"
begin

crunch globals_equiv[wp]: set_untyped_cap_as_full "globals_equiv st"

lemma cap_insert_globals_equiv:
  "\<lbrace>globals_equiv s and valid_global_objs and valid_arch_state\<rbrace>
   cap_insert new_cap src_slot dest_slot
   \<lbrace>\<lambda>_. globals_equiv s\<rbrace>"
  unfolding cap_insert_def fun_app_def
  by (wpsimp wp: update_cdt_globals_equiv set_original_globals_equiv
                 set_cap_globals_equiv hoare_drop_imps dxo_wp_weak)

lemma cap_move_globals_equiv:
  "\<lbrace>globals_equiv s and valid_global_objs and valid_arch_state\<rbrace>
   cap_move new_cap src_slot dest_slot
   \<lbrace>\<lambda>_. globals_equiv s\<rbrace>"
  unfolding cap_move_def fun_app_def
  by (wpsimp wp: set_original_globals_equiv set_cdt_globals_equiv set_cap_globals_equiv dxo_wp_weak)

lemma cap_swap_globals_equiv:
  "\<lbrace>globals_equiv s and valid_global_objs and valid_arch_state\<rbrace>
   cap_swap cap1 slot1 cap2 slot2
   \<lbrace>\<lambda>_. globals_equiv s\<rbrace>"
  unfolding cap_swap_def
  by (wpsimp wp: set_original_globals_equiv set_cdt_globals_equiv set_cap_globals_equiv dxo_wp_weak)

definition is_irq_at :: "('z::state_ext) state \<Rightarrow> irq \<Rightarrow> nat \<Rightarrow> bool" where
  "is_irq_at s \<equiv> \<lambda>irq pos. irq_at pos (irq_masks (machine_state s)) = Some irq"

text \<open>
  We require that interrupts recur in order to ensure that no individual
  big step ever diverges.
\<close>
definition irq_is_recurring :: "irq \<Rightarrow> ('z::state_ext) state \<Rightarrow> bool" where
  "irq_is_recurring irq s \<equiv> \<forall>n. (\<exists>m. is_irq_at s irq (n+m))"

text \<open>
  There is only one interrupt turned on, namely @{term irq}, and it is
  a timer interrupt.
\<close>
definition only_timer_irq :: "irq \<Rightarrow> 'z::state_ext state \<Rightarrow> bool" where
  "only_timer_irq irq s \<equiv> (\<forall>x. interrupt_states s x = IRQTimer \<longrightarrow> x = irq) \<and> irq_is_recurring irq s"

end


locale CNode_IF_2 = CNode_IF_1 state_ext_t
  for state_ext_t :: "'s :: state_ext itself"
  and f :: "('s state, 'a) nondet_monad" +
  assumes is_irq_at_triv:
    "(\<And>P. \<lbrace>(\<lambda>s. P (irq_masks (machine_state s))) and Q\<rbrace>
           f
           \<lbrace>\<lambda>rv s. P (irq_masks (machine_state s))\<rbrace>)
     \<Longrightarrow> \<lbrace>(\<lambda>s. P (is_irq_at s)) and Q\<rbrace>
         f
         \<lbrace>\<lambda>rv s. P (is_irq_at s)\<rbrace>"
  and is_irq_at_not_masked:
    "is_irq_at (s :: det_state) irq pos \<Longrightarrow> \<not> irq_masks (machine_state s) irq"
begin

lemma irq_is_recurring_triv:
  assumes a: "\<And>P. \<lbrace>(\<lambda>s. P (irq_masks (machine_state s))) and Q\<rbrace>
                   (f :: ('s state, 'a) nondet_monad)
                   \<lbrace>\<lambda>rv s. P (irq_masks (machine_state s))\<rbrace>"
  shows "\<lbrace>irq_is_recurring irq and Q\<rbrace> f \<lbrace>\<lambda>_. irq_is_recurring irq\<rbrace>"
  apply (clarsimp simp: irq_is_recurring_def valid_def)
  apply (rule use_valid[OF _ is_irq_at_triv[OF a]])
   apply assumption
  apply simp
  done

lemma domain_sep_inv_to_interrupt_states_pres:
  assumes a: "\<And>st. \<lbrace>domain_sep_inv False (st :: 's state) and P\<rbrace> f \<lbrace>\<lambda>_. domain_sep_inv False st\<rbrace>"
  shows "\<lbrace>(\<lambda>s. Q (interrupt_states s)) and P and domain_sep_inv False st\<rbrace>
         f
         \<lbrace>\<lambda>_ s. Q (interrupt_states s)\<rbrace>"
  apply (clarsimp simp: valid_def)
   apply (erule use_valid)
   apply (rule hoare_strengthen_post)
    apply (rule_tac st3=s in a)
   apply (fastforce simp: domain_sep_inv_def)
  apply (simp add: domain_sep_inv_refl)
  done

lemma only_timer_irq_pres:
  assumes a: "\<And>P. \<lbrace>(\<lambda>s. P (irq_masks (machine_state s))) and Q\<rbrace>
                   f
                   \<lbrace>\<lambda>rv s. P (irq_masks (machine_state s))\<rbrace>"
  assumes b:
    "\<And>st :: 's state. \<lbrace>domain_sep_inv False st and P\<rbrace> f \<lbrace>\<lambda>_. domain_sep_inv False st\<rbrace>"
  shows
    "\<lbrace>only_timer_irq irq and Q and P and (\<lambda>s. domain_sep_inv False (st :: 's state) s)\<rbrace>
     f
     \<lbrace>\<lambda>_. only_timer_irq irq\<rbrace>"
  apply (clarsimp simp: only_timer_irq_def valid_def)
  apply (rule conjI)
   apply (clarsimp)
   apply (drule spec)
   apply (erule mp)
   apply (erule contrapos_pp, erule use_valid, rule domain_sep_inv_to_interrupt_states_pres, rule b, fastforce)
  apply (erule use_valid[OF _ irq_is_recurring_triv[OF a]])
  by simp

definition only_timer_irq_inv :: "irq \<Rightarrow> 'z :: state_ext state \<Rightarrow> 'z state \<Rightarrow> bool" where
  "only_timer_irq_inv irq st \<equiv> domain_sep_inv False st and only_timer_irq irq"

lemma only_timer_irq_inv_pres:
  assumes a:
    "\<And>P. \<lbrace>(\<lambda>s. P (irq_masks (machine_state s))) and Q\<rbrace>
          f
          \<lbrace>\<lambda>rv s. P (irq_masks (machine_state s))\<rbrace>"
  assumes b: "\<And>st :: 's state. \<lbrace>domain_sep_inv False st and P\<rbrace> f \<lbrace>\<lambda>_. domain_sep_inv False st\<rbrace>"
  shows "\<lbrace>only_timer_irq_inv irq (st :: 's state) and Q and P\<rbrace> f \<lbrace>\<lambda>_. only_timer_irq_inv irq st\<rbrace>"
  apply (clarsimp simp: only_timer_irq_inv_def valid_def)
  apply (rule conjI)
   apply (erule use_valid[OF _ b], simp)
  apply (erule use_valid[OF _ only_timer_irq_pres[OF a b]])
  apply simp
  done

lemma only_timer_irq_inv_domain_sep_inv[intro]:
  "only_timer_irq_inv irq st s \<Longrightarrow> domain_sep_inv False st s"
  by (simp add: only_timer_irq_inv_def)

lemma irq_is_recurring_not_masked:
  "irq_is_recurring irq (s :: det_state) \<Longrightarrow> \<not> irq_masks (machine_state s) irq"
  apply (clarsimp simp: irq_is_recurring_def)
  apply (blast dest: is_irq_at_not_masked)
  done

lemma only_timer_irq_inv_determines_irq_masks:
  "\<lbrakk>invs (s :: det_state); only_timer_irq_inv irq st s\<rbrakk> \<Longrightarrow>
   \<not> irq_masks (machine_state s) irq \<and>
   (\<forall>x. x \<noteq> irq \<longrightarrow> irq_masks (machine_state s) x)"
  apply (rule conjI)
   apply (clarsimp simp: only_timer_irq_inv_def only_timer_irq_def)
   apply (blast dest: irq_is_recurring_not_masked)
  apply (clarsimp simp: only_timer_irq_inv_def domain_sep_inv_def only_timer_irq_def)
  apply (case_tac "interrupt_states s x")
    apply (fastforce simp: invs_def valid_state_def valid_irq_states_def valid_irq_masks_def)
   apply fastforce+
  done

lemma dmo_getActiveIRQ_globals_equiv:
  "\<lbrace>globals_equiv st\<rbrace> do_machine_op (getActiveIRQ in_kernel) \<lbrace>\<lambda>_. globals_equiv st\<rbrace>"
  apply (wp dmo_getActiveIRQ_wp)
  apply (auto simp: globals_equiv_def idle_equiv_def)
  done

crunches reset_work_units, work_units_limit_reached, update_work_units
  for only_timer_irq_inv[wp]: "only_timer_irq_inv irq st"
  (simp: only_timer_irq_inv_def only_timer_irq_def irq_is_recurring_def is_irq_at_def)

end


lemma gets_irq_masks_equiv_valid:
  "equiv_valid_inv I A (\<lambda>s. \<not> (irq_masks s) irq \<and> (\<forall>x. x \<noteq> irq \<longrightarrow> (irq_masks s) x)) (gets irq_masks)"
  by (fastforce simp: equiv_valid_def2 equiv_valid_2_def in_monad)

lemma irq_state_increment_reads_respects_memory:
  "equiv_valid_inv (equiv_machine_state P and equiv_irq_state)
                   (equiv_for (\<lambda>x. aag_can_affect_label aag l \<and>
                                   pasObjectAbs aag x \<in> subjectReads (pasPolicy aag) l)
                              underlying_memory)
                   \<top> (modify (\<lambda>s. s\<lparr>irq_state := Suc (irq_state s)\<rparr>))"
  apply (simp add: equiv_valid_def2)
  apply (rule modify_ev2)
  apply (fastforce intro: equiv_forI elim: equiv_forE)
  done

lemma irq_state_increment_reads_respects_device:
  "equiv_valid_inv (equiv_machine_state P and equiv_irq_state)
                   (equiv_for (\<lambda>x. aag_can_affect_label aag l \<and>
                                   pasObjectAbs aag x \<in> subjectReads (pasPolicy aag) l)
                              device_state)
                   \<top> (modify (\<lambda>s. s\<lparr>irq_state := Suc (irq_state s)\<rparr>))"
  apply (simp add: equiv_valid_def2)
  apply (rule modify_ev2)
  apply (fastforce intro: equiv_forI elim: equiv_forE)
  done

lemma use_equiv_valid_inv:
  "\<lbrakk> x \<in> fst (f st); y \<in> fst (f s); g s; g st; I s st; P s st; equiv_valid_inv I P g f \<rbrakk>
     \<Longrightarrow> fst x = fst y \<and> P (snd y) (snd x) \<and> I (snd y) (snd x)"
  apply (clarsimp simp add: equiv_valid_def spec_equiv_valid_def equiv_valid_2_def)
  apply (drule spec)+
  apply (erule impE)
   apply fastforce
  apply (drule(1) bspec | clarsimp)+
  done

lemma equiv_valid_inv_conj_lift:
  assumes P: "equiv_valid_inv I (\<lambda>s s'. P s s') g f"
      and P': "equiv_valid_inv I (\<lambda>s s'. P' s s') g f"
  shows "equiv_valid_inv I (\<lambda>s s'. P s s' \<and> P' s s') g f"
  apply (clarsimp simp add: equiv_valid_def spec_equiv_valid_def equiv_valid_2_def)
  apply (frule_tac st = t and s = st in use_equiv_valid_inv[OF _ _ _ _ _ _ P])
       apply fastforce+
  apply (frule_tac st = t and s = st in use_equiv_valid_inv[OF _ _ _ _ _ _ P'])
       apply fastforce+
  done

lemma reset_work_units_reads_respects[wp]:
  "reads_respects aag l \<top> reset_work_units"
  apply (simp add: reset_work_units_def equiv_valid_def2)
  apply (rule modify_ev2)
  apply (force intro: reads_equiv_work_units_completed_update affects_equiv_work_units_completed_update)
  done

lemma update_work_units_reads_respects[wp]:
  "reads_respects aag l \<top> update_work_units"
  apply (simp add: update_work_units_def equiv_valid_def2)
  apply (rule modify_ev2)
  apply (force intro: reads_equiv_work_units_completed_update' affects_equiv_work_units_completed_update')
  done

lemma work_units_limit_reached_reads_respects[wp]:
  "reads_respects aag l \<top> work_units_limit_reached"
  apply (simp add: work_units_limit_reached_def, wp)
  apply force
  done

crunch invs[wp]: work_units_limit_reached invs

lemma preemption_point_def2:
  "(preemption_point :: (unit,det_ext) p_monad) =
   doE liftE update_work_units;
       rv \<leftarrow> liftE work_units_limit_reached;
       if rv then doE liftE reset_work_units;
                      liftE (do_machine_op (getActiveIRQ True)) >>=E
                      case_option (returnOk ()) (K (throwError $ ()))
                  odE
       else returnOk()
   odE"
  apply (rule ext)
  apply (simp add: preemption_point_def OR_choiceE_def wrap_ext_bool_det_ext_ext_def
                   ef_mk_ef work_units_limit_reached_def select_f_def empty_fail_cond)
  apply (clarsimp simp: work_units_limit_reached_def gets_def liftE_def select_f_def get_def
                        lift_def return_def bind_def bindE_def split_def image_def
                 split: option.splits sum.splits)
  done

lemma all_children_descendants_equal:
  "\<lbrakk> equiv_for P id s t; all_children P s; all_children P t; P slot \<rbrakk>
     \<Longrightarrow> descendants_of slot s = descendants_of slot t"
  apply (clarsimp | rule equalityI)+
   apply (frule_tac p="(a, b)" and q="slot" and m=s in all_children_descendants_of)
     apply (simp)+
   apply (simp add: descendants_of_def cdt_parent_rel_def is_cdt_parent_def)
   apply (rule_tac r'="{(p, c). s c = Some p}" and Q="P" in trancl_subset_equivalence)
     apply (clarsimp)+
    apply (frule_tac p="(aa, ba)" and q="slot" in all_children_descendants_of)
      apply (fastforce simp: descendants_of_def cdt_parent_rel_def is_cdt_parent_def)+
   apply (fastforce simp: equiv_for_def)
  apply (clarsimp)
  apply (frule_tac p="(a, b)" and q="slot" and m=t in all_children_descendants_of)
    apply (simp)+
  apply (simp add: descendants_of_def cdt_parent_rel_def is_cdt_parent_def)
  apply (rule_tac r'="{(p, c). t c = Some p}" and Q="P" in trancl_subset_equivalence)
    apply (clarsimp)+
   apply (frule_tac p="(aa, ba)" and q="slot" and m=t in all_children_descendants_of)
     apply (fastforce simp: equiv_for_def descendants_of_def cdt_parent_rel_def is_cdt_parent_def)+
  done

lemma cca_can_read:
  "\<lbrakk> valid_mdb s; valid_objs s; cdt_change_allowed' aag slot s; pas_refined aag s \<rbrakk>
     \<Longrightarrow> aag_can_read aag (fst slot)"
  apply (frule(3) cdt_change_allowed_delete_derived)
  by (rule read_delder_thread_read_thread_rev[OF reads_lrefl])

lemma all_children_subjectReads:
  "pas_refined aag s \<Longrightarrow> all_children (aag_can_read aag \<circ> fst) (cdt s)"
  apply (rule all_childrenI)
  apply simp
  apply (erule read_delder_thread_read_thread_rev)
  by (rule aag_cdt_link_DeleteDerived)

lemma descendants_of_eq:
  "\<lbrakk> reads_equiv aag s t; affects_equiv aag l s t;
     pas_refined aag s; pas_refined aag t; is_subject aag (fst slot) \<rbrakk>
   \<Longrightarrow> descendants_of slot (cdt s) = descendants_of slot (cdt t)"
  apply (rule all_children_descendants_equal[OF _ all_children_subjectReads all_children_subjectReads])
     apply (elim reads_equivE)
     apply (solves\<open>simp add: equiv_for_apply\<close>)
  by force+

lemma gets_descendants_of_revrv:
  "reads_equiv_valid_rv_inv (affects_equiv aag l) aag (=)
                            (pas_refined aag and K (is_subject aag (fst slot)))
                            (gets (descendants_of slot \<circ> cdt))"
  apply (rule gets_evrv'')
  apply clarsimp
  by (rule descendants_of_eq)

lemma silc_dom_equiv_trans[elim]:
  "\<lbrakk> silc_dom_equiv aag s t; silc_dom_equiv aag t u \<rbrakk>
     \<Longrightarrow> silc_dom_equiv aag s u"
  by (auto simp: silc_dom_equiv_def elim: equiv_for_trans)

lemma silc_dom_equiv_sym[elim]:
  "silc_dom_equiv aag s t \<Longrightarrow> silc_dom_equiv aag t s"
  by (auto simp: silc_dom_equiv_def elim: equiv_for_sym)

lemma reads_respects_f:
  "\<lbrakk> reads_respects aag l P f; \<lbrace>silc_inv aag st and Q\<rbrace> f \<lbrace>\<lambda>_. silc_inv aag st\<rbrace> \<rbrakk>
     \<Longrightarrow> reads_respects_f aag l (silc_inv aag st and P and Q) f"
  apply (clarsimp simp: equiv_valid_def2 equiv_valid_2_def reads_equiv_f_def)
  apply (rule conjI, fastforce)
  apply (rule conjI, fastforce)
  apply (subst conj_commute, rule conjI, fastforce)
  apply (rule silc_dom_equiv_trans)
   apply (rule silc_dom_equiv_sym)
   apply (rule silc_inv_silc_dom_equiv)
   apply (erule (1) use_valid, simp)
  apply (rule silc_inv_silc_dom_equiv)
  apply (erule (1) use_valid, simp)
  done


locale CNode_IF_3 = CNode_IF_2 +
  fixes aag :: "'a subject_label PAS"
  assumes dmo_getActiveIRQ_reads_respects:
    "reads_respects aag l (invs and only_timer_irq_inv irq st) (do_machine_op (getActiveIRQ in_kernel))"
begin

lemma dmo_getActiveIRQ_reads_respects_g :
  "reads_respects_g aag l (invs and only_timer_irq_inv irq st) (do_machine_op (getActiveIRQ in_kernel))"
  apply (rule equiv_valid_guard_imp[OF reads_respects_g])
    apply (rule dmo_getActiveIRQ_reads_respects)
   apply (rule doesnt_touch_globalsI)
   apply (wp dmo_getActiveIRQ_globals_equiv | blast)+
  done

lemma preemption_point_reads_respects:
  "reads_respects aag l (invs and only_timer_irq_inv irq st) preemption_point"
  apply (simp add: preemption_point_def2)
  apply (wp | wpc | simp add: comp_def)+
           apply ((wp dmo_getActiveIRQ_reads_respects hoare_TrueI | simp
                   | wp (once) hoare_drop_imps)+)[8]
   apply wp
  apply force
  done

lemma preemption_point_reads_respects_f:
  "reads_respects_f aag l (invs and only_timer_irq_inv irq st' and silc_inv aag st) preemption_point"
  apply (rule equiv_valid_guard_imp)
   apply (rule reads_respects_f)
    apply (rule preemption_point_reads_respects)
   apply (wp, force+)
  done

end


abbreviation
  reads_spec_equiv_valid_f ::
    "det_state \<Rightarrow> (det_state \<Rightarrow> det_state \<Rightarrow> bool) \<Rightarrow> (det_state \<Rightarrow> det_state \<Rightarrow> bool) \<Rightarrow>
    'a subject_label PAS \<Rightarrow> (det_state \<Rightarrow> bool) \<Rightarrow> (det_state, 'b) nondet_monad \<Rightarrow> bool" where
  "reads_spec_equiv_valid_f s A B aag P f \<equiv> spec_equiv_valid s (reads_equiv_f aag) A B P f"

abbreviation reads_spec_equiv_valid_f_inv
where
  "reads_spec_equiv_valid_f_inv s A aag P f \<equiv> reads_spec_equiv_valid_f s A A aag P f"

abbreviation
  spec_reads_respects_f :: "det_state \<Rightarrow> 'a subject_label PAS \<Rightarrow> 'a subject_label \<Rightarrow>
                            (det_state \<Rightarrow> bool) \<Rightarrow> (det_state,'b) nondet_monad \<Rightarrow> bool" where
  "spec_reads_respects_f s aag l P f \<equiv> reads_spec_equiv_valid_f_inv s (affects_equiv aag l) aag P f"

definition reads_equiv_f_g where
  "reads_equiv_f_g aag s s' \<equiv> reads_equiv aag s s' \<and> globals_equiv s s' \<and> silc_dom_equiv aag s s'"

abbreviation reads_respects_f_g :: "'a subject_label PAS \<Rightarrow> 'a subject_label \<Rightarrow>
                                    (det_state \<Rightarrow> bool) \<Rightarrow> (det_state,'b) nondet_monad \<Rightarrow> bool" where
"reads_respects_f_g aag l P f \<equiv>
   equiv_valid_inv (reads_equiv_f_g aag) (affects_equiv aag l) P f"

lemma reads_equiv_f_g_conj:
  "reads_equiv_f_g aag s s' \<equiv> reads_equiv_f aag s s' \<and> reads_equiv_g aag s s'"
  by (simp add: reads_equiv_f_g_def reads_equiv_f_def reads_equiv_g_def conj_comms)

lemma reads_respects_f_g:
  "\<lbrakk> reads_respects_f aag l P f; doesnt_touch_globals Q f \<rbrakk>
     \<Longrightarrow> reads_respects_f_g aag l (P and Q) f"
  apply (clarsimp simp: equiv_valid_def2 equiv_valid_2_def)
  apply (subst (asm) reads_equiv_f_g_conj, erule conjE)
  apply (subst reads_equiv_f_g_conj)
  apply (drule reads_equiv_gD)
  apply clarsimp
  apply (subgoal_tac "reads_equiv_g aag b ba", blast)
  apply (subgoal_tac "globals_equiv b ba", fastforce intro: reads_equiv_gI simp: reads_equiv_f_def)
  apply (rule globals_equiv_trans)
   apply (rule globals_equiv_sym)
   apply (fastforce intro: globals_equivI)
  apply (rule globals_equiv_trans)
   apply (assumption)
  apply (fastforce intro: globals_equivI)
  done

lemma reads_equiv_valid_rv_inv_f:
  assumes a: "reads_equiv_valid_rv_inv A aag R P f"
  assumes b: "\<And>P. \<lbrace>P\<rbrace> f \<lbrace>\<lambda>_. P\<rbrace>"
  shows "equiv_valid_rv_inv (reads_equiv_f aag) A R P f"
  apply (clarsimp simp: equiv_valid_2_def reads_equiv_f_def)
  apply (insert a, clarsimp simp: equiv_valid_2_def)
  apply (drule_tac x=s in spec, drule_tac x=t in spec, clarsimp)
  apply (drule (1) bspec, clarsimp)
  apply (drule (1) bspec, clarsimp)
  apply (drule state_unchanged[OF b])+
  by simp

lemma set_cap_silc_inv':
  "\<lbrace>silc_inv aag st and (\<lambda>s. \<not> cap_points_to_label aag cap (pasObjectAbs aag (fst slot))
                             \<longrightarrow> (\<exists>lslot. lslot \<in> slots_holding_overlapping_caps cap s \<and>
                                          pasObjectAbs aag (fst lslot) = SilcLabel))
                    and K (pasObjectAbs aag (fst slot) \<noteq> SilcLabel)\<rbrace>
   set_cap cap slot
   \<lbrace>\<lambda>_. silc_inv aag st\<rbrace>"
  apply (rule hoare_pre)
   apply (rule set_cap_silc_inv)
  by blast

lemma set_cap_reads_respects_f:
  "reads_respects_f aag l
     (silc_inv aag st and (\<lambda>s. \<not> cap_points_to_label aag cap (pasObjectAbs aag (fst slot))
                               \<longrightarrow> (\<exists>lslot. lslot \<in> slots_holding_overlapping_caps cap s \<and>
                                            pasObjectAbs aag (fst lslot) = SilcLabel))
                      and K (is_subject aag (fst slot))) (set_cap cap slot)"
  apply (rule equiv_valid_guard_imp)
   apply (rule reads_respects_f[OF set_cap_reads_respects])
   apply (wp set_cap_silc_inv')
   apply (auto simp: silc_inv_def)
  done

lemma select_ext_ev:
  "(\<And>s t. I s t \<Longrightarrow> A s t \<Longrightarrow> a s \<in> S \<Longrightarrow> a t \<in> S \<Longrightarrow> a s = a t)
   \<Longrightarrow> equiv_valid_inv I A (\<top> :: det_state \<Rightarrow> bool) (select_ext a S)"
  apply (clarsimp simp: select_ext_def gets_def get_def assert_def return_def bind_def)
  apply (simp add: equiv_valid_def2 equiv_valid_2_def return_def fail_def)
  done

end
