xref: /openbmc/linux/arch/arm64/kvm/mmio.c (revision 278002edb19bce2c628fafb0af936e77000f3a5b)
19ed24f4bSMarc Zyngier // SPDX-License-Identifier: GPL-2.0-only
29ed24f4bSMarc Zyngier /*
39ed24f4bSMarc Zyngier  * Copyright (C) 2012 - Virtual Open Systems and Columbia University
49ed24f4bSMarc Zyngier  * Author: Christoffer Dall <c.dall@virtualopensystems.com>
59ed24f4bSMarc Zyngier  */
69ed24f4bSMarc Zyngier 
79ed24f4bSMarc Zyngier #include <linux/kvm_host.h>
89ed24f4bSMarc Zyngier #include <asm/kvm_emulate.h>
99ed24f4bSMarc Zyngier #include <trace/events/kvm.h>
109ed24f4bSMarc Zyngier 
119ed24f4bSMarc Zyngier #include "trace.h"
129ed24f4bSMarc Zyngier 
kvm_mmio_write_buf(void * buf,unsigned int len,unsigned long data)139ed24f4bSMarc Zyngier void kvm_mmio_write_buf(void *buf, unsigned int len, unsigned long data)
149ed24f4bSMarc Zyngier {
159ed24f4bSMarc Zyngier 	void *datap = NULL;
169ed24f4bSMarc Zyngier 	union {
179ed24f4bSMarc Zyngier 		u8	byte;
189ed24f4bSMarc Zyngier 		u16	hword;
199ed24f4bSMarc Zyngier 		u32	word;
209ed24f4bSMarc Zyngier 		u64	dword;
219ed24f4bSMarc Zyngier 	} tmp;
229ed24f4bSMarc Zyngier 
239ed24f4bSMarc Zyngier 	switch (len) {
249ed24f4bSMarc Zyngier 	case 1:
259ed24f4bSMarc Zyngier 		tmp.byte	= data;
269ed24f4bSMarc Zyngier 		datap		= &tmp.byte;
279ed24f4bSMarc Zyngier 		break;
289ed24f4bSMarc Zyngier 	case 2:
299ed24f4bSMarc Zyngier 		tmp.hword	= data;
309ed24f4bSMarc Zyngier 		datap		= &tmp.hword;
319ed24f4bSMarc Zyngier 		break;
329ed24f4bSMarc Zyngier 	case 4:
339ed24f4bSMarc Zyngier 		tmp.word	= data;
349ed24f4bSMarc Zyngier 		datap		= &tmp.word;
359ed24f4bSMarc Zyngier 		break;
369ed24f4bSMarc Zyngier 	case 8:
379ed24f4bSMarc Zyngier 		tmp.dword	= data;
389ed24f4bSMarc Zyngier 		datap		= &tmp.dword;
399ed24f4bSMarc Zyngier 		break;
409ed24f4bSMarc Zyngier 	}
419ed24f4bSMarc Zyngier 
429ed24f4bSMarc Zyngier 	memcpy(buf, datap, len);
439ed24f4bSMarc Zyngier }
449ed24f4bSMarc Zyngier 
kvm_mmio_read_buf(const void * buf,unsigned int len)459ed24f4bSMarc Zyngier unsigned long kvm_mmio_read_buf(const void *buf, unsigned int len)
469ed24f4bSMarc Zyngier {
479ed24f4bSMarc Zyngier 	unsigned long data = 0;
489ed24f4bSMarc Zyngier 	union {
499ed24f4bSMarc Zyngier 		u16	hword;
509ed24f4bSMarc Zyngier 		u32	word;
519ed24f4bSMarc Zyngier 		u64	dword;
529ed24f4bSMarc Zyngier 	} tmp;
539ed24f4bSMarc Zyngier 
549ed24f4bSMarc Zyngier 	switch (len) {
559ed24f4bSMarc Zyngier 	case 1:
569ed24f4bSMarc Zyngier 		data = *(u8 *)buf;
579ed24f4bSMarc Zyngier 		break;
589ed24f4bSMarc Zyngier 	case 2:
599ed24f4bSMarc Zyngier 		memcpy(&tmp.hword, buf, len);
609ed24f4bSMarc Zyngier 		data = tmp.hword;
619ed24f4bSMarc Zyngier 		break;
629ed24f4bSMarc Zyngier 	case 4:
639ed24f4bSMarc Zyngier 		memcpy(&tmp.word, buf, len);
649ed24f4bSMarc Zyngier 		data = tmp.word;
659ed24f4bSMarc Zyngier 		break;
669ed24f4bSMarc Zyngier 	case 8:
679ed24f4bSMarc Zyngier 		memcpy(&tmp.dword, buf, len);
689ed24f4bSMarc Zyngier 		data = tmp.dword;
699ed24f4bSMarc Zyngier 		break;
709ed24f4bSMarc Zyngier 	}
719ed24f4bSMarc Zyngier 
729ed24f4bSMarc Zyngier 	return data;
739ed24f4bSMarc Zyngier }
749ed24f4bSMarc Zyngier 
kvm_pending_sync_exception(struct kvm_vcpu * vcpu)75*ea6b5d98SOliver Upton static bool kvm_pending_sync_exception(struct kvm_vcpu *vcpu)
76*ea6b5d98SOliver Upton {
77*ea6b5d98SOliver Upton 	if (!vcpu_get_flag(vcpu, PENDING_EXCEPTION))
78*ea6b5d98SOliver Upton 		return false;
79*ea6b5d98SOliver Upton 
80*ea6b5d98SOliver Upton 	if (vcpu_el1_is_32bit(vcpu)) {
81*ea6b5d98SOliver Upton 		switch (vcpu_get_flag(vcpu, EXCEPT_MASK)) {
82*ea6b5d98SOliver Upton 		case unpack_vcpu_flag(EXCEPT_AA32_UND):
83*ea6b5d98SOliver Upton 		case unpack_vcpu_flag(EXCEPT_AA32_IABT):
84*ea6b5d98SOliver Upton 		case unpack_vcpu_flag(EXCEPT_AA32_DABT):
85*ea6b5d98SOliver Upton 			return true;
86*ea6b5d98SOliver Upton 		default:
87*ea6b5d98SOliver Upton 			return false;
88*ea6b5d98SOliver Upton 		}
89*ea6b5d98SOliver Upton 	} else {
90*ea6b5d98SOliver Upton 		switch (vcpu_get_flag(vcpu, EXCEPT_MASK)) {
91*ea6b5d98SOliver Upton 		case unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC):
92*ea6b5d98SOliver Upton 		case unpack_vcpu_flag(EXCEPT_AA64_EL2_SYNC):
93*ea6b5d98SOliver Upton 			return true;
94*ea6b5d98SOliver Upton 		default:
95*ea6b5d98SOliver Upton 			return false;
96*ea6b5d98SOliver Upton 		}
97*ea6b5d98SOliver Upton 	}
98*ea6b5d98SOliver Upton }
99*ea6b5d98SOliver Upton 
1009ed24f4bSMarc Zyngier /**
1019ed24f4bSMarc Zyngier  * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation
1029ed24f4bSMarc Zyngier  *			     or in-kernel IO emulation
1039ed24f4bSMarc Zyngier  *
1049ed24f4bSMarc Zyngier  * @vcpu: The VCPU pointer
1059ed24f4bSMarc Zyngier  */
kvm_handle_mmio_return(struct kvm_vcpu * vcpu)10674cc7e0cSTianjia Zhang int kvm_handle_mmio_return(struct kvm_vcpu *vcpu)
1079ed24f4bSMarc Zyngier {
1089ed24f4bSMarc Zyngier 	unsigned long data;
1099ed24f4bSMarc Zyngier 	unsigned int len;
1109ed24f4bSMarc Zyngier 	int mask;
1119ed24f4bSMarc Zyngier 
112*ea6b5d98SOliver Upton 	/*
113*ea6b5d98SOliver Upton 	 * Detect if the MMIO return was already handled or if userspace aborted
114*ea6b5d98SOliver Upton 	 * the MMIO access.
115*ea6b5d98SOliver Upton 	 */
116*ea6b5d98SOliver Upton 	if (unlikely(!vcpu->mmio_needed || kvm_pending_sync_exception(vcpu)))
1173fe534a0SFuad Tabba 		return 1;
1189ed24f4bSMarc Zyngier 
1199ed24f4bSMarc Zyngier 	vcpu->mmio_needed = 0;
1209ed24f4bSMarc Zyngier 
1219ed24f4bSMarc Zyngier 	if (!kvm_vcpu_dabt_iswrite(vcpu)) {
12274cc7e0cSTianjia Zhang 		struct kvm_run *run = vcpu->run;
12374cc7e0cSTianjia Zhang 
1249ed24f4bSMarc Zyngier 		len = kvm_vcpu_dabt_get_as(vcpu);
1259ed24f4bSMarc Zyngier 		data = kvm_mmio_read_buf(run->mmio.data, len);
1269ed24f4bSMarc Zyngier 
1279ed24f4bSMarc Zyngier 		if (kvm_vcpu_dabt_issext(vcpu) &&
1289ed24f4bSMarc Zyngier 		    len < sizeof(unsigned long)) {
1299ed24f4bSMarc Zyngier 			mask = 1U << ((len * 8) - 1);
1309ed24f4bSMarc Zyngier 			data = (data ^ mask) - mask;
1319ed24f4bSMarc Zyngier 		}
1329ed24f4bSMarc Zyngier 
1339ed24f4bSMarc Zyngier 		if (!kvm_vcpu_dabt_issf(vcpu))
1349ed24f4bSMarc Zyngier 			data = data & 0xffffffff;
1359ed24f4bSMarc Zyngier 
1369ed24f4bSMarc Zyngier 		trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
1379ed24f4bSMarc Zyngier 			       &data);
1389ed24f4bSMarc Zyngier 		data = vcpu_data_host_to_guest(vcpu, data, len);
1399ed24f4bSMarc Zyngier 		vcpu_set_reg(vcpu, kvm_vcpu_dabt_get_rd(vcpu), data);
1409ed24f4bSMarc Zyngier 	}
1419ed24f4bSMarc Zyngier 
1429ed24f4bSMarc Zyngier 	/*
1439ed24f4bSMarc Zyngier 	 * The MMIO instruction is emulated and should not be re-executed
1449ed24f4bSMarc Zyngier 	 * in the guest.
1459ed24f4bSMarc Zyngier 	 */
146cdb5e02eSMarc Zyngier 	kvm_incr_pc(vcpu);
1479ed24f4bSMarc Zyngier 
1483fe534a0SFuad Tabba 	return 1;
1499ed24f4bSMarc Zyngier }
1509ed24f4bSMarc Zyngier 
io_mem_abort(struct kvm_vcpu * vcpu,phys_addr_t fault_ipa)15174cc7e0cSTianjia Zhang int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)
1529ed24f4bSMarc Zyngier {
15374cc7e0cSTianjia Zhang 	struct kvm_run *run = vcpu->run;
1549ed24f4bSMarc Zyngier 	unsigned long data;
1559ed24f4bSMarc Zyngier 	unsigned long rt;
1569ed24f4bSMarc Zyngier 	int ret;
1579ed24f4bSMarc Zyngier 	bool is_write;
1589ed24f4bSMarc Zyngier 	int len;
1599ed24f4bSMarc Zyngier 	u8 data_buf[8];
1609ed24f4bSMarc Zyngier 
1619ed24f4bSMarc Zyngier 	/*
1629ed24f4bSMarc Zyngier 	 * No valid syndrome? Ask userspace for help if it has
163656012c7SFuad Tabba 	 * volunteered to do so, and bail out otherwise.
1649ed24f4bSMarc Zyngier 	 */
1659ed24f4bSMarc Zyngier 	if (!kvm_vcpu_dabt_isvalid(vcpu)) {
16606394531SMarc Zyngier 		if (test_bit(KVM_ARCH_FLAG_RETURN_NISV_IO_ABORT_TO_USER,
16706394531SMarc Zyngier 			     &vcpu->kvm->arch.flags)) {
1689ed24f4bSMarc Zyngier 			run->exit_reason = KVM_EXIT_ARM_NISV;
1699ed24f4bSMarc Zyngier 			run->arm_nisv.esr_iss = kvm_vcpu_dabt_iss_nisv_sanitized(vcpu);
1709ed24f4bSMarc Zyngier 			run->arm_nisv.fault_ipa = fault_ipa;
1719ed24f4bSMarc Zyngier 			return 0;
1729ed24f4bSMarc Zyngier 		}
1739ed24f4bSMarc Zyngier 
1749ed24f4bSMarc Zyngier 		kvm_pr_unimpl("Data abort outside memslots with no valid syndrome info\n");
1759ed24f4bSMarc Zyngier 		return -ENOSYS;
1769ed24f4bSMarc Zyngier 	}
1779ed24f4bSMarc Zyngier 
1789ed24f4bSMarc Zyngier 	/*
1799ed24f4bSMarc Zyngier 	 * Prepare MMIO operation. First decode the syndrome data we get
1809ed24f4bSMarc Zyngier 	 * from the CPU. Then try if some in-kernel emulation feels
1819ed24f4bSMarc Zyngier 	 * responsible, otherwise let user space do its magic.
1829ed24f4bSMarc Zyngier 	 */
1839ed24f4bSMarc Zyngier 	is_write = kvm_vcpu_dabt_iswrite(vcpu);
1849ed24f4bSMarc Zyngier 	len = kvm_vcpu_dabt_get_as(vcpu);
1859ed24f4bSMarc Zyngier 	rt = kvm_vcpu_dabt_get_rd(vcpu);
1869ed24f4bSMarc Zyngier 
1879ed24f4bSMarc Zyngier 	if (is_write) {
1889ed24f4bSMarc Zyngier 		data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt),
1899ed24f4bSMarc Zyngier 					       len);
1909ed24f4bSMarc Zyngier 
1919ed24f4bSMarc Zyngier 		trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, &data);
1929ed24f4bSMarc Zyngier 		kvm_mmio_write_buf(data_buf, len, data);
1939ed24f4bSMarc Zyngier 
1949ed24f4bSMarc Zyngier 		ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len,
1959ed24f4bSMarc Zyngier 				       data_buf);
1969ed24f4bSMarc Zyngier 	} else {
1979ed24f4bSMarc Zyngier 		trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len,
1989ed24f4bSMarc Zyngier 			       fault_ipa, NULL);
1999ed24f4bSMarc Zyngier 
2009ed24f4bSMarc Zyngier 		ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len,
2019ed24f4bSMarc Zyngier 				      data_buf);
2029ed24f4bSMarc Zyngier 	}
2039ed24f4bSMarc Zyngier 
2049ed24f4bSMarc Zyngier 	/* Now prepare kvm_run for the potential return to userland. */
2059ed24f4bSMarc Zyngier 	run->mmio.is_write	= is_write;
2069ed24f4bSMarc Zyngier 	run->mmio.phys_addr	= fault_ipa;
2079ed24f4bSMarc Zyngier 	run->mmio.len		= len;
2089ed24f4bSMarc Zyngier 	vcpu->mmio_needed	= 1;
2099ed24f4bSMarc Zyngier 
2109ed24f4bSMarc Zyngier 	if (!ret) {
2119ed24f4bSMarc Zyngier 		/* We handled the access successfully in the kernel. */
2129ed24f4bSMarc Zyngier 		if (!is_write)
2139ed24f4bSMarc Zyngier 			memcpy(run->mmio.data, data_buf, len);
2149ed24f4bSMarc Zyngier 		vcpu->stat.mmio_exit_kernel++;
21574cc7e0cSTianjia Zhang 		kvm_handle_mmio_return(vcpu);
2169ed24f4bSMarc Zyngier 		return 1;
2179ed24f4bSMarc Zyngier 	}
2189ed24f4bSMarc Zyngier 
2199ed24f4bSMarc Zyngier 	if (is_write)
2209ed24f4bSMarc Zyngier 		memcpy(run->mmio.data, data_buf, len);
2219ed24f4bSMarc Zyngier 	vcpu->stat.mmio_exit_user++;
2229ed24f4bSMarc Zyngier 	run->exit_reason	= KVM_EXIT_MMIO;
2239ed24f4bSMarc Zyngier 	return 0;
2249ed24f4bSMarc Zyngier }
225