Linux Audio

Check our new training course

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