openocd/src/target/mem_ap.c

301 lines
7.5 KiB
C
Raw Normal View History

/*****************************************************************************
* Copyright (C) 2016 by Matthias Welwarsky <matthias.welwarsky@sysgo.com> *
* *
* 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. *
****************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "target.h"
#include "target_type.h"
#include "arm_adi_v5.h"
#include "register.h"
#include <jtag/jtag.h>
#define MEM_AP_COMMON_MAGIC 0x4DE4DA50
struct mem_ap {
int common_magic;
struct adiv5_dap *dap;
struct adiv5_ap *ap;
int ap_num;
};
static int mem_ap_target_create(struct target *target, Jim_Interp *interp)
{
struct mem_ap *mem_ap;
struct adiv5_private_config *pc;
pc = (struct adiv5_private_config *)target->private_config;
if (!pc)
return ERROR_FAIL;
if (pc->ap_num == DP_APSEL_INVALID) {
LOG_ERROR("AP number not specified");
return ERROR_FAIL;
}
mem_ap = calloc(1, sizeof(struct mem_ap));
if (!mem_ap) {
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
mem_ap->ap_num = pc->ap_num;
mem_ap->common_magic = MEM_AP_COMMON_MAGIC;
mem_ap->dap = pc->dap;
target->arch_info = mem_ap;
if (!target->gdb_port_override)
target->gdb_port_override = strdup("disabled");
return ERROR_OK;
}
static int mem_ap_init_target(struct command_context *cmd_ctx, struct target *target)
{
LOG_DEBUG("%s", __func__);
target->state = TARGET_UNKNOWN;
target->debug_reason = DBG_REASON_UNDEFINED;
return ERROR_OK;
}
static void mem_ap_deinit_target(struct target *target)
{
arm_adi_v5: add ap refcount and add get/put around ap use While an ADIv5 DAP can only have 256 AP, ADIv6 can provide till 2**40 (1,099,511,627,776) AP per DAP. The actual trivial code implementation for ADIv5 (that uses an array of 256 ap in the struct adiv5_dap) cannot be extended as-is to handle ADIv6. The simple array of 256 AP can be reused as a dynamic storage for ADIv6 ap: - the ADIv5 AP number is replaced by the ADIv6 base address; - the index of the array (equal to ADIv5 AP number) has no link to any ADIv6 property; - the ADIv6 base_address has to be searched in the array of AP. The 256 elements in the AP array should be enough for any device available today. In future it can be easily increased, if needed. To efficiently use the 256 elements in the AP array, the code should associate one element of the array to an ADIv6 AP (through the AP base address), then cancel the association when the AP is not anymore needed. This is important to avoid saturating the AP array while exploring the device through 'dap apreg' commands. Add a reference counter in the struct adiv5_ap to track how many times the struct has been associated with the same base address. Introduce the function dap_get_ap() to associate and return the struct, and dap_put_ap() to release the struct. For the moment the code covers ADIv5 only, so the association is through the index. Use the two functions above and dap_find_get_ap() throughout the code. Check the return value of dap_get_ap(). It is always not NULL in the current ADIv5-only implementation, but can be NULL for ADIv6 when there are no more available AP in the array. Instrument dap_queue_ap_read() and dap_queue_ap_write() to log an error message if the AP has reference counter zero, meaning that the AP has not been 'get' yet. This helps identifying AP used without get/put, e.g. code missed by this patch, or merged later. Instrument dap_cleanup_all() to log an error message if an AP has reference counter not zero at openocd exit, meaning that the AP has not been 'put' yet. Change-Id: I98316eb42b9f3d9c9bbbb6c73b1091b53f629092 Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com> Reviewed-on: https://review.openocd.org/c/openocd/+/6455 Reviewed-by: Daniel Goehring <dgoehrin@os.amperecomputing.com> Tested-by: jenkins
2021-08-04 21:07:57 +00:00
struct mem_ap *mem_ap = target->arch_info;
LOG_DEBUG("%s", __func__);
arm_adi_v5: add ap refcount and add get/put around ap use While an ADIv5 DAP can only have 256 AP, ADIv6 can provide till 2**40 (1,099,511,627,776) AP per DAP. The actual trivial code implementation for ADIv5 (that uses an array of 256 ap in the struct adiv5_dap) cannot be extended as-is to handle ADIv6. The simple array of 256 AP can be reused as a dynamic storage for ADIv6 ap: - the ADIv5 AP number is replaced by the ADIv6 base address; - the index of the array (equal to ADIv5 AP number) has no link to any ADIv6 property; - the ADIv6 base_address has to be searched in the array of AP. The 256 elements in the AP array should be enough for any device available today. In future it can be easily increased, if needed. To efficiently use the 256 elements in the AP array, the code should associate one element of the array to an ADIv6 AP (through the AP base address), then cancel the association when the AP is not anymore needed. This is important to avoid saturating the AP array while exploring the device through 'dap apreg' commands. Add a reference counter in the struct adiv5_ap to track how many times the struct has been associated with the same base address. Introduce the function dap_get_ap() to associate and return the struct, and dap_put_ap() to release the struct. For the moment the code covers ADIv5 only, so the association is through the index. Use the two functions above and dap_find_get_ap() throughout the code. Check the return value of dap_get_ap(). It is always not NULL in the current ADIv5-only implementation, but can be NULL for ADIv6 when there are no more available AP in the array. Instrument dap_queue_ap_read() and dap_queue_ap_write() to log an error message if the AP has reference counter zero, meaning that the AP has not been 'get' yet. This helps identifying AP used without get/put, e.g. code missed by this patch, or merged later. Instrument dap_cleanup_all() to log an error message if an AP has reference counter not zero at openocd exit, meaning that the AP has not been 'put' yet. Change-Id: I98316eb42b9f3d9c9bbbb6c73b1091b53f629092 Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com> Reviewed-on: https://review.openocd.org/c/openocd/+/6455 Reviewed-by: Daniel Goehring <dgoehrin@os.amperecomputing.com> Tested-by: jenkins
2021-08-04 21:07:57 +00:00
if (mem_ap->ap)
dap_put_ap(mem_ap->ap);
free(target->private_config);
free(target->arch_info);
return;
}
static int mem_ap_arch_state(struct target *target)
{
LOG_DEBUG("%s", __func__);
return ERROR_OK;
}
static int mem_ap_poll(struct target *target)
{
if (target->state == TARGET_UNKNOWN) {
target->state = TARGET_RUNNING;
target->debug_reason = DBG_REASON_NOTHALTED;
}
return ERROR_OK;
}
static int mem_ap_halt(struct target *target)
{
LOG_DEBUG("%s", __func__);
target->state = TARGET_HALTED;
target->debug_reason = DBG_REASON_DBGRQ;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return ERROR_OK;
}
static int mem_ap_resume(struct target *target, int current, target_addr_t address,
int handle_breakpoints, int debug_execution)
{
LOG_DEBUG("%s", __func__);
target->state = TARGET_RUNNING;
target->debug_reason = DBG_REASON_NOTHALTED;
return ERROR_OK;
}
static int mem_ap_step(struct target *target, int current, target_addr_t address,
int handle_breakpoints)
{
LOG_DEBUG("%s", __func__);
target->state = TARGET_HALTED;
target->debug_reason = DBG_REASON_DBGRQ;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
return ERROR_OK;
}
static int mem_ap_assert_reset(struct target *target)
{
target->state = TARGET_RESET;
target->debug_reason = DBG_REASON_UNDEFINED;
LOG_DEBUG("%s", __func__);
return ERROR_OK;
}
static int mem_ap_examine(struct target *target)
{
struct mem_ap *mem_ap = target->arch_info;
if (!target_was_examined(target)) {
arm_adi_v5: add ap refcount and add get/put around ap use While an ADIv5 DAP can only have 256 AP, ADIv6 can provide till 2**40 (1,099,511,627,776) AP per DAP. The actual trivial code implementation for ADIv5 (that uses an array of 256 ap in the struct adiv5_dap) cannot be extended as-is to handle ADIv6. The simple array of 256 AP can be reused as a dynamic storage for ADIv6 ap: - the ADIv5 AP number is replaced by the ADIv6 base address; - the index of the array (equal to ADIv5 AP number) has no link to any ADIv6 property; - the ADIv6 base_address has to be searched in the array of AP. The 256 elements in the AP array should be enough for any device available today. In future it can be easily increased, if needed. To efficiently use the 256 elements in the AP array, the code should associate one element of the array to an ADIv6 AP (through the AP base address), then cancel the association when the AP is not anymore needed. This is important to avoid saturating the AP array while exploring the device through 'dap apreg' commands. Add a reference counter in the struct adiv5_ap to track how many times the struct has been associated with the same base address. Introduce the function dap_get_ap() to associate and return the struct, and dap_put_ap() to release the struct. For the moment the code covers ADIv5 only, so the association is through the index. Use the two functions above and dap_find_get_ap() throughout the code. Check the return value of dap_get_ap(). It is always not NULL in the current ADIv5-only implementation, but can be NULL for ADIv6 when there are no more available AP in the array. Instrument dap_queue_ap_read() and dap_queue_ap_write() to log an error message if the AP has reference counter zero, meaning that the AP has not been 'get' yet. This helps identifying AP used without get/put, e.g. code missed by this patch, or merged later. Instrument dap_cleanup_all() to log an error message if an AP has reference counter not zero at openocd exit, meaning that the AP has not been 'put' yet. Change-Id: I98316eb42b9f3d9c9bbbb6c73b1091b53f629092 Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com> Reviewed-on: https://review.openocd.org/c/openocd/+/6455 Reviewed-by: Daniel Goehring <dgoehrin@os.amperecomputing.com> Tested-by: jenkins
2021-08-04 21:07:57 +00:00
if (mem_ap->ap) {
dap_put_ap(mem_ap->ap);
mem_ap->ap = NULL;
}
mem_ap->ap = dap_get_ap(mem_ap->dap, mem_ap->ap_num);
if (!mem_ap->ap) {
LOG_ERROR("Cannot get AP");
return ERROR_FAIL;
}
target_set_examined(target);
target->state = TARGET_UNKNOWN;
target->debug_reason = DBG_REASON_UNDEFINED;
return mem_ap_init(mem_ap->ap);
}
return ERROR_OK;
}
static int mem_ap_deassert_reset(struct target *target)
{
if (target->reset_halt) {
target->state = TARGET_HALTED;
target->debug_reason = DBG_REASON_DBGRQ;
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
} else {
target->state = TARGET_RUNNING;
target->debug_reason = DBG_REASON_NOTHALTED;
}
LOG_DEBUG("%s", __func__);
return ERROR_OK;
}
static int mem_ap_reg_get(struct reg *reg)
{
return ERROR_OK;
}
static int mem_ap_reg_set(struct reg *reg, uint8_t *buf)
{
return ERROR_OK;
}
static struct reg_arch_type mem_ap_reg_arch_type = {
.get = mem_ap_reg_get,
.set = mem_ap_reg_set,
};
const char *mem_ap_get_gdb_arch(struct target *target)
{
return "arm";
}
/*
* Dummy ARM register emulation:
* reg[0..15]: 32 bits, r0~r12, sp, lr, pc
* reg[16..23]: 96 bits, f0~f7
* reg[24]: 32 bits, fps
* reg[25]: 32 bits, cpsr
*
* Set 'exist' only to reg[0..15], so initial response to GDB is correct
*/
#define NUM_REGS 26
#define MAX_REG_SIZE 96
#define REG_EXIST(n) ((n) < 16)
#define REG_SIZE(n) ((((n) >= 16) && ((n) < 24)) ? 96 : 32)
struct mem_ap_alloc_reg_list {
/* reg_list must be the first field */
struct reg *reg_list[NUM_REGS];
struct reg regs[NUM_REGS];
uint8_t regs_value[MAX_REG_SIZE / 8];
};
static int mem_ap_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
int *reg_list_size, enum target_register_class reg_class)
{
struct mem_ap_alloc_reg_list *mem_ap_alloc = calloc(1, sizeof(struct mem_ap_alloc_reg_list));
if (!mem_ap_alloc) {
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
*reg_list = mem_ap_alloc->reg_list;
*reg_list_size = NUM_REGS;
struct reg *regs = mem_ap_alloc->regs;
for (int i = 0; i < NUM_REGS; i++) {
regs[i].number = i;
regs[i].value = mem_ap_alloc->regs_value;
regs[i].size = REG_SIZE(i);
regs[i].exist = REG_EXIST(i);
regs[i].type = &mem_ap_reg_arch_type;
(*reg_list)[i] = &regs[i];
}
return ERROR_OK;
}
static int mem_ap_read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
struct mem_ap *mem_ap = target->arch_info;
LOG_DEBUG("Reading memory at physical address " TARGET_ADDR_FMT
"; size %" PRIu32 "; count %" PRIu32, address, size, count);
if (count == 0 || !buffer)
return ERROR_COMMAND_SYNTAX_ERROR;
return mem_ap_read_buf(mem_ap->ap, buffer, size, count, address);
}
static int mem_ap_write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count,
const uint8_t *buffer)
{
struct mem_ap *mem_ap = target->arch_info;
LOG_DEBUG("Writing memory at physical address " TARGET_ADDR_FMT
"; size %" PRIu32 "; count %" PRIu32, address, size, count);
if (count == 0 || !buffer)
return ERROR_COMMAND_SYNTAX_ERROR;
return mem_ap_write_buf(mem_ap->ap, buffer, size, count, address);
}
struct target_type mem_ap_target = {
.name = "mem_ap",
.target_create = mem_ap_target_create,
.init_target = mem_ap_init_target,
.deinit_target = mem_ap_deinit_target,
.examine = mem_ap_examine,
.target_jim_configure = adiv5_jim_configure,
.poll = mem_ap_poll,
.arch_state = mem_ap_arch_state,
.halt = mem_ap_halt,
.resume = mem_ap_resume,
.step = mem_ap_step,
.assert_reset = mem_ap_assert_reset,
.deassert_reset = mem_ap_deassert_reset,
.get_gdb_arch = mem_ap_get_gdb_arch,
.get_gdb_reg_list = mem_ap_get_gdb_reg_list,
.read_memory = mem_ap_read_memory,
.write_memory = mem_ap_write_memory,
};