Commit 94afd069 authored by Jordan Niethe's avatar Jordan Niethe Committed by Michael Ellerman
Browse files

powerpc: Use a datatype for instructions



Currently unsigned ints are used to represent instructions on powerpc.
This has worked well as instructions have always been 4 byte words.

However, ISA v3.1 introduces some changes to instructions that mean
this scheme will no longer work as well. This change is Prefixed
Instructions. A prefixed instruction is made up of a word prefix
followed by a word suffix to make an 8 byte double word instruction.
No matter the endianness of the system the prefix always comes first.
Prefixed instructions are only planned for powerpc64.

Introduce a ppc_inst type to represent both prefixed and word
instructions on powerpc64 while keeping it possible to exclusively
have word instructions on powerpc32.

Signed-off-by: default avatarJordan Niethe <jniethe5@gmail.com>
[mpe: Fix compile error in emulate_spe()]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200506034050.24806-12-jniethe5@gmail.com
parent 217862d9
Loading
Loading
Loading
Loading
+16 −16
Original line number Diff line number Diff line
@@ -23,33 +23,33 @@
#define BRANCH_ABSOLUTE	0x2

bool is_offset_in_branch_range(long offset);
int create_branch(unsigned int *instr, const unsigned int *addr,
int create_branch(struct ppc_inst *instr, const struct ppc_inst *addr,
		  unsigned long target, int flags);
int create_cond_branch(unsigned int *instr, const unsigned int *addr,
int create_cond_branch(struct ppc_inst *instr, const struct ppc_inst *addr,
		       unsigned long target, int flags);
int patch_branch(unsigned int *addr, unsigned long target, int flags);
int patch_instruction(unsigned int *addr, unsigned int instr);
int raw_patch_instruction(unsigned int *addr, unsigned int instr);
int patch_branch(struct ppc_inst *addr, unsigned long target, int flags);
int patch_instruction(struct ppc_inst *addr, struct ppc_inst instr);
int raw_patch_instruction(struct ppc_inst *addr, struct ppc_inst instr);

static inline unsigned long patch_site_addr(s32 *site)
{
	return (unsigned long)site + *site;
}

static inline int patch_instruction_site(s32 *site, unsigned int instr)
static inline int patch_instruction_site(s32 *site, struct ppc_inst instr)
{
	return patch_instruction((unsigned int *)patch_site_addr(site), instr);
	return patch_instruction((struct ppc_inst *)patch_site_addr(site), instr);
}

static inline int patch_branch_site(s32 *site, unsigned long target, int flags)
{
	return patch_branch((unsigned int *)patch_site_addr(site), target, flags);
	return patch_branch((struct ppc_inst *)patch_site_addr(site), target, flags);
}

static inline int modify_instruction(unsigned int *addr, unsigned int clr,
				     unsigned int set)
{
	return patch_instruction(addr, ppc_inst((*addr & ~clr) | set));
	return patch_instruction((struct ppc_inst *)addr, ppc_inst((*addr & ~clr) | set));
}

static inline int modify_instruction_site(s32 *site, unsigned int clr, unsigned int set)
@@ -57,13 +57,13 @@ static inline int modify_instruction_site(s32 *site, unsigned int clr, unsigned
	return modify_instruction((unsigned int *)patch_site_addr(site), clr, set);
}

int instr_is_relative_branch(unsigned int instr);
int instr_is_relative_link_branch(unsigned int instr);
int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr);
unsigned long branch_target(const unsigned int *instr);
int translate_branch(unsigned int *instr, const unsigned int *dest,
		     const unsigned int *src);
extern bool is_conditional_branch(unsigned int instr);
int instr_is_relative_branch(struct ppc_inst instr);
int instr_is_relative_link_branch(struct ppc_inst instr);
int instr_is_branch_to_addr(const struct ppc_inst *instr, unsigned long addr);
unsigned long branch_target(const struct ppc_inst *instr);
int translate_branch(struct ppc_inst *instr, const struct ppc_inst *dest,
		     const struct ppc_inst *src);
extern bool is_conditional_branch(struct ppc_inst instr);
#ifdef CONFIG_PPC_BOOK3E_64
void __patch_exception(int exc, unsigned long addr);
#define patch_exception(exc, name) do { \
+11 −7
Original line number Diff line number Diff line
@@ -6,26 +6,30 @@
 * Instruction data type for POWER
 */

#define ppc_inst(x) (x)
struct ppc_inst {
	u32 val;
} __packed;

static inline u32 ppc_inst_val(u32 x)
#define ppc_inst(x) ((struct ppc_inst){ .val = x })

static inline u32 ppc_inst_val(struct ppc_inst x)
{
	return x;
	return x.val;
}

static inline int ppc_inst_primary_opcode(u32 x)
static inline int ppc_inst_primary_opcode(struct ppc_inst x)
{
	return ppc_inst_val(x) >> 26;
}

static inline u32 ppc_inst_swab(u32 x)
static inline struct ppc_inst ppc_inst_swab(struct ppc_inst x)
{
	return ppc_inst(swab32(ppc_inst_val(x)));
}

static inline bool ppc_inst_equal(u32 x, u32 y)
static inline bool ppc_inst_equal(struct ppc_inst x, struct ppc_inst y)
{
	return x == y;
	return ppc_inst_val(x) == ppc_inst_val(y);
}

#endif /* _ASM_POWERPC_INST_H */
+3 −2
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
/*
 * Copyright (C) 2004 Paul Mackerras <paulus@au.ibm.com>, IBM
 */
#include <asm/inst.h>

struct pt_regs;

@@ -132,7 +133,7 @@ union vsx_reg {
 * otherwise.
 */
extern int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
			 unsigned int instr);
			 struct ppc_inst instr);

/*
 * Emulate an instruction that can be executed just by updating
@@ -149,7 +150,7 @@ void emulate_update_regs(struct pt_regs *reg, struct instruction_op *op);
 * 0 if it could not be emulated, or -1 for an instruction that
 * should not be emulated (rfid, mtmsrd clearing MSR_RI, etc.).
 */
extern int emulate_step(struct pt_regs *regs, unsigned int instr);
extern int emulate_step(struct pt_regs *regs, struct ppc_inst instr);

/*
 * Emulate a load or store instruction by reading/writing the
+3 −2
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@

#include <linux/notifier.h>
#include <asm/probes.h>
#include <asm/inst.h>

typedef ppc_opcode_t uprobe_opcode_t;

@@ -23,8 +24,8 @@ typedef ppc_opcode_t uprobe_opcode_t;

struct arch_uprobe {
	union {
		u32	insn;
		u32	ixol;
		struct ppc_inst	insn;
		struct ppc_inst	ixol;
	};
};

+5 −4
Original line number Diff line number Diff line
@@ -105,7 +105,7 @@ static struct aligninfo spe_aligninfo[32] = {
 * so we don't need the address swizzling.
 */
static int emulate_spe(struct pt_regs *regs, unsigned int reg,
		       unsigned int instr)
		       struct ppc_inst ppc_instr)
{
	int ret;
	union {
@@ -116,8 +116,9 @@ static int emulate_spe(struct pt_regs *regs, unsigned int reg,
	} data, temp;
	unsigned char __user *p, *addr;
	unsigned long *evr = &current->thread.evr[reg];
	unsigned int nb, flags;
	unsigned int nb, flags, instr;

	instr = ppc_inst_val(ppc_instr);
	instr = (instr >> 1) & 0x1f;

	/* DAR has the operand effective address */
@@ -294,7 +295,7 @@ static int emulate_spe(struct pt_regs *regs, unsigned int reg,

int fix_alignment(struct pt_regs *regs)
{
	unsigned int instr;
	struct ppc_inst instr;
	struct instruction_op op;
	int r, type;

@@ -304,7 +305,7 @@ int fix_alignment(struct pt_regs *regs)
	 */
	CHECK_FULL_REGS(regs);

	if (unlikely(__get_user(instr, (unsigned int __user *)regs->nip)))
	if (unlikely(__get_user(instr.val, (unsigned int __user *)regs->nip)))
		return -EFAULT;
	if ((regs->msr & MSR_LE) != (MSR_KERNEL & MSR_LE)) {
		/* We don't handle PPC little-endian any more... */
Loading