1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 #ifndef _ASM_POWERPC_INST_H 3 #define _ASM_POWERPC_INST_H 4 5 #include <asm/ppc-opcode.h> 6 #include <asm/reg.h> 7 #include <asm/disassemble.h> 8 #include <asm/uaccess.h> 9 10 #define ___get_user_instr(gu_op, dest, ptr) \ 11 ({ \ 12 long __gui_ret; \ 13 u32 __user *__gui_ptr = (u32 __user *)ptr; \ 14 ppc_inst_t __gui_inst; \ 15 unsigned int __prefix, __suffix; \ 16 \ 17 __chk_user_ptr(ptr); \ 18 __gui_ret = gu_op(__prefix, __gui_ptr); \ 19 if (__gui_ret == 0) { \ 20 if (IS_ENABLED(CONFIG_PPC64) && (__prefix >> 26) == OP_PREFIX) { \ 21 __gui_ret = gu_op(__suffix, __gui_ptr + 1); \ 22 __gui_inst = ppc_inst_prefix(__prefix, __suffix); \ 23 } else { \ 24 __gui_inst = ppc_inst(__prefix); \ 25 } \ 26 if (__gui_ret == 0) \ 27 (dest) = __gui_inst; \ 28 } \ 29 __gui_ret; \ 30 }) 31 32 #define get_user_instr(x, ptr) ___get_user_instr(get_user, x, ptr) 33 34 #define __get_user_instr(x, ptr) ___get_user_instr(__get_user, x, ptr) 35 36 /* 37 * Instruction data type for POWER 38 */ 39 40 #if defined(CONFIG_PPC64) || defined(__CHECKER__) 41 static inline u32 ppc_inst_val(ppc_inst_t x) 42 { 43 return x.val; 44 } 45 46 #define ppc_inst(x) ((ppc_inst_t){ .val = (x) }) 47 48 #else 49 static inline u32 ppc_inst_val(ppc_inst_t x) 50 { 51 return x; 52 } 53 #define ppc_inst(x) (x) 54 #endif 55 56 static inline int ppc_inst_primary_opcode(ppc_inst_t x) 57 { 58 return ppc_inst_val(x) >> 26; 59 } 60 61 #ifdef CONFIG_PPC64 62 #define ppc_inst_prefix(x, y) ((ppc_inst_t){ .val = (x), .suffix = (y) }) 63 64 static inline u32 ppc_inst_suffix(ppc_inst_t x) 65 { 66 return x.suffix; 67 } 68 69 #else 70 #define ppc_inst_prefix(x, y) ((void)y, ppc_inst(x)) 71 72 static inline u32 ppc_inst_suffix(ppc_inst_t x) 73 { 74 return 0; 75 } 76 77 #endif /* CONFIG_PPC64 */ 78 79 static inline ppc_inst_t ppc_inst_read(const u32 *ptr) 80 { 81 if (IS_ENABLED(CONFIG_PPC64) && (*ptr >> 26) == OP_PREFIX) 82 return ppc_inst_prefix(*ptr, *(ptr + 1)); 83 else 84 return ppc_inst(*ptr); 85 } 86 87 static inline bool ppc_inst_prefixed(ppc_inst_t x) 88 { 89 return IS_ENABLED(CONFIG_PPC64) && ppc_inst_primary_opcode(x) == OP_PREFIX; 90 } 91 92 static inline ppc_inst_t ppc_inst_swab(ppc_inst_t x) 93 { 94 return ppc_inst_prefix(swab32(ppc_inst_val(x)), swab32(ppc_inst_suffix(x))); 95 } 96 97 static inline bool ppc_inst_equal(ppc_inst_t x, ppc_inst_t y) 98 { 99 if (ppc_inst_val(x) != ppc_inst_val(y)) 100 return false; 101 if (!ppc_inst_prefixed(x)) 102 return true; 103 return ppc_inst_suffix(x) == ppc_inst_suffix(y); 104 } 105 106 static inline int ppc_inst_len(ppc_inst_t x) 107 { 108 return ppc_inst_prefixed(x) ? 8 : 4; 109 } 110 111 /* 112 * Return the address of the next instruction, if the instruction @value was 113 * located at @location. 114 */ 115 static inline u32 *ppc_inst_next(u32 *location, u32 *value) 116 { 117 ppc_inst_t tmp; 118 119 tmp = ppc_inst_read(value); 120 121 return (void *)location + ppc_inst_len(tmp); 122 } 123 124 static inline unsigned long ppc_inst_as_ulong(ppc_inst_t x) 125 { 126 if (IS_ENABLED(CONFIG_PPC32)) 127 return ppc_inst_val(x); 128 else if (IS_ENABLED(CONFIG_CPU_LITTLE_ENDIAN)) 129 return (u64)ppc_inst_suffix(x) << 32 | ppc_inst_val(x); 130 else 131 return (u64)ppc_inst_val(x) << 32 | ppc_inst_suffix(x); 132 } 133 134 static inline void ppc_inst_write(u32 *ptr, ppc_inst_t x) 135 { 136 if (!ppc_inst_prefixed(x)) 137 *ptr = ppc_inst_val(x); 138 else 139 *(u64 *)ptr = ppc_inst_as_ulong(x); 140 } 141 142 static inline int __copy_inst_from_kernel_nofault(ppc_inst_t *inst, u32 *src) 143 { 144 unsigned int val, suffix; 145 146 /* See https://github.com/ClangBuiltLinux/linux/issues/1521 */ 147 #if defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION < 140000 148 val = suffix = 0; 149 #endif 150 __get_kernel_nofault(&val, src, u32, Efault); 151 if (IS_ENABLED(CONFIG_PPC64) && get_op(val) == OP_PREFIX) { 152 __get_kernel_nofault(&suffix, src + 1, u32, Efault); 153 *inst = ppc_inst_prefix(val, suffix); 154 } else { 155 *inst = ppc_inst(val); 156 } 157 return 0; 158 Efault: 159 return -EFAULT; 160 } 161 162 static inline int copy_inst_from_kernel_nofault(ppc_inst_t *inst, u32 *src) 163 { 164 if (unlikely(!is_kernel_addr((unsigned long)src))) 165 return -ERANGE; 166 167 return __copy_inst_from_kernel_nofault(inst, src); 168 } 169 170 #endif /* _ASM_POWERPC_INST_H */ 171
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.