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/csr.h>
14 #include <asm/hwcap.h>
15 #include <asm/sbi.h>
16
kvm_arch_dev_ioctl(struct file * filp,unsigned int ioctl,unsigned long arg)17 long kvm_arch_dev_ioctl(struct file *filp,
18 unsigned int ioctl, unsigned long arg)
19 {
20 return -EINVAL;
21 }
22
kvm_arch_hardware_enable(void)23 int kvm_arch_hardware_enable(void)
24 {
25 unsigned long hideleg, hedeleg;
26
27 hedeleg = 0;
28 hedeleg |= (1UL << EXC_INST_MISALIGNED);
29 hedeleg |= (1UL << EXC_BREAKPOINT);
30 hedeleg |= (1UL << EXC_SYSCALL);
31 hedeleg |= (1UL << EXC_INST_PAGE_FAULT);
32 hedeleg |= (1UL << EXC_LOAD_PAGE_FAULT);
33 hedeleg |= (1UL << EXC_STORE_PAGE_FAULT);
34 csr_write(CSR_HEDELEG, hedeleg);
35
36 hideleg = 0;
37 hideleg |= (1UL << IRQ_VS_SOFT);
38 hideleg |= (1UL << IRQ_VS_TIMER);
39 hideleg |= (1UL << IRQ_VS_EXT);
40 csr_write(CSR_HIDELEG, hideleg);
41
42 /* VS should access only the time counter directly. Everything else should trap */
43 csr_write(CSR_HCOUNTEREN, 0x02);
44
45 csr_write(CSR_HVIP, 0);
46
47 return 0;
48 }
49
kvm_arch_hardware_disable(void)50 void kvm_arch_hardware_disable(void)
51 {
52 /*
53 * After clearing the hideleg CSR, the host kernel will receive
54 * spurious interrupts if hvip CSR has pending interrupts and the
55 * corresponding enable bits in vsie CSR are asserted. To avoid it,
56 * hvip CSR and vsie CSR must be cleared before clearing hideleg CSR.
57 */
58 csr_write(CSR_VSIE, 0);
59 csr_write(CSR_HVIP, 0);
60 csr_write(CSR_HEDELEG, 0);
61 csr_write(CSR_HIDELEG, 0);
62 }
63
riscv_kvm_init(void)64 static int __init riscv_kvm_init(void)
65 {
66 const char *str;
67
68 if (!riscv_isa_extension_available(NULL, h)) {
69 kvm_info("hypervisor extension not available\n");
70 return -ENODEV;
71 }
72
73 if (sbi_spec_is_0_1()) {
74 kvm_info("require SBI v0.2 or higher\n");
75 return -ENODEV;
76 }
77
78 if (sbi_probe_extension(SBI_EXT_RFENCE) <= 0) {
79 kvm_info("require SBI RFENCE extension\n");
80 return -ENODEV;
81 }
82
83 kvm_riscv_gstage_mode_detect();
84
85 kvm_riscv_gstage_vmid_detect();
86
87 kvm_info("hypervisor extension available\n");
88
89 switch (kvm_riscv_gstage_mode()) {
90 case HGATP_MODE_SV32X4:
91 str = "Sv32x4";
92 break;
93 case HGATP_MODE_SV39X4:
94 str = "Sv39x4";
95 break;
96 case HGATP_MODE_SV48X4:
97 str = "Sv48x4";
98 break;
99 case HGATP_MODE_SV57X4:
100 str = "Sv57x4";
101 break;
102 default:
103 return -ENODEV;
104 }
105 kvm_info("using %s G-stage page table format\n", str);
106
107 kvm_info("VMID %ld bits available\n", kvm_riscv_gstage_vmid_bits());
108
109 return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE);
110 }
111 module_init(riscv_kvm_init);
112
riscv_kvm_exit(void)113 static void __exit riscv_kvm_exit(void)
114 {
115 kvm_exit();
116 }
117 module_exit(riscv_kvm_exit);
118