Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | /* |
| 3 | * Copyright 2008 Michael Ellerman, IBM Corporation. |
| 4 | */ |
| 5 | |
| 6 | #include <linux/vmalloc.h> |
| 7 | #include <linux/init.h> |
| 8 | |
| 9 | #include <asm/code-patching.h> |
| 10 | |
| 11 | static int __init instr_is_branch_to_addr(const u32 *instr, unsigned long addr) |
| 12 | { |
| 13 | if (instr_is_branch_iform(ppc_inst_read(instr)) || |
| 14 | instr_is_branch_bform(ppc_inst_read(instr))) |
| 15 | return branch_target(instr) == addr; |
| 16 | |
| 17 | return 0; |
| 18 | } |
| 19 | |
| 20 | static void __init test_trampoline(void) |
| 21 | { |
| 22 | asm ("nop;nop;\n"); |
| 23 | } |
| 24 | |
| 25 | #define check(x) do { \ |
| 26 | if (!(x)) \ |
| 27 | pr_err("code-patching: test failed at line %d\n", __LINE__); \ |
| 28 | } while (0) |
| 29 | |
| 30 | static void __init test_branch_iform(void) |
| 31 | { |
| 32 | int err; |
| 33 | ppc_inst_t instr; |
| 34 | u32 tmp[2]; |
| 35 | u32 *iptr = tmp; |
| 36 | unsigned long addr = (unsigned long)tmp; |
| 37 | |
| 38 | /* The simplest case, branch to self, no flags */ |
| 39 | check(instr_is_branch_iform(ppc_inst(0x48000000))); |
| 40 | /* All bits of target set, and flags */ |
| 41 | check(instr_is_branch_iform(ppc_inst(0x4bffffff))); |
| 42 | /* High bit of opcode set, which is wrong */ |
| 43 | check(!instr_is_branch_iform(ppc_inst(0xcbffffff))); |
| 44 | /* Middle bits of opcode set, which is wrong */ |
| 45 | check(!instr_is_branch_iform(ppc_inst(0x7bffffff))); |
| 46 | |
| 47 | /* Simplest case, branch to self with link */ |
| 48 | check(instr_is_branch_iform(ppc_inst(0x48000001))); |
| 49 | /* All bits of targets set */ |
| 50 | check(instr_is_branch_iform(ppc_inst(0x4bfffffd))); |
| 51 | /* Some bits of targets set */ |
| 52 | check(instr_is_branch_iform(ppc_inst(0x4bff00fd))); |
| 53 | /* Must be a valid branch to start with */ |
| 54 | check(!instr_is_branch_iform(ppc_inst(0x7bfffffd))); |
| 55 | |
| 56 | /* Absolute branch to 0x100 */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 57 | ppc_inst_write(iptr, ppc_inst(0x48000103)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 58 | check(instr_is_branch_to_addr(iptr, 0x100)); |
| 59 | /* Absolute branch to 0x420fc */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 60 | ppc_inst_write(iptr, ppc_inst(0x480420ff)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 61 | check(instr_is_branch_to_addr(iptr, 0x420fc)); |
| 62 | /* Maximum positive relative branch, + 20MB - 4B */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 63 | ppc_inst_write(iptr, ppc_inst(0x49fffffc)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 64 | check(instr_is_branch_to_addr(iptr, addr + 0x1FFFFFC)); |
| 65 | /* Smallest negative relative branch, - 4B */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 66 | ppc_inst_write(iptr, ppc_inst(0x4bfffffc)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 67 | check(instr_is_branch_to_addr(iptr, addr - 4)); |
| 68 | /* Largest negative relative branch, - 32 MB */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 69 | ppc_inst_write(iptr, ppc_inst(0x4a000000)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 70 | check(instr_is_branch_to_addr(iptr, addr - 0x2000000)); |
| 71 | |
| 72 | /* Branch to self, with link */ |
| 73 | err = create_branch(&instr, iptr, addr, BRANCH_SET_LINK); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 74 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 75 | check(instr_is_branch_to_addr(iptr, addr)); |
| 76 | |
| 77 | /* Branch to self - 0x100, with link */ |
| 78 | err = create_branch(&instr, iptr, addr - 0x100, BRANCH_SET_LINK); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 79 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 80 | check(instr_is_branch_to_addr(iptr, addr - 0x100)); |
| 81 | |
| 82 | /* Branch to self + 0x100, no link */ |
| 83 | err = create_branch(&instr, iptr, addr + 0x100, 0); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 84 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 85 | check(instr_is_branch_to_addr(iptr, addr + 0x100)); |
| 86 | |
| 87 | /* Maximum relative negative offset, - 32 MB */ |
| 88 | err = create_branch(&instr, iptr, addr - 0x2000000, BRANCH_SET_LINK); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 89 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 90 | check(instr_is_branch_to_addr(iptr, addr - 0x2000000)); |
| 91 | |
| 92 | /* Out of range relative negative offset, - 32 MB + 4*/ |
| 93 | err = create_branch(&instr, iptr, addr - 0x2000004, BRANCH_SET_LINK); |
| 94 | check(err); |
| 95 | |
| 96 | /* Out of range relative positive offset, + 32 MB */ |
| 97 | err = create_branch(&instr, iptr, addr + 0x2000000, BRANCH_SET_LINK); |
| 98 | check(err); |
| 99 | |
| 100 | /* Unaligned target */ |
| 101 | err = create_branch(&instr, iptr, addr + 3, BRANCH_SET_LINK); |
| 102 | check(err); |
| 103 | |
| 104 | /* Check flags are masked correctly */ |
| 105 | err = create_branch(&instr, iptr, addr, 0xFFFFFFFC); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 106 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 107 | check(instr_is_branch_to_addr(iptr, addr)); |
| 108 | check(ppc_inst_equal(instr, ppc_inst(0x48000000))); |
| 109 | } |
| 110 | |
| 111 | static void __init test_create_function_call(void) |
| 112 | { |
| 113 | u32 *iptr; |
| 114 | unsigned long dest; |
| 115 | ppc_inst_t instr; |
| 116 | |
| 117 | /* Check we can create a function call */ |
| 118 | iptr = (u32 *)ppc_function_entry(test_trampoline); |
| 119 | dest = ppc_function_entry(test_create_function_call); |
| 120 | create_branch(&instr, iptr, dest, BRANCH_SET_LINK); |
| 121 | patch_instruction(iptr, instr); |
| 122 | check(instr_is_branch_to_addr(iptr, dest)); |
| 123 | } |
| 124 | |
| 125 | static void __init test_branch_bform(void) |
| 126 | { |
| 127 | int err; |
| 128 | unsigned long addr; |
| 129 | ppc_inst_t instr; |
| 130 | u32 tmp[2]; |
| 131 | u32 *iptr = tmp; |
| 132 | unsigned int flags; |
| 133 | |
| 134 | addr = (unsigned long)iptr; |
| 135 | |
| 136 | /* The simplest case, branch to self, no flags */ |
| 137 | check(instr_is_branch_bform(ppc_inst(0x40000000))); |
| 138 | /* All bits of target set, and flags */ |
| 139 | check(instr_is_branch_bform(ppc_inst(0x43ffffff))); |
| 140 | /* High bit of opcode set, which is wrong */ |
| 141 | check(!instr_is_branch_bform(ppc_inst(0xc3ffffff))); |
| 142 | /* Middle bits of opcode set, which is wrong */ |
| 143 | check(!instr_is_branch_bform(ppc_inst(0x7bffffff))); |
| 144 | |
| 145 | /* Absolute conditional branch to 0x100 */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 146 | ppc_inst_write(iptr, ppc_inst(0x43ff0103)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 147 | check(instr_is_branch_to_addr(iptr, 0x100)); |
| 148 | /* Absolute conditional branch to 0x20fc */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 149 | ppc_inst_write(iptr, ppc_inst(0x43ff20ff)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 150 | check(instr_is_branch_to_addr(iptr, 0x20fc)); |
| 151 | /* Maximum positive relative conditional branch, + 32 KB - 4B */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 152 | ppc_inst_write(iptr, ppc_inst(0x43ff7ffc)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 153 | check(instr_is_branch_to_addr(iptr, addr + 0x7FFC)); |
| 154 | /* Smallest negative relative conditional branch, - 4B */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 155 | ppc_inst_write(iptr, ppc_inst(0x43fffffc)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 156 | check(instr_is_branch_to_addr(iptr, addr - 4)); |
| 157 | /* Largest negative relative conditional branch, - 32 KB */ |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 158 | ppc_inst_write(iptr, ppc_inst(0x43ff8000)); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 159 | check(instr_is_branch_to_addr(iptr, addr - 0x8000)); |
| 160 | |
| 161 | /* All condition code bits set & link */ |
| 162 | flags = 0x3ff000 | BRANCH_SET_LINK; |
| 163 | |
| 164 | /* Branch to self */ |
| 165 | err = create_cond_branch(&instr, iptr, addr, flags); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 166 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 167 | check(instr_is_branch_to_addr(iptr, addr)); |
| 168 | |
| 169 | /* Branch to self - 0x100 */ |
| 170 | err = create_cond_branch(&instr, iptr, addr - 0x100, flags); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 171 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 172 | check(instr_is_branch_to_addr(iptr, addr - 0x100)); |
| 173 | |
| 174 | /* Branch to self + 0x100 */ |
| 175 | err = create_cond_branch(&instr, iptr, addr + 0x100, flags); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 176 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 177 | check(instr_is_branch_to_addr(iptr, addr + 0x100)); |
| 178 | |
| 179 | /* Maximum relative negative offset, - 32 KB */ |
| 180 | err = create_cond_branch(&instr, iptr, addr - 0x8000, flags); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 181 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 182 | check(instr_is_branch_to_addr(iptr, addr - 0x8000)); |
| 183 | |
| 184 | /* Out of range relative negative offset, - 32 KB + 4*/ |
| 185 | err = create_cond_branch(&instr, iptr, addr - 0x8004, flags); |
| 186 | check(err); |
| 187 | |
| 188 | /* Out of range relative positive offset, + 32 KB */ |
| 189 | err = create_cond_branch(&instr, iptr, addr + 0x8000, flags); |
| 190 | check(err); |
| 191 | |
| 192 | /* Unaligned target */ |
| 193 | err = create_cond_branch(&instr, iptr, addr + 3, flags); |
| 194 | check(err); |
| 195 | |
| 196 | /* Check flags are masked correctly */ |
| 197 | err = create_cond_branch(&instr, iptr, addr, 0xFFFFFFFC); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 198 | ppc_inst_write(iptr, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 199 | check(instr_is_branch_to_addr(iptr, addr)); |
| 200 | check(ppc_inst_equal(instr, ppc_inst(0x43FF0000))); |
| 201 | } |
| 202 | |
| 203 | static void __init test_translate_branch(void) |
| 204 | { |
| 205 | unsigned long addr; |
| 206 | void *p, *q; |
| 207 | ppc_inst_t instr; |
| 208 | void *buf; |
| 209 | |
| 210 | buf = vmalloc(PAGE_ALIGN(0x2000000 + 1)); |
| 211 | check(buf); |
| 212 | if (!buf) |
| 213 | return; |
| 214 | |
| 215 | /* Simple case, branch to self moved a little */ |
| 216 | p = buf; |
| 217 | addr = (unsigned long)p; |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 218 | create_branch(&instr, p, addr, 0); |
| 219 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 220 | check(instr_is_branch_to_addr(p, addr)); |
| 221 | q = p + 4; |
| 222 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 223 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 224 | check(instr_is_branch_to_addr(q, addr)); |
| 225 | |
| 226 | /* Maximum negative case, move b . to addr + 32 MB */ |
| 227 | p = buf; |
| 228 | addr = (unsigned long)p; |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 229 | create_branch(&instr, p, addr, 0); |
| 230 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 231 | q = buf + 0x2000000; |
| 232 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 233 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 234 | check(instr_is_branch_to_addr(p, addr)); |
| 235 | check(instr_is_branch_to_addr(q, addr)); |
| 236 | check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x4a000000))); |
| 237 | |
| 238 | /* Maximum positive case, move x to x - 32 MB + 4 */ |
| 239 | p = buf + 0x2000000; |
| 240 | addr = (unsigned long)p; |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 241 | create_branch(&instr, p, addr, 0); |
| 242 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 243 | q = buf + 4; |
| 244 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 245 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 246 | check(instr_is_branch_to_addr(p, addr)); |
| 247 | check(instr_is_branch_to_addr(q, addr)); |
| 248 | check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x49fffffc))); |
| 249 | |
| 250 | /* Jump to x + 16 MB moved to x + 20 MB */ |
| 251 | p = buf; |
| 252 | addr = 0x1000000 + (unsigned long)buf; |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 253 | create_branch(&instr, p, addr, BRANCH_SET_LINK); |
| 254 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 255 | q = buf + 0x1400000; |
| 256 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 257 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 258 | check(instr_is_branch_to_addr(p, addr)); |
| 259 | check(instr_is_branch_to_addr(q, addr)); |
| 260 | |
| 261 | /* Jump to x + 16 MB moved to x - 16 MB + 4 */ |
| 262 | p = buf + 0x1000000; |
| 263 | addr = 0x2000000 + (unsigned long)buf; |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 264 | create_branch(&instr, p, addr, 0); |
| 265 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 266 | q = buf + 4; |
| 267 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 268 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 269 | check(instr_is_branch_to_addr(p, addr)); |
| 270 | check(instr_is_branch_to_addr(q, addr)); |
| 271 | |
| 272 | |
| 273 | /* Conditional branch tests */ |
| 274 | |
| 275 | /* Simple case, branch to self moved a little */ |
| 276 | p = buf; |
| 277 | addr = (unsigned long)p; |
| 278 | create_cond_branch(&instr, p, addr, 0); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 279 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 280 | check(instr_is_branch_to_addr(p, addr)); |
| 281 | q = buf + 4; |
| 282 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 283 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 284 | check(instr_is_branch_to_addr(q, addr)); |
| 285 | |
| 286 | /* Maximum negative case, move b . to addr + 32 KB */ |
| 287 | p = buf; |
| 288 | addr = (unsigned long)p; |
| 289 | create_cond_branch(&instr, p, addr, 0xFFFFFFFC); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 290 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 291 | q = buf + 0x8000; |
| 292 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 293 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 294 | check(instr_is_branch_to_addr(p, addr)); |
| 295 | check(instr_is_branch_to_addr(q, addr)); |
| 296 | check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x43ff8000))); |
| 297 | |
| 298 | /* Maximum positive case, move x to x - 32 KB + 4 */ |
| 299 | p = buf + 0x8000; |
| 300 | addr = (unsigned long)p; |
| 301 | create_cond_branch(&instr, p, addr, 0xFFFFFFFC); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 302 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 303 | q = buf + 4; |
| 304 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 305 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 306 | check(instr_is_branch_to_addr(p, addr)); |
| 307 | check(instr_is_branch_to_addr(q, addr)); |
| 308 | check(ppc_inst_equal(ppc_inst_read(q), ppc_inst(0x43ff7ffc))); |
| 309 | |
| 310 | /* Jump to x + 12 KB moved to x + 20 KB */ |
| 311 | p = buf; |
| 312 | addr = 0x3000 + (unsigned long)buf; |
| 313 | create_cond_branch(&instr, p, addr, BRANCH_SET_LINK); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 314 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 315 | q = buf + 0x5000; |
| 316 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 317 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 318 | check(instr_is_branch_to_addr(p, addr)); |
| 319 | check(instr_is_branch_to_addr(q, addr)); |
| 320 | |
| 321 | /* Jump to x + 8 KB moved to x - 8 KB + 4 */ |
| 322 | p = buf + 0x2000; |
| 323 | addr = 0x4000 + (unsigned long)buf; |
| 324 | create_cond_branch(&instr, p, addr, 0); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 325 | ppc_inst_write(p, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 326 | q = buf + 4; |
| 327 | translate_branch(&instr, q, p); |
Christophe Leroy | 309a0a6 | 2021-12-02 13:00:27 +0100 | [diff] [blame] | 328 | ppc_inst_write(q, instr); |
Christophe Leroy | f30a578 | 2021-12-02 13:00:26 +0100 | [diff] [blame] | 329 | check(instr_is_branch_to_addr(p, addr)); |
| 330 | check(instr_is_branch_to_addr(q, addr)); |
| 331 | |
| 332 | /* Free the buffer we were using */ |
| 333 | vfree(buf); |
| 334 | } |
| 335 | |
| 336 | static void __init test_prefixed_patching(void) |
| 337 | { |
| 338 | u32 *iptr = (u32 *)ppc_function_entry(test_trampoline); |
| 339 | u32 expected[2] = {OP_PREFIX << 26, 0}; |
| 340 | ppc_inst_t inst = ppc_inst_prefix(OP_PREFIX << 26, 0); |
| 341 | |
| 342 | if (!IS_ENABLED(CONFIG_PPC64)) |
| 343 | return; |
| 344 | |
| 345 | patch_instruction(iptr, inst); |
| 346 | |
| 347 | check(!memcmp(iptr, expected, sizeof(expected))); |
| 348 | } |
| 349 | |
| 350 | static int __init test_code_patching(void) |
| 351 | { |
| 352 | pr_info("Running code patching self-tests ...\n"); |
| 353 | |
| 354 | test_branch_iform(); |
| 355 | test_branch_bform(); |
| 356 | test_create_function_call(); |
| 357 | test_translate_branch(); |
| 358 | test_prefixed_patching(); |
| 359 | |
| 360 | return 0; |
| 361 | } |
| 362 | late_initcall(test_code_patching); |