xref: /openbmc/linux/arch/riscv/kvm/vcpu_exit.c (revision dff03381)
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/kvm_host.h>
10 #include <asm/csr.h>
11 
12 static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run,
13 			     struct kvm_cpu_trap *trap)
14 {
15 	struct kvm_memory_slot *memslot;
16 	unsigned long hva, fault_addr;
17 	bool writable;
18 	gfn_t gfn;
19 	int ret;
20 
21 	fault_addr = (trap->htval << 2) | (trap->stval & 0x3);
22 	gfn = fault_addr >> PAGE_SHIFT;
23 	memslot = gfn_to_memslot(vcpu->kvm, gfn);
24 	hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable);
25 
26 	if (kvm_is_error_hva(hva) ||
27 	    (trap->scause == EXC_STORE_GUEST_PAGE_FAULT && !writable)) {
28 		switch (trap->scause) {
29 		case EXC_LOAD_GUEST_PAGE_FAULT:
30 			return kvm_riscv_vcpu_mmio_load(vcpu, run,
31 							fault_addr,
32 							trap->htinst);
33 		case EXC_STORE_GUEST_PAGE_FAULT:
34 			return kvm_riscv_vcpu_mmio_store(vcpu, run,
35 							 fault_addr,
36 							 trap->htinst);
37 		default:
38 			return -EOPNOTSUPP;
39 		};
40 	}
41 
42 	ret = kvm_riscv_gstage_map(vcpu, memslot, fault_addr, hva,
43 		(trap->scause == EXC_STORE_GUEST_PAGE_FAULT) ? true : false);
44 	if (ret < 0)
45 		return ret;
46 
47 	return 1;
48 }
49 
50 /**
51  * kvm_riscv_vcpu_unpriv_read -- Read machine word from Guest memory
52  *
53  * @vcpu: The VCPU pointer
54  * @read_insn: Flag representing whether we are reading instruction
55  * @guest_addr: Guest address to read
56  * @trap: Output pointer to trap details
57  */
58 unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu,
59 					 bool read_insn,
60 					 unsigned long guest_addr,
61 					 struct kvm_cpu_trap *trap)
62 {
63 	register unsigned long taddr asm("a0") = (unsigned long)trap;
64 	register unsigned long ttmp asm("a1");
65 	register unsigned long val asm("t0");
66 	register unsigned long tmp asm("t1");
67 	register unsigned long addr asm("t2") = guest_addr;
68 	unsigned long flags;
69 	unsigned long old_stvec, old_hstatus;
70 
71 	local_irq_save(flags);
72 
73 	old_hstatus = csr_swap(CSR_HSTATUS, vcpu->arch.guest_context.hstatus);
74 	old_stvec = csr_swap(CSR_STVEC, (ulong)&__kvm_riscv_unpriv_trap);
75 
76 	if (read_insn) {
77 		/*
78 		 * HLVX.HU instruction
79 		 * 0110010 00011 rs1 100 rd 1110011
80 		 */
81 		asm volatile ("\n"
82 			".option push\n"
83 			".option norvc\n"
84 			"add %[ttmp], %[taddr], 0\n"
85 			/*
86 			 * HLVX.HU %[val], (%[addr])
87 			 * HLVX.HU t0, (t2)
88 			 * 0110010 00011 00111 100 00101 1110011
89 			 */
90 			".word 0x6433c2f3\n"
91 			"andi %[tmp], %[val], 3\n"
92 			"addi %[tmp], %[tmp], -3\n"
93 			"bne %[tmp], zero, 2f\n"
94 			"addi %[addr], %[addr], 2\n"
95 			/*
96 			 * HLVX.HU %[tmp], (%[addr])
97 			 * HLVX.HU t1, (t2)
98 			 * 0110010 00011 00111 100 00110 1110011
99 			 */
100 			".word 0x6433c373\n"
101 			"sll %[tmp], %[tmp], 16\n"
102 			"add %[val], %[val], %[tmp]\n"
103 			"2:\n"
104 			".option pop"
105 		: [val] "=&r" (val), [tmp] "=&r" (tmp),
106 		  [taddr] "+&r" (taddr), [ttmp] "+&r" (ttmp),
107 		  [addr] "+&r" (addr) : : "memory");
108 
109 		if (trap->scause == EXC_LOAD_PAGE_FAULT)
110 			trap->scause = EXC_INST_PAGE_FAULT;
111 	} else {
112 		/*
113 		 * HLV.D instruction
114 		 * 0110110 00000 rs1 100 rd 1110011
115 		 *
116 		 * HLV.W instruction
117 		 * 0110100 00000 rs1 100 rd 1110011
118 		 */
119 		asm volatile ("\n"
120 			".option push\n"
121 			".option norvc\n"
122 			"add %[ttmp], %[taddr], 0\n"
123 #ifdef CONFIG_64BIT
124 			/*
125 			 * HLV.D %[val], (%[addr])
126 			 * HLV.D t0, (t2)
127 			 * 0110110 00000 00111 100 00101 1110011
128 			 */
129 			".word 0x6c03c2f3\n"
130 #else
131 			/*
132 			 * HLV.W %[val], (%[addr])
133 			 * HLV.W t0, (t2)
134 			 * 0110100 00000 00111 100 00101 1110011
135 			 */
136 			".word 0x6803c2f3\n"
137 #endif
138 			".option pop"
139 		: [val] "=&r" (val),
140 		  [taddr] "+&r" (taddr), [ttmp] "+&r" (ttmp)
141 		: [addr] "r" (addr) : "memory");
142 	}
143 
144 	csr_write(CSR_STVEC, old_stvec);
145 	csr_write(CSR_HSTATUS, old_hstatus);
146 
147 	local_irq_restore(flags);
148 
149 	return val;
150 }
151 
152 /**
153  * kvm_riscv_vcpu_trap_redirect -- Redirect trap to Guest
154  *
155  * @vcpu: The VCPU pointer
156  * @trap: Trap details
157  */
158 void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu,
159 				  struct kvm_cpu_trap *trap)
160 {
161 	unsigned long vsstatus = csr_read(CSR_VSSTATUS);
162 
163 	/* Change Guest SSTATUS.SPP bit */
164 	vsstatus &= ~SR_SPP;
165 	if (vcpu->arch.guest_context.sstatus & SR_SPP)
166 		vsstatus |= SR_SPP;
167 
168 	/* Change Guest SSTATUS.SPIE bit */
169 	vsstatus &= ~SR_SPIE;
170 	if (vsstatus & SR_SIE)
171 		vsstatus |= SR_SPIE;
172 
173 	/* Clear Guest SSTATUS.SIE bit */
174 	vsstatus &= ~SR_SIE;
175 
176 	/* Update Guest SSTATUS */
177 	csr_write(CSR_VSSTATUS, vsstatus);
178 
179 	/* Update Guest SCAUSE, STVAL, and SEPC */
180 	csr_write(CSR_VSCAUSE, trap->scause);
181 	csr_write(CSR_VSTVAL, trap->stval);
182 	csr_write(CSR_VSEPC, trap->sepc);
183 
184 	/* Set Guest PC to Guest exception vector */
185 	vcpu->arch.guest_context.sepc = csr_read(CSR_VSTVEC);
186 }
187 
188 /*
189  * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
190  * proper exit to userspace.
191  */
192 int kvm_riscv_vcpu_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
193 			struct kvm_cpu_trap *trap)
194 {
195 	int ret;
196 
197 	/* If we got host interrupt then do nothing */
198 	if (trap->scause & CAUSE_IRQ_FLAG)
199 		return 1;
200 
201 	/* Handle guest traps */
202 	ret = -EFAULT;
203 	run->exit_reason = KVM_EXIT_UNKNOWN;
204 	switch (trap->scause) {
205 	case EXC_VIRTUAL_INST_FAULT:
206 		if (vcpu->arch.guest_context.hstatus & HSTATUS_SPV)
207 			ret = kvm_riscv_vcpu_virtual_insn(vcpu, run, trap);
208 		break;
209 	case EXC_INST_GUEST_PAGE_FAULT:
210 	case EXC_LOAD_GUEST_PAGE_FAULT:
211 	case EXC_STORE_GUEST_PAGE_FAULT:
212 		if (vcpu->arch.guest_context.hstatus & HSTATUS_SPV)
213 			ret = gstage_page_fault(vcpu, run, trap);
214 		break;
215 	case EXC_SUPERVISOR_SYSCALL:
216 		if (vcpu->arch.guest_context.hstatus & HSTATUS_SPV)
217 			ret = kvm_riscv_vcpu_sbi_ecall(vcpu, run);
218 		break;
219 	default:
220 		break;
221 	}
222 
223 	/* Print details in-case of error */
224 	if (ret < 0) {
225 		kvm_err("VCPU exit error %d\n", ret);
226 		kvm_err("SEPC=0x%lx SSTATUS=0x%lx HSTATUS=0x%lx\n",
227 			vcpu->arch.guest_context.sepc,
228 			vcpu->arch.guest_context.sstatus,
229 			vcpu->arch.guest_context.hstatus);
230 		kvm_err("SCAUSE=0x%lx STVAL=0x%lx HTVAL=0x%lx HTINST=0x%lx\n",
231 			trap->scause, trap->stval, trap->htval, trap->htinst);
232 	}
233 
234 	return ret;
235 }
236