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:
Julien Grall authored and Ian Campbell committed 2014-10-01 12:09:50 +01:00
1 parent bc8e20414a
commit 3e802c6ca1
8 files changed
+252 -6

No files matched your search

+23
View File
@@ -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
View File
@@ -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",
+8
View File
@@ -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 */
+13
View File
@@ -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__ */
+10
View File
@@ -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__ */
+74 -3
View File
@@ -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 -1
View File
@@ -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__ */
+17 -1
View File
@@ -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? */
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.