/* * Copyright 2019 The Hafnium Authors. * * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file or at * https://opensource.org/licenses/BSD-3-Clause. */ #pragma once #include "hf/arch/types.h" #include "hf/addr.h" #include "hf/interrupt_desc.h" #include "hf/spinlock.h" #include "vmapi/hf/ffa.h" /** Action for non secure interrupt by SPMC. */ #define NS_ACTION_QUEUED 0 #define NS_ACTION_ME 1 #define NS_ACTION_SIGNALED 2 #define NS_ACTION_INVALID 3 enum vcpu_state { /** The vCPU is switched off. */ VCPU_STATE_OFF, /** The vCPU is currently running. */ VCPU_STATE_RUNNING, /** The vCPU is waiting to be allocated CPU cycles to do work. */ VCPU_STATE_WAITING, /** * The vCPU is blocked and waiting for some work to complete on * its behalf. */ VCPU_STATE_BLOCKED, /** The vCPU has been preempted by an interrupt. */ VCPU_STATE_PREEMPTED, /** The vCPU is waiting for an interrupt. */ VCPU_STATE_BLOCKED_INTERRUPT, /** The vCPU has aborted. */ VCPU_STATE_ABORTED, }; /** Refer to section 7 of the FF-A v1.1 EAC0 spec. */ enum partition_runtime_model { RTM_NONE, /** Runtime model for FFA_RUN. */ RTM_FFA_RUN, /** Runtime model for FFA_MSG_SEND_DIRECT_REQUEST. */ RTM_FFA_DIR_REQ, /** Runtime model for Secure Interrupt handling. */ RTM_SEC_INTERRUPT, /** Runtime model for SP Initialization. */ RTM_SP_INIT, }; /** Refer to section 8.2.3 of the FF-A EAC0 spec. */ enum schedule_mode { NONE, /** Normal world scheduled mode. */ NWD_MODE, /** SPMC scheduled mode. */ SPMC_MODE, }; struct interrupts { /** Bitfield keeping track of which interrupts are enabled. */ struct interrupt_bitmap interrupt_enabled; /** Bitfield keeping track of which interrupts are pending. */ struct interrupt_bitmap interrupt_pending; /** Bitfield recording the interrupt pin configuration. */ struct interrupt_bitmap interrupt_type; /** * The number of interrupts which are currently both enabled and * pending. Count independently virtual IRQ and FIQ interrupt types * i.e. the sum of the two counters is the number of bits set in * interrupt_enable & interrupt_pending. */ uint32_t enabled_and_pending_irq_count; uint32_t enabled_and_pending_fiq_count; }; struct vcpu_fault_info { ipaddr_t ipaddr; vaddr_t vaddr; vaddr_t pc; uint32_t mode; }; struct call_chain { /** Previous node in the SP call chain. */ struct vcpu *prev_node; /** Next node in the SP call chain. */ struct vcpu *next_node; }; struct vcpu { struct spinlock lock; /* * The state is only changed in the context of the vCPU being run. This * ensures the scheduler can easily keep track of the vCPU state as * transitions are indicated by the return code from the run call. */ enum vcpu_state state; bool is_bootstrapped; struct cpu *cpu; struct vm *vm; struct arch_regs regs; struct interrupts interrupts; /* * Determine whether the 'regs' field is available for use. This is set * to false when a vCPU is about to run on a physical CPU, and is set * back to true when it is descheduled. This is not relevant for the * primary VM vCPUs in the normal world (or the "other world VM" vCPUs * in the secure world) as they are pinned to physical CPUs and there * is no contention to take care of. */ bool regs_available; /* * If the current vCPU is executing as a consequence of a * FFA_MSG_SEND_DIRECT_REQ invocation, then this member holds the * originating VM ID from which the call originated. * The value HF_INVALID_VM_ID implies the vCPU is not executing as * a result of a prior FFA_MSG_SEND_DIRECT_REQ invocation. */ ffa_vm_id_t direct_request_origin_vm_id; /** Determine whether partition is currently handling managed exit. */ bool processing_managed_exit; /** * Determine whether vCPU is currently handling secure interrupt. */ bool processing_secure_interrupt; bool secure_interrupt_deactivated; /** * INTID of the current secure interrupt being processed by this vCPU. */ uint32_t current_sec_interrupt_id; /** * Track current vCPU which got pre-empted when secure interrupt * triggered. */ struct vcpu *preempted_vcpu; /** * Current value of the Priority Mask register which is saved/restored * during secure interrupt handling. */ uint8_t priority_mask; /** * Per FF-A v1.1-Beta0 spec section 8.3, an SP can use multiple * mechanisms to signal completion of secure interrupt handling. SP * can invoke explicit FF-A ABIs, namely FFA_MSG_WAIT and FFA_RUN, * when in WAITING/BLOCKED state respectively, but has to perform * implicit signal completion mechanism by dropping the priority * of the virtual secure interrupt when SPMC signaled the virtual * interrupt in PREEMPTED state(The vCPU was preempted by a Self S-Int * while running). This variable helps SPMC to keep a track of such * mechanism and perform appropriate bookkeeping. */ bool implicit_completion_signal; /** SP call chain. */ struct call_chain call_chain; /** * Indicates if the current vCPU is running in SPMC scheduled * mode or Normal World scheduled mode. */ enum schedule_mode scheduling_mode; /** * Present action taken by SP in response to a non secure interrupt * based on the precedence rules as specified in section 8.3.1.4 of * the FF-A v1.1 EAC0 spec. */ uint8_t present_action_ns_interrupts; /** * If the action in response to a non secure interrupt is to queue it, * this field is used to save and restore the current priority mask. */ uint8_t mask_ns_interrupts; /** Partition Runtime Model. */ enum partition_runtime_model rt_model; }; /** Encapsulates a vCPU whose lock is held. */ struct vcpu_locked { struct vcpu *vcpu; }; /** Container for two vcpu_locked structures. */ struct two_vcpu_locked { struct vcpu_locked vcpu1; struct vcpu_locked vcpu2; }; struct vcpu_locked vcpu_lock(struct vcpu *vcpu); struct two_vcpu_locked vcpu_lock_both(struct vcpu *vcpu1, struct vcpu *vcpu2); void vcpu_unlock(struct vcpu_locked *locked); void vcpu_init(struct vcpu *vcpu, struct vm *vm); void vcpu_on(struct vcpu_locked vcpu, ipaddr_t entry, uintreg_t arg); ffa_vcpu_index_t vcpu_index(const struct vcpu *vcpu); bool vcpu_is_off(struct vcpu_locked vcpu); bool vcpu_secondary_reset_and_start(struct vcpu_locked vcpu_locked, ipaddr_t entry, uintreg_t arg); bool vcpu_handle_page_fault(const struct vcpu *current, struct vcpu_fault_info *f); void vcpu_reset(struct vcpu *vcpu); void vcpu_set_phys_core_idx(struct vcpu *vcpu); static inline bool vcpu_is_virt_interrupt_enabled(struct interrupts *interrupts, uint32_t intid) { return interrupt_bitmap_get_value(&interrupts->interrupt_enabled, intid) == 1U; } static inline void vcpu_virt_interrupt_set_enabled( struct interrupts *interrupts, uint32_t intid) { interrupt_bitmap_set_value(&interrupts->interrupt_enabled, intid); } static inline void vcpu_virt_interrupt_clear_enabled( struct interrupts *interrupts, uint32_t intid) { interrupt_bitmap_clear_value(&interrupts->interrupt_enabled, intid); } static inline bool vcpu_is_virt_interrupt_pending(struct interrupts *interrupts, uint32_t intid) { return interrupt_bitmap_get_value(&interrupts->interrupt_pending, intid) == 1U; } static inline void vcpu_virt_interrupt_set_pending( struct interrupts *interrupts, uint32_t intid) { interrupt_bitmap_set_value(&interrupts->interrupt_pending, intid); } static inline void vcpu_virt_interrupt_clear_pending( struct interrupts *interrupts, uint32_t intid) { interrupt_bitmap_clear_value(&interrupts->interrupt_pending, intid); } static inline enum interrupt_type vcpu_virt_interrupt_get_type( struct interrupts *interrupts, uint32_t intid) { return (enum interrupt_type)interrupt_bitmap_get_value( &interrupts->interrupt_type, intid); } static inline void vcpu_virt_interrupt_set_type(struct interrupts *interrupts, uint32_t intid, enum interrupt_type type) { if (type == INTERRUPT_TYPE_IRQ) { interrupt_bitmap_clear_value(&interrupts->interrupt_type, intid); } else { interrupt_bitmap_set_value(&interrupts->interrupt_type, intid); } } static inline void vcpu_irq_count_increment(struct vcpu_locked vcpu_locked) { vcpu_locked.vcpu->interrupts.enabled_and_pending_irq_count++; } static inline void vcpu_irq_count_decrement(struct vcpu_locked vcpu_locked) { vcpu_locked.vcpu->interrupts.enabled_and_pending_irq_count--; } static inline void vcpu_fiq_count_increment(struct vcpu_locked vcpu_locked) { vcpu_locked.vcpu->interrupts.enabled_and_pending_fiq_count++; } static inline void vcpu_fiq_count_decrement(struct vcpu_locked vcpu_locked) { vcpu_locked.vcpu->interrupts.enabled_and_pending_fiq_count--; } static inline void vcpu_interrupt_count_increment( struct vcpu_locked vcpu_locked, struct interrupts *interrupts, uint32_t intid) { if (vcpu_virt_interrupt_get_type(interrupts, intid) == INTERRUPT_TYPE_IRQ) { vcpu_irq_count_increment(vcpu_locked); } else { vcpu_fiq_count_increment(vcpu_locked); } } static inline void vcpu_interrupt_count_decrement( struct vcpu_locked vcpu_locked, struct interrupts *interrupts, uint32_t intid) { if (vcpu_virt_interrupt_get_type(interrupts, intid) == INTERRUPT_TYPE_IRQ) { vcpu_irq_count_decrement(vcpu_locked); } else { vcpu_fiq_count_decrement(vcpu_locked); } } static inline uint32_t vcpu_interrupt_irq_count_get( struct vcpu_locked vcpu_locked) { return vcpu_locked.vcpu->interrupts.enabled_and_pending_irq_count; } static inline uint32_t vcpu_interrupt_fiq_count_get( struct vcpu_locked vcpu_locked) { return vcpu_locked.vcpu->interrupts.enabled_and_pending_fiq_count; } static inline uint32_t vcpu_interrupt_count_get(struct vcpu_locked vcpu_locked) { return vcpu_locked.vcpu->interrupts.enabled_and_pending_irq_count + vcpu_locked.vcpu->interrupts.enabled_and_pending_fiq_count; } static inline void vcpu_call_chain_extend(struct vcpu *vcpu1, struct vcpu *vcpu2) { vcpu1->call_chain.next_node = vcpu2; vcpu2->call_chain.prev_node = vcpu1; } static inline void vcpu_call_chain_remove_node(struct vcpu *vcpu1, struct vcpu *vcpu2) { vcpu1->call_chain.prev_node = NULL; vcpu2->call_chain.next_node = NULL; }