mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-15 15:04:27 +08:00
8d0eff6385
The kvmppc_get_last_inst function recently received a facelift that allowed us to pass an enum of the type of instruction we want to read into it rather than an unreadable boolean. Unfortunately, not all callers ended up passing the enum. This wasn't really an issue as "true" and "false" happen to match the two enum values we have, but it's still hard to read. Update all callers of kvmppc_get_last_inst() to follow the new calling convention. Signed-off-by: Alexander Graf <agraf@suse.de>
273 lines
6.7 KiB
C
273 lines
6.7 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License, version 2, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Copyright IBM Corp. 2007
|
|
* Copyright 2011 Freescale Semiconductor, Inc.
|
|
*
|
|
* Authors: Hollis Blanchard <hollisb@us.ibm.com>
|
|
*/
|
|
|
|
#include <linux/jiffies.h>
|
|
#include <linux/hrtimer.h>
|
|
#include <linux/types.h>
|
|
#include <linux/string.h>
|
|
#include <linux/kvm_host.h>
|
|
#include <linux/clockchips.h>
|
|
|
|
#include <asm/reg.h>
|
|
#include <asm/time.h>
|
|
#include <asm/byteorder.h>
|
|
#include <asm/kvm_ppc.h>
|
|
#include <asm/disassemble.h>
|
|
#include <asm/ppc-opcode.h>
|
|
#include "timing.h"
|
|
#include "trace.h"
|
|
|
|
/* XXX to do:
|
|
* lhax
|
|
* lhaux
|
|
* lswx
|
|
* lswi
|
|
* stswx
|
|
* stswi
|
|
* lha
|
|
* lhau
|
|
* lmw
|
|
* stmw
|
|
*
|
|
*/
|
|
int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct kvm_run *run = vcpu->run;
|
|
u32 inst;
|
|
int ra, rs, rt;
|
|
enum emulation_result emulated;
|
|
int advance = 1;
|
|
|
|
/* this default type might be overwritten by subcategories */
|
|
kvmppc_set_exit_type(vcpu, EMULATED_INST_EXITS);
|
|
|
|
emulated = kvmppc_get_last_inst(vcpu, INST_GENERIC, &inst);
|
|
if (emulated != EMULATE_DONE)
|
|
return emulated;
|
|
|
|
ra = get_ra(inst);
|
|
rs = get_rs(inst);
|
|
rt = get_rt(inst);
|
|
|
|
switch (get_op(inst)) {
|
|
case 31:
|
|
switch (get_xop(inst)) {
|
|
case OP_31_XOP_LWZX:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
|
|
break;
|
|
|
|
case OP_31_XOP_LBZX:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
|
|
break;
|
|
|
|
case OP_31_XOP_LBZUX:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_31_XOP_STWX:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
4, 1);
|
|
break;
|
|
|
|
case OP_31_XOP_STBX:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
1, 1);
|
|
break;
|
|
|
|
case OP_31_XOP_STBUX:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
1, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_31_XOP_LHAX:
|
|
emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
|
|
break;
|
|
|
|
case OP_31_XOP_LHZX:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
|
|
break;
|
|
|
|
case OP_31_XOP_LHZUX:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_31_XOP_STHX:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
2, 1);
|
|
break;
|
|
|
|
case OP_31_XOP_STHUX:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
2, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_31_XOP_DCBST:
|
|
case OP_31_XOP_DCBF:
|
|
case OP_31_XOP_DCBI:
|
|
/* Do nothing. The guest is performing dcbi because
|
|
* hardware DMA is not snooped by the dcache, but
|
|
* emulated DMA either goes through the dcache as
|
|
* normal writes, or the host kernel has handled dcache
|
|
* coherence. */
|
|
break;
|
|
|
|
case OP_31_XOP_LWBRX:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 4, 0);
|
|
break;
|
|
|
|
case OP_31_XOP_STWBRX:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
4, 0);
|
|
break;
|
|
|
|
case OP_31_XOP_LHBRX:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 2, 0);
|
|
break;
|
|
|
|
case OP_31_XOP_STHBRX:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
2, 0);
|
|
break;
|
|
|
|
default:
|
|
emulated = EMULATE_FAIL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case OP_LWZ:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
|
|
break;
|
|
|
|
/* TBD: Add support for other 64 bit load variants like ldu, ldux, ldx etc. */
|
|
case OP_LD:
|
|
rt = get_rt(inst);
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 8, 1);
|
|
break;
|
|
|
|
case OP_LWZU:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_LBZ:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
|
|
break;
|
|
|
|
case OP_LBZU:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_STW:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
4, 1);
|
|
break;
|
|
|
|
/* TBD: Add support for other 64 bit store variants like stdu, stdux, stdx etc. */
|
|
case OP_STD:
|
|
rs = get_rs(inst);
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
8, 1);
|
|
break;
|
|
|
|
case OP_STWU:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
4, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_STB:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
1, 1);
|
|
break;
|
|
|
|
case OP_STBU:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
1, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_LHZ:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
|
|
break;
|
|
|
|
case OP_LHZU:
|
|
emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_LHA:
|
|
emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
|
|
break;
|
|
|
|
case OP_LHAU:
|
|
emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
case OP_STH:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
2, 1);
|
|
break;
|
|
|
|
case OP_STHU:
|
|
emulated = kvmppc_handle_store(run, vcpu,
|
|
kvmppc_get_gpr(vcpu, rs),
|
|
2, 1);
|
|
kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed);
|
|
break;
|
|
|
|
default:
|
|
emulated = EMULATE_FAIL;
|
|
break;
|
|
}
|
|
|
|
if (emulated == EMULATE_FAIL) {
|
|
advance = 0;
|
|
kvmppc_core_queue_program(vcpu, 0);
|
|
}
|
|
|
|
trace_kvm_ppc_instr(inst, kvmppc_get_pc(vcpu), emulated);
|
|
|
|
/* Advance past emulated instruction. */
|
|
if (advance)
|
|
kvmppc_set_pc(vcpu, kvmppc_get_pc(vcpu) + 4);
|
|
|
|
return emulated;
|
|
}
|