linux/virt/kvm/arm/mmio.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 - Virtual Open Systems and Columbia University
   3 * Author: Christoffer Dall <c.dall@virtualopensystems.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License, version 2, as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write to the Free Software
  16 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  17 */
  18
  19#include <linux/kvm_host.h>
  20#include <asm/kvm_mmio.h>
  21#include <asm/kvm_emulate.h>
  22#include <trace/events/kvm.h>
  23
  24#include "trace.h"
  25
  26void kvm_mmio_write_buf(void *buf, unsigned int len, unsigned long data)
  27{
  28        void *datap = NULL;
  29        union {
  30                u8      byte;
  31                u16     hword;
  32                u32     word;
  33                u64     dword;
  34        } tmp;
  35
  36        switch (len) {
  37        case 1:
  38                tmp.byte        = data;
  39                datap           = &tmp.byte;
  40                break;
  41        case 2:
  42                tmp.hword       = data;
  43                datap           = &tmp.hword;
  44                break;
  45        case 4:
  46                tmp.word        = data;
  47                datap           = &tmp.word;
  48                break;
  49        case 8:
  50                tmp.dword       = data;
  51                datap           = &tmp.dword;
  52                break;
  53        }
  54
  55        memcpy(buf, datap, len);
  56}
  57
  58unsigned long kvm_mmio_read_buf(const void *buf, unsigned int len)
  59{
  60        unsigned long data = 0;
  61        union {
  62                u16     hword;
  63                u32     word;
  64                u64     dword;
  65        } tmp;
  66
  67        switch (len) {
  68        case 1:
  69                data = *(u8 *)buf;
  70                break;
  71        case 2:
  72                memcpy(&tmp.hword, buf, len);
  73                data = tmp.hword;
  74                break;
  75        case 4:
  76                memcpy(&tmp.word, buf, len);
  77                data = tmp.word;
  78                break;
  79        case 8:
  80                memcpy(&tmp.dword, buf, len);
  81                data = tmp.dword;
  82                break;
  83        }
  84
  85        return data;
  86}
  87
  88/**
  89 * kvm_handle_mmio_return -- Handle MMIO loads after user space emulation
  90 *                           or in-kernel IO emulation
  91 *
  92 * @vcpu: The VCPU pointer
  93 * @run:  The VCPU run struct containing the mmio data
  94 */
  95int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
  96{
  97        unsigned long data;
  98        unsigned int len;
  99        int mask;
 100
 101        if (!run->mmio.is_write) {
 102                len = run->mmio.len;
 103                if (len > sizeof(unsigned long))
 104                        return -EINVAL;
 105
 106                data = kvm_mmio_read_buf(run->mmio.data, len);
 107
 108                if (vcpu->arch.mmio_decode.sign_extend &&
 109                    len < sizeof(unsigned long)) {
 110                        mask = 1U << ((len * 8) - 1);
 111                        data = (data ^ mask) - mask;
 112                }
 113
 114                trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
 115                               &data);
 116                data = vcpu_data_host_to_guest(vcpu, data, len);
 117                vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data);
 118        }
 119
 120        /*
 121         * The MMIO instruction is emulated and should not be re-executed
 122         * in the guest.
 123         */
 124        kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
 125
 126        return 0;
 127}
 128
 129static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len)
 130{
 131        unsigned long rt;
 132        int access_size;
 133        bool sign_extend;
 134
 135        if (kvm_vcpu_dabt_iss1tw(vcpu)) {
 136                /* page table accesses IO mem: tell guest to fix its TTBR */
 137                kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
 138                return 1;
 139        }
 140
 141        access_size = kvm_vcpu_dabt_get_as(vcpu);
 142        if (unlikely(access_size < 0))
 143                return access_size;
 144
 145        *is_write = kvm_vcpu_dabt_iswrite(vcpu);
 146        sign_extend = kvm_vcpu_dabt_issext(vcpu);
 147        rt = kvm_vcpu_dabt_get_rd(vcpu);
 148
 149        *len = access_size;
 150        vcpu->arch.mmio_decode.sign_extend = sign_extend;
 151        vcpu->arch.mmio_decode.rt = rt;
 152
 153        return 0;
 154}
 155
 156int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
 157                 phys_addr_t fault_ipa)
 158{
 159        unsigned long data;
 160        unsigned long rt;
 161        int ret;
 162        bool is_write;
 163        int len;
 164        u8 data_buf[8];
 165
 166        /*
 167         * Prepare MMIO operation. First decode the syndrome data we get
 168         * from the CPU. Then try if some in-kernel emulation feels
 169         * responsible, otherwise let user space do its magic.
 170         */
 171        if (kvm_vcpu_dabt_isvalid(vcpu)) {
 172                ret = decode_hsr(vcpu, &is_write, &len);
 173                if (ret)
 174                        return ret;
 175        } else {
 176                kvm_err("load/store instruction decoding not implemented\n");
 177                return -ENOSYS;
 178        }
 179
 180        rt = vcpu->arch.mmio_decode.rt;
 181
 182        if (is_write) {
 183                data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt),
 184                                               len);
 185
 186                trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, &data);
 187                kvm_mmio_write_buf(data_buf, len, data);
 188
 189                ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len,
 190                                       data_buf);
 191        } else {
 192                trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len,
 193                               fault_ipa, NULL);
 194
 195                ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len,
 196                                      data_buf);
 197        }
 198
 199        /* Now prepare kvm_run for the potential return to userland. */
 200        run->mmio.is_write      = is_write;
 201        run->mmio.phys_addr     = fault_ipa;
 202        run->mmio.len           = len;
 203
 204        if (!ret) {
 205                /* We handled the access successfully in the kernel. */
 206                if (!is_write)
 207                        memcpy(run->mmio.data, data_buf, len);
 208                vcpu->stat.mmio_exit_kernel++;
 209                kvm_handle_mmio_return(vcpu, run);
 210                return 1;
 211        }
 212
 213        if (is_write)
 214                memcpy(run->mmio.data, data_buf, len);
 215        vcpu->stat.mmio_exit_user++;
 216        run->exit_reason        = KVM_EXIT_MMIO;
 217        return 0;
 218}
 219