|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v5 22/25] arm: trap handlers
From: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
Functions executed exiting from the guest and returning to the guest:
trap and hypercall handlers and leave_hypervisor_tail.
Signed-off-by: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx>
Signed-off-by: Ian Campbell <ian.campbell@xxxxxxxxxx>
Signed-off-by: Tim Deegan <Tim.Deegan@xxxxxxxxxx>
---
xen/arch/arm/traps.c | 609 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 609 insertions(+), 0 deletions(-)
create mode 100644 xen/arch/arm/traps.c
diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
new file mode 100644
index 0000000..4346dd7
--- /dev/null
+++ b/xen/arch/arm/traps.c
@@ -0,0 +1,609 @@
+/*
+ * xen/arch/arm/traps.c
+ *
+ * ARM Trap handlers
+ *
+ * Copyright (c) 2011 Citrix Systems.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <xen/config.h>
+#include <xen/init.h>
+#include <xen/string.h>
+#include <xen/version.h>
+#include <xen/smp.h>
+#include <xen/symbols.h>
+#include <xen/irq.h>
+#include <xen/lib.h>
+#include <xen/mm.h>
+#include <xen/errno.h>
+#include <xen/hypercall.h>
+#include <xen/softirq.h>
+#include <public/xen.h>
+#include <asm/regs.h>
+#include <asm/cpregs.h>
+
+#include "io.h"
+#include "vtimer.h"
+#include "gic.h"
+
+/* The base of the stack must always be double-word aligned, which means
+ * that both the kernel half of struct cpu_user_regs (which is pushed in
+ * entry.S) and struct cpu_info (which lives at the bottom of a Xen
+ * stack) must be doubleword-aligned in size. */
+static inline void check_stack_alignment_constraints(void) {
+ BUILD_BUG_ON((sizeof (struct cpu_user_regs)) & 0x7);
+ BUILD_BUG_ON((offsetof(struct cpu_user_regs, r8_fiq)) & 0x7);
+ BUILD_BUG_ON((sizeof (struct cpu_info)) & 0x7);
+}
+
+static int debug_stack_lines = 20;
+integer_param("debug_stack_lines", debug_stack_lines);
+
+#define stack_words_per_line 8
+
+asmlinkage void __div0(void)
+{
+ printk("Division by zero in hypervisor.\n");
+ BUG();
+}
+
+/* XXX could/should be common code */
+static void print_xen_info(void)
+{
+ char taint_str[TAINT_STRING_MAX_LEN];
+ char debug = 'n';
+
+#ifndef NDEBUG
+ debug = 'y';
+#endif
+
+ printk("----[ Xen-%d.%d%s x86_64 debug=%c %s ]----\n",
+ xen_major_version(), xen_minor_version(), xen_extra_version(),
+ debug, print_tainted(taint_str));
+}
+
+static const char *decode_fsc(uint32_t fsc, int *level)
+{
+ const char *msg = NULL;
+
+ switch ( fsc & 0x3f )
+ {
+ case FSC_FLT_TRANS ... FSC_FLT_TRANS + 3:
+ msg = "Translation fault";
+ *level = fsc & FSC_LL_MASK;
+ break;
+ case FSC_FLT_ACCESS ... FSC_FLT_ACCESS + 3:
+ msg = "Access fault";
+ *level = fsc & FSC_LL_MASK;
+ break;
+ case FSC_FLT_PERM ... FSC_FLT_PERM + 3:
+ msg = "Permission fault";
+ *level = fsc & FSC_LL_MASK;
+ break;
+
+ case FSC_SEA:
+ msg = "Synchronous External Abort";
+ break;
+ case FSC_SPE:
+ msg = "Memory Access Synchronous Parity Error";
+ break;
+ case FSC_APE:
+ msg = "Memory Access Asynchronous Parity Error";
+ break;
+ case FSC_SEATT ... FSC_SEATT + 3:
+ msg = "Sync. Ext. Abort Translation Table";
+ *level = fsc & FSC_LL_MASK;
+ break;
+ case FSC_SPETT ... FSC_SPETT + 3:
+ msg = "Sync. Parity. Error Translation Table";
+ *level = fsc & FSC_LL_MASK;
+ break;
+ case FSC_AF:
+ msg = "Alignment Fault";
+ break;
+ case FSC_DE:
+ msg = "Debug Event";
+ break;
+
+ case FSC_LKD:
+ msg = "Implementation Fault: Lockdown Abort";
+ break;
+ case FSC_CPR:
+ msg = "Implementation Fault: Coprocossor Abort";
+ break;
+
+ default:
+ msg = "Unknown Failure";
+ break;
+ }
+ return msg;
+}
+
+static const char *fsc_level_str(int level)
+{
+ switch ( level )
+ {
+ case -1: return "";
+ case 1: return " at level 1";
+ case 2: return " at level 2";
+ case 3: return " at level 3";
+ default: return " (level invalid)";
+ }
+}
+
+void panic_PAR(uint64_t par, const char *when)
+{
+ if ( par & PAR_F )
+ {
+ const char *msg;
+ int level = -1;
+ int stage = par & PAR_STAGE2 ? 2 : 1;
+ int second_in_first = !!(par & PAR_STAGE21);
+
+ msg = decode_fsc( (par&PAR_FSC_MASK) >> PAR_FSC_SHIFT, &level);
+
+ printk("PAR: %010"PRIx64": %s stage %d%s%s\n",
+ par, msg,
+ stage,
+ second_in_first ? " during second stage lookup" : "",
+ fsc_level_str(level));
+ }
+ else
+ {
+ printk("PAR: %010"PRIx64": paddr:%010"PRIx64
+ " attr %"PRIx64" sh %"PRIx64" %s\n",
+ par, par & PADDR_MASK, par >> PAR_MAIR_SHIFT,
+ (par & PAR_SH_MASK) >> PAR_SH_SHIFT,
+ (par & PAR_NS) ? "Non-Secure" : "Secure");
+ }
+ panic("Error during %s-to-physical address translation\n", when);
+}
+
+void show_registers(struct cpu_user_regs *regs)
+{
+ static const char *mode_strings[] = {
+ [PSR_MODE_USR] = "USR",
+ [PSR_MODE_FIQ] = "FIQ",
+ [PSR_MODE_IRQ] = "IRQ",
+ [PSR_MODE_SVC] = "SVC",
+ [PSR_MODE_MON] = "MON",
+ [PSR_MODE_ABT] = "ABT",
+ [PSR_MODE_HYP] = "HYP",
+ [PSR_MODE_UND] = "UND",
+ [PSR_MODE_SYS] = "SYS"
+ };
+
+ print_xen_info();
+ printk("CPU: %d\n", smp_processor_id());
+ printk("PC: %08"PRIx32, regs->pc);
+ if ( !guest_mode(regs) )
+ print_symbol(" %s", regs->pc);
+ printk("\n");
+ printk("CPSR: %08"PRIx32" MODE:%s\n", regs->cpsr,
+ mode_strings[regs->cpsr & PSR_MODE_MASK]);
+ printk(" R0: %08"PRIx32" R1: %08"PRIx32" R2: %08"PRIx32" R3:
%08"PRIx32"\n",
+ regs->r0, regs->r1, regs->r2, regs->r3);
+ printk(" R4: %08"PRIx32" R5: %08"PRIx32" R6: %08"PRIx32" R7:
%08"PRIx32"\n",
+ regs->r4, regs->r5, regs->r6, regs->r7);
+ printk(" R8: %08"PRIx32" R9: %08"PRIx32" R10:%08"PRIx32"
R11:%08"PRIx32" R12:%08"PRIx32"\n",
+ regs->r8, regs->r9, regs->r10, regs->r11, regs->r12);
+
+ if ( guest_mode(regs) )
+ {
+ printk("USR: SP: %08"PRIx32" LR: %08"PRIx32" CPSR:%08"PRIx32"\n",
+ regs->sp_usr, regs->lr_usr, regs->cpsr);
+ printk("SVC: SP: %08"PRIx32" LR: %08"PRIx32" SPSR:%08"PRIx32"\n",
+ regs->sp_svc, regs->lr_svc, regs->spsr_svc);
+ printk("ABT: SP: %08"PRIx32" LR: %08"PRIx32" SPSR:%08"PRIx32"\n",
+ regs->sp_abt, regs->lr_abt, regs->spsr_abt);
+ printk("UND: SP: %08"PRIx32" LR: %08"PRIx32" SPSR:%08"PRIx32"\n",
+ regs->sp_und, regs->lr_und, regs->spsr_und);
+ printk("IRQ: SP: %08"PRIx32" LR: %08"PRIx32" SPSR:%08"PRIx32"\n",
+ regs->sp_irq, regs->lr_irq, regs->spsr_irq);
+ printk("FIQ: SP: %08"PRIx32" LR: %08"PRIx32" SPSR:%08"PRIx32"\n",
+ regs->sp_fiq, regs->lr_fiq, regs->spsr_fiq);
+ printk("FIQ: R8: %08"PRIx32" R9: %08"PRIx32" R10:%08"PRIx32"
R11:%08"PRIx32" R12:%08"PRIx32"\n",
+ regs->r8_fiq, regs->r9_fiq, regs->r10_fiq, regs->r11_fiq,
regs->r11_fiq);
+ printk("\n");
+ printk("TTBR0 %08"PRIx32" TTBR1 %08"PRIx32" TTBCR %08"PRIx32"\n",
+ READ_CP32(TTBR0), READ_CP32(TTBR1), READ_CP32(TTBCR));
+ printk("SCTLR %08"PRIx32"\n", READ_CP32(SCTLR));
+ printk("VTTBR %010"PRIx64"\n", READ_CP64(VTTBR));
+ printk("\n");
+ }
+ else
+ {
+ printk(" SP: %08"PRIx32" LR: %08"PRIx32"\n", regs->sp, regs->lr);
+ printk("\n");
+ }
+
+ printk("HTTBR %"PRIx64"\n", READ_CP64(HTTBR));
+ printk("HDFAR %"PRIx32"\n", READ_CP32(HDFAR));
+ printk("HIFAR %"PRIx32"\n", READ_CP32(HIFAR));
+ printk("HPFAR %"PRIx32"\n", READ_CP32(HPFAR));
+ printk("HCR %08"PRIx32"\n", READ_CP32(HCR));
+ printk("HSR %"PRIx32"\n", READ_CP32(HSR));
+ printk("\n");
+
+ printk("DFSR %"PRIx32" DFAR %"PRIx32"\n", READ_CP32(DFSR),
READ_CP32(DFAR));
+ printk("IFSR %"PRIx32" IFAR %"PRIx32"\n", READ_CP32(IFSR),
READ_CP32(IFAR));
+ printk("\n");
+}
+
+static void show_guest_stack(struct cpu_user_regs *regs)
+{
+ printk("GUEST STACK GOES HERE\n");
+}
+
+#define STACK_BEFORE_EXCEPTION(regs) ((uint32_t*)(regs)->sp)
+
+static void show_trace(struct cpu_user_regs *regs)
+{
+ uint32_t *frame, next, addr, low, high;
+
+ printk("Xen call trace:\n ");
+
+ printk("[<%p>]", _p(regs->pc));
+ print_symbol(" %s\n ", regs->pc);
+
+ /* Bounds for range of valid frame pointer. */
+ low = (uint32_t)(STACK_BEFORE_EXCEPTION(regs)/* - 2*/);
+ high = (low & ~(STACK_SIZE - 1)) +
+ (STACK_SIZE - sizeof(struct cpu_info));
+
+ /* Frame:
+ * (largest address)
+ * | cpu_info
+ * | [...] |
+ * | return addr <-----------------, |
+ * | fp --------------------------------+----'
+ * | [...] |
+ * | return addr <------------, |
+ * | fp ---------------------------+----'
+ * | [...] |
+ * | return addr <- regs->fp |
+ * | fp ---------------------------'
+ * |
+ * v (smallest address, sp)
+ */
+
+ /* The initial frame pointer. */
+ next = regs->fp;
+
+ for ( ; ; )
+ {
+ if ( (next < low) || (next >= high) )
+ break;
+ {
+ /* Ordinary stack frame. */
+ frame = (uint32_t *)next;
+ next = frame[-1];
+ addr = frame[0];
+ }
+
+ printk("[<%p>]", _p(addr));
+ print_symbol(" %s\n ", addr);
+
+ low = (uint32_t)&frame[1];
+ }
+
+ printk("\n");
+}
+
+void show_stack(struct cpu_user_regs *regs)
+{
+ uint32_t *stack = STACK_BEFORE_EXCEPTION(regs), addr;
+ int i;
+
+ if ( guest_mode(regs) )
+ return show_guest_stack(regs);
+
+ printk("Xen stack trace from sp=%p:\n ", stack);
+
+ for ( i = 0; i < (debug_stack_lines*stack_words_per_line); i++ )
+ {
+ if ( ((long)stack & (STACK_SIZE-BYTES_PER_LONG)) == 0 )
+ break;
+ if ( (i != 0) && ((i % stack_words_per_line) == 0) )
+ printk("\n ");
+
+ addr = *stack++;
+ printk(" %p", _p(addr));
+ }
+ if ( i == 0 )
+ printk("Stack empty.");
+ printk("\n");
+
+ show_trace(regs);
+}
+
+void show_execution_state(struct cpu_user_regs *regs)
+{
+ show_registers(regs);
+ show_stack(regs);
+}
+
+static void do_unexpected_trap(const char *msg, struct cpu_user_regs *regs)
+{
+ printk("Unexpected Trap: %s\n", msg);
+ show_execution_state(regs);
+ while(1);
+}
+
+asmlinkage void do_trap_undefined_instruction(struct cpu_user_regs *regs)
+{
+ do_unexpected_trap("Undefined Instruction", regs);
+}
+
+asmlinkage void do_trap_supervisor_call(struct cpu_user_regs *regs)
+{
+ do_unexpected_trap("Supervisor Call", regs);
+}
+
+asmlinkage void do_trap_prefetch_abort(struct cpu_user_regs *regs)
+{
+ do_unexpected_trap("Prefetch Abort", regs);
+}
+
+asmlinkage void do_trap_data_abort(struct cpu_user_regs *regs)
+{
+ do_unexpected_trap("Data Abort", regs);
+}
+
+unsigned long do_arch_0(unsigned int cmd, unsigned long long value)
+{
+ printk("do_arch_0 cmd=%x arg=%llx\n", cmd, value);
+ return 0;
+}
+
+typedef unsigned long arm_hypercall_t(
+ unsigned int, unsigned int, unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int, unsigned int, unsigned int);
+
+#define HYPERCALL(x) \
+ [ __HYPERVISOR_ ## x ] = (arm_hypercall_t *) do_ ## x
+
+static arm_hypercall_t *arm_hypercall_table[] = {
+ HYPERCALL(arch_0),
+ HYPERCALL(sched_op),
+ HYPERCALL(console_io),
+};
+
+static void do_debug_trap(struct cpu_user_regs *regs, unsigned int code)
+{
+ uint32_t reg, *r;
+
+ switch ( code ) {
+ case 0xe0 ... 0xef:
+ reg = code - 0xe0;
+ r = ®s->r0 + reg;
+ printk("R%d = %#010"PRIx32" at %#010"PRIx32"\n", reg, *r, regs->pc);
+ break;
+ case 0xfd:
+ printk("Reached %08"PRIx32"\n", regs->pc);
+ break;
+ case 0xfe:
+ printk("%c", (char)(regs->r0 & 0xff));
+ break;
+ case 0xff:
+ printk("DEBUG\n");
+ show_execution_state(regs);
+ break;
+ default:
+ panic("Unhandled debug trap %#x\n", code);
+ break;
+ }
+}
+
+static void do_trap_hypercall(struct cpu_user_regs *regs, unsigned long iss)
+{
+ local_irq_enable();
+
+ regs->r0 = arm_hypercall_table[iss](regs->r0,
+ regs->r1,
+ regs->r2,
+ regs->r3,
+ regs->r4,
+ regs->r5,
+ regs->r6,
+ regs->r7,
+ regs->r8,
+ regs->r9);
+}
+
+static void do_cp15_32(struct cpu_user_regs *regs,
+ union hsr hsr)
+{
+ struct hsr_cp32 cp32 = hsr.cp32;
+ uint32_t *r = ®s->r0 + cp32.reg;
+
+ if ( !cp32.ccvalid ) {
+ dprintk(XENLOG_ERR, "cp_15(32): need to handle invalid condition
codes\n");
+ domain_crash_synchronous();
+ }
+ if ( cp32.cc != 0xe ) {
+ dprintk(XENLOG_ERR, "cp_15(32): need to handle condition codes %x\n",
+ cp32.cc);
+ domain_crash_synchronous();
+ }
+
+ switch ( hsr.bits & HSR_CP32_REGS_MASK )
+ {
+ case HSR_CPREG32(CLIDR):
+ if ( !cp32.read )
+ {
+ dprintk(XENLOG_ERR,
+ "attempt to write to read-only register CLIDR\n");
+ domain_crash_synchronous();
+ }
+ *r = READ_CP32(CLIDR);
+ break;
+ case HSR_CPREG32(CCSIDR):
+ if ( !cp32.read )
+ {
+ dprintk(XENLOG_ERR,
+ "attempt to write to read-only register CSSIDR\n");
+ domain_crash_synchronous();
+ }
+ *r = READ_CP32(CCSIDR);
+ break;
+ case HSR_CPREG32(DCCISW):
+ if ( cp32.read )
+ {
+ dprintk(XENLOG_ERR,
+ "attempt to read from write-only register DCCISW\n");
+ domain_crash_synchronous();
+ }
+ WRITE_CP32(*r, DCCISW);
+ break;
+ case HSR_CPREG32(CNTP_CTL):
+ case HSR_CPREG32(CNTP_TVAL):
+ /* emulate timer */
+ break;
+ default:
+ printk("%s p15, %d, r%d, cr%d, cr%d, %d @ %#08x\n",
+ cp32.read ? "mrc" : "mcr",
+ cp32.op1, cp32.reg, cp32.crn, cp32.crm, cp32.op2, regs->pc);
+ panic("unhandled 32-bit CP15 access %#x\n", hsr.bits &
HSR_CP32_REGS_MASK);
+ }
+ regs->pc += cp32.len ? 4 : 2;
+
+}
+
+static void do_cp15_64(struct cpu_user_regs *regs,
+ union hsr hsr)
+{
+ struct hsr_cp64 cp64 = hsr.cp64;
+
+ if ( !cp64.ccvalid ) {
+ dprintk(XENLOG_ERR, "cp_15(64): need to handle invalid condition
codes\n");
+ domain_crash_synchronous();
+ }
+ if ( cp64.cc != 0xe ) {
+ dprintk(XENLOG_ERR, "cp_15(64): need to handle condition codes %x\n",
+ cp64.cc);
+ domain_crash_synchronous();
+ }
+
+ switch ( hsr.bits & HSR_CP64_REGS_MASK )
+ {
+ case HSR_CPREG64(CNTPCT):
+ /* emulate timer */
+ break;
+ default:
+ printk("%s p15, %d, r%d, r%d, cr%d @ %#08x\n",
+ cp64.read ? "mrrc" : "mcrr",
+ cp64.op1, cp64.reg1, cp64.reg2, cp64.crm, regs->pc);
+ panic("unhandled 64-bit CP15 access %#x\n", hsr.bits &
HSR_CP64_REGS_MASK);
+ }
+ regs->pc += cp64.len ? 4 : 2;
+
+}
+
+static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
+ struct hsr_dabt dabt)
+{
+ const char *msg;
+ int level = -1;
+ mmio_info_t info;
+
+ if (dabt.s1ptw)
+ goto bad_data_abort;
+
+ info.dabt = dabt;
+ info.gva = READ_CP32(HDFAR);
+ info.gpa = gva_to_ipa(info.gva);
+
+ if (handle_mmio(&info))
+ {
+ regs->pc += dabt.len ? 4 : 2;
+ return;
+ }
+
+bad_data_abort:
+
+ msg = decode_fsc( dabt.dfsc, &level);
+
+ printk("Guest data abort: %s%s%s\n"
+ " gva=%"PRIx32" gpa=%"PRIpaddr"\n",
+ msg, dabt.s1ptw ? " S2 during S1" : "",
+ fsc_level_str(level),
+ info.gva, info.gpa);
+ if (dabt.valid)
+ printk(" size=%d sign=%d write=%d reg=%d\n",
+ dabt.size, dabt.sign, dabt.write, dabt.reg);
+ else
+ printk(" instruction syndrome invalid\n");
+ printk(" eat=%d cm=%d s1ptw=%d dfsc=%d\n",
+ dabt.eat, dabt.cache, dabt.s1ptw, dabt.dfsc);
+
+ show_execution_state(regs);
+ panic("Unhandled guest data abort\n");
+}
+
+asmlinkage void do_trap_hypervisor(struct cpu_user_regs *regs)
+{
+ union hsr hsr = { .bits = READ_CP32(HSR) };
+
+ switch (hsr.ec) {
+ case HSR_EC_CP15_32:
+ do_cp15_32(regs, hsr);
+ break;
+ case HSR_EC_CP15_64:
+ do_cp15_64(regs, hsr);
+ break;
+ case HSR_EC_HVC:
+ if ( (hsr.iss & 0xff00) == 0xff00 )
+ return do_debug_trap(regs, hsr.iss & 0x00ff);
+ do_trap_hypercall(regs, hsr.iss);
+ break;
+ case HSR_EC_DATA_ABORT_GUEST:
+ do_trap_data_abort_guest(regs, hsr.dabt);
+ break;
+ default:
+ printk("Hypervisor Trap. HSR=0x%x EC=0x%x IL=%x Syndrome=%"PRIx32"\n",
+ hsr.bits, hsr.ec, hsr.len, hsr.iss);
+ do_unexpected_trap("Hypervisor", regs);
+ }
+}
+
+asmlinkage void do_trap_irq(struct cpu_user_regs *regs)
+{
+ gic_interrupt(regs, 0);
+}
+
+asmlinkage void do_trap_fiq(struct cpu_user_regs *regs)
+{
+ gic_interrupt(regs, 1);
+}
+
+asmlinkage void leave_hypervisor_tail(void)
+{
+ while (1)
+ {
+ local_irq_disable();
+ if (!softirq_pending(smp_processor_id()))
+ return;
+ local_irq_enable();
+ do_softirq();
+ }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
1.7.2.5
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |