| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (C) 2020 - Google Inc |
| * Author: Andrew Scull <ascull@google.com> |
| */ |
| |
| #include <asm/kvm_asm.h> |
| #include <asm/kvm_hyp.h> |
| |
| #include <kvm/arm_hypercalls.h> |
| |
| typedef unsigned long (*hypcall_fn_t)(unsigned long, unsigned long, unsigned long); |
| |
| DEFINE_PER_CPU(struct kvm_nvhe_hyp_params, kvm_nvhe_hyp_params); |
| DEFINE_PER_CPU(struct kvm_vcpu, kvm_host_vcpu); |
| |
| static void handle_host_hcall(struct kvm_vcpu *host_vcpu) |
| { |
| unsigned long ret = 0; |
| |
| switch (smccc_get_function(host_vcpu)) { |
| case KVM_HOST_SMCCC_FUNC(__kvm_flush_vm_context): |
| __kvm_flush_vm_context(); |
| break; |
| case KVM_HOST_SMCCC_FUNC(__kvm_tlb_flush_vmid_ipa): { |
| struct kvm *kvm = |
| (struct kvm *)smccc_get_arg1(host_vcpu); |
| phys_addr_t ipa = smccc_get_arg2(host_vcpu); |
| |
| __kvm_tlb_flush_vmid_ipa(kvm, ipa); |
| break; |
| } |
| case KVM_HOST_SMCCC_FUNC(__kvm_tlb_flush_vmid): { |
| struct kvm *kvm = |
| (struct kvm *)smccc_get_arg1(host_vcpu); |
| |
| __kvm_tlb_flush_vmid(kvm); |
| break; |
| } |
| case KVM_HOST_SMCCC_FUNC(__kvm_tlb_flush_local_vmid): { |
| struct kvm_vcpu *vcpu = |
| (struct kvm_vcpu *)smccc_get_arg1(host_vcpu); |
| |
| __kvm_tlb_flush_local_vmid(vcpu); |
| break; |
| } |
| case KVM_HOST_SMCCC_FUNC(__kvm_timer_set_cntvoff): { |
| u64 cntvoff = smccc_get_arg1(host_vcpu); |
| |
| __kvm_timer_set_cntvoff(cntvoff); |
| break; |
| } |
| case KVM_HOST_SMCCC_FUNC(__kvm_vcpu_run): { |
| struct kvm_vcpu *vcpu = |
| (struct kvm_vcpu *)smccc_get_arg1(host_vcpu); |
| |
| ret = __kvm_vcpu_run(vcpu); |
| break; |
| } |
| case KVM_HOST_SMCCC_FUNC(__kvm_enable_ssbs): |
| __kvm_enable_ssbs(); |
| break; |
| case KVM_HOST_SMCCC_FUNC(__vgic_v3_get_ich_vtr_el2): |
| ret = __vgic_v3_get_ich_vtr_el2(); |
| break; |
| case KVM_HOST_SMCCC_FUNC(__vgic_v3_read_vmcr): |
| ret = __vgic_v3_read_vmcr(); |
| break; |
| case KVM_HOST_SMCCC_FUNC(__vgic_v3_write_vmcr): { |
| u32 vmcr = smccc_get_arg1(host_vcpu); |
| |
| __vgic_v3_write_vmcr(vmcr); |
| break; |
| } |
| case KVM_HOST_SMCCC_FUNC(__vgic_v3_init_lrs): |
| __vgic_v3_init_lrs(); |
| break; |
| case KVM_HOST_SMCCC_FUNC(__kvm_get_mdcr_el2): |
| ret = __kvm_get_mdcr_el2(); |
| break; |
| case KVM_HOST_SMCCC_FUNC(__vgic_v3_save_aprs): { |
| struct vgic_v3_cpu_if *cpu_if = |
| (struct vgic_v3_cpu_if *)smccc_get_arg1(host_vcpu); |
| |
| __vgic_v3_save_aprs(cpu_if); |
| break; |
| } |
| case KVM_HOST_SMCCC_FUNC(__vgic_v3_restore_aprs): { |
| struct vgic_v3_cpu_if *cpu_if = |
| (struct vgic_v3_cpu_if *)smccc_get_arg1(host_vcpu); |
| |
| __vgic_v3_restore_aprs(cpu_if); |
| break; |
| } |
| default: |
| /* Invalid host HVC. */ |
| smccc_set_retval(host_vcpu, SMCCC_RET_NOT_SUPPORTED, 0, 0, 0); |
| return; |
| } |
| |
| smccc_set_retval(host_vcpu, SMCCC_RET_SUCCESS, ret, 0, 0); |
| } |
| |
| void __noreturn kvm_hyp_main(struct kvm_nvhe_hyp_params *params) |
| { |
| /* Set tpidr_el2 for use by HYP */ |
| struct kvm_vcpu *host_vcpu; |
| struct kvm_host_data *hyp_data; |
| struct kvm_cpu_context *hyp_ctxt; |
| |
| host_vcpu = this_cpu_ptr(&kvm_host_vcpu); |
| hyp_data = this_cpu_ptr(&kvm_host_data); |
| hyp_ctxt = &hyp_data->host_ctxt; |
| |
| __sysreg_save_state_nvhe(&host_vcpu->arch.ctxt); |
| |
| /* |
| * The first time entering the host is seen by the host as the return |
| * of the initialization HVC so mark it as successful. |
| */ |
| smccc_set_retval(host_vcpu, SMCCC_RET_SUCCESS, 0, 0, 0); |
| |
| while (true) { |
| u64 exit_code; |
| |
| /* |
| * Set the running cpu for the vectors to pass to __guest_exit |
| * so it can get the cpu context. |
| */ |
| hyp_ctxt->__hyp_running_vcpu = host_vcpu; |
| |
| /* |
| * Enter the host now that we feel like we're in charge. |
| * |
| * This should merge with __kvm_vcpu_run as host becomes more |
| * vcpu-like. |
| */ |
| exit_code = __guest_enter(host_vcpu, hyp_ctxt); |
| |
| /* TODO: handle exit codes properly */ |
| |
| if (exit_code == ARM_EXCEPTION_TRAP) |
| handle_host_hcall(host_vcpu); |
| } |
| } |