diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h index 172b08ff3760..3f046a475abe 100644 --- a/arch/arm/include/asm/traps.h +++ b/arch/arm/include/asm/traps.h @@ -28,6 +28,7 @@ static inline int __in_irqentry_text(unsigned long ptr) ptr < (unsigned long)&__irqentry_text_end; } +extern void get_timer_count_hook_init(void); extern void __init early_trap_init(void *); extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame); extern void ptrace_break(struct pt_regs *regs); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index c053abd1fb53..284affee152b 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -711,6 +712,42 @@ late_initcall(arm_mrc_hook_init); #endif +static int get_timer_count_trap(struct pt_regs *regs, unsigned int instr) +{ + u64 cval; + unsigned int res; + int rd = (instr >> 12) & 0xF; + int rn = (instr >> 16) & 0xF; + + res = arm_check_condition(instr, regs->ARM_cpsr); + if (res == ARM_OPCODE_CONDTEST_FAIL) { + regs->ARM_pc += 4; + return 0; + } + + if (rd == 15 || rn == 15) + return 1; + cval = __arch_counter_get_cntvct(); + regs->uregs[rd] = cval; + regs->uregs[rn] = cval >> 32; + regs->ARM_pc += 4; + return 0; +} + +static struct undef_hook get_timer_count_hook = { + .instr_mask = 0x0ff00fff, + .instr_val = 0x0c500f1e, + .cpsr_mask = MODE_MASK, + .cpsr_val = USR_MODE, + .fn = get_timer_count_trap, +}; + +void get_timer_count_hook_init(void) +{ + register_undef_hook(&get_timer_count_hook); +} +EXPORT_SYMBOL(get_timer_count_hook_init); + /* * A data abort trap was taken, but we did not handle the instruction. * Try to abort the user program, or panic if it was the kernel. diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h index 59690613ac31..7e6a0061a389 100644 --- a/arch/arm64/include/asm/traps.h +++ b/arch/arm64/include/asm/traps.h @@ -110,4 +110,5 @@ static inline u32 arm64_ras_serror_get_severity(u32 esr) bool arm64_is_fatal_ras_serror(struct pt_regs *regs, unsigned int esr); void __noreturn arm64_serror_panic(struct pt_regs *regs, u32 esr); +static inline void get_timer_count_hook_init(void) {} #endif diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 8546463f8874..e37cd76b6163 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -387,7 +387,7 @@ config SUN50I_ERRATUM_UNKNOWN1 config ARM_ARCH_TIMER_VCT_ACCESS bool "Support for ARM architected timer virtual counter access in userspace" - default !ARM64 + default n depends on ARM_ARCH_TIMER help This option enables support for reading the ARM architected timer's diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 3102c91eeef9..1dd13a6b80c4 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -1519,6 +1520,7 @@ static int __init arch_timer_mem_of_init(struct device_node *np) ret = arch_timer_mem_frame_register(frame); if (!ret && !arch_timer_needs_of_probing()) ret = arch_timer_common_init(); + get_timer_count_hook_init(); out: kfree(timer_mem); return ret;