diff options
author | Suzuki K Poulose <suzuki.poulose@arm.com> | 2016-09-09 09:07:15 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2016-09-09 10:03:29 -0400 |
commit | 9dbd5bb25c56e35e6b4c34d968689a1ded850924 (patch) | |
tree | f81f727180de3d8eef52c1d69d72a46cc196eea5 /arch/arm64/kernel/traps.c | |
parent | 072f0a633838aca13b5a8b211eb64f5c445cfd7c (diff) |
arm64: Refactor sysinstr exception handling
Right now we trap some of the user space data cache operations
based on a few Errata (ARM 819472, 826319, 827319 and 824069).
We need to trap userspace access to CTR_EL0, if we detect mismatched
cache line size. Since both these traps share the EC, refactor
the handler a little bit to make it a bit more reader friendly.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm64/kernel/traps.c')
-rw-r--r-- | arch/arm64/kernel/traps.c | 73 |
1 files changed, 47 insertions, 26 deletions
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index e04f83873af7..224f64eddd93 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c | |||
@@ -447,36 +447,29 @@ void cpu_enable_cache_maint_trap(void *__unused) | |||
447 | : "=r" (res) \ | 447 | : "=r" (res) \ |
448 | : "r" (address), "i" (-EFAULT) ) | 448 | : "r" (address), "i" (-EFAULT) ) |
449 | 449 | ||
450 | asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs) | 450 | static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs) |
451 | { | 451 | { |
452 | unsigned long address; | 452 | unsigned long address; |
453 | int ret; | 453 | int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT; |
454 | int crm = (esr & ESR_ELx_SYS64_ISS_CRM_MASK) >> ESR_ELx_SYS64_ISS_CRM_SHIFT; | ||
455 | int ret = 0; | ||
454 | 456 | ||
455 | /* if this is a write with: Op0=1, Op2=1, Op1=3, CRn=7 */ | 457 | address = (rt == 31) ? 0 : regs->regs[rt]; |
456 | if ((esr & 0x01fffc01) == 0x0012dc00) { | ||
457 | int rt = (esr >> 5) & 0x1f; | ||
458 | int crm = (esr >> 1) & 0x0f; | ||
459 | 458 | ||
460 | address = (rt == 31) ? 0 : regs->regs[rt]; | 459 | switch (crm) { |
461 | 460 | case ESR_ELx_SYS64_ISS_CRM_DC_CVAU: /* DC CVAU, gets promoted */ | |
462 | switch (crm) { | 461 | __user_cache_maint("dc civac", address, ret); |
463 | case 11: /* DC CVAU, gets promoted */ | 462 | break; |
464 | __user_cache_maint("dc civac", address, ret); | 463 | case ESR_ELx_SYS64_ISS_CRM_DC_CVAC: /* DC CVAC, gets promoted */ |
465 | break; | 464 | __user_cache_maint("dc civac", address, ret); |
466 | case 10: /* DC CVAC, gets promoted */ | 465 | break; |
467 | __user_cache_maint("dc civac", address, ret); | 466 | case ESR_ELx_SYS64_ISS_CRM_DC_CIVAC: /* DC CIVAC */ |
468 | break; | 467 | __user_cache_maint("dc civac", address, ret); |
469 | case 14: /* DC CIVAC */ | 468 | break; |
470 | __user_cache_maint("dc civac", address, ret); | 469 | case ESR_ELx_SYS64_ISS_CRM_IC_IVAU: /* IC IVAU */ |
471 | break; | 470 | __user_cache_maint("ic ivau", address, ret); |
472 | case 5: /* IC IVAU */ | 471 | break; |
473 | __user_cache_maint("ic ivau", address, ret); | 472 | default: |
474 | break; | ||
475 | default: | ||
476 | force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0); | ||
477 | return; | ||
478 | } | ||
479 | } else { | ||
480 | force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0); | 473 | force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0); |
481 | return; | 474 | return; |
482 | } | 475 | } |
@@ -487,6 +480,34 @@ asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs) | |||
487 | regs->pc += 4; | 480 | regs->pc += 4; |
488 | } | 481 | } |
489 | 482 | ||
483 | struct sys64_hook { | ||
484 | unsigned int esr_mask; | ||
485 | unsigned int esr_val; | ||
486 | void (*handler)(unsigned int esr, struct pt_regs *regs); | ||
487 | }; | ||
488 | |||
489 | static struct sys64_hook sys64_hooks[] = { | ||
490 | { | ||
491 | .esr_mask = ESR_ELx_SYS64_ISS_EL0_CACHE_OP_MASK, | ||
492 | .esr_val = ESR_ELx_SYS64_ISS_EL0_CACHE_OP_VAL, | ||
493 | .handler = user_cache_maint_handler, | ||
494 | }, | ||
495 | {}, | ||
496 | }; | ||
497 | |||
498 | asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs) | ||
499 | { | ||
500 | struct sys64_hook *hook; | ||
501 | |||
502 | for (hook = sys64_hooks; hook->handler; hook++) | ||
503 | if ((hook->esr_mask & esr) == hook->esr_val) { | ||
504 | hook->handler(esr, regs); | ||
505 | return; | ||
506 | } | ||
507 | |||
508 | force_signal_inject(SIGILL, ILL_ILLOPC, regs, 0); | ||
509 | } | ||
510 | |||
490 | long compat_arm_syscall(struct pt_regs *regs); | 511 | long compat_arm_syscall(struct pt_regs *regs); |
491 | 512 | ||
492 | asmlinkage long do_ni_syscall(struct pt_regs *regs) | 513 | asmlinkage long do_ni_syscall(struct pt_regs *regs) |