Skip to content

Commit 9fb7410

Browse files
Dave P Martinwildea01
Dave P Martin
authored andcommitted
arm64/BUG: Use BRK instruction for generic BUG traps
Currently, the minimal default BUG() implementation from asm- generic is used for arm64. This patch uses the BRK software breakpoint instruction to generate a trap instead, similarly to most other arches, with the generic BUG code generating the dmesg boilerplate. This allows bug metadata to be moved to a separate table and reduces the amount of inline code at BUG and WARN sites. This also avoids clobbering any registers before they can be dumped. To mitigate the size of the bug table further, this patch makes use of the existing infrastructure for encoding addresses within the bug table as 32-bit offsets instead of absolute pointers. (Note that this limits the kernel size to 2GB.) Traps are registered at arch_initcall time for aarch64, but BUG has minimal real dependencies and it is desirable to be able to generate bug splats as early as possible. This patch redirects all debug exceptions caused by BRK directly to bug_handler() until the full debug exception support has been initialised. Signed-off-by: Dave Martin <[email protected]> Reviewed-by: Catalin Marinas <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent d7a33f4 commit 9fb7410

File tree

5 files changed

+142
-3
lines changed

5 files changed

+142
-3
lines changed

arch/arm64/Kconfig

+8
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ config TRACE_IRQFLAGS_SUPPORT
113113
config RWSEM_XCHGADD_ALGORITHM
114114
def_bool y
115115

116+
config GENERIC_BUG
117+
def_bool y
118+
depends on BUG
119+
120+
config GENERIC_BUG_RELATIVE_POINTERS
121+
def_bool y
122+
depends on GENERIC_BUG
123+
116124
config GENERIC_HWEIGHT
117125
def_bool y
118126

arch/arm64/include/asm/bug.h

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (C) 2015 ARM Limited
3+
* Author: Dave Martin <[email protected]>
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License version 2 as
7+
* published by the Free Software Foundation.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
#ifndef _ARCH_ARM64_ASM_BUG_H
19+
#define _ARCH_ARM64_ASM_BUG_H
20+
21+
#include <asm/debug-monitors.h>
22+
23+
#ifdef CONFIG_GENERIC_BUG
24+
#define HAVE_ARCH_BUG
25+
26+
#ifdef CONFIG_DEBUG_BUGVERBOSE
27+
#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
28+
#define __BUGVERBOSE_LOCATION(file, line) \
29+
".pushsection .rodata.str,\"aMS\",@progbits,1\n" \
30+
"2: .string \"" file "\"\n\t" \
31+
".popsection\n\t" \
32+
\
33+
".long 2b - 0b\n\t" \
34+
".short " #line "\n\t"
35+
#else
36+
#define _BUGVERBOSE_LOCATION(file, line)
37+
#endif
38+
39+
#define _BUG_FLAGS(flags) __BUG_FLAGS(flags)
40+
41+
#define __BUG_FLAGS(flags) asm volatile ( \
42+
".pushsection __bug_table,\"a\"\n\t" \
43+
".align 2\n\t" \
44+
"0: .long 1f - 0b\n\t" \
45+
_BUGVERBOSE_LOCATION(__FILE__, __LINE__) \
46+
".short " #flags "\n\t" \
47+
".popsection\n" \
48+
\
49+
"1: brk %[imm]" \
50+
:: [imm] "i" (BUG_BRK_IMM) \
51+
)
52+
53+
#define BUG() do { \
54+
_BUG_FLAGS(0); \
55+
unreachable(); \
56+
} while (0)
57+
58+
#define __WARN_TAINT(taint) _BUG_FLAGS(BUGFLAG_TAINT(taint))
59+
60+
#endif /* ! CONFIG_GENERIC_BUG */
61+
62+
#include <asm-generic/bug.h>
63+
64+
#endif /* ! _ARCH_ARM64_ASM_BUG_H */

arch/arm64/include/asm/debug-monitors.h

+2
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@
5252
* 0x100: for triggering a fault on purpose (reserved)
5353
* 0x400: for dynamic BRK instruction
5454
* 0x401: for compile time BRK instruction
55+
* 0x800: kernel-mode BUG() and WARN() traps
5556
*/
5657
#define FAULT_BRK_IMM 0x100
5758
#define KGDB_DYN_DBG_BRK_IMM 0x400
5859
#define KGDB_COMPILED_DBG_BRK_IMM 0x401
60+
#define BUG_BRK_IMM 0x800
5961

6062
/*
6163
* BRK instruction encoding

arch/arm64/kernel/traps.c

+58-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
*/
1919

20+
#include <linux/bug.h>
2021
#include <linux/signal.h>
2122
#include <linux/personality.h>
2223
#include <linux/kallsyms.h>
@@ -32,8 +33,10 @@
3233
#include <linux/syscalls.h>
3334

3435
#include <asm/atomic.h>
36+
#include <asm/bug.h>
3537
#include <asm/debug-monitors.h>
3638
#include <asm/esr.h>
39+
#include <asm/insn.h>
3740
#include <asm/traps.h>
3841
#include <asm/stacktrace.h>
3942
#include <asm/exception.h>
@@ -466,7 +469,61 @@ void __pgd_error(const char *file, int line, unsigned long val)
466469
pr_crit("%s:%d: bad pgd %016lx.\n", file, line, val);
467470
}
468471

472+
/* GENERIC_BUG traps */
473+
474+
int is_valid_bugaddr(unsigned long addr)
475+
{
476+
/*
477+
* bug_handler() only called for BRK #BUG_BRK_IMM.
478+
* So the answer is trivial -- any spurious instances with no
479+
* bug table entry will be rejected by report_bug() and passed
480+
* back to the debug-monitors code and handled as a fatal
481+
* unexpected debug exception.
482+
*/
483+
return 1;
484+
}
485+
486+
static int bug_handler(struct pt_regs *regs, unsigned int esr)
487+
{
488+
if (user_mode(regs))
489+
return DBG_HOOK_ERROR;
490+
491+
switch (report_bug(regs->pc, regs)) {
492+
case BUG_TRAP_TYPE_BUG:
493+
die("Oops - BUG", regs, 0);
494+
break;
495+
496+
case BUG_TRAP_TYPE_WARN:
497+
break;
498+
499+
default:
500+
/* unknown/unrecognised bug trap type */
501+
return DBG_HOOK_ERROR;
502+
}
503+
504+
/* If thread survives, skip over the BUG instruction and continue: */
505+
regs->pc += AARCH64_INSN_SIZE; /* skip BRK and resume */
506+
return DBG_HOOK_HANDLED;
507+
}
508+
509+
static struct break_hook bug_break_hook = {
510+
.esr_val = 0xf2000000 | BUG_BRK_IMM,
511+
.esr_mask = 0xffffffff,
512+
.fn = bug_handler,
513+
};
514+
515+
/*
516+
* Initial handler for AArch64 BRK exceptions
517+
* This handler only used until debug_traps_init().
518+
*/
519+
int __init early_brk64(unsigned long addr, unsigned int esr,
520+
struct pt_regs *regs)
521+
{
522+
return bug_handler(regs, esr) != DBG_HOOK_HANDLED;
523+
}
524+
525+
/* This registration must happen early, before debug_traps_init(). */
469526
void __init trap_init(void)
470527
{
471-
return;
528+
register_break_hook(&bug_break_hook);
472529
}

arch/arm64/mm/fault.c

+10-2
Original file line numberDiff line numberDiff line change
@@ -501,14 +501,22 @@ asmlinkage void __exception do_sp_pc_abort(unsigned long addr,
501501
arm64_notify_die("Oops - SP/PC alignment exception", regs, &info, esr);
502502
}
503503

504-
static struct fault_info debug_fault_info[] = {
504+
int __init early_brk64(unsigned long addr, unsigned int esr,
505+
struct pt_regs *regs);
506+
507+
/*
508+
* __refdata because early_brk64 is __init, but the reference to it is
509+
* clobbered at arch_initcall time.
510+
* See traps.c and debug-monitors.c:debug_traps_init().
511+
*/
512+
static struct fault_info __refdata debug_fault_info[] = {
505513
{ do_bad, SIGTRAP, TRAP_HWBKPT, "hardware breakpoint" },
506514
{ do_bad, SIGTRAP, TRAP_HWBKPT, "hardware single-step" },
507515
{ do_bad, SIGTRAP, TRAP_HWBKPT, "hardware watchpoint" },
508516
{ do_bad, SIGBUS, 0, "unknown 3" },
509517
{ do_bad, SIGTRAP, TRAP_BRKPT, "aarch32 BKPT" },
510518
{ do_bad, SIGTRAP, 0, "aarch32 vector catch" },
511-
{ do_bad, SIGTRAP, TRAP_BRKPT, "aarch64 BRK" },
519+
{ early_brk64, SIGTRAP, TRAP_BRKPT, "aarch64 BRK" },
512520
{ do_bad, SIGBUS, 0, "unknown 7" },
513521
};
514522

0 commit comments

Comments
 (0)