linux/arch/ia64/kvm/mmio.c
<<
>>
Prefs
   1/*
   2 * mmio.c: MMIO emulation components.
   3 * Copyright (c) 2004, Intel Corporation.
   4 *  Yaozu Dong (Eddie Dong) (Eddie.dong@intel.com)
   5 *  Kun Tian (Kevin Tian) (Kevin.tian@intel.com)
   6 *
   7 * Copyright (c) 2007 Intel Corporation  KVM support.
   8 * Xuefei Xu (Anthony Xu) (anthony.xu@intel.com)
   9 * Xiantao Zhang  (xiantao.zhang@intel.com)
  10 *
  11 * This program is free software; you can redistribute it and/or modify it
  12 * under the terms and conditions of the GNU General Public License,
  13 * version 2, as published by the Free Software Foundation.
  14 *
  15 * This program is distributed in the hope it will be useful, but WITHOUT
  16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  17 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  18 * more details.
  19 *
  20 * You should have received a copy of the GNU General Public License along with
  21 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  22 * Place - Suite 330, Boston, MA 02111-1307 USA.
  23 *
  24 */
  25
  26#include <linux/kvm_host.h>
  27
  28#include "vcpu.h"
  29
  30static void vlsapic_write_xtp(struct kvm_vcpu *v, uint8_t val)
  31{
  32        VLSAPIC_XTP(v) = val;
  33}
  34
  35/*
  36 * LSAPIC OFFSET
  37 */
  38#define PIB_LOW_HALF(ofst)     !(ofst & (1 << 20))
  39#define PIB_OFST_INTA          0x1E0000
  40#define PIB_OFST_XTP           0x1E0008
  41
  42/*
  43 * execute write IPI op.
  44 */
  45static void vlsapic_write_ipi(struct kvm_vcpu *vcpu,
  46                                        uint64_t addr, uint64_t data)
  47{
  48        struct exit_ctl_data *p = &current_vcpu->arch.exit_data;
  49        unsigned long psr;
  50
  51        local_irq_save(psr);
  52
  53        p->exit_reason = EXIT_REASON_IPI;
  54        p->u.ipi_data.addr.val = addr;
  55        p->u.ipi_data.data.val = data;
  56        vmm_transition(current_vcpu);
  57
  58        local_irq_restore(psr);
  59
  60}
  61
  62void lsapic_write(struct kvm_vcpu *v, unsigned long addr,
  63                        unsigned long length, unsigned long val)
  64{
  65        addr &= (PIB_SIZE - 1);
  66
  67        switch (addr) {
  68        case PIB_OFST_INTA:
  69                /*panic_domain(NULL, "Undefined write on PIB INTA\n");*/
  70                panic_vm(v);
  71                break;
  72        case PIB_OFST_XTP:
  73                if (length == 1) {
  74                        vlsapic_write_xtp(v, val);
  75                } else {
  76                        /*panic_domain(NULL,
  77                        "Undefined write on PIB XTP\n");*/
  78                        panic_vm(v);
  79                }
  80                break;
  81        default:
  82                if (PIB_LOW_HALF(addr)) {
  83                        /*lower half */
  84                        if (length != 8)
  85                                /*panic_domain(NULL,
  86                                "Can't LHF write with size %ld!\n",
  87                                length);*/
  88                                panic_vm(v);
  89                        else
  90                                vlsapic_write_ipi(v, addr, val);
  91                } else {   /*   upper half
  92                                printk("IPI-UHF write %lx\n",addr);*/
  93                        panic_vm(v);
  94                }
  95                break;
  96        }
  97}
  98
  99unsigned long lsapic_read(struct kvm_vcpu *v, unsigned long addr,
 100                unsigned long length)
 101{
 102        uint64_t result = 0;
 103
 104        addr &= (PIB_SIZE - 1);
 105
 106        switch (addr) {
 107        case PIB_OFST_INTA:
 108                if (length == 1) /* 1 byte load */
 109                        ; /* There is no i8259, there is no INTA access*/
 110                else
 111                        /*panic_domain(NULL,"Undefined read on PIB INTA\n"); */
 112                        panic_vm(v);
 113
 114                break;
 115        case PIB_OFST_XTP:
 116                if (length == 1) {
 117                        result = VLSAPIC_XTP(v);
 118                        /* printk("read xtp %lx\n", result); */
 119                } else {
 120                        /*panic_domain(NULL,
 121                        "Undefined read on PIB XTP\n");*/
 122                        panic_vm(v);
 123                }
 124                break;
 125        default:
 126                panic_vm(v);
 127                break;
 128        }
 129        return result;
 130}
 131
 132static void mmio_access(struct kvm_vcpu *vcpu, u64 src_pa, u64 *dest,
 133                                        u16 s, int ma, int dir)
 134{
 135        unsigned long iot;
 136        struct exit_ctl_data *p = &vcpu->arch.exit_data;
 137        unsigned long psr;
 138
 139        iot = __gpfn_is_io(src_pa >> PAGE_SHIFT);
 140
 141        local_irq_save(psr);
 142
 143        /*Intercept the acces for PIB range*/
 144        if (iot == GPFN_PIB) {
 145                if (!dir)
 146                        lsapic_write(vcpu, src_pa, s, *dest);
 147                else
 148                        *dest = lsapic_read(vcpu, src_pa, s);
 149                goto out;
 150        }
 151        p->exit_reason = EXIT_REASON_MMIO_INSTRUCTION;
 152        p->u.ioreq.addr = src_pa;
 153        p->u.ioreq.size = s;
 154        p->u.ioreq.dir = dir;
 155        if (dir == IOREQ_WRITE)
 156                p->u.ioreq.data = *dest;
 157        p->u.ioreq.state = STATE_IOREQ_READY;
 158        vmm_transition(vcpu);
 159
 160        if (p->u.ioreq.state == STATE_IORESP_READY) {
 161                if (dir == IOREQ_READ)
 162                        /* it's necessary to ensure zero extending */
 163                        *dest = p->u.ioreq.data & (~0UL >> (64-(s*8)));
 164        } else
 165                panic_vm(vcpu);
 166out:
 167        local_irq_restore(psr);
 168        return ;
 169}
 170
 171/*
 172   dir 1: read 0:write
 173   inst_type 0:integer 1:floating point
 174 */
 175#define SL_INTEGER      0       /* store/load interger*/
 176#define SL_FLOATING     1       /* store/load floating*/
 177
 178void emulate_io_inst(struct kvm_vcpu *vcpu, u64 padr, u64 ma)
 179{
 180        struct kvm_pt_regs *regs;
 181        IA64_BUNDLE bundle;
 182        int slot, dir = 0;
 183        int inst_type = -1;
 184        u16 size = 0;
 185        u64 data, slot1a, slot1b, temp, update_reg;
 186        s32 imm;
 187        INST64 inst;
 188
 189        regs = vcpu_regs(vcpu);
 190
 191        if (fetch_code(vcpu, regs->cr_iip, &bundle)) {
 192                /* if fetch code fail, return and try again */
 193                return;
 194        }
 195        slot = ((struct ia64_psr *)&(regs->cr_ipsr))->ri;
 196        if (!slot)
 197                inst.inst = bundle.slot0;
 198        else if (slot == 1) {
 199                slot1a = bundle.slot1a;
 200                slot1b = bundle.slot1b;
 201                inst.inst = slot1a + (slot1b << 18);
 202        } else if (slot == 2)
 203                inst.inst = bundle.slot2;
 204
 205        /* Integer Load/Store */
 206        if (inst.M1.major == 4 && inst.M1.m == 0 && inst.M1.x == 0) {
 207                inst_type = SL_INTEGER;
 208                size = (inst.M1.x6 & 0x3);
 209                if ((inst.M1.x6 >> 2) > 0xb) {
 210                        /*write*/
 211                        dir = IOREQ_WRITE;
 212                        data = vcpu_get_gr(vcpu, inst.M4.r2);
 213                } else if ((inst.M1.x6 >> 2) < 0xb) {
 214                        /*read*/
 215                        dir = IOREQ_READ;
 216                }
 217        } else if (inst.M2.major == 4 && inst.M2.m == 1 && inst.M2.x == 0) {
 218                /* Integer Load + Reg update */
 219                inst_type = SL_INTEGER;
 220                dir = IOREQ_READ;
 221                size = (inst.M2.x6 & 0x3);
 222                temp = vcpu_get_gr(vcpu, inst.M2.r3);
 223                update_reg = vcpu_get_gr(vcpu, inst.M2.r2);
 224                temp += update_reg;
 225                vcpu_set_gr(vcpu, inst.M2.r3, temp, 0);
 226        } else if (inst.M3.major == 5) {
 227                /*Integer Load/Store + Imm update*/
 228                inst_type = SL_INTEGER;
 229                size = (inst.M3.x6&0x3);
 230                if ((inst.M5.x6 >> 2) > 0xb) {
 231                        /*write*/
 232                        dir = IOREQ_WRITE;
 233                        data = vcpu_get_gr(vcpu, inst.M5.r2);
 234                        temp = vcpu_get_gr(vcpu, inst.M5.r3);
 235                        imm = (inst.M5.s << 31) | (inst.M5.i << 30) |
 236                                (inst.M5.imm7 << 23);
 237                        temp += imm >> 23;
 238                        vcpu_set_gr(vcpu, inst.M5.r3, temp, 0);
 239
 240                } else if ((inst.M3.x6 >> 2) < 0xb) {
 241                        /*read*/
 242                        dir = IOREQ_READ;
 243                        temp = vcpu_get_gr(vcpu, inst.M3.r3);
 244                        imm = (inst.M3.s << 31) | (inst.M3.i << 30) |
 245                                (inst.M3.imm7 << 23);
 246                        temp += imm >> 23;
 247                        vcpu_set_gr(vcpu, inst.M3.r3, temp, 0);
 248
 249                }
 250        } else if (inst.M9.major == 6 && inst.M9.x6 == 0x3B
 251                                && inst.M9.m == 0 && inst.M9.x == 0) {
 252                /* Floating-point spill*/
 253                struct ia64_fpreg v;
 254
 255                inst_type = SL_FLOATING;
 256                dir = IOREQ_WRITE;
 257                vcpu_get_fpreg(vcpu, inst.M9.f2, &v);
 258                /* Write high word. FIXME: this is a kludge!  */
 259                v.u.bits[1] &= 0x3ffff;
 260                mmio_access(vcpu, padr + 8, &v.u.bits[1], 8, ma, IOREQ_WRITE);
 261                data = v.u.bits[0];
 262                size = 3;
 263        } else if (inst.M10.major == 7 && inst.M10.x6 == 0x3B) {
 264                /* Floating-point spill + Imm update */
 265                struct ia64_fpreg v;
 266
 267                inst_type = SL_FLOATING;
 268                dir = IOREQ_WRITE;
 269                vcpu_get_fpreg(vcpu, inst.M10.f2, &v);
 270                temp = vcpu_get_gr(vcpu, inst.M10.r3);
 271                imm = (inst.M10.s << 31) | (inst.M10.i << 30) |
 272                        (inst.M10.imm7 << 23);
 273                temp += imm >> 23;
 274                vcpu_set_gr(vcpu, inst.M10.r3, temp, 0);
 275
 276                /* Write high word.FIXME: this is a kludge!  */
 277                v.u.bits[1] &= 0x3ffff;
 278                mmio_access(vcpu, padr + 8, &v.u.bits[1], 8, ma, IOREQ_WRITE);
 279                data = v.u.bits[0];
 280                size = 3;
 281        } else if (inst.M10.major == 7 && inst.M10.x6 == 0x31) {
 282                /* Floating-point stf8 + Imm update */
 283                struct ia64_fpreg v;
 284                inst_type = SL_FLOATING;
 285                dir = IOREQ_WRITE;
 286                size = 3;
 287                vcpu_get_fpreg(vcpu, inst.M10.f2, &v);
 288                data = v.u.bits[0]; /* Significand.  */
 289                temp = vcpu_get_gr(vcpu, inst.M10.r3);
 290                imm = (inst.M10.s << 31) | (inst.M10.i << 30) |
 291                        (inst.M10.imm7 << 23);
 292                temp += imm >> 23;
 293                vcpu_set_gr(vcpu, inst.M10.r3, temp, 0);
 294        } else if (inst.M15.major == 7 && inst.M15.x6 >= 0x2c
 295                        && inst.M15.x6 <= 0x2f) {
 296                temp = vcpu_get_gr(vcpu, inst.M15.r3);
 297                imm = (inst.M15.s << 31) | (inst.M15.i << 30) |
 298                        (inst.M15.imm7 << 23);
 299                temp += imm >> 23;
 300                vcpu_set_gr(vcpu, inst.M15.r3, temp, 0);
 301
 302                vcpu_increment_iip(vcpu);
 303                return;
 304        } else if (inst.M12.major == 6 && inst.M12.m == 1
 305                        && inst.M12.x == 1 && inst.M12.x6 == 1) {
 306                /* Floating-point Load Pair + Imm ldfp8 M12*/
 307                struct ia64_fpreg v;
 308
 309                inst_type = SL_FLOATING;
 310                dir = IOREQ_READ;
 311                size = 8;     /*ldfd*/
 312                mmio_access(vcpu, padr, &data, size, ma, dir);
 313                v.u.bits[0] = data;
 314                v.u.bits[1] = 0x1003E;
 315                vcpu_set_fpreg(vcpu, inst.M12.f1, &v);
 316                padr += 8;
 317                mmio_access(vcpu, padr, &data, size, ma, dir);
 318                v.u.bits[0] = data;
 319                v.u.bits[1] = 0x1003E;
 320                vcpu_set_fpreg(vcpu, inst.M12.f2, &v);
 321                padr += 8;
 322                vcpu_set_gr(vcpu, inst.M12.r3, padr, 0);
 323                vcpu_increment_iip(vcpu);
 324                return;
 325        } else {
 326                inst_type = -1;
 327                panic_vm(vcpu);
 328        }
 329
 330        size = 1 << size;
 331        if (dir == IOREQ_WRITE) {
 332                mmio_access(vcpu, padr, &data, size, ma, dir);
 333        } else {
 334                mmio_access(vcpu, padr, &data, size, ma, dir);
 335                if (inst_type == SL_INTEGER)
 336                        vcpu_set_gr(vcpu, inst.M1.r1, data, 0);
 337                else
 338                        panic_vm(vcpu);
 339
 340        }
 341        vcpu_increment_iip(vcpu);
 342}
 343
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.