(* * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) * * SPDX-License-Identifier: GPL-2.0-only *) (* * Operations on CSpace *) theory CSpace_D imports PageTableUnmap_D begin (* Does the given cap have any children? *) definition has_children :: "cdl_cap_ref \ cdl_state \ bool" where "has_children parent s = (\child. is_cdt_parent s parent child)" (* * Ensure that the given cap does not contain any children * in the CDT. *) definition ensure_no_children :: "cdl_cap_ref \ unit except_monad" where "ensure_no_children x \ doE c \ liftE $ gets (has_children x); whenE c $ throw odE" (* Ensure that the given cap slot is empty. *) definition ensure_empty :: "cdl_cap_ref \ unit except_monad" where "ensure_empty cap_ref \ doE cap \ liftE $ get_cap cap_ref; unlessE (cap = NullCap) $ throw odE" (* Insert a new cap into an object. The cap will have no parent. *) definition insert_cap_orphan :: "cdl_cap \ cdl_cap_ref \ unit k_monad" where "insert_cap_orphan new_cap dest_slot \ do old_cap \ get_cap dest_slot; assert (old_cap = NullCap); set_cap dest_slot new_cap od" primrec (nonexhaustive) available_range :: "cdl_cap \ cdl_object_id set" where "available_range (UntypedCap _ r available) = available" definition set_available_range :: "cdl_cap \ cdl_object_id set \ cdl_cap" where "set_available_range cap nrange \ case cap of UntypedCap d r available \ UntypedCap d r nrange | _ \ cap" lemmas set_avaiable_range_simps[simp] = set_available_range_def[split_simps cdl_cap.split] definition set_untyped_cap_as_full :: "cdl_cap \ cdl_cap \ cdl_cap_ref \ unit k_monad" where "set_untyped_cap_as_full src_cap new_cap src_slot \ if (is_untyped_cap src_cap \ is_untyped_cap new_cap \ cap_objects src_cap = cap_objects new_cap) then (set_cap src_slot (set_available_range src_cap {})) else return ()" (* Insert a new cap into an object. The cap will be a sibling. *) definition insert_cap_sibling :: "cdl_cap \ cdl_cap_ref \ cdl_cap_ref \ unit k_monad" where "insert_cap_sibling new_cap src_slot dest_slot \ do src_cap \ get_cap src_slot; old_cap \ get_cap dest_slot; assert (old_cap = NullCap); set_untyped_cap_as_full src_cap new_cap src_slot; set_cap dest_slot new_cap; p \ gets $ opt_parent src_slot; case p of None \ return () | Some parent \ set_parent dest_slot parent od" (* Insert a new cap into an object. The cap will be a child. *) definition insert_cap_child :: "cdl_cap \ cdl_cap_ref \ cdl_cap_ref \ unit k_monad" where "insert_cap_child new_cap src_slot dest_slot \ do src_cap \ get_cap src_slot; old_cap \ get_cap dest_slot; assert (old_cap = NullCap); set_untyped_cap_as_full src_cap new_cap src_slot; set_cap dest_slot new_cap; set_parent dest_slot src_slot od" (* * Delete an ASID pool. *) definition delete_asid_pool :: "cdl_cnode_index \ cdl_object_id \ unit k_monad" where "delete_asid_pool base ptr \ do asid_table \ gets cdl_asid_table; asid_table' \ return $ asid_table (base \ NullCap); modify (\s. s \cdl_asid_table := asid_table'\) od \ return ()" (* * Delete a particular ASID, decactivating the PD using it * in the process. *) definition delete_asid :: "cdl_asid \ cdl_object_id \ unit k_monad" where "delete_asid asid pd \ do asid_table \ gets cdl_asid_table; case asid_table (fst asid) of Some NullCap \ return () | Some (AsidPoolCap p _) \ set_cap (p, (snd asid)) NullCap | _ \ fail od \ return ()" definition get_irq_slot :: "cdl_irq \ cdl_state \ cdl_cap_ref" where "get_irq_slot irq s \ (cdl_irq_node s irq, 0)" text \Actions to be taken after deleting an IRQ Handler capability.\ definition deleting_irq_handler :: "cdl_irq \ unit k_monad" where "deleting_irq_handler irq \ gets (get_irq_slot irq) >>= delete_cap_simple" definition cancel_ipc ::"cdl_object_id \ unit k_monad" where "cancel_ipc ptr \ do cap \ KHeap_D.get_cap (ptr,tcb_pending_op_slot); (case cap of PendingSyncRecvCap _ is_reply _ \ ( do when is_reply $ update_thread_fault ptr (\x. False); revoke_cap_simple (ptr,tcb_replycap_slot); when (\ is_reply) $ set_cap (ptr,tcb_pending_op_slot) NullCap od ) | PendingSyncSendCap _ _ _ _ _ _ \ (do revoke_cap_simple (ptr,tcb_replycap_slot); set_cap (ptr,tcb_pending_op_slot) NullCap od) | PendingNtfnRecvCap _ \ (do revoke_cap_simple (ptr,tcb_replycap_slot); set_cap (ptr, tcb_pending_op_slot) NullCap od) | _ \ return ()) od" definition prepare_thread_delete ::"cdl_object_id \ unit k_monad" where "prepare_thread_delete ptr \ return ()" (* for ARM it does nothing *) text \Actions that must be taken when a capability is deleted. Returns a Zombie capability if deletion requires a long-running operation and also a possible IRQ to be cleared.\ fun finalise_cap :: "cdl_cap \ bool \ (cdl_cap \ cdl_cap) k_monad" where "finalise_cap NullCap final = return (NullCap, NullCap)" | "finalise_cap RestartCap final = return (NullCap, NullCap)" | "finalise_cap (UntypedCap dev r a) final = return (NullCap, NullCap)" | "finalise_cap (EndpointCap r b R) final = (liftM (K (NullCap, NullCap)) $ when final $ cancel_all_ipc r)" | "finalise_cap (NotificationCap r b R) final = (liftM (K (NullCap, NullCap)) $ when final $ do unbind_maybe_notification r; cancel_all_ipc r od)" | "finalise_cap (ReplyCap r R) final = return (NullCap, NullCap)" | "finalise_cap (MasterReplyCap r) final = return (NullCap, NullCap)" | "finalise_cap (CNodeCap r bits g sz) final = (return (if final then ZombieCap r else NullCap, NullCap))" | "finalise_cap (TcbCap r) final = (do when final $ (do unbind_notification r; cancel_ipc r; KHeap_D.set_cap (r, tcb_pending_op_slot) cdl_cap.NullCap; prepare_thread_delete r od); return (if final then (ZombieCap r) else NullCap, NullCap) od)" | "finalise_cap (PendingSyncSendCap r _ _ _ _ _) final = return (NullCap, NullCap)" | "finalise_cap (PendingSyncRecvCap r _ _) final = return (NullCap, NullCap)" | "finalise_cap (PendingNtfnRecvCap r) final = return (NullCap, NullCap)" | "finalise_cap IrqControlCap final = return (NullCap, NullCap)" | "finalise_cap (IrqHandlerCap irq) final = ( if final then do deleting_irq_handler irq; return (NullCap, (IrqHandlerCap irq)) od else return (NullCap, NullCap))" | "finalise_cap (ZombieCap r) final = (do assert final; return (ZombieCap r, NullCap) od)" | "finalise_cap (AsidPoolCap ptr asid) final = ( if final then do delete_asid_pool asid ptr; return (NullCap, NullCap) od else return (NullCap, NullCap))" | "finalise_cap AsidControlCap final = return (NullCap,NullCap)" | "finalise_cap (PageDirectoryCap ptr x (Some asid)) final = ( if final \ x = Real then do delete_asid asid ptr; return (NullCap, NullCap) od else return (NullCap, NullCap))" | "finalise_cap (PageTableCap ptr x (Some asid)) final = ( if (final \ x = Real) then do unmap_page_table asid ptr; return (NullCap, NullCap) od else return (NullCap, NullCap))" | "finalise_cap (FrameCap dev ptr _ s x (Some asid)) final = ( if x = Real then do unmap_page asid ptr s; return (NullCap, NullCap) od else return (NullCap, NullCap))" | "finalise_cap _ final = return (NullCap, NullCap)" text \The fast_finalise operation is used to delete a capability when it is known that a long-running operation is impossible. It is equivalent to calling the regular finalise operation. It cannot be defined in that way as doing so would create a circular definition.\ lemma fast_finalise_def2: "fast_finalise cap final = do assert (can_fast_finalise cap); result \ finalise_cap cap final; assert (result = (NullCap, NullCap)) od" unfolding can_fast_finalise_def by (rule finalise_cap.cases[of "(cap,final)"]; simp add: assert_def liftM_def) (* * Atomically swap the two given caps. *) definition swap_cap :: "cdl_cap \ cdl_cap_ref \ cdl_cap \ cdl_cap_ref \ unit k_monad" where "swap_cap cap1 slot1 cap2 slot2 \ do set_cap slot1 cap2; set_cap slot2 cap1; swap_parents slot1 slot2 od" (* * Move the given cap from one location to another, * possibly modifying it along the way. *) definition move_cap :: "cdl_cap \ cdl_cap_ref \ cdl_cap_ref \ unit k_monad" where "move_cap cap src_slot dest_slot \ do insert_cap_orphan cap dest_slot; set_cap src_slot NullCap; swap_parents src_slot dest_slot od" definition monadic_rel_optionation_form :: "('a \ ('s, 'b) nondet_monad) \ (('a \ 's) option \ ('b \ 's) option) set" where "monadic_rel_optionation_form f = {(x, y). (x \ None \ y \ None \ the y \ fst (case_prod f (the x))) \ (x \ None \ y = None \ snd (case_prod f (the x))) \ (x = None \ y = None)}" definition monadic_option_dest :: "('a \ 's) option set \ (('a \ 's) set \ bool)" where "monadic_option_dest S = (Some -` S, None \ S)" lemma use_option_form: "f x = (\s. monadic_option_dest (monadic_rel_optionation_form f `` {Some (x, s)}))" by (simp add: monadic_rel_optionation_form_def monadic_option_dest_def) lemma ex_option: " (\x. P x) = ((\y. P (Some y)) \ P None)" apply safe apply (case_tac x, auto) done lemma use_option_form_bind: "f x >>= g = (\s. monadic_option_dest ((monadic_rel_optionation_form f O monadic_rel_optionation_form g) `` {Some (x, s)}))" apply (rule ext) apply (simp add: monadic_rel_optionation_form_def monadic_option_dest_def bind_def split_def) apply (simp add: relcomp_unfold ex_option image_def prod_eq_iff Bex_def) apply fastforce done definition monadic_trancl :: "('a \ ('s, 'a) nondet_monad) \ 'a \ ('s, 'a) nondet_monad" where "monadic_trancl f x = (\s. monadic_option_dest ((monadic_rel_optionation_form f)\<^sup>* `` {Some (x, s)}))" definition monadic_trancl_preemptible :: "('a \ ('s, 'e + 'a) nondet_monad) \ ('a \ ('s, 'e + 'a) nondet_monad)" where "monadic_trancl_preemptible f x = monadic_trancl (lift f) (Inr x)" definition cap_removeable :: "cdl_cap \ cdl_cap_ref \ cdl_state \ bool" where "cap_removeable cap slot s = (cap = NullCap \ (\p. cap = ZombieCap p \ swp opt_cap s ` (({p} \ UNIV) - {slot}) \ {Some NullCap, None}))" definition finalise_slot_inner1 :: "cdl_cap_ref \ (cdl_cap \ bool) k_monad" where "finalise_slot_inner1 victim = do cap \ get_cap victim; final \ is_final_cap cap; (cap', irqopt) \ finalise_cap cap final; removeable \ gets $ cap_removeable cap' victim; when (\ removeable) (set_cap victim cap') \ set_cap victim cap'; return (cap', removeable) od" definition get_zombie_range :: "cdl_cap \ cdl_state \ cdl_cap_ref set" where "get_zombie_range cap = (\s. case cap of ZombieCap p \ dom (swp opt_cap s) \ ({p} \ UNIV) | _ \ {})" definition swap_for_delete :: "cdl_cap_ref \ cdl_cap_ref \ unit k_monad" where "swap_for_delete ptr1 ptr2 = do cap1 \ get_cap ptr1; cap2 \ get_cap ptr2; swap_cap cap1 ptr1 cap2 ptr2 od" definition "finalise_slot_inner2 = (\(region, finalised). liftE (do (victim', remove) \ select region; (cap', removeable) \ finalise_slot_inner1 victim'; region' \ gets $ get_zombie_range cap'; return (region \ (region' \ {True}), if removeable then {(victim', remove)} else {}) od) \ liftE (do (slot, slot') \ select {(x, y). (x, True) \ region \ (y, True) \ region \ x \ y}; swap_for_delete slot slot'; return (region, {}) od) \ liftE (do victim' \ select {x. (x, True) \ finalised}; empty_slot victim'; return (region, {}) od) \ throw )" definition finalise_slot :: "cdl_cap_ref \ unit preempt_monad" where "finalise_slot victim = doE (region, finalised) \ monadic_trancl_preemptible finalise_slot_inner2 ({(victim, False)}, {}); whenE (victim \ fst ` finalised) throw odE" definition delete_cap :: "cdl_cap_ref \ unit preempt_monad" where "delete_cap victim = doE finalise_slot victim; liftE $ empty_slot victim odE" (* * Revoke all the descendants of the given cap. * * If the CDT is being modelled, this will delete all the * descendants of the given cap. Wonderful things happen * if we happen to, in this process, delete something * that contains the cap we are trying to revoke. *) definition revoke_cap :: "cdl_cap_ref \ unit preempt_monad" where "revoke_cap victim = doE fin \ monadic_trancl_preemptible (K (doE S \ liftE $ gets $ descendants_of victim; if S = {} then returnOk True else doE child \ liftE $ select S; cap \ liftE $ get_cap child; assertE (cap \ NullCap); delete_cap child; Monads_D.throw \ returnOk False odE odE)) False; unlessE fin throw odE" (* * Get the badge the given thread object is using to * perform its IPC send operation. *) definition get_tcb_ep_badge :: "cdl_tcb \ cdl_badge option" where "get_tcb_ep_badge t \ case (cdl_tcb_caps t tcb_pending_op_slot) of Some (PendingSyncSendCap _ badge _ _ _ _) \ Some badge | _ \ None" (* * Cancel all pending send operations to the given endpoint * that are using the given badge. *) definition cancel_badged_sends :: "cdl_object_id \ cdl_badge \ unit k_monad" where "cancel_badged_sends ep badge \ modify (\s. s\cdl_objects := map_option (\obj. case obj of Tcb t \ if (is_thread_blocked_on_endpoint t ep \ get_tcb_ep_badge t = Some badge) then Tcb (remove_pending_operation t cdl_cap.RestartCap) else Tcb t | _ \ obj) \ (cdl_objects s)\)" (* * Regenerate the target object. * * Any children of the cap are first revoked. The object * is then reset into its original (as-if just created) * state. But maybe not. It's complex. * * In the C implementation, attempting to recycle a * non-master cap may do something that is not * a recycle. (Should be perhaps return an error?) *) definition clear_object_caps :: "cdl_object_id \ unit k_monad" where "clear_object_caps ptr = do ptrs \ gets (\s. {cptr. fst cptr = ptr \ opt_cap cptr s \ None}); ptrlist \ select {xs. set xs = ptrs \ distinct xs}; mapM_x empty_slot ptrlist od" definition cdl_default_tcb :: "cdl_object" where "cdl_default_tcb \ Tcb \cdl_tcb_caps = [tcb_cspace_slot \ cdl_cap.NullCap, tcb_vspace_slot \ cdl_cap.NullCap, tcb_replycap_slot \ cdl_cap.NullCap, tcb_caller_slot \ cdl_cap.NullCap, tcb_ipcbuffer_slot \ cdl_cap.NullCap, tcb_pending_op_slot \ cdl_cap.NullCap, tcb_boundntfn_slot \ cdl_cap.NullCap], cdl_tcb_fault_endpoint = 0, cdl_tcb_intent = \cdl_intent_op = None, cdl_intent_error = False,cdl_intent_cap = 0, cdl_intent_extras = [], cdl_intent_recv_slot = None\, cdl_tcb_has_fault = False, cdl_tcb_domain = minBound\" definition obj_tcb :: "cdl_object \ cdl_tcb" where "obj_tcb obj \ case obj of Tcb tcb \ tcb" definition tcb_caps_merge :: "cdl_tcb \ cdl_tcb \ cdl_tcb" where "tcb_caps_merge regtcb captcb \ regtcb\cdl_tcb_caps := (cdl_tcb_caps captcb)(tcb_pending_op_slot \ the (cdl_tcb_caps regtcb tcb_pending_op_slot), tcb_boundntfn_slot \ the (cdl_tcb_caps regtcb tcb_boundntfn_slot))\" definition merge_with_dft_tcb :: "cdl_object_id \ unit k_monad" where "merge_with_dft_tcb o_id \ do new_intent \ select UNIV; KHeap_D.update_thread o_id (cdl_tcb_intent_update (\x. new_intent) \ (tcb_caps_merge (obj_tcb cdl_default_tcb))) od" fun reset_mem_mapping :: "cdl_cap \ cdl_cap" where "reset_mem_mapping (FrameCap dev p rts sz b mp) = FrameCap dev p rts sz b None" | "reset_mem_mapping (PageTableCap ptr b mp) = PageTableCap ptr b None" | "reset_mem_mapping (PageDirectoryCap ptr b ma) = PageDirectoryCap ptr b None" | "reset_mem_mapping cap = cap" (* * Walk a user's CSpace to convert a user's CPTR into a cap slot. *) function resolve_address_bits :: "cdl_cap \ cdl_cptr \ nat \ (cdl_cap_ref \ nat) fault_monad" where "resolve_address_bits cnode_cap cap_ptr remaining_size = doE unlessE (is_cnode_cap cnode_cap) $ throw; \ \Fetch the next level CNode.\ cnode \ liftE $ get_cnode $ cap_object cnode_cap; radix_size \ returnOk $ cdl_cnode_size_bits cnode; guard_size \ returnOk $ cap_guard_size cnode_cap; cap_guard \ returnOk $ cap_guard cnode_cap; level_size \ returnOk (radix_size + guard_size); assertE (level_size \ 0); \ \Ensure the guard matches up.\ guard \ returnOk $ (cap_ptr >> (remaining_size-guard_size)) && (mask guard_size); unlessE (guard_size \ remaining_size \ guard = cap_guard) $ throw; \ \Ensure we still enough unresolved bits left in our CPTR.\ whenE (level_size > remaining_size) $ throw; \ \Find the next slot.\ offset \ returnOk $ (cap_ptr >> (remaining_size-level_size)) && (mask radix_size); slot \ returnOk (cap_object cnode_cap, unat offset); size_left \ returnOk (remaining_size - level_size); if (size_left = 0) then returnOk (slot, 0) else doE next_cap \ liftE $ get_cap (slot); if is_cnode_cap next_cap then resolve_address_bits next_cap cap_ptr size_left else returnOk (slot, size_left) odE odE" by fastforce+ termination resolve_address_bits apply (relation "measure (\(a,b,c). c)") apply (auto simp: in_monad) done definition lookup_slot :: "cdl_object_id \ cdl_cptr \ cdl_cap_ref fault_monad" where "lookup_slot thread cptr \ doE cspace_root \ liftE $ get_cap (thread, tcb_cspace_slot); (slot, _) \ resolve_address_bits cspace_root cptr word_bits; returnOk slot odE" definition lookup_cap :: "cdl_object_id \ cdl_cptr \ cdl_cap fault_monad" where "lookup_cap thread cptr \ doE slot \ lookup_slot thread cptr; liftE $ get_cap slot odE" definition lookup_cap_and_slot :: "cdl_object_id \ cdl_cptr \ (cdl_cap \ cdl_cap_ref) fault_monad" where "lookup_cap_and_slot thread cptr \ doE slot \ lookup_slot thread cptr; cap \ liftE $ get_cap slot; returnOk (cap, slot) odE" definition lookup_slot_for_cnode_op :: "cdl_cap \ cdl_cptr \ nat \ cdl_cap_ref except_monad" where "lookup_slot_for_cnode_op croot cptr depth \ doE whenE (depth < 1 \ depth > word_bits) throw; (slot, rem) \ fault_to_except $ resolve_address_bits croot cptr depth; if rem = 0 then returnOk slot else throw odE" (* * Update the badge of a cap, masking off bits the lower specs are unable * to store for implementation reasons. *) definition badge_update :: "word32 \ cdl_cap \ cdl_cap" where "badge_update data cap \ update_cap_badge (data && mask badge_bits) cap" (* * Transform a capability based on a request from the user. * * The "data" word is interpreted differently for different cap types. * * We return a set of possible caps to allow for non-deterministic * implementations, to avoid messy implementation details of the CDT * in lower-level models. *) definition update_cap_data :: "bool \ word32 \ cdl_cap \ cdl_cap k_monad" where "update_cap_data preserve data cap \ return $ case cap of EndpointCap _ b _ \ if b = 0 \ \ preserve then badge_update data cap else NullCap | NotificationCap _ b _ \ if b = 0 \ \ preserve then badge_update data cap else NullCap | CNodeCap object guard guard_size sz \ let reserved_bits = 3; guard_bits = 18; guard_size_bits = 5; new_guard_size = unat ((data >> reserved_bits) && mask guard_size_bits); new_guard = (data >> (reserved_bits + guard_size_bits)) && mask (min (unat ((data >> reserved_bits) && mask guard_size_bits)) guard_bits) in if new_guard_size + sz > word_bits then NullCap else (CNodeCap object new_guard new_guard_size sz) | _ \ cap" (* * Some caps may not be copied/minted. In this case the following function * returns NullCap or throws. * * PageTable and PageDirectory caps may not be copied if already mapped. This is * left out here and modelled by nondeterminism. *) definition derive_cap :: "cdl_cap_ref \ cdl_cap \ cdl_cap except_monad" where "derive_cap slot cap \ case cap of UntypedCap _ _ _ \ doE ensure_no_children slot; returnOk cap odE | ReplyCap _ _ \ returnOk NullCap | MasterReplyCap oref \ returnOk NullCap | IrqControlCap \ returnOk NullCap | ZombieCap _ \ returnOk NullCap | FrameCap dev p r sz b x \ returnOk (FrameCap dev p r sz b None) | PageTableCap _ _ _ \ throw \ returnOk cap | PageDirectoryCap _ _ _ \ throw \ returnOk cap | _ \ returnOk cap" (* This function is here to make it available in both Tcb_D and PageTable_D *) (* Modify the TCB's IpcBuffer or Registers in an arbitrary fashion. *) definition corrupt_tcb_intent :: "cdl_object_id \ unit k_monad" where "corrupt_tcb_intent target_tcb \ do new_intent \ select UNIV; update_thread target_tcb (\t. t\cdl_tcb_intent := new_intent\) od" end