blackmagic/src/cortexm3.c

574 lines
16 KiB
C

/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2011 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* 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 3 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file implements debugging functionality specific to ARM
* the Cortex-M3 core. This should be generic to ARMv7-M as it is
* implemented according to the "ARMv7-M Architectue Reference Manual",
* ARM doc DDI0403C.
*
* Issues:
* There are way too many magic numbers used here.
*/
#include <stdio.h>
#include "general.h"
#include "jtagtap.h"
#include "jtag_scan.h"
#include "adiv5.h"
#include "target.h"
#include "cortexm3.h"
#include "lmi.h"
#include "stm32_tgt.h"
static char cm3_driver_str[] = "ARM Cortex-M3";
/* Private peripheral bus base address */
#define CM3_PPB_BASE 0xE0000000
#define CM3_SCS_BASE (CM3_PPB_BASE + 0xE000)
#define CM3_AIRCR (CM3_SCS_BASE + 0xD0C)
#define CM3_HFSR (CM3_SCS_BASE + 0xD2C)
#define CM3_DFSR (CM3_SCS_BASE + 0xD30)
#define CM3_DHCSR (CM3_SCS_BASE + 0xDF0)
#define CM3_DCRSR (CM3_SCS_BASE + 0xDF4)
#define CM3_DCRDR (CM3_SCS_BASE + 0xDF8)
#define CM3_DEMCR (CM3_SCS_BASE + 0xDFC)
#define CM3_FPB_BASE (CM3_PPB_BASE + 0x2000)
/* ARM Literature uses FP_*, we use CM3_FPB_* consistently */
#define CM3_FPB_CTRL (CM3_FPB_BASE + 0x000)
#define CM3_FPB_REMAP (CM3_FPB_BASE + 0x004)
#define CM3_FPB_COMP(i) (CM3_FPB_BASE + 0x008 + (4*(i)))
#define CM3_DWT_BASE (CM3_PPB_BASE + 0x1000)
#define CM3_DWT_CTRL (CM3_DWT_BASE + 0x000)
#define CM3_DWT_COMP(i) (CM3_DWT_BASE + 0x020 + (0x10*(i)))
#define CM3_DWT_MASK(i) (CM3_DWT_BASE + 0x024 + (0x10*(i)))
#define CM3_DWT_FUNC(i) (CM3_DWT_BASE + 0x028 + (0x10*(i)))
/* Application Interrupt and Reset Control Register (AIRCR) */
#define CM3_AIRCR_VECTKEY (0x05FA << 16)
/* Bits 31:16 - Read as VECTKETSTAT, 0xFA05 */
#define CM3_AIRCR_ENDIANESS (1 << 15)
/* Bits 15:11 - Unused, reserved */
#define CM3_AIRCR_PRIGROUP (7 << 8)
/* Bits 7:3 - Unused, reserved */
#define CM3_AIRCR_SYSRESETREQ (1 << 2)
#define CM3_AIRCR_VECTCLRACTIVE (1 << 1)
#define CM3_AIRCR_VECTRESET (1 << 0)
/* HardFault Status Register (HFSR) */
#define CM3_HFSR_DEBUGEVT (1 << 31)
#define CM3_HFSR_FORCED (1 << 30)
/* Bits 29:2 - Not specified */
#define CM3_HFSR_VECTTBL (1 << 1)
/* Bits 0 - Reserved */
/* Debug Fault Status Register (DFSR) */
/* Bits 31:5 - Reserved */
#define CM3_DFSR_RESETALL 0x1F
#define CM3_DFSR_EXTERNAL (1 << 4)
#define CM3_DFSR_VCATCH (1 << 3)
#define CM3_DFSR_DWTTRAP (1 << 2)
#define CM3_DFSR_BKPT (1 << 1)
#define CM3_DFSR_HALTED (1 << 0)
/* Debug Halting Control and Status Register (DHCSR) */
/* This key must be written to bits 31:16 for write to take effect */
#define CM3_DHCSR_DBGKEY 0xA05F0000
/* Bits 31:26 - Reserved */
#define CM3_DHCSR_S_RESET_ST (1 << 25)
#define CM3_DHCSR_S_RETIRE_ST (1 << 24)
/* Bits 23:20 - Reserved */
#define CM3_DHCSR_S_LOCKUP (1 << 19)
#define CM3_DHCSR_S_SLEEP (1 << 18)
#define CM3_DHCSR_S_HALT (1 << 17)
#define CM3_DHCSR_S_REGRDY (1 << 16)
/* Bits 15:6 - Reserved */
#define CM3_DHCSR_C_SNAPSTALL (1 << 5)
/* Bit 4 - Reserved */
#define CM3_DHCSR_C_MASKINTS (1 << 3)
#define CM3_DHCSR_C_STEP (1 << 2)
#define CM3_DHCSR_C_HALT (1 << 1)
#define CM3_DHCSR_C_DEBUGEN (1 << 0)
/* Debug Core Register Selector Register (DCRSR) */
#define CM3_DCRSR_REGSEL_MASK 0x0000001F
#define CM3_DCRSR_REGSEL_XPSR 0x00000010
#define CM3_DCRSR_REGSEL_MSP 0x00000011
#define CM3_DCRSR_REGSEL_PSP 0x00000012
/* Debug Exception and Monitor Control Register (DEMCR) */
/* Bits 31:25 - Reserved */
#define CM3_DEMCR_TRCENA (1 << 24)
/* Bits 23:20 - Reserved */
#define CM3_DEMCR_MON_REQ (1 << 19)
#define CM3_DEMCR_MON_STEP (1 << 18)
#define CM3_DEMCR_VC_MON_PEND (1 << 17)
#define CM3_DEMCR_VC_MON_EN (1 << 16)
/* Bits 15:11 - Reserved */
#define CM3_DEMCR_VC_HARDERR (1 << 10)
#define CM3_DEMCR_VC_INTERR (1 << 9)
#define CM3_DEMCR_VC_BUSERR (1 << 8)
#define CM3_DEMCR_VC_STATERR (1 << 7)
#define CM3_DEMCR_VC_CHKERR (1 << 6)
#define CM3_DEMCR_VC_NOCPERR (1 << 5)
#define CM3_DEMCR_VC_MMERR (1 << 4)
/* Bits 3:1 - Reserved */
#define CM3_DEMCR_VC_CORERESET (1 << 0)
/* Flash Patch and Breakpoint Control Register (FP_CTRL) */
/* Bits 32:15 - Reserved */
/* Bits 14:12 - NUM_CODE2 */
/* Bits 11:8 - NUM_LIT */
/* Bits 7:4 - NUM_CODE1 */
/* Bits 3:2 - Unspecified */
#define CM3_FPB_CTRL_KEY (1 << 1)
#define CM3_FPB_CTRL_ENABLE (1 << 0)
/* Data Watchpoint and Trace Mask Register (DWT_MASKx) */
#define CM3_DWT_MASK_BYTE (0 << 0)
#define CM3_DWT_MASK_HALFWORD (1 << 0)
#define CM3_DWT_MASK_WORD (3 << 0)
/* Data Watchpoint and Trace Function Register (DWT_FUNCTIONx) */
#define CM3_DWT_FUNC_MATCHED (1 << 24)
#define CM3_DWT_FUNC_DATAVSIZE_WORD (2 << 10)
#define CM3_DWT_FUNC_FUNC_READ (5 << 0)
#define CM3_DWT_FUNC_FUNC_WRITE (6 << 0)
#define CM3_DWT_FUNC_FUNC_ACCESS (7 << 0)
static void cm3_attach(struct target_s *target);
static void cm3_detach(struct target_s *target);
static int cm3_regs_read(struct target_s *target, void *data);
static int cm3_regs_write(struct target_s *target, const void *data);
static int cm3_pc_write(struct target_s *target, const uint32_t val);
static void cm3_reset(struct target_s *target);
static void cm3_halt_resume(struct target_s *target, uint8_t step);
static int cm3_halt_wait(struct target_s *target);
static void cm3_halt_request(struct target_s *target);
static int cm3_fault_unwind(struct target_s *target);
static int cm3_set_hw_bp(struct target_s *target, uint32_t addr);
static int cm3_clear_hw_bp(struct target_s *target, uint32_t addr);
static int cm3_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
static int cm3_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len);
static int cm3_check_hw_wp(struct target_s *target, uint32_t *addr);
/* Watchpoint unit status */
static struct wp_unit_s {
uint32_t addr;
uint8_t type;
uint8_t size;
} hw_watchpoint[4];
/* Breakpoint unit status */
static uint32_t hw_breakpoint[6];
int
cm3_probe(struct target_s *target)
{
target->driver = cm3_driver_str;
target->attach = cm3_attach;
target->detach = cm3_detach;
/* Should probe here to make sure it's Cortex-M3 */
target->regs_read = cm3_regs_read;
target->regs_write = cm3_regs_write;
target->pc_write = cm3_pc_write;
target->reset = cm3_reset;
target->halt_request = cm3_halt_request;
target->halt_wait = cm3_halt_wait;
target->halt_resume = cm3_halt_resume;
target->fault_unwind = cm3_fault_unwind;
target->regs_size = 16<<2;
/* if not STM32 try LMI */
if(stm32_probe(target) != 0)
lmi_probe(target);
return 0;
}
static void
cm3_attach(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
int i;
target_halt_request(target);
while(!target_halt_wait(target));
/* Request halt on reset */
adiv5_ap_mem_write(t->ap, CM3_DEMCR,
CM3_DEMCR_TRCENA | CM3_DEMCR_VC_HARDERR |
CM3_DEMCR_VC_CORERESET);
/* Reset DFSR flags */
adiv5_ap_mem_write(t->ap, CM3_DFSR, CM3_DFSR_RESETALL);
/* Clear any stale breakpoints */
for(i = 0; i < 6; i++) {
adiv5_ap_mem_write(t->ap, CM3_FPB_COMP(i), 0);
hw_breakpoint[i] = 0;
}
/* Clear any stale watchpoints */
for(i = 0; i < 4; i++) {
adiv5_ap_mem_write(t->ap, CM3_DWT_FUNC(i), 0);
hw_watchpoint[i].type = 0;
}
/* Flash Patch Control Register: set ENABLE */
adiv5_ap_mem_write(t->ap, CM3_FPB_CTRL,
CM3_FPB_CTRL_KEY | CM3_FPB_CTRL_ENABLE);
target->set_hw_bp = cm3_set_hw_bp;
target->clear_hw_bp = cm3_clear_hw_bp;
/* Data Watchpoint and Trace */
target->set_hw_wp = cm3_set_hw_wp;
target->clear_hw_wp = cm3_clear_hw_wp;
target->check_hw_wp = cm3_check_hw_wp;
}
static void
cm3_detach(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
int i;
/* Clear any stale breakpoints */
for(i = 0; i < 6; i++)
adiv5_ap_mem_write(t->ap, CM3_FPB_COMP(i), 0);
/* Clear any stale watchpoints */
for(i = 0; i < 4; i++)
adiv5_ap_mem_write(t->ap, CM3_DWT_FUNC(i), 0);
/* Disable debug */
adiv5_ap_mem_write(t->ap, CM3_DHCSR, CM3_DHCSR_DBGKEY);
}
static int
cm3_regs_read(struct target_s *target, void *data)
{
struct target_ap_s *t = (void *)target;
uint32_t *regs = data;
int i;
/* FIXME: Describe what's really going on here */
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0xE000EDF0);
adiv5_ap_write(t->ap, 0x14, 0); /* Required to switch banks */
*regs++ = adiv5_dp_read_ap(t->ap->dp, 0x18);
for(i = 1; i < 16; i++) {
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, i);
*regs++ = adiv5_dp_read_ap(t->ap->dp, 0x18);
}
return 0;
}
static int
cm3_regs_write(struct target_s *target, const void *data)
{
struct target_ap_s *t = (void *)target;
const uint32_t *regs = data;
int i;
/* FIXME: Describe what's really going on here */
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0xE000EDF0);
adiv5_ap_write(t->ap, 0x18, *regs++); /* Required to switch banks */
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, 0x10000);
for(i = 1; i < 16; i++) {
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x18, *regs++);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, i | 0x10000);
}
return 0;
}
static int
cm3_pc_write(struct target_s *target, const uint32_t val)
{
struct target_ap_s *t = (void *)target;
adiv5_ap_write(t->ap, 0x00, 0xA2000052);
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x04, 0xE000EDF0);
adiv5_ap_write(t->ap, 0x18, val); /* Required to switch banks */
adiv5_dp_low_access(t->ap->dp, 1, 0, 0x14, 0x1000F);
return 0;
}
/* The following three routines implement target halt/resume
* using the core debug registers in the NVIC. */
static void
cm3_reset(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
jtagtap_srst();
/* Request system reset from NVIC: SRST doesn't work correctly */
/* This could be VECTRESET: 0x05FA0001 (reset only core)
* or SYSRESETREQ: 0x05FA0004 (system reset)
*/
adiv5_ap_mem_write(t->ap, CM3_AIRCR,
CM3_AIRCR_VECTKEY | CM3_AIRCR_SYSRESETREQ);
/* Poll for release from reset */
while(adiv5_ap_mem_read(t->ap, CM3_AIRCR) &
(CM3_AIRCR_VECTRESET | CM3_AIRCR_SYSRESETREQ));
/* Reset DFSR flags */
adiv5_ap_mem_write(t->ap, CM3_DFSR, CM3_DFSR_RESETALL);
}
static void
cm3_halt_request(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
adiv5_ap_mem_write(t->ap, CM3_DHCSR,
CM3_DHCSR_DBGKEY | CM3_DHCSR_C_HALT | CM3_DHCSR_C_DEBUGEN);
}
static int
cm3_halt_wait(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
return adiv5_ap_mem_read(t->ap, CM3_DHCSR) & CM3_DHCSR_S_HALT;
}
static void
cm3_halt_resume(struct target_s *target, uint8_t step)
{
struct target_ap_s *t = (void *)target;
static uint8_t old_step = 0;
uint32_t dhcsr = CM3_DHCSR_DBGKEY | CM3_DHCSR_C_DEBUGEN;
if(step) dhcsr |= CM3_DHCSR_C_STEP | CM3_DHCSR_C_MASKINTS;
/* Disable interrupts while single stepping... */
if(step != old_step) {
adiv5_ap_mem_write(t->ap, CM3_DHCSR, dhcsr | CM3_DHCSR_C_HALT);
old_step = step;
}
adiv5_ap_mem_write(t->ap, CM3_DHCSR, dhcsr);
}
static int cm3_fault_unwind(struct target_s *target)
{
struct target_ap_s *t = (void *)target;
uint32_t dfsr = adiv5_ap_mem_read(t->ap, CM3_DFSR);
uint32_t hfsr = adiv5_ap_mem_read(t->ap, CM3_HFSR);
adiv5_ap_mem_write(t->ap, CM3_DFSR, dfsr);/* write back to reset */
adiv5_ap_mem_write(t->ap, CM3_HFSR, hfsr);/* write back to reset */
/* We check for FORCED in the HardFault Status Register to avoid
* catching core resets */
if((dfsr & CM3_DFSR_VCATCH) && (hfsr & CM3_HFSR_FORCED)) {
/* Unwind exception */
uint32_t regs[16];
uint32_t stack[8];
/* Read registers for post-exception stack pointer */
target_regs_read(target, regs);
/* Read stack for pre-exception registers */
target_mem_read_words(target, stack, regs[13], sizeof(stack));
regs[13] += sizeof(stack); /* Adjust SP for pop */
regs[0] = stack[0];
regs[1] = stack[1];
regs[2] = stack[2];
regs[3] = stack[3];
regs[12] = stack[4];
regs[14] = stack[5];
regs[15] = stack[6];
/* FIXME: stack[7] contains xPSR when this is supported */
/* Reset exception state to allow resuming from restored
* state.
*/
adiv5_ap_mem_write(t->ap, CM3_AIRCR,
CM3_AIRCR_VECTKEY | CM3_AIRCR_VECTCLRACTIVE);
/* Write pre-exception registers back to core */
target_regs_write(target, regs);
return 1;
}
return 0;
}
/* The following routines implement hardware breakpoints.
* The Flash Patch and Breakpoint (FPB) system is used. */
static int
cm3_set_hw_bp(struct target_s *target, uint32_t addr)
{
struct target_ap_s *t = (void *)target;
uint32_t val = addr & 0x1FFFFFFC;
int i;
val |= (addr & 2)?0x80000000:0x40000000;
val |= 1;
for(i = 0; i < 6; i++)
if((hw_breakpoint[i] & 1) == 0) break;
if(i == 6) return -1;
hw_breakpoint[i] = addr | 1;
adiv5_ap_mem_write(t->ap, CM3_FPB_COMP(i), val);
return 0;
}
static int
cm3_clear_hw_bp(struct target_s *target, uint32_t addr)
{
struct target_ap_s *t = (void *)target;
int i;
for(i = 0; i < 6; i++)
if((hw_breakpoint[i] & ~1) == addr) break;
if(i == 6) return -1;
hw_breakpoint[i] = 0;
adiv5_ap_mem_write(t->ap, CM3_FPB_COMP(i), 0);
return 0;
}
/* The following routines implement hardware watchpoints.
* The Data Watch and Trace (DWT) system is used. */
static int
cm3_set_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
{
struct target_ap_s *t = (void *)target;
int i;
switch(len) { /* Convert bytes size to mask size */
case 1: len = CM3_DWT_MASK_BYTE; break;
case 2: len = CM3_DWT_MASK_HALFWORD; break;
case 4: len = CM3_DWT_MASK_WORD; break;
default:
return -1;
}
switch(type) { /* Convert gdb type to function type */
case 2: type = CM3_DWT_FUNC_FUNC_WRITE; break;
case 3: type = CM3_DWT_FUNC_FUNC_READ; break;
case 4: type = CM3_DWT_FUNC_FUNC_ACCESS; break;
default:
return -1;
}
for(i = 0; i < 4; i++)
if((hw_watchpoint[i].type) == 0) break;
if(i == 4) return -2;
hw_watchpoint[i].type = type;
hw_watchpoint[i].addr = addr;
hw_watchpoint[i].size = len;
adiv5_ap_mem_write(t->ap, CM3_DWT_COMP(i), addr);
adiv5_ap_mem_write(t->ap, CM3_DWT_MASK(i), len);
adiv5_ap_mem_write(t->ap, CM3_DWT_FUNC(i),
CM3_DWT_FUNC_DATAVSIZE_WORD | type);
return 0;
}
static int
cm3_clear_hw_wp(struct target_s *target, uint8_t type, uint32_t addr, uint8_t len)
{
struct target_ap_s *t = (void *)target;
int i;
switch(len) {
case 1: len = CM3_DWT_MASK_BYTE; break;
case 2: len = CM3_DWT_MASK_HALFWORD; break;
case 4: len = CM3_DWT_MASK_WORD; break;
default:
return -1;
}
switch(type) {
case 2: type = CM3_DWT_FUNC_FUNC_WRITE; break;
case 3: type = CM3_DWT_FUNC_FUNC_READ; break;
case 4: type = CM3_DWT_FUNC_FUNC_ACCESS; break;
default:
return -1;
}
for(i = 0; i < 4; i++)
if((hw_watchpoint[i].addr == addr) &&
(hw_watchpoint[i].type == type) &&
(hw_watchpoint[i].size == len)) break;
if(i == 4) return -2;
hw_watchpoint[i].type = 0;
adiv5_ap_mem_write(t->ap, CM3_DWT_FUNC(i), 0);
return 0;
}
static int
cm3_check_hw_wp(struct target_s *target, uint32_t *addr)
{
struct target_ap_s *t = (void *)target;
int i;
for(i = 0; i < 4; i++)
/* if SET and MATCHED then break */
if(hw_watchpoint[i].type &&
(adiv5_ap_mem_read(t->ap, CM3_DWT_FUNC(i)) &
CM3_DWT_FUNC_MATCHED))
break;
if(i == 4) return 0;
*addr = hw_watchpoint[i].addr;
return 1;
}