diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2015-03-27 09:09:21 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2015-03-30 06:03:42 -0400 |
commit | 0978fb25f86b7595821cee6955679250d47c6438 (patch) | |
tree | 3f4bafcc51a1df7f4adac354bd8956f77bd66e58 /arch/arm64 | |
parent | 849176c96dab827548176e851525bd1c3fff5a6a (diff) |
arm64: insn: Add aarch64_insn_decode_immediate
Patching an instruction sometimes requires extracting the immediate
field from this instruction. To facilitate this, and avoid
potential duplication of code, add aarch64_insn_decode_immediate
as the reciprocal to aarch64_insn_encode_immediate.
Acked-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm64')
-rw-r--r-- | arch/arm64/include/asm/insn.h | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/insn.c | 81 |
2 files changed, 66 insertions, 16 deletions
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index d2f49423c5dc..f81b328d9cf4 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h | |||
@@ -285,6 +285,7 @@ bool aarch64_insn_is_nop(u32 insn); | |||
285 | int aarch64_insn_read(void *addr, u32 *insnp); | 285 | int aarch64_insn_read(void *addr, u32 *insnp); |
286 | int aarch64_insn_write(void *addr, u32 insn); | 286 | int aarch64_insn_write(void *addr, u32 insn); |
287 | enum aarch64_insn_encoding_class aarch64_get_insn_class(u32 insn); | 287 | enum aarch64_insn_encoding_class aarch64_get_insn_class(u32 insn); |
288 | u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn); | ||
288 | u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | 289 | u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, |
289 | u32 insn, u64 imm); | 290 | u32 insn, u64 imm); |
290 | u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | 291 | u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, |
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index c8eca88f12e6..924902083e47 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c | |||
@@ -265,23 +265,13 @@ int __kprobes aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt) | |||
265 | return aarch64_insn_patch_text_sync(addrs, insns, cnt); | 265 | return aarch64_insn_patch_text_sync(addrs, insns, cnt); |
266 | } | 266 | } |
267 | 267 | ||
268 | u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | 268 | static int __kprobes aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type, |
269 | u32 insn, u64 imm) | 269 | u32 *maskp, int *shiftp) |
270 | { | 270 | { |
271 | u32 immlo, immhi, lomask, himask, mask; | 271 | u32 mask; |
272 | int shift; | 272 | int shift; |
273 | 273 | ||
274 | switch (type) { | 274 | switch (type) { |
275 | case AARCH64_INSN_IMM_ADR: | ||
276 | lomask = 0x3; | ||
277 | himask = 0x7ffff; | ||
278 | immlo = imm & lomask; | ||
279 | imm >>= 2; | ||
280 | immhi = imm & himask; | ||
281 | imm = (immlo << 24) | (immhi); | ||
282 | mask = (lomask << 24) | (himask); | ||
283 | shift = 5; | ||
284 | break; | ||
285 | case AARCH64_INSN_IMM_26: | 275 | case AARCH64_INSN_IMM_26: |
286 | mask = BIT(26) - 1; | 276 | mask = BIT(26) - 1; |
287 | shift = 0; | 277 | shift = 0; |
@@ -320,9 +310,68 @@ u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | |||
320 | shift = 16; | 310 | shift = 16; |
321 | break; | 311 | break; |
322 | default: | 312 | default: |
323 | pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n", | 313 | return -EINVAL; |
324 | type); | 314 | } |
325 | return 0; | 315 | |
316 | *maskp = mask; | ||
317 | *shiftp = shift; | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | #define ADR_IMM_HILOSPLIT 2 | ||
323 | #define ADR_IMM_SIZE SZ_2M | ||
324 | #define ADR_IMM_LOMASK ((1 << ADR_IMM_HILOSPLIT) - 1) | ||
325 | #define ADR_IMM_HIMASK ((ADR_IMM_SIZE >> ADR_IMM_HILOSPLIT) - 1) | ||
326 | #define ADR_IMM_LOSHIFT 29 | ||
327 | #define ADR_IMM_HISHIFT 5 | ||
328 | |||
329 | u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn) | ||
330 | { | ||
331 | u32 immlo, immhi, mask; | ||
332 | int shift; | ||
333 | |||
334 | switch (type) { | ||
335 | case AARCH64_INSN_IMM_ADR: | ||
336 | shift = 0; | ||
337 | immlo = (insn >> ADR_IMM_LOSHIFT) & ADR_IMM_LOMASK; | ||
338 | immhi = (insn >> ADR_IMM_HISHIFT) & ADR_IMM_HIMASK; | ||
339 | insn = (immhi << ADR_IMM_HILOSPLIT) | immlo; | ||
340 | mask = ADR_IMM_SIZE - 1; | ||
341 | break; | ||
342 | default: | ||
343 | if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { | ||
344 | pr_err("aarch64_insn_decode_immediate: unknown immediate encoding %d\n", | ||
345 | type); | ||
346 | return 0; | ||
347 | } | ||
348 | } | ||
349 | |||
350 | return (insn >> shift) & mask; | ||
351 | } | ||
352 | |||
353 | u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | ||
354 | u32 insn, u64 imm) | ||
355 | { | ||
356 | u32 immlo, immhi, mask; | ||
357 | int shift; | ||
358 | |||
359 | switch (type) { | ||
360 | case AARCH64_INSN_IMM_ADR: | ||
361 | shift = 0; | ||
362 | immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT; | ||
363 | imm >>= ADR_IMM_HILOSPLIT; | ||
364 | immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT; | ||
365 | imm = immlo | immhi; | ||
366 | mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) | | ||
367 | (ADR_IMM_HIMASK << ADR_IMM_HISHIFT)); | ||
368 | break; | ||
369 | default: | ||
370 | if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { | ||
371 | pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n", | ||
372 | type); | ||
373 | return 0; | ||
374 | } | ||
326 | } | 375 | } |
327 | 376 | ||
328 | /* Update the immediate field. */ | 377 | /* Update the immediate field. */ |