aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPunit Agrawal <punit.agrawal@arm.com>2014-11-18 06:41:26 -0500
committerWill Deacon <will.deacon@arm.com>2014-11-20 11:34:48 -0500
commitc852f320584600a372646055d8229e063949eee7 (patch)
tree57f88ee08e59727db2ac8d305ad89f15990b4ecd
parentbd35a4adc4131c530ec7d90242555eac7b3dbe3f (diff)
arm64: Emulate CP15 Barrier instructions
The CP15 barrier instructions (CP15ISB, CP15DSB and CP15DMB) are deprecated in the ARMv7 architecture, superseded by ISB, DSB and DMB instructions respectively. Some implementations may provide the ability to disable the CP15 barriers by disabling the CP15BEN bit in SCTLR_EL1. If not enabled, the encodings for these instructions become undefined. To support legacy software using these instructions, this patch register hooks to - * emulate CP15 barriers and warn the user about their use * toggle CP15BEN in SCTLR_EL1 Signed-off-by: Punit Agrawal <punit.agrawal@arm.com> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--Documentation/arm64/legacy_instructions.txt5
-rw-r--r--arch/arm64/Kconfig15
-rw-r--r--arch/arm64/include/asm/insn.h2
-rw-r--r--arch/arm64/kernel/armv8_deprecated.c131
-rw-r--r--arch/arm64/kernel/insn.c13
5 files changed, 166 insertions, 0 deletions
diff --git a/Documentation/arm64/legacy_instructions.txt b/Documentation/arm64/legacy_instructions.txt
index 5ab58614b7ed..a3b3da2ec6ed 100644
--- a/Documentation/arm64/legacy_instructions.txt
+++ b/Documentation/arm64/legacy_instructions.txt
@@ -38,3 +38,8 @@ Supported legacy instructions
38Node: /proc/sys/abi/swp 38Node: /proc/sys/abi/swp
39Status: Obsolete 39Status: Obsolete
40Default: Undef (0) 40Default: Undef (0)
41
42* CP15 Barriers
43Node: /proc/sys/abi/cp15_barrier
44Status: Deprecated
45Default: Emulate (1)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 2b6213840ec8..8e74dfe1e74d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -201,6 +201,21 @@ config SWP_EMULATION
201 201
202 If unsure, say Y 202 If unsure, say Y
203 203
204config CP15_BARRIER_EMULATION
205 bool "Emulate CP15 Barrier instructions"
206 help
207 The CP15 barrier instructions - CP15ISB, CP15DSB, and
208 CP15DMB - are deprecated in ARMv8 (and ARMv7). It is
209 strongly recommended to use the ISB, DSB, and DMB
210 instructions instead.
211
212 Say Y here to enable software emulation of these
213 instructions for AArch32 userspace code. When this option is
214 enabled, CP15 barrier usage is traced which can help
215 identify software that needs updating.
216
217 If unsure, say Y
218
204endif 219endif
205 220
206endmenu 221endmenu
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index 3ecc57c47c04..e2ff32a93b5c 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -362,6 +362,8 @@ bool aarch32_insn_is_wide(u32 insn);
362#define A32_RT2_OFFSET 0 362#define A32_RT2_OFFSET 0
363 363
364u32 aarch32_insn_extract_reg_num(u32 insn, int offset); 364u32 aarch32_insn_extract_reg_num(u32 insn, int offset);
365u32 aarch32_insn_mcr_extract_opc2(u32 insn);
366u32 aarch32_insn_mcr_extract_crm(u32 insn);
365#endif /* __ASSEMBLY__ */ 367#endif /* __ASSEMBLY__ */
366 368
367#endif /* __ASM_INSN_H */ 369#endif /* __ASM_INSN_H */
diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c
index 98865a7868f1..401c2e544ede 100644
--- a/arch/arm64/kernel/armv8_deprecated.c
+++ b/arch/arm64/kernel/armv8_deprecated.c
@@ -6,6 +6,7 @@
6 * published by the Free Software Foundation. 6 * published by the Free Software Foundation.
7 */ 7 */
8 8
9#include <linux/cpu.h>
9#include <linux/init.h> 10#include <linux/init.h>
10#include <linux/list.h> 11#include <linux/list.h>
11#include <linux/perf_event.h> 12#include <linux/perf_event.h>
@@ -391,6 +392,133 @@ static struct insn_emulation_ops swp_ops = {
391 .set_hw_mode = NULL, 392 .set_hw_mode = NULL,
392}; 393};
393 394
395static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
396{
397 perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
398
399 switch (arm_check_condition(instr, regs->pstate)) {
400 case ARM_OPCODE_CONDTEST_PASS:
401 break;
402 case ARM_OPCODE_CONDTEST_FAIL:
403 /* Condition failed - return to next instruction */
404 goto ret;
405 case ARM_OPCODE_CONDTEST_UNCOND:
406 /* If unconditional encoding - not a barrier instruction */
407 return -EFAULT;
408 default:
409 return -EINVAL;
410 }
411
412 switch (aarch32_insn_mcr_extract_crm(instr)) {
413 case 10:
414 /*
415 * dmb - mcr p15, 0, Rt, c7, c10, 5
416 * dsb - mcr p15, 0, Rt, c7, c10, 4
417 */
418 if (aarch32_insn_mcr_extract_opc2(instr) == 5)
419 dmb(sy);
420 else
421 dsb(sy);
422 break;
423 case 5:
424 /*
425 * isb - mcr p15, 0, Rt, c7, c5, 4
426 *
427 * Taking an exception or returning from one acts as an
428 * instruction barrier. So no explicit barrier needed here.
429 */
430 break;
431 }
432
433ret:
434 pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n",
435 current->comm, (unsigned long)current->pid, regs->pc);
436
437 regs->pc += 4;
438 return 0;
439}
440
441#define SCTLR_EL1_CP15BEN (1 << 5)
442
443static inline void config_sctlr_el1(u32 clear, u32 set)
444{
445 u32 val;
446
447 asm volatile("mrs %0, sctlr_el1" : "=r" (val));
448 val &= ~clear;
449 val |= set;
450 asm volatile("msr sctlr_el1, %0" : : "r" (val));
451}
452
453static void enable_cp15_ben(void *info)
454{
455 config_sctlr_el1(0, SCTLR_EL1_CP15BEN);
456}
457
458static void disable_cp15_ben(void *info)
459{
460 config_sctlr_el1(SCTLR_EL1_CP15BEN, 0);
461}
462
463static int cpu_hotplug_notify(struct notifier_block *b,
464 unsigned long action, void *hcpu)
465{
466 switch (action) {
467 case CPU_STARTING:
468 case CPU_STARTING_FROZEN:
469 enable_cp15_ben(NULL);
470 return NOTIFY_DONE;
471 case CPU_DYING:
472 case CPU_DYING_FROZEN:
473 disable_cp15_ben(NULL);
474 return NOTIFY_DONE;
475 }
476
477 return NOTIFY_OK;
478}
479
480static struct notifier_block cpu_hotplug_notifier = {
481 .notifier_call = cpu_hotplug_notify,
482};
483
484static int cp15_barrier_set_hw_mode(bool enable)
485{
486 if (enable) {
487 register_cpu_notifier(&cpu_hotplug_notifier);
488 on_each_cpu(enable_cp15_ben, NULL, true);
489 } else {
490 unregister_cpu_notifier(&cpu_hotplug_notifier);
491 on_each_cpu(disable_cp15_ben, NULL, true);
492 }
493
494 return true;
495}
496
497static struct undef_hook cp15_barrier_hooks[] = {
498 {
499 .instr_mask = 0x0fff0fdf,
500 .instr_val = 0x0e070f9a,
501 .pstate_mask = COMPAT_PSR_MODE_MASK,
502 .pstate_val = COMPAT_PSR_MODE_USR,
503 .fn = cp15barrier_handler,
504 },
505 {
506 .instr_mask = 0x0fff0fff,
507 .instr_val = 0x0e070f95,
508 .pstate_mask = COMPAT_PSR_MODE_MASK,
509 .pstate_val = COMPAT_PSR_MODE_USR,
510 .fn = cp15barrier_handler,
511 },
512 { }
513};
514
515static struct insn_emulation_ops cp15_barrier_ops = {
516 .name = "cp15_barrier",
517 .status = INSN_DEPRECATED,
518 .hooks = cp15_barrier_hooks,
519 .set_hw_mode = cp15_barrier_set_hw_mode,
520};
521
394/* 522/*
395 * Invoked as late_initcall, since not needed before init spawned. 523 * Invoked as late_initcall, since not needed before init spawned.
396 */ 524 */
@@ -399,6 +527,9 @@ static int __init armv8_deprecated_init(void)
399 if (IS_ENABLED(CONFIG_SWP_EMULATION)) 527 if (IS_ENABLED(CONFIG_SWP_EMULATION))
400 register_insn_emulation(&swp_ops); 528 register_insn_emulation(&swp_ops);
401 529
530 if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION))
531 register_insn_emulation(&cp15_barrier_ops);
532
402 register_insn_emulation_sysctl(ctl_abi); 533 register_insn_emulation_sysctl(ctl_abi);
403 534
404 return 0; 535 return 0;
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 63122dcd8524..819e409029ce 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -972,3 +972,16 @@ u32 aarch32_insn_extract_reg_num(u32 insn, int offset)
972{ 972{
973 return (insn & (0xf << offset)) >> offset; 973 return (insn & (0xf << offset)) >> offset;
974} 974}
975
976#define OPC2_MASK 0x7
977#define OPC2_OFFSET 5
978u32 aarch32_insn_mcr_extract_opc2(u32 insn)
979{
980 return (insn & (OPC2_MASK << OPC2_OFFSET)) >> OPC2_OFFSET;
981}
982
983#define CRM_MASK 0xf
984u32 aarch32_insn_mcr_extract_crm(u32 insn)
985{
986 return insn & CRM_MASK;
987}