Skip to content

Commit 2a28307

Browse files
wildea01ctmarinas
authored andcommitted
arm64: debug: avoid accessing mdscr_el1 on fault paths where possible
Since mdscr_el1 is part of the debug register group, it is highly likely to be trapped by a hypervisor to prevent virtual machines from debugging (buggering?) each other. Unfortunately, this absolutely destroys our performance, since we access the register on many of our low-level fault handling paths to keep track of the various debug state machines. This patch removes our dependency on mdscr_el1 in the case that debugging is not being used. More specifically we: - Use TIF_SINGLESTEP to indicate that a task is stepping at EL0 and avoid disabling step in the MDSCR when we don't need to. MDSCR_EL1.SS handling is moved to kernel_entry, when trapping from userspace. - Ensure debug exceptions are re-enabled on *all* exception entry paths, even the debug exception handling path (where we re-enable exceptions after invoking the handler). Since we can now rely on MDSCR_EL1.SS being cleared by the entry code, exception handlers can usually enable debug immediately before enabling interrupts. - Remove all debug exception unmasking from ret_to_user and el1_preempt, since we will never get here with debug exceptions masked. This results in a slight change to kernel debug behaviour, where we now step into interrupt handlers and data aborts from EL1 when debugging the kernel, which is actually a useful thing to do. A side-effect of this is that it *does* potentially prevent stepping off {break,watch}points when there is a high-frequency interrupt source (e.g. a timer), so a debugger would need to use either breakpoints or manually disable interrupts to get around this issue. With this patch applied, guest performance is restored under KVM when debug register accesses are trapped (and we get a measurable performance increase on the host on Cortex-A57 too). Cc: Ian Campbell <[email protected]> Tested-by: Marc Zyngier <[email protected]> Signed-off-by: Will Deacon <[email protected]> Signed-off-by: Catalin Marinas <[email protected]>
1 parent dc60b77 commit 2a28307

File tree

2 files changed

+42
-54
lines changed

2 files changed

+42
-54
lines changed

arch/arm64/include/asm/assembler.h

+16-7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#endif
2222

2323
#include <asm/ptrace.h>
24+
#include <asm/thread_info.h>
2425

2526
/*
2627
* Stack pushing/popping (register pairs only). Equivalent to store decrement
@@ -68,23 +69,31 @@
6869
msr daifclr, #8
6970
.endm
7071

71-
.macro disable_step, tmp
72+
.macro disable_step_tsk, flgs, tmp
73+
tbz \flgs, #TIF_SINGLESTEP, 9990f
7274
mrs \tmp, mdscr_el1
7375
bic \tmp, \tmp, #1
7476
msr mdscr_el1, \tmp
77+
isb // Synchronise with enable_dbg
78+
9990:
7579
.endm
7680

77-
.macro enable_step, tmp
81+
.macro enable_step_tsk, flgs, tmp
82+
tbz \flgs, #TIF_SINGLESTEP, 9990f
83+
disable_dbg
7884
mrs \tmp, mdscr_el1
7985
orr \tmp, \tmp, #1
8086
msr mdscr_el1, \tmp
87+
9990:
8188
.endm
8289

83-
.macro enable_dbg_if_not_stepping, tmp
84-
mrs \tmp, mdscr_el1
85-
tbnz \tmp, #0, 9990f
86-
enable_dbg
87-
9990:
90+
/*
91+
* Enable both debug exceptions and interrupts. This is likely to be
92+
* faster than two daifclr operations, since writes to this register
93+
* are self-synchronising.
94+
*/
95+
.macro enable_dbg_and_irq
96+
msr daifclr, #(8 | 2)
8897
.endm
8998

9099
/*

arch/arm64/kernel/entry.S

+26-47
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
push x0, x1
6161
.if \el == 0
6262
mrs x21, sp_el0
63+
get_thread_info tsk // Ensure MDSCR_EL1.SS is clear,
64+
ldr x19, [tsk, #TI_FLAGS] // since we can unmask debug
65+
disable_step_tsk x19, x20 // exceptions when scheduling.
6366
.else
6467
add x21, sp, #S_FRAME_SIZE
6568
.endif
@@ -259,7 +262,7 @@ el1_da:
259262
* Data abort handling
260263
*/
261264
mrs x0, far_el1
262-
enable_dbg_if_not_stepping x2
265+
enable_dbg
263266
// re-enable interrupts if they were enabled in the aborted context
264267
tbnz x23, #7, 1f // PSR_I_BIT
265268
enable_irq
@@ -275,13 +278,15 @@ el1_sp_pc:
275278
* Stack or PC alignment exception handling
276279
*/
277280
mrs x0, far_el1
281+
enable_dbg
278282
mov x1, x25
279283
mov x2, sp
280284
b do_sp_pc_abort
281285
el1_undef:
282286
/*
283287
* Undefined instruction
284288
*/
289+
enable_dbg
285290
mov x0, sp
286291
b do_undefinstr
287292
el1_dbg:
@@ -294,10 +299,11 @@ el1_dbg:
294299
mrs x0, far_el1
295300
mov x2, sp // struct pt_regs
296301
bl do_debug_exception
297-
302+
enable_dbg
298303
kernel_exit 1
299304
el1_inv:
300305
// TODO: add support for undefined instructions in kernel mode
306+
enable_dbg
301307
mov x0, sp
302308
mov x1, #BAD_SYNC
303309
mrs x2, esr_el1
@@ -307,7 +313,7 @@ ENDPROC(el1_sync)
307313
.align 6
308314
el1_irq:
309315
kernel_entry 1
310-
enable_dbg_if_not_stepping x0
316+
enable_dbg
311317
#ifdef CONFIG_TRACE_IRQFLAGS
312318
bl trace_hardirqs_off
313319
#endif
@@ -332,8 +338,7 @@ ENDPROC(el1_irq)
332338
#ifdef CONFIG_PREEMPT
333339
el1_preempt:
334340
mov x24, lr
335-
1: enable_dbg
336-
bl preempt_schedule_irq // irq en/disable is done inside
341+
1: bl preempt_schedule_irq // irq en/disable is done inside
337342
ldr x0, [tsk, #TI_FLAGS] // get new tasks TI_FLAGS
338343
tbnz x0, #TIF_NEED_RESCHED, 1b // needs rescheduling?
339344
ret x24
@@ -349,7 +354,7 @@ el0_sync:
349354
lsr x24, x25, #ESR_EL1_EC_SHIFT // exception class
350355
cmp x24, #ESR_EL1_EC_SVC64 // SVC in 64-bit state
351356
b.eq el0_svc
352-
adr lr, ret_from_exception
357+
adr lr, ret_to_user
353358
cmp x24, #ESR_EL1_EC_DABT_EL0 // data abort in EL0
354359
b.eq el0_da
355360
cmp x24, #ESR_EL1_EC_IABT_EL0 // instruction abort in EL0
@@ -378,7 +383,7 @@ el0_sync_compat:
378383
lsr x24, x25, #ESR_EL1_EC_SHIFT // exception class
379384
cmp x24, #ESR_EL1_EC_SVC32 // SVC in 32-bit state
380385
b.eq el0_svc_compat
381-
adr lr, ret_from_exception
386+
adr lr, ret_to_user
382387
cmp x24, #ESR_EL1_EC_DABT_EL0 // data abort in EL0
383388
b.eq el0_da
384389
cmp x24, #ESR_EL1_EC_IABT_EL0 // instruction abort in EL0
@@ -423,11 +428,8 @@ el0_da:
423428
*/
424429
mrs x0, far_el1
425430
bic x0, x0, #(0xff << 56)
426-
disable_step x1
427-
isb
428-
enable_dbg
429431
// enable interrupts before calling the main handler
430-
enable_irq
432+
enable_dbg_and_irq
431433
mov x1, x25
432434
mov x2, sp
433435
b do_mem_abort
@@ -436,25 +438,24 @@ el0_ia:
436438
* Instruction abort handling
437439
*/
438440
mrs x0, far_el1
439-
disable_step x1
440-
isb
441-
enable_dbg
442441
// enable interrupts before calling the main handler
443-
enable_irq
442+
enable_dbg_and_irq
444443
orr x1, x25, #1 << 24 // use reserved ISS bit for instruction aborts
445444
mov x2, sp
446445
b do_mem_abort
447446
el0_fpsimd_acc:
448447
/*
449448
* Floating Point or Advanced SIMD access
450449
*/
450+
enable_dbg
451451
mov x0, x25
452452
mov x1, sp
453453
b do_fpsimd_acc
454454
el0_fpsimd_exc:
455455
/*
456456
* Floating Point or Advanced SIMD exception
457457
*/
458+
enable_dbg
458459
mov x0, x25
459460
mov x1, sp
460461
b do_fpsimd_exc
@@ -463,33 +464,32 @@ el0_sp_pc:
463464
* Stack or PC alignment exception handling
464465
*/
465466
mrs x0, far_el1
466-
disable_step x1
467-
isb
468-
enable_dbg
469467
// enable interrupts before calling the main handler
470-
enable_irq
468+
enable_dbg_and_irq
471469
mov x1, x25
472470
mov x2, sp
473471
b do_sp_pc_abort
474472
el0_undef:
475473
/*
476474
* Undefined instruction
477475
*/
478-
mov x0, sp
479476
// enable interrupts before calling the main handler
480-
enable_irq
477+
enable_dbg_and_irq
478+
mov x0, sp
481479
b do_undefinstr
482480
el0_dbg:
483481
/*
484482
* Debug exception handling
485483
*/
486484
tbnz x24, #0, el0_inv // EL0 only
487485
mrs x0, far_el1
488-
disable_step x1
489486
mov x1, x25
490487
mov x2, sp
491-
b do_debug_exception
488+
bl do_debug_exception
489+
enable_dbg
490+
b ret_to_user
492491
el0_inv:
492+
enable_dbg
493493
mov x0, sp
494494
mov x1, #BAD_SYNC
495495
mrs x2, esr_el1
@@ -500,30 +500,19 @@ ENDPROC(el0_sync)
500500
el0_irq:
501501
kernel_entry 0
502502
el0_irq_naked:
503-
disable_step x1
504-
isb
505503
enable_dbg
506504
#ifdef CONFIG_TRACE_IRQFLAGS
507505
bl trace_hardirqs_off
508506
#endif
509507

510508
irq_handler
511-
get_thread_info tsk
512509

513510
#ifdef CONFIG_TRACE_IRQFLAGS
514511
bl trace_hardirqs_on
515512
#endif
516513
b ret_to_user
517514
ENDPROC(el0_irq)
518515

519-
/*
520-
* This is the return code to user mode for abort handlers
521-
*/
522-
ret_from_exception:
523-
get_thread_info tsk
524-
b ret_to_user
525-
ENDPROC(ret_from_exception)
526-
527516
/*
528517
* Register switch for AArch64. The callee-saved registers need to be saved
529518
* and restored. On entry:
@@ -563,10 +552,7 @@ ret_fast_syscall:
563552
ldr x1, [tsk, #TI_FLAGS]
564553
and x2, x1, #_TIF_WORK_MASK
565554
cbnz x2, fast_work_pending
566-
tbz x1, #TIF_SINGLESTEP, fast_exit
567-
disable_dbg
568-
enable_step x2
569-
fast_exit:
555+
enable_step_tsk x1, x2
570556
kernel_exit 0, ret = 1
571557

572558
/*
@@ -585,7 +571,6 @@ work_pending:
585571
bl do_notify_resume
586572
b ret_to_user
587573
work_resched:
588-
enable_dbg
589574
bl schedule
590575

591576
/*
@@ -596,9 +581,7 @@ ret_to_user:
596581
ldr x1, [tsk, #TI_FLAGS]
597582
and x2, x1, #_TIF_WORK_MASK
598583
cbnz x2, work_pending
599-
tbz x1, #TIF_SINGLESTEP, no_work_pending
600-
disable_dbg
601-
enable_step x2
584+
enable_step_tsk x1, x2
602585
no_work_pending:
603586
kernel_exit 0, ret = 0
604587
ENDPROC(ret_to_user)
@@ -625,12 +608,8 @@ el0_svc:
625608
mov sc_nr, #__NR_syscalls
626609
el0_svc_naked: // compat entry point
627610
stp x0, scno, [sp, #S_ORIG_X0] // save the original x0 and syscall number
628-
disable_step x16
629-
isb
630-
enable_dbg
631-
enable_irq
611+
enable_dbg_and_irq
632612

633-
get_thread_info tsk
634613
ldr x16, [tsk, #TI_FLAGS] // check for syscall tracing
635614
tbnz x16, #TIF_SYSCALL_TRACE, __sys_trace // are we tracing syscalls?
636615
adr lr, ret_fast_syscall // return address

0 commit comments

Comments
 (0)