| /* SPDX-License-Identifier: GPL-2.0-only */ |
| /* |
| * Copyright (C) 2019 Western Digital Corporation or its affiliates. |
| * |
| * Authors: |
| * Anup Patel <anup.patel@wdc.com> |
| */ |
| |
| #ifndef __RISCV_KVM_HOST_H__ |
| #define __RISCV_KVM_HOST_H__ |
| |
| #include <linux/types.h> |
| #include <linux/kvm.h> |
| #include <linux/kvm_types.h> |
| #include <linux/spinlock.h> |
| #include <asm/hwcap.h> |
| #include <asm/kvm_aia.h> |
| #include <asm/ptrace.h> |
| #include <asm/kvm_vcpu_fp.h> |
| #include <asm/kvm_vcpu_insn.h> |
| #include <asm/kvm_vcpu_sbi.h> |
| #include <asm/kvm_vcpu_timer.h> |
| #include <asm/kvm_vcpu_pmu.h> |
| |
| #define KVM_MAX_VCPUS 1024 |
| |
| #define KVM_HALT_POLL_NS_DEFAULT 500000 |
| |
| #define KVM_VCPU_MAX_FEATURES 0 |
| |
| #define KVM_IRQCHIP_NUM_PINS 1024 |
| |
| #define KVM_REQ_SLEEP \ |
| KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) |
| #define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(1) |
| #define KVM_REQ_UPDATE_HGATP KVM_ARCH_REQ(2) |
| #define KVM_REQ_FENCE_I \ |
| KVM_ARCH_REQ_FLAGS(3, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) |
| #define KVM_REQ_HFENCE_GVMA_VMID_ALL KVM_REQ_TLB_FLUSH |
| #define KVM_REQ_HFENCE_VVMA_ALL \ |
| KVM_ARCH_REQ_FLAGS(4, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) |
| #define KVM_REQ_HFENCE \ |
| KVM_ARCH_REQ_FLAGS(5, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) |
| #define KVM_REQ_STEAL_UPDATE KVM_ARCH_REQ(6) |
| |
| #define KVM_HEDELEG_DEFAULT (BIT(EXC_INST_MISALIGNED) | \ |
| BIT(EXC_BREAKPOINT) | \ |
| BIT(EXC_SYSCALL) | \ |
| BIT(EXC_INST_PAGE_FAULT) | \ |
| BIT(EXC_LOAD_PAGE_FAULT) | \ |
| BIT(EXC_STORE_PAGE_FAULT)) |
| |
| #define KVM_HIDELEG_DEFAULT (BIT(IRQ_VS_SOFT) | \ |
| BIT(IRQ_VS_TIMER) | \ |
| BIT(IRQ_VS_EXT)) |
| |
| enum kvm_riscv_hfence_type { |
| KVM_RISCV_HFENCE_UNKNOWN = 0, |
| KVM_RISCV_HFENCE_GVMA_VMID_GPA, |
| KVM_RISCV_HFENCE_VVMA_ASID_GVA, |
| KVM_RISCV_HFENCE_VVMA_ASID_ALL, |
| KVM_RISCV_HFENCE_VVMA_GVA, |
| }; |
| |
| struct kvm_riscv_hfence { |
| enum kvm_riscv_hfence_type type; |
| unsigned long asid; |
| unsigned long order; |
| gpa_t addr; |
| gpa_t size; |
| }; |
| |
| #define KVM_RISCV_VCPU_MAX_HFENCE 64 |
| |
| struct kvm_vm_stat { |
| struct kvm_vm_stat_generic generic; |
| }; |
| |
| struct kvm_vcpu_stat { |
| struct kvm_vcpu_stat_generic generic; |
| u64 ecall_exit_stat; |
| u64 wfi_exit_stat; |
| u64 wrs_exit_stat; |
| u64 mmio_exit_user; |
| u64 mmio_exit_kernel; |
| u64 csr_exit_user; |
| u64 csr_exit_kernel; |
| u64 signal_exits; |
| u64 exits; |
| }; |
| |
| struct kvm_arch_memory_slot { |
| }; |
| |
| struct kvm_vmid { |
| /* |
| * Writes to vmid_version and vmid happen with vmid_lock held |
| * whereas reads happen without any lock held. |
| */ |
| unsigned long vmid_version; |
| unsigned long vmid; |
| }; |
| |
| struct kvm_arch { |
| /* G-stage vmid */ |
| struct kvm_vmid vmid; |
| |
| /* G-stage page table */ |
| pgd_t *pgd; |
| phys_addr_t pgd_phys; |
| |
| /* Guest Timer */ |
| struct kvm_guest_timer timer; |
| |
| /* AIA Guest/VM context */ |
| struct kvm_aia aia; |
| }; |
| |
| struct kvm_cpu_trap { |
| unsigned long sepc; |
| unsigned long scause; |
| unsigned long stval; |
| unsigned long htval; |
| unsigned long htinst; |
| }; |
| |
| struct kvm_cpu_context { |
| unsigned long zero; |
| unsigned long ra; |
| unsigned long sp; |
| unsigned long gp; |
| unsigned long tp; |
| unsigned long t0; |
| unsigned long t1; |
| unsigned long t2; |
| unsigned long s0; |
| unsigned long s1; |
| unsigned long a0; |
| unsigned long a1; |
| unsigned long a2; |
| unsigned long a3; |
| unsigned long a4; |
| unsigned long a5; |
| unsigned long a6; |
| unsigned long a7; |
| unsigned long s2; |
| unsigned long s3; |
| unsigned long s4; |
| unsigned long s5; |
| unsigned long s6; |
| unsigned long s7; |
| unsigned long s8; |
| unsigned long s9; |
| unsigned long s10; |
| unsigned long s11; |
| unsigned long t3; |
| unsigned long t4; |
| unsigned long t5; |
| unsigned long t6; |
| unsigned long sepc; |
| unsigned long sstatus; |
| unsigned long hstatus; |
| union __riscv_fp_state fp; |
| struct __riscv_v_ext_state vector; |
| }; |
| |
| struct kvm_vcpu_csr { |
| unsigned long vsstatus; |
| unsigned long vsie; |
| unsigned long vstvec; |
| unsigned long vsscratch; |
| unsigned long vsepc; |
| unsigned long vscause; |
| unsigned long vstval; |
| unsigned long hvip; |
| unsigned long vsatp; |
| unsigned long scounteren; |
| unsigned long senvcfg; |
| }; |
| |
| struct kvm_vcpu_config { |
| u64 henvcfg; |
| u64 hstateen0; |
| unsigned long hedeleg; |
| }; |
| |
| struct kvm_vcpu_smstateen_csr { |
| unsigned long sstateen0; |
| }; |
| |
| struct kvm_vcpu_arch { |
| /* VCPU ran at least once */ |
| bool ran_atleast_once; |
| |
| /* Last Host CPU on which Guest VCPU exited */ |
| int last_exit_cpu; |
| |
| /* ISA feature bits (similar to MISA) */ |
| DECLARE_BITMAP(isa, RISCV_ISA_EXT_MAX); |
| |
| /* Vendor, Arch, and Implementation details */ |
| unsigned long mvendorid; |
| unsigned long marchid; |
| unsigned long mimpid; |
| |
| /* SSCRATCH, STVEC, and SCOUNTEREN of Host */ |
| unsigned long host_sscratch; |
| unsigned long host_stvec; |
| unsigned long host_scounteren; |
| unsigned long host_senvcfg; |
| unsigned long host_sstateen0; |
| |
| /* CPU context of Host */ |
| struct kvm_cpu_context host_context; |
| |
| /* CPU context of Guest VCPU */ |
| struct kvm_cpu_context guest_context; |
| |
| /* CPU CSR context of Guest VCPU */ |
| struct kvm_vcpu_csr guest_csr; |
| |
| /* CPU Smstateen CSR context of Guest VCPU */ |
| struct kvm_vcpu_smstateen_csr smstateen_csr; |
| |
| /* CPU context upon Guest VCPU reset */ |
| struct kvm_cpu_context guest_reset_context; |
| spinlock_t reset_cntx_lock; |
| |
| /* CPU CSR context upon Guest VCPU reset */ |
| struct kvm_vcpu_csr guest_reset_csr; |
| |
| /* |
| * VCPU interrupts |
| * |
| * We have a lockless approach for tracking pending VCPU interrupts |
| * implemented using atomic bitops. The irqs_pending bitmap represent |
| * pending interrupts whereas irqs_pending_mask represent bits changed |
| * in irqs_pending. Our approach is modeled around multiple producer |
| * and single consumer problem where the consumer is the VCPU itself. |
| */ |
| #define KVM_RISCV_VCPU_NR_IRQS 64 |
| DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS); |
| DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS); |
| |
| /* VCPU Timer */ |
| struct kvm_vcpu_timer timer; |
| |
| /* HFENCE request queue */ |
| spinlock_t hfence_lock; |
| unsigned long hfence_head; |
| unsigned long hfence_tail; |
| struct kvm_riscv_hfence hfence_queue[KVM_RISCV_VCPU_MAX_HFENCE]; |
| |
| /* MMIO instruction details */ |
| struct kvm_mmio_decode mmio_decode; |
| |
| /* CSR instruction details */ |
| struct kvm_csr_decode csr_decode; |
| |
| /* SBI context */ |
| struct kvm_vcpu_sbi_context sbi_context; |
| |
| /* AIA VCPU context */ |
| struct kvm_vcpu_aia aia_context; |
| |
| /* Cache pages needed to program page tables with spinlock held */ |
| struct kvm_mmu_memory_cache mmu_page_cache; |
| |
| /* VCPU power state */ |
| struct kvm_mp_state mp_state; |
| spinlock_t mp_state_lock; |
| |
| /* Don't run the VCPU (blocked) */ |
| bool pause; |
| |
| /* Performance monitoring context */ |
| struct kvm_pmu pmu_context; |
| |
| /* 'static' configurations which are set only once */ |
| struct kvm_vcpu_config cfg; |
| |
| /* SBI steal-time accounting */ |
| struct { |
| gpa_t shmem; |
| u64 last_steal; |
| } sta; |
| }; |
| |
| static inline void kvm_arch_sync_events(struct kvm *kvm) {} |
| |
| #define KVM_RISCV_GSTAGE_TLB_MIN_ORDER 12 |
| |
| void kvm_riscv_local_hfence_gvma_vmid_gpa(unsigned long vmid, |
| gpa_t gpa, gpa_t gpsz, |
| unsigned long order); |
| void kvm_riscv_local_hfence_gvma_vmid_all(unsigned long vmid); |
| void kvm_riscv_local_hfence_gvma_gpa(gpa_t gpa, gpa_t gpsz, |
| unsigned long order); |
| void kvm_riscv_local_hfence_gvma_all(void); |
| void kvm_riscv_local_hfence_vvma_asid_gva(unsigned long vmid, |
| unsigned long asid, |
| unsigned long gva, |
| unsigned long gvsz, |
| unsigned long order); |
| void kvm_riscv_local_hfence_vvma_asid_all(unsigned long vmid, |
| unsigned long asid); |
| void kvm_riscv_local_hfence_vvma_gva(unsigned long vmid, |
| unsigned long gva, unsigned long gvsz, |
| unsigned long order); |
| void kvm_riscv_local_hfence_vvma_all(unsigned long vmid); |
| |
| void kvm_riscv_local_tlb_sanitize(struct kvm_vcpu *vcpu); |
| |
| void kvm_riscv_fence_i_process(struct kvm_vcpu *vcpu); |
| void kvm_riscv_hfence_gvma_vmid_all_process(struct kvm_vcpu *vcpu); |
| void kvm_riscv_hfence_vvma_all_process(struct kvm_vcpu *vcpu); |
| void kvm_riscv_hfence_process(struct kvm_vcpu *vcpu); |
| |
| void kvm_riscv_fence_i(struct kvm *kvm, |
| unsigned long hbase, unsigned long hmask); |
| void kvm_riscv_hfence_gvma_vmid_gpa(struct kvm *kvm, |
| unsigned long hbase, unsigned long hmask, |
| gpa_t gpa, gpa_t gpsz, |
| unsigned long order); |
| void kvm_riscv_hfence_gvma_vmid_all(struct kvm *kvm, |
| unsigned long hbase, unsigned long hmask); |
| void kvm_riscv_hfence_vvma_asid_gva(struct kvm *kvm, |
| unsigned long hbase, unsigned long hmask, |
| unsigned long gva, unsigned long gvsz, |
| unsigned long order, unsigned long asid); |
| void kvm_riscv_hfence_vvma_asid_all(struct kvm *kvm, |
| unsigned long hbase, unsigned long hmask, |
| unsigned long asid); |
| void kvm_riscv_hfence_vvma_gva(struct kvm *kvm, |
| unsigned long hbase, unsigned long hmask, |
| unsigned long gva, unsigned long gvsz, |
| unsigned long order); |
| void kvm_riscv_hfence_vvma_all(struct kvm *kvm, |
| unsigned long hbase, unsigned long hmask); |
| |
| int kvm_riscv_gstage_ioremap(struct kvm *kvm, gpa_t gpa, |
| phys_addr_t hpa, unsigned long size, |
| bool writable, bool in_atomic); |
| void kvm_riscv_gstage_iounmap(struct kvm *kvm, gpa_t gpa, |
| unsigned long size); |
| int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, |
| struct kvm_memory_slot *memslot, |
| gpa_t gpa, unsigned long hva, bool is_write); |
| int kvm_riscv_gstage_alloc_pgd(struct kvm *kvm); |
| void kvm_riscv_gstage_free_pgd(struct kvm *kvm); |
| void kvm_riscv_gstage_update_hgatp(struct kvm_vcpu *vcpu); |
| void __init kvm_riscv_gstage_mode_detect(void); |
| unsigned long __init kvm_riscv_gstage_mode(void); |
| int kvm_riscv_gstage_gpa_bits(void); |
| |
| void __init kvm_riscv_gstage_vmid_detect(void); |
| unsigned long kvm_riscv_gstage_vmid_bits(void); |
| int kvm_riscv_gstage_vmid_init(struct kvm *kvm); |
| bool kvm_riscv_gstage_vmid_ver_changed(struct kvm_vmid *vmid); |
| void kvm_riscv_gstage_vmid_update(struct kvm_vcpu *vcpu); |
| |
| int kvm_riscv_setup_default_irq_routing(struct kvm *kvm, u32 lines); |
| |
| void __kvm_riscv_unpriv_trap(void); |
| |
| unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu, |
| bool read_insn, |
| unsigned long guest_addr, |
| struct kvm_cpu_trap *trap); |
| void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu, |
| struct kvm_cpu_trap *trap); |
| int kvm_riscv_vcpu_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, |
| struct kvm_cpu_trap *trap); |
| |
| void __kvm_riscv_switch_to(struct kvm_vcpu_arch *vcpu_arch); |
| |
| void kvm_riscv_vcpu_setup_isa(struct kvm_vcpu *vcpu); |
| unsigned long kvm_riscv_vcpu_num_regs(struct kvm_vcpu *vcpu); |
| int kvm_riscv_vcpu_copy_reg_indices(struct kvm_vcpu *vcpu, |
| u64 __user *uindices); |
| int kvm_riscv_vcpu_get_reg(struct kvm_vcpu *vcpu, |
| const struct kvm_one_reg *reg); |
| int kvm_riscv_vcpu_set_reg(struct kvm_vcpu *vcpu, |
| const struct kvm_one_reg *reg); |
| |
| int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq); |
| int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq); |
| void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu); |
| void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu); |
| bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask); |
| void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu); |
| void kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu); |
| void __kvm_riscv_vcpu_power_on(struct kvm_vcpu *vcpu); |
| void kvm_riscv_vcpu_power_on(struct kvm_vcpu *vcpu); |
| bool kvm_riscv_vcpu_stopped(struct kvm_vcpu *vcpu); |
| |
| void kvm_riscv_vcpu_sbi_sta_reset(struct kvm_vcpu *vcpu); |
| void kvm_riscv_vcpu_record_steal_time(struct kvm_vcpu *vcpu); |
| |
| #endif /* __RISCV_KVM_HOST_H__ */ |