1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2019 Western Digital Corporation or its affiliates.
4 *
5 * Authors:
6 * Anup Patel <anup.patel@wdc.com>
7 */
8
9#include <linux/errno.h>
10#include <linux/err.h>
11#include <linux/module.h>
12#include <linux/kvm_host.h>
13#include <asm/cpufeature.h>
14#include <asm/kvm_mmu.h>
15#include <asm/kvm_nacl.h>
16#include <asm/sbi.h>
17
18DEFINE_STATIC_KEY_FALSE(kvm_riscv_vsstage_tlb_no_gpa);
19
20static void kvm_riscv_setup_vendor_features(void)
21{
22 /* Andes AX66: split two-stage TLBs */
23 if (riscv_cached_mvendorid(0) == ANDES_VENDOR_ID &&
24 (riscv_cached_marchid(0) & 0xFFFF) == 0x8A66) {
25 static_branch_enable(&kvm_riscv_vsstage_tlb_no_gpa);
26 kvm_info("VS-stage TLB does not cache guest physical address and VMID\n");
27 }
28}
29
30long kvm_arch_dev_ioctl(struct file *filp,
31 unsigned int ioctl, unsigned long arg)
32{
33 return -EINVAL;
34}
35
36int kvm_arch_enable_virtualization_cpu(void)
37{
38 int rc;
39
40 rc = kvm_riscv_nacl_enable();
41 if (rc)
42 return rc;
43
44 csr_write(CSR_HEDELEG, KVM_HEDELEG_DEFAULT);
45 csr_write(CSR_HIDELEG, KVM_HIDELEG_DEFAULT);
46
47 /* VS should access only the time counter directly. Everything else should trap */
48 csr_write(CSR_HCOUNTEREN, 0x02);
49
50 csr_write(CSR_HVIP, 0);
51
52 kvm_riscv_aia_enable();
53
54 return 0;
55}
56
57void kvm_arch_disable_virtualization_cpu(void)
58{
59 kvm_riscv_aia_disable();
60
61 /*
62 * After clearing the hideleg CSR, the host kernel will receive
63 * spurious interrupts if hvip CSR has pending interrupts and the
64 * corresponding enable bits in vsie CSR are asserted. To avoid it,
65 * hvip CSR and vsie CSR must be cleared before clearing hideleg CSR.
66 */
67 csr_write(CSR_VSIE, 0);
68 csr_write(CSR_HVIP, 0);
69 csr_write(CSR_HEDELEG, 0);
70 csr_write(CSR_HIDELEG, 0);
71
72 kvm_riscv_nacl_disable();
73}
74
75static void kvm_riscv_teardown(void)
76{
77 kvm_riscv_aia_exit();
78 kvm_riscv_nacl_exit();
79 kvm_unregister_perf_callbacks();
80}
81
82static int __init riscv_kvm_init(void)
83{
84 int rc;
85 char slist[64];
86 const char *str;
87
88 if (!riscv_isa_extension_available(NULL, h)) {
89 kvm_info("hypervisor extension not available\n");
90 return -ENODEV;
91 }
92
93 if (sbi_spec_is_0_1()) {
94 kvm_info("require SBI v0.2 or higher\n");
95 return -ENODEV;
96 }
97
98 if (!sbi_probe_extension(SBI_EXT_RFENCE)) {
99 kvm_info("require SBI RFENCE extension\n");
100 return -ENODEV;
101 }
102
103 rc = kvm_riscv_nacl_init();
104 if (rc && rc != -ENODEV)
105 return rc;
106
107 kvm_riscv_gstage_mode_detect();
108 switch (kvm_riscv_gstage_mode) {
109 case HGATP_MODE_SV32X4:
110 str = "Sv32x4";
111 break;
112 case HGATP_MODE_SV39X4:
113 str = "Sv39x4";
114 break;
115 case HGATP_MODE_SV48X4:
116 str = "Sv48x4";
117 break;
118 case HGATP_MODE_SV57X4:
119 str = "Sv57x4";
120 break;
121 default:
122 kvm_riscv_nacl_exit();
123 return -ENODEV;
124 }
125
126 kvm_riscv_gstage_vmid_detect();
127
128 rc = kvm_riscv_aia_init();
129 if (rc && rc != -ENODEV) {
130 kvm_riscv_nacl_exit();
131 return rc;
132 }
133
134 kvm_info("hypervisor extension available\n");
135
136 if (kvm_riscv_nacl_available()) {
137 rc = 0;
138 slist[0] = '\0';
139 if (kvm_riscv_nacl_sync_csr_available()) {
140 if (rc)
141 strcat(p: slist, q: ", ");
142 strcat(p: slist, q: "sync_csr");
143 rc++;
144 }
145 if (kvm_riscv_nacl_sync_hfence_available()) {
146 if (rc)
147 strcat(p: slist, q: ", ");
148 strcat(p: slist, q: "sync_hfence");
149 rc++;
150 }
151 if (kvm_riscv_nacl_sync_sret_available()) {
152 if (rc)
153 strcat(p: slist, q: ", ");
154 strcat(p: slist, q: "sync_sret");
155 rc++;
156 }
157 if (kvm_riscv_nacl_autoswap_csr_available()) {
158 if (rc)
159 strcat(p: slist, q: ", ");
160 strcat(p: slist, q: "autoswap_csr");
161 rc++;
162 }
163 kvm_info("using SBI nested acceleration with %s\n",
164 (rc) ? slist : "no features");
165 }
166
167 kvm_info("using %s G-stage page table format\n", str);
168
169 kvm_info("VMID %ld bits available\n", kvm_riscv_gstage_vmid_bits());
170
171 if (kvm_riscv_aia_available())
172 kvm_info("AIA available with %d guest external interrupts\n",
173 kvm_riscv_aia_nr_hgei);
174
175 kvm_riscv_setup_vendor_features();
176
177 kvm_register_perf_callbacks(NULL);
178
179 rc = kvm_init(vcpu_size: sizeof(struct kvm_vcpu), vcpu_align: 0, THIS_MODULE);
180 if (rc) {
181 kvm_riscv_teardown();
182 return rc;
183 }
184
185 return 0;
186}
187module_init(riscv_kvm_init);
188
189static void __exit riscv_kvm_exit(void)
190{
191 kvm_exit();
192
193 kvm_riscv_teardown();
194}
195module_exit(riscv_kvm_exit);
196

source code of linux/arch/riscv/kvm/main.c