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/sbi.h> |
13 | #include <asm/kvm_vcpu_timer.h> |
14 | #include <asm/kvm_vcpu_sbi.h> |
15 | |
16 | static int kvm_sbi_ext_v01_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, |
17 | struct kvm_vcpu_sbi_return *retdata) |
18 | { |
19 | ulong hmask; |
20 | int i, ret = 0; |
21 | u64 next_cycle; |
22 | struct kvm_vcpu *rvcpu; |
23 | struct kvm *kvm = vcpu->kvm; |
24 | struct kvm_cpu_context *cp = &vcpu->arch.guest_context; |
25 | struct kvm_cpu_trap *utrap = retdata->utrap; |
26 | |
27 | switch (cp->a7) { |
28 | case SBI_EXT_0_1_CONSOLE_GETCHAR: |
29 | case SBI_EXT_0_1_CONSOLE_PUTCHAR: |
30 | /* |
31 | * The CONSOLE_GETCHAR/CONSOLE_PUTCHAR SBI calls cannot be |
32 | * handled in kernel so we forward these to user-space |
33 | */ |
34 | kvm_riscv_vcpu_sbi_forward(vcpu, run); |
35 | retdata->uexit = true; |
36 | break; |
37 | case SBI_EXT_0_1_SET_TIMER: |
38 | #if __riscv_xlen == 32 |
39 | next_cycle = ((u64)cp->a1 << 32) | (u64)cp->a0; |
40 | #else |
41 | next_cycle = (u64)cp->a0; |
42 | #endif |
43 | ret = kvm_riscv_vcpu_timer_next_event(vcpu, next_cycle); |
44 | break; |
45 | case SBI_EXT_0_1_CLEAR_IPI: |
46 | ret = kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_SOFT); |
47 | break; |
48 | case SBI_EXT_0_1_SEND_IPI: |
49 | if (cp->a0) |
50 | hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0, utrap); |
51 | else |
52 | hmask = (1UL << atomic_read(v: &kvm->online_vcpus)) - 1; |
53 | if (utrap->scause) |
54 | break; |
55 | |
56 | for_each_set_bit(i, &hmask, BITS_PER_LONG) { |
57 | rvcpu = kvm_get_vcpu_by_id(kvm: vcpu->kvm, id: i); |
58 | ret = kvm_riscv_vcpu_set_interrupt(rvcpu, IRQ_VS_SOFT); |
59 | if (ret < 0) |
60 | break; |
61 | } |
62 | break; |
63 | case SBI_EXT_0_1_SHUTDOWN: |
64 | kvm_riscv_vcpu_sbi_system_reset(vcpu, run, |
65 | KVM_SYSTEM_EVENT_SHUTDOWN, 0); |
66 | retdata->uexit = true; |
67 | break; |
68 | case SBI_EXT_0_1_REMOTE_FENCE_I: |
69 | case SBI_EXT_0_1_REMOTE_SFENCE_VMA: |
70 | case SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID: |
71 | if (cp->a0) |
72 | hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0, utrap); |
73 | else |
74 | hmask = (1UL << atomic_read(v: &kvm->online_vcpus)) - 1; |
75 | if (utrap->scause) |
76 | break; |
77 | |
78 | if (cp->a7 == SBI_EXT_0_1_REMOTE_FENCE_I) |
79 | kvm_riscv_fence_i(vcpu->kvm, 0, hmask); |
80 | else if (cp->a7 == SBI_EXT_0_1_REMOTE_SFENCE_VMA) { |
81 | if (cp->a1 == 0 && cp->a2 == 0) |
82 | kvm_riscv_hfence_vvma_all(vcpu->kvm, |
83 | 0, hmask); |
84 | else |
85 | kvm_riscv_hfence_vvma_gva(vcpu->kvm, |
86 | 0, hmask, |
87 | cp->a1, cp->a2, |
88 | PAGE_SHIFT); |
89 | } else { |
90 | if (cp->a1 == 0 && cp->a2 == 0) |
91 | kvm_riscv_hfence_vvma_asid_all(vcpu->kvm, |
92 | 0, hmask, |
93 | cp->a3); |
94 | else |
95 | kvm_riscv_hfence_vvma_asid_gva(vcpu->kvm, |
96 | 0, hmask, |
97 | cp->a1, cp->a2, |
98 | PAGE_SHIFT, |
99 | cp->a3); |
100 | } |
101 | break; |
102 | default: |
103 | retdata->err_val = SBI_ERR_NOT_SUPPORTED; |
104 | break; |
105 | } |
106 | |
107 | return ret; |
108 | } |
109 | |
110 | const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = { |
111 | .extid_start = SBI_EXT_0_1_SET_TIMER, |
112 | .extid_end = SBI_EXT_0_1_SHUTDOWN, |
113 | .handler = kvm_sbi_ext_v01_handler, |
114 | }; |
115 | |