(* * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) * * SPDX-License-Identifier: GPL-2.0-only *) chapter "Decoding Architecture-specific System Calls" theory ArchDecode_A imports Interrupt_A InvocationLabels_A "ExecSpec.InvocationLabels_H" begin context Arch begin global_naming RISCV64_A section "Helper definitions" definition check_vp_alignment :: "vmpage_size \ machine_word \ (unit,'z::state_ext) se_monad" where "check_vp_alignment sz vptr \ unlessE (is_aligned vptr (pageBitsForSize sz)) $ throwError AlignmentError" definition page_base :: "vspace_ref \ vmpage_size \ vspace_ref" where "page_base vaddr vmsize \ vaddr && ~~ mask (pageBitsForSize vmsize)" section "Architecture-specific Decode Functions" definition arch_check_irq :: "data \ (unit,'z::state_ext) se_monad" where "arch_check_irq irq \ whenE (irq > ucast maxIRQ \ irq = ucast irqInvalid) $ throwError (RangeError 1 (ucast maxIRQ))" definition arch_decode_irq_control_invocation :: "data \ data list \ cslot_ptr \ cap list \ (arch_irq_control_invocation,'z::state_ext) se_monad" where "arch_decode_irq_control_invocation label args src_slot cps \ (if invocation_type label = ArchInvocationLabel RISCVIRQIssueIRQHandler then if length args \ 4 \ length cps \ 1 then let irq_word = args ! 0; trigger = args ! 1; index = args ! 2; depth = args ! 3; cnode = cps ! 0; irq = ucast irq_word in doE arch_check_irq irq_word; irq_active \ liftE $ is_irq_active irq; whenE irq_active $ throwError RevokeFirst; dest_slot \ lookup_target_slot cnode (data_to_cptr index) (unat depth); ensure_empty dest_slot; returnOk $ RISCVIRQControlInvocation irq dest_slot src_slot (trigger \ 0) odE else throwError TruncatedMessage else throwError IllegalOperation)" definition attribs_from_word :: "machine_word \ vm_attributes" where "attribs_from_word w \ if \ w!!0 then {Execute} else {}" definition make_user_pte :: "vspace_ref \ vm_attributes \ vm_rights \ pte" where "make_user_pte addr attr rights \ if rights = {} \ attr = {} then InvalidPTE else PagePTE (ucast (addr >> pageBits)) (attr \ {User}) rights" definition check_slot :: "obj_ref \ (pte \ bool) \ (unit,'z::state_ext) se_monad" where "check_slot slot test = doE pte \ liftE $ get_pte slot; unlessE (test pte) $ throwError DeleteFirst odE" type_synonym 'z arch_decoder = "data \ data list \ cslot_ptr \ arch_cap \ (cap \ cslot_ptr) list \ (arch_invocation,'z) se_monad" definition decode_fr_inv_map :: "'z::state_ext arch_decoder" where "decode_fr_inv_map label args cte cap extra_caps \ case cap of FrameCap p R pgsz dev mapped_address \ if length args > 2 \ length extra_caps > 0 then let vaddr = args ! 0; rights_mask = args ! 1; attr = args ! 2; vspace_cap = fst (extra_caps ! 0) in doE (pt, asid) \ case vspace_cap of ArchObjectCap (PageTableCap pt (Some (asid, _))) \ returnOk (pt, asid) | _ \ throwError $ InvalidCapability 1; pt' \ lookup_error_on_failure False $ find_vspace_for_asid asid; whenE (pt' \ pt) $ throwError $ InvalidCapability 1; pg_bits \ returnOk $ pageBitsForSize pgsz; vtop \ returnOk $ vaddr + mask (pageBitsForSize pgsz); whenE (vtop \ user_vtop) $ throwError $ InvalidArgument 0; check_vp_alignment pgsz vaddr; (level, slot) \ liftE $ gets_the $ pt_lookup_slot pt vaddr \ ptes_of; unlessE (pt_bits_left level = pg_bits) $ throwError $ FailedLookup False $ MissingCapability $ pt_bits_left level; case mapped_address of Some (asid', vaddr') \ doE whenE (asid' \ asid) (throwError $ InvalidCapability 1); whenE (vaddr' \ vaddr) (throwError $ InvalidArgument 0); check_slot slot (Not \ is_PageTablePTE) odE | None \ check_slot slot ((=) InvalidPTE); vm_rights \ returnOk $ mask_vm_rights R (data_to_rights rights_mask); attribs \ returnOk $ attribs_from_word attr; pte \ returnOk $ make_user_pte (addrFromPPtr p) attribs vm_rights; returnOk $ InvokePage $ PageMap (FrameCap p R pgsz dev (Some (asid,vaddr))) cte (pte,slot) odE else throwError TruncatedMessage | _ \ fail" definition decode_frame_invocation :: "'z::state_ext arch_decoder" where "decode_frame_invocation label args cte cap extra_caps \ if invocation_type label = ArchInvocationLabel RISCVPageMap then decode_fr_inv_map label args cte cap extra_caps else if invocation_type label = ArchInvocationLabel RISCVPageUnmap then returnOk $ InvokePage $ PageUnmap cap cte else if invocation_type label = ArchInvocationLabel RISCVPageGetAddress then returnOk $ InvokePage $ PageGetAddr (acap_obj cap) else throwError IllegalOperation" definition decode_pt_inv_map :: "'z::state_ext arch_decoder" where "decode_pt_inv_map label args cte cap extra_caps \ case cap of PageTableCap p mapped_address \ if length args > 1 \ length extra_caps > 0 then let vaddr = args ! 0; attr = args ! 1; vspace_cap = fst (extra_caps ! 0) in doE whenE (mapped_address \ None) $ throwError $ InvalidCapability 0; (pt, asid) \ case vspace_cap of ArchObjectCap (PageTableCap pt (Some (asid,_))) \ returnOk (pt, asid) | _ \ throwError $ InvalidCapability 1; whenE (user_vtop \ vaddr) $ throwError $ InvalidArgument 0; pt' \ lookup_error_on_failure False $ find_vspace_for_asid asid; whenE (pt' \ pt) $ throwError $ InvalidCapability 1; (level, slot) \ liftE $ gets_the $ pt_lookup_slot pt vaddr \ ptes_of; old_pte \ liftE $ get_pte slot; whenE (pt_bits_left level = pageBits \ old_pte \ InvalidPTE) $ throwError DeleteFirst; pte \ returnOk $ PageTablePTE (ucast (addrFromPPtr p >> pageBits)) {}; cap' <- returnOk $ PageTableCap p $ Some (asid, vaddr && ~~mask (pt_bits_left level)); returnOk $ InvokePageTable $ PageTableMap cap' cte pte slot odE else throwError TruncatedMessage | _ \ fail" definition decode_page_table_invocation :: "'z::state_ext arch_decoder" where "decode_page_table_invocation label args cte cap extra_caps \ if invocation_type label = ArchInvocationLabel RISCVPageTableMap then decode_pt_inv_map label args cte cap extra_caps else if invocation_type label = ArchInvocationLabel RISCVPageTableUnmap then doE final \ liftE $ is_final_cap (ArchObjectCap cap); unlessE final $ throwError RevokeFirst; case cap of PageTableCap pt (Some (asid, _)) \ doE \ \cannot invoke unmap on top level page table\ pt_opt \ liftE $ gets $ vspace_for_asid asid; whenE (pt_opt = Some pt) $ throwError RevokeFirst odE | _ \ returnOk (); returnOk $ InvokePageTable $ PageTableUnmap cap cte odE else throwError IllegalOperation" definition decode_asid_control_invocation :: "'z::state_ext arch_decoder" where "decode_asid_control_invocation label args cte cap extra_caps \ if invocation_type label = ArchInvocationLabel RISCVASIDControlMakePool then if length args > 1 \ length extra_caps > 1 then let index = args ! 0; depth = args ! 1; (untyped, parent_slot) = extra_caps ! 0; root = fst (extra_caps ! 1) in doE asid_table \ liftE $ gets (riscv_asid_table \ arch_state); free_set \ returnOk (- dom asid_table); whenE (free_set = {}) $ throwError DeleteFirst; free \ liftE $ select_ext (\_. free_asid_select asid_table) free_set; base \ returnOk (ucast free << asid_low_bits); (p,n) \ case untyped of UntypedCap False p n _ \ returnOk (p,n) | _ \ throwError $ InvalidCapability 1; frame \ if n = pageBits then doE ensure_no_children parent_slot; returnOk p odE else throwError $ InvalidCapability 1; dest_slot \ lookup_target_slot root (to_bl index) (unat depth); ensure_empty dest_slot; returnOk $ InvokeASIDControl $ MakePool frame dest_slot parent_slot base odE else throwError TruncatedMessage else throwError IllegalOperation" definition decode_asid_pool_invocation :: "'z::state_ext arch_decoder" where "decode_asid_pool_invocation label args cte cap extra_caps \ if invocation_type label = ArchInvocationLabel RISCVASIDPoolAssign then if length extra_caps > 0 then let (pt_cap, pt_cap_slot) = extra_caps ! 0; p = acap_obj cap; base = acap_asid_base cap in case pt_cap of ArchObjectCap (PageTableCap _ None) \ doE asid_table \ liftE $ gets (riscv_asid_table \ arch_state); pool_ptr \ returnOk (asid_table (asid_high_bits_of base)); whenE (pool_ptr = None) $ throwError $ FailedLookup False InvalidRoot; whenE (p \ the pool_ptr) $ throwError $ InvalidCapability 0; pool \ liftE $ get_asid_pool p; free_set \ returnOk (- dom pool \ {x. ucast x + base \ 0}); whenE (free_set = {}) $ throwError DeleteFirst; offset \ liftE $ select_ext (\_. free_asid_pool_select pool base) free_set; returnOk $ InvokeASIDPool $ Assign (ucast offset + base) p pt_cap_slot odE | _ \ throwError $ InvalidCapability 1 else throwError TruncatedMessage else throwError IllegalOperation" definition arch_decode_invocation :: "data \ data list \ cap_ref \ cslot_ptr \ arch_cap \ (cap \ cslot_ptr) list \ (arch_invocation,'z::state_ext) se_monad" where "arch_decode_invocation label args x_slot cte cap extra_caps \ case cap of PageTableCap _ _ \ decode_page_table_invocation label args cte cap extra_caps | FrameCap _ _ _ _ _ \ decode_frame_invocation label args cte cap extra_caps | ASIDControlCap \ decode_asid_control_invocation label args cte cap extra_caps | ASIDPoolCap _ _ \ decode_asid_pool_invocation label args cte cap extra_caps" section "Interface Functions used in Decode" definition arch_data_to_obj_type :: "nat \ aobject_type option" where "arch_data_to_obj_type n \ if n = 0 then Some HugePageObj else if n = 1 then Some SmallPageObj else if n = 2 then Some LargePageObj else if n = 3 then Some PageTableObj else None" end end