From 6fca397871cd461a0c50ac23d5b3552000f35316 Mon Sep 17 00:00:00 2001 From: Liviu Ionescu Date: Tue, 26 Jul 2016 01:09:25 +0300 Subject: [PATCH] BKPT, if DHCSR.C_DEBUGEN, interrupts CPU - add some more registers for SCB, DCB --- gdbstub.c | 17 ++++++ hw/cortexm/cortexm-nvic.c | 91 ++++++++++++++++++++++++++----- include/exec/gdbstub.h | 4 ++ include/hw/cortexm/cortexm-nvic.h | 23 ++++++++ target-arm/helper.c | 48 ++++++++++++++++ 5 files changed, 170 insertions(+), 13 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 1f864a5bb6..d2421ae5ca 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1252,6 +1252,12 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) const char *type; int ret; +#if defined(CONFIG_GNU_ARM_ECLIPSE) +#if defined(DEBUG_GDB) + printf("vm_state %d, %d\n", running, state); +#endif +#endif + if (running || s->state == RS_INACTIVE) { return; } @@ -1498,6 +1504,10 @@ void gdb_exit(CPUArchState *env, int code) #ifndef CONFIG_USER_ONLY qemu_chr_delete(s->chr); #endif + +#if defined(CONFIG_GNU_ARM_ECLIPSE) + gdbserver_state = NULL; +#endif } #ifdef CONFIG_USER_ONLY @@ -1804,3 +1814,10 @@ int gdbserver_start(const char *device) return 0; } #endif + +#if defined(CONFIG_GNU_ARM_ECLIPSE) +int gdbserver_is_started(void) +{ + return (gdbserver_state != NULL); +} +#endif diff --git a/hw/cortexm/cortexm-nvic.c b/hw/cortexm/cortexm-nvic.c index 631b663ae7..eb0a8be136 100644 --- a/hw/cortexm/cortexm-nvic.c +++ b/hw/cortexm/cortexm-nvic.c @@ -24,6 +24,12 @@ * NVIC. Much of that is also implemented here. */ +#include "config.h" +#if defined(CONFIG_GNU_ARM_ECLIPSE) +#include "sysemu/sysemu.h" +#include "exec/gdbstub.h" +#endif + #include "hw/sysbus.h" #include "qemu/timer.h" #include "hw/arm/arm.h" @@ -187,6 +193,9 @@ static uint32_t nvic_readl(CortexMNVICState *s, uint32_t offset) } case 0x1c: /* SysTick Calibration Value. */ return 10000; + + // System Control Block 0xE000ED00 - 0xE000ED8C + case 0xd00: /* CPUID Base. */ cpu = ARM_CPU(current_cpu); return cpu->midr; @@ -264,18 +273,26 @@ static uint32_t nvic_readl(CortexMNVICState *s, uint32_t offset) if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18); return val; + case 0xd28: /* Configurable Fault Status. */ - /* TODO: Implement Fault Status. */ - qemu_log_mask(LOG_UNIMP, "Configurable Fault Status unimplemented\n"); - return 0; + return s->scb.cfsr; + case 0xd2c: /* Hard Fault Status. */ + return s->scb.hfsr & 0xC0000003; + case 0xd30: /* Debug Fault Status. */ + return s->scb.dfsr; + case 0xd34: /* Mem Manage Address. */ + return s->scb.mmfar; + case 0xd38: /* Bus Fault Address. */ + return s->scb.bfar; + case 0xd3c: /* Aux Fault Status. */ - /* TODO: Implement fault status registers. */ - qemu_log_mask(LOG_UNIMP, "Fault status registers unimplemented\n"); - return 0; + return s->scb.afsr; + + // Reserved for CPUID registers, 0xE000ED40 - 0xE000ED84 case 0xd40: /* PFR0. */ return 0x00000030; case 0xd44: /* PRF1. */ @@ -302,16 +319,26 @@ static uint32_t nvic_readl(CortexMNVICState *s, uint32_t offset) return 0x01111110; case 0xd70: /* ISAR4. */ return 0x01310102; + #if defined(CONFIG_GNU_ARM_ECLIPSE) + + // Debug Control Block 0xE000EDF0 - 0xE000EEFF + case 0xDF0: /* DHCSR. */ + return s->dcb.dhcsr & 0x0000001F; + case 0xDF4: /* DCRSR. */ + qemu_log_mask(LOG_GUEST_ERROR, "NVIC: read WO DCRSR\n"); + return 0; + case 0xDF8: /* DCRDR. */ + return s->dcb.dcrdr; + case 0xDFC: /* DEMCR. */ - /* TODO: Implement debug registers. */ - qemu_log_mask(LOG_UNIMP, "NVIC: debug register %08X unimplemented\n", - offset); - return 0; + return s->dcb.demcr & 0x10F03F1; + #endif + default: qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset); return 0; @@ -355,6 +382,8 @@ static void nvic_writel(CortexMNVICState *s, uint32_t offset, uint32_t value) systick_reload(s, 1); s->systick.control &= ~SYSTICK_COUNTFLAG; break; + + // System Control Block 0xE000ED00 - 0xE000ED8C case 0xd04: /* Interrupt Control State. */ if (value & (1 << 31)) { cortexm_nvic_set_pending(s, ARMV7M_EXCP_NMI); @@ -413,7 +442,9 @@ static void nvic_writel(CortexMNVICState *s, uint32_t offset, uint32_t value) qemu_log_mask(LOG_UNIMP, "NVIC: fault status registers unimplemented\n"); break; + #if defined(CONFIG_GNU_ARM_ECLIPSE) + case 0xD88: /* CPACR. */ if (value & (((3UL << 10 * 2) | (3UL << 11 * 2)))) { /* Attempt to enable CP10 & CP11 (the FPU). */ @@ -423,15 +454,31 @@ static void nvic_writel(CortexMNVICState *s, uint32_t offset, uint32_t value) } break; + // Debug Control Block 0xE000EDF0 - 0xE000EEFF + // All registers are 32-bits wide. + // See also SCB.DFSR 0xE000ED30 + case 0xDF0: /* DHCSR. */ + if ((value & 0xFFFF0000) == 0xA05F0000) { + s->dcb.dhcsr = value & 0x0000001E; + } + break; + case 0xDF4: /* DCRSR. */ + s->dcb.dcrsr = value & 0x0001003F; + // TODO: implement read/write register + break; + case 0xDF8: /* DCRDR. */ + s->dcb.dcrdr = value; + break; + case 0xDFC: /* DEMCR. */ - /* TODO: Implement debug registers. */ - qemu_log_mask(LOG_UNIMP, "NVIC: debug register %08X unimplemented\n", - offset); + s->dcb.demcr = value & 0x010F03F1; break; + #endif + case 0xf00: /* Software Triggered Interrupt Register */ if ((value & 0x1ff) < s->num_irq) { gic_set_pending_private(&s->gic, 0, value & 0x1ff); @@ -608,6 +655,24 @@ static void cortexm_nvic_reset_callback(DeviceState *dev) /* The NVIC as a whole is always enabled. */ s->gic.ctlr = 1; systick_reset(s); + + // System Control Block + s->scb.scr = 0; + s->scb.ccr = 0; // Implementation dependent + s->scb.cfsr = 0; + s->scb.hfsr = 0; + s->scb.dfsr = 0; + s->scb.mmfar = 0; + s->scb.bfar = 0; + s->scb.afsr = 0; + + // Debug Control Block + + s->dcb.dhcsr = gdbserver_is_started() ? 1 : 0; // C_DEBUGEN[0] + + s->dcb.dcrsr = 0; + s->dcb.dcrdr = 0; + s->dcb.demcr = 0; } static void cortexm_nvic_class_init_callback(ObjectClass *klass, void *data) diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index d9e8cf7715..901427304c 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -111,6 +111,10 @@ int gdbserver_start(int); int gdbserver_start(const char *port); #endif +#if defined(CONFIG_GNU_ARM_ECLIPSE) +int gdbserver_is_started(void); +#endif + /** * gdb_has_xml: * This is an ugly hack to cope with both new and old gdb. diff --git a/include/hw/cortexm/cortexm-nvic.h b/include/hw/cortexm/cortexm-nvic.h index ed3d19b2ed..f520049ea4 100644 --- a/include/hw/cortexm/cortexm-nvic.h +++ b/include/hw/cortexm/cortexm-nvic.h @@ -78,6 +78,29 @@ typedef struct { MemoryRegion container; uint32_t num_irq; qemu_irq sysresetreq; + + // System Control Block 0xE000ED00 - 0xE000ED8C + struct { + uint32_t scr; // 0xE000ED10, RW, 0x00000000, System Control Block + uint32_t ccr; // 0xE000ED14, RW, IMPL, Configuration & Control Register + // 0xE000ED24, RW, System Handler Control and State Register + uint32_t cfsr; // 0xE000ED28, RW, 0x00000000, Configurable Fault Status Register + uint32_t hfsr; // 0xE000ED2C, RW, 0x00000000, Hard Fault Status Register + uint32_t dfsr; // 0xE000ED30, RW, 0x00000000, Debug Fault Status Register + uint32_t mmfar; // 0xE000ED34, RW, UNK, MemManage Fault Address Register + uint32_t bfar; // 0xE000ED38, RW, UNK, Bus Fault Address Register + uint32_t afsr; // 0xE000ED3C, RW, UNK, Auxiliary Fault Status Register + } scb; + + // Debug Control Block 0xE000EDF0 - 0xE000EEFF + // All registers are 32-bits wide. + struct { + uint32_t dhcsr; // 0xE000EDF0, RW, Debug Halting Control and Status Register, + uint32_t dcrsr; // 0xE000EDF4, WO, Debug Core Register Selector Register + uint32_t dcrdr; // 0xE000EDF8, RW, Debug Core Register Data Register + uint32_t demcr; // 0xE000EDFC, RW, 0x00000000, Debug Exception and Monitor Control Register + } dcb; + } CortexMNVICState; /* ------------------------------------------------------------------------- */ diff --git a/target-arm/helper.c b/target-arm/helper.c index 0960c0b0aa..4edf61a781 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -15,6 +15,7 @@ #if defined(CONFIG_GNU_ARM_ECLIPSE) #include "hw/intc/gic_internal.h" +#include "hw/cortexm/cortexm-nvic.h" #endif #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ @@ -5577,6 +5578,43 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM); return; case EXCP_BKPT: + +#if defined(CONFIG_GNU_ARM_ECLIPSE) + /* + * The Breakpoint (BKPT) instruction provides for software + * breakpoints. It can generate a DebugMonitor exception or + * cause a running system to halt depending on the debug + * configuration. + * + * C1.5 Debug event behavior + * + * Entry to Debug state. If halting debug is enabled, a + * debug event halts the processor in Debug state. Setting + * the DHCSR.C_DEBUGEN bit to 1 enables halting debug. + * + * A DebugMonitor exception. If halting debug is disabled and + * the DebugMonitor exception is enabled, a debug event causes + * a DebugMonitor exception when the group priority of the + * DebugMonitor exception is greater than the current execution + * priority. + * + * Debug Fault Status Register, DFSR + * Shows which debug event occurred. + * BKPT, bit[1] Indicates a debug event generated by BKPT + * instruction execution or a breakpoint match in FPB: + * 0 No breakpoint debug event. + * 1 At least one breakpoint debug event. + * + * HardFault on breakpoint (BKPT) escalation + * Status bit HFSR.DEBUGEVT + * Vector catch bit DEMCR.VC_HARDERR + * A BKPT instruction is executed while halting debug is + * disabled and the DebugMonitor is disabled or the DebugMonitor + * priority is lower than or equal to the execution priority. + * The exception escalates to a HardFault. + */ +#endif + if (semihosting_enabled()) { int nr; nr = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff; @@ -5589,6 +5627,16 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) return; } } + +#if defined(CONFIG_GNU_ARM_ECLIPSE) + CortexMNVICState* nvic = CORTEXM_NVIC_STATE(env->nvic); + // Check DHCSR.C_DEBUGEN + if (nvic->dcb.dhcsr & 1) { + cpu_interrupt(cs, CPU_INTERRUPT_DEBUG); + return; + } +#endif + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG); return; case EXCP_IRQ: