commit
bcf5b1eaa3
|
@ -18,6 +18,7 @@ SRC = \
|
|||
adiv5_jtagdp.c \
|
||||
adiv5_swdp.c \
|
||||
command.c \
|
||||
cortexa.c \
|
||||
cortexm.c \
|
||||
crc32.c \
|
||||
efm32.c \
|
||||
|
|
189
src/adiv5.c
189
src/adiv5.c
|
@ -20,27 +20,29 @@
|
|||
|
||||
/* This file implements the transport generic functions of the
|
||||
* ARM Debug Interface v5 Architecure Specification, ARM doc IHI0031A.
|
||||
*
|
||||
* Issues:
|
||||
* Currently doesn't use ROM table for introspection, just assumes
|
||||
* the device is Cortex-M3.
|
||||
*/
|
||||
#include "general.h"
|
||||
#include "jtag_scan.h"
|
||||
#include "gdb_packet.h"
|
||||
#include "adiv5.h"
|
||||
#include "target.h"
|
||||
#include "cortexm.h"
|
||||
#include "exception.h"
|
||||
|
||||
#ifndef DO_RESET_SEQ
|
||||
#define DO_RESET_SEQ 0
|
||||
#endif
|
||||
|
||||
static const char adiv5_driver_str[] = "ARM ADIv5 MEM-AP";
|
||||
/* ROM table CIDR values */
|
||||
#define CIDR_ROM_TABLE 0xb105100d
|
||||
#define CIDR_GENERIC_IP 0xb105e00d
|
||||
#define CIDR_DEBUG 0xb105900d
|
||||
|
||||
static bool ap_check_error(target *t);
|
||||
#define PIDR_REV_MASK 0x0FFF00000ULL
|
||||
#define PIDR_ARMv7M 0x4000BB000ULL
|
||||
#define PIDR_ARMv7MF 0x4000BB00CULL
|
||||
#define PIDR_ARMv7A 0x4000BBC09ULL
|
||||
|
||||
static void ap_mem_read(target *t, void *dest, uint32_t src, size_t len);
|
||||
static void ap_mem_write(target *t, uint32_t dest, const void *src, size_t len);
|
||||
extern bool cortexa_probe(ADIv5_AP_t *apb, uint32_t debug_base);
|
||||
|
||||
void adiv5_dp_ref(ADIv5_DP_t *dp)
|
||||
{
|
||||
|
@ -62,8 +64,6 @@ void adiv5_ap_unref(ADIv5_AP_t *ap)
|
|||
{
|
||||
if (--(ap->refcnt) == 0) {
|
||||
adiv5_dp_unref(ap->dp);
|
||||
if (ap->priv)
|
||||
ap->priv_free(ap->priv);
|
||||
free(ap);
|
||||
}
|
||||
}
|
||||
|
@ -73,13 +73,118 @@ void adiv5_dp_write(ADIv5_DP_t *dp, uint16_t addr, uint32_t value)
|
|||
dp->low_access(dp, ADIV5_LOW_WRITE, addr, value);
|
||||
}
|
||||
|
||||
static uint32_t adiv5_mem_read32(ADIv5_AP_t *ap, uint32_t addr)
|
||||
{
|
||||
uint32_t ret;
|
||||
adiv5_mem_read(ap, &ret, addr, sizeof(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void adiv5_component_probe(ADIv5_AP_t *ap, uint32_t addr)
|
||||
{
|
||||
addr &= ~3;
|
||||
uint64_t pidr = 0;
|
||||
uint32_t cidr = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint32_t x = adiv5_mem_read32(ap, addr + 0xfe0 + 4*i);
|
||||
pidr |= (x & 0xff) << (i * 8);
|
||||
}
|
||||
pidr |= (uint64_t)adiv5_mem_read32(ap, addr + 0xfd0) << 32;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
uint32_t x = adiv5_mem_read32(ap, addr + 0xff0 + 4*i);
|
||||
cidr |= ((uint64_t)(x & 0xff)) << (i * 8);
|
||||
}
|
||||
|
||||
switch (cidr) {
|
||||
case CIDR_ROM_TABLE: /* This is a ROM table, probe recursively */
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint32_t entry = adiv5_mem_read32(ap, addr + i*4);
|
||||
if (entry == 0)
|
||||
break;
|
||||
|
||||
if ((entry & 1) == 0)
|
||||
continue;
|
||||
|
||||
adiv5_component_probe(ap, addr + (entry & ~0xfff));
|
||||
}
|
||||
break;
|
||||
case CIDR_GENERIC_IP:
|
||||
switch (pidr & ~PIDR_REV_MASK) {
|
||||
case PIDR_ARMv7MF:
|
||||
case PIDR_ARMv7M:
|
||||
cortexm_probe(ap);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CIDR_DEBUG:
|
||||
switch (pidr & ~PIDR_REV_MASK) {
|
||||
case PIDR_ARMv7A:
|
||||
cortexa_probe(ap, addr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ADIv5_AP_t *adiv5_new_ap(ADIv5_DP_t *dp, uint8_t apsel)
|
||||
{
|
||||
ADIv5_AP_t *ap, tmpap;
|
||||
|
||||
/* Assume valid and try to read IDR */
|
||||
memset(&tmpap, 0, sizeof(tmpap));
|
||||
tmpap.dp = dp;
|
||||
tmpap.apsel = apsel;
|
||||
tmpap.idr = adiv5_ap_read(&tmpap, ADIV5_AP_IDR);
|
||||
|
||||
if(!tmpap.idr) /* IDR Invalid - Should we not continue here? */
|
||||
return NULL;
|
||||
|
||||
/* Check for ARM Mem-AP */
|
||||
uint16_t mfg = (tmpap.idr >> 17) & 0x3ff;
|
||||
uint8_t cls = (tmpap.idr >> 13) & 0xf;
|
||||
uint8_t type = tmpap.idr & 0xf;
|
||||
if (mfg != 0x23B) /* Ditch if not ARM */
|
||||
return NULL;
|
||||
if ((cls != 8) || (type == 0)) /* Ditch if not Mem-AP */
|
||||
return NULL;
|
||||
|
||||
/* It's valid to so create a heap copy */
|
||||
ap = malloc(sizeof(*ap));
|
||||
memcpy(ap, &tmpap, sizeof(*ap));
|
||||
adiv5_dp_ref(dp);
|
||||
|
||||
ap->cfg = adiv5_ap_read(ap, ADIV5_AP_CFG);
|
||||
ap->base = adiv5_ap_read(ap, ADIV5_AP_BASE);
|
||||
ap->csw = adiv5_ap_read(ap, ADIV5_AP_CSW) &
|
||||
~(ADIV5_AP_CSW_SIZE_MASK | ADIV5_AP_CSW_ADDRINC_MASK);
|
||||
|
||||
if (ap->csw & ADIV5_AP_CSW_TRINPROG) {
|
||||
gdb_out("AP transaction in progress. Target may not be usable.\n");
|
||||
ap->csw &= ~ADIV5_AP_CSW_TRINPROG;
|
||||
}
|
||||
|
||||
DEBUG("%3d: IDR=%08X CFG=%08X BASE=%08X CSW=%08X\n",
|
||||
apsel, ap->idr, ap->cfg, ap->base, ap->csw);
|
||||
|
||||
return ap;
|
||||
}
|
||||
|
||||
|
||||
void adiv5_dp_init(ADIv5_DP_t *dp)
|
||||
{
|
||||
uint32_t ctrlstat;
|
||||
uint32_t ctrlstat = 0;
|
||||
|
||||
adiv5_dp_ref(dp);
|
||||
|
||||
ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT);
|
||||
volatile struct exception e;
|
||||
TRY_CATCH (e, EXCEPTION_TIMEOUT) {
|
||||
ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT);
|
||||
}
|
||||
if (e.type) {
|
||||
gdb_out("DP not responding! Trying abort sequence...\n");
|
||||
adiv5_dp_abort(dp, ADIV5_DP_ABORT_DAPABORT);
|
||||
ctrlstat = adiv5_dp_read(dp, ADIV5_DP_CTRLSTAT);
|
||||
}
|
||||
|
||||
/* Write request for system and debug power up */
|
||||
adiv5_dp_write(dp, ADIV5_DP_CTRLSTAT,
|
||||
|
@ -113,56 +218,26 @@ void adiv5_dp_init(ADIv5_DP_t *dp)
|
|||
|
||||
/* Probe for APs on this DP */
|
||||
for(int i = 0; i < 256; i++) {
|
||||
ADIv5_AP_t *ap, tmpap;
|
||||
target *t;
|
||||
ADIv5_AP_t *ap = adiv5_new_ap(dp, i);
|
||||
if (ap == NULL)
|
||||
continue;
|
||||
|
||||
/* Assume valid and try to read IDR */
|
||||
memset(&tmpap, 0, sizeof(tmpap));
|
||||
tmpap.dp = dp;
|
||||
tmpap.apsel = i;
|
||||
tmpap.idr = adiv5_ap_read(&tmpap, ADIV5_AP_IDR);
|
||||
|
||||
if(!tmpap.idr) /* IDR Invalid - Should we not continue here? */
|
||||
break;
|
||||
|
||||
/* It's valid to so create a heap copy */
|
||||
ap = malloc(sizeof(*ap));
|
||||
memcpy(ap, &tmpap, sizeof(*ap));
|
||||
adiv5_dp_ref(dp);
|
||||
|
||||
ap->cfg = adiv5_ap_read(ap, ADIV5_AP_CFG);
|
||||
ap->base = adiv5_ap_read(ap, ADIV5_AP_BASE);
|
||||
ap->csw = adiv5_ap_read(ap, ADIV5_AP_CSW) &
|
||||
~(ADIV5_AP_CSW_SIZE_MASK | ADIV5_AP_CSW_ADDRINC_MASK);
|
||||
if (ap->base == 0xffffffff) {
|
||||
/* No debug entries... useless AP */
|
||||
adiv5_ap_unref(ap);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Should probe further here to make sure it's a valid target.
|
||||
* AP should be unref'd if not valid.
|
||||
*/
|
||||
|
||||
/* Prepend to target list... */
|
||||
t = target_new(sizeof(*t));
|
||||
adiv5_ap_ref(ap);
|
||||
t->priv = ap;
|
||||
t->priv_free = (void (*)(void *))adiv5_ap_unref;
|
||||
|
||||
t->driver = adiv5_driver_str;
|
||||
t->check_error = ap_check_error;
|
||||
|
||||
t->mem_read = ap_mem_read;
|
||||
t->mem_write = ap_mem_write;
|
||||
|
||||
/* The rest sould only be added after checking ROM table */
|
||||
cortexm_probe(t);
|
||||
adiv5_component_probe(ap, ap->base);
|
||||
}
|
||||
adiv5_dp_unref(dp);
|
||||
}
|
||||
|
||||
static bool ap_check_error(target *t)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
return adiv5_dp_error(ap->dp) != 0;
|
||||
}
|
||||
|
||||
enum align {
|
||||
ALIGN_BYTE = 0,
|
||||
ALIGN_HALFWORD = 1,
|
||||
|
@ -208,10 +283,9 @@ static void * extract(void *dest, uint32_t src, uint32_t val, enum align align)
|
|||
return (uint8_t *)dest + (1 << align);
|
||||
}
|
||||
|
||||
static void
|
||||
ap_mem_read(target *t, void *dest, uint32_t src, size_t len)
|
||||
void
|
||||
adiv5_mem_read(ADIv5_AP_t *ap, void *dest, uint32_t src, size_t len)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
uint32_t tmp;
|
||||
uint32_t osrc = src;
|
||||
enum align align = MIN(ALIGNOF(src), ALIGNOF(len));
|
||||
|
@ -237,10 +311,9 @@ ap_mem_read(target *t, void *dest, uint32_t src, size_t len)
|
|||
extract(dest, src, tmp, align);
|
||||
}
|
||||
|
||||
static void
|
||||
ap_mem_write(target *t, uint32_t dest, const void *src, size_t len)
|
||||
void
|
||||
adiv5_mem_write(ADIv5_AP_t *ap, uint32_t dest, const void *src, size_t len)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
uint32_t odest = dest;
|
||||
enum align align = MIN(ALIGNOF(dest), ALIGNOF(len));
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ static uint32_t adiv5_jtagdp_error(ADIv5_DP_t *dp);
|
|||
static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t RnW,
|
||||
uint16_t addr, uint32_t value);
|
||||
|
||||
static void adiv5_jtagdp_abort(ADIv5_DP_t *dp, uint32_t abort);
|
||||
|
||||
void adiv5_jtag_dp_handler(jtag_dev_t *dev)
|
||||
{
|
||||
|
@ -55,6 +56,7 @@ void adiv5_jtag_dp_handler(jtag_dev_t *dev)
|
|||
dp->dp_read = adiv5_jtagdp_read;
|
||||
dp->error = adiv5_jtagdp_error;
|
||||
dp->low_access = adiv5_jtagdp_low_access;
|
||||
dp->abort = adiv5_jtagdp_abort;
|
||||
|
||||
adiv5_dp_init(dp);
|
||||
}
|
||||
|
@ -100,3 +102,10 @@ static uint32_t adiv5_jtagdp_low_access(ADIv5_DP_t *dp, uint8_t RnW,
|
|||
return (uint32_t)(response >> 3);
|
||||
}
|
||||
|
||||
static void adiv5_jtagdp_abort(ADIv5_DP_t *dp, uint32_t abort)
|
||||
{
|
||||
uint64_t request = (uint64_t)abort << 3;
|
||||
jtag_dev_write_ir(dp->dev, IR_ABORT);
|
||||
jtag_dev_shift_dr(dp->dev, NULL, (const uint8_t*)&request, 35);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ static uint32_t adiv5_swdp_error(ADIv5_DP_t *dp);
|
|||
static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW,
|
||||
uint16_t addr, uint32_t value);
|
||||
|
||||
static void adiv5_swdp_abort(ADIv5_DP_t *dp, uint32_t abort);
|
||||
|
||||
int adiv5_swdp_scan(void)
|
||||
{
|
||||
|
@ -65,6 +66,7 @@ int adiv5_swdp_scan(void)
|
|||
dp->dp_read = adiv5_swdp_read;
|
||||
dp->error = adiv5_swdp_error;
|
||||
dp->low_access = adiv5_swdp_low_access;
|
||||
dp->abort = adiv5_swdp_abort;
|
||||
|
||||
adiv5_swdp_error(dp);
|
||||
adiv5_dp_init(dp);
|
||||
|
@ -158,3 +160,8 @@ static uint32_t adiv5_swdp_low_access(ADIv5_DP_t *dp, uint8_t RnW,
|
|||
return response;
|
||||
}
|
||||
|
||||
static void adiv5_swdp_abort(ADIv5_DP_t *dp, uint32_t abort)
|
||||
{
|
||||
adiv5_dp_write(dp, ADIV5_DP_ABORT, abort);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,597 @@
|
|||
/*
|
||||
* This file is part of the Black Magic Debug project.
|
||||
*
|
||||
* Copyright (C) 2016 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-A9 core. This should be generic to ARMv7-A as it is
|
||||
* implemented according to the "ARMv7-A Architecture Reference Manual",
|
||||
* ARM doc DDI0406C.
|
||||
*
|
||||
* Cache line length is from Cortex-A9 TRM, may differ for others.
|
||||
* Janky reset code is for Zynq-7000 which disconnects the DP from the JTAG
|
||||
* scan chain during reset.
|
||||
*/
|
||||
#include "general.h"
|
||||
#include "exception.h"
|
||||
#include "jtagtap.h"
|
||||
#include "jtag_scan.h"
|
||||
#include "adiv5.h"
|
||||
#include "target.h"
|
||||
#include "command.h"
|
||||
#include "gdb_packet.h"
|
||||
#include "cortexm.h"
|
||||
#include "morse.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
static char cortexa_driver_str[] = "ARM Cortex-A";
|
||||
|
||||
/* Signals returned by cortexa_halt_wait() */
|
||||
#define SIGINT 2
|
||||
#define SIGTRAP 5
|
||||
#define SIGSEGV 11
|
||||
#define SIGLOST 29
|
||||
|
||||
static bool cortexa_attach(target *t);
|
||||
static void cortexa_detach(target *t);
|
||||
static void cortexa_halt_resume(target *t, bool step);
|
||||
|
||||
static void cortexa_regs_read(target *t, void *data);
|
||||
static void cortexa_regs_write(target *t, const void *data);
|
||||
static void cortexa_regs_read_internal(target *t);
|
||||
static void cortexa_regs_write_internal(target *t);
|
||||
|
||||
static void cortexa_reset(target *t);
|
||||
static int cortexa_halt_wait(target *t);
|
||||
static void cortexa_halt_request(target *t);
|
||||
|
||||
static int cortexa_set_hw_bp(target *t, uint32_t addr, uint8_t len);
|
||||
static int cortexa_clear_hw_bp(target *t, uint32_t addr, uint8_t len);
|
||||
static uint32_t bp_bas(uint32_t addr, uint8_t len);
|
||||
|
||||
static void apb_write(target *t, uint16_t reg, uint32_t val);
|
||||
static uint32_t apb_read(target *t, uint16_t reg);
|
||||
static void write_gpreg(target *t, uint8_t regno, uint32_t val);
|
||||
|
||||
struct cortexa_priv {
|
||||
uint32_t base;
|
||||
ADIv5_AP_t *apb;
|
||||
ADIv5_AP_t *ahb;
|
||||
struct {
|
||||
uint32_t r[16];
|
||||
uint32_t cpsr;
|
||||
uint32_t fpscr;
|
||||
uint64_t d[16];
|
||||
} reg_cache;
|
||||
unsigned hw_breakpoint_max;
|
||||
unsigned hw_breakpoint[16];
|
||||
uint32_t bpc0;
|
||||
};
|
||||
|
||||
/* This may be specific to Cortex-A9 */
|
||||
#define CACHE_LINE_LENGTH (8*4)
|
||||
|
||||
/* Debug APB registers */
|
||||
#define DBGDIDR 0
|
||||
|
||||
#define DBGDTRRX 32 /* DCC: Host to target */
|
||||
#define DBGITR 33
|
||||
|
||||
#define DBGDSCR 34
|
||||
#define DBGDSCR_TXFULL (1 << 29)
|
||||
#define DBGDSCR_INSTRCOMPL (1 << 24)
|
||||
#define DBGDSCR_EXTDCCMODE_STALL (1 << 20)
|
||||
#define DBGDSCR_EXTDCCMODE_MASK (3 << 20)
|
||||
#define DBGDSCR_HDBGEN (1 << 14)
|
||||
#define DBGDSCR_ITREN (1 << 13)
|
||||
#define DBGDSCR_INTDIS (1 << 11)
|
||||
#define DBGDSCR_UND_I (1 << 8)
|
||||
#define DBGDSCR_MOE_MASK (0xf << 2)
|
||||
#define DBGDSCR_MOE_HALT_REQ (0x0 << 2)
|
||||
#define DBGDSCR_RESTARTED (1 << 1)
|
||||
#define DBGDSCR_HALTED (1 << 0)
|
||||
|
||||
#define DBGDTRTX 35 /* DCC: Target to host */
|
||||
|
||||
#define DBGDRCR 36
|
||||
#define DBGDRCR_CSE (1 << 2)
|
||||
#define DBGDRCR_RRQ (1 << 1)
|
||||
#define DBGDRCR_HRQ (1 << 0)
|
||||
|
||||
#define DBGBVR(i) (64+(i))
|
||||
#define DBGBCR(i) (80+(i))
|
||||
#define DBGBCR_INST_MISMATCH (4 << 20)
|
||||
#define DBGBCR_BAS_ANY (0xf << 5)
|
||||
#define DBGBCR_BAS_LOW_HW (0x3 << 5)
|
||||
#define DBGBCR_BAS_HIGH_HW (0xc << 5)
|
||||
#define DBGBCR_EN (1 << 0)
|
||||
|
||||
/* Instruction encodings for accessing the coprocessor interface */
|
||||
#define MCR 0xee000010
|
||||
#define MRC 0xee100010
|
||||
#define CPREG(coproc, opc1, rt, crn, crm, opc2) \
|
||||
(((opc1) << 21) | ((crn) << 16) | ((rt) << 12) | \
|
||||
((coproc) << 8) | ((opc2) << 5) | (crm))
|
||||
|
||||
/* Debug registers CP14 */
|
||||
#define DBGDTRRXint CPREG(14, 0, 0, 0, 5, 0)
|
||||
#define DBGDTRTXint CPREG(14, 0, 0, 0, 5, 0)
|
||||
|
||||
/* Cache management registers CP15 */
|
||||
#define ICIALLU CPREG(15, 0, 0, 7, 5, 0)
|
||||
#define DCCIMVAC CPREG(15, 0, 0, 7, 14, 1)
|
||||
#define DCCMVAC CPREG(15, 0, 0, 7, 10, 1)
|
||||
|
||||
/* Thumb mode bit in CPSR */
|
||||
#define CPSR_THUMB (1 << 5)
|
||||
|
||||
/* GDB register map / target description */
|
||||
static const char tdesc_cortex_a[] =
|
||||
"<?xml version=\"1.0\"?>"
|
||||
"<!DOCTYPE feature SYSTEM \"gdb-target.dtd\">"
|
||||
"<target>"
|
||||
" <architecture>arm</architecture>"
|
||||
" <feature name=\"org.gnu.gdb.arm.core\">"
|
||||
" <reg name=\"r0\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r1\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r2\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r3\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r4\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r5\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r6\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r7\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r8\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r9\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r10\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r11\" bitsize=\"32\"/>"
|
||||
" <reg name=\"r12\" bitsize=\"32\"/>"
|
||||
" <reg name=\"sp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
||||
" <reg name=\"lr\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||
" <reg name=\"pc\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||
" <reg name=\"cpsr\" bitsize=\"32\"/>"
|
||||
" </feature>"
|
||||
" <feature name=\"org.gnu.gdb.arm.vfp\">"
|
||||
" <reg name=\"fpscr\" bitsize=\"32\"/>"
|
||||
" <reg name=\"d0\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d1\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d2\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d3\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d4\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d5\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d6\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d7\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d8\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d9\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d10\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d11\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d12\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d13\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d14\" bitsize=\"64\" type=\"float\"/>"
|
||||
" <reg name=\"d15\" bitsize=\"64\" type=\"float\"/>"
|
||||
" </feature>"
|
||||
"</target>";
|
||||
|
||||
static void apb_write(target *t, uint16_t reg, uint32_t val)
|
||||
{
|
||||
struct cortexa_priv *priv = t->priv;
|
||||
ADIv5_AP_t *ap = priv->apb;
|
||||
uint32_t addr = priv->base + 4*reg;
|
||||
adiv5_ap_write(ap, ADIV5_AP_TAR, addr);
|
||||
adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_DRW, val);
|
||||
}
|
||||
|
||||
static uint32_t apb_read(target *t, uint16_t reg)
|
||||
{
|
||||
struct cortexa_priv *priv = t->priv;
|
||||
ADIv5_AP_t *ap = priv->apb;
|
||||
uint32_t addr = priv->base + 4*reg;
|
||||
adiv5_ap_write(ap, ADIV5_AP_TAR, addr);
|
||||
adiv5_dp_low_access(ap->dp, ADIV5_LOW_READ, ADIV5_AP_DRW, 0);
|
||||
return adiv5_dp_low_access(ap->dp, ADIV5_LOW_READ, ADIV5_DP_RDBUFF, 0);
|
||||
}
|
||||
|
||||
static void cortexa_mem_read(target *t, void *dest, uint32_t src, size_t len)
|
||||
{
|
||||
/* Clean cache before reading */
|
||||
for (uint32_t cl = src & ~(CACHE_LINE_LENGTH-1);
|
||||
cl < src + len; cl += CACHE_LINE_LENGTH) {
|
||||
write_gpreg(t, 0, cl);
|
||||
apb_write(t, DBGITR, MCR | DCCMVAC);
|
||||
}
|
||||
|
||||
ADIv5_AP_t *ahb = ((struct cortexa_priv*)t->priv)->ahb;
|
||||
adiv5_mem_read(ahb, dest, src, len);
|
||||
}
|
||||
|
||||
static void cortexa_mem_write(target *t, uint32_t dest, const void *src, size_t len)
|
||||
{
|
||||
/* Clean and invalidate cache before writing */
|
||||
for (uint32_t cl = dest & ~(CACHE_LINE_LENGTH-1);
|
||||
cl < dest + len; cl += CACHE_LINE_LENGTH) {
|
||||
write_gpreg(t, 0, cl);
|
||||
apb_write(t, DBGITR, MCR | DCCIMVAC);
|
||||
}
|
||||
ADIv5_AP_t *ahb = ((struct cortexa_priv*)t->priv)->ahb;
|
||||
adiv5_mem_write(ahb, dest, src, len);
|
||||
}
|
||||
|
||||
static bool cortexa_check_error(target *t)
|
||||
{
|
||||
ADIv5_AP_t *ahb = ((struct cortexa_priv*)t->priv)->ahb;
|
||||
return adiv5_dp_error(ahb->dp) != 0;
|
||||
}
|
||||
|
||||
|
||||
bool cortexa_probe(ADIv5_AP_t *apb, uint32_t debug_base)
|
||||
{
|
||||
target *t;
|
||||
|
||||
DEBUG("%s base=0x%08"PRIx32"\n", __func__, debug_base);
|
||||
|
||||
/* Prepend to target list... */
|
||||
t = target_new(sizeof(*t));
|
||||
adiv5_ap_ref(apb);
|
||||
struct cortexa_priv *priv = calloc(1, sizeof(*priv));
|
||||
t->priv = priv;
|
||||
t->priv_free = free;
|
||||
priv->apb = apb;
|
||||
/* FIXME Find a better way to find the AHB. This is likely to be
|
||||
* device specific. */
|
||||
priv->ahb = adiv5_new_ap(apb->dp, 0);
|
||||
adiv5_ap_ref(priv->ahb);
|
||||
priv->base = debug_base;
|
||||
/* Set up APB CSW, we won't touch this again */
|
||||
uint32_t csw = apb->csw | ADIV5_AP_CSW_SIZE_WORD;
|
||||
adiv5_ap_write(apb, ADIV5_AP_CSW, csw);
|
||||
uint32_t dbgdidr = apb_read(t, DBGDIDR);
|
||||
priv->hw_breakpoint_max = ((dbgdidr >> 24) & 15)+1;
|
||||
DEBUG("Target has %d breakpoints\n", priv->hw_breakpoint_max);
|
||||
|
||||
t->check_error = cortexa_check_error;
|
||||
|
||||
t->mem_read = cortexa_mem_read;
|
||||
t->mem_write = cortexa_mem_write;
|
||||
|
||||
t->driver = cortexa_driver_str;
|
||||
|
||||
t->attach = cortexa_attach;
|
||||
t->detach = cortexa_detach;
|
||||
|
||||
t->tdesc = tdesc_cortex_a;
|
||||
t->regs_read = cortexa_regs_read;
|
||||
t->regs_write = cortexa_regs_write;
|
||||
|
||||
t->reset = cortexa_reset;
|
||||
t->halt_request = cortexa_halt_request;
|
||||
t->halt_wait = cortexa_halt_wait;
|
||||
t->halt_resume = cortexa_halt_resume;
|
||||
t->regs_size = sizeof(priv->reg_cache);
|
||||
|
||||
t->set_hw_bp = cortexa_set_hw_bp;
|
||||
t->clear_hw_bp = cortexa_clear_hw_bp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cortexa_attach(target *t)
|
||||
{
|
||||
struct cortexa_priv *priv = t->priv;
|
||||
int tries;
|
||||
|
||||
/* Clear any pending fault condition */
|
||||
target_check_error(t);
|
||||
|
||||
/* Enable halting debug mode */
|
||||
uint32_t dbgdscr = apb_read(t, DBGDSCR);
|
||||
dbgdscr |= DBGDSCR_HDBGEN | DBGDSCR_ITREN;
|
||||
dbgdscr = (dbgdscr & ~DBGDSCR_EXTDCCMODE_MASK) | DBGDSCR_EXTDCCMODE_STALL;
|
||||
apb_write(t, DBGDSCR, dbgdscr);
|
||||
DEBUG("DBGDSCR = 0x%08x\n", dbgdscr);
|
||||
|
||||
target_halt_request(t);
|
||||
tries = 10;
|
||||
while(!platform_srst_get_val() && !target_halt_wait(t) && --tries)
|
||||
platform_delay(2);
|
||||
if(!tries)
|
||||
return false;
|
||||
|
||||
/* Clear any stale breakpoints */
|
||||
for(unsigned i = 0; i < priv->hw_breakpoint_max; i++) {
|
||||
apb_write(t, DBGBCR(i), 0);
|
||||
priv->hw_breakpoint[i] = 0;
|
||||
}
|
||||
|
||||
platform_srst_set_val(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cortexa_detach(target *t)
|
||||
{
|
||||
struct cortexa_priv *priv = t->priv;
|
||||
|
||||
/* Clear any stale breakpoints */
|
||||
for(unsigned i = 0; i < priv->hw_breakpoint_max; i++) {
|
||||
priv->hw_breakpoint[i] = 0;
|
||||
apb_write(t, DBGBCR(i), 0);
|
||||
}
|
||||
|
||||
/* Restore any clobbered registers */
|
||||
cortexa_regs_write_internal(t);
|
||||
|
||||
uint32_t dbgdscr = apb_read(t, DBGDSCR);
|
||||
/* Disable halting debug mode */
|
||||
dbgdscr &= ~(DBGDSCR_HDBGEN | DBGDSCR_ITREN);
|
||||
apb_write(t, DBGDSCR, dbgdscr);
|
||||
/* Clear sticky error and resume */
|
||||
apb_write(t, DBGDRCR, DBGDRCR_CSE | DBGDRCR_RRQ);
|
||||
}
|
||||
|
||||
|
||||
static uint32_t read_gpreg(target *t, uint8_t regno)
|
||||
{
|
||||
/* To read a register we use DBGITR to load an MCR instruction
|
||||
* that sends the value via DCC DBGDTRTX using the CP14 interface.
|
||||
*/
|
||||
uint32_t instr = MCR | DBGDTRTXint | ((regno & 0xf) << 12);
|
||||
apb_write(t, DBGITR, instr);
|
||||
/* Return value read from DCC channel */
|
||||
return apb_read(t, DBGDTRTX);
|
||||
}
|
||||
|
||||
static void write_gpreg(target *t, uint8_t regno, uint32_t val)
|
||||
{
|
||||
/* Write value to DCC channel */
|
||||
apb_write(t, DBGDTRRX, val);
|
||||
/* Run instruction to load register */
|
||||
uint32_t instr = MRC | DBGDTRRXint | ((regno & 0xf) << 12);
|
||||
apb_write(t, DBGITR, instr);
|
||||
}
|
||||
|
||||
static void cortexa_regs_read(target *t, void *data)
|
||||
{
|
||||
struct cortexa_priv *priv = (struct cortexa_priv *)t->priv;
|
||||
memcpy(data, &priv->reg_cache, t->regs_size);
|
||||
}
|
||||
|
||||
static void cortexa_regs_write(target *t, const void *data)
|
||||
{
|
||||
struct cortexa_priv *priv = (struct cortexa_priv *)t->priv;
|
||||
memcpy(&priv->reg_cache, data, t->regs_size);
|
||||
}
|
||||
|
||||
static void cortexa_regs_read_internal(target *t)
|
||||
{
|
||||
struct cortexa_priv *priv = (struct cortexa_priv *)t->priv;
|
||||
/* Read general purpose registers */
|
||||
for (int i = 0; i < 16; i++) {
|
||||
priv->reg_cache.r[i] = read_gpreg(t, i);
|
||||
}
|
||||
/* Read CPSR */
|
||||
apb_write(t, DBGITR, 0xE10F0000); /* mrs r0, CPSR */
|
||||
priv->reg_cache.cpsr = read_gpreg(t, 0);
|
||||
/* Read FPSCR */
|
||||
apb_write(t, DBGITR, 0xeef10a10); /* vmrs r0, fpscr */
|
||||
priv->reg_cache.fpscr = read_gpreg(t, 0);
|
||||
/* Read out VFP registers */
|
||||
for (int i = 0; i < 16; i++) {
|
||||
/* Read D[i] to R0/R1 */
|
||||
apb_write(t, DBGITR, 0xEC510B10 | i); /* vmov r0, r1, d0 */
|
||||
priv->reg_cache.d[i] = ((uint64_t)read_gpreg(t, 1) << 32) | read_gpreg(t, 0);
|
||||
}
|
||||
priv->reg_cache.r[15] -= (priv->reg_cache.cpsr & CPSR_THUMB) ? 4 : 8;
|
||||
}
|
||||
|
||||
static void cortexa_regs_write_internal(target *t)
|
||||
{
|
||||
struct cortexa_priv *priv = (struct cortexa_priv *)t->priv;
|
||||
/* First write back floats */
|
||||
for (int i = 0; i < 16; i++) {
|
||||
write_gpreg(t, 1, priv->reg_cache.d[i] >> 32);
|
||||
write_gpreg(t, 0, priv->reg_cache.d[i]);
|
||||
apb_write(t, DBGITR, 0xec410b10 | i); /* vmov d[i], r0, r1 */
|
||||
}
|
||||
/* Write back FPSCR */
|
||||
write_gpreg(t, 0, priv->reg_cache.fpscr);
|
||||
apb_write(t, DBGITR, 0xeee10a10); /* vmsr fpscr, r0 */
|
||||
/* Write back the CPSR */
|
||||
write_gpreg(t, 0, priv->reg_cache.cpsr);
|
||||
apb_write(t, DBGITR, 0xe12ff000); /* msr CPSR_fsxc, r0 */
|
||||
/* Write back PC, via r0. MRC clobbers CPSR instead */
|
||||
write_gpreg(t, 0, priv->reg_cache.r[15]);
|
||||
apb_write(t, DBGITR, 0xe1a0f000); /* mov pc, r0 */
|
||||
/* Finally the GP registers now that we're done using them */
|
||||
for (int i = 0; i < 15; i++) {
|
||||
write_gpreg(t, i, priv->reg_cache.r[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void cortexa_reset(target *t)
|
||||
{
|
||||
/* This mess is Xilinx Zynq specific
|
||||
* See Zynq-7000 TRM, Xilinx doc UG585
|
||||
*/
|
||||
#define ZYNQ_SLCR_UNLOCK 0xf8000008
|
||||
#define ZYNQ_SLCR_UNLOCK_KEY 0xdf0d
|
||||
#define ZYNQ_SLCR_PSS_RST_CTRL 0xf8000200
|
||||
target_mem_write32(t, ZYNQ_SLCR_UNLOCK, ZYNQ_SLCR_UNLOCK_KEY);
|
||||
target_mem_write32(t, ZYNQ_SLCR_PSS_RST_CTRL, 1);
|
||||
|
||||
/* Try hard reset too */
|
||||
platform_srst_set_val(true);
|
||||
platform_srst_set_val(false);
|
||||
|
||||
/* Spin until Xilinx reconnects us */
|
||||
volatile struct exception e;
|
||||
do {
|
||||
TRY_CATCH (e, EXCEPTION_ALL) {
|
||||
apb_read(t, DBGDIDR);
|
||||
}
|
||||
} while (e.type == EXCEPTION_ERROR);
|
||||
|
||||
platform_delay(100);
|
||||
|
||||
cortexa_attach(t);
|
||||
}
|
||||
|
||||
static void cortexa_halt_request(target *t)
|
||||
{
|
||||
volatile struct exception e;
|
||||
TRY_CATCH (e, EXCEPTION_TIMEOUT) {
|
||||
apb_write(t, DBGDRCR, DBGDRCR_HRQ);
|
||||
}
|
||||
if (e.type) {
|
||||
gdb_out("Timeout sending interrupt, is target in WFI?\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int cortexa_halt_wait(target *t)
|
||||
{
|
||||
volatile uint32_t dbgdscr = 0;
|
||||
volatile struct exception e;
|
||||
TRY_CATCH (e, EXCEPTION_ALL) {
|
||||
/* If this times out because the target is in WFI then
|
||||
* the target is still running. */
|
||||
dbgdscr = apb_read(t, DBGDSCR);
|
||||
}
|
||||
switch (e.type) {
|
||||
case EXCEPTION_ERROR:
|
||||
/* Oh crap, there's no recovery from this... */
|
||||
target_list_free();
|
||||
morse("TARGET LOST.", 1);
|
||||
return SIGLOST;
|
||||
case EXCEPTION_TIMEOUT:
|
||||
/* Timeout isn't a problem, target could be in WFI */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(dbgdscr & DBGDSCR_HALTED)) /* Not halted */
|
||||
return 0;
|
||||
|
||||
DEBUG("%s: DBGDSCR = 0x%08x\n", __func__, dbgdscr);
|
||||
/* Reenable DBGITR */
|
||||
dbgdscr |= DBGDSCR_ITREN;
|
||||
apb_write(t, DBGDSCR, dbgdscr);
|
||||
|
||||
/* Find out why we halted */
|
||||
int sig;
|
||||
switch (dbgdscr & DBGDSCR_MOE_MASK) {
|
||||
case DBGDSCR_MOE_HALT_REQ:
|
||||
sig = SIGINT;
|
||||
break;
|
||||
default:
|
||||
sig = SIGTRAP;
|
||||
}
|
||||
|
||||
cortexa_regs_read_internal(t);
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
void cortexa_halt_resume(target *t, bool step)
|
||||
{
|
||||
struct cortexa_priv *priv = t->priv;
|
||||
/* Set breakpoint comarator for single stepping if needed */
|
||||
if (step) {
|
||||
uint32_t addr = priv->reg_cache.r[15];
|
||||
uint32_t bas = bp_bas(addr, (priv->reg_cache.cpsr & CPSR_THUMB) ? 2 : 4);
|
||||
DEBUG("step 0x%08x %x\n", addr, bas);
|
||||
/* Set match any breakpoint */
|
||||
apb_write(t, DBGBVR(0), priv->reg_cache.r[15] & ~3);
|
||||
apb_write(t, DBGBCR(0), DBGBCR_INST_MISMATCH | bas |
|
||||
DBGBCR_EN);
|
||||
} else {
|
||||
apb_write(t, DBGBVR(0), priv->hw_breakpoint[0] & ~3);
|
||||
apb_write(t, DBGBCR(0), priv->bpc0);
|
||||
}
|
||||
|
||||
/* Write back register cache */
|
||||
cortexa_regs_write_internal(t);
|
||||
|
||||
apb_write(t, DBGITR, MCR | ICIALLU); /* invalidate cache */
|
||||
|
||||
/* Disable DBGITR. Not sure why, but RRQ is ignored otherwise. */
|
||||
uint32_t dbgdscr = apb_read(t, DBGDSCR);
|
||||
if (step)
|
||||
dbgdscr |= DBGDSCR_INTDIS;
|
||||
else
|
||||
dbgdscr &= ~DBGDSCR_INTDIS;
|
||||
dbgdscr &= ~DBGDSCR_ITREN;
|
||||
apb_write(t, DBGDSCR, dbgdscr);
|
||||
|
||||
do {
|
||||
apb_write(t, DBGDRCR, DBGDRCR_CSE | DBGDRCR_RRQ);
|
||||
dbgdscr = apb_read(t, DBGDSCR);
|
||||
DEBUG("%s: DBGDSCR = 0x%08x\n", __func__, dbgdscr);
|
||||
} while (!(dbgdscr & DBGDSCR_RESTARTED));
|
||||
}
|
||||
|
||||
/* Breakpoints */
|
||||
static uint32_t bp_bas(uint32_t addr, uint8_t len)
|
||||
{
|
||||
if (len == 4)
|
||||
return DBGBCR_BAS_ANY;
|
||||
else if (addr & 2)
|
||||
return DBGBCR_BAS_HIGH_HW;
|
||||
else
|
||||
return DBGBCR_BAS_LOW_HW;
|
||||
}
|
||||
|
||||
static int cortexa_set_hw_bp(target *t, uint32_t addr, uint8_t len)
|
||||
{
|
||||
struct cortexa_priv *priv = t->priv;
|
||||
unsigned i;
|
||||
|
||||
for(i = 0; i < priv->hw_breakpoint_max; i++)
|
||||
if((priv->hw_breakpoint[i] & 1) == 0) break;
|
||||
|
||||
if(i == priv->hw_breakpoint_max) return -1;
|
||||
|
||||
priv->hw_breakpoint[i] = addr | 1;
|
||||
|
||||
apb_write(t, DBGBVR(i), addr & ~3);
|
||||
uint32_t bpc = bp_bas(addr, len) | DBGBCR_EN;
|
||||
apb_write(t, DBGBCR(i), bpc);
|
||||
if (i == 0)
|
||||
priv->bpc0 = bpc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cortexa_clear_hw_bp(target *t, uint32_t addr, uint8_t len)
|
||||
{
|
||||
struct cortexa_priv *priv = t->priv;
|
||||
unsigned i;
|
||||
|
||||
(void)len;
|
||||
|
||||
for (i = 0; i < priv->hw_breakpoint_max; i++)
|
||||
if ((priv->hw_breakpoint[i] & ~1) == addr)
|
||||
break;
|
||||
if (i == priv->hw_breakpoint_max)
|
||||
return -1;
|
||||
|
||||
priv->hw_breakpoint[i] = 0;
|
||||
|
||||
apb_write(t, DBGBCR(i), 0);
|
||||
if (i == 0)
|
||||
priv->bpc0 = 0;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -67,8 +67,8 @@ static int cortexm_halt_wait(target *t);
|
|||
static void cortexm_halt_request(target *t);
|
||||
static int cortexm_fault_unwind(target *t);
|
||||
|
||||
static int cortexm_set_hw_bp(target *t, uint32_t addr);
|
||||
static int cortexm_clear_hw_bp(target *t, uint32_t addr);
|
||||
static int cortexm_set_hw_bp(target *t, uint32_t addr, uint8_t len);
|
||||
static int cortexm_clear_hw_bp(target *t, uint32_t addr, uint8_t len);
|
||||
|
||||
static int cortexm_set_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len);
|
||||
static int cortexm_clear_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len);
|
||||
|
@ -82,6 +82,7 @@ static int cortexm_hostio_request(target *t);
|
|||
static void cortexm_hostio_reply(target *t, int32_t retcode, uint32_t errcode);
|
||||
|
||||
struct cortexm_priv {
|
||||
ADIv5_AP_t *ap;
|
||||
bool stepping;
|
||||
bool on_bkpt;
|
||||
/* Watchpoint unit status */
|
||||
|
@ -197,8 +198,48 @@ static const char tdesc_cortex_mf[] =
|
|||
" </feature>"
|
||||
"</target>";
|
||||
|
||||
bool cortexm_probe(target *t)
|
||||
ADIv5_AP_t *cortexm_ap(target *t)
|
||||
{
|
||||
return ((struct cortexm_priv *)t->priv)->ap;
|
||||
}
|
||||
|
||||
static void cortexm_mem_read(target *t, void *dest, uint32_t src, size_t len)
|
||||
{
|
||||
adiv5_mem_read(cortexm_ap(t), dest, src, len);
|
||||
}
|
||||
|
||||
static void cortexm_mem_write(target *t, uint32_t dest, const void *src, size_t len)
|
||||
{
|
||||
adiv5_mem_write(cortexm_ap(t), dest, src, len);
|
||||
}
|
||||
|
||||
static bool cortexm_check_error(target *t)
|
||||
{
|
||||
ADIv5_AP_t *ap = cortexm_ap(t);
|
||||
return adiv5_dp_error(ap->dp) != 0;
|
||||
}
|
||||
|
||||
static void cortexm_priv_free(void *priv)
|
||||
{
|
||||
adiv5_ap_unref(((struct cortexm_priv *)priv)->ap);
|
||||
free(priv);
|
||||
}
|
||||
|
||||
bool cortexm_probe(ADIv5_AP_t *ap)
|
||||
{
|
||||
target *t;
|
||||
|
||||
t = target_new(sizeof(*t));
|
||||
adiv5_ap_ref(ap);
|
||||
struct cortexm_priv *priv = calloc(1, sizeof(*priv));
|
||||
t->priv = priv;
|
||||
t->priv_free = cortexm_priv_free;
|
||||
priv->ap = ap;
|
||||
|
||||
t->check_error = cortexm_check_error;
|
||||
t->mem_read = cortexm_mem_read;
|
||||
t->mem_write = cortexm_mem_write;
|
||||
|
||||
t->driver = cortexm_driver_str;
|
||||
|
||||
t->attach = cortexm_attach;
|
||||
|
@ -229,11 +270,6 @@ bool cortexm_probe(target *t)
|
|||
t->tdesc = tdesc_cortex_mf;
|
||||
}
|
||||
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = calloc(1, sizeof(*priv));
|
||||
ap->priv = priv;
|
||||
ap->priv_free = free;
|
||||
|
||||
/* Default vectors to catch */
|
||||
priv->demcr = CORTEXM_DEMCR_TRCENA | CORTEXM_DEMCR_VC_HARDERR |
|
||||
CORTEXM_DEMCR_VC_CORERESET;
|
||||
|
@ -260,8 +296,7 @@ bool cortexm_probe(target *t)
|
|||
|
||||
bool cortexm_attach(target *t)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
unsigned i;
|
||||
uint32_t r;
|
||||
int tries;
|
||||
|
@ -323,8 +358,7 @@ bool cortexm_attach(target *t)
|
|||
|
||||
void cortexm_detach(target *t)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
unsigned i;
|
||||
|
||||
/* Clear any stale breakpoints */
|
||||
|
@ -343,7 +377,7 @@ enum { DB_DHCSR, DB_DCRSR, DB_DCRDR, DB_DEMCR };
|
|||
|
||||
static void cortexm_regs_read(target *t, void *data)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
ADIv5_AP_t *ap = cortexm_ap(t);
|
||||
uint32_t *regs = data;
|
||||
unsigned i;
|
||||
|
||||
|
@ -374,7 +408,7 @@ static void cortexm_regs_read(target *t, void *data)
|
|||
|
||||
static void cortexm_regs_write(target *t, const void *data)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
ADIv5_AP_t *ap = cortexm_ap(t);
|
||||
const uint32_t *regs = data;
|
||||
unsigned i;
|
||||
|
||||
|
@ -459,8 +493,7 @@ static void cortexm_halt_request(target *t)
|
|||
|
||||
static int cortexm_halt_wait(target *t)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
|
||||
volatile uint32_t dhcsr = 0;
|
||||
volatile struct exception e;
|
||||
|
@ -521,8 +554,7 @@ static int cortexm_halt_wait(target *t)
|
|||
|
||||
void cortexm_halt_resume(target *t, bool step)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
uint32_t dhcsr = CORTEXM_DHCSR_DBGKEY | CORTEXM_DHCSR_C_DEBUGEN;
|
||||
|
||||
if (step)
|
||||
|
@ -637,10 +669,10 @@ int cortexm_run_stub(target *t, uint32_t loadaddr,
|
|||
/* The following routines implement hardware breakpoints.
|
||||
* The Flash Patch and Breakpoint (FPB) system is used. */
|
||||
|
||||
static int cortexm_set_hw_bp(target *t, uint32_t addr)
|
||||
static int cortexm_set_hw_bp(target *t, uint32_t addr, uint8_t len)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
(void)len;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
uint32_t val = addr;
|
||||
unsigned i;
|
||||
|
||||
|
@ -662,10 +694,10 @@ static int cortexm_set_hw_bp(target *t, uint32_t addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cortexm_clear_hw_bp(target *t, uint32_t addr)
|
||||
static int cortexm_clear_hw_bp(target *t, uint32_t addr, uint8_t len)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
(void)len;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
unsigned i;
|
||||
|
||||
for(i = 0; i < priv->hw_breakpoint_max; i++)
|
||||
|
@ -686,8 +718,7 @@ static int cortexm_clear_hw_bp(target *t, uint32_t addr)
|
|||
static int
|
||||
cortexm_set_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
unsigned i;
|
||||
|
||||
switch(len) { /* Convert bytes size to mask size */
|
||||
|
@ -728,8 +759,7 @@ cortexm_set_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len)
|
|||
static int
|
||||
cortexm_clear_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
unsigned i;
|
||||
|
||||
switch(len) {
|
||||
|
@ -764,8 +794,7 @@ cortexm_clear_hw_wp(target *t, uint8_t type, uint32_t addr, uint8_t len)
|
|||
|
||||
static int cortexm_check_hw_wp(target *t, uint32_t *addr)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
unsigned i;
|
||||
|
||||
for(i = 0; i < priv->hw_watchpoint_max; i++)
|
||||
|
@ -783,8 +812,7 @@ static int cortexm_check_hw_wp(target *t, uint32_t *addr)
|
|||
|
||||
static bool cortexm_vector_catch(target *t, int argc, char *argv[])
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
const char *vectors[] = {"reset", NULL, NULL, NULL, "mm", "nocp",
|
||||
"chk", "stat", "bus", "int", "hard"};
|
||||
uint32_t tmp = 0;
|
||||
|
@ -862,8 +890,7 @@ static bool cortexm_vector_catch(target *t, int argc, char *argv[])
|
|||
|
||||
static int cortexm_hostio_request(target *t)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
uint32_t arm_regs[t->regs_size];
|
||||
uint32_t params[4];
|
||||
|
||||
|
@ -964,8 +991,7 @@ static int cortexm_hostio_request(target *t)
|
|||
|
||||
static void cortexm_hostio_reply(target *t, int32_t retcode, uint32_t errcode)
|
||||
{
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
struct cortexm_priv *priv = ap->priv;
|
||||
struct cortexm_priv *priv = t->priv;
|
||||
uint32_t arm_regs[t->regs_size];
|
||||
|
||||
DEBUG("syscall return ret=%d errno=%d\n", retcode, errcode);
|
||||
|
|
|
@ -252,7 +252,7 @@ char variant_string[40];
|
|||
bool efm32_probe(target *t)
|
||||
{
|
||||
/* Read the IDCODE register from the SW-DP */
|
||||
ADIv5_AP_t *ap = adiv5_target_ap(t);
|
||||
ADIv5_AP_t *ap = cortexm_ap(t);
|
||||
uint32_t ap_idcode = ap->dp->idcode;
|
||||
|
||||
/* Check the idcode is silabs. See AN0062 Section 2.2 */
|
||||
|
|
|
@ -452,9 +452,9 @@ handle_z_packet(char *packet, int plen)
|
|||
switch(type) {
|
||||
case 1: /* Hardware breakpoint */
|
||||
if(set)
|
||||
ret = target_set_hw_bp(cur_target, addr);
|
||||
ret = target_set_hw_bp(cur_target, addr, len);
|
||||
else
|
||||
ret = target_clear_hw_bp(cur_target, addr);
|
||||
ret = target_clear_hw_bp(cur_target, addr, len);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#define __ADIV5_H
|
||||
|
||||
#include "jtag_scan.h"
|
||||
#include "target.h"
|
||||
|
||||
#define ADIV5_APnDP 0x100
|
||||
#define ADIV5_DP_REG(x) (x)
|
||||
|
@ -111,6 +110,7 @@ typedef struct ADIv5_DP_s {
|
|||
uint32_t (*error)(struct ADIv5_DP_s *dp);
|
||||
uint32_t (*low_access)(struct ADIv5_DP_s *dp, uint8_t RnW,
|
||||
uint16_t addr, uint32_t value);
|
||||
void (*abort)(struct ADIv5_DP_s *dp, uint32_t abort);
|
||||
|
||||
union {
|
||||
jtag_dev_t *dev;
|
||||
|
@ -134,6 +134,11 @@ static inline uint32_t adiv5_dp_low_access(struct ADIv5_DP_s *dp, uint8_t RnW,
|
|||
return dp->low_access(dp, RnW, addr, value);
|
||||
}
|
||||
|
||||
static inline void adiv5_dp_abort(struct ADIv5_DP_s *dp, uint32_t abort)
|
||||
{
|
||||
return dp->abort(dp, abort);
|
||||
}
|
||||
|
||||
typedef struct ADIv5_AP_s {
|
||||
int refcnt;
|
||||
|
||||
|
@ -144,14 +149,12 @@ typedef struct ADIv5_AP_s {
|
|||
uint32_t cfg;
|
||||
uint32_t base;
|
||||
uint32_t csw;
|
||||
|
||||
void *priv;
|
||||
void (*priv_free)(void *);
|
||||
} ADIv5_AP_t;
|
||||
|
||||
void adiv5_dp_init(ADIv5_DP_t *dp);
|
||||
void adiv5_dp_write(ADIv5_DP_t *dp, uint16_t addr, uint32_t value);
|
||||
|
||||
ADIv5_AP_t *adiv5_new_ap(ADIv5_DP_t *dp, uint8_t apsel);
|
||||
void adiv5_dp_ref(ADIv5_DP_t *dp);
|
||||
void adiv5_ap_ref(ADIv5_AP_t *ap);
|
||||
void adiv5_dp_unref(ADIv5_DP_t *dp);
|
||||
|
@ -163,10 +166,8 @@ uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint16_t addr);
|
|||
void adiv5_jtag_dp_handler(jtag_dev_t *dev);
|
||||
int adiv5_swdp_scan(void);
|
||||
|
||||
static inline ADIv5_AP_t *adiv5_target_ap(target *target)
|
||||
{
|
||||
return target->priv;
|
||||
}
|
||||
void adiv5_mem_read(ADIv5_AP_t *ap, void *dest, uint32_t src, size_t len);
|
||||
void adiv5_mem_write(ADIv5_AP_t *ap, uint32_t dest, const void *src, size_t len);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#define __CORTEXM_H
|
||||
|
||||
#include "target.h"
|
||||
#include "adiv5.h"
|
||||
|
||||
/* Private peripheral bus base address */
|
||||
#define CORTEXM_PPB_BASE 0xE0000000
|
||||
|
@ -155,6 +156,9 @@
|
|||
|
||||
#define CORTEXM_TOPT_INHIBIT_SRST (1 << 2)
|
||||
|
||||
bool cortexm_probe(ADIv5_AP_t *ap);
|
||||
ADIv5_AP_t *cortexm_ap(target *t);
|
||||
|
||||
bool cortexm_attach(target *t);
|
||||
void cortexm_detach(target *t);
|
||||
void cortexm_halt_resume(target *t, bool step);
|
||||
|
|
|
@ -76,11 +76,11 @@ target *target_attach(target *t, target_destroy_callback destroy_cb);
|
|||
(target)->halt_resume((target), (step))
|
||||
|
||||
/* Break-/watchpoint functions */
|
||||
#define target_set_hw_bp(target, addr) \
|
||||
(target)->set_hw_bp((target), (addr))
|
||||
#define target_set_hw_bp(target, addr, len) \
|
||||
(target)->set_hw_bp((target), (addr), (len))
|
||||
|
||||
#define target_clear_hw_bp(target, addr) \
|
||||
(target)->clear_hw_bp((target), (addr))
|
||||
#define target_clear_hw_bp(target, addr, len) \
|
||||
(target)->clear_hw_bp((target), (addr), (len))
|
||||
|
||||
|
||||
#define target_set_hw_wp(target, type, addr, len) \
|
||||
|
@ -169,8 +169,8 @@ struct target_s {
|
|||
void (*halt_resume)(target *t, bool step);
|
||||
|
||||
/* Break-/watchpoint functions */
|
||||
int (*set_hw_bp)(target *t, uint32_t addr);
|
||||
int (*clear_hw_bp)(target *t, uint32_t addr);
|
||||
int (*set_hw_bp)(target *t, uint32_t addr, uint8_t len);
|
||||
int (*clear_hw_bp)(target *t, uint32_t addr, uint8_t len);
|
||||
|
||||
int (*set_hw_wp)(target *t, uint8_t type, uint32_t addr, uint8_t len);
|
||||
int (*clear_hw_wp)(target *t, uint8_t type, uint32_t addr, uint8_t len);
|
||||
|
@ -257,7 +257,6 @@ static inline void target_mem_write8(target *t, uint32_t addr, uint8_t value)
|
|||
/* Probe for various targets.
|
||||
* Actual functions implemented in their respective drivers.
|
||||
*/
|
||||
bool cortexm_probe(target *t);
|
||||
bool stm32f1_probe(target *t);
|
||||
bool stm32f4_probe(target *t);
|
||||
bool stm32l0_probe(target *t);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "morse.h"
|
||||
#include "jtag_scan.h"
|
||||
#include "gdb_packet.h"
|
||||
#include "target.h"
|
||||
#include "adiv5.h"
|
||||
|
||||
struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1];
|
||||
|
|
Loading…
Reference in New Issue