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/cpufeature.h> |
15 | #include <asm/sbi.h> |
16 | |
17 | long kvm_arch_dev_ioctl(struct file *filp, |
18 | unsigned int ioctl, unsigned long arg) |
19 | { |
20 | return -EINVAL; |
21 | } |
22 | |
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 | kvm_riscv_aia_enable(); |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | void kvm_arch_hardware_disable(void) |
53 | { |
54 | kvm_riscv_aia_disable(); |
55 | |
56 | /* |
57 | * After clearing the hideleg CSR, the host kernel will receive |
58 | * spurious interrupts if hvip CSR has pending interrupts and the |
59 | * corresponding enable bits in vsie CSR are asserted. To avoid it, |
60 | * hvip CSR and vsie CSR must be cleared before clearing hideleg CSR. |
61 | */ |
62 | csr_write(CSR_VSIE, 0); |
63 | csr_write(CSR_HVIP, 0); |
64 | csr_write(CSR_HEDELEG, 0); |
65 | csr_write(CSR_HIDELEG, 0); |
66 | } |
67 | |
68 | static int __init riscv_kvm_init(void) |
69 | { |
70 | int rc; |
71 | const char *str; |
72 | |
73 | if (!riscv_isa_extension_available(NULL, h)) { |
74 | kvm_info("hypervisor extension not available\n" ); |
75 | return -ENODEV; |
76 | } |
77 | |
78 | if (sbi_spec_is_0_1()) { |
79 | kvm_info("require SBI v0.2 or higher\n" ); |
80 | return -ENODEV; |
81 | } |
82 | |
83 | if (!sbi_probe_extension(SBI_EXT_RFENCE)) { |
84 | kvm_info("require SBI RFENCE extension\n" ); |
85 | return -ENODEV; |
86 | } |
87 | |
88 | kvm_riscv_gstage_mode_detect(); |
89 | |
90 | kvm_riscv_gstage_vmid_detect(); |
91 | |
92 | rc = kvm_riscv_aia_init(); |
93 | if (rc && rc != -ENODEV) |
94 | return rc; |
95 | |
96 | kvm_info("hypervisor extension available\n" ); |
97 | |
98 | switch (kvm_riscv_gstage_mode()) { |
99 | case HGATP_MODE_SV32X4: |
100 | str = "Sv32x4" ; |
101 | break; |
102 | case HGATP_MODE_SV39X4: |
103 | str = "Sv39x4" ; |
104 | break; |
105 | case HGATP_MODE_SV48X4: |
106 | str = "Sv48x4" ; |
107 | break; |
108 | case HGATP_MODE_SV57X4: |
109 | str = "Sv57x4" ; |
110 | break; |
111 | default: |
112 | return -ENODEV; |
113 | } |
114 | kvm_info("using %s G-stage page table format\n" , str); |
115 | |
116 | kvm_info("VMID %ld bits available\n" , kvm_riscv_gstage_vmid_bits()); |
117 | |
118 | if (kvm_riscv_aia_available()) |
119 | kvm_info("AIA available with %d guest external interrupts\n" , |
120 | kvm_riscv_aia_nr_hgei); |
121 | |
122 | rc = kvm_init(vcpu_size: sizeof(struct kvm_vcpu), vcpu_align: 0, THIS_MODULE); |
123 | if (rc) { |
124 | kvm_riscv_aia_exit(); |
125 | return rc; |
126 | } |
127 | |
128 | return 0; |
129 | } |
130 | module_init(riscv_kvm_init); |
131 | |
132 | static void __exit riscv_kvm_exit(void) |
133 | { |
134 | kvm_riscv_aia_exit(); |
135 | |
136 | kvm_exit(); |
137 | } |
138 | module_exit(riscv_kvm_exit); |
139 | |