cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

emulate_loadstore.c (9177B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *
      4 * Copyright IBM Corp. 2007
      5 * Copyright 2011 Freescale Semiconductor, Inc.
      6 *
      7 * Authors: Hollis Blanchard <hollisb@us.ibm.com>
      8 */
      9
     10#include <linux/jiffies.h>
     11#include <linux/hrtimer.h>
     12#include <linux/types.h>
     13#include <linux/string.h>
     14#include <linux/kvm_host.h>
     15#include <linux/clockchips.h>
     16
     17#include <asm/reg.h>
     18#include <asm/time.h>
     19#include <asm/byteorder.h>
     20#include <asm/kvm_ppc.h>
     21#include <asm/disassemble.h>
     22#include <asm/ppc-opcode.h>
     23#include <asm/sstep.h>
     24#include "timing.h"
     25#include "trace.h"
     26
     27#ifdef CONFIG_PPC_FPU
     28static bool kvmppc_check_fp_disabled(struct kvm_vcpu *vcpu)
     29{
     30	if (!(kvmppc_get_msr(vcpu) & MSR_FP)) {
     31		kvmppc_core_queue_fpunavail(vcpu);
     32		return true;
     33	}
     34
     35	return false;
     36}
     37#endif /* CONFIG_PPC_FPU */
     38
     39#ifdef CONFIG_VSX
     40static bool kvmppc_check_vsx_disabled(struct kvm_vcpu *vcpu)
     41{
     42	if (!(kvmppc_get_msr(vcpu) & MSR_VSX)) {
     43		kvmppc_core_queue_vsx_unavail(vcpu);
     44		return true;
     45	}
     46
     47	return false;
     48}
     49#endif /* CONFIG_VSX */
     50
     51#ifdef CONFIG_ALTIVEC
     52static bool kvmppc_check_altivec_disabled(struct kvm_vcpu *vcpu)
     53{
     54	if (!(kvmppc_get_msr(vcpu) & MSR_VEC)) {
     55		kvmppc_core_queue_vec_unavail(vcpu);
     56		return true;
     57	}
     58
     59	return false;
     60}
     61#endif /* CONFIG_ALTIVEC */
     62
     63/*
     64 * XXX to do:
     65 * lfiwax, lfiwzx
     66 * vector loads and stores
     67 *
     68 * Instructions that trap when used on cache-inhibited mappings
     69 * are not emulated here: multiple and string instructions,
     70 * lq/stq, and the load-reserve/store-conditional instructions.
     71 */
     72int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu)
     73{
     74	u32 inst;
     75	enum emulation_result emulated = EMULATE_FAIL;
     76	struct instruction_op op;
     77
     78	/* this default type might be overwritten by subcategories */
     79	kvmppc_set_exit_type(vcpu, EMULATED_INST_EXITS);
     80
     81	emulated = kvmppc_get_last_inst(vcpu, INST_GENERIC, &inst);
     82	if (emulated != EMULATE_DONE)
     83		return emulated;
     84
     85	vcpu->arch.mmio_vsx_copy_nums = 0;
     86	vcpu->arch.mmio_vsx_offset = 0;
     87	vcpu->arch.mmio_copy_type = KVMPPC_VSX_COPY_NONE;
     88	vcpu->arch.mmio_sp64_extend = 0;
     89	vcpu->arch.mmio_sign_extend = 0;
     90	vcpu->arch.mmio_vmx_copy_nums = 0;
     91	vcpu->arch.mmio_vmx_offset = 0;
     92	vcpu->arch.mmio_host_swabbed = 0;
     93
     94	emulated = EMULATE_FAIL;
     95	vcpu->arch.regs.msr = vcpu->arch.shared->msr;
     96	if (analyse_instr(&op, &vcpu->arch.regs, ppc_inst(inst)) == 0) {
     97		int type = op.type & INSTR_TYPE_MASK;
     98		int size = GETSIZE(op.type);
     99
    100		vcpu->mmio_is_write = OP_IS_STORE(type);
    101
    102		switch (type) {
    103		case LOAD:  {
    104			int instr_byte_swap = op.type & BYTEREV;
    105
    106			if (op.type & SIGNEXT)
    107				emulated = kvmppc_handle_loads(vcpu,
    108						op.reg, size, !instr_byte_swap);
    109			else
    110				emulated = kvmppc_handle_load(vcpu,
    111						op.reg, size, !instr_byte_swap);
    112
    113			if ((op.type & UPDATE) && (emulated != EMULATE_FAIL))
    114				kvmppc_set_gpr(vcpu, op.update_reg, op.ea);
    115
    116			break;
    117		}
    118#ifdef CONFIG_PPC_FPU
    119		case LOAD_FP:
    120			if (kvmppc_check_fp_disabled(vcpu))
    121				return EMULATE_DONE;
    122
    123			if (op.type & FPCONV)
    124				vcpu->arch.mmio_sp64_extend = 1;
    125
    126			if (op.type & SIGNEXT)
    127				emulated = kvmppc_handle_loads(vcpu,
    128					     KVM_MMIO_REG_FPR|op.reg, size, 1);
    129			else
    130				emulated = kvmppc_handle_load(vcpu,
    131					     KVM_MMIO_REG_FPR|op.reg, size, 1);
    132
    133			if ((op.type & UPDATE) && (emulated != EMULATE_FAIL))
    134				kvmppc_set_gpr(vcpu, op.update_reg, op.ea);
    135
    136			break;
    137#endif
    138#ifdef CONFIG_ALTIVEC
    139		case LOAD_VMX:
    140			if (kvmppc_check_altivec_disabled(vcpu))
    141				return EMULATE_DONE;
    142
    143			/* Hardware enforces alignment of VMX accesses */
    144			vcpu->arch.vaddr_accessed &= ~((unsigned long)size - 1);
    145			vcpu->arch.paddr_accessed &= ~((unsigned long)size - 1);
    146
    147			if (size == 16) { /* lvx */
    148				vcpu->arch.mmio_copy_type =
    149						KVMPPC_VMX_COPY_DWORD;
    150			} else if (size == 4) { /* lvewx  */
    151				vcpu->arch.mmio_copy_type =
    152						KVMPPC_VMX_COPY_WORD;
    153			} else if (size == 2) { /* lvehx  */
    154				vcpu->arch.mmio_copy_type =
    155						KVMPPC_VMX_COPY_HWORD;
    156			} else if (size == 1) { /* lvebx  */
    157				vcpu->arch.mmio_copy_type =
    158						KVMPPC_VMX_COPY_BYTE;
    159			} else
    160				break;
    161
    162			vcpu->arch.mmio_vmx_offset =
    163				(vcpu->arch.vaddr_accessed & 0xf)/size;
    164
    165			if (size == 16) {
    166				vcpu->arch.mmio_vmx_copy_nums = 2;
    167				emulated = kvmppc_handle_vmx_load(vcpu,
    168						KVM_MMIO_REG_VMX|op.reg,
    169						8, 1);
    170			} else {
    171				vcpu->arch.mmio_vmx_copy_nums = 1;
    172				emulated = kvmppc_handle_vmx_load(vcpu,
    173						KVM_MMIO_REG_VMX|op.reg,
    174						size, 1);
    175			}
    176			break;
    177#endif
    178#ifdef CONFIG_VSX
    179		case LOAD_VSX: {
    180			int io_size_each;
    181
    182			if (op.vsx_flags & VSX_CHECK_VEC) {
    183				if (kvmppc_check_altivec_disabled(vcpu))
    184					return EMULATE_DONE;
    185			} else {
    186				if (kvmppc_check_vsx_disabled(vcpu))
    187					return EMULATE_DONE;
    188			}
    189
    190			if (op.vsx_flags & VSX_FPCONV)
    191				vcpu->arch.mmio_sp64_extend = 1;
    192
    193			if (op.element_size == 8)  {
    194				if (op.vsx_flags & VSX_SPLAT)
    195					vcpu->arch.mmio_copy_type =
    196						KVMPPC_VSX_COPY_DWORD_LOAD_DUMP;
    197				else
    198					vcpu->arch.mmio_copy_type =
    199						KVMPPC_VSX_COPY_DWORD;
    200			} else if (op.element_size == 4) {
    201				if (op.vsx_flags & VSX_SPLAT)
    202					vcpu->arch.mmio_copy_type =
    203						KVMPPC_VSX_COPY_WORD_LOAD_DUMP;
    204				else
    205					vcpu->arch.mmio_copy_type =
    206						KVMPPC_VSX_COPY_WORD;
    207			} else
    208				break;
    209
    210			if (size < op.element_size) {
    211				/* precision convert case: lxsspx, etc */
    212				vcpu->arch.mmio_vsx_copy_nums = 1;
    213				io_size_each = size;
    214			} else { /* lxvw4x, lxvd2x, etc */
    215				vcpu->arch.mmio_vsx_copy_nums =
    216					size/op.element_size;
    217				io_size_each = op.element_size;
    218			}
    219
    220			emulated = kvmppc_handle_vsx_load(vcpu,
    221					KVM_MMIO_REG_VSX|op.reg, io_size_each,
    222					1, op.type & SIGNEXT);
    223			break;
    224		}
    225#endif
    226		case STORE:
    227			/* if need byte reverse, op.val has been reversed by
    228			 * analyse_instr().
    229			 */
    230			emulated = kvmppc_handle_store(vcpu, op.val, size, 1);
    231
    232			if ((op.type & UPDATE) && (emulated != EMULATE_FAIL))
    233				kvmppc_set_gpr(vcpu, op.update_reg, op.ea);
    234
    235			break;
    236#ifdef CONFIG_PPC_FPU
    237		case STORE_FP:
    238			if (kvmppc_check_fp_disabled(vcpu))
    239				return EMULATE_DONE;
    240
    241			/* The FP registers need to be flushed so that
    242			 * kvmppc_handle_store() can read actual FP vals
    243			 * from vcpu->arch.
    244			 */
    245			if (vcpu->kvm->arch.kvm_ops->giveup_ext)
    246				vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu,
    247						MSR_FP);
    248
    249			if (op.type & FPCONV)
    250				vcpu->arch.mmio_sp64_extend = 1;
    251
    252			emulated = kvmppc_handle_store(vcpu,
    253					VCPU_FPR(vcpu, op.reg), size, 1);
    254
    255			if ((op.type & UPDATE) && (emulated != EMULATE_FAIL))
    256				kvmppc_set_gpr(vcpu, op.update_reg, op.ea);
    257
    258			break;
    259#endif
    260#ifdef CONFIG_ALTIVEC
    261		case STORE_VMX:
    262			if (kvmppc_check_altivec_disabled(vcpu))
    263				return EMULATE_DONE;
    264
    265			/* Hardware enforces alignment of VMX accesses. */
    266			vcpu->arch.vaddr_accessed &= ~((unsigned long)size - 1);
    267			vcpu->arch.paddr_accessed &= ~((unsigned long)size - 1);
    268
    269			if (vcpu->kvm->arch.kvm_ops->giveup_ext)
    270				vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu,
    271						MSR_VEC);
    272			if (size == 16) { /* stvx */
    273				vcpu->arch.mmio_copy_type =
    274						KVMPPC_VMX_COPY_DWORD;
    275			} else if (size == 4) { /* stvewx  */
    276				vcpu->arch.mmio_copy_type =
    277						KVMPPC_VMX_COPY_WORD;
    278			} else if (size == 2) { /* stvehx  */
    279				vcpu->arch.mmio_copy_type =
    280						KVMPPC_VMX_COPY_HWORD;
    281			} else if (size == 1) { /* stvebx  */
    282				vcpu->arch.mmio_copy_type =
    283						KVMPPC_VMX_COPY_BYTE;
    284			} else
    285				break;
    286
    287			vcpu->arch.mmio_vmx_offset =
    288				(vcpu->arch.vaddr_accessed & 0xf)/size;
    289
    290			if (size == 16) {
    291				vcpu->arch.mmio_vmx_copy_nums = 2;
    292				emulated = kvmppc_handle_vmx_store(vcpu,
    293						op.reg, 8, 1);
    294			} else {
    295				vcpu->arch.mmio_vmx_copy_nums = 1;
    296				emulated = kvmppc_handle_vmx_store(vcpu,
    297						op.reg, size, 1);
    298			}
    299
    300			break;
    301#endif
    302#ifdef CONFIG_VSX
    303		case STORE_VSX: {
    304			int io_size_each;
    305
    306			if (op.vsx_flags & VSX_CHECK_VEC) {
    307				if (kvmppc_check_altivec_disabled(vcpu))
    308					return EMULATE_DONE;
    309			} else {
    310				if (kvmppc_check_vsx_disabled(vcpu))
    311					return EMULATE_DONE;
    312			}
    313
    314			if (vcpu->kvm->arch.kvm_ops->giveup_ext)
    315				vcpu->kvm->arch.kvm_ops->giveup_ext(vcpu,
    316						MSR_VSX);
    317
    318			if (op.vsx_flags & VSX_FPCONV)
    319				vcpu->arch.mmio_sp64_extend = 1;
    320
    321			if (op.element_size == 8)
    322				vcpu->arch.mmio_copy_type =
    323						KVMPPC_VSX_COPY_DWORD;
    324			else if (op.element_size == 4)
    325				vcpu->arch.mmio_copy_type =
    326						KVMPPC_VSX_COPY_WORD;
    327			else
    328				break;
    329
    330			if (size < op.element_size) {
    331				/* precise conversion case, like stxsspx */
    332				vcpu->arch.mmio_vsx_copy_nums = 1;
    333				io_size_each = size;
    334			} else { /* stxvw4x, stxvd2x, etc */
    335				vcpu->arch.mmio_vsx_copy_nums =
    336						size/op.element_size;
    337				io_size_each = op.element_size;
    338			}
    339
    340			emulated = kvmppc_handle_vsx_store(vcpu,
    341					op.reg, io_size_each, 1);
    342			break;
    343		}
    344#endif
    345		case CACHEOP:
    346			/* Do nothing. The guest is performing dcbi because
    347			 * hardware DMA is not snooped by the dcache, but
    348			 * emulated DMA either goes through the dcache as
    349			 * normal writes, or the host kernel has handled dcache
    350			 * coherence.
    351			 */
    352			emulated = EMULATE_DONE;
    353			break;
    354		default:
    355			break;
    356		}
    357	}
    358
    359	trace_kvm_ppc_instr(inst, kvmppc_get_pc(vcpu), emulated);
    360
    361	/* Advance past emulated instruction. */
    362	if (emulated != EMULATE_FAIL)
    363		kvmppc_set_pc(vcpu, kvmppc_get_pc(vcpu) + 4);
    364
    365	return emulated;
    366}