xref: /linux/arch/riscv/kvm/main.c (revision 509d3f45847627f4c5cdce004c3ec79262b5239c)
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 
18 DEFINE_STATIC_KEY_FALSE(kvm_riscv_vsstage_tlb_no_gpa);
19 
20 static 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 
30 long kvm_arch_dev_ioctl(struct file *filp,
31 			unsigned int ioctl, unsigned long arg)
32 {
33 	return -EINVAL;
34 }
35 
36 int 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 
57 void 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 
75 static void kvm_riscv_teardown(void)
76 {
77 	kvm_riscv_aia_exit();
78 	kvm_riscv_nacl_exit();
79 	kvm_unregister_perf_callbacks();
80 }
81 
82 static 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(slist, ", ");
142 			strcat(slist, "sync_csr");
143 			rc++;
144 		}
145 		if (kvm_riscv_nacl_sync_hfence_available()) {
146 			if (rc)
147 				strcat(slist, ", ");
148 			strcat(slist, "sync_hfence");
149 			rc++;
150 		}
151 		if (kvm_riscv_nacl_sync_sret_available()) {
152 			if (rc)
153 				strcat(slist, ", ");
154 			strcat(slist, "sync_sret");
155 			rc++;
156 		}
157 		if (kvm_riscv_nacl_autoswap_csr_available()) {
158 			if (rc)
159 				strcat(slist, ", ");
160 			strcat(slist, "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(sizeof(struct kvm_vcpu), 0, THIS_MODULE);
180 	if (rc) {
181 		kvm_riscv_teardown();
182 		return rc;
183 	}
184 
185 	return 0;
186 }
187 module_init(riscv_kvm_init);
188 
189 static void __exit riscv_kvm_exit(void)
190 {
191 	kvm_exit();
192 
193 	kvm_riscv_teardown();
194 }
195 module_exit(riscv_kvm_exit);
196