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