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        return 0;
 121}
 122
 123static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len)
 124{
 125        unsigned long rt;
 126        int access_size;
 127        bool sign_extend;
 128
 129        if (kvm_vcpu_dabt_iss1tw(vcpu)) {
 130                /* page table accesses IO mem: tell guest to fix its TTBR */
 131                kvm_inject_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
 132                return 1;
 133        }
 134
 135        access_size = kvm_vcpu_dabt_get_as(vcpu);
 136        if (unlikely(access_size < 0))
 137                return access_size;
 138
 139        *is_write = kvm_vcpu_dabt_iswrite(vcpu);
 140        sign_extend = kvm_vcpu_dabt_issext(vcpu);
 141        rt = kvm_vcpu_dabt_get_rd(vcpu);
 142
 143        *len = access_size;
 144        vcpu->arch.mmio_decode.sign_extend = sign_extend;
 145        vcpu->arch.mmio_decode.rt = rt;
 146
 147        /*
 148         * The MMIO instruction is emulated and should not be re-executed
 149         * in the guest.
 150         */
 151        kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
 152        return 0;
 153}
 154
 155int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
 156                 phys_addr_t fault_ipa)
 157{
 158        unsigned long data;
 159        unsigned long rt;
 160        int ret;
 161        bool is_write;
 162        int len;
 163        u8 data_buf[8];
 164
 165        /*
 166         * Prepare MMIO operation. First decode the syndrome data we get
 167         * from the CPU. Then try if some in-kernel emulation feels
 168         * responsible, otherwise let user space do its magic.
 169         */
 170        if (kvm_vcpu_dabt_isvalid(vcpu)) {
 171                ret = decode_hsr(vcpu, &is_write, &len);
 172                if (ret)
 173                        return ret;
 174        } else {
 175                kvm_err("load/store instruction decoding not implemented\n");
 176                return -ENOSYS;
 177        }
 178
 179        rt = vcpu->arch.mmio_decode.rt;
 180
 181        if (is_write) {
 182                data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt),
 183                                               len);
 184
 185                trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, data);
 186                kvm_mmio_write_buf(data_buf, len, data);
 187
 188                ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len,
 189                                       data_buf);
 190        } else {
 191                trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len,
 192                               fault_ipa, 0);
 193
 194                ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len,
 195                                      data_buf);
 196        }
 197
 198        /* Now prepare kvm_run for the potential return to userland. */
 199        run->mmio.is_write      = is_write;
 200        run->mmio.phys_addr     = fault_ipa;
 201        run->mmio.len           = len;
 202
 203        if (!ret) {
 204                /* We handled the access successfully in the kernel. */
 205                if (!is_write)
 206                        memcpy(run->mmio.data, data_buf, len);
 207                vcpu->stat.mmio_exit_kernel++;
 208                kvm_handle_mmio_return(vcpu, run);
 209                return 1;
 210        }
 211
 212        if (is_write)
 213                memcpy(run->mmio.data, data_buf, len);
 214        vcpu->stat.mmio_exit_user++;
 215        run->exit_reason        = KVM_EXIT_MMIO;
 216        return 0;
 217}
 218