162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Single-step support. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 Paul Mackerras <paulus@au.ibm.com>, IBM 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/kprobes.h> 962306a36Sopenharmony_ci#include <linux/ptrace.h> 1062306a36Sopenharmony_ci#include <linux/prefetch.h> 1162306a36Sopenharmony_ci#include <asm/sstep.h> 1262306a36Sopenharmony_ci#include <asm/processor.h> 1362306a36Sopenharmony_ci#include <linux/uaccess.h> 1462306a36Sopenharmony_ci#include <asm/cpu_has_feature.h> 1562306a36Sopenharmony_ci#include <asm/cputable.h> 1662306a36Sopenharmony_ci#include <asm/disassemble.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#ifdef CONFIG_PPC64 1962306a36Sopenharmony_ci/* Bits in SRR1 that are copied from MSR */ 2062306a36Sopenharmony_ci#define MSR_MASK 0xffffffff87c0ffffUL 2162306a36Sopenharmony_ci#else 2262306a36Sopenharmony_ci#define MSR_MASK 0x87c0ffff 2362306a36Sopenharmony_ci#endif 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Bits in XER */ 2662306a36Sopenharmony_ci#define XER_SO 0x80000000U 2762306a36Sopenharmony_ci#define XER_OV 0x40000000U 2862306a36Sopenharmony_ci#define XER_CA 0x20000000U 2962306a36Sopenharmony_ci#define XER_OV32 0x00080000U 3062306a36Sopenharmony_ci#define XER_CA32 0x00040000U 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#ifdef CONFIG_VSX 3362306a36Sopenharmony_ci#define VSX_REGISTER_XTP(rd) ((((rd) & 1) << 5) | ((rd) & 0xfe)) 3462306a36Sopenharmony_ci#endif 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#ifdef CONFIG_PPC_FPU 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * Functions in ldstfp.S 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ciextern void get_fpr(int rn, double *p); 4162306a36Sopenharmony_ciextern void put_fpr(int rn, const double *p); 4262306a36Sopenharmony_ciextern void get_vr(int rn, __vector128 *p); 4362306a36Sopenharmony_ciextern void put_vr(int rn, __vector128 *p); 4462306a36Sopenharmony_ciextern void load_vsrn(int vsr, const void *p); 4562306a36Sopenharmony_ciextern void store_vsrn(int vsr, void *p); 4662306a36Sopenharmony_ciextern void conv_sp_to_dp(const float *sp, double *dp); 4762306a36Sopenharmony_ciextern void conv_dp_to_sp(const double *dp, float *sp); 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#ifdef __powerpc64__ 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Functions in quad.S 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ciextern int do_lq(unsigned long ea, unsigned long *regs); 5562306a36Sopenharmony_ciextern int do_stq(unsigned long ea, unsigned long val0, unsigned long val1); 5662306a36Sopenharmony_ciextern int do_lqarx(unsigned long ea, unsigned long *regs); 5762306a36Sopenharmony_ciextern int do_stqcx(unsigned long ea, unsigned long val0, unsigned long val1, 5862306a36Sopenharmony_ci unsigned int *crp); 5962306a36Sopenharmony_ci#endif 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN__ 6262306a36Sopenharmony_ci#define IS_LE 1 6362306a36Sopenharmony_ci#define IS_BE 0 6462306a36Sopenharmony_ci#else 6562306a36Sopenharmony_ci#define IS_LE 0 6662306a36Sopenharmony_ci#define IS_BE 1 6762306a36Sopenharmony_ci#endif 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Emulate the truncation of 64 bit values in 32-bit mode. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic nokprobe_inline unsigned long truncate_if_32bit(unsigned long msr, 7362306a36Sopenharmony_ci unsigned long val) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci if ((msr & MSR_64BIT) == 0) 7662306a36Sopenharmony_ci val &= 0xffffffffUL; 7762306a36Sopenharmony_ci return val; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * Determine whether a conditional branch instruction would branch. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_cistatic nokprobe_inline int branch_taken(unsigned int instr, 8462306a36Sopenharmony_ci const struct pt_regs *regs, 8562306a36Sopenharmony_ci struct instruction_op *op) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci unsigned int bo = (instr >> 21) & 0x1f; 8862306a36Sopenharmony_ci unsigned int bi; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if ((bo & 4) == 0) { 9162306a36Sopenharmony_ci /* decrement counter */ 9262306a36Sopenharmony_ci op->type |= DECCTR; 9362306a36Sopenharmony_ci if (((bo >> 1) & 1) ^ (regs->ctr == 1)) 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci if ((bo & 0x10) == 0) { 9762306a36Sopenharmony_ci /* check bit from CR */ 9862306a36Sopenharmony_ci bi = (instr >> 16) & 0x1f; 9962306a36Sopenharmony_ci if (((regs->ccr >> (31 - bi)) & 1) != ((bo >> 3) & 1)) 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci return 1; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic nokprobe_inline long address_ok(struct pt_regs *regs, 10662306a36Sopenharmony_ci unsigned long ea, int nb) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci if (!user_mode(regs)) 10962306a36Sopenharmony_ci return 1; 11062306a36Sopenharmony_ci if (access_ok((void __user *)ea, nb)) 11162306a36Sopenharmony_ci return 1; 11262306a36Sopenharmony_ci if (access_ok((void __user *)ea, 1)) 11362306a36Sopenharmony_ci /* Access overlaps the end of the user region */ 11462306a36Sopenharmony_ci regs->dar = TASK_SIZE_MAX - 1; 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci regs->dar = ea; 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* 12162306a36Sopenharmony_ci * Calculate effective address for a D-form instruction 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_cistatic nokprobe_inline unsigned long dform_ea(unsigned int instr, 12462306a36Sopenharmony_ci const struct pt_regs *regs) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int ra; 12762306a36Sopenharmony_ci unsigned long ea; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ra = (instr >> 16) & 0x1f; 13062306a36Sopenharmony_ci ea = (signed short) instr; /* sign-extend */ 13162306a36Sopenharmony_ci if (ra) 13262306a36Sopenharmony_ci ea += regs->gpr[ra]; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return ea; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#ifdef __powerpc64__ 13862306a36Sopenharmony_ci/* 13962306a36Sopenharmony_ci * Calculate effective address for a DS-form instruction 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic nokprobe_inline unsigned long dsform_ea(unsigned int instr, 14262306a36Sopenharmony_ci const struct pt_regs *regs) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int ra; 14562306a36Sopenharmony_ci unsigned long ea; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ra = (instr >> 16) & 0x1f; 14862306a36Sopenharmony_ci ea = (signed short) (instr & ~3); /* sign-extend */ 14962306a36Sopenharmony_ci if (ra) 15062306a36Sopenharmony_ci ea += regs->gpr[ra]; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return ea; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* 15662306a36Sopenharmony_ci * Calculate effective address for a DQ-form instruction 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_cistatic nokprobe_inline unsigned long dqform_ea(unsigned int instr, 15962306a36Sopenharmony_ci const struct pt_regs *regs) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int ra; 16262306a36Sopenharmony_ci unsigned long ea; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ra = (instr >> 16) & 0x1f; 16562306a36Sopenharmony_ci ea = (signed short) (instr & ~0xf); /* sign-extend */ 16662306a36Sopenharmony_ci if (ra) 16762306a36Sopenharmony_ci ea += regs->gpr[ra]; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return ea; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci#endif /* __powerpc64 */ 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* 17462306a36Sopenharmony_ci * Calculate effective address for an X-form instruction 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistatic nokprobe_inline unsigned long xform_ea(unsigned int instr, 17762306a36Sopenharmony_ci const struct pt_regs *regs) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci int ra, rb; 18062306a36Sopenharmony_ci unsigned long ea; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ra = (instr >> 16) & 0x1f; 18362306a36Sopenharmony_ci rb = (instr >> 11) & 0x1f; 18462306a36Sopenharmony_ci ea = regs->gpr[rb]; 18562306a36Sopenharmony_ci if (ra) 18662306a36Sopenharmony_ci ea += regs->gpr[ra]; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return ea; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* 19262306a36Sopenharmony_ci * Calculate effective address for a MLS:D-form / 8LS:D-form 19362306a36Sopenharmony_ci * prefixed instruction 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic nokprobe_inline unsigned long mlsd_8lsd_ea(unsigned int instr, 19662306a36Sopenharmony_ci unsigned int suffix, 19762306a36Sopenharmony_ci const struct pt_regs *regs) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int ra, prefix_r; 20062306a36Sopenharmony_ci unsigned int dd; 20162306a36Sopenharmony_ci unsigned long ea, d0, d1, d; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci prefix_r = GET_PREFIX_R(instr); 20462306a36Sopenharmony_ci ra = GET_PREFIX_RA(suffix); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci d0 = instr & 0x3ffff; 20762306a36Sopenharmony_ci d1 = suffix & 0xffff; 20862306a36Sopenharmony_ci d = (d0 << 16) | d1; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * sign extend a 34 bit number 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci dd = (unsigned int)(d >> 2); 21462306a36Sopenharmony_ci ea = (signed int)dd; 21562306a36Sopenharmony_ci ea = (ea << 2) | (d & 0x3); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (!prefix_r && ra) 21862306a36Sopenharmony_ci ea += regs->gpr[ra]; 21962306a36Sopenharmony_ci else if (!prefix_r && !ra) 22062306a36Sopenharmony_ci ; /* Leave ea as is */ 22162306a36Sopenharmony_ci else if (prefix_r) 22262306a36Sopenharmony_ci ea += regs->nip; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * (prefix_r && ra) is an invalid form. Should already be 22662306a36Sopenharmony_ci * checked for by caller! 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return ea; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* 23362306a36Sopenharmony_ci * Return the largest power of 2, not greater than sizeof(unsigned long), 23462306a36Sopenharmony_ci * such that x is a multiple of it. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistatic nokprobe_inline unsigned long max_align(unsigned long x) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci x |= sizeof(unsigned long); 23962306a36Sopenharmony_ci return x & -x; /* isolates rightmost bit */ 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic nokprobe_inline unsigned long byterev_2(unsigned long x) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci return ((x >> 8) & 0xff) | ((x & 0xff) << 8); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic nokprobe_inline unsigned long byterev_4(unsigned long x) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci return ((x >> 24) & 0xff) | ((x >> 8) & 0xff00) | 25062306a36Sopenharmony_ci ((x & 0xff00) << 8) | ((x & 0xff) << 24); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci#ifdef __powerpc64__ 25462306a36Sopenharmony_cistatic nokprobe_inline unsigned long byterev_8(unsigned long x) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci return (byterev_4(x) << 32) | byterev_4(x >> 32); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci#endif 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic nokprobe_inline void do_byte_reverse(void *ptr, int nb) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci switch (nb) { 26362306a36Sopenharmony_ci case 2: 26462306a36Sopenharmony_ci *(u16 *)ptr = byterev_2(*(u16 *)ptr); 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci case 4: 26762306a36Sopenharmony_ci *(u32 *)ptr = byterev_4(*(u32 *)ptr); 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci#ifdef __powerpc64__ 27062306a36Sopenharmony_ci case 8: 27162306a36Sopenharmony_ci *(unsigned long *)ptr = byterev_8(*(unsigned long *)ptr); 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case 16: { 27462306a36Sopenharmony_ci unsigned long *up = (unsigned long *)ptr; 27562306a36Sopenharmony_ci unsigned long tmp; 27662306a36Sopenharmony_ci tmp = byterev_8(up[0]); 27762306a36Sopenharmony_ci up[0] = byterev_8(up[1]); 27862306a36Sopenharmony_ci up[1] = tmp; 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci case 32: { 28262306a36Sopenharmony_ci unsigned long *up = (unsigned long *)ptr; 28362306a36Sopenharmony_ci unsigned long tmp; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci tmp = byterev_8(up[0]); 28662306a36Sopenharmony_ci up[0] = byterev_8(up[3]); 28762306a36Sopenharmony_ci up[3] = tmp; 28862306a36Sopenharmony_ci tmp = byterev_8(up[2]); 28962306a36Sopenharmony_ci up[2] = byterev_8(up[1]); 29062306a36Sopenharmony_ci up[1] = tmp; 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci#endif 29562306a36Sopenharmony_ci default: 29662306a36Sopenharmony_ci WARN_ON_ONCE(1); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic __always_inline int 30162306a36Sopenharmony_ci__read_mem_aligned(unsigned long *dest, unsigned long ea, int nb, struct pt_regs *regs) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci unsigned long x = 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci switch (nb) { 30662306a36Sopenharmony_ci case 1: 30762306a36Sopenharmony_ci unsafe_get_user(x, (unsigned char __user *)ea, Efault); 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci case 2: 31062306a36Sopenharmony_ci unsafe_get_user(x, (unsigned short __user *)ea, Efault); 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci case 4: 31362306a36Sopenharmony_ci unsafe_get_user(x, (unsigned int __user *)ea, Efault); 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci#ifdef __powerpc64__ 31662306a36Sopenharmony_ci case 8: 31762306a36Sopenharmony_ci unsafe_get_user(x, (unsigned long __user *)ea, Efault); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci#endif 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci *dest = x; 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ciEfault: 32562306a36Sopenharmony_ci regs->dar = ea; 32662306a36Sopenharmony_ci return -EFAULT; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic nokprobe_inline int 33062306a36Sopenharmony_ciread_mem_aligned(unsigned long *dest, unsigned long ea, int nb, struct pt_regs *regs) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci int err; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (is_kernel_addr(ea)) 33562306a36Sopenharmony_ci return __read_mem_aligned(dest, ea, nb, regs); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (user_read_access_begin((void __user *)ea, nb)) { 33862306a36Sopenharmony_ci err = __read_mem_aligned(dest, ea, nb, regs); 33962306a36Sopenharmony_ci user_read_access_end(); 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci err = -EFAULT; 34262306a36Sopenharmony_ci regs->dar = ea; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return err; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* 34962306a36Sopenharmony_ci * Copy from userspace to a buffer, using the largest possible 35062306a36Sopenharmony_ci * aligned accesses, up to sizeof(long). 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_cistatic __always_inline int __copy_mem_in(u8 *dest, unsigned long ea, int nb, struct pt_regs *regs) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci int c; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci for (; nb > 0; nb -= c) { 35762306a36Sopenharmony_ci c = max_align(ea); 35862306a36Sopenharmony_ci if (c > nb) 35962306a36Sopenharmony_ci c = max_align(nb); 36062306a36Sopenharmony_ci switch (c) { 36162306a36Sopenharmony_ci case 1: 36262306a36Sopenharmony_ci unsafe_get_user(*dest, (u8 __user *)ea, Efault); 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci case 2: 36562306a36Sopenharmony_ci unsafe_get_user(*(u16 *)dest, (u16 __user *)ea, Efault); 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci case 4: 36862306a36Sopenharmony_ci unsafe_get_user(*(u32 *)dest, (u32 __user *)ea, Efault); 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci#ifdef __powerpc64__ 37162306a36Sopenharmony_ci case 8: 37262306a36Sopenharmony_ci unsafe_get_user(*(u64 *)dest, (u64 __user *)ea, Efault); 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci#endif 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci dest += c; 37762306a36Sopenharmony_ci ea += c; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ciEfault: 38262306a36Sopenharmony_ci regs->dar = ea; 38362306a36Sopenharmony_ci return -EFAULT; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic nokprobe_inline int copy_mem_in(u8 *dest, unsigned long ea, int nb, struct pt_regs *regs) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci int err; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (is_kernel_addr(ea)) 39162306a36Sopenharmony_ci return __copy_mem_in(dest, ea, nb, regs); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (user_read_access_begin((void __user *)ea, nb)) { 39462306a36Sopenharmony_ci err = __copy_mem_in(dest, ea, nb, regs); 39562306a36Sopenharmony_ci user_read_access_end(); 39662306a36Sopenharmony_ci } else { 39762306a36Sopenharmony_ci err = -EFAULT; 39862306a36Sopenharmony_ci regs->dar = ea; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return err; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic nokprobe_inline int read_mem_unaligned(unsigned long *dest, 40562306a36Sopenharmony_ci unsigned long ea, int nb, 40662306a36Sopenharmony_ci struct pt_regs *regs) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci union { 40962306a36Sopenharmony_ci unsigned long ul; 41062306a36Sopenharmony_ci u8 b[sizeof(unsigned long)]; 41162306a36Sopenharmony_ci } u; 41262306a36Sopenharmony_ci int i; 41362306a36Sopenharmony_ci int err; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci u.ul = 0; 41662306a36Sopenharmony_ci i = IS_BE ? sizeof(unsigned long) - nb : 0; 41762306a36Sopenharmony_ci err = copy_mem_in(&u.b[i], ea, nb, regs); 41862306a36Sopenharmony_ci if (!err) 41962306a36Sopenharmony_ci *dest = u.ul; 42062306a36Sopenharmony_ci return err; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci/* 42462306a36Sopenharmony_ci * Read memory at address ea for nb bytes, return 0 for success 42562306a36Sopenharmony_ci * or -EFAULT if an error occurred. N.B. nb must be 1, 2, 4 or 8. 42662306a36Sopenharmony_ci * If nb < sizeof(long), the result is right-justified on BE systems. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_cistatic int read_mem(unsigned long *dest, unsigned long ea, int nb, 42962306a36Sopenharmony_ci struct pt_regs *regs) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci if (!address_ok(regs, ea, nb)) 43262306a36Sopenharmony_ci return -EFAULT; 43362306a36Sopenharmony_ci if ((ea & (nb - 1)) == 0) 43462306a36Sopenharmony_ci return read_mem_aligned(dest, ea, nb, regs); 43562306a36Sopenharmony_ci return read_mem_unaligned(dest, ea, nb, regs); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ciNOKPROBE_SYMBOL(read_mem); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic __always_inline int 44062306a36Sopenharmony_ci__write_mem_aligned(unsigned long val, unsigned long ea, int nb, struct pt_regs *regs) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci switch (nb) { 44362306a36Sopenharmony_ci case 1: 44462306a36Sopenharmony_ci unsafe_put_user(val, (unsigned char __user *)ea, Efault); 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci case 2: 44762306a36Sopenharmony_ci unsafe_put_user(val, (unsigned short __user *)ea, Efault); 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci case 4: 45062306a36Sopenharmony_ci unsafe_put_user(val, (unsigned int __user *)ea, Efault); 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci#ifdef __powerpc64__ 45362306a36Sopenharmony_ci case 8: 45462306a36Sopenharmony_ci unsafe_put_user(val, (unsigned long __user *)ea, Efault); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci#endif 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ciEfault: 46162306a36Sopenharmony_ci regs->dar = ea; 46262306a36Sopenharmony_ci return -EFAULT; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic nokprobe_inline int 46662306a36Sopenharmony_ciwrite_mem_aligned(unsigned long val, unsigned long ea, int nb, struct pt_regs *regs) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci int err; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (is_kernel_addr(ea)) 47162306a36Sopenharmony_ci return __write_mem_aligned(val, ea, nb, regs); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (user_write_access_begin((void __user *)ea, nb)) { 47462306a36Sopenharmony_ci err = __write_mem_aligned(val, ea, nb, regs); 47562306a36Sopenharmony_ci user_write_access_end(); 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci err = -EFAULT; 47862306a36Sopenharmony_ci regs->dar = ea; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return err; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci/* 48562306a36Sopenharmony_ci * Copy from a buffer to userspace, using the largest possible 48662306a36Sopenharmony_ci * aligned accesses, up to sizeof(long). 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_cistatic __always_inline int __copy_mem_out(u8 *dest, unsigned long ea, int nb, struct pt_regs *regs) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci int c; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci for (; nb > 0; nb -= c) { 49362306a36Sopenharmony_ci c = max_align(ea); 49462306a36Sopenharmony_ci if (c > nb) 49562306a36Sopenharmony_ci c = max_align(nb); 49662306a36Sopenharmony_ci switch (c) { 49762306a36Sopenharmony_ci case 1: 49862306a36Sopenharmony_ci unsafe_put_user(*dest, (u8 __user *)ea, Efault); 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci case 2: 50162306a36Sopenharmony_ci unsafe_put_user(*(u16 *)dest, (u16 __user *)ea, Efault); 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci case 4: 50462306a36Sopenharmony_ci unsafe_put_user(*(u32 *)dest, (u32 __user *)ea, Efault); 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci#ifdef __powerpc64__ 50762306a36Sopenharmony_ci case 8: 50862306a36Sopenharmony_ci unsafe_put_user(*(u64 *)dest, (u64 __user *)ea, Efault); 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci#endif 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci dest += c; 51362306a36Sopenharmony_ci ea += c; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ciEfault: 51862306a36Sopenharmony_ci regs->dar = ea; 51962306a36Sopenharmony_ci return -EFAULT; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic nokprobe_inline int copy_mem_out(u8 *dest, unsigned long ea, int nb, struct pt_regs *regs) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci int err; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (is_kernel_addr(ea)) 52762306a36Sopenharmony_ci return __copy_mem_out(dest, ea, nb, regs); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (user_write_access_begin((void __user *)ea, nb)) { 53062306a36Sopenharmony_ci err = __copy_mem_out(dest, ea, nb, regs); 53162306a36Sopenharmony_ci user_write_access_end(); 53262306a36Sopenharmony_ci } else { 53362306a36Sopenharmony_ci err = -EFAULT; 53462306a36Sopenharmony_ci regs->dar = ea; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return err; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic nokprobe_inline int write_mem_unaligned(unsigned long val, 54162306a36Sopenharmony_ci unsigned long ea, int nb, 54262306a36Sopenharmony_ci struct pt_regs *regs) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci union { 54562306a36Sopenharmony_ci unsigned long ul; 54662306a36Sopenharmony_ci u8 b[sizeof(unsigned long)]; 54762306a36Sopenharmony_ci } u; 54862306a36Sopenharmony_ci int i; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci u.ul = val; 55162306a36Sopenharmony_ci i = IS_BE ? sizeof(unsigned long) - nb : 0; 55262306a36Sopenharmony_ci return copy_mem_out(&u.b[i], ea, nb, regs); 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci/* 55662306a36Sopenharmony_ci * Write memory at address ea for nb bytes, return 0 for success 55762306a36Sopenharmony_ci * or -EFAULT if an error occurred. N.B. nb must be 1, 2, 4 or 8. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_cistatic int write_mem(unsigned long val, unsigned long ea, int nb, 56062306a36Sopenharmony_ci struct pt_regs *regs) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci if (!address_ok(regs, ea, nb)) 56362306a36Sopenharmony_ci return -EFAULT; 56462306a36Sopenharmony_ci if ((ea & (nb - 1)) == 0) 56562306a36Sopenharmony_ci return write_mem_aligned(val, ea, nb, regs); 56662306a36Sopenharmony_ci return write_mem_unaligned(val, ea, nb, regs); 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ciNOKPROBE_SYMBOL(write_mem); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci#ifdef CONFIG_PPC_FPU 57162306a36Sopenharmony_ci/* 57262306a36Sopenharmony_ci * These access either the real FP register or the image in the 57362306a36Sopenharmony_ci * thread_struct, depending on regs->msr & MSR_FP. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_cistatic int do_fp_load(struct instruction_op *op, unsigned long ea, 57662306a36Sopenharmony_ci struct pt_regs *regs, bool cross_endian) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci int err, rn, nb; 57962306a36Sopenharmony_ci union { 58062306a36Sopenharmony_ci int i; 58162306a36Sopenharmony_ci unsigned int u; 58262306a36Sopenharmony_ci float f; 58362306a36Sopenharmony_ci double d[2]; 58462306a36Sopenharmony_ci unsigned long l[2]; 58562306a36Sopenharmony_ci u8 b[2 * sizeof(double)]; 58662306a36Sopenharmony_ci } u; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci nb = GETSIZE(op->type); 58962306a36Sopenharmony_ci if (nb > sizeof(u)) 59062306a36Sopenharmony_ci return -EINVAL; 59162306a36Sopenharmony_ci if (!address_ok(regs, ea, nb)) 59262306a36Sopenharmony_ci return -EFAULT; 59362306a36Sopenharmony_ci rn = op->reg; 59462306a36Sopenharmony_ci err = copy_mem_in(u.b, ea, nb, regs); 59562306a36Sopenharmony_ci if (err) 59662306a36Sopenharmony_ci return err; 59762306a36Sopenharmony_ci if (unlikely(cross_endian)) { 59862306a36Sopenharmony_ci do_byte_reverse(u.b, min(nb, 8)); 59962306a36Sopenharmony_ci if (nb == 16) 60062306a36Sopenharmony_ci do_byte_reverse(&u.b[8], 8); 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci preempt_disable(); 60362306a36Sopenharmony_ci if (nb == 4) { 60462306a36Sopenharmony_ci if (op->type & FPCONV) 60562306a36Sopenharmony_ci conv_sp_to_dp(&u.f, &u.d[0]); 60662306a36Sopenharmony_ci else if (op->type & SIGNEXT) 60762306a36Sopenharmony_ci u.l[0] = u.i; 60862306a36Sopenharmony_ci else 60962306a36Sopenharmony_ci u.l[0] = u.u; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci if (regs->msr & MSR_FP) 61262306a36Sopenharmony_ci put_fpr(rn, &u.d[0]); 61362306a36Sopenharmony_ci else 61462306a36Sopenharmony_ci current->thread.TS_FPR(rn) = u.l[0]; 61562306a36Sopenharmony_ci if (nb == 16) { 61662306a36Sopenharmony_ci /* lfdp */ 61762306a36Sopenharmony_ci rn |= 1; 61862306a36Sopenharmony_ci if (regs->msr & MSR_FP) 61962306a36Sopenharmony_ci put_fpr(rn, &u.d[1]); 62062306a36Sopenharmony_ci else 62162306a36Sopenharmony_ci current->thread.TS_FPR(rn) = u.l[1]; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci preempt_enable(); 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ciNOKPROBE_SYMBOL(do_fp_load); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int do_fp_store(struct instruction_op *op, unsigned long ea, 62962306a36Sopenharmony_ci struct pt_regs *regs, bool cross_endian) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci int rn, nb; 63262306a36Sopenharmony_ci union { 63362306a36Sopenharmony_ci unsigned int u; 63462306a36Sopenharmony_ci float f; 63562306a36Sopenharmony_ci double d[2]; 63662306a36Sopenharmony_ci unsigned long l[2]; 63762306a36Sopenharmony_ci u8 b[2 * sizeof(double)]; 63862306a36Sopenharmony_ci } u; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci nb = GETSIZE(op->type); 64162306a36Sopenharmony_ci if (nb > sizeof(u)) 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci if (!address_ok(regs, ea, nb)) 64462306a36Sopenharmony_ci return -EFAULT; 64562306a36Sopenharmony_ci rn = op->reg; 64662306a36Sopenharmony_ci preempt_disable(); 64762306a36Sopenharmony_ci if (regs->msr & MSR_FP) 64862306a36Sopenharmony_ci get_fpr(rn, &u.d[0]); 64962306a36Sopenharmony_ci else 65062306a36Sopenharmony_ci u.l[0] = current->thread.TS_FPR(rn); 65162306a36Sopenharmony_ci if (nb == 4) { 65262306a36Sopenharmony_ci if (op->type & FPCONV) 65362306a36Sopenharmony_ci conv_dp_to_sp(&u.d[0], &u.f); 65462306a36Sopenharmony_ci else 65562306a36Sopenharmony_ci u.u = u.l[0]; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci if (nb == 16) { 65862306a36Sopenharmony_ci rn |= 1; 65962306a36Sopenharmony_ci if (regs->msr & MSR_FP) 66062306a36Sopenharmony_ci get_fpr(rn, &u.d[1]); 66162306a36Sopenharmony_ci else 66262306a36Sopenharmony_ci u.l[1] = current->thread.TS_FPR(rn); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci preempt_enable(); 66562306a36Sopenharmony_ci if (unlikely(cross_endian)) { 66662306a36Sopenharmony_ci do_byte_reverse(u.b, min(nb, 8)); 66762306a36Sopenharmony_ci if (nb == 16) 66862306a36Sopenharmony_ci do_byte_reverse(&u.b[8], 8); 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci return copy_mem_out(u.b, ea, nb, regs); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ciNOKPROBE_SYMBOL(do_fp_store); 67362306a36Sopenharmony_ci#endif 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci#ifdef CONFIG_ALTIVEC 67662306a36Sopenharmony_ci/* For Altivec/VMX, no need to worry about alignment */ 67762306a36Sopenharmony_cistatic nokprobe_inline int do_vec_load(int rn, unsigned long ea, 67862306a36Sopenharmony_ci int size, struct pt_regs *regs, 67962306a36Sopenharmony_ci bool cross_endian) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci int err; 68262306a36Sopenharmony_ci union { 68362306a36Sopenharmony_ci __vector128 v; 68462306a36Sopenharmony_ci u8 b[sizeof(__vector128)]; 68562306a36Sopenharmony_ci } u = {}; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (size > sizeof(u)) 68862306a36Sopenharmony_ci return -EINVAL; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (!address_ok(regs, ea & ~0xfUL, 16)) 69162306a36Sopenharmony_ci return -EFAULT; 69262306a36Sopenharmony_ci /* align to multiple of size */ 69362306a36Sopenharmony_ci ea &= ~(size - 1); 69462306a36Sopenharmony_ci err = copy_mem_in(&u.b[ea & 0xf], ea, size, regs); 69562306a36Sopenharmony_ci if (err) 69662306a36Sopenharmony_ci return err; 69762306a36Sopenharmony_ci if (unlikely(cross_endian)) 69862306a36Sopenharmony_ci do_byte_reverse(&u.b[ea & 0xf], size); 69962306a36Sopenharmony_ci preempt_disable(); 70062306a36Sopenharmony_ci if (regs->msr & MSR_VEC) 70162306a36Sopenharmony_ci put_vr(rn, &u.v); 70262306a36Sopenharmony_ci else 70362306a36Sopenharmony_ci current->thread.vr_state.vr[rn] = u.v; 70462306a36Sopenharmony_ci preempt_enable(); 70562306a36Sopenharmony_ci return 0; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic nokprobe_inline int do_vec_store(int rn, unsigned long ea, 70962306a36Sopenharmony_ci int size, struct pt_regs *regs, 71062306a36Sopenharmony_ci bool cross_endian) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci union { 71362306a36Sopenharmony_ci __vector128 v; 71462306a36Sopenharmony_ci u8 b[sizeof(__vector128)]; 71562306a36Sopenharmony_ci } u; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (size > sizeof(u)) 71862306a36Sopenharmony_ci return -EINVAL; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (!address_ok(regs, ea & ~0xfUL, 16)) 72162306a36Sopenharmony_ci return -EFAULT; 72262306a36Sopenharmony_ci /* align to multiple of size */ 72362306a36Sopenharmony_ci ea &= ~(size - 1); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci preempt_disable(); 72662306a36Sopenharmony_ci if (regs->msr & MSR_VEC) 72762306a36Sopenharmony_ci get_vr(rn, &u.v); 72862306a36Sopenharmony_ci else 72962306a36Sopenharmony_ci u.v = current->thread.vr_state.vr[rn]; 73062306a36Sopenharmony_ci preempt_enable(); 73162306a36Sopenharmony_ci if (unlikely(cross_endian)) 73262306a36Sopenharmony_ci do_byte_reverse(&u.b[ea & 0xf], size); 73362306a36Sopenharmony_ci return copy_mem_out(&u.b[ea & 0xf], ea, size, regs); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci#endif /* CONFIG_ALTIVEC */ 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci#ifdef __powerpc64__ 73862306a36Sopenharmony_cistatic nokprobe_inline int emulate_lq(struct pt_regs *regs, unsigned long ea, 73962306a36Sopenharmony_ci int reg, bool cross_endian) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci int err; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!address_ok(regs, ea, 16)) 74462306a36Sopenharmony_ci return -EFAULT; 74562306a36Sopenharmony_ci /* if aligned, should be atomic */ 74662306a36Sopenharmony_ci if ((ea & 0xf) == 0) { 74762306a36Sopenharmony_ci err = do_lq(ea, ®s->gpr[reg]); 74862306a36Sopenharmony_ci } else { 74962306a36Sopenharmony_ci err = read_mem(®s->gpr[reg + IS_LE], ea, 8, regs); 75062306a36Sopenharmony_ci if (!err) 75162306a36Sopenharmony_ci err = read_mem(®s->gpr[reg + IS_BE], ea + 8, 8, regs); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci if (!err && unlikely(cross_endian)) 75462306a36Sopenharmony_ci do_byte_reverse(®s->gpr[reg], 16); 75562306a36Sopenharmony_ci return err; 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic nokprobe_inline int emulate_stq(struct pt_regs *regs, unsigned long ea, 75962306a36Sopenharmony_ci int reg, bool cross_endian) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci int err; 76262306a36Sopenharmony_ci unsigned long vals[2]; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (!address_ok(regs, ea, 16)) 76562306a36Sopenharmony_ci return -EFAULT; 76662306a36Sopenharmony_ci vals[0] = regs->gpr[reg]; 76762306a36Sopenharmony_ci vals[1] = regs->gpr[reg + 1]; 76862306a36Sopenharmony_ci if (unlikely(cross_endian)) 76962306a36Sopenharmony_ci do_byte_reverse(vals, 16); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* if aligned, should be atomic */ 77262306a36Sopenharmony_ci if ((ea & 0xf) == 0) 77362306a36Sopenharmony_ci return do_stq(ea, vals[0], vals[1]); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci err = write_mem(vals[IS_LE], ea, 8, regs); 77662306a36Sopenharmony_ci if (!err) 77762306a36Sopenharmony_ci err = write_mem(vals[IS_BE], ea + 8, 8, regs); 77862306a36Sopenharmony_ci return err; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci#endif /* __powerpc64 */ 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci#ifdef CONFIG_VSX 78362306a36Sopenharmony_civoid emulate_vsx_load(struct instruction_op *op, union vsx_reg *reg, 78462306a36Sopenharmony_ci const void *mem, bool rev) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci int size, read_size; 78762306a36Sopenharmony_ci int i, j; 78862306a36Sopenharmony_ci const unsigned int *wp; 78962306a36Sopenharmony_ci const unsigned short *hp; 79062306a36Sopenharmony_ci const unsigned char *bp; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci size = GETSIZE(op->type); 79362306a36Sopenharmony_ci reg->d[0] = reg->d[1] = 0; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci switch (op->element_size) { 79662306a36Sopenharmony_ci case 32: 79762306a36Sopenharmony_ci /* [p]lxvp[x] */ 79862306a36Sopenharmony_ci case 16: 79962306a36Sopenharmony_ci /* whole vector; lxv[x] or lxvl[l] */ 80062306a36Sopenharmony_ci if (size == 0) 80162306a36Sopenharmony_ci break; 80262306a36Sopenharmony_ci memcpy(reg, mem, size); 80362306a36Sopenharmony_ci if (IS_LE && (op->vsx_flags & VSX_LDLEFT)) 80462306a36Sopenharmony_ci rev = !rev; 80562306a36Sopenharmony_ci if (rev) 80662306a36Sopenharmony_ci do_byte_reverse(reg, size); 80762306a36Sopenharmony_ci break; 80862306a36Sopenharmony_ci case 8: 80962306a36Sopenharmony_ci /* scalar loads, lxvd2x, lxvdsx */ 81062306a36Sopenharmony_ci read_size = (size >= 8) ? 8 : size; 81162306a36Sopenharmony_ci i = IS_LE ? 8 : 8 - read_size; 81262306a36Sopenharmony_ci memcpy(®->b[i], mem, read_size); 81362306a36Sopenharmony_ci if (rev) 81462306a36Sopenharmony_ci do_byte_reverse(®->b[i], 8); 81562306a36Sopenharmony_ci if (size < 8) { 81662306a36Sopenharmony_ci if (op->type & SIGNEXT) { 81762306a36Sopenharmony_ci /* size == 4 is the only case here */ 81862306a36Sopenharmony_ci reg->d[IS_LE] = (signed int) reg->d[IS_LE]; 81962306a36Sopenharmony_ci } else if (op->vsx_flags & VSX_FPCONV) { 82062306a36Sopenharmony_ci preempt_disable(); 82162306a36Sopenharmony_ci conv_sp_to_dp(®->fp[1 + IS_LE], 82262306a36Sopenharmony_ci ®->dp[IS_LE]); 82362306a36Sopenharmony_ci preempt_enable(); 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci } else { 82662306a36Sopenharmony_ci if (size == 16) { 82762306a36Sopenharmony_ci unsigned long v = *(unsigned long *)(mem + 8); 82862306a36Sopenharmony_ci reg->d[IS_BE] = !rev ? v : byterev_8(v); 82962306a36Sopenharmony_ci } else if (op->vsx_flags & VSX_SPLAT) 83062306a36Sopenharmony_ci reg->d[IS_BE] = reg->d[IS_LE]; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci case 4: 83462306a36Sopenharmony_ci /* lxvw4x, lxvwsx */ 83562306a36Sopenharmony_ci wp = mem; 83662306a36Sopenharmony_ci for (j = 0; j < size / 4; ++j) { 83762306a36Sopenharmony_ci i = IS_LE ? 3 - j : j; 83862306a36Sopenharmony_ci reg->w[i] = !rev ? *wp++ : byterev_4(*wp++); 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci if (op->vsx_flags & VSX_SPLAT) { 84162306a36Sopenharmony_ci u32 val = reg->w[IS_LE ? 3 : 0]; 84262306a36Sopenharmony_ci for (; j < 4; ++j) { 84362306a36Sopenharmony_ci i = IS_LE ? 3 - j : j; 84462306a36Sopenharmony_ci reg->w[i] = val; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci break; 84862306a36Sopenharmony_ci case 2: 84962306a36Sopenharmony_ci /* lxvh8x */ 85062306a36Sopenharmony_ci hp = mem; 85162306a36Sopenharmony_ci for (j = 0; j < size / 2; ++j) { 85262306a36Sopenharmony_ci i = IS_LE ? 7 - j : j; 85362306a36Sopenharmony_ci reg->h[i] = !rev ? *hp++ : byterev_2(*hp++); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci break; 85662306a36Sopenharmony_ci case 1: 85762306a36Sopenharmony_ci /* lxvb16x */ 85862306a36Sopenharmony_ci bp = mem; 85962306a36Sopenharmony_ci for (j = 0; j < size; ++j) { 86062306a36Sopenharmony_ci i = IS_LE ? 15 - j : j; 86162306a36Sopenharmony_ci reg->b[i] = *bp++; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(emulate_vsx_load); 86762306a36Sopenharmony_ciNOKPROBE_SYMBOL(emulate_vsx_load); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_civoid emulate_vsx_store(struct instruction_op *op, const union vsx_reg *reg, 87062306a36Sopenharmony_ci void *mem, bool rev) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci int size, write_size; 87362306a36Sopenharmony_ci int i, j; 87462306a36Sopenharmony_ci union vsx_reg buf; 87562306a36Sopenharmony_ci unsigned int *wp; 87662306a36Sopenharmony_ci unsigned short *hp; 87762306a36Sopenharmony_ci unsigned char *bp; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci size = GETSIZE(op->type); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci switch (op->element_size) { 88262306a36Sopenharmony_ci case 32: 88362306a36Sopenharmony_ci /* [p]stxvp[x] */ 88462306a36Sopenharmony_ci if (size == 0) 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci if (rev) { 88762306a36Sopenharmony_ci /* reverse 32 bytes */ 88862306a36Sopenharmony_ci union vsx_reg buf32[2]; 88962306a36Sopenharmony_ci buf32[0].d[0] = byterev_8(reg[1].d[1]); 89062306a36Sopenharmony_ci buf32[0].d[1] = byterev_8(reg[1].d[0]); 89162306a36Sopenharmony_ci buf32[1].d[0] = byterev_8(reg[0].d[1]); 89262306a36Sopenharmony_ci buf32[1].d[1] = byterev_8(reg[0].d[0]); 89362306a36Sopenharmony_ci memcpy(mem, buf32, size); 89462306a36Sopenharmony_ci } else { 89562306a36Sopenharmony_ci memcpy(mem, reg, size); 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci break; 89862306a36Sopenharmony_ci case 16: 89962306a36Sopenharmony_ci /* stxv, stxvx, stxvl, stxvll */ 90062306a36Sopenharmony_ci if (size == 0) 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci if (IS_LE && (op->vsx_flags & VSX_LDLEFT)) 90362306a36Sopenharmony_ci rev = !rev; 90462306a36Sopenharmony_ci if (rev) { 90562306a36Sopenharmony_ci /* reverse 16 bytes */ 90662306a36Sopenharmony_ci buf.d[0] = byterev_8(reg->d[1]); 90762306a36Sopenharmony_ci buf.d[1] = byterev_8(reg->d[0]); 90862306a36Sopenharmony_ci reg = &buf; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci memcpy(mem, reg, size); 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci case 8: 91362306a36Sopenharmony_ci /* scalar stores, stxvd2x */ 91462306a36Sopenharmony_ci write_size = (size >= 8) ? 8 : size; 91562306a36Sopenharmony_ci i = IS_LE ? 8 : 8 - write_size; 91662306a36Sopenharmony_ci if (size < 8 && op->vsx_flags & VSX_FPCONV) { 91762306a36Sopenharmony_ci buf.d[0] = buf.d[1] = 0; 91862306a36Sopenharmony_ci preempt_disable(); 91962306a36Sopenharmony_ci conv_dp_to_sp(®->dp[IS_LE], &buf.fp[1 + IS_LE]); 92062306a36Sopenharmony_ci preempt_enable(); 92162306a36Sopenharmony_ci reg = &buf; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci memcpy(mem, ®->b[i], write_size); 92462306a36Sopenharmony_ci if (size == 16) 92562306a36Sopenharmony_ci memcpy(mem + 8, ®->d[IS_BE], 8); 92662306a36Sopenharmony_ci if (unlikely(rev)) { 92762306a36Sopenharmony_ci do_byte_reverse(mem, write_size); 92862306a36Sopenharmony_ci if (size == 16) 92962306a36Sopenharmony_ci do_byte_reverse(mem + 8, 8); 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci break; 93262306a36Sopenharmony_ci case 4: 93362306a36Sopenharmony_ci /* stxvw4x */ 93462306a36Sopenharmony_ci wp = mem; 93562306a36Sopenharmony_ci for (j = 0; j < size / 4; ++j) { 93662306a36Sopenharmony_ci i = IS_LE ? 3 - j : j; 93762306a36Sopenharmony_ci *wp++ = !rev ? reg->w[i] : byterev_4(reg->w[i]); 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci break; 94062306a36Sopenharmony_ci case 2: 94162306a36Sopenharmony_ci /* stxvh8x */ 94262306a36Sopenharmony_ci hp = mem; 94362306a36Sopenharmony_ci for (j = 0; j < size / 2; ++j) { 94462306a36Sopenharmony_ci i = IS_LE ? 7 - j : j; 94562306a36Sopenharmony_ci *hp++ = !rev ? reg->h[i] : byterev_2(reg->h[i]); 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci break; 94862306a36Sopenharmony_ci case 1: 94962306a36Sopenharmony_ci /* stvxb16x */ 95062306a36Sopenharmony_ci bp = mem; 95162306a36Sopenharmony_ci for (j = 0; j < size; ++j) { 95262306a36Sopenharmony_ci i = IS_LE ? 15 - j : j; 95362306a36Sopenharmony_ci *bp++ = reg->b[i]; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci break; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(emulate_vsx_store); 95962306a36Sopenharmony_ciNOKPROBE_SYMBOL(emulate_vsx_store); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic nokprobe_inline int do_vsx_load(struct instruction_op *op, 96262306a36Sopenharmony_ci unsigned long ea, struct pt_regs *regs, 96362306a36Sopenharmony_ci bool cross_endian) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci int reg = op->reg; 96662306a36Sopenharmony_ci int i, j, nr_vsx_regs; 96762306a36Sopenharmony_ci u8 mem[32]; 96862306a36Sopenharmony_ci union vsx_reg buf[2]; 96962306a36Sopenharmony_ci int size = GETSIZE(op->type); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (!address_ok(regs, ea, size) || copy_mem_in(mem, ea, size, regs)) 97262306a36Sopenharmony_ci return -EFAULT; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci nr_vsx_regs = max(1ul, size / sizeof(__vector128)); 97562306a36Sopenharmony_ci emulate_vsx_load(op, buf, mem, cross_endian); 97662306a36Sopenharmony_ci preempt_disable(); 97762306a36Sopenharmony_ci if (reg < 32) { 97862306a36Sopenharmony_ci /* FP regs + extensions */ 97962306a36Sopenharmony_ci if (regs->msr & MSR_FP) { 98062306a36Sopenharmony_ci for (i = 0; i < nr_vsx_regs; i++) { 98162306a36Sopenharmony_ci j = IS_LE ? nr_vsx_regs - i - 1 : i; 98262306a36Sopenharmony_ci load_vsrn(reg + i, &buf[j].v); 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci } else { 98562306a36Sopenharmony_ci for (i = 0; i < nr_vsx_regs; i++) { 98662306a36Sopenharmony_ci j = IS_LE ? nr_vsx_regs - i - 1 : i; 98762306a36Sopenharmony_ci current->thread.fp_state.fpr[reg + i][0] = buf[j].d[0]; 98862306a36Sopenharmony_ci current->thread.fp_state.fpr[reg + i][1] = buf[j].d[1]; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci } else { 99262306a36Sopenharmony_ci if (regs->msr & MSR_VEC) { 99362306a36Sopenharmony_ci for (i = 0; i < nr_vsx_regs; i++) { 99462306a36Sopenharmony_ci j = IS_LE ? nr_vsx_regs - i - 1 : i; 99562306a36Sopenharmony_ci load_vsrn(reg + i, &buf[j].v); 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci } else { 99862306a36Sopenharmony_ci for (i = 0; i < nr_vsx_regs; i++) { 99962306a36Sopenharmony_ci j = IS_LE ? nr_vsx_regs - i - 1 : i; 100062306a36Sopenharmony_ci current->thread.vr_state.vr[reg - 32 + i] = buf[j].v; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci preempt_enable(); 100562306a36Sopenharmony_ci return 0; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic nokprobe_inline int do_vsx_store(struct instruction_op *op, 100962306a36Sopenharmony_ci unsigned long ea, struct pt_regs *regs, 101062306a36Sopenharmony_ci bool cross_endian) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci int reg = op->reg; 101362306a36Sopenharmony_ci int i, j, nr_vsx_regs; 101462306a36Sopenharmony_ci u8 mem[32]; 101562306a36Sopenharmony_ci union vsx_reg buf[2]; 101662306a36Sopenharmony_ci int size = GETSIZE(op->type); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (!address_ok(regs, ea, size)) 101962306a36Sopenharmony_ci return -EFAULT; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci nr_vsx_regs = max(1ul, size / sizeof(__vector128)); 102262306a36Sopenharmony_ci preempt_disable(); 102362306a36Sopenharmony_ci if (reg < 32) { 102462306a36Sopenharmony_ci /* FP regs + extensions */ 102562306a36Sopenharmony_ci if (regs->msr & MSR_FP) { 102662306a36Sopenharmony_ci for (i = 0; i < nr_vsx_regs; i++) { 102762306a36Sopenharmony_ci j = IS_LE ? nr_vsx_regs - i - 1 : i; 102862306a36Sopenharmony_ci store_vsrn(reg + i, &buf[j].v); 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci } else { 103162306a36Sopenharmony_ci for (i = 0; i < nr_vsx_regs; i++) { 103262306a36Sopenharmony_ci j = IS_LE ? nr_vsx_regs - i - 1 : i; 103362306a36Sopenharmony_ci buf[j].d[0] = current->thread.fp_state.fpr[reg + i][0]; 103462306a36Sopenharmony_ci buf[j].d[1] = current->thread.fp_state.fpr[reg + i][1]; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci } else { 103862306a36Sopenharmony_ci if (regs->msr & MSR_VEC) { 103962306a36Sopenharmony_ci for (i = 0; i < nr_vsx_regs; i++) { 104062306a36Sopenharmony_ci j = IS_LE ? nr_vsx_regs - i - 1 : i; 104162306a36Sopenharmony_ci store_vsrn(reg + i, &buf[j].v); 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci } else { 104462306a36Sopenharmony_ci for (i = 0; i < nr_vsx_regs; i++) { 104562306a36Sopenharmony_ci j = IS_LE ? nr_vsx_regs - i - 1 : i; 104662306a36Sopenharmony_ci buf[j].v = current->thread.vr_state.vr[reg - 32 + i]; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci preempt_enable(); 105162306a36Sopenharmony_ci emulate_vsx_store(op, buf, mem, cross_endian); 105262306a36Sopenharmony_ci return copy_mem_out(mem, ea, size, regs); 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci#endif /* CONFIG_VSX */ 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic __always_inline int __emulate_dcbz(unsigned long ea) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci unsigned long i; 105962306a36Sopenharmony_ci unsigned long size = l1_dcache_bytes(); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci for (i = 0; i < size; i += sizeof(long)) 106262306a36Sopenharmony_ci unsafe_put_user(0, (unsigned long __user *)(ea + i), Efault); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci return 0; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ciEfault: 106762306a36Sopenharmony_ci return -EFAULT; 106862306a36Sopenharmony_ci} 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ciint emulate_dcbz(unsigned long ea, struct pt_regs *regs) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci int err; 107362306a36Sopenharmony_ci unsigned long size = l1_dcache_bytes(); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci ea = truncate_if_32bit(regs->msr, ea); 107662306a36Sopenharmony_ci ea &= ~(size - 1); 107762306a36Sopenharmony_ci if (!address_ok(regs, ea, size)) 107862306a36Sopenharmony_ci return -EFAULT; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (is_kernel_addr(ea)) { 108162306a36Sopenharmony_ci err = __emulate_dcbz(ea); 108262306a36Sopenharmony_ci } else if (user_write_access_begin((void __user *)ea, size)) { 108362306a36Sopenharmony_ci err = __emulate_dcbz(ea); 108462306a36Sopenharmony_ci user_write_access_end(); 108562306a36Sopenharmony_ci } else { 108662306a36Sopenharmony_ci err = -EFAULT; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (err) 109062306a36Sopenharmony_ci regs->dar = ea; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci return err; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ciNOKPROBE_SYMBOL(emulate_dcbz); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci#define __put_user_asmx(x, addr, err, op, cr) \ 109862306a36Sopenharmony_ci __asm__ __volatile__( \ 109962306a36Sopenharmony_ci ".machine push\n" \ 110062306a36Sopenharmony_ci ".machine power8\n" \ 110162306a36Sopenharmony_ci "1: " op " %2,0,%3\n" \ 110262306a36Sopenharmony_ci ".machine pop\n" \ 110362306a36Sopenharmony_ci " mfcr %1\n" \ 110462306a36Sopenharmony_ci "2:\n" \ 110562306a36Sopenharmony_ci ".section .fixup,\"ax\"\n" \ 110662306a36Sopenharmony_ci "3: li %0,%4\n" \ 110762306a36Sopenharmony_ci " b 2b\n" \ 110862306a36Sopenharmony_ci ".previous\n" \ 110962306a36Sopenharmony_ci EX_TABLE(1b, 3b) \ 111062306a36Sopenharmony_ci : "=r" (err), "=r" (cr) \ 111162306a36Sopenharmony_ci : "r" (x), "r" (addr), "i" (-EFAULT), "0" (err)) 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci#define __get_user_asmx(x, addr, err, op) \ 111462306a36Sopenharmony_ci __asm__ __volatile__( \ 111562306a36Sopenharmony_ci ".machine push\n" \ 111662306a36Sopenharmony_ci ".machine power8\n" \ 111762306a36Sopenharmony_ci "1: "op" %1,0,%2\n" \ 111862306a36Sopenharmony_ci ".machine pop\n" \ 111962306a36Sopenharmony_ci "2:\n" \ 112062306a36Sopenharmony_ci ".section .fixup,\"ax\"\n" \ 112162306a36Sopenharmony_ci "3: li %0,%3\n" \ 112262306a36Sopenharmony_ci " b 2b\n" \ 112362306a36Sopenharmony_ci ".previous\n" \ 112462306a36Sopenharmony_ci EX_TABLE(1b, 3b) \ 112562306a36Sopenharmony_ci : "=r" (err), "=r" (x) \ 112662306a36Sopenharmony_ci : "r" (addr), "i" (-EFAULT), "0" (err)) 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci#define __cacheop_user_asmx(addr, err, op) \ 112962306a36Sopenharmony_ci __asm__ __volatile__( \ 113062306a36Sopenharmony_ci "1: "op" 0,%1\n" \ 113162306a36Sopenharmony_ci "2:\n" \ 113262306a36Sopenharmony_ci ".section .fixup,\"ax\"\n" \ 113362306a36Sopenharmony_ci "3: li %0,%3\n" \ 113462306a36Sopenharmony_ci " b 2b\n" \ 113562306a36Sopenharmony_ci ".previous\n" \ 113662306a36Sopenharmony_ci EX_TABLE(1b, 3b) \ 113762306a36Sopenharmony_ci : "=r" (err) \ 113862306a36Sopenharmony_ci : "r" (addr), "i" (-EFAULT), "0" (err)) 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_cistatic nokprobe_inline void set_cr0(const struct pt_regs *regs, 114162306a36Sopenharmony_ci struct instruction_op *op) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci long val = op->val; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci op->type |= SETCC; 114662306a36Sopenharmony_ci op->ccval = (regs->ccr & 0x0fffffff) | ((regs->xer >> 3) & 0x10000000); 114762306a36Sopenharmony_ci if (!(regs->msr & MSR_64BIT)) 114862306a36Sopenharmony_ci val = (int) val; 114962306a36Sopenharmony_ci if (val < 0) 115062306a36Sopenharmony_ci op->ccval |= 0x80000000; 115162306a36Sopenharmony_ci else if (val > 0) 115262306a36Sopenharmony_ci op->ccval |= 0x40000000; 115362306a36Sopenharmony_ci else 115462306a36Sopenharmony_ci op->ccval |= 0x20000000; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic nokprobe_inline void set_ca32(struct instruction_op *op, bool val) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ARCH_300)) { 116062306a36Sopenharmony_ci if (val) 116162306a36Sopenharmony_ci op->xerval |= XER_CA32; 116262306a36Sopenharmony_ci else 116362306a36Sopenharmony_ci op->xerval &= ~XER_CA32; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic nokprobe_inline void add_with_carry(const struct pt_regs *regs, 116862306a36Sopenharmony_ci struct instruction_op *op, int rd, 116962306a36Sopenharmony_ci unsigned long val1, unsigned long val2, 117062306a36Sopenharmony_ci unsigned long carry_in) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci unsigned long val = val1 + val2; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (carry_in) 117562306a36Sopenharmony_ci ++val; 117662306a36Sopenharmony_ci op->type = COMPUTE | SETREG | SETXER; 117762306a36Sopenharmony_ci op->reg = rd; 117862306a36Sopenharmony_ci op->val = val; 117962306a36Sopenharmony_ci val = truncate_if_32bit(regs->msr, val); 118062306a36Sopenharmony_ci val1 = truncate_if_32bit(regs->msr, val1); 118162306a36Sopenharmony_ci op->xerval = regs->xer; 118262306a36Sopenharmony_ci if (val < val1 || (carry_in && val == val1)) 118362306a36Sopenharmony_ci op->xerval |= XER_CA; 118462306a36Sopenharmony_ci else 118562306a36Sopenharmony_ci op->xerval &= ~XER_CA; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci set_ca32(op, (unsigned int)val < (unsigned int)val1 || 118862306a36Sopenharmony_ci (carry_in && (unsigned int)val == (unsigned int)val1)); 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_cistatic nokprobe_inline void do_cmp_signed(const struct pt_regs *regs, 119262306a36Sopenharmony_ci struct instruction_op *op, 119362306a36Sopenharmony_ci long v1, long v2, int crfld) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci unsigned int crval, shift; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci op->type = COMPUTE | SETCC; 119862306a36Sopenharmony_ci crval = (regs->xer >> 31) & 1; /* get SO bit */ 119962306a36Sopenharmony_ci if (v1 < v2) 120062306a36Sopenharmony_ci crval |= 8; 120162306a36Sopenharmony_ci else if (v1 > v2) 120262306a36Sopenharmony_ci crval |= 4; 120362306a36Sopenharmony_ci else 120462306a36Sopenharmony_ci crval |= 2; 120562306a36Sopenharmony_ci shift = (7 - crfld) * 4; 120662306a36Sopenharmony_ci op->ccval = (regs->ccr & ~(0xf << shift)) | (crval << shift); 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_cistatic nokprobe_inline void do_cmp_unsigned(const struct pt_regs *regs, 121062306a36Sopenharmony_ci struct instruction_op *op, 121162306a36Sopenharmony_ci unsigned long v1, 121262306a36Sopenharmony_ci unsigned long v2, int crfld) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci unsigned int crval, shift; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci op->type = COMPUTE | SETCC; 121762306a36Sopenharmony_ci crval = (regs->xer >> 31) & 1; /* get SO bit */ 121862306a36Sopenharmony_ci if (v1 < v2) 121962306a36Sopenharmony_ci crval |= 8; 122062306a36Sopenharmony_ci else if (v1 > v2) 122162306a36Sopenharmony_ci crval |= 4; 122262306a36Sopenharmony_ci else 122362306a36Sopenharmony_ci crval |= 2; 122462306a36Sopenharmony_ci shift = (7 - crfld) * 4; 122562306a36Sopenharmony_ci op->ccval = (regs->ccr & ~(0xf << shift)) | (crval << shift); 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_cistatic nokprobe_inline void do_cmpb(const struct pt_regs *regs, 122962306a36Sopenharmony_ci struct instruction_op *op, 123062306a36Sopenharmony_ci unsigned long v1, unsigned long v2) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci unsigned long long out_val, mask; 123362306a36Sopenharmony_ci int i; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci out_val = 0; 123662306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 123762306a36Sopenharmony_ci mask = 0xffUL << (i * 8); 123862306a36Sopenharmony_ci if ((v1 & mask) == (v2 & mask)) 123962306a36Sopenharmony_ci out_val |= mask; 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci op->val = out_val; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci/* 124562306a36Sopenharmony_ci * The size parameter is used to adjust the equivalent popcnt instruction. 124662306a36Sopenharmony_ci * popcntb = 8, popcntw = 32, popcntd = 64 124762306a36Sopenharmony_ci */ 124862306a36Sopenharmony_cistatic nokprobe_inline void do_popcnt(const struct pt_regs *regs, 124962306a36Sopenharmony_ci struct instruction_op *op, 125062306a36Sopenharmony_ci unsigned long v1, int size) 125162306a36Sopenharmony_ci{ 125262306a36Sopenharmony_ci unsigned long long out = v1; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci out -= (out >> 1) & 0x5555555555555555ULL; 125562306a36Sopenharmony_ci out = (0x3333333333333333ULL & out) + 125662306a36Sopenharmony_ci (0x3333333333333333ULL & (out >> 2)); 125762306a36Sopenharmony_ci out = (out + (out >> 4)) & 0x0f0f0f0f0f0f0f0fULL; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if (size == 8) { /* popcntb */ 126062306a36Sopenharmony_ci op->val = out; 126162306a36Sopenharmony_ci return; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci out += out >> 8; 126462306a36Sopenharmony_ci out += out >> 16; 126562306a36Sopenharmony_ci if (size == 32) { /* popcntw */ 126662306a36Sopenharmony_ci op->val = out & 0x0000003f0000003fULL; 126762306a36Sopenharmony_ci return; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci out = (out + (out >> 32)) & 0x7f; 127162306a36Sopenharmony_ci op->val = out; /* popcntd */ 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci#ifdef CONFIG_PPC64 127562306a36Sopenharmony_cistatic nokprobe_inline void do_bpermd(const struct pt_regs *regs, 127662306a36Sopenharmony_ci struct instruction_op *op, 127762306a36Sopenharmony_ci unsigned long v1, unsigned long v2) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci unsigned char perm, idx; 128062306a36Sopenharmony_ci unsigned int i; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci perm = 0; 128362306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 128462306a36Sopenharmony_ci idx = (v1 >> (i * 8)) & 0xff; 128562306a36Sopenharmony_ci if (idx < 64) 128662306a36Sopenharmony_ci if (v2 & PPC_BIT(idx)) 128762306a36Sopenharmony_ci perm |= 1 << i; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci op->val = perm; 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ci#endif /* CONFIG_PPC64 */ 129262306a36Sopenharmony_ci/* 129362306a36Sopenharmony_ci * The size parameter adjusts the equivalent prty instruction. 129462306a36Sopenharmony_ci * prtyw = 32, prtyd = 64 129562306a36Sopenharmony_ci */ 129662306a36Sopenharmony_cistatic nokprobe_inline void do_prty(const struct pt_regs *regs, 129762306a36Sopenharmony_ci struct instruction_op *op, 129862306a36Sopenharmony_ci unsigned long v, int size) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci unsigned long long res = v ^ (v >> 8); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci res ^= res >> 16; 130362306a36Sopenharmony_ci if (size == 32) { /* prtyw */ 130462306a36Sopenharmony_ci op->val = res & 0x0000000100000001ULL; 130562306a36Sopenharmony_ci return; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci res ^= res >> 32; 130962306a36Sopenharmony_ci op->val = res & 1; /*prtyd */ 131062306a36Sopenharmony_ci} 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_cistatic nokprobe_inline int trap_compare(long v1, long v2) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci int ret = 0; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (v1 < v2) 131762306a36Sopenharmony_ci ret |= 0x10; 131862306a36Sopenharmony_ci else if (v1 > v2) 131962306a36Sopenharmony_ci ret |= 0x08; 132062306a36Sopenharmony_ci else 132162306a36Sopenharmony_ci ret |= 0x04; 132262306a36Sopenharmony_ci if ((unsigned long)v1 < (unsigned long)v2) 132362306a36Sopenharmony_ci ret |= 0x02; 132462306a36Sopenharmony_ci else if ((unsigned long)v1 > (unsigned long)v2) 132562306a36Sopenharmony_ci ret |= 0x01; 132662306a36Sopenharmony_ci return ret; 132762306a36Sopenharmony_ci} 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci/* 133062306a36Sopenharmony_ci * Elements of 32-bit rotate and mask instructions. 133162306a36Sopenharmony_ci */ 133262306a36Sopenharmony_ci#define MASK32(mb, me) ((0xffffffffUL >> (mb)) + \ 133362306a36Sopenharmony_ci ((signed long)-0x80000000L >> (me)) + ((me) >= (mb))) 133462306a36Sopenharmony_ci#ifdef __powerpc64__ 133562306a36Sopenharmony_ci#define MASK64_L(mb) (~0UL >> (mb)) 133662306a36Sopenharmony_ci#define MASK64_R(me) ((signed long)-0x8000000000000000L >> (me)) 133762306a36Sopenharmony_ci#define MASK64(mb, me) (MASK64_L(mb) + MASK64_R(me) + ((me) >= (mb))) 133862306a36Sopenharmony_ci#define DATA32(x) (((x) & 0xffffffffUL) | (((x) & 0xffffffffUL) << 32)) 133962306a36Sopenharmony_ci#else 134062306a36Sopenharmony_ci#define DATA32(x) (x) 134162306a36Sopenharmony_ci#endif 134262306a36Sopenharmony_ci#define ROTATE(x, n) ((n) ? (((x) << (n)) | ((x) >> (8 * sizeof(long) - (n)))) : (x)) 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci/* 134562306a36Sopenharmony_ci * Decode an instruction, and return information about it in *op 134662306a36Sopenharmony_ci * without changing *regs. 134762306a36Sopenharmony_ci * Integer arithmetic and logical instructions, branches, and barrier 134862306a36Sopenharmony_ci * instructions can be emulated just using the information in *op. 134962306a36Sopenharmony_ci * 135062306a36Sopenharmony_ci * Return value is 1 if the instruction can be emulated just by 135162306a36Sopenharmony_ci * updating *regs with the information in *op, -1 if we need the 135262306a36Sopenharmony_ci * GPRs but *regs doesn't contain the full register set, or 0 135362306a36Sopenharmony_ci * otherwise. 135462306a36Sopenharmony_ci */ 135562306a36Sopenharmony_ciint analyse_instr(struct instruction_op *op, const struct pt_regs *regs, 135662306a36Sopenharmony_ci ppc_inst_t instr) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci#ifdef CONFIG_PPC64 135962306a36Sopenharmony_ci unsigned int suffixopcode, prefixtype, prefix_r; 136062306a36Sopenharmony_ci#endif 136162306a36Sopenharmony_ci unsigned int opcode, ra, rb, rc, rd, spr, u; 136262306a36Sopenharmony_ci unsigned long int imm; 136362306a36Sopenharmony_ci unsigned long int val, val2; 136462306a36Sopenharmony_ci unsigned int mb, me, sh; 136562306a36Sopenharmony_ci unsigned int word, suffix; 136662306a36Sopenharmony_ci long ival; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci word = ppc_inst_val(instr); 136962306a36Sopenharmony_ci suffix = ppc_inst_suffix(instr); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci op->type = COMPUTE; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci opcode = ppc_inst_primary_opcode(instr); 137462306a36Sopenharmony_ci switch (opcode) { 137562306a36Sopenharmony_ci case 16: /* bc */ 137662306a36Sopenharmony_ci op->type = BRANCH; 137762306a36Sopenharmony_ci imm = (signed short)(word & 0xfffc); 137862306a36Sopenharmony_ci if ((word & 2) == 0) 137962306a36Sopenharmony_ci imm += regs->nip; 138062306a36Sopenharmony_ci op->val = truncate_if_32bit(regs->msr, imm); 138162306a36Sopenharmony_ci if (word & 1) 138262306a36Sopenharmony_ci op->type |= SETLK; 138362306a36Sopenharmony_ci if (branch_taken(word, regs, op)) 138462306a36Sopenharmony_ci op->type |= BRTAKEN; 138562306a36Sopenharmony_ci return 1; 138662306a36Sopenharmony_ci case 17: /* sc */ 138762306a36Sopenharmony_ci if ((word & 0xfe2) == 2) 138862306a36Sopenharmony_ci op->type = SYSCALL; 138962306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && 139062306a36Sopenharmony_ci (word & 0xfe3) == 1) { /* scv */ 139162306a36Sopenharmony_ci op->type = SYSCALL_VECTORED_0; 139262306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 139362306a36Sopenharmony_ci goto unknown_opcode; 139462306a36Sopenharmony_ci } else 139562306a36Sopenharmony_ci op->type = UNKNOWN; 139662306a36Sopenharmony_ci return 0; 139762306a36Sopenharmony_ci case 18: /* b */ 139862306a36Sopenharmony_ci op->type = BRANCH | BRTAKEN; 139962306a36Sopenharmony_ci imm = word & 0x03fffffc; 140062306a36Sopenharmony_ci if (imm & 0x02000000) 140162306a36Sopenharmony_ci imm -= 0x04000000; 140262306a36Sopenharmony_ci if ((word & 2) == 0) 140362306a36Sopenharmony_ci imm += regs->nip; 140462306a36Sopenharmony_ci op->val = truncate_if_32bit(regs->msr, imm); 140562306a36Sopenharmony_ci if (word & 1) 140662306a36Sopenharmony_ci op->type |= SETLK; 140762306a36Sopenharmony_ci return 1; 140862306a36Sopenharmony_ci case 19: 140962306a36Sopenharmony_ci switch ((word >> 1) & 0x3ff) { 141062306a36Sopenharmony_ci case 0: /* mcrf */ 141162306a36Sopenharmony_ci op->type = COMPUTE + SETCC; 141262306a36Sopenharmony_ci rd = 7 - ((word >> 23) & 0x7); 141362306a36Sopenharmony_ci ra = 7 - ((word >> 18) & 0x7); 141462306a36Sopenharmony_ci rd *= 4; 141562306a36Sopenharmony_ci ra *= 4; 141662306a36Sopenharmony_ci val = (regs->ccr >> ra) & 0xf; 141762306a36Sopenharmony_ci op->ccval = (regs->ccr & ~(0xfUL << rd)) | (val << rd); 141862306a36Sopenharmony_ci return 1; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci case 16: /* bclr */ 142162306a36Sopenharmony_ci case 528: /* bcctr */ 142262306a36Sopenharmony_ci op->type = BRANCH; 142362306a36Sopenharmony_ci imm = (word & 0x400)? regs->ctr: regs->link; 142462306a36Sopenharmony_ci op->val = truncate_if_32bit(regs->msr, imm); 142562306a36Sopenharmony_ci if (word & 1) 142662306a36Sopenharmony_ci op->type |= SETLK; 142762306a36Sopenharmony_ci if (branch_taken(word, regs, op)) 142862306a36Sopenharmony_ci op->type |= BRTAKEN; 142962306a36Sopenharmony_ci return 1; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci case 18: /* rfid, scary */ 143262306a36Sopenharmony_ci if (regs->msr & MSR_PR) 143362306a36Sopenharmony_ci goto priv; 143462306a36Sopenharmony_ci op->type = RFI; 143562306a36Sopenharmony_ci return 0; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci case 150: /* isync */ 143862306a36Sopenharmony_ci op->type = BARRIER | BARRIER_ISYNC; 143962306a36Sopenharmony_ci return 1; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci case 33: /* crnor */ 144262306a36Sopenharmony_ci case 129: /* crandc */ 144362306a36Sopenharmony_ci case 193: /* crxor */ 144462306a36Sopenharmony_ci case 225: /* crnand */ 144562306a36Sopenharmony_ci case 257: /* crand */ 144662306a36Sopenharmony_ci case 289: /* creqv */ 144762306a36Sopenharmony_ci case 417: /* crorc */ 144862306a36Sopenharmony_ci case 449: /* cror */ 144962306a36Sopenharmony_ci op->type = COMPUTE + SETCC; 145062306a36Sopenharmony_ci ra = (word >> 16) & 0x1f; 145162306a36Sopenharmony_ci rb = (word >> 11) & 0x1f; 145262306a36Sopenharmony_ci rd = (word >> 21) & 0x1f; 145362306a36Sopenharmony_ci ra = (regs->ccr >> (31 - ra)) & 1; 145462306a36Sopenharmony_ci rb = (regs->ccr >> (31 - rb)) & 1; 145562306a36Sopenharmony_ci val = (word >> (6 + ra * 2 + rb)) & 1; 145662306a36Sopenharmony_ci op->ccval = (regs->ccr & ~(1UL << (31 - rd))) | 145762306a36Sopenharmony_ci (val << (31 - rd)); 145862306a36Sopenharmony_ci return 1; 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci break; 146162306a36Sopenharmony_ci case 31: 146262306a36Sopenharmony_ci switch ((word >> 1) & 0x3ff) { 146362306a36Sopenharmony_ci case 598: /* sync */ 146462306a36Sopenharmony_ci op->type = BARRIER + BARRIER_SYNC; 146562306a36Sopenharmony_ci#ifdef __powerpc64__ 146662306a36Sopenharmony_ci switch ((word >> 21) & 3) { 146762306a36Sopenharmony_ci case 1: /* lwsync */ 146862306a36Sopenharmony_ci op->type = BARRIER + BARRIER_LWSYNC; 146962306a36Sopenharmony_ci break; 147062306a36Sopenharmony_ci case 2: /* ptesync */ 147162306a36Sopenharmony_ci op->type = BARRIER + BARRIER_PTESYNC; 147262306a36Sopenharmony_ci break; 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci#endif 147562306a36Sopenharmony_ci return 1; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci case 854: /* eieio */ 147862306a36Sopenharmony_ci op->type = BARRIER + BARRIER_EIEIO; 147962306a36Sopenharmony_ci return 1; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci break; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci rd = (word >> 21) & 0x1f; 148562306a36Sopenharmony_ci ra = (word >> 16) & 0x1f; 148662306a36Sopenharmony_ci rb = (word >> 11) & 0x1f; 148762306a36Sopenharmony_ci rc = (word >> 6) & 0x1f; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci switch (opcode) { 149062306a36Sopenharmony_ci#ifdef __powerpc64__ 149162306a36Sopenharmony_ci case 1: 149262306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_31)) 149362306a36Sopenharmony_ci goto unknown_opcode; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci prefix_r = GET_PREFIX_R(word); 149662306a36Sopenharmony_ci ra = GET_PREFIX_RA(suffix); 149762306a36Sopenharmony_ci rd = (suffix >> 21) & 0x1f; 149862306a36Sopenharmony_ci op->reg = rd; 149962306a36Sopenharmony_ci op->val = regs->gpr[rd]; 150062306a36Sopenharmony_ci suffixopcode = get_op(suffix); 150162306a36Sopenharmony_ci prefixtype = (word >> 24) & 0x3; 150262306a36Sopenharmony_ci switch (prefixtype) { 150362306a36Sopenharmony_ci case 2: 150462306a36Sopenharmony_ci if (prefix_r && ra) 150562306a36Sopenharmony_ci return 0; 150662306a36Sopenharmony_ci switch (suffixopcode) { 150762306a36Sopenharmony_ci case 14: /* paddi */ 150862306a36Sopenharmony_ci op->type = COMPUTE | PREFIXED; 150962306a36Sopenharmony_ci op->val = mlsd_8lsd_ea(word, suffix, regs); 151062306a36Sopenharmony_ci goto compute_done; 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci break; 151462306a36Sopenharmony_ci case 2: /* tdi */ 151562306a36Sopenharmony_ci if (rd & trap_compare(regs->gpr[ra], (short) word)) 151662306a36Sopenharmony_ci goto trap; 151762306a36Sopenharmony_ci return 1; 151862306a36Sopenharmony_ci#endif 151962306a36Sopenharmony_ci case 3: /* twi */ 152062306a36Sopenharmony_ci if (rd & trap_compare((int)regs->gpr[ra], (short) word)) 152162306a36Sopenharmony_ci goto trap; 152262306a36Sopenharmony_ci return 1; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci#ifdef __powerpc64__ 152562306a36Sopenharmony_ci case 4: 152662306a36Sopenharmony_ci /* 152762306a36Sopenharmony_ci * There are very many instructions with this primary opcode 152862306a36Sopenharmony_ci * introduced in the ISA as early as v2.03. However, the ones 152962306a36Sopenharmony_ci * we currently emulate were all introduced with ISA 3.0 153062306a36Sopenharmony_ci */ 153162306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 153262306a36Sopenharmony_ci goto unknown_opcode; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci switch (word & 0x3f) { 153562306a36Sopenharmony_ci case 48: /* maddhd */ 153662306a36Sopenharmony_ci asm volatile(PPC_MADDHD(%0, %1, %2, %3) : 153762306a36Sopenharmony_ci "=r" (op->val) : "r" (regs->gpr[ra]), 153862306a36Sopenharmony_ci "r" (regs->gpr[rb]), "r" (regs->gpr[rc])); 153962306a36Sopenharmony_ci goto compute_done; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci case 49: /* maddhdu */ 154262306a36Sopenharmony_ci asm volatile(PPC_MADDHDU(%0, %1, %2, %3) : 154362306a36Sopenharmony_ci "=r" (op->val) : "r" (regs->gpr[ra]), 154462306a36Sopenharmony_ci "r" (regs->gpr[rb]), "r" (regs->gpr[rc])); 154562306a36Sopenharmony_ci goto compute_done; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci case 51: /* maddld */ 154862306a36Sopenharmony_ci asm volatile(PPC_MADDLD(%0, %1, %2, %3) : 154962306a36Sopenharmony_ci "=r" (op->val) : "r" (regs->gpr[ra]), 155062306a36Sopenharmony_ci "r" (regs->gpr[rb]), "r" (regs->gpr[rc])); 155162306a36Sopenharmony_ci goto compute_done; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci /* 155562306a36Sopenharmony_ci * There are other instructions from ISA 3.0 with the same 155662306a36Sopenharmony_ci * primary opcode which do not have emulation support yet. 155762306a36Sopenharmony_ci */ 155862306a36Sopenharmony_ci goto unknown_opcode; 155962306a36Sopenharmony_ci#endif 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci case 7: /* mulli */ 156262306a36Sopenharmony_ci op->val = regs->gpr[ra] * (short) word; 156362306a36Sopenharmony_ci goto compute_done; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci case 8: /* subfic */ 156662306a36Sopenharmony_ci imm = (short) word; 156762306a36Sopenharmony_ci add_with_carry(regs, op, rd, ~regs->gpr[ra], imm, 1); 156862306a36Sopenharmony_ci return 1; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci case 10: /* cmpli */ 157162306a36Sopenharmony_ci imm = (unsigned short) word; 157262306a36Sopenharmony_ci val = regs->gpr[ra]; 157362306a36Sopenharmony_ci#ifdef __powerpc64__ 157462306a36Sopenharmony_ci if ((rd & 1) == 0) 157562306a36Sopenharmony_ci val = (unsigned int) val; 157662306a36Sopenharmony_ci#endif 157762306a36Sopenharmony_ci do_cmp_unsigned(regs, op, val, imm, rd >> 2); 157862306a36Sopenharmony_ci return 1; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci case 11: /* cmpi */ 158162306a36Sopenharmony_ci imm = (short) word; 158262306a36Sopenharmony_ci val = regs->gpr[ra]; 158362306a36Sopenharmony_ci#ifdef __powerpc64__ 158462306a36Sopenharmony_ci if ((rd & 1) == 0) 158562306a36Sopenharmony_ci val = (int) val; 158662306a36Sopenharmony_ci#endif 158762306a36Sopenharmony_ci do_cmp_signed(regs, op, val, imm, rd >> 2); 158862306a36Sopenharmony_ci return 1; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci case 12: /* addic */ 159162306a36Sopenharmony_ci imm = (short) word; 159262306a36Sopenharmony_ci add_with_carry(regs, op, rd, regs->gpr[ra], imm, 0); 159362306a36Sopenharmony_ci return 1; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci case 13: /* addic. */ 159662306a36Sopenharmony_ci imm = (short) word; 159762306a36Sopenharmony_ci add_with_carry(regs, op, rd, regs->gpr[ra], imm, 0); 159862306a36Sopenharmony_ci set_cr0(regs, op); 159962306a36Sopenharmony_ci return 1; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci case 14: /* addi */ 160262306a36Sopenharmony_ci imm = (short) word; 160362306a36Sopenharmony_ci if (ra) 160462306a36Sopenharmony_ci imm += regs->gpr[ra]; 160562306a36Sopenharmony_ci op->val = imm; 160662306a36Sopenharmony_ci goto compute_done; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci case 15: /* addis */ 160962306a36Sopenharmony_ci imm = ((short) word) << 16; 161062306a36Sopenharmony_ci if (ra) 161162306a36Sopenharmony_ci imm += regs->gpr[ra]; 161262306a36Sopenharmony_ci op->val = imm; 161362306a36Sopenharmony_ci goto compute_done; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci case 19: 161662306a36Sopenharmony_ci if (((word >> 1) & 0x1f) == 2) { 161762306a36Sopenharmony_ci /* addpcis */ 161862306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 161962306a36Sopenharmony_ci goto unknown_opcode; 162062306a36Sopenharmony_ci imm = (short) (word & 0xffc1); /* d0 + d2 fields */ 162162306a36Sopenharmony_ci imm |= (word >> 15) & 0x3e; /* d1 field */ 162262306a36Sopenharmony_ci op->val = regs->nip + (imm << 16) + 4; 162362306a36Sopenharmony_ci goto compute_done; 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci op->type = UNKNOWN; 162662306a36Sopenharmony_ci return 0; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci case 20: /* rlwimi */ 162962306a36Sopenharmony_ci mb = (word >> 6) & 0x1f; 163062306a36Sopenharmony_ci me = (word >> 1) & 0x1f; 163162306a36Sopenharmony_ci val = DATA32(regs->gpr[rd]); 163262306a36Sopenharmony_ci imm = MASK32(mb, me); 163362306a36Sopenharmony_ci op->val = (regs->gpr[ra] & ~imm) | (ROTATE(val, rb) & imm); 163462306a36Sopenharmony_ci goto logical_done; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci case 21: /* rlwinm */ 163762306a36Sopenharmony_ci mb = (word >> 6) & 0x1f; 163862306a36Sopenharmony_ci me = (word >> 1) & 0x1f; 163962306a36Sopenharmony_ci val = DATA32(regs->gpr[rd]); 164062306a36Sopenharmony_ci op->val = ROTATE(val, rb) & MASK32(mb, me); 164162306a36Sopenharmony_ci goto logical_done; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci case 23: /* rlwnm */ 164462306a36Sopenharmony_ci mb = (word >> 6) & 0x1f; 164562306a36Sopenharmony_ci me = (word >> 1) & 0x1f; 164662306a36Sopenharmony_ci rb = regs->gpr[rb] & 0x1f; 164762306a36Sopenharmony_ci val = DATA32(regs->gpr[rd]); 164862306a36Sopenharmony_ci op->val = ROTATE(val, rb) & MASK32(mb, me); 164962306a36Sopenharmony_ci goto logical_done; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci case 24: /* ori */ 165262306a36Sopenharmony_ci op->val = regs->gpr[rd] | (unsigned short) word; 165362306a36Sopenharmony_ci goto logical_done_nocc; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci case 25: /* oris */ 165662306a36Sopenharmony_ci imm = (unsigned short) word; 165762306a36Sopenharmony_ci op->val = regs->gpr[rd] | (imm << 16); 165862306a36Sopenharmony_ci goto logical_done_nocc; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci case 26: /* xori */ 166162306a36Sopenharmony_ci op->val = regs->gpr[rd] ^ (unsigned short) word; 166262306a36Sopenharmony_ci goto logical_done_nocc; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci case 27: /* xoris */ 166562306a36Sopenharmony_ci imm = (unsigned short) word; 166662306a36Sopenharmony_ci op->val = regs->gpr[rd] ^ (imm << 16); 166762306a36Sopenharmony_ci goto logical_done_nocc; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci case 28: /* andi. */ 167062306a36Sopenharmony_ci op->val = regs->gpr[rd] & (unsigned short) word; 167162306a36Sopenharmony_ci set_cr0(regs, op); 167262306a36Sopenharmony_ci goto logical_done_nocc; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci case 29: /* andis. */ 167562306a36Sopenharmony_ci imm = (unsigned short) word; 167662306a36Sopenharmony_ci op->val = regs->gpr[rd] & (imm << 16); 167762306a36Sopenharmony_ci set_cr0(regs, op); 167862306a36Sopenharmony_ci goto logical_done_nocc; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci#ifdef __powerpc64__ 168162306a36Sopenharmony_ci case 30: /* rld* */ 168262306a36Sopenharmony_ci mb = ((word >> 6) & 0x1f) | (word & 0x20); 168362306a36Sopenharmony_ci val = regs->gpr[rd]; 168462306a36Sopenharmony_ci if ((word & 0x10) == 0) { 168562306a36Sopenharmony_ci sh = rb | ((word & 2) << 4); 168662306a36Sopenharmony_ci val = ROTATE(val, sh); 168762306a36Sopenharmony_ci switch ((word >> 2) & 3) { 168862306a36Sopenharmony_ci case 0: /* rldicl */ 168962306a36Sopenharmony_ci val &= MASK64_L(mb); 169062306a36Sopenharmony_ci break; 169162306a36Sopenharmony_ci case 1: /* rldicr */ 169262306a36Sopenharmony_ci val &= MASK64_R(mb); 169362306a36Sopenharmony_ci break; 169462306a36Sopenharmony_ci case 2: /* rldic */ 169562306a36Sopenharmony_ci val &= MASK64(mb, 63 - sh); 169662306a36Sopenharmony_ci break; 169762306a36Sopenharmony_ci case 3: /* rldimi */ 169862306a36Sopenharmony_ci imm = MASK64(mb, 63 - sh); 169962306a36Sopenharmony_ci val = (regs->gpr[ra] & ~imm) | 170062306a36Sopenharmony_ci (val & imm); 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci op->val = val; 170362306a36Sopenharmony_ci goto logical_done; 170462306a36Sopenharmony_ci } else { 170562306a36Sopenharmony_ci sh = regs->gpr[rb] & 0x3f; 170662306a36Sopenharmony_ci val = ROTATE(val, sh); 170762306a36Sopenharmony_ci switch ((word >> 1) & 7) { 170862306a36Sopenharmony_ci case 0: /* rldcl */ 170962306a36Sopenharmony_ci op->val = val & MASK64_L(mb); 171062306a36Sopenharmony_ci goto logical_done; 171162306a36Sopenharmony_ci case 1: /* rldcr */ 171262306a36Sopenharmony_ci op->val = val & MASK64_R(mb); 171362306a36Sopenharmony_ci goto logical_done; 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci#endif 171762306a36Sopenharmony_ci op->type = UNKNOWN; /* illegal instruction */ 171862306a36Sopenharmony_ci return 0; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci case 31: 172162306a36Sopenharmony_ci /* isel occupies 32 minor opcodes */ 172262306a36Sopenharmony_ci if (((word >> 1) & 0x1f) == 15) { 172362306a36Sopenharmony_ci mb = (word >> 6) & 0x1f; /* bc field */ 172462306a36Sopenharmony_ci val = (regs->ccr >> (31 - mb)) & 1; 172562306a36Sopenharmony_ci val2 = (ra) ? regs->gpr[ra] : 0; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci op->val = (val) ? val2 : regs->gpr[rb]; 172862306a36Sopenharmony_ci goto compute_done; 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci switch ((word >> 1) & 0x3ff) { 173262306a36Sopenharmony_ci case 4: /* tw */ 173362306a36Sopenharmony_ci if (rd == 0x1f || 173462306a36Sopenharmony_ci (rd & trap_compare((int)regs->gpr[ra], 173562306a36Sopenharmony_ci (int)regs->gpr[rb]))) 173662306a36Sopenharmony_ci goto trap; 173762306a36Sopenharmony_ci return 1; 173862306a36Sopenharmony_ci#ifdef __powerpc64__ 173962306a36Sopenharmony_ci case 68: /* td */ 174062306a36Sopenharmony_ci if (rd & trap_compare(regs->gpr[ra], regs->gpr[rb])) 174162306a36Sopenharmony_ci goto trap; 174262306a36Sopenharmony_ci return 1; 174362306a36Sopenharmony_ci#endif 174462306a36Sopenharmony_ci case 83: /* mfmsr */ 174562306a36Sopenharmony_ci if (regs->msr & MSR_PR) 174662306a36Sopenharmony_ci goto priv; 174762306a36Sopenharmony_ci op->type = MFMSR; 174862306a36Sopenharmony_ci op->reg = rd; 174962306a36Sopenharmony_ci return 0; 175062306a36Sopenharmony_ci case 146: /* mtmsr */ 175162306a36Sopenharmony_ci if (regs->msr & MSR_PR) 175262306a36Sopenharmony_ci goto priv; 175362306a36Sopenharmony_ci op->type = MTMSR; 175462306a36Sopenharmony_ci op->reg = rd; 175562306a36Sopenharmony_ci op->val = 0xffffffff & ~(MSR_ME | MSR_LE); 175662306a36Sopenharmony_ci return 0; 175762306a36Sopenharmony_ci#ifdef CONFIG_PPC64 175862306a36Sopenharmony_ci case 178: /* mtmsrd */ 175962306a36Sopenharmony_ci if (regs->msr & MSR_PR) 176062306a36Sopenharmony_ci goto priv; 176162306a36Sopenharmony_ci op->type = MTMSR; 176262306a36Sopenharmony_ci op->reg = rd; 176362306a36Sopenharmony_ci /* only MSR_EE and MSR_RI get changed if bit 15 set */ 176462306a36Sopenharmony_ci /* mtmsrd doesn't change MSR_HV, MSR_ME or MSR_LE */ 176562306a36Sopenharmony_ci imm = (word & 0x10000)? 0x8002: 0xefffffffffffeffeUL; 176662306a36Sopenharmony_ci op->val = imm; 176762306a36Sopenharmony_ci return 0; 176862306a36Sopenharmony_ci#endif 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci case 19: /* mfcr */ 177162306a36Sopenharmony_ci imm = 0xffffffffUL; 177262306a36Sopenharmony_ci if ((word >> 20) & 1) { 177362306a36Sopenharmony_ci imm = 0xf0000000UL; 177462306a36Sopenharmony_ci for (sh = 0; sh < 8; ++sh) { 177562306a36Sopenharmony_ci if (word & (0x80000 >> sh)) 177662306a36Sopenharmony_ci break; 177762306a36Sopenharmony_ci imm >>= 4; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci } 178062306a36Sopenharmony_ci op->val = regs->ccr & imm; 178162306a36Sopenharmony_ci goto compute_done; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci case 128: /* setb */ 178462306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 178562306a36Sopenharmony_ci goto unknown_opcode; 178662306a36Sopenharmony_ci /* 178762306a36Sopenharmony_ci * 'ra' encodes the CR field number (bfa) in the top 3 bits. 178862306a36Sopenharmony_ci * Since each CR field is 4 bits, 178962306a36Sopenharmony_ci * we can simply mask off the bottom two bits (bfa * 4) 179062306a36Sopenharmony_ci * to yield the first bit in the CR field. 179162306a36Sopenharmony_ci */ 179262306a36Sopenharmony_ci ra = ra & ~0x3; 179362306a36Sopenharmony_ci /* 'val' stores bits of the CR field (bfa) */ 179462306a36Sopenharmony_ci val = regs->ccr >> (CR0_SHIFT - ra); 179562306a36Sopenharmony_ci /* checks if the LT bit of CR field (bfa) is set */ 179662306a36Sopenharmony_ci if (val & 8) 179762306a36Sopenharmony_ci op->val = -1; 179862306a36Sopenharmony_ci /* checks if the GT bit of CR field (bfa) is set */ 179962306a36Sopenharmony_ci else if (val & 4) 180062306a36Sopenharmony_ci op->val = 1; 180162306a36Sopenharmony_ci else 180262306a36Sopenharmony_ci op->val = 0; 180362306a36Sopenharmony_ci goto compute_done; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci case 144: /* mtcrf */ 180662306a36Sopenharmony_ci op->type = COMPUTE + SETCC; 180762306a36Sopenharmony_ci imm = 0xf0000000UL; 180862306a36Sopenharmony_ci val = regs->gpr[rd]; 180962306a36Sopenharmony_ci op->ccval = regs->ccr; 181062306a36Sopenharmony_ci for (sh = 0; sh < 8; ++sh) { 181162306a36Sopenharmony_ci if (word & (0x80000 >> sh)) 181262306a36Sopenharmony_ci op->ccval = (op->ccval & ~imm) | 181362306a36Sopenharmony_ci (val & imm); 181462306a36Sopenharmony_ci imm >>= 4; 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci return 1; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci case 339: /* mfspr */ 181962306a36Sopenharmony_ci spr = ((word >> 16) & 0x1f) | ((word >> 6) & 0x3e0); 182062306a36Sopenharmony_ci op->type = MFSPR; 182162306a36Sopenharmony_ci op->reg = rd; 182262306a36Sopenharmony_ci op->spr = spr; 182362306a36Sopenharmony_ci if (spr == SPRN_XER || spr == SPRN_LR || 182462306a36Sopenharmony_ci spr == SPRN_CTR) 182562306a36Sopenharmony_ci return 1; 182662306a36Sopenharmony_ci return 0; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci case 467: /* mtspr */ 182962306a36Sopenharmony_ci spr = ((word >> 16) & 0x1f) | ((word >> 6) & 0x3e0); 183062306a36Sopenharmony_ci op->type = MTSPR; 183162306a36Sopenharmony_ci op->val = regs->gpr[rd]; 183262306a36Sopenharmony_ci op->spr = spr; 183362306a36Sopenharmony_ci if (spr == SPRN_XER || spr == SPRN_LR || 183462306a36Sopenharmony_ci spr == SPRN_CTR) 183562306a36Sopenharmony_ci return 1; 183662306a36Sopenharmony_ci return 0; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci/* 183962306a36Sopenharmony_ci * Compare instructions 184062306a36Sopenharmony_ci */ 184162306a36Sopenharmony_ci case 0: /* cmp */ 184262306a36Sopenharmony_ci val = regs->gpr[ra]; 184362306a36Sopenharmony_ci val2 = regs->gpr[rb]; 184462306a36Sopenharmony_ci#ifdef __powerpc64__ 184562306a36Sopenharmony_ci if ((rd & 1) == 0) { 184662306a36Sopenharmony_ci /* word (32-bit) compare */ 184762306a36Sopenharmony_ci val = (int) val; 184862306a36Sopenharmony_ci val2 = (int) val2; 184962306a36Sopenharmony_ci } 185062306a36Sopenharmony_ci#endif 185162306a36Sopenharmony_ci do_cmp_signed(regs, op, val, val2, rd >> 2); 185262306a36Sopenharmony_ci return 1; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci case 32: /* cmpl */ 185562306a36Sopenharmony_ci val = regs->gpr[ra]; 185662306a36Sopenharmony_ci val2 = regs->gpr[rb]; 185762306a36Sopenharmony_ci#ifdef __powerpc64__ 185862306a36Sopenharmony_ci if ((rd & 1) == 0) { 185962306a36Sopenharmony_ci /* word (32-bit) compare */ 186062306a36Sopenharmony_ci val = (unsigned int) val; 186162306a36Sopenharmony_ci val2 = (unsigned int) val2; 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci#endif 186462306a36Sopenharmony_ci do_cmp_unsigned(regs, op, val, val2, rd >> 2); 186562306a36Sopenharmony_ci return 1; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci case 508: /* cmpb */ 186862306a36Sopenharmony_ci do_cmpb(regs, op, regs->gpr[rd], regs->gpr[rb]); 186962306a36Sopenharmony_ci goto logical_done_nocc; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci/* 187262306a36Sopenharmony_ci * Arithmetic instructions 187362306a36Sopenharmony_ci */ 187462306a36Sopenharmony_ci case 8: /* subfc */ 187562306a36Sopenharmony_ci add_with_carry(regs, op, rd, ~regs->gpr[ra], 187662306a36Sopenharmony_ci regs->gpr[rb], 1); 187762306a36Sopenharmony_ci goto arith_done; 187862306a36Sopenharmony_ci#ifdef __powerpc64__ 187962306a36Sopenharmony_ci case 9: /* mulhdu */ 188062306a36Sopenharmony_ci asm("mulhdu %0,%1,%2" : "=r" (op->val) : 188162306a36Sopenharmony_ci "r" (regs->gpr[ra]), "r" (regs->gpr[rb])); 188262306a36Sopenharmony_ci goto arith_done; 188362306a36Sopenharmony_ci#endif 188462306a36Sopenharmony_ci case 10: /* addc */ 188562306a36Sopenharmony_ci add_with_carry(regs, op, rd, regs->gpr[ra], 188662306a36Sopenharmony_ci regs->gpr[rb], 0); 188762306a36Sopenharmony_ci goto arith_done; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci case 11: /* mulhwu */ 189062306a36Sopenharmony_ci asm("mulhwu %0,%1,%2" : "=r" (op->val) : 189162306a36Sopenharmony_ci "r" (regs->gpr[ra]), "r" (regs->gpr[rb])); 189262306a36Sopenharmony_ci goto arith_done; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci case 40: /* subf */ 189562306a36Sopenharmony_ci op->val = regs->gpr[rb] - regs->gpr[ra]; 189662306a36Sopenharmony_ci goto arith_done; 189762306a36Sopenharmony_ci#ifdef __powerpc64__ 189862306a36Sopenharmony_ci case 73: /* mulhd */ 189962306a36Sopenharmony_ci asm("mulhd %0,%1,%2" : "=r" (op->val) : 190062306a36Sopenharmony_ci "r" (regs->gpr[ra]), "r" (regs->gpr[rb])); 190162306a36Sopenharmony_ci goto arith_done; 190262306a36Sopenharmony_ci#endif 190362306a36Sopenharmony_ci case 75: /* mulhw */ 190462306a36Sopenharmony_ci asm("mulhw %0,%1,%2" : "=r" (op->val) : 190562306a36Sopenharmony_ci "r" (regs->gpr[ra]), "r" (regs->gpr[rb])); 190662306a36Sopenharmony_ci goto arith_done; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci case 104: /* neg */ 190962306a36Sopenharmony_ci op->val = -regs->gpr[ra]; 191062306a36Sopenharmony_ci goto arith_done; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci case 136: /* subfe */ 191362306a36Sopenharmony_ci add_with_carry(regs, op, rd, ~regs->gpr[ra], 191462306a36Sopenharmony_ci regs->gpr[rb], regs->xer & XER_CA); 191562306a36Sopenharmony_ci goto arith_done; 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci case 138: /* adde */ 191862306a36Sopenharmony_ci add_with_carry(regs, op, rd, regs->gpr[ra], 191962306a36Sopenharmony_ci regs->gpr[rb], regs->xer & XER_CA); 192062306a36Sopenharmony_ci goto arith_done; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci case 200: /* subfze */ 192362306a36Sopenharmony_ci add_with_carry(regs, op, rd, ~regs->gpr[ra], 0L, 192462306a36Sopenharmony_ci regs->xer & XER_CA); 192562306a36Sopenharmony_ci goto arith_done; 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci case 202: /* addze */ 192862306a36Sopenharmony_ci add_with_carry(regs, op, rd, regs->gpr[ra], 0L, 192962306a36Sopenharmony_ci regs->xer & XER_CA); 193062306a36Sopenharmony_ci goto arith_done; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci case 232: /* subfme */ 193362306a36Sopenharmony_ci add_with_carry(regs, op, rd, ~regs->gpr[ra], -1L, 193462306a36Sopenharmony_ci regs->xer & XER_CA); 193562306a36Sopenharmony_ci goto arith_done; 193662306a36Sopenharmony_ci#ifdef __powerpc64__ 193762306a36Sopenharmony_ci case 233: /* mulld */ 193862306a36Sopenharmony_ci op->val = regs->gpr[ra] * regs->gpr[rb]; 193962306a36Sopenharmony_ci goto arith_done; 194062306a36Sopenharmony_ci#endif 194162306a36Sopenharmony_ci case 234: /* addme */ 194262306a36Sopenharmony_ci add_with_carry(regs, op, rd, regs->gpr[ra], -1L, 194362306a36Sopenharmony_ci regs->xer & XER_CA); 194462306a36Sopenharmony_ci goto arith_done; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci case 235: /* mullw */ 194762306a36Sopenharmony_ci op->val = (long)(int) regs->gpr[ra] * 194862306a36Sopenharmony_ci (int) regs->gpr[rb]; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci goto arith_done; 195162306a36Sopenharmony_ci#ifdef __powerpc64__ 195262306a36Sopenharmony_ci case 265: /* modud */ 195362306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 195462306a36Sopenharmony_ci goto unknown_opcode; 195562306a36Sopenharmony_ci op->val = regs->gpr[ra] % regs->gpr[rb]; 195662306a36Sopenharmony_ci goto compute_done; 195762306a36Sopenharmony_ci#endif 195862306a36Sopenharmony_ci case 266: /* add */ 195962306a36Sopenharmony_ci op->val = regs->gpr[ra] + regs->gpr[rb]; 196062306a36Sopenharmony_ci goto arith_done; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci case 267: /* moduw */ 196362306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 196462306a36Sopenharmony_ci goto unknown_opcode; 196562306a36Sopenharmony_ci op->val = (unsigned int) regs->gpr[ra] % 196662306a36Sopenharmony_ci (unsigned int) regs->gpr[rb]; 196762306a36Sopenharmony_ci goto compute_done; 196862306a36Sopenharmony_ci#ifdef __powerpc64__ 196962306a36Sopenharmony_ci case 457: /* divdu */ 197062306a36Sopenharmony_ci op->val = regs->gpr[ra] / regs->gpr[rb]; 197162306a36Sopenharmony_ci goto arith_done; 197262306a36Sopenharmony_ci#endif 197362306a36Sopenharmony_ci case 459: /* divwu */ 197462306a36Sopenharmony_ci op->val = (unsigned int) regs->gpr[ra] / 197562306a36Sopenharmony_ci (unsigned int) regs->gpr[rb]; 197662306a36Sopenharmony_ci goto arith_done; 197762306a36Sopenharmony_ci#ifdef __powerpc64__ 197862306a36Sopenharmony_ci case 489: /* divd */ 197962306a36Sopenharmony_ci op->val = (long int) regs->gpr[ra] / 198062306a36Sopenharmony_ci (long int) regs->gpr[rb]; 198162306a36Sopenharmony_ci goto arith_done; 198262306a36Sopenharmony_ci#endif 198362306a36Sopenharmony_ci case 491: /* divw */ 198462306a36Sopenharmony_ci op->val = (int) regs->gpr[ra] / 198562306a36Sopenharmony_ci (int) regs->gpr[rb]; 198662306a36Sopenharmony_ci goto arith_done; 198762306a36Sopenharmony_ci#ifdef __powerpc64__ 198862306a36Sopenharmony_ci case 425: /* divde[.] */ 198962306a36Sopenharmony_ci asm volatile(PPC_DIVDE(%0, %1, %2) : 199062306a36Sopenharmony_ci "=r" (op->val) : "r" (regs->gpr[ra]), 199162306a36Sopenharmony_ci "r" (regs->gpr[rb])); 199262306a36Sopenharmony_ci goto arith_done; 199362306a36Sopenharmony_ci case 393: /* divdeu[.] */ 199462306a36Sopenharmony_ci asm volatile(PPC_DIVDEU(%0, %1, %2) : 199562306a36Sopenharmony_ci "=r" (op->val) : "r" (regs->gpr[ra]), 199662306a36Sopenharmony_ci "r" (regs->gpr[rb])); 199762306a36Sopenharmony_ci goto arith_done; 199862306a36Sopenharmony_ci#endif 199962306a36Sopenharmony_ci case 755: /* darn */ 200062306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 200162306a36Sopenharmony_ci goto unknown_opcode; 200262306a36Sopenharmony_ci switch (ra & 0x3) { 200362306a36Sopenharmony_ci case 0: 200462306a36Sopenharmony_ci /* 32-bit conditioned */ 200562306a36Sopenharmony_ci asm volatile(PPC_DARN(%0, 0) : "=r" (op->val)); 200662306a36Sopenharmony_ci goto compute_done; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci case 1: 200962306a36Sopenharmony_ci /* 64-bit conditioned */ 201062306a36Sopenharmony_ci asm volatile(PPC_DARN(%0, 1) : "=r" (op->val)); 201162306a36Sopenharmony_ci goto compute_done; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci case 2: 201462306a36Sopenharmony_ci /* 64-bit raw */ 201562306a36Sopenharmony_ci asm volatile(PPC_DARN(%0, 2) : "=r" (op->val)); 201662306a36Sopenharmony_ci goto compute_done; 201762306a36Sopenharmony_ci } 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci goto unknown_opcode; 202062306a36Sopenharmony_ci#ifdef __powerpc64__ 202162306a36Sopenharmony_ci case 777: /* modsd */ 202262306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 202362306a36Sopenharmony_ci goto unknown_opcode; 202462306a36Sopenharmony_ci op->val = (long int) regs->gpr[ra] % 202562306a36Sopenharmony_ci (long int) regs->gpr[rb]; 202662306a36Sopenharmony_ci goto compute_done; 202762306a36Sopenharmony_ci#endif 202862306a36Sopenharmony_ci case 779: /* modsw */ 202962306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 203062306a36Sopenharmony_ci goto unknown_opcode; 203162306a36Sopenharmony_ci op->val = (int) regs->gpr[ra] % 203262306a36Sopenharmony_ci (int) regs->gpr[rb]; 203362306a36Sopenharmony_ci goto compute_done; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci/* 203762306a36Sopenharmony_ci * Logical instructions 203862306a36Sopenharmony_ci */ 203962306a36Sopenharmony_ci case 26: /* cntlzw */ 204062306a36Sopenharmony_ci val = (unsigned int) regs->gpr[rd]; 204162306a36Sopenharmony_ci op->val = ( val ? __builtin_clz(val) : 32 ); 204262306a36Sopenharmony_ci goto logical_done; 204362306a36Sopenharmony_ci#ifdef __powerpc64__ 204462306a36Sopenharmony_ci case 58: /* cntlzd */ 204562306a36Sopenharmony_ci val = regs->gpr[rd]; 204662306a36Sopenharmony_ci op->val = ( val ? __builtin_clzl(val) : 64 ); 204762306a36Sopenharmony_ci goto logical_done; 204862306a36Sopenharmony_ci#endif 204962306a36Sopenharmony_ci case 28: /* and */ 205062306a36Sopenharmony_ci op->val = regs->gpr[rd] & regs->gpr[rb]; 205162306a36Sopenharmony_ci goto logical_done; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci case 60: /* andc */ 205462306a36Sopenharmony_ci op->val = regs->gpr[rd] & ~regs->gpr[rb]; 205562306a36Sopenharmony_ci goto logical_done; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci case 122: /* popcntb */ 205862306a36Sopenharmony_ci do_popcnt(regs, op, regs->gpr[rd], 8); 205962306a36Sopenharmony_ci goto logical_done_nocc; 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci case 124: /* nor */ 206262306a36Sopenharmony_ci op->val = ~(regs->gpr[rd] | regs->gpr[rb]); 206362306a36Sopenharmony_ci goto logical_done; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci case 154: /* prtyw */ 206662306a36Sopenharmony_ci do_prty(regs, op, regs->gpr[rd], 32); 206762306a36Sopenharmony_ci goto logical_done_nocc; 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci case 186: /* prtyd */ 207062306a36Sopenharmony_ci do_prty(regs, op, regs->gpr[rd], 64); 207162306a36Sopenharmony_ci goto logical_done_nocc; 207262306a36Sopenharmony_ci#ifdef CONFIG_PPC64 207362306a36Sopenharmony_ci case 252: /* bpermd */ 207462306a36Sopenharmony_ci do_bpermd(regs, op, regs->gpr[rd], regs->gpr[rb]); 207562306a36Sopenharmony_ci goto logical_done_nocc; 207662306a36Sopenharmony_ci#endif 207762306a36Sopenharmony_ci case 284: /* xor */ 207862306a36Sopenharmony_ci op->val = ~(regs->gpr[rd] ^ regs->gpr[rb]); 207962306a36Sopenharmony_ci goto logical_done; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci case 316: /* xor */ 208262306a36Sopenharmony_ci op->val = regs->gpr[rd] ^ regs->gpr[rb]; 208362306a36Sopenharmony_ci goto logical_done; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci case 378: /* popcntw */ 208662306a36Sopenharmony_ci do_popcnt(regs, op, regs->gpr[rd], 32); 208762306a36Sopenharmony_ci goto logical_done_nocc; 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci case 412: /* orc */ 209062306a36Sopenharmony_ci op->val = regs->gpr[rd] | ~regs->gpr[rb]; 209162306a36Sopenharmony_ci goto logical_done; 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci case 444: /* or */ 209462306a36Sopenharmony_ci op->val = regs->gpr[rd] | regs->gpr[rb]; 209562306a36Sopenharmony_ci goto logical_done; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci case 476: /* nand */ 209862306a36Sopenharmony_ci op->val = ~(regs->gpr[rd] & regs->gpr[rb]); 209962306a36Sopenharmony_ci goto logical_done; 210062306a36Sopenharmony_ci#ifdef CONFIG_PPC64 210162306a36Sopenharmony_ci case 506: /* popcntd */ 210262306a36Sopenharmony_ci do_popcnt(regs, op, regs->gpr[rd], 64); 210362306a36Sopenharmony_ci goto logical_done_nocc; 210462306a36Sopenharmony_ci#endif 210562306a36Sopenharmony_ci case 538: /* cnttzw */ 210662306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 210762306a36Sopenharmony_ci goto unknown_opcode; 210862306a36Sopenharmony_ci val = (unsigned int) regs->gpr[rd]; 210962306a36Sopenharmony_ci op->val = (val ? __builtin_ctz(val) : 32); 211062306a36Sopenharmony_ci goto logical_done; 211162306a36Sopenharmony_ci#ifdef __powerpc64__ 211262306a36Sopenharmony_ci case 570: /* cnttzd */ 211362306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 211462306a36Sopenharmony_ci goto unknown_opcode; 211562306a36Sopenharmony_ci val = regs->gpr[rd]; 211662306a36Sopenharmony_ci op->val = (val ? __builtin_ctzl(val) : 64); 211762306a36Sopenharmony_ci goto logical_done; 211862306a36Sopenharmony_ci#endif 211962306a36Sopenharmony_ci case 922: /* extsh */ 212062306a36Sopenharmony_ci op->val = (signed short) regs->gpr[rd]; 212162306a36Sopenharmony_ci goto logical_done; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci case 954: /* extsb */ 212462306a36Sopenharmony_ci op->val = (signed char) regs->gpr[rd]; 212562306a36Sopenharmony_ci goto logical_done; 212662306a36Sopenharmony_ci#ifdef __powerpc64__ 212762306a36Sopenharmony_ci case 986: /* extsw */ 212862306a36Sopenharmony_ci op->val = (signed int) regs->gpr[rd]; 212962306a36Sopenharmony_ci goto logical_done; 213062306a36Sopenharmony_ci#endif 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci/* 213362306a36Sopenharmony_ci * Shift instructions 213462306a36Sopenharmony_ci */ 213562306a36Sopenharmony_ci case 24: /* slw */ 213662306a36Sopenharmony_ci sh = regs->gpr[rb] & 0x3f; 213762306a36Sopenharmony_ci if (sh < 32) 213862306a36Sopenharmony_ci op->val = (regs->gpr[rd] << sh) & 0xffffffffUL; 213962306a36Sopenharmony_ci else 214062306a36Sopenharmony_ci op->val = 0; 214162306a36Sopenharmony_ci goto logical_done; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci case 536: /* srw */ 214462306a36Sopenharmony_ci sh = regs->gpr[rb] & 0x3f; 214562306a36Sopenharmony_ci if (sh < 32) 214662306a36Sopenharmony_ci op->val = (regs->gpr[rd] & 0xffffffffUL) >> sh; 214762306a36Sopenharmony_ci else 214862306a36Sopenharmony_ci op->val = 0; 214962306a36Sopenharmony_ci goto logical_done; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci case 792: /* sraw */ 215262306a36Sopenharmony_ci op->type = COMPUTE + SETREG + SETXER; 215362306a36Sopenharmony_ci sh = regs->gpr[rb] & 0x3f; 215462306a36Sopenharmony_ci ival = (signed int) regs->gpr[rd]; 215562306a36Sopenharmony_ci op->val = ival >> (sh < 32 ? sh : 31); 215662306a36Sopenharmony_ci op->xerval = regs->xer; 215762306a36Sopenharmony_ci if (ival < 0 && (sh >= 32 || (ival & ((1ul << sh) - 1)) != 0)) 215862306a36Sopenharmony_ci op->xerval |= XER_CA; 215962306a36Sopenharmony_ci else 216062306a36Sopenharmony_ci op->xerval &= ~XER_CA; 216162306a36Sopenharmony_ci set_ca32(op, op->xerval & XER_CA); 216262306a36Sopenharmony_ci goto logical_done; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci case 824: /* srawi */ 216562306a36Sopenharmony_ci op->type = COMPUTE + SETREG + SETXER; 216662306a36Sopenharmony_ci sh = rb; 216762306a36Sopenharmony_ci ival = (signed int) regs->gpr[rd]; 216862306a36Sopenharmony_ci op->val = ival >> sh; 216962306a36Sopenharmony_ci op->xerval = regs->xer; 217062306a36Sopenharmony_ci if (ival < 0 && (ival & ((1ul << sh) - 1)) != 0) 217162306a36Sopenharmony_ci op->xerval |= XER_CA; 217262306a36Sopenharmony_ci else 217362306a36Sopenharmony_ci op->xerval &= ~XER_CA; 217462306a36Sopenharmony_ci set_ca32(op, op->xerval & XER_CA); 217562306a36Sopenharmony_ci goto logical_done; 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci#ifdef __powerpc64__ 217862306a36Sopenharmony_ci case 27: /* sld */ 217962306a36Sopenharmony_ci sh = regs->gpr[rb] & 0x7f; 218062306a36Sopenharmony_ci if (sh < 64) 218162306a36Sopenharmony_ci op->val = regs->gpr[rd] << sh; 218262306a36Sopenharmony_ci else 218362306a36Sopenharmony_ci op->val = 0; 218462306a36Sopenharmony_ci goto logical_done; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci case 539: /* srd */ 218762306a36Sopenharmony_ci sh = regs->gpr[rb] & 0x7f; 218862306a36Sopenharmony_ci if (sh < 64) 218962306a36Sopenharmony_ci op->val = regs->gpr[rd] >> sh; 219062306a36Sopenharmony_ci else 219162306a36Sopenharmony_ci op->val = 0; 219262306a36Sopenharmony_ci goto logical_done; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci case 794: /* srad */ 219562306a36Sopenharmony_ci op->type = COMPUTE + SETREG + SETXER; 219662306a36Sopenharmony_ci sh = regs->gpr[rb] & 0x7f; 219762306a36Sopenharmony_ci ival = (signed long int) regs->gpr[rd]; 219862306a36Sopenharmony_ci op->val = ival >> (sh < 64 ? sh : 63); 219962306a36Sopenharmony_ci op->xerval = regs->xer; 220062306a36Sopenharmony_ci if (ival < 0 && (sh >= 64 || (ival & ((1ul << sh) - 1)) != 0)) 220162306a36Sopenharmony_ci op->xerval |= XER_CA; 220262306a36Sopenharmony_ci else 220362306a36Sopenharmony_ci op->xerval &= ~XER_CA; 220462306a36Sopenharmony_ci set_ca32(op, op->xerval & XER_CA); 220562306a36Sopenharmony_ci goto logical_done; 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci case 826: /* sradi with sh_5 = 0 */ 220862306a36Sopenharmony_ci case 827: /* sradi with sh_5 = 1 */ 220962306a36Sopenharmony_ci op->type = COMPUTE + SETREG + SETXER; 221062306a36Sopenharmony_ci sh = rb | ((word & 2) << 4); 221162306a36Sopenharmony_ci ival = (signed long int) regs->gpr[rd]; 221262306a36Sopenharmony_ci op->val = ival >> sh; 221362306a36Sopenharmony_ci op->xerval = regs->xer; 221462306a36Sopenharmony_ci if (ival < 0 && (ival & ((1ul << sh) - 1)) != 0) 221562306a36Sopenharmony_ci op->xerval |= XER_CA; 221662306a36Sopenharmony_ci else 221762306a36Sopenharmony_ci op->xerval &= ~XER_CA; 221862306a36Sopenharmony_ci set_ca32(op, op->xerval & XER_CA); 221962306a36Sopenharmony_ci goto logical_done; 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci case 890: /* extswsli with sh_5 = 0 */ 222262306a36Sopenharmony_ci case 891: /* extswsli with sh_5 = 1 */ 222362306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 222462306a36Sopenharmony_ci goto unknown_opcode; 222562306a36Sopenharmony_ci op->type = COMPUTE + SETREG; 222662306a36Sopenharmony_ci sh = rb | ((word & 2) << 4); 222762306a36Sopenharmony_ci val = (signed int) regs->gpr[rd]; 222862306a36Sopenharmony_ci if (sh) 222962306a36Sopenharmony_ci op->val = ROTATE(val, sh) & MASK64(0, 63 - sh); 223062306a36Sopenharmony_ci else 223162306a36Sopenharmony_ci op->val = val; 223262306a36Sopenharmony_ci goto logical_done; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci#endif /* __powerpc64__ */ 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci/* 223762306a36Sopenharmony_ci * Cache instructions 223862306a36Sopenharmony_ci */ 223962306a36Sopenharmony_ci case 54: /* dcbst */ 224062306a36Sopenharmony_ci op->type = MKOP(CACHEOP, DCBST, 0); 224162306a36Sopenharmony_ci op->ea = xform_ea(word, regs); 224262306a36Sopenharmony_ci return 0; 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci case 86: /* dcbf */ 224562306a36Sopenharmony_ci op->type = MKOP(CACHEOP, DCBF, 0); 224662306a36Sopenharmony_ci op->ea = xform_ea(word, regs); 224762306a36Sopenharmony_ci return 0; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci case 246: /* dcbtst */ 225062306a36Sopenharmony_ci op->type = MKOP(CACHEOP, DCBTST, 0); 225162306a36Sopenharmony_ci op->ea = xform_ea(word, regs); 225262306a36Sopenharmony_ci op->reg = rd; 225362306a36Sopenharmony_ci return 0; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci case 278: /* dcbt */ 225662306a36Sopenharmony_ci op->type = MKOP(CACHEOP, DCBTST, 0); 225762306a36Sopenharmony_ci op->ea = xform_ea(word, regs); 225862306a36Sopenharmony_ci op->reg = rd; 225962306a36Sopenharmony_ci return 0; 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci case 982: /* icbi */ 226262306a36Sopenharmony_ci op->type = MKOP(CACHEOP, ICBI, 0); 226362306a36Sopenharmony_ci op->ea = xform_ea(word, regs); 226462306a36Sopenharmony_ci return 0; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci case 1014: /* dcbz */ 226762306a36Sopenharmony_ci op->type = MKOP(CACHEOP, DCBZ, 0); 226862306a36Sopenharmony_ci op->ea = xform_ea(word, regs); 226962306a36Sopenharmony_ci return 0; 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci break; 227262306a36Sopenharmony_ci } 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci/* 227562306a36Sopenharmony_ci * Loads and stores. 227662306a36Sopenharmony_ci */ 227762306a36Sopenharmony_ci op->type = UNKNOWN; 227862306a36Sopenharmony_ci op->update_reg = ra; 227962306a36Sopenharmony_ci op->reg = rd; 228062306a36Sopenharmony_ci op->val = regs->gpr[rd]; 228162306a36Sopenharmony_ci u = (word >> 20) & UPDATE; 228262306a36Sopenharmony_ci op->vsx_flags = 0; 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci switch (opcode) { 228562306a36Sopenharmony_ci case 31: 228662306a36Sopenharmony_ci u = word & UPDATE; 228762306a36Sopenharmony_ci op->ea = xform_ea(word, regs); 228862306a36Sopenharmony_ci switch ((word >> 1) & 0x3ff) { 228962306a36Sopenharmony_ci case 20: /* lwarx */ 229062306a36Sopenharmony_ci op->type = MKOP(LARX, 0, 4); 229162306a36Sopenharmony_ci break; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci case 150: /* stwcx. */ 229462306a36Sopenharmony_ci op->type = MKOP(STCX, 0, 4); 229562306a36Sopenharmony_ci break; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci#ifdef CONFIG_PPC_HAS_LBARX_LHARX 229862306a36Sopenharmony_ci case 52: /* lbarx */ 229962306a36Sopenharmony_ci op->type = MKOP(LARX, 0, 1); 230062306a36Sopenharmony_ci break; 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci case 694: /* stbcx. */ 230362306a36Sopenharmony_ci op->type = MKOP(STCX, 0, 1); 230462306a36Sopenharmony_ci break; 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci case 116: /* lharx */ 230762306a36Sopenharmony_ci op->type = MKOP(LARX, 0, 2); 230862306a36Sopenharmony_ci break; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci case 726: /* sthcx. */ 231162306a36Sopenharmony_ci op->type = MKOP(STCX, 0, 2); 231262306a36Sopenharmony_ci break; 231362306a36Sopenharmony_ci#endif 231462306a36Sopenharmony_ci#ifdef __powerpc64__ 231562306a36Sopenharmony_ci case 84: /* ldarx */ 231662306a36Sopenharmony_ci op->type = MKOP(LARX, 0, 8); 231762306a36Sopenharmony_ci break; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci case 214: /* stdcx. */ 232062306a36Sopenharmony_ci op->type = MKOP(STCX, 0, 8); 232162306a36Sopenharmony_ci break; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci case 276: /* lqarx */ 232462306a36Sopenharmony_ci if (!((rd & 1) || rd == ra || rd == rb)) 232562306a36Sopenharmony_ci op->type = MKOP(LARX, 0, 16); 232662306a36Sopenharmony_ci break; 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci case 182: /* stqcx. */ 232962306a36Sopenharmony_ci if (!(rd & 1)) 233062306a36Sopenharmony_ci op->type = MKOP(STCX, 0, 16); 233162306a36Sopenharmony_ci break; 233262306a36Sopenharmony_ci#endif 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci case 23: /* lwzx */ 233562306a36Sopenharmony_ci case 55: /* lwzux */ 233662306a36Sopenharmony_ci op->type = MKOP(LOAD, u, 4); 233762306a36Sopenharmony_ci break; 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci case 87: /* lbzx */ 234062306a36Sopenharmony_ci case 119: /* lbzux */ 234162306a36Sopenharmony_ci op->type = MKOP(LOAD, u, 1); 234262306a36Sopenharmony_ci break; 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci#ifdef CONFIG_ALTIVEC 234562306a36Sopenharmony_ci /* 234662306a36Sopenharmony_ci * Note: for the load/store vector element instructions, 234762306a36Sopenharmony_ci * bits of the EA say which field of the VMX register to use. 234862306a36Sopenharmony_ci */ 234962306a36Sopenharmony_ci case 7: /* lvebx */ 235062306a36Sopenharmony_ci op->type = MKOP(LOAD_VMX, 0, 1); 235162306a36Sopenharmony_ci op->element_size = 1; 235262306a36Sopenharmony_ci break; 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci case 39: /* lvehx */ 235562306a36Sopenharmony_ci op->type = MKOP(LOAD_VMX, 0, 2); 235662306a36Sopenharmony_ci op->element_size = 2; 235762306a36Sopenharmony_ci break; 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci case 71: /* lvewx */ 236062306a36Sopenharmony_ci op->type = MKOP(LOAD_VMX, 0, 4); 236162306a36Sopenharmony_ci op->element_size = 4; 236262306a36Sopenharmony_ci break; 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci case 103: /* lvx */ 236562306a36Sopenharmony_ci case 359: /* lvxl */ 236662306a36Sopenharmony_ci op->type = MKOP(LOAD_VMX, 0, 16); 236762306a36Sopenharmony_ci op->element_size = 16; 236862306a36Sopenharmony_ci break; 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci case 135: /* stvebx */ 237162306a36Sopenharmony_ci op->type = MKOP(STORE_VMX, 0, 1); 237262306a36Sopenharmony_ci op->element_size = 1; 237362306a36Sopenharmony_ci break; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci case 167: /* stvehx */ 237662306a36Sopenharmony_ci op->type = MKOP(STORE_VMX, 0, 2); 237762306a36Sopenharmony_ci op->element_size = 2; 237862306a36Sopenharmony_ci break; 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci case 199: /* stvewx */ 238162306a36Sopenharmony_ci op->type = MKOP(STORE_VMX, 0, 4); 238262306a36Sopenharmony_ci op->element_size = 4; 238362306a36Sopenharmony_ci break; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci case 231: /* stvx */ 238662306a36Sopenharmony_ci case 487: /* stvxl */ 238762306a36Sopenharmony_ci op->type = MKOP(STORE_VMX, 0, 16); 238862306a36Sopenharmony_ci break; 238962306a36Sopenharmony_ci#endif /* CONFIG_ALTIVEC */ 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci#ifdef __powerpc64__ 239262306a36Sopenharmony_ci case 21: /* ldx */ 239362306a36Sopenharmony_ci case 53: /* ldux */ 239462306a36Sopenharmony_ci op->type = MKOP(LOAD, u, 8); 239562306a36Sopenharmony_ci break; 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci case 149: /* stdx */ 239862306a36Sopenharmony_ci case 181: /* stdux */ 239962306a36Sopenharmony_ci op->type = MKOP(STORE, u, 8); 240062306a36Sopenharmony_ci break; 240162306a36Sopenharmony_ci#endif 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci case 151: /* stwx */ 240462306a36Sopenharmony_ci case 183: /* stwux */ 240562306a36Sopenharmony_ci op->type = MKOP(STORE, u, 4); 240662306a36Sopenharmony_ci break; 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci case 215: /* stbx */ 240962306a36Sopenharmony_ci case 247: /* stbux */ 241062306a36Sopenharmony_ci op->type = MKOP(STORE, u, 1); 241162306a36Sopenharmony_ci break; 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci case 279: /* lhzx */ 241462306a36Sopenharmony_ci case 311: /* lhzux */ 241562306a36Sopenharmony_ci op->type = MKOP(LOAD, u, 2); 241662306a36Sopenharmony_ci break; 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci#ifdef __powerpc64__ 241962306a36Sopenharmony_ci case 341: /* lwax */ 242062306a36Sopenharmony_ci case 373: /* lwaux */ 242162306a36Sopenharmony_ci op->type = MKOP(LOAD, SIGNEXT | u, 4); 242262306a36Sopenharmony_ci break; 242362306a36Sopenharmony_ci#endif 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci case 343: /* lhax */ 242662306a36Sopenharmony_ci case 375: /* lhaux */ 242762306a36Sopenharmony_ci op->type = MKOP(LOAD, SIGNEXT | u, 2); 242862306a36Sopenharmony_ci break; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci case 407: /* sthx */ 243162306a36Sopenharmony_ci case 439: /* sthux */ 243262306a36Sopenharmony_ci op->type = MKOP(STORE, u, 2); 243362306a36Sopenharmony_ci break; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci#ifdef __powerpc64__ 243662306a36Sopenharmony_ci case 532: /* ldbrx */ 243762306a36Sopenharmony_ci op->type = MKOP(LOAD, BYTEREV, 8); 243862306a36Sopenharmony_ci break; 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci#endif 244162306a36Sopenharmony_ci case 533: /* lswx */ 244262306a36Sopenharmony_ci op->type = MKOP(LOAD_MULTI, 0, regs->xer & 0x7f); 244362306a36Sopenharmony_ci break; 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci case 534: /* lwbrx */ 244662306a36Sopenharmony_ci op->type = MKOP(LOAD, BYTEREV, 4); 244762306a36Sopenharmony_ci break; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci case 597: /* lswi */ 245062306a36Sopenharmony_ci if (rb == 0) 245162306a36Sopenharmony_ci rb = 32; /* # bytes to load */ 245262306a36Sopenharmony_ci op->type = MKOP(LOAD_MULTI, 0, rb); 245362306a36Sopenharmony_ci op->ea = ra ? regs->gpr[ra] : 0; 245462306a36Sopenharmony_ci break; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci#ifdef CONFIG_PPC_FPU 245762306a36Sopenharmony_ci case 535: /* lfsx */ 245862306a36Sopenharmony_ci case 567: /* lfsux */ 245962306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, u | FPCONV, 4); 246062306a36Sopenharmony_ci break; 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci case 599: /* lfdx */ 246362306a36Sopenharmony_ci case 631: /* lfdux */ 246462306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, u, 8); 246562306a36Sopenharmony_ci break; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci case 663: /* stfsx */ 246862306a36Sopenharmony_ci case 695: /* stfsux */ 246962306a36Sopenharmony_ci op->type = MKOP(STORE_FP, u | FPCONV, 4); 247062306a36Sopenharmony_ci break; 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci case 727: /* stfdx */ 247362306a36Sopenharmony_ci case 759: /* stfdux */ 247462306a36Sopenharmony_ci op->type = MKOP(STORE_FP, u, 8); 247562306a36Sopenharmony_ci break; 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci#ifdef __powerpc64__ 247862306a36Sopenharmony_ci case 791: /* lfdpx */ 247962306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, 0, 16); 248062306a36Sopenharmony_ci break; 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci case 855: /* lfiwax */ 248362306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, SIGNEXT, 4); 248462306a36Sopenharmony_ci break; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci case 887: /* lfiwzx */ 248762306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, 0, 4); 248862306a36Sopenharmony_ci break; 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci case 919: /* stfdpx */ 249162306a36Sopenharmony_ci op->type = MKOP(STORE_FP, 0, 16); 249262306a36Sopenharmony_ci break; 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci case 983: /* stfiwx */ 249562306a36Sopenharmony_ci op->type = MKOP(STORE_FP, 0, 4); 249662306a36Sopenharmony_ci break; 249762306a36Sopenharmony_ci#endif /* __powerpc64 */ 249862306a36Sopenharmony_ci#endif /* CONFIG_PPC_FPU */ 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci#ifdef __powerpc64__ 250162306a36Sopenharmony_ci case 660: /* stdbrx */ 250262306a36Sopenharmony_ci op->type = MKOP(STORE, BYTEREV, 8); 250362306a36Sopenharmony_ci op->val = byterev_8(regs->gpr[rd]); 250462306a36Sopenharmony_ci break; 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci#endif 250762306a36Sopenharmony_ci case 661: /* stswx */ 250862306a36Sopenharmony_ci op->type = MKOP(STORE_MULTI, 0, regs->xer & 0x7f); 250962306a36Sopenharmony_ci break; 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci case 662: /* stwbrx */ 251262306a36Sopenharmony_ci op->type = MKOP(STORE, BYTEREV, 4); 251362306a36Sopenharmony_ci op->val = byterev_4(regs->gpr[rd]); 251462306a36Sopenharmony_ci break; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci case 725: /* stswi */ 251762306a36Sopenharmony_ci if (rb == 0) 251862306a36Sopenharmony_ci rb = 32; /* # bytes to store */ 251962306a36Sopenharmony_ci op->type = MKOP(STORE_MULTI, 0, rb); 252062306a36Sopenharmony_ci op->ea = ra ? regs->gpr[ra] : 0; 252162306a36Sopenharmony_ci break; 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci case 790: /* lhbrx */ 252462306a36Sopenharmony_ci op->type = MKOP(LOAD, BYTEREV, 2); 252562306a36Sopenharmony_ci break; 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci case 918: /* sthbrx */ 252862306a36Sopenharmony_ci op->type = MKOP(STORE, BYTEREV, 2); 252962306a36Sopenharmony_ci op->val = byterev_2(regs->gpr[rd]); 253062306a36Sopenharmony_ci break; 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci#ifdef CONFIG_VSX 253362306a36Sopenharmony_ci case 12: /* lxsiwzx */ 253462306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 253562306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 4); 253662306a36Sopenharmony_ci op->element_size = 8; 253762306a36Sopenharmony_ci break; 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci case 76: /* lxsiwax */ 254062306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 254162306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, SIGNEXT, 4); 254262306a36Sopenharmony_ci op->element_size = 8; 254362306a36Sopenharmony_ci break; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci case 140: /* stxsiwx */ 254662306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 254762306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 4); 254862306a36Sopenharmony_ci op->element_size = 8; 254962306a36Sopenharmony_ci break; 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci case 268: /* lxvx */ 255262306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 255362306a36Sopenharmony_ci goto unknown_opcode; 255462306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 255562306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 16); 255662306a36Sopenharmony_ci op->element_size = 16; 255762306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 255862306a36Sopenharmony_ci break; 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci case 269: /* lxvl */ 256162306a36Sopenharmony_ci case 301: { /* lxvll */ 256262306a36Sopenharmony_ci int nb; 256362306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 256462306a36Sopenharmony_ci goto unknown_opcode; 256562306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 256662306a36Sopenharmony_ci op->ea = ra ? regs->gpr[ra] : 0; 256762306a36Sopenharmony_ci nb = regs->gpr[rb] & 0xff; 256862306a36Sopenharmony_ci if (nb > 16) 256962306a36Sopenharmony_ci nb = 16; 257062306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, nb); 257162306a36Sopenharmony_ci op->element_size = 16; 257262306a36Sopenharmony_ci op->vsx_flags = ((word & 0x20) ? VSX_LDLEFT : 0) | 257362306a36Sopenharmony_ci VSX_CHECK_VEC; 257462306a36Sopenharmony_ci break; 257562306a36Sopenharmony_ci } 257662306a36Sopenharmony_ci case 332: /* lxvdsx */ 257762306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 257862306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 8); 257962306a36Sopenharmony_ci op->element_size = 8; 258062306a36Sopenharmony_ci op->vsx_flags = VSX_SPLAT; 258162306a36Sopenharmony_ci break; 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci case 333: /* lxvpx */ 258462306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_31)) 258562306a36Sopenharmony_ci goto unknown_opcode; 258662306a36Sopenharmony_ci op->reg = VSX_REGISTER_XTP(rd); 258762306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 32); 258862306a36Sopenharmony_ci op->element_size = 32; 258962306a36Sopenharmony_ci break; 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci case 364: /* lxvwsx */ 259262306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 259362306a36Sopenharmony_ci goto unknown_opcode; 259462306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 259562306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 4); 259662306a36Sopenharmony_ci op->element_size = 4; 259762306a36Sopenharmony_ci op->vsx_flags = VSX_SPLAT | VSX_CHECK_VEC; 259862306a36Sopenharmony_ci break; 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci case 396: /* stxvx */ 260162306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 260262306a36Sopenharmony_ci goto unknown_opcode; 260362306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 260462306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 16); 260562306a36Sopenharmony_ci op->element_size = 16; 260662306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 260762306a36Sopenharmony_ci break; 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci case 397: /* stxvl */ 261062306a36Sopenharmony_ci case 429: { /* stxvll */ 261162306a36Sopenharmony_ci int nb; 261262306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 261362306a36Sopenharmony_ci goto unknown_opcode; 261462306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 261562306a36Sopenharmony_ci op->ea = ra ? regs->gpr[ra] : 0; 261662306a36Sopenharmony_ci nb = regs->gpr[rb] & 0xff; 261762306a36Sopenharmony_ci if (nb > 16) 261862306a36Sopenharmony_ci nb = 16; 261962306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, nb); 262062306a36Sopenharmony_ci op->element_size = 16; 262162306a36Sopenharmony_ci op->vsx_flags = ((word & 0x20) ? VSX_LDLEFT : 0) | 262262306a36Sopenharmony_ci VSX_CHECK_VEC; 262362306a36Sopenharmony_ci break; 262462306a36Sopenharmony_ci } 262562306a36Sopenharmony_ci case 461: /* stxvpx */ 262662306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_31)) 262762306a36Sopenharmony_ci goto unknown_opcode; 262862306a36Sopenharmony_ci op->reg = VSX_REGISTER_XTP(rd); 262962306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 32); 263062306a36Sopenharmony_ci op->element_size = 32; 263162306a36Sopenharmony_ci break; 263262306a36Sopenharmony_ci case 524: /* lxsspx */ 263362306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 263462306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 4); 263562306a36Sopenharmony_ci op->element_size = 8; 263662306a36Sopenharmony_ci op->vsx_flags = VSX_FPCONV; 263762306a36Sopenharmony_ci break; 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci case 588: /* lxsdx */ 264062306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 264162306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 8); 264262306a36Sopenharmony_ci op->element_size = 8; 264362306a36Sopenharmony_ci break; 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci case 652: /* stxsspx */ 264662306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 264762306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 4); 264862306a36Sopenharmony_ci op->element_size = 8; 264962306a36Sopenharmony_ci op->vsx_flags = VSX_FPCONV; 265062306a36Sopenharmony_ci break; 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci case 716: /* stxsdx */ 265362306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 265462306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 8); 265562306a36Sopenharmony_ci op->element_size = 8; 265662306a36Sopenharmony_ci break; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci case 780: /* lxvw4x */ 265962306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 266062306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 16); 266162306a36Sopenharmony_ci op->element_size = 4; 266262306a36Sopenharmony_ci break; 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci case 781: /* lxsibzx */ 266562306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 266662306a36Sopenharmony_ci goto unknown_opcode; 266762306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 266862306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 1); 266962306a36Sopenharmony_ci op->element_size = 8; 267062306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 267162306a36Sopenharmony_ci break; 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_ci case 812: /* lxvh8x */ 267462306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 267562306a36Sopenharmony_ci goto unknown_opcode; 267662306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 267762306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 16); 267862306a36Sopenharmony_ci op->element_size = 2; 267962306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 268062306a36Sopenharmony_ci break; 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci case 813: /* lxsihzx */ 268362306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 268462306a36Sopenharmony_ci goto unknown_opcode; 268562306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 268662306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 2); 268762306a36Sopenharmony_ci op->element_size = 8; 268862306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 268962306a36Sopenharmony_ci break; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci case 844: /* lxvd2x */ 269262306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 269362306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 16); 269462306a36Sopenharmony_ci op->element_size = 8; 269562306a36Sopenharmony_ci break; 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci case 876: /* lxvb16x */ 269862306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 269962306a36Sopenharmony_ci goto unknown_opcode; 270062306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 270162306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 16); 270262306a36Sopenharmony_ci op->element_size = 1; 270362306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 270462306a36Sopenharmony_ci break; 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci case 908: /* stxvw4x */ 270762306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 270862306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 16); 270962306a36Sopenharmony_ci op->element_size = 4; 271062306a36Sopenharmony_ci break; 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci case 909: /* stxsibx */ 271362306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 271462306a36Sopenharmony_ci goto unknown_opcode; 271562306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 271662306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 1); 271762306a36Sopenharmony_ci op->element_size = 8; 271862306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 271962306a36Sopenharmony_ci break; 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci case 940: /* stxvh8x */ 272262306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 272362306a36Sopenharmony_ci goto unknown_opcode; 272462306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 272562306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 16); 272662306a36Sopenharmony_ci op->element_size = 2; 272762306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 272862306a36Sopenharmony_ci break; 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci case 941: /* stxsihx */ 273162306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 273262306a36Sopenharmony_ci goto unknown_opcode; 273362306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 273462306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 2); 273562306a36Sopenharmony_ci op->element_size = 8; 273662306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 273762306a36Sopenharmony_ci break; 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci case 972: /* stxvd2x */ 274062306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 274162306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 16); 274262306a36Sopenharmony_ci op->element_size = 8; 274362306a36Sopenharmony_ci break; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci case 1004: /* stxvb16x */ 274662306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 274762306a36Sopenharmony_ci goto unknown_opcode; 274862306a36Sopenharmony_ci op->reg = rd | ((word & 1) << 5); 274962306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 16); 275062306a36Sopenharmony_ci op->element_size = 1; 275162306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 275262306a36Sopenharmony_ci break; 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci#endif /* CONFIG_VSX */ 275562306a36Sopenharmony_ci } 275662306a36Sopenharmony_ci break; 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci case 32: /* lwz */ 275962306a36Sopenharmony_ci case 33: /* lwzu */ 276062306a36Sopenharmony_ci op->type = MKOP(LOAD, u, 4); 276162306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 276262306a36Sopenharmony_ci break; 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci case 34: /* lbz */ 276562306a36Sopenharmony_ci case 35: /* lbzu */ 276662306a36Sopenharmony_ci op->type = MKOP(LOAD, u, 1); 276762306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 276862306a36Sopenharmony_ci break; 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci case 36: /* stw */ 277162306a36Sopenharmony_ci case 37: /* stwu */ 277262306a36Sopenharmony_ci op->type = MKOP(STORE, u, 4); 277362306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 277462306a36Sopenharmony_ci break; 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci case 38: /* stb */ 277762306a36Sopenharmony_ci case 39: /* stbu */ 277862306a36Sopenharmony_ci op->type = MKOP(STORE, u, 1); 277962306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 278062306a36Sopenharmony_ci break; 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci case 40: /* lhz */ 278362306a36Sopenharmony_ci case 41: /* lhzu */ 278462306a36Sopenharmony_ci op->type = MKOP(LOAD, u, 2); 278562306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 278662306a36Sopenharmony_ci break; 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci case 42: /* lha */ 278962306a36Sopenharmony_ci case 43: /* lhau */ 279062306a36Sopenharmony_ci op->type = MKOP(LOAD, SIGNEXT | u, 2); 279162306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 279262306a36Sopenharmony_ci break; 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci case 44: /* sth */ 279562306a36Sopenharmony_ci case 45: /* sthu */ 279662306a36Sopenharmony_ci op->type = MKOP(STORE, u, 2); 279762306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 279862306a36Sopenharmony_ci break; 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_ci case 46: /* lmw */ 280162306a36Sopenharmony_ci if (ra >= rd) 280262306a36Sopenharmony_ci break; /* invalid form, ra in range to load */ 280362306a36Sopenharmony_ci op->type = MKOP(LOAD_MULTI, 0, 4 * (32 - rd)); 280462306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 280562306a36Sopenharmony_ci break; 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_ci case 47: /* stmw */ 280862306a36Sopenharmony_ci op->type = MKOP(STORE_MULTI, 0, 4 * (32 - rd)); 280962306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 281062306a36Sopenharmony_ci break; 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci#ifdef CONFIG_PPC_FPU 281362306a36Sopenharmony_ci case 48: /* lfs */ 281462306a36Sopenharmony_ci case 49: /* lfsu */ 281562306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, u | FPCONV, 4); 281662306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 281762306a36Sopenharmony_ci break; 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_ci case 50: /* lfd */ 282062306a36Sopenharmony_ci case 51: /* lfdu */ 282162306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, u, 8); 282262306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 282362306a36Sopenharmony_ci break; 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci case 52: /* stfs */ 282662306a36Sopenharmony_ci case 53: /* stfsu */ 282762306a36Sopenharmony_ci op->type = MKOP(STORE_FP, u | FPCONV, 4); 282862306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 282962306a36Sopenharmony_ci break; 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ci case 54: /* stfd */ 283262306a36Sopenharmony_ci case 55: /* stfdu */ 283362306a36Sopenharmony_ci op->type = MKOP(STORE_FP, u, 8); 283462306a36Sopenharmony_ci op->ea = dform_ea(word, regs); 283562306a36Sopenharmony_ci break; 283662306a36Sopenharmony_ci#endif 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ci#ifdef __powerpc64__ 283962306a36Sopenharmony_ci case 56: /* lq */ 284062306a36Sopenharmony_ci if (!((rd & 1) || (rd == ra))) 284162306a36Sopenharmony_ci op->type = MKOP(LOAD, 0, 16); 284262306a36Sopenharmony_ci op->ea = dqform_ea(word, regs); 284362306a36Sopenharmony_ci break; 284462306a36Sopenharmony_ci#endif 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci#ifdef CONFIG_VSX 284762306a36Sopenharmony_ci case 57: /* lfdp, lxsd, lxssp */ 284862306a36Sopenharmony_ci op->ea = dsform_ea(word, regs); 284962306a36Sopenharmony_ci switch (word & 3) { 285062306a36Sopenharmony_ci case 0: /* lfdp */ 285162306a36Sopenharmony_ci if (rd & 1) 285262306a36Sopenharmony_ci break; /* reg must be even */ 285362306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, 0, 16); 285462306a36Sopenharmony_ci break; 285562306a36Sopenharmony_ci case 2: /* lxsd */ 285662306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 285762306a36Sopenharmony_ci goto unknown_opcode; 285862306a36Sopenharmony_ci op->reg = rd + 32; 285962306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 8); 286062306a36Sopenharmony_ci op->element_size = 8; 286162306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 286262306a36Sopenharmony_ci break; 286362306a36Sopenharmony_ci case 3: /* lxssp */ 286462306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 286562306a36Sopenharmony_ci goto unknown_opcode; 286662306a36Sopenharmony_ci op->reg = rd + 32; 286762306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 4); 286862306a36Sopenharmony_ci op->element_size = 8; 286962306a36Sopenharmony_ci op->vsx_flags = VSX_FPCONV | VSX_CHECK_VEC; 287062306a36Sopenharmony_ci break; 287162306a36Sopenharmony_ci } 287262306a36Sopenharmony_ci break; 287362306a36Sopenharmony_ci#endif /* CONFIG_VSX */ 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci#ifdef __powerpc64__ 287662306a36Sopenharmony_ci case 58: /* ld[u], lwa */ 287762306a36Sopenharmony_ci op->ea = dsform_ea(word, regs); 287862306a36Sopenharmony_ci switch (word & 3) { 287962306a36Sopenharmony_ci case 0: /* ld */ 288062306a36Sopenharmony_ci op->type = MKOP(LOAD, 0, 8); 288162306a36Sopenharmony_ci break; 288262306a36Sopenharmony_ci case 1: /* ldu */ 288362306a36Sopenharmony_ci op->type = MKOP(LOAD, UPDATE, 8); 288462306a36Sopenharmony_ci break; 288562306a36Sopenharmony_ci case 2: /* lwa */ 288662306a36Sopenharmony_ci op->type = MKOP(LOAD, SIGNEXT, 4); 288762306a36Sopenharmony_ci break; 288862306a36Sopenharmony_ci } 288962306a36Sopenharmony_ci break; 289062306a36Sopenharmony_ci#endif 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci#ifdef CONFIG_VSX 289362306a36Sopenharmony_ci case 6: 289462306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_31)) 289562306a36Sopenharmony_ci goto unknown_opcode; 289662306a36Sopenharmony_ci op->ea = dqform_ea(word, regs); 289762306a36Sopenharmony_ci op->reg = VSX_REGISTER_XTP(rd); 289862306a36Sopenharmony_ci op->element_size = 32; 289962306a36Sopenharmony_ci switch (word & 0xf) { 290062306a36Sopenharmony_ci case 0: /* lxvp */ 290162306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 32); 290262306a36Sopenharmony_ci break; 290362306a36Sopenharmony_ci case 1: /* stxvp */ 290462306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 32); 290562306a36Sopenharmony_ci break; 290662306a36Sopenharmony_ci } 290762306a36Sopenharmony_ci break; 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci case 61: /* stfdp, lxv, stxsd, stxssp, stxv */ 291062306a36Sopenharmony_ci switch (word & 7) { 291162306a36Sopenharmony_ci case 0: /* stfdp with LSB of DS field = 0 */ 291262306a36Sopenharmony_ci case 4: /* stfdp with LSB of DS field = 1 */ 291362306a36Sopenharmony_ci op->ea = dsform_ea(word, regs); 291462306a36Sopenharmony_ci op->type = MKOP(STORE_FP, 0, 16); 291562306a36Sopenharmony_ci break; 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci case 1: /* lxv */ 291862306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 291962306a36Sopenharmony_ci goto unknown_opcode; 292062306a36Sopenharmony_ci op->ea = dqform_ea(word, regs); 292162306a36Sopenharmony_ci if (word & 8) 292262306a36Sopenharmony_ci op->reg = rd + 32; 292362306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, 0, 16); 292462306a36Sopenharmony_ci op->element_size = 16; 292562306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 292662306a36Sopenharmony_ci break; 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci case 2: /* stxsd with LSB of DS field = 0 */ 292962306a36Sopenharmony_ci case 6: /* stxsd with LSB of DS field = 1 */ 293062306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 293162306a36Sopenharmony_ci goto unknown_opcode; 293262306a36Sopenharmony_ci op->ea = dsform_ea(word, regs); 293362306a36Sopenharmony_ci op->reg = rd + 32; 293462306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 8); 293562306a36Sopenharmony_ci op->element_size = 8; 293662306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 293762306a36Sopenharmony_ci break; 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci case 3: /* stxssp with LSB of DS field = 0 */ 294062306a36Sopenharmony_ci case 7: /* stxssp with LSB of DS field = 1 */ 294162306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 294262306a36Sopenharmony_ci goto unknown_opcode; 294362306a36Sopenharmony_ci op->ea = dsform_ea(word, regs); 294462306a36Sopenharmony_ci op->reg = rd + 32; 294562306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 4); 294662306a36Sopenharmony_ci op->element_size = 8; 294762306a36Sopenharmony_ci op->vsx_flags = VSX_FPCONV | VSX_CHECK_VEC; 294862306a36Sopenharmony_ci break; 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci case 5: /* stxv */ 295162306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_300)) 295262306a36Sopenharmony_ci goto unknown_opcode; 295362306a36Sopenharmony_ci op->ea = dqform_ea(word, regs); 295462306a36Sopenharmony_ci if (word & 8) 295562306a36Sopenharmony_ci op->reg = rd + 32; 295662306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, 0, 16); 295762306a36Sopenharmony_ci op->element_size = 16; 295862306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 295962306a36Sopenharmony_ci break; 296062306a36Sopenharmony_ci } 296162306a36Sopenharmony_ci break; 296262306a36Sopenharmony_ci#endif /* CONFIG_VSX */ 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci#ifdef __powerpc64__ 296562306a36Sopenharmony_ci case 62: /* std[u] */ 296662306a36Sopenharmony_ci op->ea = dsform_ea(word, regs); 296762306a36Sopenharmony_ci switch (word & 3) { 296862306a36Sopenharmony_ci case 0: /* std */ 296962306a36Sopenharmony_ci op->type = MKOP(STORE, 0, 8); 297062306a36Sopenharmony_ci break; 297162306a36Sopenharmony_ci case 1: /* stdu */ 297262306a36Sopenharmony_ci op->type = MKOP(STORE, UPDATE, 8); 297362306a36Sopenharmony_ci break; 297462306a36Sopenharmony_ci case 2: /* stq */ 297562306a36Sopenharmony_ci if (!(rd & 1)) 297662306a36Sopenharmony_ci op->type = MKOP(STORE, 0, 16); 297762306a36Sopenharmony_ci break; 297862306a36Sopenharmony_ci } 297962306a36Sopenharmony_ci break; 298062306a36Sopenharmony_ci case 1: /* Prefixed instructions */ 298162306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_31)) 298262306a36Sopenharmony_ci goto unknown_opcode; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci prefix_r = GET_PREFIX_R(word); 298562306a36Sopenharmony_ci ra = GET_PREFIX_RA(suffix); 298662306a36Sopenharmony_ci op->update_reg = ra; 298762306a36Sopenharmony_ci rd = (suffix >> 21) & 0x1f; 298862306a36Sopenharmony_ci op->reg = rd; 298962306a36Sopenharmony_ci op->val = regs->gpr[rd]; 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_ci suffixopcode = get_op(suffix); 299262306a36Sopenharmony_ci prefixtype = (word >> 24) & 0x3; 299362306a36Sopenharmony_ci switch (prefixtype) { 299462306a36Sopenharmony_ci case 0: /* Type 00 Eight-Byte Load/Store */ 299562306a36Sopenharmony_ci if (prefix_r && ra) 299662306a36Sopenharmony_ci break; 299762306a36Sopenharmony_ci op->ea = mlsd_8lsd_ea(word, suffix, regs); 299862306a36Sopenharmony_ci switch (suffixopcode) { 299962306a36Sopenharmony_ci case 41: /* plwa */ 300062306a36Sopenharmony_ci op->type = MKOP(LOAD, PREFIXED | SIGNEXT, 4); 300162306a36Sopenharmony_ci break; 300262306a36Sopenharmony_ci#ifdef CONFIG_VSX 300362306a36Sopenharmony_ci case 42: /* plxsd */ 300462306a36Sopenharmony_ci op->reg = rd + 32; 300562306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, PREFIXED, 8); 300662306a36Sopenharmony_ci op->element_size = 8; 300762306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 300862306a36Sopenharmony_ci break; 300962306a36Sopenharmony_ci case 43: /* plxssp */ 301062306a36Sopenharmony_ci op->reg = rd + 32; 301162306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, PREFIXED, 4); 301262306a36Sopenharmony_ci op->element_size = 8; 301362306a36Sopenharmony_ci op->vsx_flags = VSX_FPCONV | VSX_CHECK_VEC; 301462306a36Sopenharmony_ci break; 301562306a36Sopenharmony_ci case 46: /* pstxsd */ 301662306a36Sopenharmony_ci op->reg = rd + 32; 301762306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, PREFIXED, 8); 301862306a36Sopenharmony_ci op->element_size = 8; 301962306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 302062306a36Sopenharmony_ci break; 302162306a36Sopenharmony_ci case 47: /* pstxssp */ 302262306a36Sopenharmony_ci op->reg = rd + 32; 302362306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, PREFIXED, 4); 302462306a36Sopenharmony_ci op->element_size = 8; 302562306a36Sopenharmony_ci op->vsx_flags = VSX_FPCONV | VSX_CHECK_VEC; 302662306a36Sopenharmony_ci break; 302762306a36Sopenharmony_ci case 51: /* plxv1 */ 302862306a36Sopenharmony_ci op->reg += 32; 302962306a36Sopenharmony_ci fallthrough; 303062306a36Sopenharmony_ci case 50: /* plxv0 */ 303162306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, PREFIXED, 16); 303262306a36Sopenharmony_ci op->element_size = 16; 303362306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 303462306a36Sopenharmony_ci break; 303562306a36Sopenharmony_ci case 55: /* pstxv1 */ 303662306a36Sopenharmony_ci op->reg = rd + 32; 303762306a36Sopenharmony_ci fallthrough; 303862306a36Sopenharmony_ci case 54: /* pstxv0 */ 303962306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, PREFIXED, 16); 304062306a36Sopenharmony_ci op->element_size = 16; 304162306a36Sopenharmony_ci op->vsx_flags = VSX_CHECK_VEC; 304262306a36Sopenharmony_ci break; 304362306a36Sopenharmony_ci#endif /* CONFIG_VSX */ 304462306a36Sopenharmony_ci case 56: /* plq */ 304562306a36Sopenharmony_ci op->type = MKOP(LOAD, PREFIXED, 16); 304662306a36Sopenharmony_ci break; 304762306a36Sopenharmony_ci case 57: /* pld */ 304862306a36Sopenharmony_ci op->type = MKOP(LOAD, PREFIXED, 8); 304962306a36Sopenharmony_ci break; 305062306a36Sopenharmony_ci#ifdef CONFIG_VSX 305162306a36Sopenharmony_ci case 58: /* plxvp */ 305262306a36Sopenharmony_ci op->reg = VSX_REGISTER_XTP(rd); 305362306a36Sopenharmony_ci op->type = MKOP(LOAD_VSX, PREFIXED, 32); 305462306a36Sopenharmony_ci op->element_size = 32; 305562306a36Sopenharmony_ci break; 305662306a36Sopenharmony_ci#endif /* CONFIG_VSX */ 305762306a36Sopenharmony_ci case 60: /* pstq */ 305862306a36Sopenharmony_ci op->type = MKOP(STORE, PREFIXED, 16); 305962306a36Sopenharmony_ci break; 306062306a36Sopenharmony_ci case 61: /* pstd */ 306162306a36Sopenharmony_ci op->type = MKOP(STORE, PREFIXED, 8); 306262306a36Sopenharmony_ci break; 306362306a36Sopenharmony_ci#ifdef CONFIG_VSX 306462306a36Sopenharmony_ci case 62: /* pstxvp */ 306562306a36Sopenharmony_ci op->reg = VSX_REGISTER_XTP(rd); 306662306a36Sopenharmony_ci op->type = MKOP(STORE_VSX, PREFIXED, 32); 306762306a36Sopenharmony_ci op->element_size = 32; 306862306a36Sopenharmony_ci break; 306962306a36Sopenharmony_ci#endif /* CONFIG_VSX */ 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci break; 307262306a36Sopenharmony_ci case 1: /* Type 01 Eight-Byte Register-to-Register */ 307362306a36Sopenharmony_ci break; 307462306a36Sopenharmony_ci case 2: /* Type 10 Modified Load/Store */ 307562306a36Sopenharmony_ci if (prefix_r && ra) 307662306a36Sopenharmony_ci break; 307762306a36Sopenharmony_ci op->ea = mlsd_8lsd_ea(word, suffix, regs); 307862306a36Sopenharmony_ci switch (suffixopcode) { 307962306a36Sopenharmony_ci case 32: /* plwz */ 308062306a36Sopenharmony_ci op->type = MKOP(LOAD, PREFIXED, 4); 308162306a36Sopenharmony_ci break; 308262306a36Sopenharmony_ci case 34: /* plbz */ 308362306a36Sopenharmony_ci op->type = MKOP(LOAD, PREFIXED, 1); 308462306a36Sopenharmony_ci break; 308562306a36Sopenharmony_ci case 36: /* pstw */ 308662306a36Sopenharmony_ci op->type = MKOP(STORE, PREFIXED, 4); 308762306a36Sopenharmony_ci break; 308862306a36Sopenharmony_ci case 38: /* pstb */ 308962306a36Sopenharmony_ci op->type = MKOP(STORE, PREFIXED, 1); 309062306a36Sopenharmony_ci break; 309162306a36Sopenharmony_ci case 40: /* plhz */ 309262306a36Sopenharmony_ci op->type = MKOP(LOAD, PREFIXED, 2); 309362306a36Sopenharmony_ci break; 309462306a36Sopenharmony_ci case 42: /* plha */ 309562306a36Sopenharmony_ci op->type = MKOP(LOAD, PREFIXED | SIGNEXT, 2); 309662306a36Sopenharmony_ci break; 309762306a36Sopenharmony_ci case 44: /* psth */ 309862306a36Sopenharmony_ci op->type = MKOP(STORE, PREFIXED, 2); 309962306a36Sopenharmony_ci break; 310062306a36Sopenharmony_ci case 48: /* plfs */ 310162306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, PREFIXED | FPCONV, 4); 310262306a36Sopenharmony_ci break; 310362306a36Sopenharmony_ci case 50: /* plfd */ 310462306a36Sopenharmony_ci op->type = MKOP(LOAD_FP, PREFIXED, 8); 310562306a36Sopenharmony_ci break; 310662306a36Sopenharmony_ci case 52: /* pstfs */ 310762306a36Sopenharmony_ci op->type = MKOP(STORE_FP, PREFIXED | FPCONV, 4); 310862306a36Sopenharmony_ci break; 310962306a36Sopenharmony_ci case 54: /* pstfd */ 311062306a36Sopenharmony_ci op->type = MKOP(STORE_FP, PREFIXED, 8); 311162306a36Sopenharmony_ci break; 311262306a36Sopenharmony_ci } 311362306a36Sopenharmony_ci break; 311462306a36Sopenharmony_ci case 3: /* Type 11 Modified Register-to-Register */ 311562306a36Sopenharmony_ci break; 311662306a36Sopenharmony_ci } 311762306a36Sopenharmony_ci#endif /* __powerpc64__ */ 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_ci } 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ci if (OP_IS_LOAD_STORE(op->type) && (op->type & UPDATE)) { 312262306a36Sopenharmony_ci switch (GETTYPE(op->type)) { 312362306a36Sopenharmony_ci case LOAD: 312462306a36Sopenharmony_ci if (ra == rd) 312562306a36Sopenharmony_ci goto unknown_opcode; 312662306a36Sopenharmony_ci fallthrough; 312762306a36Sopenharmony_ci case STORE: 312862306a36Sopenharmony_ci case LOAD_FP: 312962306a36Sopenharmony_ci case STORE_FP: 313062306a36Sopenharmony_ci if (ra == 0) 313162306a36Sopenharmony_ci goto unknown_opcode; 313262306a36Sopenharmony_ci } 313362306a36Sopenharmony_ci } 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci#ifdef CONFIG_VSX 313662306a36Sopenharmony_ci if ((GETTYPE(op->type) == LOAD_VSX || 313762306a36Sopenharmony_ci GETTYPE(op->type) == STORE_VSX) && 313862306a36Sopenharmony_ci !cpu_has_feature(CPU_FTR_VSX)) { 313962306a36Sopenharmony_ci return -1; 314062306a36Sopenharmony_ci } 314162306a36Sopenharmony_ci#endif /* CONFIG_VSX */ 314262306a36Sopenharmony_ci 314362306a36Sopenharmony_ci return 0; 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci unknown_opcode: 314662306a36Sopenharmony_ci op->type = UNKNOWN; 314762306a36Sopenharmony_ci return 0; 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci logical_done: 315062306a36Sopenharmony_ci if (word & 1) 315162306a36Sopenharmony_ci set_cr0(regs, op); 315262306a36Sopenharmony_ci logical_done_nocc: 315362306a36Sopenharmony_ci op->reg = ra; 315462306a36Sopenharmony_ci op->type |= SETREG; 315562306a36Sopenharmony_ci return 1; 315662306a36Sopenharmony_ci 315762306a36Sopenharmony_ci arith_done: 315862306a36Sopenharmony_ci if (word & 1) 315962306a36Sopenharmony_ci set_cr0(regs, op); 316062306a36Sopenharmony_ci compute_done: 316162306a36Sopenharmony_ci op->reg = rd; 316262306a36Sopenharmony_ci op->type |= SETREG; 316362306a36Sopenharmony_ci return 1; 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci priv: 316662306a36Sopenharmony_ci op->type = INTERRUPT | 0x700; 316762306a36Sopenharmony_ci op->val = SRR1_PROGPRIV; 316862306a36Sopenharmony_ci return 0; 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci trap: 317162306a36Sopenharmony_ci op->type = INTERRUPT | 0x700; 317262306a36Sopenharmony_ci op->val = SRR1_PROGTRAP; 317362306a36Sopenharmony_ci return 0; 317462306a36Sopenharmony_ci} 317562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(analyse_instr); 317662306a36Sopenharmony_ciNOKPROBE_SYMBOL(analyse_instr); 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci/* 317962306a36Sopenharmony_ci * For PPC32 we always use stwu with r1 to change the stack pointer. 318062306a36Sopenharmony_ci * So this emulated store may corrupt the exception frame, now we 318162306a36Sopenharmony_ci * have to provide the exception frame trampoline, which is pushed 318262306a36Sopenharmony_ci * below the kprobed function stack. So we only update gpr[1] but 318362306a36Sopenharmony_ci * don't emulate the real store operation. We will do real store 318462306a36Sopenharmony_ci * operation safely in exception return code by checking this flag. 318562306a36Sopenharmony_ci */ 318662306a36Sopenharmony_cistatic nokprobe_inline int handle_stack_update(unsigned long ea, struct pt_regs *regs) 318762306a36Sopenharmony_ci{ 318862306a36Sopenharmony_ci /* 318962306a36Sopenharmony_ci * Check if we already set since that means we'll 319062306a36Sopenharmony_ci * lose the previous value. 319162306a36Sopenharmony_ci */ 319262306a36Sopenharmony_ci WARN_ON(test_thread_flag(TIF_EMULATE_STACK_STORE)); 319362306a36Sopenharmony_ci set_thread_flag(TIF_EMULATE_STACK_STORE); 319462306a36Sopenharmony_ci return 0; 319562306a36Sopenharmony_ci} 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_cistatic nokprobe_inline void do_signext(unsigned long *valp, int size) 319862306a36Sopenharmony_ci{ 319962306a36Sopenharmony_ci switch (size) { 320062306a36Sopenharmony_ci case 2: 320162306a36Sopenharmony_ci *valp = (signed short) *valp; 320262306a36Sopenharmony_ci break; 320362306a36Sopenharmony_ci case 4: 320462306a36Sopenharmony_ci *valp = (signed int) *valp; 320562306a36Sopenharmony_ci break; 320662306a36Sopenharmony_ci } 320762306a36Sopenharmony_ci} 320862306a36Sopenharmony_ci 320962306a36Sopenharmony_cistatic nokprobe_inline void do_byterev(unsigned long *valp, int size) 321062306a36Sopenharmony_ci{ 321162306a36Sopenharmony_ci switch (size) { 321262306a36Sopenharmony_ci case 2: 321362306a36Sopenharmony_ci *valp = byterev_2(*valp); 321462306a36Sopenharmony_ci break; 321562306a36Sopenharmony_ci case 4: 321662306a36Sopenharmony_ci *valp = byterev_4(*valp); 321762306a36Sopenharmony_ci break; 321862306a36Sopenharmony_ci#ifdef __powerpc64__ 321962306a36Sopenharmony_ci case 8: 322062306a36Sopenharmony_ci *valp = byterev_8(*valp); 322162306a36Sopenharmony_ci break; 322262306a36Sopenharmony_ci#endif 322362306a36Sopenharmony_ci } 322462306a36Sopenharmony_ci} 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_ci/* 322762306a36Sopenharmony_ci * Emulate an instruction that can be executed just by updating 322862306a36Sopenharmony_ci * fields in *regs. 322962306a36Sopenharmony_ci */ 323062306a36Sopenharmony_civoid emulate_update_regs(struct pt_regs *regs, struct instruction_op *op) 323162306a36Sopenharmony_ci{ 323262306a36Sopenharmony_ci unsigned long next_pc; 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci next_pc = truncate_if_32bit(regs->msr, regs->nip + GETLENGTH(op->type)); 323562306a36Sopenharmony_ci switch (GETTYPE(op->type)) { 323662306a36Sopenharmony_ci case COMPUTE: 323762306a36Sopenharmony_ci if (op->type & SETREG) 323862306a36Sopenharmony_ci regs->gpr[op->reg] = op->val; 323962306a36Sopenharmony_ci if (op->type & SETCC) 324062306a36Sopenharmony_ci regs->ccr = op->ccval; 324162306a36Sopenharmony_ci if (op->type & SETXER) 324262306a36Sopenharmony_ci regs->xer = op->xerval; 324362306a36Sopenharmony_ci break; 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_ci case BRANCH: 324662306a36Sopenharmony_ci if (op->type & SETLK) 324762306a36Sopenharmony_ci regs->link = next_pc; 324862306a36Sopenharmony_ci if (op->type & BRTAKEN) 324962306a36Sopenharmony_ci next_pc = op->val; 325062306a36Sopenharmony_ci if (op->type & DECCTR) 325162306a36Sopenharmony_ci --regs->ctr; 325262306a36Sopenharmony_ci break; 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci case BARRIER: 325562306a36Sopenharmony_ci switch (op->type & BARRIER_MASK) { 325662306a36Sopenharmony_ci case BARRIER_SYNC: 325762306a36Sopenharmony_ci mb(); 325862306a36Sopenharmony_ci break; 325962306a36Sopenharmony_ci case BARRIER_ISYNC: 326062306a36Sopenharmony_ci isync(); 326162306a36Sopenharmony_ci break; 326262306a36Sopenharmony_ci case BARRIER_EIEIO: 326362306a36Sopenharmony_ci eieio(); 326462306a36Sopenharmony_ci break; 326562306a36Sopenharmony_ci#ifdef CONFIG_PPC64 326662306a36Sopenharmony_ci case BARRIER_LWSYNC: 326762306a36Sopenharmony_ci asm volatile("lwsync" : : : "memory"); 326862306a36Sopenharmony_ci break; 326962306a36Sopenharmony_ci case BARRIER_PTESYNC: 327062306a36Sopenharmony_ci asm volatile("ptesync" : : : "memory"); 327162306a36Sopenharmony_ci break; 327262306a36Sopenharmony_ci#endif 327362306a36Sopenharmony_ci } 327462306a36Sopenharmony_ci break; 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ci case MFSPR: 327762306a36Sopenharmony_ci switch (op->spr) { 327862306a36Sopenharmony_ci case SPRN_XER: 327962306a36Sopenharmony_ci regs->gpr[op->reg] = regs->xer & 0xffffffffUL; 328062306a36Sopenharmony_ci break; 328162306a36Sopenharmony_ci case SPRN_LR: 328262306a36Sopenharmony_ci regs->gpr[op->reg] = regs->link; 328362306a36Sopenharmony_ci break; 328462306a36Sopenharmony_ci case SPRN_CTR: 328562306a36Sopenharmony_ci regs->gpr[op->reg] = regs->ctr; 328662306a36Sopenharmony_ci break; 328762306a36Sopenharmony_ci default: 328862306a36Sopenharmony_ci WARN_ON_ONCE(1); 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci break; 329162306a36Sopenharmony_ci 329262306a36Sopenharmony_ci case MTSPR: 329362306a36Sopenharmony_ci switch (op->spr) { 329462306a36Sopenharmony_ci case SPRN_XER: 329562306a36Sopenharmony_ci regs->xer = op->val & 0xffffffffUL; 329662306a36Sopenharmony_ci break; 329762306a36Sopenharmony_ci case SPRN_LR: 329862306a36Sopenharmony_ci regs->link = op->val; 329962306a36Sopenharmony_ci break; 330062306a36Sopenharmony_ci case SPRN_CTR: 330162306a36Sopenharmony_ci regs->ctr = op->val; 330262306a36Sopenharmony_ci break; 330362306a36Sopenharmony_ci default: 330462306a36Sopenharmony_ci WARN_ON_ONCE(1); 330562306a36Sopenharmony_ci } 330662306a36Sopenharmony_ci break; 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci default: 330962306a36Sopenharmony_ci WARN_ON_ONCE(1); 331062306a36Sopenharmony_ci } 331162306a36Sopenharmony_ci regs_set_return_ip(regs, next_pc); 331262306a36Sopenharmony_ci} 331362306a36Sopenharmony_ciNOKPROBE_SYMBOL(emulate_update_regs); 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci/* 331662306a36Sopenharmony_ci * Emulate a previously-analysed load or store instruction. 331762306a36Sopenharmony_ci * Return values are: 331862306a36Sopenharmony_ci * 0 = instruction emulated successfully 331962306a36Sopenharmony_ci * -EFAULT = address out of range or access faulted (regs->dar 332062306a36Sopenharmony_ci * contains the faulting address) 332162306a36Sopenharmony_ci * -EACCES = misaligned access, instruction requires alignment 332262306a36Sopenharmony_ci * -EINVAL = unknown operation in *op 332362306a36Sopenharmony_ci */ 332462306a36Sopenharmony_ciint emulate_loadstore(struct pt_regs *regs, struct instruction_op *op) 332562306a36Sopenharmony_ci{ 332662306a36Sopenharmony_ci int err, size, type; 332762306a36Sopenharmony_ci int i, rd, nb; 332862306a36Sopenharmony_ci unsigned int cr; 332962306a36Sopenharmony_ci unsigned long val; 333062306a36Sopenharmony_ci unsigned long ea; 333162306a36Sopenharmony_ci bool cross_endian; 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci err = 0; 333462306a36Sopenharmony_ci size = GETSIZE(op->type); 333562306a36Sopenharmony_ci type = GETTYPE(op->type); 333662306a36Sopenharmony_ci cross_endian = (regs->msr & MSR_LE) != (MSR_KERNEL & MSR_LE); 333762306a36Sopenharmony_ci ea = truncate_if_32bit(regs->msr, op->ea); 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci switch (type) { 334062306a36Sopenharmony_ci case LARX: 334162306a36Sopenharmony_ci if (ea & (size - 1)) 334262306a36Sopenharmony_ci return -EACCES; /* can't handle misaligned */ 334362306a36Sopenharmony_ci if (!address_ok(regs, ea, size)) 334462306a36Sopenharmony_ci return -EFAULT; 334562306a36Sopenharmony_ci err = 0; 334662306a36Sopenharmony_ci val = 0; 334762306a36Sopenharmony_ci switch (size) { 334862306a36Sopenharmony_ci#ifdef CONFIG_PPC_HAS_LBARX_LHARX 334962306a36Sopenharmony_ci case 1: 335062306a36Sopenharmony_ci __get_user_asmx(val, ea, err, "lbarx"); 335162306a36Sopenharmony_ci break; 335262306a36Sopenharmony_ci case 2: 335362306a36Sopenharmony_ci __get_user_asmx(val, ea, err, "lharx"); 335462306a36Sopenharmony_ci break; 335562306a36Sopenharmony_ci#endif 335662306a36Sopenharmony_ci case 4: 335762306a36Sopenharmony_ci __get_user_asmx(val, ea, err, "lwarx"); 335862306a36Sopenharmony_ci break; 335962306a36Sopenharmony_ci#ifdef __powerpc64__ 336062306a36Sopenharmony_ci case 8: 336162306a36Sopenharmony_ci __get_user_asmx(val, ea, err, "ldarx"); 336262306a36Sopenharmony_ci break; 336362306a36Sopenharmony_ci case 16: 336462306a36Sopenharmony_ci err = do_lqarx(ea, ®s->gpr[op->reg]); 336562306a36Sopenharmony_ci break; 336662306a36Sopenharmony_ci#endif 336762306a36Sopenharmony_ci default: 336862306a36Sopenharmony_ci return -EINVAL; 336962306a36Sopenharmony_ci } 337062306a36Sopenharmony_ci if (err) { 337162306a36Sopenharmony_ci regs->dar = ea; 337262306a36Sopenharmony_ci break; 337362306a36Sopenharmony_ci } 337462306a36Sopenharmony_ci if (size < 16) 337562306a36Sopenharmony_ci regs->gpr[op->reg] = val; 337662306a36Sopenharmony_ci break; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci case STCX: 337962306a36Sopenharmony_ci if (ea & (size - 1)) 338062306a36Sopenharmony_ci return -EACCES; /* can't handle misaligned */ 338162306a36Sopenharmony_ci if (!address_ok(regs, ea, size)) 338262306a36Sopenharmony_ci return -EFAULT; 338362306a36Sopenharmony_ci err = 0; 338462306a36Sopenharmony_ci switch (size) { 338562306a36Sopenharmony_ci#ifdef __powerpc64__ 338662306a36Sopenharmony_ci case 1: 338762306a36Sopenharmony_ci __put_user_asmx(op->val, ea, err, "stbcx.", cr); 338862306a36Sopenharmony_ci break; 338962306a36Sopenharmony_ci case 2: 339062306a36Sopenharmony_ci __put_user_asmx(op->val, ea, err, "sthcx.", cr); 339162306a36Sopenharmony_ci break; 339262306a36Sopenharmony_ci#endif 339362306a36Sopenharmony_ci case 4: 339462306a36Sopenharmony_ci __put_user_asmx(op->val, ea, err, "stwcx.", cr); 339562306a36Sopenharmony_ci break; 339662306a36Sopenharmony_ci#ifdef __powerpc64__ 339762306a36Sopenharmony_ci case 8: 339862306a36Sopenharmony_ci __put_user_asmx(op->val, ea, err, "stdcx.", cr); 339962306a36Sopenharmony_ci break; 340062306a36Sopenharmony_ci case 16: 340162306a36Sopenharmony_ci err = do_stqcx(ea, regs->gpr[op->reg], 340262306a36Sopenharmony_ci regs->gpr[op->reg + 1], &cr); 340362306a36Sopenharmony_ci break; 340462306a36Sopenharmony_ci#endif 340562306a36Sopenharmony_ci default: 340662306a36Sopenharmony_ci return -EINVAL; 340762306a36Sopenharmony_ci } 340862306a36Sopenharmony_ci if (!err) 340962306a36Sopenharmony_ci regs->ccr = (regs->ccr & 0x0fffffff) | 341062306a36Sopenharmony_ci (cr & 0xe0000000) | 341162306a36Sopenharmony_ci ((regs->xer >> 3) & 0x10000000); 341262306a36Sopenharmony_ci else 341362306a36Sopenharmony_ci regs->dar = ea; 341462306a36Sopenharmony_ci break; 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci case LOAD: 341762306a36Sopenharmony_ci#ifdef __powerpc64__ 341862306a36Sopenharmony_ci if (size == 16) { 341962306a36Sopenharmony_ci err = emulate_lq(regs, ea, op->reg, cross_endian); 342062306a36Sopenharmony_ci break; 342162306a36Sopenharmony_ci } 342262306a36Sopenharmony_ci#endif 342362306a36Sopenharmony_ci err = read_mem(®s->gpr[op->reg], ea, size, regs); 342462306a36Sopenharmony_ci if (!err) { 342562306a36Sopenharmony_ci if (op->type & SIGNEXT) 342662306a36Sopenharmony_ci do_signext(®s->gpr[op->reg], size); 342762306a36Sopenharmony_ci if ((op->type & BYTEREV) == (cross_endian ? 0 : BYTEREV)) 342862306a36Sopenharmony_ci do_byterev(®s->gpr[op->reg], size); 342962306a36Sopenharmony_ci } 343062306a36Sopenharmony_ci break; 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci#ifdef CONFIG_PPC_FPU 343362306a36Sopenharmony_ci case LOAD_FP: 343462306a36Sopenharmony_ci /* 343562306a36Sopenharmony_ci * If the instruction is in userspace, we can emulate it even 343662306a36Sopenharmony_ci * if the VMX state is not live, because we have the state 343762306a36Sopenharmony_ci * stored in the thread_struct. If the instruction is in 343862306a36Sopenharmony_ci * the kernel, we must not touch the state in the thread_struct. 343962306a36Sopenharmony_ci */ 344062306a36Sopenharmony_ci if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_FP)) 344162306a36Sopenharmony_ci return 0; 344262306a36Sopenharmony_ci err = do_fp_load(op, ea, regs, cross_endian); 344362306a36Sopenharmony_ci break; 344462306a36Sopenharmony_ci#endif 344562306a36Sopenharmony_ci#ifdef CONFIG_ALTIVEC 344662306a36Sopenharmony_ci case LOAD_VMX: 344762306a36Sopenharmony_ci if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_VEC)) 344862306a36Sopenharmony_ci return 0; 344962306a36Sopenharmony_ci err = do_vec_load(op->reg, ea, size, regs, cross_endian); 345062306a36Sopenharmony_ci break; 345162306a36Sopenharmony_ci#endif 345262306a36Sopenharmony_ci#ifdef CONFIG_VSX 345362306a36Sopenharmony_ci case LOAD_VSX: { 345462306a36Sopenharmony_ci unsigned long msrbit = MSR_VSX; 345562306a36Sopenharmony_ci 345662306a36Sopenharmony_ci /* 345762306a36Sopenharmony_ci * Some VSX instructions check the MSR_VEC bit rather than MSR_VSX 345862306a36Sopenharmony_ci * when the target of the instruction is a vector register. 345962306a36Sopenharmony_ci */ 346062306a36Sopenharmony_ci if (op->reg >= 32 && (op->vsx_flags & VSX_CHECK_VEC)) 346162306a36Sopenharmony_ci msrbit = MSR_VEC; 346262306a36Sopenharmony_ci if (!(regs->msr & MSR_PR) && !(regs->msr & msrbit)) 346362306a36Sopenharmony_ci return 0; 346462306a36Sopenharmony_ci err = do_vsx_load(op, ea, regs, cross_endian); 346562306a36Sopenharmony_ci break; 346662306a36Sopenharmony_ci } 346762306a36Sopenharmony_ci#endif 346862306a36Sopenharmony_ci case LOAD_MULTI: 346962306a36Sopenharmony_ci if (!address_ok(regs, ea, size)) 347062306a36Sopenharmony_ci return -EFAULT; 347162306a36Sopenharmony_ci rd = op->reg; 347262306a36Sopenharmony_ci for (i = 0; i < size; i += 4) { 347362306a36Sopenharmony_ci unsigned int v32 = 0; 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci nb = size - i; 347662306a36Sopenharmony_ci if (nb > 4) 347762306a36Sopenharmony_ci nb = 4; 347862306a36Sopenharmony_ci err = copy_mem_in((u8 *) &v32, ea, nb, regs); 347962306a36Sopenharmony_ci if (err) 348062306a36Sopenharmony_ci break; 348162306a36Sopenharmony_ci if (unlikely(cross_endian)) 348262306a36Sopenharmony_ci v32 = byterev_4(v32); 348362306a36Sopenharmony_ci regs->gpr[rd] = v32; 348462306a36Sopenharmony_ci ea += 4; 348562306a36Sopenharmony_ci /* reg number wraps from 31 to 0 for lsw[ix] */ 348662306a36Sopenharmony_ci rd = (rd + 1) & 0x1f; 348762306a36Sopenharmony_ci } 348862306a36Sopenharmony_ci break; 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_ci case STORE: 349162306a36Sopenharmony_ci#ifdef __powerpc64__ 349262306a36Sopenharmony_ci if (size == 16) { 349362306a36Sopenharmony_ci err = emulate_stq(regs, ea, op->reg, cross_endian); 349462306a36Sopenharmony_ci break; 349562306a36Sopenharmony_ci } 349662306a36Sopenharmony_ci#endif 349762306a36Sopenharmony_ci if ((op->type & UPDATE) && size == sizeof(long) && 349862306a36Sopenharmony_ci op->reg == 1 && op->update_reg == 1 && 349962306a36Sopenharmony_ci !(regs->msr & MSR_PR) && 350062306a36Sopenharmony_ci ea >= regs->gpr[1] - STACK_INT_FRAME_SIZE) { 350162306a36Sopenharmony_ci err = handle_stack_update(ea, regs); 350262306a36Sopenharmony_ci break; 350362306a36Sopenharmony_ci } 350462306a36Sopenharmony_ci if (unlikely(cross_endian)) 350562306a36Sopenharmony_ci do_byterev(&op->val, size); 350662306a36Sopenharmony_ci err = write_mem(op->val, ea, size, regs); 350762306a36Sopenharmony_ci break; 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci#ifdef CONFIG_PPC_FPU 351062306a36Sopenharmony_ci case STORE_FP: 351162306a36Sopenharmony_ci if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_FP)) 351262306a36Sopenharmony_ci return 0; 351362306a36Sopenharmony_ci err = do_fp_store(op, ea, regs, cross_endian); 351462306a36Sopenharmony_ci break; 351562306a36Sopenharmony_ci#endif 351662306a36Sopenharmony_ci#ifdef CONFIG_ALTIVEC 351762306a36Sopenharmony_ci case STORE_VMX: 351862306a36Sopenharmony_ci if (!(regs->msr & MSR_PR) && !(regs->msr & MSR_VEC)) 351962306a36Sopenharmony_ci return 0; 352062306a36Sopenharmony_ci err = do_vec_store(op->reg, ea, size, regs, cross_endian); 352162306a36Sopenharmony_ci break; 352262306a36Sopenharmony_ci#endif 352362306a36Sopenharmony_ci#ifdef CONFIG_VSX 352462306a36Sopenharmony_ci case STORE_VSX: { 352562306a36Sopenharmony_ci unsigned long msrbit = MSR_VSX; 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci /* 352862306a36Sopenharmony_ci * Some VSX instructions check the MSR_VEC bit rather than MSR_VSX 352962306a36Sopenharmony_ci * when the target of the instruction is a vector register. 353062306a36Sopenharmony_ci */ 353162306a36Sopenharmony_ci if (op->reg >= 32 && (op->vsx_flags & VSX_CHECK_VEC)) 353262306a36Sopenharmony_ci msrbit = MSR_VEC; 353362306a36Sopenharmony_ci if (!(regs->msr & MSR_PR) && !(regs->msr & msrbit)) 353462306a36Sopenharmony_ci return 0; 353562306a36Sopenharmony_ci err = do_vsx_store(op, ea, regs, cross_endian); 353662306a36Sopenharmony_ci break; 353762306a36Sopenharmony_ci } 353862306a36Sopenharmony_ci#endif 353962306a36Sopenharmony_ci case STORE_MULTI: 354062306a36Sopenharmony_ci if (!address_ok(regs, ea, size)) 354162306a36Sopenharmony_ci return -EFAULT; 354262306a36Sopenharmony_ci rd = op->reg; 354362306a36Sopenharmony_ci for (i = 0; i < size; i += 4) { 354462306a36Sopenharmony_ci unsigned int v32 = regs->gpr[rd]; 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ci nb = size - i; 354762306a36Sopenharmony_ci if (nb > 4) 354862306a36Sopenharmony_ci nb = 4; 354962306a36Sopenharmony_ci if (unlikely(cross_endian)) 355062306a36Sopenharmony_ci v32 = byterev_4(v32); 355162306a36Sopenharmony_ci err = copy_mem_out((u8 *) &v32, ea, nb, regs); 355262306a36Sopenharmony_ci if (err) 355362306a36Sopenharmony_ci break; 355462306a36Sopenharmony_ci ea += 4; 355562306a36Sopenharmony_ci /* reg number wraps from 31 to 0 for stsw[ix] */ 355662306a36Sopenharmony_ci rd = (rd + 1) & 0x1f; 355762306a36Sopenharmony_ci } 355862306a36Sopenharmony_ci break; 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_ci default: 356162306a36Sopenharmony_ci return -EINVAL; 356262306a36Sopenharmony_ci } 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci if (err) 356562306a36Sopenharmony_ci return err; 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci if (op->type & UPDATE) 356862306a36Sopenharmony_ci regs->gpr[op->update_reg] = op->ea; 356962306a36Sopenharmony_ci 357062306a36Sopenharmony_ci return 0; 357162306a36Sopenharmony_ci} 357262306a36Sopenharmony_ciNOKPROBE_SYMBOL(emulate_loadstore); 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ci/* 357562306a36Sopenharmony_ci * Emulate instructions that cause a transfer of control, 357662306a36Sopenharmony_ci * loads and stores, and a few other instructions. 357762306a36Sopenharmony_ci * Returns 1 if the step was emulated, 0 if not, 357862306a36Sopenharmony_ci * or -1 if the instruction is one that should not be stepped, 357962306a36Sopenharmony_ci * such as an rfid, or a mtmsrd that would clear MSR_RI. 358062306a36Sopenharmony_ci */ 358162306a36Sopenharmony_ciint emulate_step(struct pt_regs *regs, ppc_inst_t instr) 358262306a36Sopenharmony_ci{ 358362306a36Sopenharmony_ci struct instruction_op op; 358462306a36Sopenharmony_ci int r, err, type; 358562306a36Sopenharmony_ci unsigned long val; 358662306a36Sopenharmony_ci unsigned long ea; 358762306a36Sopenharmony_ci 358862306a36Sopenharmony_ci r = analyse_instr(&op, regs, instr); 358962306a36Sopenharmony_ci if (r < 0) 359062306a36Sopenharmony_ci return r; 359162306a36Sopenharmony_ci if (r > 0) { 359262306a36Sopenharmony_ci emulate_update_regs(regs, &op); 359362306a36Sopenharmony_ci return 1; 359462306a36Sopenharmony_ci } 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ci err = 0; 359762306a36Sopenharmony_ci type = GETTYPE(op.type); 359862306a36Sopenharmony_ci 359962306a36Sopenharmony_ci if (OP_IS_LOAD_STORE(type)) { 360062306a36Sopenharmony_ci err = emulate_loadstore(regs, &op); 360162306a36Sopenharmony_ci if (err) 360262306a36Sopenharmony_ci return 0; 360362306a36Sopenharmony_ci goto instr_done; 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci switch (type) { 360762306a36Sopenharmony_ci case CACHEOP: 360862306a36Sopenharmony_ci ea = truncate_if_32bit(regs->msr, op.ea); 360962306a36Sopenharmony_ci if (!address_ok(regs, ea, 8)) 361062306a36Sopenharmony_ci return 0; 361162306a36Sopenharmony_ci switch (op.type & CACHEOP_MASK) { 361262306a36Sopenharmony_ci case DCBST: 361362306a36Sopenharmony_ci __cacheop_user_asmx(ea, err, "dcbst"); 361462306a36Sopenharmony_ci break; 361562306a36Sopenharmony_ci case DCBF: 361662306a36Sopenharmony_ci __cacheop_user_asmx(ea, err, "dcbf"); 361762306a36Sopenharmony_ci break; 361862306a36Sopenharmony_ci case DCBTST: 361962306a36Sopenharmony_ci if (op.reg == 0) 362062306a36Sopenharmony_ci prefetchw((void *) ea); 362162306a36Sopenharmony_ci break; 362262306a36Sopenharmony_ci case DCBT: 362362306a36Sopenharmony_ci if (op.reg == 0) 362462306a36Sopenharmony_ci prefetch((void *) ea); 362562306a36Sopenharmony_ci break; 362662306a36Sopenharmony_ci case ICBI: 362762306a36Sopenharmony_ci __cacheop_user_asmx(ea, err, "icbi"); 362862306a36Sopenharmony_ci break; 362962306a36Sopenharmony_ci case DCBZ: 363062306a36Sopenharmony_ci err = emulate_dcbz(ea, regs); 363162306a36Sopenharmony_ci break; 363262306a36Sopenharmony_ci } 363362306a36Sopenharmony_ci if (err) { 363462306a36Sopenharmony_ci regs->dar = ea; 363562306a36Sopenharmony_ci return 0; 363662306a36Sopenharmony_ci } 363762306a36Sopenharmony_ci goto instr_done; 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_ci case MFMSR: 364062306a36Sopenharmony_ci regs->gpr[op.reg] = regs->msr & MSR_MASK; 364162306a36Sopenharmony_ci goto instr_done; 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_ci case MTMSR: 364462306a36Sopenharmony_ci val = regs->gpr[op.reg]; 364562306a36Sopenharmony_ci if ((val & MSR_RI) == 0) 364662306a36Sopenharmony_ci /* can't step mtmsr[d] that would clear MSR_RI */ 364762306a36Sopenharmony_ci return -1; 364862306a36Sopenharmony_ci /* here op.val is the mask of bits to change */ 364962306a36Sopenharmony_ci regs_set_return_msr(regs, (regs->msr & ~op.val) | (val & op.val)); 365062306a36Sopenharmony_ci goto instr_done; 365162306a36Sopenharmony_ci 365262306a36Sopenharmony_ci case SYSCALL: /* sc */ 365362306a36Sopenharmony_ci /* 365462306a36Sopenharmony_ci * Per ISA v3.1, section 7.5.15 'Trace Interrupt', we can't 365562306a36Sopenharmony_ci * single step a system call instruction: 365662306a36Sopenharmony_ci * 365762306a36Sopenharmony_ci * Successful completion for an instruction means that the 365862306a36Sopenharmony_ci * instruction caused no other interrupt. Thus a Trace 365962306a36Sopenharmony_ci * interrupt never occurs for a System Call or System Call 366062306a36Sopenharmony_ci * Vectored instruction, or for a Trap instruction that 366162306a36Sopenharmony_ci * traps. 366262306a36Sopenharmony_ci */ 366362306a36Sopenharmony_ci return -1; 366462306a36Sopenharmony_ci case SYSCALL_VECTORED_0: /* scv 0 */ 366562306a36Sopenharmony_ci return -1; 366662306a36Sopenharmony_ci case RFI: 366762306a36Sopenharmony_ci return -1; 366862306a36Sopenharmony_ci } 366962306a36Sopenharmony_ci return 0; 367062306a36Sopenharmony_ci 367162306a36Sopenharmony_ci instr_done: 367262306a36Sopenharmony_ci regs_set_return_ip(regs, 367362306a36Sopenharmony_ci truncate_if_32bit(regs->msr, regs->nip + GETLENGTH(op.type))); 367462306a36Sopenharmony_ci return 1; 367562306a36Sopenharmony_ci} 367662306a36Sopenharmony_ciNOKPROBE_SYMBOL(emulate_step); 3677