mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/arm: Correctly support WARN_ON
Currently the hypervisor will hang if it hits a WARN_ON.
The implementation uses an undefined instruction, made ourself because ARM
don't provide one, to implement BUG/ASSERT/WARN_ON, and sets up the
different tables (one for each type) which contain useful information.
This is based on the x86 implementation (include/asm-x86/bug.h). Unfortunately
the structure can't be shared because many ARM{32,64} gcc versions doesn't
correctly support %c. The support for executing a function in an exception handler
is also keep unimplemented on ARM. Therefore, dump_execution_state is
implemented as WARN()
The current opcode used to go in exception mode may not be undefined on ARM64.
Use the instruction "brk" to generate a software debug exception.
Signed-off-by: Julien Grall <julien.grall@linaro.org>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
This commit is contained in:
1 parent
bc8e20414a
commit
3e802c6ca1
8 files changed
+252
-6
No files matched your search
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <xen/config.h>
|
||||
#include <xen/lib.h>
|
||||
#include <xen/kernel.h>
|
||||
|
||||
#include <public/xen.h>
|
||||
|
||||
@@ -25,6 +26,28 @@
|
||||
|
||||
asmlinkage void do_trap_undefined_instruction(struct cpu_user_regs *regs)
|
||||
{
|
||||
uint32_t pc = regs->pc;
|
||||
uint32_t instr;
|
||||
|
||||
if ( !is_kernel_text(pc) &&
|
||||
(system_state >= SYS_STATE_active || !is_kernel_inittext(pc)) )
|
||||
goto die;
|
||||
|
||||
/* PC should be always a multiple of 4, as Xen is using ARM instruction set */
|
||||
if ( regs->pc & 0x3 )
|
||||
goto die;
|
||||
|
||||
instr = *((uint32_t *)pc);
|
||||
if ( instr != BUG_OPCODE )
|
||||
goto die;
|
||||
|
||||
if ( do_bug_frame(regs, pc) )
|
||||
goto die;
|
||||
|
||||
regs->pc += 4;
|
||||
return;
|
||||
|
||||
die:
|
||||
do_unexpected_trap("Undefined Instruction", regs);
|
||||
}
|
||||
|
||||
|
||||
+106
-1
@@ -32,6 +32,7 @@
|
||||
#include <xen/domain_page.h>
|
||||
#include <public/sched.h>
|
||||
#include <public/xen.h>
|
||||
#include <asm/debugger.h>
|
||||
#include <asm/event.h>
|
||||
#include <asm/regs.h>
|
||||
#include <asm/cpregs.h>
|
||||
@@ -1050,6 +1051,102 @@ void do_unexpected_trap(const char *msg, struct cpu_user_regs *regs)
|
||||
panic("CPU%d: Unexpected Trap: %s\n", smp_processor_id(), msg);
|
||||
}
|
||||
|
||||
int do_bug_frame(struct cpu_user_regs *regs, vaddr_t pc)
|
||||
{
|
||||
const struct bug_frame *bug;
|
||||
const char *prefix = "", *filename, *predicate;
|
||||
unsigned long fixup;
|
||||
int id, lineno;
|
||||
static const struct bug_frame *const stop_frames[] = {
|
||||
__stop_bug_frames_0,
|
||||
__stop_bug_frames_1,
|
||||
__stop_bug_frames_2,
|
||||
NULL
|
||||
};
|
||||
|
||||
for ( bug = __start_bug_frames, id = 0; stop_frames[id]; ++bug )
|
||||
{
|
||||
while ( unlikely(bug == stop_frames[id]) )
|
||||
++id;
|
||||
|
||||
if ( ((vaddr_t)bug_loc(bug)) == pc )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !stop_frames[id] )
|
||||
return -ENOENT;
|
||||
|
||||
/* WARN, BUG or ASSERT: decode the filename pointer and line number. */
|
||||
filename = bug_file(bug);
|
||||
if ( !is_kernel(filename) )
|
||||
return -EINVAL;
|
||||
fixup = strlen(filename);
|
||||
if ( fixup > 50 )
|
||||
{
|
||||
filename += fixup - 47;
|
||||
prefix = "...";
|
||||
}
|
||||
lineno = bug_line(bug);
|
||||
|
||||
switch ( id )
|
||||
{
|
||||
case BUGFRAME_warn:
|
||||
printk("Xen WARN at %s%s:%d\n", prefix, filename, lineno);
|
||||
show_execution_state(regs);
|
||||
return 0;
|
||||
|
||||
case BUGFRAME_bug:
|
||||
printk("Xen BUG at %s%s:%d\n", prefix, filename, lineno);
|
||||
|
||||
if ( debugger_trap_fatal(TRAP_invalid_op, regs) )
|
||||
return 0;
|
||||
|
||||
show_execution_state(regs);
|
||||
panic("Xen BUG at %s%s:%d", prefix, filename, lineno);
|
||||
|
||||
case BUGFRAME_assert:
|
||||
/* ASSERT: decode the predicate string pointer. */
|
||||
predicate = bug_msg(bug);
|
||||
if ( !is_kernel(predicate) )
|
||||
predicate = "<unknown>";
|
||||
|
||||
printk("Assertion '%s' failed at %s%s:%d\n",
|
||||
predicate, prefix, filename, lineno);
|
||||
if ( debugger_trap_fatal(TRAP_invalid_op, regs) )
|
||||
return 0;
|
||||
show_execution_state(regs);
|
||||
panic("Assertion '%s' failed at %s%s:%d",
|
||||
predicate, prefix, filename, lineno);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARM_64
|
||||
static void do_trap_brk(struct cpu_user_regs *regs, union hsr hsr)
|
||||
{
|
||||
/* HCR_EL2.TGE and MDCR_EL2.TDE are not set so we never receive
|
||||
* software breakpoint exception for EL1 and EL0 here.
|
||||
*/
|
||||
BUG_ON(!hyp_mode(regs));
|
||||
|
||||
switch (hsr.brk.comment)
|
||||
{
|
||||
case BRK_BUG_FRAME:
|
||||
if ( do_bug_frame(regs, regs->pc) )
|
||||
goto die;
|
||||
|
||||
regs->pc += 4;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
die:
|
||||
do_unexpected_trap("Undefined Breakpoint Value", regs);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef register_t (*arm_hypercall_fn_t)(
|
||||
register_t, register_t, register_t, register_t, register_t);
|
||||
|
||||
@@ -1921,7 +2018,8 @@ asmlinkage void do_trap_hypervisor(struct cpu_user_regs *regs)
|
||||
* correctly (See XSA-102). Until that is resolved we treat any
|
||||
* trap from 32-bit userspace on 64-bit kernel as undefined.
|
||||
*/
|
||||
if ( is_64bit_domain(current->domain) && psr_mode_is_32bit(regs->cpsr) )
|
||||
if ( !hyp_mode(regs) && is_64bit_domain(current->domain) &&
|
||||
psr_mode_is_32bit(regs->cpsr) )
|
||||
{
|
||||
inject_undef_exception(regs, hsr.len);
|
||||
return;
|
||||
@@ -2006,6 +2104,13 @@ asmlinkage void do_trap_hypervisor(struct cpu_user_regs *regs)
|
||||
case HSR_EC_DATA_ABORT_LOWER_EL:
|
||||
do_trap_data_abort_guest(regs, hsr);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_ARM_64
|
||||
case HSR_EC_BRK:
|
||||
do_trap_brk(regs, hsr);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
bad_trap:
|
||||
printk("Hypervisor Trap. HSR=0x%x EC=0x%x IL=%x Syndrome=0x%"PRIx32"\n",
|
||||
|
||||
@@ -40,6 +40,14 @@ SECTIONS
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
.rodata : {
|
||||
_srodata = .; /* Read-only data */
|
||||
/* Bug frames table */
|
||||
__start_bug_frames = .;
|
||||
*(.bug_frames.0)
|
||||
__stop_bug_frames_0 = .;
|
||||
*(.bug_frames.1)
|
||||
__stop_bug_frames_1 = .;
|
||||
*(.bug_frames.2)
|
||||
__stop_bug_frames_2 = .;
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
_erodata = .; /* End of read-only data */
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef __ARM_ARM32_BUG_H__
|
||||
#define __ARM_ARM32_BUG_H__
|
||||
|
||||
#include <xen/stringify.h>
|
||||
|
||||
/* ARMv7 provides a list of undefined opcode (see A8.8.247 DDI 0406C.b)
|
||||
* Use one them encoding A1 to go in exception mode
|
||||
*/
|
||||
#define BUG_OPCODE 0xe7f00f0
|
||||
|
||||
#define BUG_INSTR ".word " __stringify(BUG_OPCODE)
|
||||
|
||||
#endif /* __ARM_ARM32_BUG_H__ */
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef __ARM_ARM64_BUG_H__
|
||||
#define __ARM_ARM64_BUG_H__
|
||||
|
||||
#include <xen/stringify.h>
|
||||
|
||||
#define BRK_BUG_FRAME 1
|
||||
|
||||
#define BUG_INSTR "brk " __stringify(BRK_BUG_FRAME)
|
||||
|
||||
#endif /* __ARM_ARM64_BUG_H__ */
|
||||
@@ -1,10 +1,81 @@
|
||||
#ifndef __ARM_BUG_H__
|
||||
#define __ARM_BUG_H__
|
||||
|
||||
#define BUG() __bug(__FILE__, __LINE__)
|
||||
#define WARN() __warn(__FILE__, __LINE__)
|
||||
#include <asm/processor.h>
|
||||
|
||||
#endif /* __X86_BUG_H__ */
|
||||
#if defined(CONFIG_ARM_32)
|
||||
# include <asm/arm32/bug.h>
|
||||
#elif defined(CONFIG_ARM_64)
|
||||
# include <asm/arm64/bug.h>
|
||||
#else
|
||||
# error "unknown ARM variant"
|
||||
#endif
|
||||
|
||||
#define BUG_DISP_WIDTH 24
|
||||
#define BUG_LINE_LO_WIDTH (31 - BUG_DISP_WIDTH)
|
||||
#define BUG_LINE_HI_WIDTH (31 - BUG_DISP_WIDTH)
|
||||
|
||||
struct bug_frame {
|
||||
signed int loc_disp; /* Relative address to the bug address */
|
||||
signed int file_disp; /* Relative address to the filename */
|
||||
signed int msg_disp; /* Relative address to the predicate (for ASSERT) */
|
||||
uint16_t line; /* Line number */
|
||||
uint32_t pad0:16; /* Padding for 8-bytes align */
|
||||
};
|
||||
|
||||
#define bug_loc(b) ((const void *)(b) + (b)->loc_disp)
|
||||
#define bug_file(b) ((const void *)(b) + (b)->file_disp);
|
||||
#define bug_line(b) ((b)->line)
|
||||
#define bug_msg(b) ((const char *)(b) + (b)->msg_disp)
|
||||
|
||||
#define BUGFRAME_warn 0
|
||||
#define BUGFRAME_bug 1
|
||||
#define BUGFRAME_assert 2
|
||||
|
||||
/* Many versions of GCC doesn't support the asm %c parameter which would
|
||||
* be preferable to this unpleasantness. We use mergeable string
|
||||
* sections to avoid multiple copies of the string appearing in the
|
||||
* Xen image.
|
||||
*/
|
||||
#define BUG_FRAME(type, line, file, has_msg, msg) do { \
|
||||
BUILD_BUG_ON((line) >> 16); \
|
||||
asm ("1:"BUG_INSTR"\n" \
|
||||
".pushsection .rodata.str, \"aMS\", %progbits, 1\n" \
|
||||
"2:\t.asciz " __stringify(file) "\n" \
|
||||
"3:\n" \
|
||||
".if " #has_msg "\n" \
|
||||
"\t.asciz " #msg "\n" \
|
||||
".endif\n" \
|
||||
".popsection\n" \
|
||||
".pushsection .bug_frames." __stringify(type) ", \"a\", %progbits\n"\
|
||||
"4:\n" \
|
||||
".long (1b - 4b)\n" \
|
||||
".long (2b - 4b)\n" \
|
||||
".long (3b - 4b)\n" \
|
||||
".hword " __stringify(line) ", 0\n" \
|
||||
".popsection"); \
|
||||
} while (0)
|
||||
|
||||
#define WARN() BUG_FRAME(BUGFRAME_warn, __LINE__, __FILE__, 0, "")
|
||||
|
||||
#define BUG() do { \
|
||||
BUG_FRAME(BUGFRAME_bug, __LINE__, __FILE__, 0, ""); \
|
||||
unreachable(); \
|
||||
} while (0)
|
||||
|
||||
#define assert_failed(msg) do { \
|
||||
BUG_FRAME(BUGFRAME_assert, __LINE__, __FILE__, 1, msg); \
|
||||
unreachable(); \
|
||||
} while (0)
|
||||
|
||||
extern const struct bug_frame __start_bug_frames[],
|
||||
__stop_bug_frames_0[],
|
||||
__stop_bug_frames_1[],
|
||||
__stop_bug_frames_2[];
|
||||
|
||||
int do_bug_frame(struct cpu_user_regs *regs, vaddr_t pc);
|
||||
|
||||
#endif /* __ARM_BUG_H__ */
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef __ARM_DEBUGGER_H__
|
||||
#define __ARM_DEBUGGER_H__
|
||||
|
||||
#define debugger_trap_fatal(v, r) ((void) 0)
|
||||
#define debugger_trap_fatal(v, r) (0)
|
||||
#define debugger_trap_immediate() ((void) 0)
|
||||
|
||||
#endif /* __ARM_DEBUGGER_H__ */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <asm/cpregs.h>
|
||||
#include <asm/sysregs.h>
|
||||
#include <public/arch-arm.h>
|
||||
|
||||
/* MIDR Main ID Register */
|
||||
#define MIDR_MASK 0xff0ffff0
|
||||
@@ -179,6 +180,7 @@
|
||||
#define HDCR_TDRA (_AC(1,U)<<11) /* Trap Debug ROM access */
|
||||
#define HDCR_TDOSA (_AC(1,U)<<10) /* Trap Debug-OS-related register access */
|
||||
#define HDCR_TDA (_AC(1,U)<<9) /* Trap Debug Access */
|
||||
#define HDCR_TDE (_AC(1,U)<<8) /* Route Soft Debug exceptions from EL1/EL1 to EL2 */
|
||||
#define HDCR_TPM (_AC(1,U)<<6) /* Trap Performance Monitors accesses */
|
||||
#define HDCR_TPMCR (_AC(1,U)<<5) /* Trap PMCR accesses */
|
||||
|
||||
@@ -205,6 +207,9 @@
|
||||
#define HSR_EC_INSTR_ABORT_CURR_EL 0x21
|
||||
#define HSR_EC_DATA_ABORT_LOWER_EL 0x24
|
||||
#define HSR_EC_DATA_ABORT_CURR_EL 0x25
|
||||
#ifdef CONFIG_ARM_64
|
||||
#define HSR_EC_BRK 0x3c
|
||||
#endif
|
||||
|
||||
/* FSR format, common */
|
||||
#define FSR_LPAE (_AC(1,UL)<<9)
|
||||
@@ -452,6 +457,17 @@ union hsr {
|
||||
unsigned long len:1; /* Instruction length */
|
||||
unsigned long ec:6; /* Exception Class */
|
||||
} dabt; /* HSR_EC_DATA_ABORT_* */
|
||||
|
||||
#ifdef CONFIG_ARM_64
|
||||
struct hsr_brk {
|
||||
unsigned long comment:16; /* Comment */
|
||||
unsigned long res0:9;
|
||||
unsigned long len:1; /* Instruction length */
|
||||
unsigned long ec:6; /* Exception Class */
|
||||
} brk;
|
||||
#endif
|
||||
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -583,7 +599,7 @@ void panic_PAR(uint64_t par);
|
||||
void show_execution_state(struct cpu_user_regs *regs);
|
||||
void show_registers(struct cpu_user_regs *regs);
|
||||
//#define dump_execution_state() run_in_exception_handler(show_execution_state)
|
||||
#define dump_execution_state() asm volatile (".word 0xe7f000f0\n"); /* XXX */
|
||||
#define dump_execution_state() WARN()
|
||||
|
||||
#define cpu_relax() barrier() /* Could yield? */
|
||||
|
||||
|
||||