1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2021 Western Digital Corporation or its affiliates. 4 * 5 * Authors: 6 * Atish Patra <atish.patra@wdc.com> 7 */ 8 9 #include <linux/errno.h> 10 #include <linux/err.h> 11 #include <linux/kvm_host.h> 12 #include <asm/csr.h> 13 #include <asm/sbi.h> 14 #include <asm/kvm_vcpu_timer.h> 15 #include <asm/kvm_vcpu_sbi.h> 16 17 static int kvm_sbi_ext_v01_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, 18 unsigned long *out_val, 19 struct kvm_cpu_trap *utrap, 20 bool *exit) 21 { 22 ulong hmask; 23 int i, ret = 0; 24 u64 next_cycle; 25 struct kvm_vcpu *rvcpu; 26 struct kvm *kvm = vcpu->kvm; 27 struct kvm_cpu_context *cp = &vcpu->arch.guest_context; 28 29 switch (cp->a7) { 30 case SBI_EXT_0_1_CONSOLE_GETCHAR: 31 case SBI_EXT_0_1_CONSOLE_PUTCHAR: 32 /* 33 * The CONSOLE_GETCHAR/CONSOLE_PUTCHAR SBI calls cannot be 34 * handled in kernel so we forward these to user-space 35 */ 36 kvm_riscv_vcpu_sbi_forward(vcpu, run); 37 *exit = true; 38 break; 39 case SBI_EXT_0_1_SET_TIMER: 40 #if __riscv_xlen == 32 41 next_cycle = ((u64)cp->a1 << 32) | (u64)cp->a0; 42 #else 43 next_cycle = (u64)cp->a0; 44 #endif 45 ret = kvm_riscv_vcpu_timer_next_event(vcpu, next_cycle); 46 break; 47 case SBI_EXT_0_1_CLEAR_IPI: 48 ret = kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_SOFT); 49 break; 50 case SBI_EXT_0_1_SEND_IPI: 51 if (cp->a0) 52 hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0, 53 utrap); 54 else 55 hmask = (1UL << atomic_read(&kvm->online_vcpus)) - 1; 56 if (utrap->scause) 57 break; 58 59 for_each_set_bit(i, &hmask, BITS_PER_LONG) { 60 rvcpu = kvm_get_vcpu_by_id(vcpu->kvm, i); 61 ret = kvm_riscv_vcpu_set_interrupt(rvcpu, IRQ_VS_SOFT); 62 if (ret < 0) 63 break; 64 } 65 break; 66 case SBI_EXT_0_1_SHUTDOWN: 67 kvm_riscv_vcpu_sbi_system_reset(vcpu, run, 68 KVM_SYSTEM_EVENT_SHUTDOWN, 0); 69 *exit = true; 70 break; 71 case SBI_EXT_0_1_REMOTE_FENCE_I: 72 case SBI_EXT_0_1_REMOTE_SFENCE_VMA: 73 case SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID: 74 if (cp->a0) 75 hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0, 76 utrap); 77 else 78 hmask = (1UL << atomic_read(&kvm->online_vcpus)) - 1; 79 if (utrap->scause) 80 break; 81 82 if (cp->a7 == SBI_EXT_0_1_REMOTE_FENCE_I) 83 kvm_riscv_fence_i(vcpu->kvm, 0, hmask); 84 else if (cp->a7 == SBI_EXT_0_1_REMOTE_SFENCE_VMA) { 85 if (cp->a1 == 0 && cp->a2 == 0) 86 kvm_riscv_hfence_vvma_all(vcpu->kvm, 87 0, hmask); 88 else 89 kvm_riscv_hfence_vvma_gva(vcpu->kvm, 90 0, hmask, 91 cp->a1, cp->a2, 92 PAGE_SHIFT); 93 } else { 94 if (cp->a1 == 0 && cp->a2 == 0) 95 kvm_riscv_hfence_vvma_asid_all(vcpu->kvm, 96 0, hmask, 97 cp->a3); 98 else 99 kvm_riscv_hfence_vvma_asid_gva(vcpu->kvm, 100 0, hmask, 101 cp->a1, cp->a2, 102 PAGE_SHIFT, 103 cp->a3); 104 } 105 break; 106 default: 107 ret = -EINVAL; 108 break; 109 } 110 111 return ret; 112 } 113 114 const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = { 115 .extid_start = SBI_EXT_0_1_SET_TIMER, 116 .extid_end = SBI_EXT_0_1_SHUTDOWN, 117 .handler = kvm_sbi_ext_v01_handler, 118 }; 119