Commit 350779a2 authored by Paul Mackerras's avatar Paul Mackerras Committed by Michael Ellerman
Browse files

powerpc: Handle most loads and stores in instruction emulation code



This extends the instruction emulation infrastructure in sstep.c to
handle all the load and store instructions defined in the Power ISA
v3.0, except for the atomic memory operations, ldmx (which was never
implemented), lfdp/stfdp, and the vector element load/stores.

The instructions added are:

Integer loads and stores: lbarx, lharx, lqarx, stbcx., sthcx., stqcx.,
lq, stq.

VSX loads and stores: lxsiwzx, lxsiwax, stxsiwx, lxvx, lxvl, lxvll,
lxvdsx, lxvwsx, stxvx, stxvl, stxvll, lxsspx, lxsdx, stxsspx, stxsdx,
lxvw4x, lxsibzx, lxvh8x, lxsihzx, lxvb16x, stxvw4x, stxsibx, stxvh8x,
stxsihx, stxvb16x, lxsd, lxssp, lxv, stxsd, stxssp, stxv.

These instructions are handled both in the analyse_instr phase and in
the emulate_step phase.

The code for lxvd2ux and stxvd2ux has been taken out, as those
instructions were never implemented in any processor and have been
taken out of the architecture, and their opcodes have been reused for
other instructions in POWER9 (lxvb16x and stxvb16x).

The emulation for the VSX loads and stores uses helper functions
which don't access registers or memory directly, which can hopefully
be reused by KVM later.

Signed-off-by: default avatarPaul Mackerras <paulus@ozlabs.org>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent ee0a54d7
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -205,6 +205,8 @@
#define PPC_INST_ISEL_MASK		0xfc00003e
#define PPC_INST_LDARX			0x7c0000a8
#define PPC_INST_STDCX			0x7c0001ad
#define PPC_INST_LQARX			0x7c000228
#define PPC_INST_STQCX			0x7c00016d
#define PPC_INST_LSWI			0x7c0004aa
#define PPC_INST_LSWX			0x7c00042a
#define PPC_INST_LWARX			0x7c000028
@@ -403,12 +405,18 @@
					__PPC_RA(a) | __PPC_RB(b))
#define	PPC_DCBZL(a, b)		stringify_in_c(.long PPC_INST_DCBZL | \
					__PPC_RA(a) | __PPC_RB(b))
#define PPC_LQARX(t, a, b, eh)	stringify_in_c(.long PPC_INST_LQARX | \
					___PPC_RT(t) | ___PPC_RA(a) | \
					___PPC_RB(b) | __PPC_EH(eh))
#define PPC_LDARX(t, a, b, eh)	stringify_in_c(.long PPC_INST_LDARX | \
					___PPC_RT(t) | ___PPC_RA(a) | \
					___PPC_RB(b) | __PPC_EH(eh))
#define PPC_LWARX(t, a, b, eh)	stringify_in_c(.long PPC_INST_LWARX | \
					___PPC_RT(t) | ___PPC_RA(a) | \
					___PPC_RB(b) | __PPC_EH(eh))
#define PPC_STQCX(t, a, b)	stringify_in_c(.long PPC_INST_STQCX | \
					___PPC_RT(t) | ___PPC_RA(a) | \
					___PPC_RB(b))
#define PPC_MSGSND(b)		stringify_in_c(.long PPC_INST_MSGSND | \
					___PPC_RB(b))
#define PPC_MSGSYNC		stringify_in_c(.long PPC_INST_MSGSYNC)
+21 −0
Original line number Diff line number Diff line
@@ -83,6 +83,12 @@ enum instruction_type {
#define DCBT		0x300
#define ICBI		0x400

/* VSX flags values */
#define VSX_FPCONV	1	/* do floating point SP/DP conversion */
#define VSX_SPLAT	2	/* store loaded value into all elements */
#define VSX_LDLEFT	4	/* load VSX register from left */
#define VSX_CHECK_VEC	8	/* check MSR_VEC not MSR_VSX for reg >= 32 */

/* Size field in type word */
#define SIZE(n)		((n) << 8)
#define GETSIZE(w)	((w) >> 8)
@@ -100,6 +106,17 @@ struct instruction_op {
	int spr;
	u32 ccval;
	u32 xerval;
	u8 element_size;	/* for VSX/VMX loads/stores */
	u8 vsx_flags;
};

union vsx_reg {
	u8	b[16];
	u16	h[8];
	u32	w[4];
	unsigned long d[2];
	float	fp[4];
	double	dp[2];
};

/*
@@ -131,3 +148,7 @@ void emulate_update_regs(struct pt_regs *reg, struct instruction_op *op);
 */
extern int emulate_step(struct pt_regs *regs, unsigned int instr);

extern void emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg,
			     const void *mem);
extern void emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg,
			      void *mem);
+1 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ obj64-$(CONFIG_KPROBES_SANITY_TEST) += test_emulate_step.o
obj-y			+= checksum_$(BITS).o checksum_wrappers.o

obj-$(CONFIG_PPC_EMULATE_SSTEP)	+= sstep.o ldstfp.o
obj64-$(CONFIG_PPC_EMULATE_SSTEP) += quad.o

obj-$(CONFIG_PPC_LIB_RHEAP) += rheap.o

+48 −22
Original line number Diff line number Diff line
@@ -178,10 +178,10 @@ _GLOBAL(do_stfd)
	EX_TABLE(2b,3b)

#ifdef CONFIG_ALTIVEC
/* Get the contents of vrN into v0; N is in r3. */
/* Get the contents of vrN into v0; N is in r3. Doesn't touch r3 or r4. */
_GLOBAL(get_vr)
	mflr	r0
	rlwinm	r3,r3,3,0xf8
	rlwinm	r6,r3,3,0xf8
	bcl	20,31,1f
	blr			/* v0 is already in v0 */
	nop
@@ -192,15 +192,15 @@ reg = 1
reg = reg + 1
	.endr
1:	mflr	r5
	add	r5,r3,r5
	add	r5,r6,r5
	mtctr	r5
	mtlr	r0
	bctr

/* Put the contents of v0 into vrN; N is in r3. */
/* Put the contents of v0 into vrN; N is in r3. Doesn't touch r3 or r4. */
_GLOBAL(put_vr)
	mflr	r0
	rlwinm	r3,r3,3,0xf8
	rlwinm	r6,r3,3,0xf8
	bcl	20,31,1f
	blr			/* v0 is already in v0 */
	nop
@@ -211,7 +211,7 @@ reg = 1
reg = reg + 1
	.endr
1:	mflr	r5
	add	r5,r3,r5
	add	r5,r6,r5
	mtctr	r5
	mtlr	r0
	bctr
@@ -313,7 +313,7 @@ reg = reg + 1
	bctr

/* Load VSX reg N from vector doubleword *p.  N is in r3, p in r4. */
_GLOBAL(do_lxvd2x)
_GLOBAL(load_vsrn)
	PPC_STLU r1,-STKFRM(r1)
	mflr	r0
	PPC_STL	r0,STKFRM+PPC_LR_STKOFF(r1)
@@ -325,41 +325,38 @@ _GLOBAL(do_lxvd2x)
	isync
	beq	cr7,1f
	STXVD2X(0,R1,R8)
1:	li	r9,-EFAULT
2:	LXVD2X(0,R0,R4)
	li	r9,0
3:	beq	cr7,4f
1:	LXVD2X(0,R0,R4)
#ifdef __LITTLE_ENDIAN__
	XXSWAPD(0,0)
#endif
	beq	cr7,4f
	bl	put_vsr
	LXVD2X(0,R1,R8)
4:	PPC_LL	r0,STKFRM+PPC_LR_STKOFF(r1)
	mtlr	r0
	MTMSRD(r6)
	isync
	mr	r3,r9
	addi	r1,r1,STKFRM
	blr
	EX_TABLE(2b,3b)

/* Store VSX reg N to vector doubleword *p.  N is in r3, p in r4. */
_GLOBAL(do_stxvd2x)
_GLOBAL(store_vsrn)
	PPC_STLU r1,-STKFRM(r1)
	mflr	r0
	PPC_STL	r0,STKFRM+PPC_LR_STKOFF(r1)
	mfmsr	r6
	oris	r7,r6,MSR_VSX@h
	cmpwi	cr7,r3,0
	li	r8,STKFRM-16
	MTMSRD(r7)
	isync
	beq	cr7,1f
	STXVD2X(0,R1,R8)
	bl	get_vsr
1:	li	r9,-EFAULT
2:	STXVD2X(0,R0,R4)
	li	r9,0
3:	beq	cr7,4f
#ifdef __LITTLE_ENDIAN__
	XXSWAPD(0,0)
#endif
	STXVD2X(0,R0,R4)
	LXVD2X(0,R1,R8)
4:	PPC_LL	r0,STKFRM+PPC_LR_STKOFF(r1)
	PPC_LL	r0,STKFRM+PPC_LR_STKOFF(r1)
	mtlr	r0
	MTMSRD(r6)
	isync
@@ -367,7 +364,36 @@ _GLOBAL(do_stxvd2x)
	addi	r1,r1,STKFRM
	blr
	EX_TABLE(2b,3b)

#endif /* CONFIG_VSX */

/* Convert single-precision to double, without disturbing FPRs. */
/* conv_sp_to_dp(float *sp, double *dp) */
_GLOBAL(conv_sp_to_dp)
	mfmsr	r6
	ori	r7, r6, MSR_FP
	MTMSRD(r7)
	isync
	stfd	fr0, -16(r1)
	lfs	fr0, 0(r3)
	stfd	fr0, 0(r4)
	lfd	fr0, -16(r1)
	MTMSRD(r6)
	isync
	blr

/* Convert single-precision to double, without disturbing FPRs. */
/* conv_sp_to_dp(double *dp, float *sp) */
_GLOBAL(conv_dp_to_sp)
	mfmsr	r6
	ori	r7, r6, MSR_FP
	MTMSRD(r7)
	isync
	stfd	fr0, -16(r1)
	lfd	fr0, 0(r3)
	stfs	fr0, 0(r4)
	lfd	fr0, -16(r1)
	MTMSRD(r6)
	isync
	blr

#endif	/* CONFIG_PPC_FPU */
+62 −0
Original line number Diff line number Diff line
/*
 * Quadword loads and stores
 * for use in instruction emulation.
 *
 * Copyright 2017 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version
 *  2 of the License, or (at your option) any later version.
 */

#include <asm/processor.h>
#include <asm/ppc_asm.h>
#include <asm/ppc-opcode.h>
#include <asm/reg.h>
#include <asm/asm-offsets.h>
#include <linux/errno.h>

/* do_lq(unsigned long ea, unsigned long *regs) */
_GLOBAL(do_lq)
1:	lq	r6, 0(r3)
	std	r6, 0(r4)
	std	r7, 8(r4)
	li	r3, 0
	blr
2:	li	r3, -EFAULT
	blr
	EX_TABLE(1b, 2b)

/* do_stq(unsigned long ea, unsigned long val0, unsigned long val1) */
_GLOBAL(do_stq)
1:	stq	r4, 0(r3)
	li	r3, 0
	blr
2:	li	r3, -EFAULT
	blr
	EX_TABLE(1b, 2b)

/* do_lqarx(unsigned long ea, unsigned long *regs) */
_GLOBAL(do_lqarx)
1:	PPC_LQARX(6, 0, 3, 0)
	std	r6, 0(r4)
	std	r7, 8(r4)
	li	r3, 0
	blr
2:	li	r3, -EFAULT
	blr
	EX_TABLE(1b, 2b)

/* do_stqcx(unsigned long ea, unsigned long val0, unsigned long val1,
	    unsigned int *crp) */

_GLOBAL(do_stqcx)
1:	PPC_STQCX(4, 0, 3)
	mfcr	r5
	stw	r5, 0(r6)
	li	r3, 0
	blr
2:	li	r3, -EFAULT
	blr
	EX_TABLE(1b, 2b)
Loading