Merge remote-tracking branch 'origin/riscv' into riscv-compliance-rebase
This commit is contained in:
@@ -19,6 +19,8 @@ NOR_DRIVERS = \
|
||||
%D%/atsamv.c \
|
||||
%D%/avrf.c \
|
||||
%D%/bluenrg-x.c \
|
||||
%D%/cc3220sf.c \
|
||||
%D%/cc26xx.c \
|
||||
%D%/cfi.c \
|
||||
%D%/dsp5680xx_flash.c \
|
||||
%D%/efm32.c \
|
||||
@@ -35,6 +37,7 @@ NOR_DRIVERS = \
|
||||
%D%/lpc2900.c \
|
||||
%D%/lpcspifi.c \
|
||||
%D%/mdr.c \
|
||||
%D%/msp432.c \
|
||||
%D%/mrvlqspi.c \
|
||||
%D%/niietcm4.c \
|
||||
%D%/non_cfi.c \
|
||||
@@ -43,6 +46,7 @@ NOR_DRIVERS = \
|
||||
%D%/ocl.c \
|
||||
%D%/pic32mx.c \
|
||||
%D%/psoc4.c \
|
||||
%D%/psoc5lp.c \
|
||||
%D%/psoc6.c \
|
||||
%D%/sim3x.c \
|
||||
%D%/spi.c \
|
||||
@@ -64,9 +68,12 @@ NOR_DRIVERS = \
|
||||
|
||||
NORHEADERS = \
|
||||
%D%/core.h \
|
||||
%D%/cc3220sf.h \
|
||||
%D%/cc26xx.h \
|
||||
%D%/cfi.h \
|
||||
%D%/driver.h \
|
||||
%D%/imp.h \
|
||||
%D%/non_cfi.h \
|
||||
%D%/ocl.h \
|
||||
%D%/spi.h
|
||||
%D%/spi.h \
|
||||
%D%/msp432.h
|
||||
|
||||
+37
-44
@@ -682,6 +682,40 @@ static const struct sam4_chip_details all_sam4_details[] = {
|
||||
},
|
||||
},
|
||||
},
|
||||
/*at91sam4sa16c - TFBGA100/VFBGA100/LQFP100*/
|
||||
{
|
||||
.chipid_cidr = 0x28a70ce0,
|
||||
.name = "at91sam4sa16c",
|
||||
.total_flash_size = 1024 * 1024,
|
||||
.total_sram_size = 160 * 1024,
|
||||
.n_gpnvms = 2,
|
||||
.n_banks = 1,
|
||||
|
||||
/* .bank[0] = { */
|
||||
{
|
||||
{
|
||||
.probed = 0,
|
||||
.pChip = NULL,
|
||||
.pBank = NULL,
|
||||
.bank_number = 0,
|
||||
.base_address = FLASH_BANK_BASE_S,
|
||||
.controller_address = 0x400e0a00,
|
||||
.flash_wait_states = 5,
|
||||
.present = 1,
|
||||
.size_bytes = 1024 * 1024,
|
||||
.nsectors = 128,
|
||||
.sector_size = 8192,
|
||||
.page_size = 512,
|
||||
},
|
||||
/* .bank[1] = {*/
|
||||
{
|
||||
.present = 0,
|
||||
.probed = 0,
|
||||
.bank_number = 1,
|
||||
|
||||
},
|
||||
},
|
||||
},
|
||||
/*atsam4s16b - LQFP64/QFN64/WLCSP64*/
|
||||
{
|
||||
.chipid_cidr = 0x289C0CE0,
|
||||
@@ -1243,50 +1277,6 @@ static const struct sam4_chip_details all_sam4_details[] = {
|
||||
.page_size = 512,
|
||||
},
|
||||
|
||||
/* .bank[1] = { */
|
||||
{
|
||||
.probed = 0,
|
||||
.pChip = NULL,
|
||||
.pBank = NULL,
|
||||
.bank_number = 1,
|
||||
.base_address = FLASH_BANK1_BASE_1024K_SD,
|
||||
.controller_address = 0x400e0c00,
|
||||
.flash_wait_states = 5,
|
||||
.present = 1,
|
||||
.size_bytes = 512 * 1024,
|
||||
.nsectors = 64,
|
||||
.sector_size = 8192,
|
||||
.page_size = 512,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
/*at91sam4sa16c*/
|
||||
{
|
||||
.chipid_cidr = 0x28a70ce0,
|
||||
.name = "at91sam4sa16c",
|
||||
.total_flash_size = 1024 * 1024,
|
||||
.total_sram_size = 160 * 1024,
|
||||
.n_gpnvms = 3,
|
||||
.n_banks = 2,
|
||||
|
||||
/* .bank[0] = { */
|
||||
{
|
||||
{
|
||||
.probed = 0,
|
||||
.pChip = NULL,
|
||||
.pBank = NULL,
|
||||
.bank_number = 0,
|
||||
.base_address = FLASH_BANK0_BASE_SD,
|
||||
.controller_address = 0x400e0a00,
|
||||
.flash_wait_states = 5,
|
||||
.present = 1,
|
||||
.size_bytes = 512 * 1024,
|
||||
.nsectors = 64,
|
||||
.sector_size = 8192,
|
||||
.page_size = 512,
|
||||
},
|
||||
|
||||
/* .bank[1] = { */
|
||||
{
|
||||
.probed = 0,
|
||||
@@ -2554,6 +2544,8 @@ static int sam4_GetDetails(struct sam4_bank_private *pPrivate)
|
||||
pPrivate->pChip->cfg.CHIPID_CIDR);
|
||||
sam4_explain_chipid_cidr(pPrivate->pChip);
|
||||
return ERROR_FAIL;
|
||||
} else {
|
||||
LOG_INFO("SAM4 Found chip %s, CIDR 0x%08x", pDetails->name, pDetails->chipid_cidr);
|
||||
}
|
||||
|
||||
/* DANGER: THERE ARE DRAGONS HERE */
|
||||
@@ -2624,6 +2616,7 @@ static int _sam4_probe(struct flash_bank *bank, int noise)
|
||||
for (x = 0; x < SAM4_MAX_FLASH_BANKS; x++) {
|
||||
if (bank->base == pPrivate->pChip->details.bank[x].base_address) {
|
||||
bank->size = pPrivate->pChip->details.bank[x].size_bytes;
|
||||
LOG_INFO("SAM4 Set flash bank to %08X - %08X, idx %d", bank->base, bank->base + bank->size, x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,15 +115,16 @@ static const struct samd_part samd10_parts[] = {
|
||||
|
||||
/* Known SAMD11 parts */
|
||||
static const struct samd_part samd11_parts[] = {
|
||||
{ 0x0, "SAMD11D14AMU", 16, 4 },
|
||||
{ 0x0, "SAMD11D14AM", 16, 4 },
|
||||
{ 0x1, "SAMD11D13AMU", 8, 4 },
|
||||
{ 0x2, "SAMD11D12AMU", 4, 4 },
|
||||
{ 0x3, "SAMD11D14ASU", 16, 4 },
|
||||
{ 0x3, "SAMD11D14ASS", 16, 4 },
|
||||
{ 0x4, "SAMD11D13ASU", 8, 4 },
|
||||
{ 0x5, "SAMD11D12ASU", 4, 4 },
|
||||
{ 0x6, "SAMD11C14A", 16, 4 },
|
||||
{ 0x7, "SAMD11C13A", 8, 4 },
|
||||
{ 0x8, "SAMD11C12A", 4, 4 },
|
||||
{ 0x9, "SAMD11D14AU", 16, 4 },
|
||||
};
|
||||
|
||||
/* Known SAMD20 parts. See Table 12-8 in 42129F–SAM–10/2013 */
|
||||
|
||||
@@ -0,0 +1,567 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "imp.h"
|
||||
#include "cc26xx.h"
|
||||
#include <helper/binarybuffer.h>
|
||||
#include <helper/time_support.h>
|
||||
#include <target/algorithm.h>
|
||||
#include <target/armv7m.h>
|
||||
#include <target/image.h>
|
||||
|
||||
#define FLASH_TIMEOUT 8000
|
||||
|
||||
struct cc26xx_bank {
|
||||
const char *family_name;
|
||||
uint32_t icepick_id;
|
||||
uint32_t user_id;
|
||||
uint32_t device_type;
|
||||
uint32_t sector_length;
|
||||
bool probed;
|
||||
struct working_area *working_area;
|
||||
struct armv7m_algorithm armv7m_info;
|
||||
const uint8_t *algo_code;
|
||||
uint32_t algo_size;
|
||||
uint32_t algo_working_size;
|
||||
uint32_t buffer_addr[2];
|
||||
uint32_t params_addr[2];
|
||||
};
|
||||
|
||||
static int cc26xx_auto_probe(struct flash_bank *bank);
|
||||
|
||||
static uint32_t cc26xx_device_type(uint32_t icepick_id, uint32_t user_id)
|
||||
{
|
||||
uint32_t device_type = 0;
|
||||
|
||||
switch (icepick_id & ICEPICK_ID_MASK) {
|
||||
case CC26X0_ICEPICK_ID:
|
||||
device_type = CC26X0_TYPE;
|
||||
break;
|
||||
case CC26X1_ICEPICK_ID:
|
||||
device_type = CC26X1_TYPE;
|
||||
break;
|
||||
case CC13X0_ICEPICK_ID:
|
||||
device_type = CC13X0_TYPE;
|
||||
break;
|
||||
case CC13X2_CC26X2_ICEPICK_ID:
|
||||
default:
|
||||
if ((user_id & USER_ID_CC13_MASK) != 0)
|
||||
device_type = CC13X2_TYPE;
|
||||
else
|
||||
device_type = CC26X2_TYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
return device_type;
|
||||
}
|
||||
|
||||
static uint32_t cc26xx_sector_length(uint32_t icepick_id)
|
||||
{
|
||||
uint32_t sector_length;
|
||||
|
||||
switch (icepick_id & ICEPICK_ID_MASK) {
|
||||
case CC26X0_ICEPICK_ID:
|
||||
case CC26X1_ICEPICK_ID:
|
||||
case CC13X0_ICEPICK_ID:
|
||||
/* Chameleon family device */
|
||||
sector_length = CC26X0_SECTOR_LENGTH;
|
||||
break;
|
||||
case CC13X2_CC26X2_ICEPICK_ID:
|
||||
default:
|
||||
/* Agama family device */
|
||||
sector_length = CC26X2_SECTOR_LENGTH;
|
||||
break;
|
||||
}
|
||||
|
||||
return sector_length;
|
||||
}
|
||||
|
||||
static int cc26xx_wait_algo_done(struct flash_bank *bank, uint32_t params_addr)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
uint32_t status_addr = params_addr + CC26XX_STATUS_OFFSET;
|
||||
uint32_t status = CC26XX_BUFFER_FULL;
|
||||
long long start_ms;
|
||||
long long elapsed_ms;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
start_ms = timeval_ms();
|
||||
while (CC26XX_BUFFER_FULL == status) {
|
||||
retval = target_read_u32(target, status_addr, &status);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
elapsed_ms = timeval_ms() - start_ms;
|
||||
if (elapsed_ms > 500)
|
||||
keep_alive();
|
||||
if (elapsed_ms > FLASH_TIMEOUT)
|
||||
break;
|
||||
};
|
||||
|
||||
if (CC26XX_BUFFER_EMPTY != status) {
|
||||
LOG_ERROR("%s: Flash operation failed", cc26xx_bank->family_name);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_init(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
int retval;
|
||||
|
||||
/* Make sure we've probed the flash to get the device and size */
|
||||
retval = cc26xx_auto_probe(bank);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Check for working area to use for flash helper algorithm */
|
||||
if (NULL != cc26xx_bank->working_area)
|
||||
target_free_working_area(target, cc26xx_bank->working_area);
|
||||
retval = target_alloc_working_area(target, cc26xx_bank->algo_working_size,
|
||||
&cc26xx_bank->working_area);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Confirm the defined working address is the area we need to use */
|
||||
if (CC26XX_ALGO_BASE_ADDRESS != cc26xx_bank->working_area->address)
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
|
||||
/* Write flash helper algorithm into target memory */
|
||||
retval = target_write_buffer(target, CC26XX_ALGO_BASE_ADDRESS,
|
||||
cc26xx_bank->algo_size, cc26xx_bank->algo_code);
|
||||
if (ERROR_OK != retval) {
|
||||
LOG_ERROR("%s: Failed to load flash helper algorithm",
|
||||
cc26xx_bank->family_name);
|
||||
target_free_working_area(target, cc26xx_bank->working_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Initialize the ARMv7 specific info to run the algorithm */
|
||||
cc26xx_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
|
||||
cc26xx_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
|
||||
|
||||
/* Begin executing the flash helper algorithm */
|
||||
retval = target_start_algorithm(target, 0, NULL, 0, NULL,
|
||||
CC26XX_ALGO_BASE_ADDRESS, 0, &cc26xx_bank->armv7m_info);
|
||||
if (ERROR_OK != retval) {
|
||||
LOG_ERROR("%s: Failed to start flash helper algorithm",
|
||||
cc26xx_bank->family_name);
|
||||
target_free_working_area(target, cc26xx_bank->working_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, the algorithm is running on the target and
|
||||
* ready to receive commands and data to flash the target
|
||||
*/
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_quit(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
int retval;
|
||||
|
||||
/* Regardless of the algo's status, attempt to halt the target */
|
||||
(void)target_halt(target);
|
||||
|
||||
/* Now confirm target halted and clean up from flash helper algorithm */
|
||||
retval = target_wait_algorithm(target, 0, NULL, 0, NULL, 0, FLASH_TIMEOUT,
|
||||
&cc26xx_bank->armv7m_info);
|
||||
|
||||
target_free_working_area(target, cc26xx_bank->working_area);
|
||||
cc26xx_bank->working_area = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_mass_erase(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
struct cc26xx_algo_params algo_params;
|
||||
|
||||
int retval;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
retval = cc26xx_init(bank);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Initialize algorithm parameters */
|
||||
buf_set_u32(algo_params.address, 0, 32, 0);
|
||||
buf_set_u32(algo_params.length, 0, 32, 4);
|
||||
buf_set_u32(algo_params.command, 0, 32, CC26XX_CMD_ERASE_ALL);
|
||||
buf_set_u32(algo_params.status, 0, 32, CC26XX_BUFFER_FULL);
|
||||
|
||||
/* Issue flash helper algorithm parameters for mass erase */
|
||||
retval = target_write_buffer(target, cc26xx_bank->params_addr[0],
|
||||
sizeof(algo_params), (uint8_t *)&algo_params);
|
||||
|
||||
/* Wait for command to complete */
|
||||
if (ERROR_OK == retval)
|
||||
retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[0]);
|
||||
|
||||
/* Regardless of errors, try to close down algo */
|
||||
(void)cc26xx_quit(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(cc26xx_flash_bank_command)
|
||||
{
|
||||
struct cc26xx_bank *cc26xx_bank;
|
||||
|
||||
if (CMD_ARGC < 6)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
cc26xx_bank = malloc(sizeof(struct cc26xx_bank));
|
||||
if (NULL == cc26xx_bank)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* Initialize private flash information */
|
||||
memset((void *)cc26xx_bank, 0x00, sizeof(struct cc26xx_bank));
|
||||
cc26xx_bank->family_name = "cc26xx";
|
||||
cc26xx_bank->device_type = CC26XX_NO_TYPE;
|
||||
cc26xx_bank->sector_length = 0x1000;
|
||||
|
||||
/* Finish initialization of bank */
|
||||
bank->driver_priv = cc26xx_bank;
|
||||
bank->next = NULL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_erase(struct flash_bank *bank, int first, int last)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
struct cc26xx_algo_params algo_params;
|
||||
|
||||
uint32_t address;
|
||||
uint32_t length;
|
||||
int retval;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
/* Do a mass erase if user requested all sectors of flash */
|
||||
if ((first == 0) && (last == (bank->num_sectors - 1))) {
|
||||
/* Request mass erase of flash */
|
||||
return cc26xx_mass_erase(bank);
|
||||
}
|
||||
|
||||
address = first * cc26xx_bank->sector_length;
|
||||
length = (last - first + 1) * cc26xx_bank->sector_length;
|
||||
|
||||
retval = cc26xx_init(bank);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Set up algorithm parameters for erase command */
|
||||
buf_set_u32(algo_params.address, 0, 32, address);
|
||||
buf_set_u32(algo_params.length, 0, 32, length);
|
||||
buf_set_u32(algo_params.command, 0, 32, CC26XX_CMD_ERASE_SECTORS);
|
||||
buf_set_u32(algo_params.status, 0, 32, CC26XX_BUFFER_FULL);
|
||||
|
||||
/* Issue flash helper algorithm parameters for erase */
|
||||
retval = target_write_buffer(target, cc26xx_bank->params_addr[0],
|
||||
sizeof(algo_params), (uint8_t *)&algo_params);
|
||||
|
||||
/* If no error, wait for erase to finish */
|
||||
if (ERROR_OK == retval)
|
||||
retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[0]);
|
||||
|
||||
/* Regardless of errors, try to close down algo */
|
||||
(void)cc26xx_quit(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_protect(struct flash_bank *bank, int set, int first,
|
||||
int last)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
struct cc26xx_algo_params algo_params[2];
|
||||
uint32_t size = 0;
|
||||
long long start_ms;
|
||||
long long elapsed_ms;
|
||||
uint32_t address;
|
||||
|
||||
uint32_t index;
|
||||
int retval;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
retval = cc26xx_init(bank);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Initialize algorithm parameters to default values */
|
||||
buf_set_u32(algo_params[0].command, 0, 32, CC26XX_CMD_PROGRAM);
|
||||
buf_set_u32(algo_params[1].command, 0, 32, CC26XX_CMD_PROGRAM);
|
||||
|
||||
/* Write requested data, ping-ponging between two buffers */
|
||||
index = 0;
|
||||
start_ms = timeval_ms();
|
||||
address = bank->base + offset;
|
||||
while (count > 0) {
|
||||
|
||||
if (count > cc26xx_bank->sector_length)
|
||||
size = cc26xx_bank->sector_length;
|
||||
else
|
||||
size = count;
|
||||
|
||||
/* Put next block of data to flash into buffer */
|
||||
retval = target_write_buffer(target, cc26xx_bank->buffer_addr[index],
|
||||
size, buffer);
|
||||
if (ERROR_OK != retval) {
|
||||
LOG_ERROR("Unable to write data to target memory");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update algo parameters for next block */
|
||||
buf_set_u32(algo_params[index].address, 0, 32, address);
|
||||
buf_set_u32(algo_params[index].length, 0, 32, size);
|
||||
buf_set_u32(algo_params[index].status, 0, 32, CC26XX_BUFFER_FULL);
|
||||
|
||||
/* Issue flash helper algorithm parameters for block write */
|
||||
retval = target_write_buffer(target, cc26xx_bank->params_addr[index],
|
||||
sizeof(algo_params[index]), (uint8_t *)&algo_params[index]);
|
||||
if (ERROR_OK != retval)
|
||||
break;
|
||||
|
||||
/* Wait for next ping pong buffer to be ready */
|
||||
index ^= 1;
|
||||
retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[index]);
|
||||
if (ERROR_OK != retval)
|
||||
break;
|
||||
|
||||
count -= size;
|
||||
buffer += size;
|
||||
address += size;
|
||||
|
||||
elapsed_ms = timeval_ms() - start_ms;
|
||||
if (elapsed_ms > 500)
|
||||
keep_alive();
|
||||
}
|
||||
|
||||
/* If no error yet, wait for last buffer to finish */
|
||||
if (ERROR_OK == retval) {
|
||||
index ^= 1;
|
||||
retval = cc26xx_wait_algo_done(bank, cc26xx_bank->params_addr[index]);
|
||||
}
|
||||
|
||||
/* Regardless of errors, try to close down algo */
|
||||
(void)cc26xx_quit(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
uint32_t sector_length;
|
||||
uint32_t value;
|
||||
int num_sectors;
|
||||
int max_sectors;
|
||||
|
||||
int retval;
|
||||
|
||||
retval = target_read_u32(target, FCFG1_ICEPICK_ID, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
cc26xx_bank->icepick_id = value;
|
||||
|
||||
retval = target_read_u32(target, FCFG1_USER_ID, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
cc26xx_bank->user_id = value;
|
||||
|
||||
cc26xx_bank->device_type = cc26xx_device_type(cc26xx_bank->icepick_id,
|
||||
cc26xx_bank->user_id);
|
||||
|
||||
sector_length = cc26xx_sector_length(cc26xx_bank->icepick_id);
|
||||
|
||||
/* Set up appropriate flash helper algorithm */
|
||||
switch (cc26xx_bank->icepick_id & ICEPICK_ID_MASK) {
|
||||
case CC26X0_ICEPICK_ID:
|
||||
case CC26X1_ICEPICK_ID:
|
||||
case CC13X0_ICEPICK_ID:
|
||||
/* Chameleon family device */
|
||||
cc26xx_bank->algo_code = cc26x0_algo;
|
||||
cc26xx_bank->algo_size = sizeof(cc26x0_algo);
|
||||
cc26xx_bank->algo_working_size = CC26X0_WORKING_SIZE;
|
||||
cc26xx_bank->buffer_addr[0] = CC26X0_ALGO_BUFFER_0;
|
||||
cc26xx_bank->buffer_addr[1] = CC26X0_ALGO_BUFFER_1;
|
||||
cc26xx_bank->params_addr[0] = CC26X0_ALGO_PARAMS_0;
|
||||
cc26xx_bank->params_addr[1] = CC26X0_ALGO_PARAMS_1;
|
||||
max_sectors = CC26X0_MAX_SECTORS;
|
||||
break;
|
||||
case CC13X2_CC26X2_ICEPICK_ID:
|
||||
default:
|
||||
/* Agama family device */
|
||||
cc26xx_bank->algo_code = cc26x2_algo;
|
||||
cc26xx_bank->algo_size = sizeof(cc26x2_algo);
|
||||
cc26xx_bank->algo_working_size = CC26X2_WORKING_SIZE;
|
||||
cc26xx_bank->buffer_addr[0] = CC26X2_ALGO_BUFFER_0;
|
||||
cc26xx_bank->buffer_addr[1] = CC26X2_ALGO_BUFFER_1;
|
||||
cc26xx_bank->params_addr[0] = CC26X2_ALGO_PARAMS_0;
|
||||
cc26xx_bank->params_addr[1] = CC26X2_ALGO_PARAMS_1;
|
||||
max_sectors = CC26X2_MAX_SECTORS;
|
||||
break;
|
||||
}
|
||||
|
||||
retval = target_read_u32(target, CC26XX_FLASH_SIZE_INFO, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
num_sectors = value & 0xff;
|
||||
if (num_sectors > max_sectors)
|
||||
num_sectors = max_sectors;
|
||||
|
||||
bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
|
||||
if (NULL == bank->sectors)
|
||||
return ERROR_FAIL;
|
||||
|
||||
bank->base = CC26XX_FLASH_BASE_ADDR;
|
||||
bank->num_sectors = num_sectors;
|
||||
bank->size = num_sectors * sector_length;
|
||||
bank->write_start_alignment = 0;
|
||||
bank->write_end_alignment = 0;
|
||||
cc26xx_bank->sector_length = sector_length;
|
||||
|
||||
for (int i = 0; i < num_sectors; i++) {
|
||||
bank->sectors[i].offset = i * sector_length;
|
||||
bank->sectors[i].size = sector_length;
|
||||
bank->sectors[i].is_erased = -1;
|
||||
bank->sectors[i].is_protected = 0;
|
||||
}
|
||||
|
||||
/* We've successfully determined the stats on the flash bank */
|
||||
cc26xx_bank->probed = true;
|
||||
|
||||
/* If we fall through to here, then all went well */
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_auto_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (bank->bank_number != 0) {
|
||||
/* Invalid bank number somehow */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!cc26xx_bank->probed)
|
||||
retval = cc26xx_probe(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc26xx_protect_check(struct flash_bank *bank)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc26xx_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||
{
|
||||
struct cc26xx_bank *cc26xx_bank = bank->driver_priv;
|
||||
int printed = 0;
|
||||
const char *device;
|
||||
|
||||
switch (cc26xx_bank->device_type) {
|
||||
case CC26X0_TYPE:
|
||||
device = "CC26x0";
|
||||
break;
|
||||
case CC26X1_TYPE:
|
||||
device = "CC26x1";
|
||||
break;
|
||||
case CC13X0_TYPE:
|
||||
device = "CC13x0";
|
||||
break;
|
||||
case CC13X2_TYPE:
|
||||
device = "CC13x2";
|
||||
break;
|
||||
case CC26X2_TYPE:
|
||||
device = "CC26x2";
|
||||
break;
|
||||
case CC26XX_NO_TYPE:
|
||||
default:
|
||||
device = "Unrecognized";
|
||||
break;
|
||||
}
|
||||
|
||||
printed = snprintf(buf, buf_size,
|
||||
"%s device: ICEPick ID 0x%08x, USER ID 0x%08x\n",
|
||||
device, cc26xx_bank->icepick_id, cc26xx_bank->user_id);
|
||||
|
||||
if (printed >= buf_size)
|
||||
return ERROR_BUF_TOO_SMALL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
struct flash_driver cc26xx_flash = {
|
||||
.name = "cc26xx",
|
||||
.flash_bank_command = cc26xx_flash_bank_command,
|
||||
.erase = cc26xx_erase,
|
||||
.protect = cc26xx_protect,
|
||||
.write = cc26xx_write,
|
||||
.read = default_flash_read,
|
||||
.probe = cc26xx_probe,
|
||||
.auto_probe = cc26xx_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = cc26xx_protect_check,
|
||||
.info = cc26xx_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_FLASH_NOR_CC26XX_H
|
||||
#define OPENOCD_FLASH_NOR_CC26XX_H
|
||||
|
||||
/* Addresses of FCFG1 registers to access ICEPick Device ID and User ID */
|
||||
#define FCFG1_ICEPICK_ID 0x50001318
|
||||
#define FCFG1_USER_ID 0x50001294
|
||||
|
||||
/* ICEPick device ID mask and values */
|
||||
#define ICEPICK_ID_MASK 0x0fffffff
|
||||
#define ICEPICK_REV_MASK 0xf0000000
|
||||
#define CC26X0_ICEPICK_ID 0x0b99a02f
|
||||
#define CC26X1_ICEPICK_ID 0x0b9bd02f
|
||||
#define CC13X0_ICEPICK_ID 0x0b9be02f
|
||||
#define CC13X2_CC26X2_ICEPICK_ID 0x0bb4102f
|
||||
|
||||
/* User ID mask for Agama CC13x2 vs CC26x2 */
|
||||
#define USER_ID_CC13_MASK 0x00800000
|
||||
|
||||
/* Common CC26xx/CC13xx flash and memory parameters */
|
||||
#define CC26XX_FLASH_BASE_ADDR 0x00000000
|
||||
#define CC26XX_FLASH_SIZE_INFO 0x4003002c
|
||||
#define CC26XX_SRAM_SIZE_INFO 0x40082250
|
||||
#define CC26XX_ALGO_BASE_ADDRESS 0x20000000
|
||||
|
||||
/* Chameleon CC26x0/CC13x0 specific parameters */
|
||||
#define CC26X0_MAX_SECTORS 32
|
||||
#define CC26X0_SECTOR_LENGTH 0x1000
|
||||
#define CC26X0_ALGO_BUFFER_0 0x20001c00
|
||||
#define CC26X0_ALGO_BUFFER_1 0x20002c00
|
||||
#define CC26X0_ALGO_PARAMS_0 0x20001bd8
|
||||
#define CC26X0_ALGO_PARAMS_1 0x20001bec
|
||||
#define CC26X0_WORKING_SIZE (CC26X0_ALGO_BUFFER_1 + CC26X0_SECTOR_LENGTH - \
|
||||
CC26XX_ALGO_BASE_ADDRESS)
|
||||
|
||||
/* Agama CC26x2/CC13x2 specific parameters */
|
||||
#define CC26X2_MAX_SECTORS 128
|
||||
#define CC26X2_SECTOR_LENGTH 0x2000
|
||||
#define CC26X2_ALGO_BUFFER_0 0x20002000
|
||||
#define CC26X2_ALGO_BUFFER_1 0x20004000
|
||||
#define CC26X2_ALGO_PARAMS_0 0x20001fd8
|
||||
#define CC26X2_ALGO_PARAMS_1 0x20001fec
|
||||
#define CC26X2_WORKING_SIZE (CC26X2_ALGO_BUFFER_1 + CC26X2_SECTOR_LENGTH - \
|
||||
CC26XX_ALGO_BASE_ADDRESS)
|
||||
|
||||
/* CC26xx flash helper algorithm buffer flags */
|
||||
#define CC26XX_BUFFER_EMPTY 0x00000000
|
||||
#define CC26XX_BUFFER_FULL 0xffffffff
|
||||
|
||||
/* CC26XX flash helper algorithm commands */
|
||||
#define CC26XX_CMD_NO_ACTION 0
|
||||
#define CC26XX_CMD_ERASE_ALL 1
|
||||
#define CC26XX_CMD_PROGRAM 2
|
||||
#define CC26XX_CMD_ERASE_AND_PROGRAM 3
|
||||
#define CC26XX_CMD_ERASE_AND_PROGRAM_WITH_RETAIN 4
|
||||
#define CC26XX_CMD_ERASE_SECTORS 5
|
||||
|
||||
/* CC26xx and CC13xx device types */
|
||||
#define CC26XX_NO_TYPE 0 /* Device type not determined yet */
|
||||
#define CC26X0_TYPE 1 /* CC26x0 Chameleon device */
|
||||
#define CC26X1_TYPE 2 /* CC26x1 Chameleon device */
|
||||
#define CC26X2_TYPE 3 /* CC26x2 Agama device */
|
||||
#define CC13X0_TYPE 4 /* CC13x0 Chameleon device */
|
||||
#define CC13X2_TYPE 5 /* CC13x2 Agama device */
|
||||
|
||||
/* Flash helper algorithm parameter block struct */
|
||||
#define CC26XX_STATUS_OFFSET 0x0c
|
||||
struct cc26xx_algo_params {
|
||||
uint8_t address[4];
|
||||
uint8_t length[4];
|
||||
uint8_t command[4];
|
||||
uint8_t status[4];
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for CC26x0 Chameleon targets */
|
||||
const uint8_t cc26x0_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/cc26xx/cc26x0_algo.inc"
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for CC26x2 Agama targets */
|
||||
const uint8_t cc26x2_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/cc26xx/cc26x2_algo.inc"
|
||||
};
|
||||
|
||||
#endif /* OPENOCD_FLASH_NOR_CC26XX_H */
|
||||
@@ -0,0 +1,529 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "imp.h"
|
||||
#include "cc3220sf.h"
|
||||
#include <helper/time_support.h>
|
||||
#include <target/algorithm.h>
|
||||
#include <target/armv7m.h>
|
||||
|
||||
#define FLASH_TIMEOUT 5000
|
||||
|
||||
struct cc3220sf_bank {
|
||||
bool probed;
|
||||
struct armv7m_algorithm armv7m_info;
|
||||
};
|
||||
|
||||
static int cc3220sf_mass_erase(struct flash_bank *bank)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
bool done;
|
||||
long long start_ms;
|
||||
long long elapsed_ms;
|
||||
uint32_t value;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
/* Set starting address to erase to zero */
|
||||
retval = target_write_u32(target, FMA_REGISTER_ADDR, 0);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Write the MERASE bit of the FMC register */
|
||||
retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_MERASE_VALUE);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Poll the MERASE bit until the mass erase is complete */
|
||||
done = false;
|
||||
start_ms = timeval_ms();
|
||||
while (!done) {
|
||||
retval = target_read_u32(target, FMC_REGISTER_ADDR, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
if ((value & FMC_MERASE_BIT) == 0) {
|
||||
/* Bit clears when mass erase is finished */
|
||||
done = true;
|
||||
} else {
|
||||
elapsed_ms = timeval_ms() - start_ms;
|
||||
if (elapsed_ms > 500)
|
||||
keep_alive();
|
||||
if (elapsed_ms > FLASH_TIMEOUT)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
/* Mass erase timed out waiting for confirmation */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(cc3220sf_flash_bank_command)
|
||||
{
|
||||
struct cc3220sf_bank *cc3220sf_bank;
|
||||
|
||||
if (CMD_ARGC < 6)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
cc3220sf_bank = malloc(sizeof(struct cc3220sf_bank));
|
||||
if (NULL == cc3220sf_bank)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* Initialize private flash information */
|
||||
cc3220sf_bank->probed = false;
|
||||
|
||||
/* Finish initialization of flash bank */
|
||||
bank->driver_priv = cc3220sf_bank;
|
||||
bank->next = NULL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc3220sf_erase(struct flash_bank *bank, int first, int last)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
bool done;
|
||||
long long start_ms;
|
||||
long long elapsed_ms;
|
||||
uint32_t address;
|
||||
uint32_t value;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
/* Do a mass erase if user requested all sectors of flash */
|
||||
if ((first == 0) && (last == (bank->num_sectors - 1))) {
|
||||
/* Request mass erase of flash */
|
||||
return cc3220sf_mass_erase(bank);
|
||||
}
|
||||
|
||||
/* Erase requested sectors one by one */
|
||||
for (int i = first; i <= last; i++) {
|
||||
|
||||
/* Determine address of sector to erase */
|
||||
address = FLASH_BASE_ADDR + i * FLASH_SECTOR_SIZE;
|
||||
|
||||
/* Set starting address to erase */
|
||||
retval = target_write_u32(target, FMA_REGISTER_ADDR, address);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Write the ERASE bit of the FMC register */
|
||||
retval = target_write_u32(target, FMC_REGISTER_ADDR, FMC_ERASE_VALUE);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Poll the ERASE bit until the erase is complete */
|
||||
done = false;
|
||||
start_ms = timeval_ms();
|
||||
while (!done) {
|
||||
retval = target_read_u32(target, FMC_REGISTER_ADDR, &value);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
if ((value & FMC_ERASE_BIT) == 0) {
|
||||
/* Bit clears when mass erase is finished */
|
||||
done = true;
|
||||
} else {
|
||||
elapsed_ms = timeval_ms() - start_ms;
|
||||
if (elapsed_ms > 500)
|
||||
keep_alive();
|
||||
if (elapsed_ms > FLASH_TIMEOUT)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
/* Sector erase timed out waiting for confirmation */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc3220sf_protect(struct flash_bank *bank, int set, int first,
|
||||
int last)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc3220sf_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
uint32_t offset, uint32_t count)
|
||||
{
|
||||
struct target *target = bank->target;
|
||||
struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
|
||||
struct working_area *algo_working_area;
|
||||
struct working_area *buffer_working_area;
|
||||
struct reg_param reg_params[3];
|
||||
uint32_t algo_base_address;
|
||||
uint32_t algo_buffer_address;
|
||||
uint32_t algo_buffer_size;
|
||||
uint32_t address;
|
||||
uint32_t remaining;
|
||||
uint32_t words;
|
||||
uint32_t result;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (TARGET_HALTED != target->state) {
|
||||
LOG_ERROR("Target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
/* Obtain working area to use for flash helper algorithm */
|
||||
retval = target_alloc_working_area(target, sizeof(cc3220sf_algo),
|
||||
&algo_working_area);
|
||||
if (ERROR_OK != retval)
|
||||
return retval;
|
||||
|
||||
/* Obtain working area to use for flash buffer */
|
||||
retval = target_alloc_working_area(target,
|
||||
target_get_working_area_avail(target), &buffer_working_area);
|
||||
if (ERROR_OK != retval) {
|
||||
target_free_working_area(target, algo_working_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
algo_base_address = algo_working_area->address;
|
||||
algo_buffer_address = buffer_working_area->address;
|
||||
algo_buffer_size = buffer_working_area->size;
|
||||
|
||||
/* Make sure buffer size is a multiple of 32 word (0x80 byte) chunks */
|
||||
/* (algo runs more efficiently if it operates on 32 words at a time) */
|
||||
if (algo_buffer_size > 0x80)
|
||||
algo_buffer_size &= ~0x7f;
|
||||
|
||||
/* Write flash helper algorithm into target memory */
|
||||
retval = target_write_buffer(target, algo_base_address,
|
||||
sizeof(cc3220sf_algo), cc3220sf_algo);
|
||||
if (ERROR_OK != retval) {
|
||||
target_free_working_area(target, algo_working_area);
|
||||
target_free_working_area(target, buffer_working_area);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Initialize the ARMv7m specific info to run the algorithm */
|
||||
cc3220sf_bank->armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
|
||||
cc3220sf_bank->armv7m_info.core_mode = ARM_MODE_THREAD;
|
||||
|
||||
/* Initialize register params for flash helper algorithm */
|
||||
init_reg_param(®_params[0], "r0", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_OUT);
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_IN_OUT);
|
||||
|
||||
/* Prepare to write to flash */
|
||||
address = FLASH_BASE_ADDR + offset;
|
||||
remaining = count;
|
||||
|
||||
/* The flash hardware can only write complete words to flash. If
|
||||
* an unaligned address is passed in, we must do a read-modify-write
|
||||
* on a word with enough bytes to align the rest of the buffer. And
|
||||
* if less than a whole word remains at the end, we must also do a
|
||||
* read-modify-write on a final word to finish up.
|
||||
*/
|
||||
|
||||
/* Do one word write to align address on 32-bit boundary if needed */
|
||||
if (0 != (address & 0x3)) {
|
||||
uint8_t head[4];
|
||||
|
||||
/* Get starting offset for data to write (will be 1 to 3) */
|
||||
uint32_t head_offset = address & 0x03;
|
||||
|
||||
/* Get the aligned address to write this first word to */
|
||||
uint32_t head_address = address & 0xfffffffc;
|
||||
|
||||
/* Retrieve what is already in flash at the head address */
|
||||
retval = target_read_buffer(target, head_address, sizeof(head), head);
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Substitute in the new data to write */
|
||||
while ((remaining > 0) && (head_offset < 4)) {
|
||||
head[head_offset] = *buffer;
|
||||
head_offset++;
|
||||
address++;
|
||||
buffer++;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Helper parameters are passed in registers R0-R2 */
|
||||
/* Set start of data buffer, address to write to, and word count */
|
||||
buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
|
||||
buf_set_u32(reg_params[1].value, 0, 32, head_address);
|
||||
buf_set_u32(reg_params[2].value, 0, 32, 1);
|
||||
|
||||
/* Write head value into buffer to flash */
|
||||
retval = target_write_buffer(target, algo_buffer_address,
|
||||
sizeof(head), head);
|
||||
}
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Execute the flash helper algorithm */
|
||||
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
|
||||
algo_base_address, 0, FLASH_TIMEOUT,
|
||||
&cc3220sf_bank->armv7m_info);
|
||||
if (ERROR_OK != retval)
|
||||
LOG_ERROR("cc3220sf: Flash algorithm failed to run");
|
||||
|
||||
/* Check that the head value was written to flash */
|
||||
result = buf_get_u32(reg_params[2].value, 0, 32);
|
||||
if (0 != result) {
|
||||
retval = ERROR_FAIL;
|
||||
LOG_ERROR("cc3220sf: Flash operation failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if there's data at end of buffer that isn't a full word */
|
||||
uint32_t tail_count = remaining & 0x03;
|
||||
/* Adjust remaining so it is a multiple of whole words */
|
||||
remaining -= tail_count;
|
||||
|
||||
while ((ERROR_OK == retval) && (remaining > 0)) {
|
||||
/* Set start of data buffer and address to write to */
|
||||
buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
|
||||
buf_set_u32(reg_params[1].value, 0, 32, address);
|
||||
|
||||
/* Download data to write into memory buffer */
|
||||
if (remaining >= algo_buffer_size) {
|
||||
/* Fill up buffer with data to flash */
|
||||
retval = target_write_buffer(target, algo_buffer_address,
|
||||
algo_buffer_size, buffer);
|
||||
if (ERROR_OK != retval)
|
||||
break;
|
||||
|
||||
/* Count to write is in 32-bit words */
|
||||
words = algo_buffer_size / 4;
|
||||
|
||||
/* Bump variables to next data */
|
||||
address += algo_buffer_size;
|
||||
buffer += algo_buffer_size;
|
||||
remaining -= algo_buffer_size;
|
||||
} else {
|
||||
/* Fill buffer with what's left of the data */
|
||||
retval = target_write_buffer(target, algo_buffer_address,
|
||||
remaining, buffer);
|
||||
if (ERROR_OK != retval)
|
||||
break;
|
||||
|
||||
/* Calculate the final word count to write */
|
||||
words = remaining / 4;
|
||||
if (0 != (remaining % 4))
|
||||
words++;
|
||||
|
||||
/* Bump variables to any final data */
|
||||
address += remaining;
|
||||
buffer += remaining;
|
||||
remaining = 0;
|
||||
}
|
||||
|
||||
/* Set number of words to write */
|
||||
buf_set_u32(reg_params[2].value, 0, 32, words);
|
||||
|
||||
/* Execute the flash helper algorithm */
|
||||
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
|
||||
algo_base_address, 0, FLASH_TIMEOUT,
|
||||
&cc3220sf_bank->armv7m_info);
|
||||
if (ERROR_OK != retval) {
|
||||
LOG_ERROR("cc3220sf: Flash algorithm failed to run");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check that all words were written to flash */
|
||||
result = buf_get_u32(reg_params[2].value, 0, 32);
|
||||
if (0 != result) {
|
||||
retval = ERROR_FAIL;
|
||||
LOG_ERROR("cc3220sf: Flash operation failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do one word write for any final bytes less than a full word */
|
||||
if ((ERROR_OK == retval) && (0 != tail_count)) {
|
||||
uint8_t tail[4];
|
||||
|
||||
/* Set starting byte offset for data to write */
|
||||
uint32_t tail_offset = 0;
|
||||
|
||||
/* Retrieve what is already in flash at the tail address */
|
||||
retval = target_read_buffer(target, address, sizeof(tail), tail);
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Substitute in the new data to write */
|
||||
while (tail_count > 0) {
|
||||
tail[tail_offset] = *buffer;
|
||||
tail_offset++;
|
||||
buffer++;
|
||||
tail_count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Set start of data buffer, address to write to, and word count */
|
||||
buf_set_u32(reg_params[0].value, 0, 32, algo_buffer_address);
|
||||
buf_set_u32(reg_params[1].value, 0, 32, address);
|
||||
buf_set_u32(reg_params[2].value, 0, 32, 1);
|
||||
|
||||
/* Write tail value into buffer to flash */
|
||||
retval = target_write_buffer(target, algo_buffer_address,
|
||||
sizeof(tail), tail);
|
||||
}
|
||||
|
||||
if (ERROR_OK == retval) {
|
||||
/* Execute the flash helper algorithm */
|
||||
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
|
||||
algo_base_address, 0, FLASH_TIMEOUT,
|
||||
&cc3220sf_bank->armv7m_info);
|
||||
if (ERROR_OK != retval)
|
||||
LOG_ERROR("cc3220sf: Flash algorithm failed to run");
|
||||
|
||||
/* Check that the tail was written to flash */
|
||||
result = buf_get_u32(reg_params[2].value, 0, 32);
|
||||
if (0 != result) {
|
||||
retval = ERROR_FAIL;
|
||||
LOG_ERROR("cc3220sf: Flash operation failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Free resources */
|
||||
destroy_reg_param(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
target_free_working_area(target, algo_working_area);
|
||||
target_free_working_area(target, buffer_working_area);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc3220sf_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
|
||||
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
int num_sectors;
|
||||
int bank_id;
|
||||
|
||||
bank_id = bank->bank_number;
|
||||
|
||||
if (0 == bank_id) {
|
||||
base = FLASH_BASE_ADDR;
|
||||
size = FLASH_NUM_SECTORS * FLASH_SECTOR_SIZE;
|
||||
num_sectors = FLASH_NUM_SECTORS;
|
||||
} else {
|
||||
/* Invalid bank number somehow */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (NULL != bank->sectors) {
|
||||
free(bank->sectors);
|
||||
bank->sectors = NULL;
|
||||
}
|
||||
|
||||
bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
|
||||
if (NULL == bank->sectors)
|
||||
return ERROR_FAIL;
|
||||
|
||||
bank->base = base;
|
||||
bank->size = size;
|
||||
bank->write_start_alignment = 0;
|
||||
bank->write_end_alignment = 0;
|
||||
bank->num_sectors = num_sectors;
|
||||
|
||||
for (int i = 0; i < num_sectors; i++) {
|
||||
bank->sectors[i].offset = i * FLASH_SECTOR_SIZE;
|
||||
bank->sectors[i].size = FLASH_SECTOR_SIZE;
|
||||
bank->sectors[i].is_erased = -1;
|
||||
bank->sectors[i].is_protected = 0;
|
||||
}
|
||||
|
||||
/* We've successfully recorded the stats on this flash bank */
|
||||
cc3220sf_bank->probed = true;
|
||||
|
||||
/* If we fall through to here, then all went well */
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc3220sf_auto_probe(struct flash_bank *bank)
|
||||
{
|
||||
struct cc3220sf_bank *cc3220sf_bank = bank->driver_priv;
|
||||
|
||||
int retval = ERROR_OK;
|
||||
|
||||
if (0 != bank->bank_number) {
|
||||
/* Invalid bank number somehow */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!cc3220sf_bank->probed)
|
||||
retval = cc3220sf_probe(bank);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int cc3220sf_protect_check(struct flash_bank *bank)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cc3220sf_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||
{
|
||||
int printed;
|
||||
|
||||
printed = snprintf(buf, buf_size, "CC3220SF with 1MB internal flash\n");
|
||||
|
||||
if (printed >= buf_size)
|
||||
return ERROR_BUF_TOO_SMALL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
struct flash_driver cc3220sf_flash = {
|
||||
.name = "cc3220sf",
|
||||
.flash_bank_command = cc3220sf_flash_bank_command,
|
||||
.erase = cc3220sf_erase,
|
||||
.protect = cc3220sf_protect,
|
||||
.write = cc3220sf_write,
|
||||
.read = default_flash_read,
|
||||
.probe = cc3220sf_probe,
|
||||
.auto_probe = cc3220sf_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = cc3220sf_protect_check,
|
||||
.info = cc3220sf_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv,
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_FLASH_NOR_CC3220SF_H
|
||||
#define OPENOCD_FLASH_NOR_CC3220SF_H
|
||||
|
||||
/* CC3220SF device types */
|
||||
#define CC3220_NO_TYPE 0 /* Device type not determined yet */
|
||||
#define CC3220_OTHER 1 /* CC3220 variant without flash */
|
||||
#define CC3220SF 2 /* CC3220SF variant with flash */
|
||||
|
||||
/* Flash parameters */
|
||||
#define FLASH_BASE_ADDR 0x01000000
|
||||
#define FLASH_SECTOR_SIZE 2048
|
||||
#define FLASH_NUM_SECTORS 512
|
||||
|
||||
/* CC2200SF flash registers */
|
||||
#define FMA_REGISTER_ADDR 0x400FD000
|
||||
#define FMC_REGISTER_ADDR 0x400FD008
|
||||
#define FMC_DEFAULT_VALUE 0xA4420000
|
||||
#define FMC_ERASE_BIT 0x00000002
|
||||
#define FMC_MERASE_BIT 0x00000004
|
||||
#define FMC_ERASE_VALUE (FMC_DEFAULT_VALUE | FMC_ERASE_BIT)
|
||||
#define FMC_MERASE_VALUE (FMC_DEFAULT_VALUE | FMC_MERASE_BIT)
|
||||
|
||||
/* Flash helper algorithm for CC3220SF */
|
||||
const uint8_t cc3220sf_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/cc3220sf/cc3220sf.inc"
|
||||
};
|
||||
|
||||
#endif /* OPENOCD_FLASH_NOR_CC3220SF_H */
|
||||
+10
-2
@@ -188,9 +188,17 @@ void flash_free_all_banks(void)
|
||||
else
|
||||
LOG_WARNING("Flash driver of %s does not support free_driver_priv()", bank->name);
|
||||
|
||||
/* For 'virtual' flash driver bank->sectors and bank->prot_blocks pointers are copied from
|
||||
* master flash_bank structure. They point to memory locations allocated by master flash driver
|
||||
* so master driver is responsible for releasing them.
|
||||
* Avoid UB caused by double-free memory corruption if flash bank is 'virtual'. */
|
||||
|
||||
if (strcmp(bank->driver->name, "virtual") != 0) {
|
||||
free(bank->sectors);
|
||||
free(bank->prot_blocks);
|
||||
}
|
||||
|
||||
free(bank->name);
|
||||
free(bank->sectors);
|
||||
free(bank->prot_blocks);
|
||||
free(bank);
|
||||
bank = next;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ extern struct flash_driver ath79_flash;
|
||||
extern struct flash_driver atsamv_flash;
|
||||
extern struct flash_driver avr_flash;
|
||||
extern struct flash_driver bluenrgx_flash;
|
||||
extern struct flash_driver cc3220sf_flash;
|
||||
extern struct flash_driver cc26xx_flash;
|
||||
extern struct flash_driver cfi_flash;
|
||||
extern struct flash_driver dsp5680xx_flash;
|
||||
extern struct flash_driver efm32_flash;
|
||||
@@ -49,6 +51,7 @@ extern struct flash_driver lpc2900_flash;
|
||||
extern struct flash_driver lpcspifi_flash;
|
||||
extern struct flash_driver mdr_flash;
|
||||
extern struct flash_driver mrvlqspi_flash;
|
||||
extern struct flash_driver msp432_flash;
|
||||
extern struct flash_driver niietcm4_flash;
|
||||
extern struct flash_driver nrf5_flash;
|
||||
extern struct flash_driver nrf51_flash;
|
||||
@@ -56,6 +59,9 @@ extern struct flash_driver numicro_flash;
|
||||
extern struct flash_driver ocl_flash;
|
||||
extern struct flash_driver pic32mx_flash;
|
||||
extern struct flash_driver psoc4_flash;
|
||||
extern struct flash_driver psoc5lp_flash;
|
||||
extern struct flash_driver psoc5lp_eeprom_flash;
|
||||
extern struct flash_driver psoc5lp_nvl_flash;
|
||||
extern struct flash_driver psoc6_flash;
|
||||
extern struct flash_driver sim3x_flash;
|
||||
extern struct flash_driver stellaris_flash;
|
||||
@@ -91,6 +97,8 @@ static struct flash_driver *flash_drivers[] = {
|
||||
&atsamv_flash,
|
||||
&avr_flash,
|
||||
&bluenrgx_flash,
|
||||
&cc3220sf_flash,
|
||||
&cc26xx_flash,
|
||||
&cfi_flash,
|
||||
&dsp5680xx_flash,
|
||||
&efm32_flash,
|
||||
@@ -108,6 +116,7 @@ static struct flash_driver *flash_drivers[] = {
|
||||
&lpcspifi_flash,
|
||||
&mdr_flash,
|
||||
&mrvlqspi_flash,
|
||||
&msp432_flash,
|
||||
&niietcm4_flash,
|
||||
&nrf5_flash,
|
||||
&nrf51_flash,
|
||||
@@ -115,6 +124,9 @@ static struct flash_driver *flash_drivers[] = {
|
||||
&ocl_flash,
|
||||
&pic32mx_flash,
|
||||
&psoc4_flash,
|
||||
&psoc5lp_flash,
|
||||
&psoc5lp_eeprom_flash,
|
||||
&psoc5lp_nvl_flash,
|
||||
&psoc6_flash,
|
||||
&sim3x_flash,
|
||||
&stellaris_flash,
|
||||
|
||||
@@ -1179,5 +1179,6 @@ struct flash_driver fespi_flash = {
|
||||
.auto_probe = fespi_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = fespi_protect_check,
|
||||
.info = get_fespi_info
|
||||
.info = get_fespi_info,
|
||||
.free_driver_priv = default_flash_free_driver_priv
|
||||
};
|
||||
|
||||
@@ -937,7 +937,7 @@ static int kinetis_create_missing_banks(struct kinetis_chip *k_chip)
|
||||
unsigned num_blocks;
|
||||
struct kinetis_flash_bank *k_bank;
|
||||
struct flash_bank *bank;
|
||||
char base_name[80], name[80], num[4];
|
||||
char base_name[69], name[80], num[4];
|
||||
char *class, *p;
|
||||
|
||||
num_blocks = k_chip->num_pflash_blocks + k_chip->num_nvm_blocks;
|
||||
@@ -948,7 +948,8 @@ static int kinetis_create_missing_banks(struct kinetis_chip *k_chip)
|
||||
|
||||
bank = k_chip->banks[0].bank;
|
||||
if (bank && bank->name) {
|
||||
strncpy(base_name, bank->name, sizeof(base_name));
|
||||
strncpy(base_name, bank->name, sizeof(base_name) - 1);
|
||||
base_name[sizeof(base_name) - 1] = '\0';
|
||||
p = strstr(base_name, ".pflash");
|
||||
if (p) {
|
||||
*p = '\0';
|
||||
@@ -960,7 +961,8 @@ static int kinetis_create_missing_banks(struct kinetis_chip *k_chip)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strncpy(base_name, target_name(k_chip->target), sizeof(base_name));
|
||||
strncpy(base_name, target_name(k_chip->target), sizeof(base_name) - 1);
|
||||
base_name[sizeof(base_name) - 1] = '\0';
|
||||
p = strstr(base_name, ".cpu");
|
||||
if (p)
|
||||
*p = '\0';
|
||||
@@ -2012,7 +2014,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
|
||||
unsigned cpu_mhz = 120;
|
||||
unsigned idx;
|
||||
bool use_nvm_marking = false;
|
||||
char flash_marking[11], nvm_marking[2];
|
||||
char flash_marking[12], nvm_marking[2];
|
||||
char name[40];
|
||||
|
||||
k_chip->probed = false;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,127 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Texas Instruments, Inc. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_FLASH_NOR_MSP432_H
|
||||
#define OPENOCD_FLASH_NOR_MSP432_H
|
||||
|
||||
/* MSP432 family types */
|
||||
#define MSP432_NO_FAMILY 0 /* Family type not determined yet */
|
||||
#define MSP432E4 1 /* MSP432E4 family of devices */
|
||||
#define MSP432P4 2 /* MSP432P4 family of devices */
|
||||
|
||||
/* MSP432 device types */
|
||||
#define MSP432_NO_TYPE 0 /* Device type not determined yet */
|
||||
#define MSP432P401X_DEPR 1 /* Early MSP432P401x offerings, now deprecated */
|
||||
#define MSP432P401X 2 /* MSP432P401x device, revision C or higher */
|
||||
#define MSP432P411X 3 /* MSP432P411x device, revision A or higher */
|
||||
#define MSP432P401X_GUESS 4 /* Assuming it's an MSP432P401x device */
|
||||
#define MSP432P411X_GUESS 5 /* Assuming it's an MSP432P411x device */
|
||||
#define MSP432E401Y 6 /* MSP432E401Y device */
|
||||
#define MSP432E411Y 7 /* MSP432E401Y device */
|
||||
#define MSP432E4X_GUESS 8 /* Assuming it's an MSP432E4x device */
|
||||
|
||||
/* MSP432P4 flash parameters */
|
||||
#define P4_FLASH_MAIN_BASE 0x00000000
|
||||
#define P4_FLASH_INFO_BASE 0x00200000
|
||||
#define P4_SECTOR_LENGTH 0x1000
|
||||
#define P4_ALGO_ENTRY_ADDR 0x01000110
|
||||
|
||||
/* MSP432E4 flash paramters */
|
||||
#define E4_FLASH_BASE 0x00000000
|
||||
#define E4_FLASH_SIZE 0x100000
|
||||
#define E4_SECTOR_LENGTH 0x4000
|
||||
#define E4_ALGO_ENTRY_ADDR 0x20000110
|
||||
|
||||
/* Flash helper algorithm key addresses */
|
||||
#define ALGO_BASE_ADDR 0x20000000
|
||||
#define ALGO_BUFFER1_ADDR 0x20002000
|
||||
#define ALGO_BUFFER2_ADDR 0x20003000
|
||||
#define ALGO_PARAMS_BASE_ADDR 0x20000150
|
||||
#define ALGO_FLASH_COMMAND_ADDR 0x20000150
|
||||
#define ALGO_RETURN_CODE_ADDR 0x20000154
|
||||
#define ALGO_FLASH_DEST_ADDR 0x2000015c
|
||||
#define ALGO_FLASH_LENGTH_ADDR 0x20000160
|
||||
#define ALGO_BUFFER1_STATUS_ADDR 0x20000164
|
||||
#define ALGO_BUFFER2_STATUS_ADDR 0x20000168
|
||||
#define ALGO_ERASE_PARAM_ADDR 0x2000016c
|
||||
#define ALGO_UNLOCK_BSL_ADDR 0x20000170
|
||||
#define ALGO_STACK_POINTER_ADDR 0x20002000
|
||||
|
||||
/* Flash helper algorithm key sizes */
|
||||
#define ALGO_BUFFER_SIZE 0x1000
|
||||
#define ALGO_WORKING_SIZE (ALGO_BUFFER2_ADDR + 0x1000 - ALGO_BASE_ADDR)
|
||||
|
||||
/* Flash helper algorithm flash commands */
|
||||
#define FLASH_NO_COMMAND 0
|
||||
#define FLASH_MASS_ERASE 1
|
||||
#define FLASH_SECTOR_ERASE 2
|
||||
#define FLASH_PROGRAM 4
|
||||
#define FLASH_INIT 8
|
||||
#define FLASH_EXIT 16
|
||||
#define FLASH_CONTINUOUS 32
|
||||
|
||||
/* Flash helper algorithm return codes */
|
||||
#define FLASH_BUSY 0x00000001
|
||||
#define FLASH_SUCCESS 0x00000ACE
|
||||
#define FLASH_ERROR 0x0000DEAD
|
||||
#define FLASH_TIMEOUT_ERROR 0xDEAD0000
|
||||
#define FLASH_VERIFY_ERROR 0xDEADDEAD
|
||||
#define FLASH_WRONG_COMMAND 0x00000BAD
|
||||
#define FLASH_POWER_ERROR 0x00DEAD00
|
||||
|
||||
/* Flash helper algorithm buffer status values */
|
||||
#define BUFFER_INACTIVE 0x00
|
||||
#define BUFFER_ACTIVE 0x01
|
||||
#define BUFFER_DATA_READY 0x10
|
||||
|
||||
/* Flash helper algorithm erase parameters */
|
||||
#define FLASH_ERASE_MAIN 0x01
|
||||
#define FLASH_ERASE_INFO 0x02
|
||||
|
||||
/* Flash helper algorithm lock/unlock BSL options */
|
||||
#define FLASH_LOCK_BSL 0x00
|
||||
#define FLASH_UNLOCK_BSL 0x0b
|
||||
|
||||
/* Flash helper algorithm parameter block struct */
|
||||
struct msp432_algo_params {
|
||||
uint8_t flash_command[4];
|
||||
uint8_t return_code[4];
|
||||
uint8_t _reserved0[4];
|
||||
uint8_t address[4];
|
||||
uint8_t length[4];
|
||||
uint8_t buffer1_status[4];
|
||||
uint8_t buffer2_status[4];
|
||||
uint8_t erase_param[4];
|
||||
uint8_t unlock_bsl[4];
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for MSP432P401x targets */
|
||||
const uint8_t msp432p401x_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/msp432/msp432p401x_algo.inc"
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for MSP432P411x targets */
|
||||
const uint8_t msp432p411x_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/msp432/msp432p411x_algo.inc"
|
||||
};
|
||||
|
||||
/* Flash helper algorithm for MSP432E4x targets */
|
||||
const uint8_t msp432e4x_algo[] = {
|
||||
#include "../../../contrib/loaders/flash/msp432/msp432e4x_algo.inc"
|
||||
};
|
||||
|
||||
#endif /* OPENOCD_FLASH_NOR_MSP432_H */
|
||||
+22
-53
@@ -108,6 +108,7 @@ enum nrf5_nvmc_config_bits {
|
||||
|
||||
struct nrf5_info {
|
||||
uint32_t code_page_size;
|
||||
uint32_t refcount;
|
||||
|
||||
struct {
|
||||
bool probed;
|
||||
@@ -204,6 +205,7 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = {
|
||||
|
||||
/* nRF52832 Devices */
|
||||
NRF5_DEVICE_DEF(0x00C7, "52832", "QFAA", "B0", 512),
|
||||
NRF5_DEVICE_DEF(0x0139, "52832", "QFAA", "E0", 512),
|
||||
};
|
||||
|
||||
static int nrf5_bank_is_probed(struct flash_bank *bank)
|
||||
@@ -531,7 +533,6 @@ static int nrf5_probe(struct flash_bank *bank)
|
||||
bank->sectors[0].size = bank->size;
|
||||
bank->sectors[0].offset = 0;
|
||||
|
||||
/* mark as unknown */
|
||||
bank->sectors[0].is_erased = 0;
|
||||
bank->sectors[0].is_protected = 0;
|
||||
|
||||
@@ -553,17 +554,6 @@ static int nrf5_auto_probe(struct flash_bank *bank)
|
||||
return nrf5_probe(bank);
|
||||
}
|
||||
|
||||
static struct flash_sector *nrf5_find_sector_by_address(struct flash_bank *bank, uint32_t address)
|
||||
{
|
||||
struct nrf5_info *chip = bank->driver_priv;
|
||||
|
||||
for (int i = 0; i < bank->num_sectors; i++)
|
||||
if (bank->sectors[i].offset <= address &&
|
||||
address < (bank->sectors[i].offset + chip->code_page_size))
|
||||
return &bank->sectors[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int nrf5_erase_all(struct nrf5_info *chip)
|
||||
{
|
||||
LOG_DEBUG("Erasing all non-volatile memory");
|
||||
@@ -615,9 +605,6 @@ static int nrf5_erase_page(struct flash_bank *bank,
|
||||
sector->offset);
|
||||
}
|
||||
|
||||
if (res == ERROR_OK)
|
||||
sector->is_erased = 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -743,48 +730,22 @@ static int nrf5_write_pages(struct flash_bank *bank, uint32_t start, uint32_t en
|
||||
{
|
||||
int res = ERROR_FAIL;
|
||||
struct nrf5_info *chip = bank->driver_priv;
|
||||
struct flash_sector *sector;
|
||||
uint32_t offset;
|
||||
|
||||
assert(start % chip->code_page_size == 0);
|
||||
assert(end % chip->code_page_size == 0);
|
||||
|
||||
/* Erase all sectors */
|
||||
for (offset = start; offset < end; offset += chip->code_page_size) {
|
||||
sector = nrf5_find_sector_by_address(bank, offset);
|
||||
if (!sector) {
|
||||
LOG_ERROR("Invalid sector @ 0x%08"PRIx32, offset);
|
||||
return ERROR_FLASH_SECTOR_INVALID;
|
||||
}
|
||||
|
||||
if (sector->is_protected) {
|
||||
LOG_ERROR("Can't erase protected sector @ 0x%08"PRIx32, offset);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sector->is_erased != 1) { /* 1 = erased, 0= not erased, -1 = unknown */
|
||||
res = nrf5_erase_page(bank, chip, sector);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to erase sector @ 0x%08"PRIx32, sector->offset);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
sector->is_erased = 0;
|
||||
}
|
||||
|
||||
res = nrf5_nvmc_write_enable(chip);
|
||||
if (res != ERROR_OK)
|
||||
goto error;
|
||||
|
||||
res = nrf5_ll_flash_write(chip, start, buffer, (end - start));
|
||||
if (res != ERROR_OK)
|
||||
goto set_read_only;
|
||||
goto error;
|
||||
|
||||
return nrf5_nvmc_read_only(chip);
|
||||
|
||||
set_read_only:
|
||||
nrf5_nvmc_read_only(chip);
|
||||
error:
|
||||
nrf5_nvmc_read_only(chip);
|
||||
LOG_ERROR("Failed to write to nrf5 flash");
|
||||
return res;
|
||||
}
|
||||
@@ -876,11 +837,9 @@ static int nrf5_uicr_flash_write(struct flash_bank *bank,
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
if (sector->is_erased != 1) {
|
||||
res = nrf5_erase_page(bank, chip, sector);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
res = nrf5_erase_page(bank, chip, sector);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
res = nrf5_nvmc_write_enable(chip);
|
||||
if (res != ERROR_OK)
|
||||
@@ -911,6 +870,18 @@ static int nrf5_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||
return chip->bank[bank->bank_number].write(bank, chip, buffer, offset, count);
|
||||
}
|
||||
|
||||
static void nrf5_free_driver_priv(struct flash_bank *bank)
|
||||
{
|
||||
struct nrf5_info *chip = bank->driver_priv;
|
||||
if (chip == NULL)
|
||||
return;
|
||||
|
||||
chip->refcount--;
|
||||
if (chip->refcount == 0) {
|
||||
free(chip);
|
||||
bank->driver_priv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command)
|
||||
{
|
||||
@@ -946,6 +917,7 @@ FLASH_BANK_COMMAND_HANDLER(nrf5_flash_bank_command)
|
||||
break;
|
||||
}
|
||||
|
||||
chip->refcount++;
|
||||
chip->bank[bank->bank_number].probed = false;
|
||||
bank->driver_priv = chip;
|
||||
|
||||
@@ -992,9 +964,6 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
|
||||
return res;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bank->num_sectors; i++)
|
||||
bank->sectors[i].is_erased = 1;
|
||||
|
||||
res = nrf5_protect_check(bank);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to check chip's write protection");
|
||||
@@ -1005,8 +974,6 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
|
||||
bank->sectors[0].is_erased = 1;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -1175,6 +1142,7 @@ struct flash_driver nrf5_flash = {
|
||||
.auto_probe = nrf5_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = nrf5_protect_check,
|
||||
.free_driver_priv = nrf5_free_driver_priv,
|
||||
};
|
||||
|
||||
/* We need to retain the flash-driver name as well as the commands
|
||||
@@ -1192,4 +1160,5 @@ struct flash_driver nrf51_flash = {
|
||||
.auto_probe = nrf5_auto_probe,
|
||||
.erase_check = default_flash_blank_check,
|
||||
.protect_check = nrf5_protect_check,
|
||||
.free_driver_priv = nrf5_free_driver_priv,
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,7 @@ const struct flash_device flash_devices[] = {
|
||||
FLASH_ID("sp s25fl164k", 0xd8, 0xc7, 0x00174001, 0x100, 0x10000, 0x800000),
|
||||
FLASH_ID("sp s25fl128", 0xd8, 0xc7, 0x00182001, 0x100, 0x10000, 0x1000000),
|
||||
FLASH_ID("sp s25fl256", 0xd8, 0xc7, 0x00190201, 0x100, 0x10000, 0x2000000),
|
||||
FLASH_ID("cy s25fl256", 0xd8, 0xc7, 0x00196001, 0x100, 0x10000, 0x2000000),
|
||||
FLASH_ID("atmel 25f512", 0x52, 0xc7, 0x0065001f, 0x80, 0x8000, 0x10000),
|
||||
FLASH_ID("atmel 25f1024", 0x52, 0x62, 0x0060001f, 0x100, 0x8000, 0x20000),
|
||||
FLASH_ID("atmel 25f2048", 0x52, 0x62, 0x0063001f, 0x100, 0x10000, 0x40000),
|
||||
|
||||
@@ -585,8 +585,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
|
||||
retval = target_write_buffer(target, write_algorithm->address,
|
||||
sizeof(stm32x_flash_write_code), stm32x_flash_write_code);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
target_free_working_area(target, write_algorithm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* memory buffer */
|
||||
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
|
||||
|
||||
@@ -252,6 +252,8 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
|
||||
|
||||
/* Clear but report errors */
|
||||
if (status & FLASH_ERROR) {
|
||||
if (retval == ERROR_OK)
|
||||
retval = ERROR_FAIL;
|
||||
/* If this operation fails, we ignore it and report the original
|
||||
* retval
|
||||
*/
|
||||
@@ -597,8 +599,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
retval = target_write_buffer(target, write_algorithm->address,
|
||||
sizeof(stm32x_flash_write_code),
|
||||
stm32x_flash_write_code);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
target_free_working_area(target, write_algorithm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* memory buffer */
|
||||
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
#define FLASH_WRPERR (1 << 17) /* Write protection error */
|
||||
#define FLASH_PGSERR (1 << 18) /* Programming sequence error */
|
||||
#define FLASH_STRBERR (1 << 19) /* Strobe error */
|
||||
#define FLASH_INCERR (1 << 21) /* Increment error */
|
||||
#define FLASH_INCERR (1 << 21) /* Inconsistency error */
|
||||
#define FLASH_OPERR (1 << 22) /* Operation error */
|
||||
#define FLASH_RDPERR (1 << 23) /* Read Protection error */
|
||||
#define FLASH_RDSERR (1 << 24) /* Secure Protection error */
|
||||
@@ -220,6 +220,8 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
|
||||
|
||||
/* Clear error + EOP flags but report errors */
|
||||
if (status & FLASH_ERROR) {
|
||||
if (retval == ERROR_OK)
|
||||
retval = ERROR_FAIL;
|
||||
/* If this operation fails, we ignore it and report the original retval */
|
||||
target_write_u32(target, stm32x_get_flash_reg(bank, FLASH_CCR), status);
|
||||
}
|
||||
@@ -495,7 +497,7 @@ static int stm32x_erase(struct flash_bank *bank, int first, int last)
|
||||
retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("erase time-out error sector %d", i);
|
||||
LOG_ERROR("erase time-out or operation error sector %d", i);
|
||||
return retval;
|
||||
}
|
||||
bank->sectors[i].is_erased = 1;
|
||||
@@ -581,8 +583,10 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
retval = target_write_buffer(target, write_algorithm->address,
|
||||
sizeof(stm32x_flash_write_code),
|
||||
stm32x_flash_write_code);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
target_free_working_area(target, write_algorithm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* memory buffer */
|
||||
while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
|
||||
|
||||
@@ -187,6 +187,8 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout)
|
||||
|
||||
/* Clear but report errors */
|
||||
if (status & FLASH_ERROR) {
|
||||
if (retval == ERROR_OK)
|
||||
retval = ERROR_FAIL;
|
||||
/* If this operation fails, we ignore it and report the original
|
||||
* retval
|
||||
*/
|
||||
@@ -474,8 +476,10 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
|
||||
retval = target_write_buffer(target, write_algorithm->address,
|
||||
sizeof(stm32l4_flash_write_code),
|
||||
stm32l4_flash_write_code);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
target_free_working_area(target, write_algorithm);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* memory buffer */
|
||||
while (target_alloc_working_area_try(target, buffer_size, &source) !=
|
||||
|
||||
@@ -146,7 +146,7 @@ static const struct stm32lx_rev stm32_425_revs[] = {
|
||||
{ 0x1000, "A" }, { 0x2000, "B" }, { 0x2008, "Y" },
|
||||
};
|
||||
static const struct stm32lx_rev stm32_427_revs[] = {
|
||||
{ 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" },
|
||||
{ 0x1000, "A" }, { 0x1018, "Y" }, { 0x1038, "X" }, { 0x10f8, "V" },
|
||||
};
|
||||
static const struct stm32lx_rev stm32_429_revs[] = {
|
||||
{ 0x1000, "A" }, { 0x1018, "Z" },
|
||||
|
||||
+31
-27
@@ -288,24 +288,6 @@ COMMAND_HANDLER(handle_flash_erase_address_command)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int flash_check_sector_parameters(struct command_context *cmd_ctx,
|
||||
uint32_t first, uint32_t last, uint32_t num_sectors)
|
||||
{
|
||||
if (!(first <= last)) {
|
||||
command_print(cmd_ctx, "ERROR: "
|
||||
"first sector must be <= last sector");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!(last <= (num_sectors - 1))) {
|
||||
command_print(cmd_ctx, "ERROR: last sector must be <= %" PRIu32,
|
||||
num_sectors - 1);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_flash_erase_command)
|
||||
{
|
||||
if (CMD_ARGC != 3)
|
||||
@@ -327,9 +309,18 @@ COMMAND_HANDLER(handle_flash_erase_command)
|
||||
else
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], last);
|
||||
|
||||
retval = flash_check_sector_parameters(CMD_CTX, first, last, p->num_sectors);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
if (!(first <= last)) {
|
||||
command_print(CMD_CTX, "ERROR: "
|
||||
"first sector must be <= last");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!(last <= (uint32_t)(p->num_sectors - 1))) {
|
||||
command_print(CMD_CTX, "ERROR: "
|
||||
"last sector must be <= %" PRIu32,
|
||||
p->num_sectors - 1);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct duration bench;
|
||||
duration_start(&bench);
|
||||
@@ -375,15 +366,28 @@ COMMAND_HANDLER(handle_flash_protect_command)
|
||||
bool set;
|
||||
COMMAND_PARSE_ON_OFF(CMD_ARGV[3], set);
|
||||
|
||||
retval = flash_check_sector_parameters(CMD_CTX, first, last, num_blocks);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
if (!(first <= last)) {
|
||||
command_print(CMD_CTX, "ERROR: "
|
||||
"first %s must be <= last",
|
||||
(p->num_prot_blocks) ? "block" : "sector");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!(last <= (uint32_t)(num_blocks - 1))) {
|
||||
command_print(CMD_CTX, "ERROR: "
|
||||
"last %s must be <= %" PRIu32,
|
||||
(p->num_prot_blocks) ? "block" : "sector",
|
||||
num_blocks - 1);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
retval = flash_driver_protect(p, set, first, last);
|
||||
if (retval == ERROR_OK) {
|
||||
command_print(CMD_CTX, "%s protection for sectors %" PRIu32
|
||||
command_print(CMD_CTX, "%s protection for %s %" PRIu32
|
||||
" through %" PRIu32 " on flash bank %d",
|
||||
(set) ? "set" : "cleared", first, last, p->bank_number);
|
||||
(set) ? "set" : "cleared",
|
||||
(p->num_prot_blocks) ? "blocks" : "sectors",
|
||||
first, last, p->bank_number);
|
||||
}
|
||||
|
||||
return retval;
|
||||
@@ -502,7 +506,7 @@ COMMAND_HANDLER(handle_flash_fill_command)
|
||||
if (count == 0)
|
||||
return ERROR_OK;
|
||||
|
||||
if (address + count >= bank->base + bank->size) {
|
||||
if (address + count * wordsize > bank->base + bank->size) {
|
||||
LOG_ERROR("Cannot cross flash bank borders");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
+9
-21
@@ -151,6 +151,7 @@ static int tms470_read_part_info(struct flash_bank *bank)
|
||||
if (bank->sectors) {
|
||||
free(bank->sectors);
|
||||
bank->sectors = NULL;
|
||||
bank->num_sectors = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -754,9 +755,6 @@ static int tms470_erase_sector(struct flash_bank *bank, int sector)
|
||||
target_write_u32(target, 0xFFFFFFDC, glbctrl);
|
||||
LOG_DEBUG("set glbctrl = 0x%08" PRIx32 "", glbctrl);
|
||||
|
||||
if (result == ERROR_OK)
|
||||
bank->sectors[sector].is_erased = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1044,27 +1042,17 @@ static int tms470_erase_check(struct flash_bank *bank)
|
||||
* an attempt to reduce the JTAG overhead.
|
||||
*/
|
||||
for (sector = 0; sector < bank->num_sectors; sector++) {
|
||||
if (bank->sectors[sector].is_erased != 1) {
|
||||
uint32_t i, addr = bank->base + bank->sectors[sector].offset;
|
||||
uint32_t i, addr = bank->base + bank->sectors[sector].offset;
|
||||
|
||||
LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector);
|
||||
LOG_INFO("checking flash bank %d sector %d", tms470_info->ordinal, sector);
|
||||
|
||||
target_read_buffer(target, addr, bank->sectors[sector].size, buffer);
|
||||
target_read_buffer(target, addr, bank->sectors[sector].size, buffer);
|
||||
|
||||
bank->sectors[sector].is_erased = 1;
|
||||
for (i = 0; i < bank->sectors[sector].size; i++) {
|
||||
if (buffer[i] != 0xff) {
|
||||
LOG_WARNING("tms470 bank %d, sector %d, not erased.",
|
||||
tms470_info->ordinal,
|
||||
sector);
|
||||
LOG_WARNING(
|
||||
"at location 0x%08" PRIx32 ": flash data is 0x%02x.",
|
||||
addr + i,
|
||||
buffer[i]);
|
||||
|
||||
bank->sectors[sector].is_erased = 0;
|
||||
break;
|
||||
}
|
||||
bank->sectors[sector].is_erased = 1;
|
||||
for (i = 0; i < bank->sectors[sector].size; i++) {
|
||||
if (buffer[i] != 0xff) {
|
||||
bank->sectors[sector].is_erased = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bank->sectors[sector].is_erased != 1) {
|
||||
|
||||
@@ -46,8 +46,13 @@ static void virtual_update_bank_info(struct flash_bank *bank)
|
||||
bank->bus_width = master_bank->bus_width;
|
||||
bank->erased_value = master_bank->erased_value;
|
||||
bank->default_padded_value = master_bank->default_padded_value;
|
||||
bank->write_start_alignment = master_bank->write_start_alignment;
|
||||
bank->write_end_alignment = master_bank->write_end_alignment;
|
||||
bank->minimal_write_gap = master_bank->minimal_write_gap;
|
||||
bank->num_sectors = master_bank->num_sectors;
|
||||
bank->sectors = master_bank->sectors;
|
||||
bank->num_prot_blocks = master_bank->num_prot_blocks;
|
||||
bank->prot_blocks = master_bank->prot_blocks;
|
||||
}
|
||||
|
||||
FLASH_BANK_COMMAND_HANDLER(virtual_flash_bank_command)
|
||||
|
||||
+27
-25
@@ -2289,37 +2289,39 @@ get_delay:
|
||||
|
||||
static int aice_usb_set_clock(int set_clock)
|
||||
{
|
||||
if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL,
|
||||
AICE_TCK_CONTROL_TCK_SCAN) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
if (set_clock & AICE_TCK_CONTROL_TCK_SCAN) {
|
||||
if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL,
|
||||
AICE_TCK_CONTROL_TCK_SCAN) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* Read out TCK_SCAN clock value */
|
||||
uint32_t scan_clock;
|
||||
if (aice_read_ctrl(AICE_READ_CTRL_GET_ICE_STATE, &scan_clock) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
/* Read out TCK_SCAN clock value */
|
||||
uint32_t scan_clock;
|
||||
if (aice_read_ctrl(AICE_READ_CTRL_GET_ICE_STATE, &scan_clock) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
scan_clock &= 0x0F;
|
||||
scan_clock &= 0x0F;
|
||||
|
||||
uint32_t scan_base_freq;
|
||||
if (scan_clock & 0x8)
|
||||
scan_base_freq = 48000; /* 48 MHz */
|
||||
else
|
||||
scan_base_freq = 30000; /* 30 MHz */
|
||||
uint32_t scan_base_freq;
|
||||
if (scan_clock & 0x8)
|
||||
scan_base_freq = 48000; /* 48 MHz */
|
||||
else
|
||||
scan_base_freq = 30000; /* 30 MHz */
|
||||
|
||||
uint32_t set_base_freq;
|
||||
if (set_clock & 0x8)
|
||||
set_base_freq = 48000;
|
||||
else
|
||||
set_base_freq = 30000;
|
||||
uint32_t set_base_freq;
|
||||
if (set_clock & 0x8)
|
||||
set_base_freq = 48000;
|
||||
else
|
||||
set_base_freq = 30000;
|
||||
|
||||
uint32_t set_freq;
|
||||
uint32_t scan_freq;
|
||||
set_freq = set_base_freq >> (set_clock & 0x7);
|
||||
scan_freq = scan_base_freq >> (scan_clock & 0x7);
|
||||
uint32_t set_freq;
|
||||
uint32_t scan_freq;
|
||||
set_freq = set_base_freq >> (set_clock & 0x7);
|
||||
scan_freq = scan_base_freq >> (scan_clock & 0x7);
|
||||
|
||||
if (scan_freq < set_freq) {
|
||||
LOG_ERROR("User specifies higher jtag clock than TCK_SCAN clock");
|
||||
return ERROR_FAIL;
|
||||
if (scan_freq < set_freq) {
|
||||
LOG_ERROR("User specifies higher jtag clock than TCK_SCAN clock");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (aice_write_ctrl(AICE_WRITE_CTRL_TCK_CONTROL, set_clock) != ERROR_OK)
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
|
||||
/* Constants for AICE command WRITE_CTRL:TCK_CONTROL */
|
||||
#define AICE_TCK_CONTROL_TCK3048 0x08
|
||||
#define AICE_TCK_CONTROL_TCK_SCAN 0x10
|
||||
|
||||
/* Constants for AICE command WRITE_CTRL:JTAG_PIN_CONTROL */
|
||||
#define AICE_JTAG_PIN_CONTROL_SRST 0x01
|
||||
|
||||
+1
-1
@@ -1843,7 +1843,7 @@ void adapter_deassert_reset(void)
|
||||
LOG_ERROR("transport is not selected");
|
||||
}
|
||||
|
||||
int adapter_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq)
|
||||
{
|
||||
if (jtag->config_trace)
|
||||
|
||||
@@ -156,10 +156,12 @@ endif
|
||||
if IMX_GPIO
|
||||
DRIVERFILES += %D%/imx_gpio.c
|
||||
endif
|
||||
|
||||
if KITPROG
|
||||
DRIVERFILES += %D%/kitprog.c
|
||||
endif
|
||||
if XDS110
|
||||
DRIVERFILES += %D%/xds110.c
|
||||
endif
|
||||
|
||||
DRIVERHEADERS = \
|
||||
%D%/bitbang.h \
|
||||
|
||||
@@ -729,6 +729,20 @@ static void cmsis_dap_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_del
|
||||
cmsis_dap_swd_queue_cmd(cmd, value, 0);
|
||||
}
|
||||
|
||||
static int cmsis_dap_get_serial_info(void)
|
||||
{
|
||||
uint8_t *data;
|
||||
|
||||
int retval = cmsis_dap_cmd_DAP_Info(INFO_ID_SERNUM, &data);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (data[0]) /* strlen */
|
||||
LOG_INFO("CMSIS-DAP: Serial# = %s", &data[1]);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cmsis_dap_get_version_info(void)
|
||||
{
|
||||
uint8_t *data;
|
||||
@@ -802,8 +816,7 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq)
|
||||
|
||||
/* When we are reconnecting, DAP_Connect needs to be rerun, at
|
||||
* least on Keil ULINK-ME */
|
||||
retval = cmsis_dap_cmd_DAP_Connect(seq == LINE_RESET || seq == JTAG_TO_SWD ?
|
||||
CONNECT_SWD : CONNECT_JTAG);
|
||||
retval = cmsis_dap_cmd_DAP_Connect(CONNECT_SWD);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
}
|
||||
@@ -842,17 +855,6 @@ static int cmsis_dap_swd_open(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (cmsis_dap_handle == NULL) {
|
||||
/* SWD init */
|
||||
retval = cmsis_dap_usb_open();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_caps_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!(cmsis_dap_handle->caps & INFO_CAPS_SWD)) {
|
||||
LOG_ERROR("CMSIS-DAP: SWD not supported");
|
||||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
@@ -873,6 +875,22 @@ static int cmsis_dap_init(void)
|
||||
int retval;
|
||||
uint8_t *data;
|
||||
|
||||
retval = cmsis_dap_usb_open();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_caps_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_version_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_serial_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (swd_mode) {
|
||||
retval = cmsis_dap_swd_open();
|
||||
if (retval != ERROR_OK)
|
||||
@@ -880,16 +898,6 @@ static int cmsis_dap_init(void)
|
||||
}
|
||||
|
||||
if (cmsis_dap_handle == NULL) {
|
||||
|
||||
/* JTAG init */
|
||||
retval = cmsis_dap_usb_open();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = cmsis_dap_get_caps_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* Connect in JTAG mode */
|
||||
if (!(cmsis_dap_handle->caps & INFO_CAPS_JTAG)) {
|
||||
LOG_ERROR("CMSIS-DAP: JTAG not supported");
|
||||
@@ -903,10 +911,6 @@ static int cmsis_dap_init(void)
|
||||
LOG_INFO("CMSIS-DAP: Interface Initialised (JTAG)");
|
||||
}
|
||||
|
||||
retval = cmsis_dap_get_version_info();
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* INFO_ID_PKT_SZ - short */
|
||||
retval = cmsis_dap_cmd_DAP_Info(INFO_ID_PKT_SZ, &data);
|
||||
if (retval != ERROR_OK)
|
||||
@@ -1458,6 +1462,12 @@ static void cmsis_dap_execute_stableclocks(struct jtag_command *cmd)
|
||||
cmsis_dap_stableclocks(cmd->cmd.runtest->num_cycles);
|
||||
}
|
||||
|
||||
static void cmsis_dap_execute_tms(struct jtag_command *cmd)
|
||||
{
|
||||
DEBUG_JTAG_IO("TMS: %d bits", cmd->cmd.tms->num_bits);
|
||||
cmsis_dap_cmd_DAP_SWJ_Sequence(cmd->cmd.tms->num_bits, cmd->cmd.tms->bits);
|
||||
}
|
||||
|
||||
/* TODO: Is there need to call cmsis_dap_flush() for the JTAG_PATHMOVE,
|
||||
* JTAG_RUNTEST, JTAG_STABLECLOCKS? */
|
||||
static void cmsis_dap_execute_command(struct jtag_command *cmd)
|
||||
@@ -1488,6 +1498,8 @@ static void cmsis_dap_execute_command(struct jtag_command *cmd)
|
||||
cmsis_dap_execute_stableclocks(cmd);
|
||||
break;
|
||||
case JTAG_TMS:
|
||||
cmsis_dap_execute_tms(cmd);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("BUG: unknown JTAG command type 0x%X encountered", cmd->type);
|
||||
exit(-1);
|
||||
@@ -1650,6 +1662,7 @@ static const char * const cmsis_dap_transport[] = { "swd", "jtag", NULL };
|
||||
|
||||
struct jtag_interface cmsis_dap_interface = {
|
||||
.name = "cmsis-dap",
|
||||
.supported = DEBUG_CAP_TMS_SEQ,
|
||||
.commands = cmsis_dap_command_handlers,
|
||||
.swd = &cmsis_dap_swd_driver,
|
||||
.transports = cmsis_dap_transport,
|
||||
|
||||
@@ -1263,7 +1263,7 @@ static bool check_trace_freq(struct jaylink_swo_speed speed,
|
||||
return false;
|
||||
}
|
||||
|
||||
static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
static int config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq)
|
||||
{
|
||||
int ret;
|
||||
@@ -1275,7 +1275,7 @@ static int config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (pin_protocol != ASYNC_UART) {
|
||||
if (pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART) {
|
||||
LOG_ERROR("Selected pin protocol is not supported.");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
@@ -204,23 +204,20 @@ static int jtag_vpi_queue_tdi_xfer(uint8_t *bits, int nb_bits, int tap_shift)
|
||||
static int jtag_vpi_queue_tdi(uint8_t *bits, int nb_bits, int tap_shift)
|
||||
{
|
||||
int nb_xfer = DIV_ROUND_UP(nb_bits, XFERT_MAX_SIZE * 8);
|
||||
uint8_t *xmit_buffer = bits;
|
||||
int xmit_nb_bits = nb_bits;
|
||||
int i = 0;
|
||||
int retval;
|
||||
|
||||
while (nb_xfer) {
|
||||
|
||||
if (nb_xfer == 1) {
|
||||
retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], xmit_nb_bits, tap_shift);
|
||||
retval = jtag_vpi_queue_tdi_xfer(bits, nb_bits, tap_shift);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
} else {
|
||||
retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], XFERT_MAX_SIZE * 8, NO_TAP_SHIFT);
|
||||
retval = jtag_vpi_queue_tdi_xfer(bits, XFERT_MAX_SIZE * 8, NO_TAP_SHIFT);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
xmit_nb_bits -= XFERT_MAX_SIZE * 8;
|
||||
i += XFERT_MAX_SIZE;
|
||||
nb_bits -= XFERT_MAX_SIZE * 8;
|
||||
if (bits)
|
||||
bits += XFERT_MAX_SIZE;
|
||||
}
|
||||
|
||||
nb_xfer--;
|
||||
@@ -318,7 +315,7 @@ static int jtag_vpi_runtest(int cycles, tap_state_t state)
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = jtag_vpi_queue_tdi(NULL, cycles, NO_TAP_SHIFT);
|
||||
retval = jtag_vpi_queue_tdi(NULL, cycles, TAP_SHIFT);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
|
||||
@@ -888,13 +888,11 @@ COMMAND_HANDLER(kitprog_handle_acquire_psoc_command)
|
||||
COMMAND_HANDLER(kitprog_handle_serial_command)
|
||||
{
|
||||
if (CMD_ARGC == 1) {
|
||||
size_t len = strlen(CMD_ARGV[0]);
|
||||
kitprog_serial = calloc(len + 1, sizeof(char));
|
||||
kitprog_serial = strdup(CMD_ARGV[0]);
|
||||
if (kitprog_serial == NULL) {
|
||||
LOG_ERROR("Failed to allocate memory for the serial number");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
strncpy(kitprog_serial, CMD_ARGV[0], len + 1);
|
||||
} else {
|
||||
LOG_ERROR("expected exactly one argument to kitprog_serial <serial-number>");
|
||||
return ERROR_FAIL;
|
||||
|
||||
@@ -2194,12 +2194,13 @@ error_open:
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int stlink_config_trace(void *handle, bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int stlink_config_trace(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq)
|
||||
{
|
||||
struct stlink_usb_handle_s *h = handle;
|
||||
|
||||
if (enabled && (h->jtag_api < 2 || pin_protocol != ASYNC_UART)) {
|
||||
if (enabled && (h->jtag_api < 2 ||
|
||||
pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) {
|
||||
LOG_ERROR("The attached ST-LINK version doesn't support this trace mode");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
@@ -58,11 +58,11 @@
|
||||
/*
|
||||
* Helper func to determine if gpio number valid
|
||||
*
|
||||
* Assume here that there will be less than 1000 gpios on a system
|
||||
* Assume here that there will be less than 10000 gpios on a system
|
||||
*/
|
||||
static int is_gpio_valid(int gpio)
|
||||
{
|
||||
return gpio >= 0 && gpio < 1000;
|
||||
return gpio >= 0 && gpio < 10000;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -89,7 +89,7 @@ static int open_write_close(const char *name, const char *valstr)
|
||||
*/
|
||||
static void unexport_sysfs_gpio(int gpio)
|
||||
{
|
||||
char gpiostr[4];
|
||||
char gpiostr[5];
|
||||
|
||||
if (!is_gpio_valid(gpio))
|
||||
return;
|
||||
@@ -113,7 +113,7 @@ static void unexport_sysfs_gpio(int gpio)
|
||||
static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
|
||||
{
|
||||
char buf[40];
|
||||
char gpiostr[4];
|
||||
char gpiostr[5];
|
||||
int ret;
|
||||
|
||||
if (!is_gpio_valid(gpio))
|
||||
|
||||
@@ -445,6 +445,7 @@ static void ublast_queue_bytes(uint8_t *bytes, int nb_bytes)
|
||||
* ublast_tms_seq - write a TMS sequence transition to JTAG
|
||||
* @bits: TMS bits to be written (bit0, bit1 .. bitN)
|
||||
* @nb_bits: number of TMS bits (between 1 and 8)
|
||||
* @skip: number of TMS bits to skip at the beginning of the series
|
||||
*
|
||||
* Write a serie of TMS transitions, where each transition consists in :
|
||||
* - writing out TCK=0, TMS=<new_state>, TDI=<???>
|
||||
@@ -452,12 +453,12 @@ static void ublast_queue_bytes(uint8_t *bytes, int nb_bytes)
|
||||
* The function ensures that at the end of the sequence, the clock (TCK) is put
|
||||
* low.
|
||||
*/
|
||||
static void ublast_tms_seq(const uint8_t *bits, int nb_bits)
|
||||
static void ublast_tms_seq(const uint8_t *bits, int nb_bits, int skip)
|
||||
{
|
||||
int i;
|
||||
|
||||
DEBUG_JTAG_IO("(bits=%02x..., nb_bits=%d)", bits[0], nb_bits);
|
||||
for (i = 0; i < nb_bits; i++)
|
||||
for (i = skip; i < nb_bits; i++)
|
||||
ublast_clock_tms((bits[i / 8] >> (i % 8)) & 0x01);
|
||||
ublast_idle_clock();
|
||||
}
|
||||
@@ -469,7 +470,7 @@ static void ublast_tms_seq(const uint8_t *bits, int nb_bits)
|
||||
static void ublast_tms(struct tms_command *cmd)
|
||||
{
|
||||
DEBUG_JTAG_IO("(num_bits=%d)", cmd->num_bits);
|
||||
ublast_tms_seq(cmd->bits, cmd->num_bits);
|
||||
ublast_tms_seq(cmd->bits, cmd->num_bits, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -501,11 +502,12 @@ static void ublast_path_move(struct pathmove_command *cmd)
|
||||
/**
|
||||
* ublast_state_move - move JTAG state to the target state
|
||||
* @state: the target state
|
||||
* @skip: number of bits to skip at the beginning of the path
|
||||
*
|
||||
* Input the correct TMS sequence to the JTAG TAP so that we end up in the
|
||||
* target state. This assumes the current state (tap_get_state()) is correct.
|
||||
*/
|
||||
static void ublast_state_move(tap_state_t state)
|
||||
static void ublast_state_move(tap_state_t state, int skip)
|
||||
{
|
||||
uint8_t tms_scan;
|
||||
int tms_len;
|
||||
@@ -516,7 +518,7 @@ static void ublast_state_move(tap_state_t state)
|
||||
return;
|
||||
tms_scan = tap_get_tms_path(tap_get_state(), state);
|
||||
tms_len = tap_get_tms_path_len(tap_get_state(), state);
|
||||
ublast_tms_seq(&tms_scan, tms_len);
|
||||
ublast_tms_seq(&tms_scan, tms_len, skip);
|
||||
tap_set_state(state);
|
||||
}
|
||||
|
||||
@@ -688,9 +690,9 @@ static void ublast_runtest(int cycles, tap_state_t state)
|
||||
{
|
||||
DEBUG_JTAG_IO("%s(cycles=%i, end_state=%d)", __func__, cycles, state);
|
||||
|
||||
ublast_state_move(TAP_IDLE);
|
||||
ublast_state_move(TAP_IDLE, 0);
|
||||
ublast_queue_tdi(NULL, cycles, SCAN_OUT);
|
||||
ublast_state_move(state);
|
||||
ublast_state_move(state, 0);
|
||||
}
|
||||
|
||||
static void ublast_stableclocks(int cycles)
|
||||
@@ -720,9 +722,9 @@ static int ublast_scan(struct scan_command *cmd)
|
||||
scan_bits = jtag_build_buffer(cmd, &buf);
|
||||
|
||||
if (cmd->ir_scan)
|
||||
ublast_state_move(TAP_IRSHIFT);
|
||||
ublast_state_move(TAP_IRSHIFT, 0);
|
||||
else
|
||||
ublast_state_move(TAP_DRSHIFT);
|
||||
ublast_state_move(TAP_DRSHIFT, 0);
|
||||
|
||||
log_buf = hexdump(buf, DIV_ROUND_UP(scan_bits, 8));
|
||||
DEBUG_JTAG_IO("%s(scan=%s, type=%s, bits=%d, buf=[%s], end_state=%d)", __func__,
|
||||
@@ -733,20 +735,15 @@ static int ublast_scan(struct scan_command *cmd)
|
||||
|
||||
ublast_queue_tdi(buf, scan_bits, type);
|
||||
|
||||
/*
|
||||
* As our JTAG is in an unstable state (IREXIT1 or DREXIT1), move it
|
||||
* forward to a stable IRPAUSE or DRPAUSE.
|
||||
*/
|
||||
ublast_clock_tms(0);
|
||||
if (cmd->ir_scan)
|
||||
tap_set_state(TAP_IRPAUSE);
|
||||
else
|
||||
tap_set_state(TAP_DRPAUSE);
|
||||
|
||||
ret = jtag_read_buffer(buf, cmd);
|
||||
if (buf)
|
||||
free(buf);
|
||||
ublast_state_move(cmd->end_state);
|
||||
/*
|
||||
* ublast_queue_tdi sends the last bit with TMS=1. We are therefore
|
||||
* already in Exit1-DR/IR and have to skip the first step on our way
|
||||
* to end_state.
|
||||
*/
|
||||
ublast_state_move(cmd->end_state, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -776,7 +773,7 @@ static void ublast_initial_wipeout(void)
|
||||
/*
|
||||
* Put JTAG in RESET state (five 1 on TMS)
|
||||
*/
|
||||
ublast_tms_seq(&tms_reset, 5);
|
||||
ublast_tms_seq(&tms_reset, 5, 0);
|
||||
tap_set_state(TAP_RESET);
|
||||
}
|
||||
|
||||
@@ -805,7 +802,7 @@ static int ublast_execute_queue(void)
|
||||
ublast_stableclocks(cmd->cmd.stableclocks->num_cycles);
|
||||
break;
|
||||
case JTAG_TLR_RESET:
|
||||
ublast_state_move(cmd->cmd.statemove->end_state);
|
||||
ublast_state_move(cmd->cmd.statemove->end_state, 0);
|
||||
break;
|
||||
case JTAG_PATHMOVE:
|
||||
ublast_path_move(cmd->cmd.pathmove);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -191,7 +191,7 @@ int hl_interface_override_target(const char **targetname)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int hl_interface_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq)
|
||||
{
|
||||
if (hl_if.layout->api->config_trace)
|
||||
|
||||
@@ -91,7 +91,7 @@ struct hl_layout_api_s {
|
||||
* its maximum supported rate there
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int (*config_trace)(void *handle, bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int (*config_trace)(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq);
|
||||
/**
|
||||
* Poll for new trace data
|
||||
|
||||
@@ -309,7 +309,7 @@ struct jtag_interface {
|
||||
* its maximum supported rate there
|
||||
* @returns ERROR_OK on success, an error code on failure.
|
||||
*/
|
||||
int (*config_trace)(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int (*config_trace)(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq);
|
||||
|
||||
/**
|
||||
@@ -328,7 +328,7 @@ extern const char * const jtag_only[];
|
||||
|
||||
void adapter_assert_reset(void);
|
||||
void adapter_deassert_reset(void);
|
||||
int adapter_config_trace(bool enabled, enum tpio_pin_protocol pin_protocol,
|
||||
int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
|
||||
uint32_t port_size, unsigned int *trace_freq);
|
||||
int adapter_poll_trace(uint8_t *buf, size_t *size);
|
||||
|
||||
|
||||
@@ -132,6 +132,9 @@ extern struct jtag_interface kitprog_interface;
|
||||
#if BUILD_IMX_GPIO == 1
|
||||
extern struct jtag_interface imx_gpio_interface;
|
||||
#endif
|
||||
#if BUILD_XDS110 == 1
|
||||
extern struct jtag_interface xds110_interface;
|
||||
#endif
|
||||
#endif /* standard drivers */
|
||||
|
||||
/**
|
||||
@@ -234,6 +237,9 @@ struct jtag_interface *jtag_interfaces[] = {
|
||||
#if BUILD_IMX_GPIO == 1
|
||||
&imx_gpio_interface,
|
||||
#endif
|
||||
#if BUILD_XDS110 == 1
|
||||
&xds110_interface,
|
||||
#endif
|
||||
#endif /* standard drivers */
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -265,8 +265,8 @@ COMMAND_HANDLER(handle_power_command)
|
||||
bool enable;
|
||||
COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable);
|
||||
setPower(enable);
|
||||
/* fall through */
|
||||
}
|
||||
/* fall through */
|
||||
case 0:
|
||||
LOG_INFO("Target power %s", savePower ? "on" : "off");
|
||||
break;
|
||||
|
||||
@@ -16,6 +16,7 @@ noinst_LTLIBRARIES += %D%/librtos.la
|
||||
%D%/mqx.c \
|
||||
%D%/riscv_debug.c \
|
||||
%D%/uCOS-III.c \
|
||||
%D%/nuttx.c \
|
||||
%D%/rtos.h \
|
||||
%D%/rtos_standard_stackings.h \
|
||||
%D%/rtos_ecos_stackings.h \
|
||||
@@ -24,6 +25,7 @@ noinst_LTLIBRARIES += %D%/librtos.la
|
||||
%D%/rtos_embkernel_stackings.h \
|
||||
%D%/rtos_mqx_stackings.h \
|
||||
%D%/rtos_ucos_iii_stackings.h \
|
||||
%D%/nuttx_header.h \
|
||||
%D%/riscv_debug.h
|
||||
|
||||
%C%_librtos_la_CFLAGS = $(AM_CFLAGS)
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
/***************************************************************************
|
||||
* Copyright 2016,2017 Sony Video & Sound Products Inc. *
|
||||
* Masatoshi Tateishi - Masatoshi.Tateishi@jp.sony.com *
|
||||
* Masayuki Ishikawa - Masayuki.Ishikawa@jp.sony.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. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <jtag/jtag.h>
|
||||
#include "target/target.h"
|
||||
#include "target/target_type.h"
|
||||
#include "target/armv7m.h"
|
||||
#include "target/cortex_m.h"
|
||||
#include "rtos.h"
|
||||
#include "helper/log.h"
|
||||
#include "helper/types.h"
|
||||
#include "server/gdb_server.h"
|
||||
|
||||
#include "nuttx_header.h"
|
||||
|
||||
|
||||
int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size);
|
||||
|
||||
#ifdef CONFIG_DISABLE_SIGNALS
|
||||
#define SIG_QUEUE_NUM 0
|
||||
#else
|
||||
#define SIG_QUEUE_NUM 1
|
||||
#endif /* CONFIG_DISABLE_SIGNALS */
|
||||
|
||||
#ifdef CONFIG_DISABLE_MQUEUE
|
||||
#define M_QUEUE_NUM 0
|
||||
#else
|
||||
#define M_QUEUE_NUM 2
|
||||
#endif /* CONFIG_DISABLE_MQUEUE */
|
||||
|
||||
#ifdef CONFIG_PAGING
|
||||
#define PAGING_QUEUE_NUM 1
|
||||
#else
|
||||
#define PAGING_QUEUE_NUM 0
|
||||
#endif /* CONFIG_PAGING */
|
||||
|
||||
|
||||
#define TASK_QUEUE_NUM (6 + SIG_QUEUE_NUM + M_QUEUE_NUM + PAGING_QUEUE_NUM)
|
||||
|
||||
|
||||
/* see nuttx/sched/os_start.c */
|
||||
static char *nuttx_symbol_list[] = {
|
||||
"g_readytorun", /* 0: must be top of this array */
|
||||
"g_tasklisttable",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* see nuttx/include/nuttx/sched.h */
|
||||
struct tcb {
|
||||
uint32_t flink;
|
||||
uint32_t blink;
|
||||
uint8_t dat[512];
|
||||
};
|
||||
|
||||
struct {
|
||||
uint32_t addr;
|
||||
uint32_t prio;
|
||||
} g_tasklist[TASK_QUEUE_NUM];
|
||||
|
||||
static char *task_state_str[] = {
|
||||
"INVALID",
|
||||
"PENDING",
|
||||
"READYTORUN",
|
||||
"RUNNING",
|
||||
"INACTIVE",
|
||||
"WAIT_SEM",
|
||||
#ifndef CONFIG_DISABLE_SIGNALS
|
||||
"WAIT_SIG",
|
||||
#endif /* CONFIG_DISABLE_SIGNALS */
|
||||
#ifndef CONFIG_DISABLE_MQUEUE
|
||||
"WAIT_MQNOTEMPTY",
|
||||
"WAIT_MQNOTFULL",
|
||||
#endif /* CONFIG_DISABLE_MQUEUE */
|
||||
#ifdef CONFIG_PAGING
|
||||
"WAIT_PAGEFILL",
|
||||
#endif /* CONFIG_PAGING */
|
||||
};
|
||||
|
||||
/* see arch/arm/include/armv7-m/irq_cmnvector.h */
|
||||
static const struct stack_register_offset nuttx_stack_offsets_cortex_m[] = {
|
||||
{ 0x28, 32 }, /* r0 */
|
||||
{ 0x2c, 32 }, /* r1 */
|
||||
{ 0x30, 32 }, /* r2 */
|
||||
{ 0x34, 32 }, /* r3 */
|
||||
{ 0x08, 32 }, /* r4 */
|
||||
{ 0x0c, 32 }, /* r5 */
|
||||
{ 0x10, 32 }, /* r6 */
|
||||
{ 0x14, 32 }, /* r7 */
|
||||
{ 0x18, 32 }, /* r8 */
|
||||
{ 0x1c, 32 }, /* r9 */
|
||||
{ 0x20, 32 }, /* r10 */
|
||||
{ 0x24, 32 }, /* r11 */
|
||||
{ 0x38, 32 }, /* r12 */
|
||||
{ 0, 32 }, /* sp */
|
||||
{ 0x3c, 32 }, /* lr */
|
||||
{ 0x40, 32 }, /* pc */
|
||||
{ 0x44, 32 }, /* xPSR */
|
||||
};
|
||||
|
||||
|
||||
static const struct rtos_register_stacking nuttx_stacking_cortex_m = {
|
||||
0x48, /* stack_registers_size */
|
||||
-1, /* stack_growth_direction */
|
||||
17, /* num_output_registers */
|
||||
0, /* stack_alignment */
|
||||
nuttx_stack_offsets_cortex_m /* register_offsets */
|
||||
};
|
||||
|
||||
static const struct stack_register_offset nuttx_stack_offsets_cortex_m_fpu[] = {
|
||||
{ 0x6c, 32 }, /* r0 */
|
||||
{ 0x70, 32 }, /* r1 */
|
||||
{ 0x74, 32 }, /* r2 */
|
||||
{ 0x78, 32 }, /* r3 */
|
||||
{ 0x08, 32 }, /* r4 */
|
||||
{ 0x0c, 32 }, /* r5 */
|
||||
{ 0x10, 32 }, /* r6 */
|
||||
{ 0x14, 32 }, /* r7 */
|
||||
{ 0x18, 32 }, /* r8 */
|
||||
{ 0x1c, 32 }, /* r9 */
|
||||
{ 0x20, 32 }, /* r10 */
|
||||
{ 0x24, 32 }, /* r11 */
|
||||
{ 0x7c, 32 }, /* r12 */
|
||||
{ 0, 32 }, /* sp */
|
||||
{ 0x80, 32 }, /* lr */
|
||||
{ 0x84, 32 }, /* pc */
|
||||
{ 0x88, 32 }, /* xPSR */
|
||||
};
|
||||
|
||||
static const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu = {
|
||||
0x8c, /* stack_registers_size */
|
||||
-1, /* stack_growth_direction */
|
||||
17, /* num_output_registers */
|
||||
0, /* stack_alignment */
|
||||
nuttx_stack_offsets_cortex_m_fpu /* register_offsets */
|
||||
};
|
||||
|
||||
static int pid_offset = PID;
|
||||
static int state_offset = STATE;
|
||||
static int name_offset = NAME;
|
||||
static int xcpreg_offset = XCPREG;
|
||||
static int name_size = NAME_SIZE;
|
||||
|
||||
static int rcmd_offset(const char *cmd, const char *name)
|
||||
{
|
||||
if (strncmp(cmd, name, strlen(name)))
|
||||
return -1;
|
||||
|
||||
if (strlen(cmd) <= strlen(name) + 1)
|
||||
return -1;
|
||||
|
||||
return atoi(cmd + strlen(name));
|
||||
}
|
||||
|
||||
static int nuttx_thread_packet(struct connection *connection,
|
||||
char const *packet, int packet_size)
|
||||
{
|
||||
char cmd[GDB_BUFFER_SIZE / 2] = "";
|
||||
|
||||
if (!strncmp(packet, "qRcmd", 5)) {
|
||||
size_t len = unhexify((uint8_t *)cmd, packet + 6, sizeof(cmd));
|
||||
int offset;
|
||||
|
||||
if (len <= 0)
|
||||
goto pass;
|
||||
|
||||
offset = rcmd_offset(cmd, "nuttx.pid_offset");
|
||||
|
||||
if (offset >= 0) {
|
||||
LOG_INFO("pid_offset: %d", offset);
|
||||
pid_offset = offset;
|
||||
goto retok;
|
||||
}
|
||||
|
||||
offset = rcmd_offset(cmd, "nuttx.state_offset");
|
||||
|
||||
if (offset >= 0) {
|
||||
LOG_INFO("state_offset: %d", offset);
|
||||
state_offset = offset;
|
||||
goto retok;
|
||||
}
|
||||
|
||||
offset = rcmd_offset(cmd, "nuttx.name_offset");
|
||||
|
||||
if (offset >= 0) {
|
||||
LOG_INFO("name_offset: %d", offset);
|
||||
name_offset = offset;
|
||||
goto retok;
|
||||
}
|
||||
|
||||
offset = rcmd_offset(cmd, "nuttx.xcpreg_offset");
|
||||
|
||||
if (offset >= 0) {
|
||||
LOG_INFO("xcpreg_offset: %d", offset);
|
||||
xcpreg_offset = offset;
|
||||
goto retok;
|
||||
}
|
||||
|
||||
offset = rcmd_offset(cmd, "nuttx.name_size");
|
||||
|
||||
if (offset >= 0) {
|
||||
LOG_INFO("name_size: %d", offset);
|
||||
name_size = offset;
|
||||
goto retok;
|
||||
}
|
||||
}
|
||||
pass:
|
||||
return rtos_thread_packet(connection, packet, packet_size);
|
||||
retok:
|
||||
gdb_put_packet(connection, "OK", 2);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
||||
static bool nuttx_detect_rtos(struct target *target)
|
||||
{
|
||||
if ((target->rtos->symbols != NULL) &&
|
||||
(target->rtos->symbols[0].address != 0) &&
|
||||
(target->rtos->symbols[1].address != 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int nuttx_create(struct target *target)
|
||||
{
|
||||
|
||||
target->rtos->gdb_thread_packet = nuttx_thread_packet;
|
||||
LOG_INFO("target type name = %s", target->type->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nuttx_update_threads(struct rtos *rtos)
|
||||
{
|
||||
uint32_t thread_count;
|
||||
struct tcb tcb;
|
||||
int ret;
|
||||
uint32_t head;
|
||||
uint32_t tcb_addr;
|
||||
uint32_t i;
|
||||
uint8_t state;
|
||||
|
||||
if (rtos->symbols == NULL) {
|
||||
LOG_ERROR("No symbols for NuttX");
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* free previous thread details */
|
||||
rtos_free_threadlist(rtos);
|
||||
|
||||
ret = target_read_buffer(rtos->target, rtos->symbols[1].address,
|
||||
sizeof(g_tasklist), (uint8_t *)&g_tasklist);
|
||||
if (ret) {
|
||||
LOG_ERROR("target_read_buffer : ret = %d\n", ret);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
thread_count = 0;
|
||||
|
||||
for (i = 0; i < TASK_QUEUE_NUM; i++) {
|
||||
|
||||
if (g_tasklist[i].addr == 0)
|
||||
continue;
|
||||
|
||||
ret = target_read_u32(rtos->target, g_tasklist[i].addr,
|
||||
&head);
|
||||
|
||||
if (ret) {
|
||||
LOG_ERROR("target_read_u32 : ret = %d\n", ret);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* readytorun head is current thread */
|
||||
if (g_tasklist[i].addr == rtos->symbols[0].address)
|
||||
rtos->current_thread = head;
|
||||
|
||||
|
||||
tcb_addr = head;
|
||||
while (tcb_addr) {
|
||||
struct thread_detail *thread;
|
||||
ret = target_read_buffer(rtos->target, tcb_addr,
|
||||
sizeof(tcb), (uint8_t *)&tcb);
|
||||
if (ret) {
|
||||
LOG_ERROR("target_read_buffer : ret = %d\n",
|
||||
ret);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
thread_count++;
|
||||
|
||||
rtos->thread_details = realloc(rtos->thread_details,
|
||||
sizeof(struct thread_detail) * thread_count);
|
||||
thread = &rtos->thread_details[thread_count - 1];
|
||||
thread->threadid = tcb_addr;
|
||||
thread->exists = true;
|
||||
|
||||
state = tcb.dat[state_offset - 8];
|
||||
thread->extra_info_str = NULL;
|
||||
if (state < sizeof(task_state_str)/sizeof(char *)) {
|
||||
thread->extra_info_str = malloc(256);
|
||||
snprintf(thread->extra_info_str, 256, "pid:%d, %s",
|
||||
tcb.dat[pid_offset - 8] |
|
||||
tcb.dat[pid_offset - 8 + 1] << 8,
|
||||
task_state_str[state]);
|
||||
}
|
||||
|
||||
if (name_offset) {
|
||||
thread->thread_name_str = malloc(name_size + 1);
|
||||
snprintf(thread->thread_name_str, name_size,
|
||||
"%s", (char *)&tcb.dat[name_offset - 8]);
|
||||
} else {
|
||||
thread->thread_name_str = malloc(sizeof("None"));
|
||||
strcpy(thread->thread_name_str, "None");
|
||||
}
|
||||
|
||||
tcb_addr = tcb.flink;
|
||||
}
|
||||
}
|
||||
rtos->thread_count = thread_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* thread_id = tcb address;
|
||||
*/
|
||||
static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
|
||||
char **hex_reg_list) {
|
||||
int retval;
|
||||
|
||||
*hex_reg_list = NULL;
|
||||
|
||||
/* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */
|
||||
bool cm4_fpu_enabled = false;
|
||||
struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
|
||||
if (is_armv7m(armv7m_target)) {
|
||||
if (armv7m_target->fp_feature == FPv4_SP) {
|
||||
/* Found ARM v7m target which includes a FPU */
|
||||
uint32_t cpacr;
|
||||
|
||||
retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Could not read CPACR register to check FPU state");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if CP10 and CP11 are set to full access. */
|
||||
if (cpacr & 0x00F00000) {
|
||||
/* Found target with enabled FPU */
|
||||
cm4_fpu_enabled = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const struct rtos_register_stacking *stacking;
|
||||
if (cm4_fpu_enabled)
|
||||
stacking = &nuttx_stacking_cortex_m_fpu;
|
||||
else
|
||||
stacking = &nuttx_stacking_cortex_m;
|
||||
|
||||
return rtos_generic_stack_read(rtos->target, stacking,
|
||||
(uint32_t)thread_id + xcpreg_offset, hex_reg_list);
|
||||
}
|
||||
|
||||
static int nuttx_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
*symbol_list = (symbol_table_elem_t *) calloc(1,
|
||||
sizeof(symbol_table_elem_t) * ARRAY_SIZE(nuttx_symbol_list));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nuttx_symbol_list); i++)
|
||||
(*symbol_list)[i].symbol_name = nuttx_symbol_list[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct rtos_type nuttx_rtos = {
|
||||
.name = "nuttx",
|
||||
.detect_rtos = nuttx_detect_rtos,
|
||||
.create = nuttx_create,
|
||||
.update_threads = nuttx_update_threads,
|
||||
.get_thread_reg_list = nuttx_get_thread_reg_list,
|
||||
.get_symbol_list_to_lookup = nuttx_get_symbol_list_to_lookup,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/***************************************************************************
|
||||
* Copyright 2016,2017 Sony Video & Sound Products Inc. *
|
||||
* Masatoshi Tateishi - Masatoshi.Tateishi@jp.sony.com *
|
||||
* Masayuki Ishikawa - Masayuki.Ishikawa@jp.sony.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. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_RTOS_NUTTX_HEADER_H
|
||||
#define OPENOCD_RTOS_NUTTX_HEADER_H
|
||||
|
||||
/* gdb script to update the header file
|
||||
according to kernel version and build option
|
||||
before executing function awareness
|
||||
kernel symbol must be loaded : symbol nuttx
|
||||
|
||||
define awareness
|
||||
set logging off
|
||||
set logging file nuttx_header.h
|
||||
set logging on
|
||||
|
||||
printf "#define PID %p\n",&((struct tcb_s *)(0))->pid
|
||||
printf "#define XCPREG %p\n",&((struct tcb_s *)(0))->xcp.regs
|
||||
printf "#define STATE %p\n",&((struct tcb_s *)(0))->task_state
|
||||
printf "#define NAME %p\n",&((struct tcb_s *)(0))->name
|
||||
printf "#define NAME_SIZE %d\n",sizeof(((struct tcb_s *)(0))->name)
|
||||
end
|
||||
|
||||
|
||||
OR ~/.gdbinit
|
||||
|
||||
|
||||
define hookpost-file
|
||||
|
||||
if &g_readytorun != 0
|
||||
eval "monitor nuttx.pid_offset %d", &((struct tcb_s *)(0))->pid
|
||||
eval "monitor nuttx.xcpreg_offset %d", &((struct tcb_s *)(0))->xcp.regs
|
||||
eval "monitor nuttx.state_offset %d", &((struct tcb_s *)(0))->task_state
|
||||
eval "monitor nuttx.name_offset %d", &((struct tcb_s *)(0))->name
|
||||
eval "monitor nuttx.name_size %d", sizeof(((struct tcb_s *)(0))->name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
*/
|
||||
|
||||
/* default offset */
|
||||
#define PID 0xc
|
||||
#define XCPREG 0x70
|
||||
#define STATE 0x19
|
||||
#define NAME 0xb8
|
||||
#define NAME_SIZE 32
|
||||
|
||||
/* defconfig of nuttx */
|
||||
/* #define CONFIG_DISABLE_SIGNALS */
|
||||
#define CONFIG_DISABLE_MQUEUE
|
||||
/* #define CONFIG_PAGING */
|
||||
|
||||
|
||||
#endif /* OPENOCD_RTOS_NUTTX_HEADER_H */
|
||||
+7
-11
@@ -22,11 +22,6 @@ static int riscv_create_rtos(struct target *target)
|
||||
|
||||
struct riscv_rtos *r = calloc(1, sizeof(*r));
|
||||
target->rtos->rtos_specific_params = r;
|
||||
#if 0
|
||||
r->target_hartid = 0;
|
||||
r->target_any_hart = true;
|
||||
r->target_every_hart = true;
|
||||
#endif
|
||||
|
||||
target->rtos->current_threadid = 1;
|
||||
target->rtos->current_thread = 1;
|
||||
@@ -129,6 +124,13 @@ static int riscv_gdb_thread_packet(struct connection *connection, const char *pa
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
if (strcmp(packet, "qC") == 0) {
|
||||
char rep_str[32];
|
||||
snprintf(rep_str, 32, "QC%" PRIx64, rtos->current_threadid);
|
||||
gdb_put_packet(connection, rep_str, strlen(rep_str));
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
return GDB_THREAD_PACKET_NOT_CONSUMED;
|
||||
|
||||
case 'Q':
|
||||
@@ -266,12 +268,6 @@ static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char
|
||||
{
|
||||
LOG_DEBUG("Updating RISC-V register list for hart %d", (int)(thread_id - 1));
|
||||
|
||||
#if 0
|
||||
LOG_ERROR(" Not actually updating");
|
||||
*hex_reg_list = 0;
|
||||
return JIM_OK;
|
||||
#endif
|
||||
|
||||
size_t n_regs = 32;
|
||||
size_t xlen = 64;
|
||||
size_t reg_chars = xlen / 8 * 2;
|
||||
|
||||
@@ -35,6 +35,7 @@ extern struct rtos_type ChibiOS_rtos;
|
||||
extern struct rtos_type embKernel_rtos;
|
||||
extern struct rtos_type mqx_rtos;
|
||||
extern struct rtos_type uCOS_III_rtos;
|
||||
extern struct rtos_type nuttx_rtos;
|
||||
extern struct rtos_type riscv_rtos;
|
||||
|
||||
static struct rtos_type *rtos_types[] = {
|
||||
@@ -46,6 +47,7 @@ static struct rtos_type *rtos_types[] = {
|
||||
&embKernel_rtos,
|
||||
&mqx_rtos,
|
||||
&uCOS_III_rtos,
|
||||
&nuttx_rtos,
|
||||
&riscv_rtos,
|
||||
NULL
|
||||
};
|
||||
|
||||
+29
-20
@@ -112,6 +112,8 @@ static char *gdb_port_next;
|
||||
static void gdb_log_callback(void *priv, const char *file, unsigned line,
|
||||
const char *function, const char *string);
|
||||
|
||||
static void gdb_sig_halted(struct connection *connection);
|
||||
|
||||
/* number of gdb connections, mainly to suppress gdb related debugging spam
|
||||
* in helper/log.c when no gdb connections are actually active */
|
||||
int gdb_actual_connections;
|
||||
@@ -720,7 +722,7 @@ static int gdb_output(struct command_context *context, const char *line)
|
||||
static void gdb_signal_reply(struct target *target, struct connection *connection)
|
||||
{
|
||||
struct gdb_connection *gdb_connection = connection->priv;
|
||||
char sig_reply[45];
|
||||
char sig_reply[65];
|
||||
char stop_reason[20];
|
||||
char current_thread[25];
|
||||
int sig_reply_len;
|
||||
@@ -765,7 +767,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
|
||||
current_thread[0] = '\0';
|
||||
if (target->rtos != NULL) {
|
||||
struct target *ct;
|
||||
snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";",
|
||||
snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";",
|
||||
target->rtos->current_thread);
|
||||
target->rtos->current_threadid = target->rtos->current_thread;
|
||||
target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
|
||||
@@ -791,64 +793,64 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio
|
||||
bool program_exited = false;
|
||||
|
||||
if (strcmp(target->fileio_info->identifier, "open") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3,
|
||||
target->fileio_info->param_4);
|
||||
else if (strcmp(target->fileio_info->identifier, "close") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1);
|
||||
else if (strcmp(target->fileio_info->identifier, "read") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3);
|
||||
else if (strcmp(target->fileio_info->identifier, "write") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3);
|
||||
else if (strcmp(target->fileio_info->identifier, "lseek") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3);
|
||||
else if (strcmp(target->fileio_info->identifier, "rename") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3,
|
||||
target->fileio_info->param_4);
|
||||
else if (strcmp(target->fileio_info->identifier, "unlink") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2);
|
||||
else if (strcmp(target->fileio_info->identifier, "stat") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2,
|
||||
target->fileio_info->param_3);
|
||||
else if (strcmp(target->fileio_info->identifier, "fstat") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2);
|
||||
else if (strcmp(target->fileio_info->identifier, "gettimeofday") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 ",%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2);
|
||||
else if (strcmp(target->fileio_info->identifier, "isatty") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1);
|
||||
else if (strcmp(target->fileio_info->identifier, "system") == 0)
|
||||
sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
|
||||
sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier,
|
||||
target->fileio_info->param_1,
|
||||
target->fileio_info->param_2);
|
||||
else if (strcmp(target->fileio_info->identifier, "exit") == 0) {
|
||||
/* If target hits exit syscall, report to GDB the program is terminated.
|
||||
* In addition, let target run its own exit syscall handler. */
|
||||
program_exited = true;
|
||||
sprintf(fileio_command, "W%02" PRIx32, target->fileio_info->param_1);
|
||||
sprintf(fileio_command, "W%02" PRIx64, target->fileio_info->param_1);
|
||||
} else {
|
||||
LOG_DEBUG("Unknown syscall: %s", target->fileio_info->identifier);
|
||||
|
||||
@@ -934,6 +936,7 @@ static int gdb_new_connection(struct connection *connection)
|
||||
|
||||
target = get_target_from_connection(connection);
|
||||
connection->priv = gdb_connection;
|
||||
connection->cmd_ctx->current_target = target;
|
||||
|
||||
/* initialize gdb connection information */
|
||||
gdb_connection->buf_p = gdb_connection->buffer;
|
||||
@@ -1357,7 +1360,8 @@ static int gdb_set_register_packet(struct connection *connection,
|
||||
int chars = (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2);
|
||||
|
||||
if ((unsigned int)chars != strlen(separator + 1)) {
|
||||
LOG_ERROR("gdb sent a packet with wrong register size");
|
||||
LOG_ERROR("gdb sent %zu bits for a %d-bit register (%s)",
|
||||
strlen(separator + 1) * 4, chars * 4, reg_list[reg_num]->name);
|
||||
free(bin_buf);
|
||||
return ERROR_SERVER_REMOTE_CLOSED;
|
||||
}
|
||||
@@ -2704,6 +2708,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
|
||||
|
||||
/* simple case, a continue packet */
|
||||
if (parse[0] == 'c') {
|
||||
gdb_running_type = 'c';
|
||||
LOG_DEBUG("target %s continue", target_name(target));
|
||||
log_add_callback(gdb_log_callback, connection);
|
||||
retval = target_resume(target, 1, 0, 0, 0);
|
||||
@@ -2730,6 +2735,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
|
||||
|
||||
/* single-step or step-over-breakpoint */
|
||||
if (parse[0] == 's') {
|
||||
gdb_running_type = 's';
|
||||
bool fake_step = false;
|
||||
|
||||
if (strncmp(parse, "s:", 2) == 0) {
|
||||
@@ -3016,9 +3022,12 @@ static int gdb_v_packet(struct connection *connection,
|
||||
|
||||
static int gdb_detach(struct connection *connection)
|
||||
{
|
||||
target_call_event_callbacks(get_target_from_connection(connection),
|
||||
TARGET_EVENT_GDB_DETACH);
|
||||
|
||||
/*
|
||||
* Only reply "OK" to GDB
|
||||
* it will close the connection and this will trigger a call to
|
||||
* gdb_connection_closed() that will in turn trigger the event
|
||||
* TARGET_EVENT_GDB_DETACH
|
||||
*/
|
||||
return gdb_put_packet(connection, "OK", 2);
|
||||
}
|
||||
|
||||
@@ -3084,7 +3093,7 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line,
|
||||
gdb_output_con(connection, string);
|
||||
}
|
||||
|
||||
void gdb_sig_halted(struct connection *connection)
|
||||
static void gdb_sig_halted(struct connection *connection)
|
||||
{
|
||||
char sig_reply[4];
|
||||
snprintf(sig_reply, 4, "T%2.2x", 2);
|
||||
|
||||
@@ -47,7 +47,6 @@ static inline struct target *get_target_from_connection(struct connection *conne
|
||||
}
|
||||
|
||||
void gdb_set_frontend_state_running(struct connection *connection);
|
||||
void gdb_sig_halted(struct connection *connection);
|
||||
|
||||
#define ERROR_GDB_BUFFER_TOO_SMALL (-800)
|
||||
#define ERROR_GDB_TIMEOUT (-801)
|
||||
|
||||
+78
-21
@@ -46,9 +46,13 @@
|
||||
|
||||
static struct service *services;
|
||||
|
||||
/* shutdown_openocd == 1: exit the main event loop, and quit the
|
||||
* debugger; 2: quit with non-zero return code */
|
||||
static int shutdown_openocd;
|
||||
enum shutdown_reason {
|
||||
CONTINUE_MAIN_LOOP, /* stay in main event loop */
|
||||
SHUTDOWN_REQUESTED, /* set by shutdown command; exit the event loop and quit the debugger */
|
||||
SHUTDOWN_WITH_ERROR_CODE, /* set by shutdown command; quit with non-zero return code */
|
||||
SHUTDOWN_WITH_SIGNAL_CODE /* set by sig_handler; exec shutdown then exit with signal as return code */
|
||||
};
|
||||
static enum shutdown_reason shutdown_openocd = CONTINUE_MAIN_LOOP;
|
||||
|
||||
/* store received signal to exit application by killing ourselves */
|
||||
static int last_signal;
|
||||
@@ -360,6 +364,35 @@ static void remove_connections(struct service *service)
|
||||
}
|
||||
}
|
||||
|
||||
int remove_service(const char *name, const char *port)
|
||||
{
|
||||
struct service *tmp;
|
||||
struct service *prev;
|
||||
|
||||
prev = services;
|
||||
|
||||
for (tmp = services; tmp; prev = tmp, tmp = tmp->next) {
|
||||
if (!strcmp(tmp->name, name) && !strcmp(tmp->port, port)) {
|
||||
remove_connections(tmp);
|
||||
|
||||
if (tmp == services)
|
||||
services = tmp->next;
|
||||
else
|
||||
prev->next = tmp->next;
|
||||
|
||||
if (tmp->type != CONNECTION_STDINOUT)
|
||||
close_socket(tmp->fd);
|
||||
|
||||
free(tmp->priv);
|
||||
free_service(tmp);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int remove_services(void)
|
||||
{
|
||||
struct service *c = services;
|
||||
@@ -413,7 +446,7 @@ int server_loop(struct command_context *command_context)
|
||||
LOG_ERROR("couldn't set SIGPIPE to SIG_IGN");
|
||||
#endif
|
||||
|
||||
while (!shutdown_openocd) {
|
||||
while (shutdown_openocd == CONTINUE_MAIN_LOOP) {
|
||||
/* monitor sockets for activity */
|
||||
fd_max = 0;
|
||||
FD_ZERO(&read_fds);
|
||||
@@ -505,7 +538,7 @@ int server_loop(struct command_context *command_context)
|
||||
for (service = services; service; service = service->next) {
|
||||
/* handle new connections on listeners */
|
||||
if ((service->fd != -1)
|
||||
&& (FD_ISSET(service->fd, &read_fds))) {
|
||||
&& (FD_ISSET(service->fd, &read_fds))) {
|
||||
if (service->max_connections != 0)
|
||||
add_connection(service, command_context);
|
||||
else {
|
||||
@@ -537,7 +570,7 @@ int server_loop(struct command_context *command_context)
|
||||
service->type == CONNECTION_STDINOUT) {
|
||||
/* if connection uses a pipe then
|
||||
* shutdown openocd on error */
|
||||
shutdown_openocd = 1;
|
||||
shutdown_openocd = SHUTDOWN_REQUESTED;
|
||||
}
|
||||
remove_connection(service, c);
|
||||
LOG_INFO("dropped '%s' connection",
|
||||
@@ -555,30 +588,49 @@ int server_loop(struct command_context *command_context)
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT)
|
||||
shutdown_openocd = 1;
|
||||
shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return shutdown_openocd != 2 ? ERROR_OK : ERROR_FAIL;
|
||||
}
|
||||
/* when quit for signal or CTRL-C, run (eventually user implemented) "shutdown" */
|
||||
if (shutdown_openocd == SHUTDOWN_WITH_SIGNAL_CODE)
|
||||
command_run_line(command_context, "shutdown");
|
||||
|
||||
#ifdef _WIN32
|
||||
BOOL WINAPI ControlHandler(DWORD dwCtrlType)
|
||||
{
|
||||
shutdown_openocd = 1;
|
||||
return TRUE;
|
||||
return shutdown_openocd == SHUTDOWN_WITH_ERROR_CODE ? ERROR_FAIL : ERROR_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sig_handler(int sig)
|
||||
{
|
||||
/* store only first signal that hits us */
|
||||
if (!last_signal)
|
||||
if (shutdown_openocd == CONTINUE_MAIN_LOOP) {
|
||||
shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE;
|
||||
last_signal = sig;
|
||||
shutdown_openocd = 1;
|
||||
LOG_DEBUG("Terminating on Signal %d", sig);
|
||||
} else
|
||||
LOG_DEBUG("Ignored extra Signal %d", sig);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
BOOL WINAPI ControlHandler(DWORD dwCtrlType)
|
||||
{
|
||||
shutdown_openocd = SHUTDOWN_WITH_SIGNAL_CODE;
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
static void sigkey_handler(int sig)
|
||||
{
|
||||
/* ignore keystroke generated signals if not in foreground process group */
|
||||
|
||||
if (tcgetpgrp(STDIN_FILENO) > 0)
|
||||
sig_handler(sig);
|
||||
else
|
||||
LOG_DEBUG("Ignored Signal %d", sig);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int server_preinit(void)
|
||||
{
|
||||
/* this currently only calls WSAStartup on native win32 systems
|
||||
@@ -600,8 +652,13 @@ int server_preinit(void)
|
||||
SetConsoleCtrlHandler(ControlHandler, TRUE);
|
||||
|
||||
signal(SIGBREAK, sig_handler);
|
||||
#endif
|
||||
signal(SIGINT, sig_handler);
|
||||
#else
|
||||
signal(SIGHUP, sig_handler);
|
||||
signal(SIGPIPE, sig_handler);
|
||||
signal(SIGQUIT, sigkey_handler);
|
||||
signal(SIGINT, sigkey_handler);
|
||||
#endif
|
||||
signal(SIGTERM, sig_handler);
|
||||
signal(SIGABRT, sig_handler);
|
||||
|
||||
@@ -682,11 +739,11 @@ COMMAND_HANDLER(handle_shutdown_command)
|
||||
{
|
||||
LOG_USER("shutdown command invoked");
|
||||
|
||||
shutdown_openocd = 1;
|
||||
shutdown_openocd = SHUTDOWN_REQUESTED;
|
||||
|
||||
if (CMD_ARGC == 1) {
|
||||
if (!strcmp(CMD_ARGV[0], "error")) {
|
||||
shutdown_openocd = 2;
|
||||
shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE;
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
@@ -743,7 +800,7 @@ static const struct command_registration server_command_handlers[] = {
|
||||
.mode = COMMAND_ANY,
|
||||
.usage = "[name]",
|
||||
.help = "Specify address by name on which to listen for "
|
||||
"incoming TCP/IP connections",
|
||||
"incoming TCP/IP connections",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
@@ -78,6 +78,7 @@ int add_service(char *name, const char *port,
|
||||
int max_connections, new_connection_handler_t new_connection_handler,
|
||||
input_handler_t in_handler, connection_closed_handler_t close_handler,
|
||||
void *priv);
|
||||
int remove_service(const char *name, const char *port);
|
||||
|
||||
int server_preinit(void);
|
||||
int server_init(struct command_context *cmd_ctx);
|
||||
|
||||
@@ -4,7 +4,9 @@ else
|
||||
OOCD_TRACE_FILES =
|
||||
endif
|
||||
|
||||
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la
|
||||
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la \
|
||||
%D%/riscv/libriscv.la
|
||||
|
||||
|
||||
STARTUP_TCL_SRCS += %D%/startup.tcl
|
||||
|
||||
@@ -21,7 +23,6 @@ noinst_LTLIBRARIES += %D%/libtarget.la
|
||||
$(NDS32_SRC) \
|
||||
$(STM8_SRC) \
|
||||
$(INTEL_IA32_SRC) \
|
||||
$(RISCV_SRC) \
|
||||
%D%/avrt.c \
|
||||
%D%/dsp563xx.c \
|
||||
%D%/dsp563xx_once.c \
|
||||
@@ -40,6 +41,7 @@ TARGET_CORE_SRC = \
|
||||
%D%/target.c \
|
||||
%D%/target_request.c \
|
||||
%D%/testee.c \
|
||||
%D%/semihosting_common.c \
|
||||
%D%/smp.c
|
||||
|
||||
ARMV4_5_SRC = \
|
||||
@@ -136,13 +138,6 @@ INTEL_IA32_SRC = \
|
||||
%D%/lakemont.c \
|
||||
%D%/x86_32_common.c
|
||||
|
||||
RISCV_SRC = \
|
||||
%D%/riscv/riscv-011.c \
|
||||
%D%/riscv/riscv-013.c \
|
||||
%D%/riscv/riscv.c \
|
||||
%D%/riscv/program.c \
|
||||
%D%/riscv/batch.c
|
||||
|
||||
%C%_libtarget_la_SOURCES += \
|
||||
%D%/algorithm.h \
|
||||
%D%/arm.h \
|
||||
@@ -218,9 +213,11 @@ RISCV_SRC = \
|
||||
%D%/nds32_v3.h \
|
||||
%D%/nds32_v3m.h \
|
||||
%D%/nds32_aice.h \
|
||||
%D%/semihosting_common.h \
|
||||
%D%/stm8.h \
|
||||
%D%/lakemont.h \
|
||||
%D%/x86_32_common.h \
|
||||
%D%/arm_cti.h
|
||||
|
||||
include %D%/openrisc/Makefile.am
|
||||
include %D%/riscv/Makefile.am
|
||||
|
||||
+171
-3
@@ -28,6 +28,7 @@
|
||||
#include "target_type.h"
|
||||
#include "armv8_opcodes.h"
|
||||
#include "armv8_cache.h"
|
||||
#include "arm_semihosting.h"
|
||||
#include <helper/time_support.h>
|
||||
|
||||
enum restart_mode {
|
||||
@@ -522,6 +523,9 @@ static int aarch64_poll(struct target *target)
|
||||
if (target->smp)
|
||||
update_halt_gdb(target, debug_reason);
|
||||
|
||||
if (arm_semihosting(target, &retval) != 0)
|
||||
return retval;
|
||||
|
||||
switch (prev_target_state) {
|
||||
case TARGET_RUNNING:
|
||||
case TARGET_UNKNOWN:
|
||||
@@ -543,6 +547,9 @@ static int aarch64_poll(struct target *target)
|
||||
|
||||
static int aarch64_halt(struct target *target)
|
||||
{
|
||||
struct armv8_common *armv8 = target_to_armv8(target);
|
||||
armv8->last_run_control_op = ARMV8_RUNCONTROL_HALT;
|
||||
|
||||
if (target->smp)
|
||||
return aarch64_halt_smp(target, false);
|
||||
|
||||
@@ -831,6 +838,9 @@ static int aarch64_resume(struct target *target, int current,
|
||||
int retval = 0;
|
||||
uint64_t addr = address;
|
||||
|
||||
struct armv8_common *armv8 = target_to_armv8(target);
|
||||
armv8->last_run_control_op = ARMV8_RUNCONTROL_RESUME;
|
||||
|
||||
if (target->state != TARGET_HALTED)
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
|
||||
@@ -1069,6 +1079,8 @@ static int aarch64_step(struct target *target, int current, target_addr_t addres
|
||||
int retval;
|
||||
uint32_t edecr;
|
||||
|
||||
armv8->last_run_control_op = ARMV8_RUNCONTROL_STEP;
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_WARNING("target not halted");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
@@ -1682,17 +1694,19 @@ static int aarch64_deassert_reset(struct target *target)
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
retval = aarch64_init_debug_access(target);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (target->reset_halt) {
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_WARNING("%s: ran after reset and before halt ...",
|
||||
target_name(target));
|
||||
retval = target_halt(target);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
return aarch64_init_debug_access(target);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int aarch64_write_cpu_memory_slow(struct target *target,
|
||||
@@ -2351,6 +2365,7 @@ static int aarch64_init_target(struct command_context *cmd_ctx,
|
||||
struct target *target)
|
||||
{
|
||||
/* examine_first() does a bunch of this */
|
||||
arm_semihosting_init(target);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -2590,6 +2605,143 @@ COMMAND_HANDLER(aarch64_mask_interrupts_command)
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
|
||||
{
|
||||
struct command_context *context;
|
||||
struct target *target;
|
||||
struct arm *arm;
|
||||
int retval;
|
||||
bool is_mcr = false;
|
||||
int arg_cnt = 0;
|
||||
|
||||
if (Jim_CompareStringImmediate(interp, argv[0], "mcr")) {
|
||||
is_mcr = true;
|
||||
arg_cnt = 7;
|
||||
} else {
|
||||
arg_cnt = 6;
|
||||
}
|
||||
|
||||
context = current_command_context(interp);
|
||||
assert(context != NULL);
|
||||
|
||||
target = get_current_target(context);
|
||||
if (target == NULL) {
|
||||
LOG_ERROR("%s: no current target", __func__);
|
||||
return JIM_ERR;
|
||||
}
|
||||
if (!target_was_examined(target)) {
|
||||
LOG_ERROR("%s: not yet examined", target_name(target));
|
||||
return JIM_ERR;
|
||||
}
|
||||
|
||||
arm = target_to_arm(target);
|
||||
if (!is_arm(arm)) {
|
||||
LOG_ERROR("%s: not an ARM", target_name(target));
|
||||
return JIM_ERR;
|
||||
}
|
||||
|
||||
if (target->state != TARGET_HALTED)
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
|
||||
if (arm->core_state == ARM_STATE_AARCH64) {
|
||||
LOG_ERROR("%s: not 32-bit arm target", target_name(target));
|
||||
return JIM_ERR;
|
||||
}
|
||||
|
||||
if (argc != arg_cnt) {
|
||||
LOG_ERROR("%s: wrong number of arguments", __func__);
|
||||
return JIM_ERR;
|
||||
}
|
||||
|
||||
int cpnum;
|
||||
uint32_t op1;
|
||||
uint32_t op2;
|
||||
uint32_t CRn;
|
||||
uint32_t CRm;
|
||||
uint32_t value;
|
||||
long l;
|
||||
|
||||
/* NOTE: parameter sequence matches ARM instruction set usage:
|
||||
* MCR pNUM, op1, rX, CRn, CRm, op2 ; write CP from rX
|
||||
* MRC pNUM, op1, rX, CRn, CRm, op2 ; read CP into rX
|
||||
* The "rX" is necessarily omitted; it uses Tcl mechanisms.
|
||||
*/
|
||||
retval = Jim_GetLong(interp, argv[1], &l);
|
||||
if (retval != JIM_OK)
|
||||
return retval;
|
||||
if (l & ~0xf) {
|
||||
LOG_ERROR("%s: %s %d out of range", __func__,
|
||||
"coprocessor", (int) l);
|
||||
return JIM_ERR;
|
||||
}
|
||||
cpnum = l;
|
||||
|
||||
retval = Jim_GetLong(interp, argv[2], &l);
|
||||
if (retval != JIM_OK)
|
||||
return retval;
|
||||
if (l & ~0x7) {
|
||||
LOG_ERROR("%s: %s %d out of range", __func__,
|
||||
"op1", (int) l);
|
||||
return JIM_ERR;
|
||||
}
|
||||
op1 = l;
|
||||
|
||||
retval = Jim_GetLong(interp, argv[3], &l);
|
||||
if (retval != JIM_OK)
|
||||
return retval;
|
||||
if (l & ~0xf) {
|
||||
LOG_ERROR("%s: %s %d out of range", __func__,
|
||||
"CRn", (int) l);
|
||||
return JIM_ERR;
|
||||
}
|
||||
CRn = l;
|
||||
|
||||
retval = Jim_GetLong(interp, argv[4], &l);
|
||||
if (retval != JIM_OK)
|
||||
return retval;
|
||||
if (l & ~0xf) {
|
||||
LOG_ERROR("%s: %s %d out of range", __func__,
|
||||
"CRm", (int) l);
|
||||
return JIM_ERR;
|
||||
}
|
||||
CRm = l;
|
||||
|
||||
retval = Jim_GetLong(interp, argv[5], &l);
|
||||
if (retval != JIM_OK)
|
||||
return retval;
|
||||
if (l & ~0x7) {
|
||||
LOG_ERROR("%s: %s %d out of range", __func__,
|
||||
"op2", (int) l);
|
||||
return JIM_ERR;
|
||||
}
|
||||
op2 = l;
|
||||
|
||||
value = 0;
|
||||
|
||||
if (is_mcr == true) {
|
||||
retval = Jim_GetLong(interp, argv[6], &l);
|
||||
if (retval != JIM_OK)
|
||||
return retval;
|
||||
value = l;
|
||||
|
||||
/* NOTE: parameters reordered! */
|
||||
/* ARMV4_5_MCR(cpnum, op1, 0, CRn, CRm, op2) */
|
||||
retval = arm->mcr(target, cpnum, op1, op2, CRn, CRm, value);
|
||||
if (retval != ERROR_OK)
|
||||
return JIM_ERR;
|
||||
} else {
|
||||
/* NOTE: parameters reordered! */
|
||||
/* ARMV4_5_MRC(cpnum, op1, 0, CRn, CRm, op2) */
|
||||
retval = arm->mrc(target, cpnum, op1, op2, CRn, CRm, &value);
|
||||
if (retval != ERROR_OK)
|
||||
return JIM_ERR;
|
||||
|
||||
Jim_SetResult(interp, Jim_NewIntObj(interp, value));
|
||||
}
|
||||
|
||||
return JIM_OK;
|
||||
}
|
||||
|
||||
static const struct command_registration aarch64_exec_command_handlers[] = {
|
||||
{
|
||||
.name = "cache_info",
|
||||
@@ -2625,9 +2777,25 @@ static const struct command_registration aarch64_exec_command_handlers[] = {
|
||||
.help = "mask aarch64 interrupts during single-step",
|
||||
.usage = "['on'|'off']",
|
||||
},
|
||||
{
|
||||
.name = "mcr",
|
||||
.mode = COMMAND_EXEC,
|
||||
.jim_handler = jim_mcrmrc,
|
||||
.help = "write coprocessor register",
|
||||
.usage = "cpnum op1 CRn CRm op2 value",
|
||||
},
|
||||
{
|
||||
.name = "mrc",
|
||||
.mode = COMMAND_EXEC,
|
||||
.jim_handler = jim_mcrmrc,
|
||||
.help = "read coprocessor register",
|
||||
.usage = "cpnum op1 CRn CRm op2",
|
||||
},
|
||||
|
||||
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
static const struct command_registration aarch64_command_handlers[] = {
|
||||
{
|
||||
.chain = armv8_command_handlers,
|
||||
|
||||
@@ -276,6 +276,16 @@ static int swd_run(struct adiv5_dap *dap)
|
||||
return swd_run_inner(dap);
|
||||
}
|
||||
|
||||
/** Put the SWJ-DP back to JTAG mode */
|
||||
static void swd_quit(struct adiv5_dap *dap)
|
||||
{
|
||||
const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
|
||||
|
||||
swd->switch_seq(SWD_TO_JTAG);
|
||||
/* flush the queue before exit */
|
||||
swd->run();
|
||||
}
|
||||
|
||||
const struct dap_ops swd_dap_ops = {
|
||||
.connect = swd_connect,
|
||||
.queue_dp_read = swd_queue_dp_read,
|
||||
@@ -284,6 +294,7 @@ const struct dap_ops swd_dap_ops = {
|
||||
.queue_ap_write = swd_queue_ap_write,
|
||||
.queue_ap_abort = swd_queue_ap_abort,
|
||||
.run = swd_run,
|
||||
.quit = swd_quit,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+3
-22
@@ -8,6 +8,9 @@
|
||||
* Copyright (C) 2009 by Øyvind Harboe
|
||||
* oyvind.harboe@zylin.com
|
||||
*
|
||||
* Copyright (C) 2018 by Liviu Ionescu
|
||||
* <ilg@livius.net>
|
||||
*
|
||||
* 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
|
||||
@@ -28,7 +31,6 @@
|
||||
#include <helper/command.h>
|
||||
#include "target.h"
|
||||
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Holds the interface to ARM cores.
|
||||
@@ -181,32 +183,11 @@ struct arm {
|
||||
/** Flag reporting armv6m based core. */
|
||||
bool is_armv6m;
|
||||
|
||||
/** Flag reporting whether semihosting is active. */
|
||||
bool is_semihosting;
|
||||
|
||||
/** Flag reporting whether semihosting fileio is active. */
|
||||
bool is_semihosting_fileio;
|
||||
|
||||
/** Flag reporting whether semihosting fileio operation is active. */
|
||||
bool semihosting_hit_fileio;
|
||||
|
||||
/** Floating point or VFP version, 0 if disabled. */
|
||||
int arm_vfp_version;
|
||||
|
||||
/** Current semihosting operation. */
|
||||
int semihosting_op;
|
||||
|
||||
/** Current semihosting result. */
|
||||
int semihosting_result;
|
||||
|
||||
/** Value to be returned by semihosting SYS_ERRNO request. */
|
||||
int semihosting_errno;
|
||||
|
||||
int (*setup_semihosting)(struct target *target, int enable);
|
||||
|
||||
/** Semihosting command line. */
|
||||
char *semihosting_cmdline;
|
||||
|
||||
/** Backpointer to the target. */
|
||||
struct target *target;
|
||||
|
||||
|
||||
+69
-22
@@ -102,8 +102,10 @@ static int mem_ap_setup_csw(struct adiv5_ap *ap, uint32_t csw)
|
||||
if (csw != ap->csw_value) {
|
||||
/* LOG_DEBUG("DAP: Set CSW %x",csw); */
|
||||
int retval = dap_queue_ap_write(ap, MEM_AP_REG_CSW, csw);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
ap->csw_value = 0;
|
||||
return retval;
|
||||
}
|
||||
ap->csw_value = csw;
|
||||
}
|
||||
return ERROR_OK;
|
||||
@@ -114,8 +116,10 @@ static int mem_ap_setup_tar(struct adiv5_ap *ap, uint32_t tar)
|
||||
if (!ap->tar_valid || tar != ap->tar_value) {
|
||||
/* LOG_DEBUG("DAP: Set TAR %x",tar); */
|
||||
int retval = dap_queue_ap_write(ap, MEM_AP_REG_TAR, tar);
|
||||
if (retval != ERROR_OK)
|
||||
if (retval != ERROR_OK) {
|
||||
ap->tar_valid = false;
|
||||
return retval;
|
||||
}
|
||||
ap->tar_value = tar;
|
||||
ap->tar_valid = true;
|
||||
}
|
||||
@@ -152,6 +156,8 @@ static uint32_t mem_ap_get_tar_increment(struct adiv5_ap *ap)
|
||||
return 2;
|
||||
case CSW_32BIT:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case CSW_ADDRINC_PACKED:
|
||||
return 4;
|
||||
@@ -1610,13 +1616,12 @@ COMMAND_HANDLER(dap_memaccess_command)
|
||||
COMMAND_HANDLER(dap_apsel_command)
|
||||
{
|
||||
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||
uint32_t apsel, apid;
|
||||
int retval;
|
||||
uint32_t apsel;
|
||||
|
||||
switch (CMD_ARGC) {
|
||||
case 0:
|
||||
apsel = dap->apsel;
|
||||
break;
|
||||
command_print(CMD_CTX, "%" PRIi32, dap->apsel);
|
||||
return ERROR_OK;
|
||||
case 1:
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel);
|
||||
/* AP address is in bits 31:24 of DP_SELECT */
|
||||
@@ -1628,18 +1633,7 @@ COMMAND_HANDLER(dap_apsel_command)
|
||||
}
|
||||
|
||||
dap->apsel = apsel;
|
||||
|
||||
retval = dap_queue_ap_read(dap_ap(dap, apsel), AP_REG_IDR, &apid);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
retval = dap_run(dap);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
command_print(CMD_CTX, "ap %" PRIi32 " selected, identification register 0x%8.8" PRIx32,
|
||||
apsel, apid);
|
||||
|
||||
return retval;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(dap_apcsw_command)
|
||||
@@ -1720,6 +1714,7 @@ COMMAND_HANDLER(dap_apreg_command)
|
||||
{
|
||||
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||
uint32_t apsel, reg, value;
|
||||
struct adiv5_ap *ap;
|
||||
int retval;
|
||||
|
||||
if (CMD_ARGC < 2 || CMD_ARGC > 3)
|
||||
@@ -1729,6 +1724,7 @@ COMMAND_HANDLER(dap_apreg_command)
|
||||
/* AP address is in bits 31:24 of DP_SELECT */
|
||||
if (apsel >= 256)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
ap = dap_ap(dap, apsel);
|
||||
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg);
|
||||
if (reg >= 256 || (reg & 3))
|
||||
@@ -1736,9 +1732,21 @@ COMMAND_HANDLER(dap_apreg_command)
|
||||
|
||||
if (CMD_ARGC == 3) {
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value);
|
||||
retval = dap_queue_ap_write(dap_ap(dap, apsel), reg, value);
|
||||
switch (reg) {
|
||||
case MEM_AP_REG_CSW:
|
||||
ap->csw_default = 0; /* invalid, force write */
|
||||
retval = mem_ap_setup_csw(ap, value);
|
||||
break;
|
||||
case MEM_AP_REG_TAR:
|
||||
ap->tar_valid = false; /* invalid, force write */
|
||||
retval = mem_ap_setup_tar(ap, value);
|
||||
break;
|
||||
default:
|
||||
retval = dap_queue_ap_write(ap, reg, value);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
retval = dap_queue_ap_read(dap_ap(dap, apsel), reg, &value);
|
||||
retval = dap_queue_ap_read(ap, reg, &value);
|
||||
}
|
||||
if (retval == ERROR_OK)
|
||||
retval = dap_run(dap);
|
||||
@@ -1752,6 +1760,37 @@ COMMAND_HANDLER(dap_apreg_command)
|
||||
return retval;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(dap_dpreg_command)
|
||||
{
|
||||
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||
uint32_t reg, value;
|
||||
int retval;
|
||||
|
||||
if (CMD_ARGC < 1 || CMD_ARGC > 2)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], reg);
|
||||
if (reg >= 256 || (reg & 3))
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
if (CMD_ARGC == 2) {
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
|
||||
retval = dap_queue_dp_write(dap, reg, value);
|
||||
} else {
|
||||
retval = dap_queue_dp_read(dap, reg, &value);
|
||||
}
|
||||
if (retval == ERROR_OK)
|
||||
retval = dap_run(dap);
|
||||
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (CMD_ARGC == 1)
|
||||
command_print(CMD_CTX, "0x%08" PRIx32, value);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(dap_ti_be_32_quirks_command)
|
||||
{
|
||||
struct adiv5_dap *dap = adiv5_get_dap(CMD_DATA);
|
||||
@@ -1787,7 +1826,7 @@ const struct command_registration dap_instance_commands[] = {
|
||||
{
|
||||
.name = "apsel",
|
||||
.handler = dap_apsel_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "Set the currently selected AP (default 0) "
|
||||
"and display the result",
|
||||
.usage = "[ap_num]",
|
||||
@@ -1795,7 +1834,7 @@ const struct command_registration dap_instance_commands[] = {
|
||||
{
|
||||
.name = "apcsw",
|
||||
.handler = dap_apcsw_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "Set CSW default bits",
|
||||
.usage = "[value [mask]]",
|
||||
},
|
||||
@@ -1816,6 +1855,14 @@ const struct command_registration dap_instance_commands[] = {
|
||||
"(reg is byte address of a word register, like 0 4 8...)",
|
||||
.usage = "ap_num reg [value]",
|
||||
},
|
||||
{
|
||||
.name = "dpreg",
|
||||
.handler = dap_dpreg_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "read/write a register from DP "
|
||||
"(reg is byte address (bank << 4 | reg) of a word register, like 0 4 8...)",
|
||||
.usage = "reg [value]",
|
||||
},
|
||||
{
|
||||
.name = "baseaddr",
|
||||
.handler = dap_baseaddr_command,
|
||||
|
||||
@@ -286,6 +286,9 @@ struct dap_ops {
|
||||
/** Executes all queued DAP operations but doesn't check
|
||||
* sticky error conditions */
|
||||
int (*sync)(struct adiv5_dap *dap);
|
||||
|
||||
/** Optional; called at OpenOCD exit */
|
||||
void (*quit)(struct adiv5_dap *dap);
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -132,8 +132,13 @@ static int dap_init_all(void)
|
||||
int dap_cleanup_all(void)
|
||||
{
|
||||
struct arm_dap_object *obj, *tmp;
|
||||
struct adiv5_dap *dap;
|
||||
|
||||
list_for_each_entry_safe(obj, tmp, &all_dap, lh) {
|
||||
dap = &obj->dap;
|
||||
if (dap->ops && dap->ops->quit)
|
||||
dap->ops->quit(dap);
|
||||
|
||||
free(obj->name);
|
||||
free(obj);
|
||||
}
|
||||
|
||||
@@ -118,15 +118,78 @@ static int evaluate_pld(uint32_t opcode,
|
||||
uint32_t address, struct arm_instruction *instruction)
|
||||
{
|
||||
/* PLD */
|
||||
if ((opcode & 0x0d70f000) == 0x0550f000) {
|
||||
if ((opcode & 0x0d30f000) == 0x0510f000) {
|
||||
uint8_t Rn;
|
||||
uint8_t U;
|
||||
unsigned offset;
|
||||
|
||||
instruction->type = ARM_PLD;
|
||||
Rn = (opcode & 0xf0000) >> 16;
|
||||
U = (opcode & 0x00800000) >> 23;
|
||||
if (Rn == 0xf) {
|
||||
/* literal */
|
||||
offset = opcode & 0x0fff;
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD %s%d",
|
||||
address, opcode, U ? "" : "-", offset);
|
||||
} else {
|
||||
uint8_t I, R;
|
||||
|
||||
snprintf(instruction->text,
|
||||
128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD ...TODO...",
|
||||
address,
|
||||
opcode);
|
||||
I = (opcode & 0x02000000) >> 25;
|
||||
R = (opcode & 0x00400000) >> 22;
|
||||
|
||||
if (I) {
|
||||
/* register PLD{W} [<Rn>,+/-<Rm>{, <shift>}] */
|
||||
offset = (opcode & 0x0F80) >> 7;
|
||||
uint8_t Rm;
|
||||
Rm = opcode & 0xf;
|
||||
|
||||
if (offset == 0) {
|
||||
/* No shift */
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d]",
|
||||
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm);
|
||||
|
||||
} else {
|
||||
uint8_t shift;
|
||||
shift = (opcode & 0x60) >> 5;
|
||||
|
||||
if (shift == 0x0) {
|
||||
/* LSL */
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, LSL #0x%x)",
|
||||
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
|
||||
} else if (shift == 0x1) {
|
||||
/* LSR */
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, LSR #0x%x)",
|
||||
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
|
||||
} else if (shift == 0x2) {
|
||||
/* ASR */
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, ASR #0x%x)",
|
||||
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
|
||||
} else if (shift == 0x3) {
|
||||
/* ROR */
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, %sr%d, ROR #0x%x)",
|
||||
address, opcode, R ? "" : "W", Rn, U ? "" : "-", Rm, offset);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* immediate PLD{W} [<Rn>, #+/-<imm12>] */
|
||||
offset = opcode & 0x0fff;
|
||||
if (offset == 0) {
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d]",
|
||||
address, opcode, R ? "" : "W", Rn);
|
||||
} else {
|
||||
snprintf(instruction->text, 128,
|
||||
"0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tPLD%s [r%d, #%s%d]",
|
||||
address, opcode, R ? "" : "W", Rn, U ? "" : "-", offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
/* DSB */
|
||||
@@ -1549,7 +1612,7 @@ static int evaluate_misc_instr(uint32_t opcode,
|
||||
}
|
||||
|
||||
/* SMLAW < y> */
|
||||
if (((opcode & 0x00600000) == 0x00100000) && (x == 0)) {
|
||||
if (((opcode & 0x00600000) == 0x00200000) && (x == 0)) {
|
||||
uint8_t Rd, Rm, Rs, Rn;
|
||||
instruction->type = ARM_SMLAWy;
|
||||
Rd = (opcode & 0xf0000) >> 16;
|
||||
@@ -1571,7 +1634,7 @@ static int evaluate_misc_instr(uint32_t opcode,
|
||||
}
|
||||
|
||||
/* SMUL < x><y> */
|
||||
if ((opcode & 0x00600000) == 0x00300000) {
|
||||
if ((opcode & 0x00600000) == 0x00600000) {
|
||||
uint8_t Rd, Rm, Rs;
|
||||
instruction->type = ARM_SMULxy;
|
||||
Rd = (opcode & 0xf0000) >> 16;
|
||||
@@ -1592,7 +1655,7 @@ static int evaluate_misc_instr(uint32_t opcode,
|
||||
}
|
||||
|
||||
/* SMULW < y> */
|
||||
if (((opcode & 0x00600000) == 0x00100000) && (x == 1)) {
|
||||
if (((opcode & 0x00600000) == 0x00200000) && (x == 1)) {
|
||||
uint8_t Rd, Rm, Rs;
|
||||
instruction->type = ARM_SMULWy;
|
||||
Rd = (opcode & 0xf0000) >> 16;
|
||||
|
||||
+92
-555
@@ -8,6 +8,9 @@
|
||||
* Copyright (C) 2016 by Square, Inc. *
|
||||
* Steven Stallion <stallion@squareup.com> *
|
||||
* *
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -43,6 +46,7 @@
|
||||
#include "arm7_9_common.h"
|
||||
#include "armv7m.h"
|
||||
#include "armv7a.h"
|
||||
#include "armv8.h"
|
||||
#include "cortex_m.h"
|
||||
#include "register.h"
|
||||
#include "arm_opcodes.h"
|
||||
@@ -52,25 +56,35 @@
|
||||
#include <helper/log.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static const int open_modeflags[12] = {
|
||||
O_RDONLY,
|
||||
O_RDONLY | O_BINARY,
|
||||
O_RDWR,
|
||||
O_RDWR | O_BINARY,
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
||||
O_RDWR | O_CREAT | O_TRUNC,
|
||||
O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
|
||||
O_WRONLY | O_CREAT | O_APPEND,
|
||||
O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
|
||||
O_RDWR | O_CREAT | O_APPEND,
|
||||
O_RDWR | O_CREAT | O_APPEND | O_BINARY
|
||||
};
|
||||
static int arm_semihosting_resume(struct target *target, int *retval)
|
||||
{
|
||||
if (is_armv8(target_to_armv8(target))) {
|
||||
struct armv8_common *armv8 = target_to_armv8(target);
|
||||
if (armv8->last_run_control_op == ARMV8_RUNCONTROL_RESUME) {
|
||||
*retval = target_resume(target, 1, 0, 0, 0);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to resume target");
|
||||
return 0;
|
||||
}
|
||||
} else if (armv8->last_run_control_op == ARMV8_RUNCONTROL_STEP)
|
||||
target->debug_reason = DBG_REASON_SINGLESTEP;
|
||||
} else {
|
||||
*retval = target_resume(target, 1, 0, 0, 0);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to resume target");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int post_result(struct target *target)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
if (!target->semihosting)
|
||||
return ERROR_FAIL;
|
||||
|
||||
/* REVISIT this looks wrong ... ARM11 and Cortex-A8
|
||||
* should work this way at least sometimes.
|
||||
*/
|
||||
@@ -79,7 +93,7 @@ static int post_result(struct target *target)
|
||||
uint32_t spsr;
|
||||
|
||||
/* return value in R0 */
|
||||
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result);
|
||||
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
|
||||
arm->core_cache->reg_list[0].dirty = 1;
|
||||
|
||||
/* LR --> PC */
|
||||
@@ -100,528 +114,28 @@ static int post_result(struct target *target)
|
||||
if (spsr & 0x20)
|
||||
arm->core_state = ARM_STATE_THUMB;
|
||||
|
||||
} else if (is_armv8(target_to_armv8(target))) {
|
||||
if (arm->core_state == ARM_STATE_AARCH64) {
|
||||
/* return value in R0 */
|
||||
buf_set_u64(arm->core_cache->reg_list[0].value, 0, 64, target->semihosting->result);
|
||||
arm->core_cache->reg_list[0].dirty = 1;
|
||||
|
||||
uint64_t pc = buf_get_u64(arm->core_cache->reg_list[32].value, 0, 64);
|
||||
buf_set_u64(arm->pc->value, 0, 64, pc + 4);
|
||||
arm->pc->dirty = 1;
|
||||
}
|
||||
} else {
|
||||
/* resume execution, this will be pc+2 to skip over the
|
||||
* bkpt instruction */
|
||||
|
||||
/* return result in R0 */
|
||||
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, arm->semihosting_result);
|
||||
buf_set_u32(arm->core_cache->reg_list[0].value, 0, 32, target->semihosting->result);
|
||||
arm->core_cache->reg_list[0].dirty = 1;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int do_semihosting(struct target *target)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
struct gdb_fileio_info *fileio_info = target->fileio_info;
|
||||
uint32_t r0 = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
|
||||
uint32_t r1 = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
|
||||
uint8_t params[16];
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* TODO: lots of security issues are not considered yet, such as:
|
||||
* - no validation on target provided file descriptors
|
||||
* - no safety checks on opened/deleted/renamed file paths
|
||||
* Beware the target app you use this support with.
|
||||
*
|
||||
* TODO: unsupported semihosting fileio operations could be
|
||||
* implemented if we had a small working area at our disposal.
|
||||
*/
|
||||
switch ((arm->semihosting_op = r0)) {
|
||||
case 0x01: /* SYS_OPEN */
|
||||
retval = target_read_memory(target, r1, 4, 3, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a = target_buffer_get_u32(target, params+0);
|
||||
uint32_t m = target_buffer_get_u32(target, params+4);
|
||||
uint32_t l = target_buffer_get_u32(target, params+8);
|
||||
uint8_t fn[256];
|
||||
retval = target_read_memory(target, a, 1, l, fn);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
fn[l] = 0;
|
||||
if (arm->is_semihosting_fileio) {
|
||||
if (strcmp((char *)fn, ":tt") == 0)
|
||||
arm->semihosting_result = 0;
|
||||
else {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "open";
|
||||
fileio_info->param_1 = a;
|
||||
fileio_info->param_2 = l;
|
||||
fileio_info->param_3 = open_modeflags[m];
|
||||
fileio_info->param_4 = 0644;
|
||||
}
|
||||
} else {
|
||||
if (l <= 255 && m <= 11) {
|
||||
if (strcmp((char *)fn, ":tt") == 0) {
|
||||
if (m < 4)
|
||||
arm->semihosting_result = dup(STDIN_FILENO);
|
||||
else
|
||||
arm->semihosting_result = dup(STDOUT_FILENO);
|
||||
} else {
|
||||
/* cygwin requires the permission setting
|
||||
* otherwise it will fail to reopen a previously
|
||||
* written file */
|
||||
arm->semihosting_result = open((char *)fn, open_modeflags[m], 0644);
|
||||
}
|
||||
arm->semihosting_errno = errno;
|
||||
} else {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02: /* SYS_CLOSE */
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "close";
|
||||
fileio_info->param_1 = fd;
|
||||
} else {
|
||||
arm->semihosting_result = close(fd);
|
||||
arm->semihosting_errno = errno;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03: /* SYS_WRITEC */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "write";
|
||||
fileio_info->param_1 = 1;
|
||||
fileio_info->param_2 = r1;
|
||||
fileio_info->param_3 = 1;
|
||||
} else {
|
||||
unsigned char c;
|
||||
retval = target_read_memory(target, r1, 1, 1, &c);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
putchar(c);
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04: /* SYS_WRITE0 */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
size_t count = 0;
|
||||
for (uint32_t a = r1;; a++) {
|
||||
unsigned char c;
|
||||
retval = target_read_memory(target, a, 1, 1, &c);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
if (c == '\0')
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "write";
|
||||
fileio_info->param_1 = 1;
|
||||
fileio_info->param_2 = r1;
|
||||
fileio_info->param_3 = count;
|
||||
} else {
|
||||
do {
|
||||
unsigned char c;
|
||||
retval = target_read_memory(target, r1++, 1, 1, &c);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
if (!c)
|
||||
break;
|
||||
putchar(c);
|
||||
} while (1);
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x05: /* SYS_WRITE */
|
||||
retval = target_read_memory(target, r1, 4, 3, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
uint32_t a = target_buffer_get_u32(target, params+4);
|
||||
size_t l = target_buffer_get_u32(target, params+8);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "write";
|
||||
fileio_info->param_1 = fd;
|
||||
fileio_info->param_2 = a;
|
||||
fileio_info->param_3 = l;
|
||||
} else {
|
||||
uint8_t *buf = malloc(l);
|
||||
if (!buf) {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = ENOMEM;
|
||||
} else {
|
||||
retval = target_read_buffer(target, a, l, buf);
|
||||
if (retval != ERROR_OK) {
|
||||
free(buf);
|
||||
return retval;
|
||||
}
|
||||
arm->semihosting_result = write(fd, buf, l);
|
||||
arm->semihosting_errno = errno;
|
||||
if (arm->semihosting_result >= 0)
|
||||
arm->semihosting_result = l - arm->semihosting_result;
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x06: /* SYS_READ */
|
||||
retval = target_read_memory(target, r1, 4, 3, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
uint32_t a = target_buffer_get_u32(target, params+4);
|
||||
ssize_t l = target_buffer_get_u32(target, params+8);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "read";
|
||||
fileio_info->param_1 = fd;
|
||||
fileio_info->param_2 = a;
|
||||
fileio_info->param_3 = l;
|
||||
} else {
|
||||
uint8_t *buf = malloc(l);
|
||||
if (!buf) {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = ENOMEM;
|
||||
} else {
|
||||
arm->semihosting_result = read(fd, buf, l);
|
||||
arm->semihosting_errno = errno;
|
||||
if (arm->semihosting_result >= 0) {
|
||||
retval = target_write_buffer(target, a, arm->semihosting_result, buf);
|
||||
if (retval != ERROR_OK) {
|
||||
free(buf);
|
||||
return retval;
|
||||
}
|
||||
arm->semihosting_result = l - arm->semihosting_result;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x07: /* SYS_READC */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
LOG_ERROR("SYS_READC not supported by semihosting fileio");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
arm->semihosting_result = getchar();
|
||||
break;
|
||||
|
||||
case 0x08: /* SYS_ISERROR */
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
arm->semihosting_result = (target_buffer_get_u32(target, params+0) != 0);
|
||||
break;
|
||||
|
||||
case 0x09: /* SYS_ISTTY */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "isatty";
|
||||
fileio_info->param_1 = r1;
|
||||
} else {
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
arm->semihosting_result = isatty(target_buffer_get_u32(target, params+0));
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0a: /* SYS_SEEK */
|
||||
retval = target_read_memory(target, r1, 4, 2, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
off_t pos = target_buffer_get_u32(target, params+4);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "lseek";
|
||||
fileio_info->param_1 = fd;
|
||||
fileio_info->param_2 = pos;
|
||||
fileio_info->param_3 = SEEK_SET;
|
||||
} else {
|
||||
arm->semihosting_result = lseek(fd, pos, SEEK_SET);
|
||||
arm->semihosting_errno = errno;
|
||||
if (arm->semihosting_result == pos)
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0c: /* SYS_FLEN */
|
||||
if (arm->is_semihosting_fileio) {
|
||||
LOG_ERROR("SYS_FLEN not supported by semihosting fileio");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
int fd = target_buffer_get_u32(target, params+0);
|
||||
struct stat buf;
|
||||
arm->semihosting_result = fstat(fd, &buf);
|
||||
if (arm->semihosting_result == -1) {
|
||||
arm->semihosting_errno = errno;
|
||||
arm->semihosting_result = -1;
|
||||
break;
|
||||
}
|
||||
arm->semihosting_result = buf.st_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0e: /* SYS_REMOVE */
|
||||
retval = target_read_memory(target, r1, 4, 2, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a = target_buffer_get_u32(target, params+0);
|
||||
uint32_t l = target_buffer_get_u32(target, params+4);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "unlink";
|
||||
fileio_info->param_1 = a;
|
||||
fileio_info->param_2 = l;
|
||||
} else {
|
||||
if (l <= 255) {
|
||||
uint8_t fn[256];
|
||||
retval = target_read_memory(target, a, 1, l, fn);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
fn[l] = 0;
|
||||
arm->semihosting_result = remove((char *)fn);
|
||||
arm->semihosting_errno = errno;
|
||||
} else {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0f: /* SYS_RENAME */
|
||||
retval = target_read_memory(target, r1, 4, 4, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a1 = target_buffer_get_u32(target, params+0);
|
||||
uint32_t l1 = target_buffer_get_u32(target, params+4);
|
||||
uint32_t a2 = target_buffer_get_u32(target, params+8);
|
||||
uint32_t l2 = target_buffer_get_u32(target, params+12);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "rename";
|
||||
fileio_info->param_1 = a1;
|
||||
fileio_info->param_2 = l1;
|
||||
fileio_info->param_3 = a2;
|
||||
fileio_info->param_4 = l2;
|
||||
} else {
|
||||
if (l1 <= 255 && l2 <= 255) {
|
||||
uint8_t fn1[256], fn2[256];
|
||||
retval = target_read_memory(target, a1, 1, l1, fn1);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
retval = target_read_memory(target, a2, 1, l2, fn2);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
fn1[l1] = 0;
|
||||
fn2[l2] = 0;
|
||||
arm->semihosting_result = rename((char *)fn1, (char *)fn2);
|
||||
arm->semihosting_errno = errno;
|
||||
} else {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x11: /* SYS_TIME */
|
||||
arm->semihosting_result = time(NULL);
|
||||
break;
|
||||
|
||||
case 0x13: /* SYS_ERRNO */
|
||||
arm->semihosting_result = arm->semihosting_errno;
|
||||
break;
|
||||
|
||||
case 0x15: /* SYS_GET_CMDLINE */
|
||||
retval = target_read_memory(target, r1, 4, 2, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a = target_buffer_get_u32(target, params+0);
|
||||
uint32_t l = target_buffer_get_u32(target, params+4);
|
||||
char *arg = arm->semihosting_cmdline != NULL ? arm->semihosting_cmdline : "";
|
||||
uint32_t s = strlen(arg) + 1;
|
||||
if (l < s)
|
||||
arm->semihosting_result = -1;
|
||||
else {
|
||||
retval = target_write_buffer(target, a, s, (uint8_t *)arg);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x16: /* SYS_HEAPINFO */
|
||||
retval = target_read_memory(target, r1, 4, 1, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t a = target_buffer_get_u32(target, params+0);
|
||||
/* tell the remote we have no idea */
|
||||
memset(params, 0, 4*4);
|
||||
retval = target_write_memory(target, a, 4, 4, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
arm->semihosting_result = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x18: /* angel_SWIreason_ReportException */
|
||||
switch (r1) {
|
||||
case 0x20026: /* ADP_Stopped_ApplicationExit */
|
||||
fprintf(stderr, "semihosting: *** application exited ***\n");
|
||||
break;
|
||||
case 0x20000: /* ADP_Stopped_BranchThroughZero */
|
||||
case 0x20001: /* ADP_Stopped_UndefinedInstr */
|
||||
case 0x20002: /* ADP_Stopped_SoftwareInterrupt */
|
||||
case 0x20003: /* ADP_Stopped_PrefetchAbort */
|
||||
case 0x20004: /* ADP_Stopped_DataAbort */
|
||||
case 0x20005: /* ADP_Stopped_AddressException */
|
||||
case 0x20006: /* ADP_Stopped_IRQ */
|
||||
case 0x20007: /* ADP_Stopped_FIQ */
|
||||
case 0x20020: /* ADP_Stopped_BreakPoint */
|
||||
case 0x20021: /* ADP_Stopped_WatchPoint */
|
||||
case 0x20022: /* ADP_Stopped_StepComplete */
|
||||
case 0x20023: /* ADP_Stopped_RunTimeErrorUnknown */
|
||||
case 0x20024: /* ADP_Stopped_InternalError */
|
||||
case 0x20025: /* ADP_Stopped_UserInterruption */
|
||||
case 0x20027: /* ADP_Stopped_StackOverflow */
|
||||
case 0x20028: /* ADP_Stopped_DivisionByZero */
|
||||
case 0x20029: /* ADP_Stopped_OSSpecific */
|
||||
default:
|
||||
fprintf(stderr, "semihosting: exception %#x\n",
|
||||
(unsigned) r1);
|
||||
}
|
||||
return target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||
|
||||
case 0x12: /* SYS_SYSTEM */
|
||||
/* Provide SYS_SYSTEM functionality. Uses the
|
||||
* libc system command, there may be a reason *NOT*
|
||||
* to use this, but as I can't think of one, I
|
||||
* implemented it this way.
|
||||
*/
|
||||
retval = target_read_memory(target, r1, 4, 2, params);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else {
|
||||
uint32_t len = target_buffer_get_u32(target, params+4);
|
||||
uint32_t c_ptr = target_buffer_get_u32(target, params);
|
||||
if (arm->is_semihosting_fileio) {
|
||||
arm->semihosting_hit_fileio = true;
|
||||
fileio_info->identifier = "system";
|
||||
fileio_info->param_1 = c_ptr;
|
||||
fileio_info->param_2 = len;
|
||||
} else {
|
||||
uint8_t cmd[256];
|
||||
if (len > 255) {
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = EINVAL;
|
||||
} else {
|
||||
memset(cmd, 0x0, 256);
|
||||
retval = target_read_memory(target, c_ptr, 1, len, cmd);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
else
|
||||
arm->semihosting_result = system((const char *)cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x0d: /* SYS_TMPNAM */
|
||||
case 0x10: /* SYS_CLOCK */
|
||||
case 0x17: /* angel_SWIreason_EnterSVC */
|
||||
case 0x30: /* SYS_ELAPSED */
|
||||
case 0x31: /* SYS_TICKFREQ */
|
||||
default:
|
||||
fprintf(stderr, "semihosting: unsupported call %#x\n",
|
||||
(unsigned) r0);
|
||||
arm->semihosting_result = -1;
|
||||
arm->semihosting_errno = ENOTSUP;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fileio_info)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
/* To avoid uneccessary duplication, semihosting prepares the
|
||||
* fileio_info structure out-of-band when the target halts. See
|
||||
* do_semihosting for more detail.
|
||||
*/
|
||||
if (!arm->is_semihosting_fileio || !arm->semihosting_hit_fileio)
|
||||
return ERROR_FAIL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int gdb_fileio_end(struct target *target, int result, int fileio_errno, bool ctrl_c)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
struct gdb_fileio_info *fileio_info = target->fileio_info;
|
||||
|
||||
/* clear pending status */
|
||||
arm->semihosting_hit_fileio = false;
|
||||
|
||||
arm->semihosting_result = result;
|
||||
arm->semihosting_errno = fileio_errno;
|
||||
|
||||
/* Some fileio results do not match up with what the semihosting
|
||||
* operation expects; for these operations, we munge the results
|
||||
* below:
|
||||
*/
|
||||
switch (arm->semihosting_op) {
|
||||
case 0x05: /* SYS_WRITE */
|
||||
if (result < 0)
|
||||
arm->semihosting_result = fileio_info->param_3;
|
||||
else
|
||||
arm->semihosting_result = 0;
|
||||
break;
|
||||
|
||||
case 0x06: /* SYS_READ */
|
||||
if (result == (int)fileio_info->param_3)
|
||||
arm->semihosting_result = 0;
|
||||
if (result <= 0)
|
||||
arm->semihosting_result = fileio_info->param_3;
|
||||
break;
|
||||
|
||||
case 0x0a: /* SYS_SEEK */
|
||||
if (result > 0)
|
||||
arm->semihosting_result = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return post_result(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize ARM semihosting support.
|
||||
*
|
||||
@@ -630,14 +144,9 @@ static int gdb_fileio_end(struct target *target, int result, int fileio_errno, b
|
||||
*/
|
||||
int arm_semihosting_init(struct target *target)
|
||||
{
|
||||
target->fileio_info = malloc(sizeof(*target->fileio_info));
|
||||
if (target->fileio_info == NULL) {
|
||||
LOG_ERROR("out of memory");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
target->type->get_gdb_fileio_info = get_gdb_fileio_info;
|
||||
target->type->gdb_fileio_end = gdb_fileio_end;
|
||||
struct arm *arm = target_to_arm(target);
|
||||
assert(arm->setup_semihosting);
|
||||
semihosting_common_init(target, arm->setup_semihosting, post_result);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
@@ -662,7 +171,11 @@ int arm_semihosting(struct target *target, int *retval)
|
||||
uint32_t pc, lr, spsr;
|
||||
struct reg *r;
|
||||
|
||||
if (!arm->is_semihosting)
|
||||
struct semihosting *semihosting = target->semihosting;
|
||||
if (!semihosting)
|
||||
return 0;
|
||||
|
||||
if (!semihosting->is_active)
|
||||
return 0;
|
||||
|
||||
if (is_arm7_9(target_to_arm7_9(target)) ||
|
||||
@@ -758,6 +271,24 @@ int arm_semihosting(struct target *target, int *retval)
|
||||
/* bkpt 0xAB */
|
||||
if (insn != 0xBEAB)
|
||||
return 0;
|
||||
} else if (is_armv8(target_to_armv8(target))) {
|
||||
if (target->debug_reason != DBG_REASON_BREAKPOINT)
|
||||
return 0;
|
||||
|
||||
if (arm->core_state == ARM_STATE_AARCH64) {
|
||||
uint32_t insn = 0;
|
||||
r = arm->pc;
|
||||
uint64_t pc64 = buf_get_u64(r->value, 0, 64);
|
||||
*retval = target_read_u32(target, pc64, &insn);
|
||||
|
||||
if (*retval != ERROR_OK)
|
||||
return 1;
|
||||
|
||||
/* bkpt 0xAB */
|
||||
if (insn != 0xD45E0000)
|
||||
return 0;
|
||||
} else
|
||||
return 1;
|
||||
} else {
|
||||
LOG_ERROR("Unsupported semi-hosting Target");
|
||||
return 0;
|
||||
@@ -766,32 +297,38 @@ int arm_semihosting(struct target *target, int *retval)
|
||||
/* Perform semihosting if we are not waiting on a fileio
|
||||
* operation to complete.
|
||||
*/
|
||||
if (!arm->semihosting_hit_fileio) {
|
||||
*retval = do_semihosting(target);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed semihosting operation");
|
||||
if (!semihosting->hit_fileio) {
|
||||
if (is_armv8(target_to_armv8(target)) &&
|
||||
arm->core_state == ARM_STATE_AARCH64) {
|
||||
/* Read op and param from register x0 and x1 respectively. */
|
||||
semihosting->op = buf_get_u64(arm->core_cache->reg_list[0].value, 0, 64);
|
||||
semihosting->param = buf_get_u64(arm->core_cache->reg_list[1].value, 0, 64);
|
||||
semihosting->word_size_bytes = 8;
|
||||
} else {
|
||||
/* Read op and param from register r0 and r1 respectively. */
|
||||
semihosting->op = buf_get_u32(arm->core_cache->reg_list[0].value, 0, 32);
|
||||
semihosting->param = buf_get_u32(arm->core_cache->reg_list[1].value, 0, 32);
|
||||
semihosting->word_size_bytes = 4;
|
||||
}
|
||||
|
||||
/* Check for ARM operation numbers. */
|
||||
if (0 <= semihosting->op && semihosting->op <= 0x31) {
|
||||
*retval = semihosting_common(target);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed semihosting operation");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* Unknown operation number, not a semihosting call. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Post result to target if we are not waiting on a fileio
|
||||
/* Resume if target it is resumable and we are not waiting on a fileio
|
||||
* operation to complete:
|
||||
*/
|
||||
if (!arm->semihosting_hit_fileio) {
|
||||
*retval = post_result(target);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to post semihosting result");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*retval = target_resume(target, 1, 0, 0, 0);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to resume target");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
if (semihosting->is_resumable && !semihosting->hit_fileio)
|
||||
return arm_semihosting_resume(target, retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#ifndef OPENOCD_TARGET_ARM_SEMIHOSTING_H
|
||||
#define OPENOCD_TARGET_ARM_SEMIHOSTING_H
|
||||
|
||||
#include "semihosting_common.h"
|
||||
|
||||
int arm_semihosting_init(struct target *target);
|
||||
int arm_semihosting(struct target *target, int *retval);
|
||||
|
||||
|
||||
+21
-120
@@ -8,6 +8,9 @@
|
||||
* Copyright (C) 2008 by Oyvind Harboe *
|
||||
* oyvind.harboe@zylin.com *
|
||||
* *
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -34,6 +37,7 @@
|
||||
#include <helper/binarybuffer.h>
|
||||
#include "algorithm.h"
|
||||
#include "register.h"
|
||||
#include "semihosting_common.h"
|
||||
|
||||
/* offsets into armv4_5 core register cache */
|
||||
enum {
|
||||
@@ -748,7 +752,7 @@ int arm_arch_state(struct target *target)
|
||||
}
|
||||
|
||||
/* avoid filling log waiting for fileio reply */
|
||||
if (arm->semihosting_hit_fileio)
|
||||
if (target->semihosting && target->semihosting->hit_fileio)
|
||||
return ERROR_OK;
|
||||
|
||||
LOG_USER("target halted in %s state due to %s, current mode: %s\n"
|
||||
@@ -758,8 +762,8 @@ int arm_arch_state(struct target *target)
|
||||
arm_mode_name(arm->core_mode),
|
||||
buf_get_u32(arm->cpsr->value, 0, 32),
|
||||
buf_get_u32(arm->pc->value, 0, 32),
|
||||
arm->is_semihosting ? ", semihosting" : "",
|
||||
arm->is_semihosting_fileio ? " fileio" : "");
|
||||
(target->semihosting && target->semihosting->is_active) ? ", semihosting" : "",
|
||||
(target->semihosting && target->semihosting->is_fileio) ? " fileio" : "");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
@@ -1094,119 +1098,10 @@ static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
|
||||
return JIM_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_arm_semihosting_command)
|
||||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
|
||||
if (target == NULL) {
|
||||
LOG_ERROR("No target selected");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
if (!is_arm(arm)) {
|
||||
command_print(CMD_CTX, "current target isn't an ARM");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!arm->setup_semihosting) {
|
||||
command_print(CMD_CTX, "semihosting not supported for current target");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (CMD_ARGC > 0) {
|
||||
int semihosting;
|
||||
|
||||
COMMAND_PARSE_ENABLE(CMD_ARGV[0], semihosting);
|
||||
|
||||
if (!target_was_examined(target)) {
|
||||
LOG_ERROR("Target not examined yet");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (arm->setup_semihosting(target, semihosting) != ERROR_OK) {
|
||||
LOG_ERROR("Failed to Configure semihosting");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
/* FIXME never let that "catch" be dropped! */
|
||||
arm->is_semihosting = semihosting;
|
||||
}
|
||||
|
||||
command_print(CMD_CTX, "semihosting is %s",
|
||||
arm->is_semihosting
|
||||
? "enabled" : "disabled");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_arm_semihosting_fileio_command)
|
||||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
|
||||
if (target == NULL) {
|
||||
LOG_ERROR("No target selected");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
if (!is_arm(arm)) {
|
||||
command_print(CMD_CTX, "current target isn't an ARM");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!arm->is_semihosting) {
|
||||
command_print(CMD_CTX, "semihosting is not enabled");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (CMD_ARGC > 0)
|
||||
COMMAND_PARSE_ENABLE(CMD_ARGV[0], arm->is_semihosting_fileio);
|
||||
|
||||
command_print(CMD_CTX, "semihosting fileio is %s",
|
||||
arm->is_semihosting_fileio
|
||||
? "enabled" : "disabled");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(handle_arm_semihosting_cmdline)
|
||||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
unsigned int i;
|
||||
|
||||
if (target == NULL) {
|
||||
LOG_ERROR("No target selected");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
if (!is_arm(arm)) {
|
||||
command_print(CMD_CTX, "current target isn't an ARM");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (!arm->setup_semihosting) {
|
||||
command_print(CMD_CTX, "semihosting not supported for current target");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
free(arm->semihosting_cmdline);
|
||||
arm->semihosting_cmdline = CMD_ARGC > 0 ? strdup(CMD_ARGV[0]) : NULL;
|
||||
|
||||
for (i = 1; i < CMD_ARGC; i++) {
|
||||
char *cmdline = alloc_printf("%s %s", arm->semihosting_cmdline, CMD_ARGV[i]);
|
||||
if (cmdline == NULL)
|
||||
break;
|
||||
free(arm->semihosting_cmdline);
|
||||
arm->semihosting_cmdline = cmdline;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_command);
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command);
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command);
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_cmdline);
|
||||
|
||||
static const struct command_registration arm_exec_command_handlers[] = {
|
||||
{
|
||||
@@ -1245,26 +1140,32 @@ static const struct command_registration arm_exec_command_handlers[] = {
|
||||
},
|
||||
{
|
||||
"semihosting",
|
||||
.handler = handle_arm_semihosting_command,
|
||||
.handler = handle_common_semihosting_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "['enable'|'disable']",
|
||||
.help = "activate support for semihosting operations",
|
||||
},
|
||||
{
|
||||
"semihosting_cmdline",
|
||||
.handler = handle_arm_semihosting_cmdline,
|
||||
.handler = handle_common_semihosting_cmdline,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "arguments",
|
||||
.help = "command line arguments to be passed to program",
|
||||
},
|
||||
{
|
||||
"semihosting_fileio",
|
||||
.handler = handle_arm_semihosting_fileio_command,
|
||||
.handler = handle_common_semihosting_fileio_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "['enable'|'disable']",
|
||||
.help = "activate support for semihosting fileio operations",
|
||||
},
|
||||
|
||||
{
|
||||
"semihosting_resexit",
|
||||
.handler = handle_common_semihosting_resumable_exit_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "['enable'|'disable']",
|
||||
.help = "activate support for semihosting resumable exit",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
const struct command_registration arm_command_handlers[] = {
|
||||
|
||||
+1
-6
@@ -124,7 +124,7 @@ done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int armv7a_read_ttbcr(struct target *target)
|
||||
int armv7a_read_ttbcr(struct target *target)
|
||||
{
|
||||
struct armv7a_common *armv7a = target_to_armv7a(target);
|
||||
struct arm_dpm *dpm = armv7a->arm.dpm;
|
||||
@@ -554,9 +554,6 @@ int armv7a_identify_cache(struct target *target)
|
||||
struct armv7a_cache_common *cache =
|
||||
&(armv7a->armv7a_mmu.armv7a_cache);
|
||||
|
||||
if (!armv7a->is_armv7r)
|
||||
armv7a_read_ttbcr(target);
|
||||
|
||||
retval = dpm->prepare(dpm);
|
||||
if (retval != ERROR_OK)
|
||||
goto done;
|
||||
@@ -729,8 +726,6 @@ int armv7a_arch_state(struct target *target)
|
||||
|
||||
arm_arch_state(target);
|
||||
|
||||
armv7a_read_ttbcr(target);
|
||||
|
||||
if (armv7a->is_armv7r) {
|
||||
LOG_USER("D-Cache: %s, I-Cache: %s",
|
||||
state[armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled],
|
||||
|
||||
@@ -194,6 +194,7 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val);
|
||||
|
||||
int armv7a_handle_cache_info_command(struct command_context *cmd_ctx,
|
||||
struct armv7a_cache_common *armv7a_cache);
|
||||
int armv7a_read_ttbcr(struct target *target);
|
||||
|
||||
extern const struct command_registration armv7a_command_handlers[];
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cach
|
||||
|
||||
LOG_DEBUG("cl %" PRId32, cl);
|
||||
do {
|
||||
keep_alive();
|
||||
c_way = size->way;
|
||||
do {
|
||||
uint32_t value = (c_index << size->index_shift)
|
||||
@@ -89,6 +90,7 @@ static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cach
|
||||
} while (c_index >= 0);
|
||||
|
||||
done:
|
||||
keep_alive();
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -164,7 +166,7 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
|
||||
struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
|
||||
uint32_t linelen = armv7a_cache->dminline;
|
||||
uint32_t va_line, va_end;
|
||||
int retval;
|
||||
int retval, i = 0;
|
||||
|
||||
retval = armv7a_l1_d_cache_sanity_check(target);
|
||||
if (retval != ERROR_OK)
|
||||
@@ -198,6 +200,8 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
|
||||
}
|
||||
|
||||
while (va_line < va_end) {
|
||||
if ((i++ & 0x3f) == 0)
|
||||
keep_alive();
|
||||
/* DCIMVAC - Invalidate data cache line by VA to PoC. */
|
||||
retval = dpm->instr_write_data_r0(dpm,
|
||||
ARMV4_5_MCR(15, 0, 0, 7, 6, 1), va_line);
|
||||
@@ -206,11 +210,13 @@ int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
|
||||
va_line += linelen;
|
||||
}
|
||||
|
||||
keep_alive();
|
||||
dpm->finish(dpm);
|
||||
return retval;
|
||||
|
||||
done:
|
||||
LOG_ERROR("d-cache invalidate failed");
|
||||
keep_alive();
|
||||
dpm->finish(dpm);
|
||||
|
||||
return retval;
|
||||
@@ -224,7 +230,7 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
|
||||
struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
|
||||
uint32_t linelen = armv7a_cache->dminline;
|
||||
uint32_t va_line, va_end;
|
||||
int retval;
|
||||
int retval, i = 0;
|
||||
|
||||
retval = armv7a_l1_d_cache_sanity_check(target);
|
||||
if (retval != ERROR_OK)
|
||||
@@ -238,6 +244,8 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
|
||||
va_end = virt + size;
|
||||
|
||||
while (va_line < va_end) {
|
||||
if ((i++ & 0x3f) == 0)
|
||||
keep_alive();
|
||||
/* DCCMVAC - Data Cache Clean by MVA to PoC */
|
||||
retval = dpm->instr_write_data_r0(dpm,
|
||||
ARMV4_5_MCR(15, 0, 0, 7, 10, 1), va_line);
|
||||
@@ -246,11 +254,13 @@ int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
|
||||
va_line += linelen;
|
||||
}
|
||||
|
||||
keep_alive();
|
||||
dpm->finish(dpm);
|
||||
return retval;
|
||||
|
||||
done:
|
||||
LOG_ERROR("d-cache invalidate failed");
|
||||
keep_alive();
|
||||
dpm->finish(dpm);
|
||||
|
||||
return retval;
|
||||
@@ -264,7 +274,7 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
|
||||
struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
|
||||
uint32_t linelen = armv7a_cache->dminline;
|
||||
uint32_t va_line, va_end;
|
||||
int retval;
|
||||
int retval, i = 0;
|
||||
|
||||
retval = armv7a_l1_d_cache_sanity_check(target);
|
||||
if (retval != ERROR_OK)
|
||||
@@ -278,6 +288,8 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
|
||||
va_end = virt + size;
|
||||
|
||||
while (va_line < va_end) {
|
||||
if ((i++ & 0x3f) == 0)
|
||||
keep_alive();
|
||||
/* DCCIMVAC */
|
||||
retval = dpm->instr_write_data_r0(dpm,
|
||||
ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
|
||||
@@ -286,11 +298,13 @@ int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
|
||||
va_line += linelen;
|
||||
}
|
||||
|
||||
keep_alive();
|
||||
dpm->finish(dpm);
|
||||
return retval;
|
||||
|
||||
done:
|
||||
LOG_ERROR("d-cache invalidate failed");
|
||||
keep_alive();
|
||||
dpm->finish(dpm);
|
||||
|
||||
return retval;
|
||||
@@ -342,7 +356,7 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
|
||||
&armv7a->armv7a_mmu.armv7a_cache;
|
||||
uint32_t linelen = armv7a_cache->iminline;
|
||||
uint32_t va_line, va_end;
|
||||
int retval;
|
||||
int retval, i = 0;
|
||||
|
||||
retval = armv7a_l1_i_cache_sanity_check(target);
|
||||
if (retval != ERROR_OK)
|
||||
@@ -356,6 +370,8 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
|
||||
va_end = virt + size;
|
||||
|
||||
while (va_line < va_end) {
|
||||
if ((i++ & 0x3f) == 0)
|
||||
keep_alive();
|
||||
/* ICIMVAU - Invalidate instruction cache by VA to PoU. */
|
||||
retval = dpm->instr_write_data_r0(dpm,
|
||||
ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
|
||||
@@ -368,10 +384,13 @@ int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
|
||||
goto done;
|
||||
va_line += linelen;
|
||||
}
|
||||
keep_alive();
|
||||
dpm->finish(dpm);
|
||||
return retval;
|
||||
|
||||
done:
|
||||
LOG_ERROR("i-cache invalidate failed");
|
||||
keep_alive();
|
||||
dpm->finish(dpm);
|
||||
|
||||
return retval;
|
||||
|
||||
+7
-3
@@ -11,6 +11,9 @@
|
||||
* Copyright (C) 2007,2008 Øyvind Harboe *
|
||||
* oyvind.harboe@zylin.com *
|
||||
* *
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -37,6 +40,7 @@
|
||||
#include "armv7m.h"
|
||||
#include "algorithm.h"
|
||||
#include "register.h"
|
||||
#include "semihosting_common.h"
|
||||
|
||||
#if 0
|
||||
#define _DEBUG_INSTRUCTION_EXECUTION_
|
||||
@@ -537,7 +541,7 @@ int armv7m_arch_state(struct target *target)
|
||||
uint32_t ctrl, sp;
|
||||
|
||||
/* avoid filling log waiting for fileio reply */
|
||||
if (arm->semihosting_hit_fileio)
|
||||
if (target->semihosting && target->semihosting->hit_fileio)
|
||||
return ERROR_OK;
|
||||
|
||||
ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32);
|
||||
@@ -552,8 +556,8 @@ int armv7m_arch_state(struct target *target)
|
||||
buf_get_u32(arm->pc->value, 0, 32),
|
||||
(ctrl & 0x02) ? 'p' : 'm',
|
||||
sp,
|
||||
arm->is_semihosting ? ", semihosting" : "",
|
||||
arm->is_semihosting_fileio ? " fileio" : "");
|
||||
(target->semihosting && target->semihosting->is_active) ? ", semihosting" : "",
|
||||
(target->semihosting && target->semihosting->is_fileio) ? " fileio" : "");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
+10
-10
@@ -62,7 +62,7 @@ int armv7m_trace_tpiu_config(struct target *target)
|
||||
target_unregister_timer_callback(armv7m_poll_trace, target);
|
||||
|
||||
|
||||
retval = adapter_config_trace(trace_config->config_type == INTERNAL,
|
||||
retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL,
|
||||
trace_config->pin_protocol,
|
||||
trace_config->port_size,
|
||||
&trace_config->trace_freq);
|
||||
@@ -83,7 +83,7 @@ int armv7m_trace_tpiu_config(struct target *target)
|
||||
trace_config->trace_freq, trace_config->traceclkin_freq,
|
||||
trace_freq);
|
||||
trace_config->trace_freq = trace_freq;
|
||||
retval = adapter_config_trace(trace_config->config_type == INTERNAL,
|
||||
retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL,
|
||||
trace_config->pin_protocol,
|
||||
trace_config->port_size,
|
||||
&trace_config->trace_freq);
|
||||
@@ -115,7 +115,7 @@ int armv7m_trace_tpiu_config(struct target *target)
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (trace_config->config_type == INTERNAL)
|
||||
if (trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL)
|
||||
target_register_timer_callback(armv7m_poll_trace, 1, 1, target);
|
||||
|
||||
target_call_event_callbacks(target, TARGET_EVENT_TRACE_CONFIG);
|
||||
@@ -173,7 +173,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
|
||||
if (CMD_ARGC == cmd_idx + 1) {
|
||||
close_trace_file(armv7m);
|
||||
|
||||
armv7m->trace_config.config_type = DISABLED;
|
||||
armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED;
|
||||
if (CMD_CTX->mode == COMMAND_EXEC)
|
||||
return armv7m_trace_tpiu_config(target);
|
||||
else
|
||||
@@ -183,13 +183,13 @@ COMMAND_HANDLER(handle_tpiu_config_command)
|
||||
!strcmp(CMD_ARGV[cmd_idx], "internal")) {
|
||||
close_trace_file(armv7m);
|
||||
|
||||
armv7m->trace_config.config_type = EXTERNAL;
|
||||
armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL;
|
||||
if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
|
||||
cmd_idx++;
|
||||
if (CMD_ARGC == cmd_idx)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
armv7m->trace_config.config_type = INTERNAL;
|
||||
armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_INTERNAL;
|
||||
|
||||
if (strcmp(CMD_ARGV[cmd_idx], "-") != 0) {
|
||||
armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
|
||||
@@ -204,7 +204,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
if (!strcmp(CMD_ARGV[cmd_idx], "sync")) {
|
||||
armv7m->trace_config.pin_protocol = SYNC;
|
||||
armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_SYNC;
|
||||
|
||||
cmd_idx++;
|
||||
if (CMD_ARGC == cmd_idx)
|
||||
@@ -213,9 +213,9 @@ COMMAND_HANDLER(handle_tpiu_config_command)
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[cmd_idx], armv7m->trace_config.port_size);
|
||||
} else {
|
||||
if (!strcmp(CMD_ARGV[cmd_idx], "manchester"))
|
||||
armv7m->trace_config.pin_protocol = ASYNC_MANCHESTER;
|
||||
armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER;
|
||||
else if (!strcmp(CMD_ARGV[cmd_idx], "uart"))
|
||||
armv7m->trace_config.pin_protocol = ASYNC_UART;
|
||||
armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_UART;
|
||||
else
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
@@ -237,7 +237,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
|
||||
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.trace_freq);
|
||||
cmd_idx++;
|
||||
} else {
|
||||
if (armv7m->trace_config.config_type != INTERNAL) {
|
||||
if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_INTERNAL) {
|
||||
LOG_ERROR("Trace port frequency can't be omitted in external capture mode");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
@@ -27,15 +27,15 @@
|
||||
*/
|
||||
|
||||
enum trace_config_type {
|
||||
DISABLED, /**< tracing is disabled */
|
||||
EXTERNAL, /**< trace output is captured externally */
|
||||
INTERNAL /**< trace output is handled by OpenOCD adapter driver */
|
||||
TRACE_CONFIG_TYPE_DISABLED, /**< tracing is disabled */
|
||||
TRACE_CONFIG_TYPE_EXTERNAL, /**< trace output is captured externally */
|
||||
TRACE_CONFIG_TYPE_INTERNAL /**< trace output is handled by OpenOCD adapter driver */
|
||||
};
|
||||
|
||||
enum tpio_pin_protocol {
|
||||
SYNC, /**< synchronous trace output */
|
||||
ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */
|
||||
ASYNC_UART /**< asynchronous output with NRZ coding */
|
||||
enum tpiu_pin_protocol {
|
||||
TPIU_PIN_PROTOCOL_SYNC, /**< synchronous trace output */
|
||||
TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */
|
||||
TPIU_PIN_PROTOCOL_ASYNC_UART /**< asynchronous output with NRZ coding */
|
||||
};
|
||||
|
||||
enum itm_ts_prescaler {
|
||||
@@ -50,7 +50,7 @@ struct armv7m_trace_config {
|
||||
enum trace_config_type config_type;
|
||||
|
||||
/** Currently active trace output mode */
|
||||
enum tpio_pin_protocol pin_protocol;
|
||||
enum tpiu_pin_protocol pin_protocol;
|
||||
/** TPIU formatter enable/disable (in async mode) */
|
||||
bool formatter;
|
||||
/** Synchronous output port width */
|
||||
|
||||
+18
-1
@@ -1,6 +1,9 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2015 by David Ung *
|
||||
* *
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* 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 *
|
||||
@@ -36,6 +39,7 @@
|
||||
#include "armv8_opcodes.h"
|
||||
#include "target.h"
|
||||
#include "target_type.h"
|
||||
#include "semihosting_common.h"
|
||||
|
||||
static const char * const armv8_state_strings[] = {
|
||||
"AArch32", "Thumb", "Jazelle", "ThumbEE", "AArch64",
|
||||
@@ -1013,11 +1017,24 @@ int armv8_handle_cache_info_command(struct command_context *cmd_ctx,
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int armv8_setup_semihosting(struct target *target, int enable)
|
||||
{
|
||||
struct arm *arm = target_to_arm(target);
|
||||
|
||||
if (arm->core_state != ARM_STATE_AARCH64) {
|
||||
LOG_ERROR("semihosting only supported in AArch64 state\n");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int armv8_init_arch_info(struct target *target, struct armv8_common *armv8)
|
||||
{
|
||||
struct arm *arm = &armv8->arm;
|
||||
arm->arch_info = armv8;
|
||||
target->arch_info = &armv8->arm;
|
||||
arm->setup_semihosting = armv8_setup_semihosting;
|
||||
/* target is useful in all function arm v4 5 compatible */
|
||||
armv8->arm.target = target;
|
||||
armv8->arm.common_magic = ARM_COMMON_MAGIC;
|
||||
@@ -1046,7 +1063,7 @@ int armv8_aarch64_state(struct target *target)
|
||||
armv8_mode_name(arm->core_mode),
|
||||
buf_get_u32(arm->cpsr->value, 0, 32),
|
||||
buf_get_u64(arm->pc->value, 0, 64),
|
||||
arm->is_semihosting ? ", semihosting" : "");
|
||||
(target->semihosting && target->semihosting->is_active) ? ", semihosting" : "");
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -113,6 +113,12 @@ enum {
|
||||
ARMV8_LAST_REG,
|
||||
};
|
||||
|
||||
enum run_control_op {
|
||||
ARMV8_RUNCONTROL_UNKNOWN = 0,
|
||||
ARMV8_RUNCONTROL_RESUME = 1,
|
||||
ARMV8_RUNCONTROL_HALT = 2,
|
||||
ARMV8_RUNCONTROL_STEP = 3,
|
||||
};
|
||||
|
||||
#define ARMV8_COMMON_MAGIC 0x0A450AAA
|
||||
|
||||
@@ -210,6 +216,9 @@ struct armv8_common {
|
||||
|
||||
struct arm_cti *cti;
|
||||
|
||||
/* last run-control command issued to this target (resume, halt, step) */
|
||||
enum run_control_op last_run_control_op;
|
||||
|
||||
/* Direct processor core register read and writes */
|
||||
int (*read_reg_u64)(struct armv8_common *armv8, int num, uint64_t *value);
|
||||
int (*write_reg_u64)(struct armv8_common *armv8, int num, uint64_t value);
|
||||
@@ -232,6 +241,11 @@ target_to_armv8(struct target *target)
|
||||
return container_of(target->arch_info, struct armv8_common, arm);
|
||||
}
|
||||
|
||||
static inline bool is_armv8(struct armv8_common *armv8)
|
||||
{
|
||||
return armv8->common_magic == ARMV8_COMMON_MAGIC;
|
||||
}
|
||||
|
||||
/* register offsets from armv8.debug_base */
|
||||
#define CPUV8_DBG_MAINID0 0xD00
|
||||
#define CPUV8_DBG_CPUFEATURE0 0xD20
|
||||
|
||||
@@ -315,11 +315,8 @@ int breakpoint_remove_internal(struct target *target, target_addr_t address)
|
||||
struct breakpoint *breakpoint = target->breakpoints;
|
||||
|
||||
while (breakpoint) {
|
||||
if ((breakpoint->address == address) && (breakpoint->asid == 0))
|
||||
break;
|
||||
else if ((breakpoint->address == 0) && (breakpoint->asid == address))
|
||||
break;
|
||||
else if ((breakpoint->address == address) && (breakpoint->asid != 0))
|
||||
if ((breakpoint->address == address) ||
|
||||
(breakpoint->address == 0 && breakpoint->asid == address))
|
||||
break;
|
||||
breakpoint = breakpoint->next;
|
||||
}
|
||||
|
||||
+18
-1
@@ -1297,6 +1297,9 @@ static int cortex_a_post_debug_entry(struct target *target)
|
||||
LOG_DEBUG("cp15_control_reg: %8.8" PRIx32, cortex_a->cp15_control_reg);
|
||||
cortex_a->cp15_control_reg_curr = cortex_a->cp15_control_reg;
|
||||
|
||||
if (!armv7a->is_armv7r)
|
||||
armv7a_read_ttbcr(target);
|
||||
|
||||
if (armv7a->armv7a_mmu.armv7a_cache.info == -1)
|
||||
armv7a_identify_cache(target);
|
||||
|
||||
@@ -3226,6 +3229,20 @@ static int cortex_a_virt2phys(struct target *target,
|
||||
struct armv7a_common *armv7a = target_to_armv7a(target);
|
||||
struct adiv5_dap *swjdp = armv7a->arm.dap;
|
||||
uint8_t apsel = swjdp->apsel;
|
||||
int mmu_enabled = 0;
|
||||
|
||||
/*
|
||||
* If the MMU was not enabled at debug entry, there is no
|
||||
* way of knowing if there was ever a valid configuration
|
||||
* for it and thus it's not safe to enable it. In this case,
|
||||
* just return the virtual address as physical.
|
||||
*/
|
||||
cortex_a_mmu(target, &mmu_enabled);
|
||||
if (!mmu_enabled) {
|
||||
*phys = virt;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
if (armv7a->memory_ap_available && (apsel == armv7a->memory_ap->ap_num)) {
|
||||
uint32_t ret;
|
||||
retval = armv7a_mmu_translate_va(target,
|
||||
@@ -3421,7 +3438,7 @@ static const struct command_registration cortex_a_exec_command_handlers[] = {
|
||||
{
|
||||
.name = "dacrfixup",
|
||||
.handler = handle_cortex_a_dacrfixup_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "set domain access control (DACR) to all-manager "
|
||||
"on memory access",
|
||||
.usage = "['on'|'off']",
|
||||
|
||||
+29
-22
@@ -165,7 +165,7 @@ static int cortex_m_single_step_core(struct target *target)
|
||||
struct armv7m_common *armv7m = &cortex_m->armv7m;
|
||||
int retval;
|
||||
|
||||
/* Mask interrupts before clearing halt, if done already. This avoids
|
||||
/* Mask interrupts before clearing halt, if not done already. This avoids
|
||||
* Erratum 377497 (fixed in r1p0) where setting MASKINTS while clearing
|
||||
* HALT can put the core into an unknown state.
|
||||
*/
|
||||
@@ -237,8 +237,11 @@ static int cortex_m_endreset_event(struct target *target)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* clear any interrupt masking */
|
||||
cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS);
|
||||
/* Restore proper interrupt masking setting. */
|
||||
if (cortex_m->isrmasking_mode == CORTEX_M_ISRMASK_ON)
|
||||
cortex_m_write_debug_halt_mask(target, C_MASKINTS, 0);
|
||||
else
|
||||
cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS);
|
||||
|
||||
/* Enable features controlled by ITM and DWT blocks, and catch only
|
||||
* the vectors we were told to pay attention to.
|
||||
@@ -1137,6 +1140,10 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint
|
||||
breakpoint->set = fp_num + 1;
|
||||
fpcr_value = breakpoint->address | 1;
|
||||
if (cortex_m->fp_rev == 0) {
|
||||
if (breakpoint->address > 0x1FFFFFFF) {
|
||||
LOG_ERROR("Cortex-M Flash Patch Breakpoint rev.1 cannot handle HW breakpoint above address 0x1FFFFFFE");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
uint32_t hilo;
|
||||
hilo = (breakpoint->address & 0x2) ? FPCR_REPLACE_BKPT_HIGH : FPCR_REPLACE_BKPT_LOW;
|
||||
fpcr_value = (fpcr_value & 0x1FFFFFFC) | hilo | 1;
|
||||
@@ -1806,11 +1813,11 @@ static int cortex_m_dwt_set_reg(struct reg *reg, uint8_t *buf)
|
||||
|
||||
struct dwt_reg {
|
||||
uint32_t addr;
|
||||
char *name;
|
||||
const char *name;
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
static struct dwt_reg dwt_base_regs[] = {
|
||||
static const struct dwt_reg dwt_base_regs[] = {
|
||||
{ DWT_CTRL, "dwt_ctrl", 32, },
|
||||
/* NOTE that Erratum 532314 (fixed r2p0) affects CYCCNT: it wrongly
|
||||
* increments while the core is asleep.
|
||||
@@ -1819,7 +1826,7 @@ static struct dwt_reg dwt_base_regs[] = {
|
||||
/* plus some 8 bit counters, useful for profiling with TPIU */
|
||||
};
|
||||
|
||||
static struct dwt_reg dwt_comp[] = {
|
||||
static const struct dwt_reg dwt_comp[] = {
|
||||
#define DWT_COMPARATOR(i) \
|
||||
{ DWT_COMP0 + 0x10 * (i), "dwt_" #i "_comp", 32, }, \
|
||||
{ DWT_MASK0 + 0x10 * (i), "dwt_" #i "_mask", 4, }, \
|
||||
@@ -1848,7 +1855,7 @@ static const struct reg_arch_type dwt_reg_type = {
|
||||
.set = cortex_m_dwt_set_reg,
|
||||
};
|
||||
|
||||
static void cortex_m_dwt_addreg(struct target *t, struct reg *r, struct dwt_reg *d)
|
||||
static void cortex_m_dwt_addreg(struct target *t, struct reg *r, const struct dwt_reg *d)
|
||||
{
|
||||
struct dwt_reg_state *state;
|
||||
|
||||
@@ -2076,7 +2083,7 @@ int cortex_m_examine(struct target *target)
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (armv7m->trace_config.config_type != DISABLED) {
|
||||
if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_DISABLED) {
|
||||
armv7m_trace_tpiu_config(target);
|
||||
armv7m_trace_itm_config(target);
|
||||
}
|
||||
@@ -2270,20 +2277,6 @@ static int cortex_m_verify_pointer(struct command_context *cmd_ctx,
|
||||
* cortexm3_target structure, which is only used with CM3 targets.
|
||||
*/
|
||||
|
||||
static const struct {
|
||||
char name[10];
|
||||
unsigned mask;
|
||||
} vec_ids[] = {
|
||||
{ "hard_err", VC_HARDERR, },
|
||||
{ "int_err", VC_INTERR, },
|
||||
{ "bus_err", VC_BUSERR, },
|
||||
{ "state_err", VC_STATERR, },
|
||||
{ "chk_err", VC_CHKERR, },
|
||||
{ "nocp_err", VC_NOCPERR, },
|
||||
{ "mm_err", VC_MMERR, },
|
||||
{ "reset", VC_CORERESET, },
|
||||
};
|
||||
|
||||
COMMAND_HANDLER(handle_cortex_m_vector_catch_command)
|
||||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
@@ -2292,6 +2285,20 @@ COMMAND_HANDLER(handle_cortex_m_vector_catch_command)
|
||||
uint32_t demcr = 0;
|
||||
int retval;
|
||||
|
||||
static const struct {
|
||||
char name[10];
|
||||
unsigned mask;
|
||||
} vec_ids[] = {
|
||||
{ "hard_err", VC_HARDERR, },
|
||||
{ "int_err", VC_INTERR, },
|
||||
{ "bus_err", VC_BUSERR, },
|
||||
{ "state_err", VC_STATERR, },
|
||||
{ "chk_err", VC_CHKERR, },
|
||||
{ "nocp_err", VC_NOCPERR, },
|
||||
{ "mm_err", VC_MMERR, },
|
||||
{ "reset", VC_CORERESET, },
|
||||
};
|
||||
|
||||
retval = cortex_m_verify_pointer(CMD_CTX, cortex_m);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
+1
-2
@@ -1048,8 +1048,7 @@ int image_calculate_checksum(uint8_t *buffer, uint32_t nbytes, uint32_t *checksu
|
||||
static bool first_init;
|
||||
if (!first_init) {
|
||||
/* Initialize the CRC table and the decoding table. */
|
||||
int i, j;
|
||||
unsigned int c;
|
||||
unsigned int i, j, c;
|
||||
for (i = 0; i < 256; i++) {
|
||||
/* as per gdb */
|
||||
for (c = i << 24, j = 8; j > 0; --j)
|
||||
|
||||
@@ -344,6 +344,8 @@ static int mips_m4k_assert_reset(struct target *target)
|
||||
jtag_add_reset(1, 1);
|
||||
else if (!srst_asserted)
|
||||
jtag_add_reset(0, 1);
|
||||
} else if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
|
||||
target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
|
||||
} else {
|
||||
if (mips_m4k->is_pic32mx) {
|
||||
LOG_DEBUG("Using MTAP reset to reset processor...");
|
||||
|
||||
+36
-37
@@ -2339,63 +2339,66 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil
|
||||
fileio_info->identifier = NULL;
|
||||
}
|
||||
|
||||
uint32_t reg_r0, reg_r1, reg_r2;
|
||||
nds32_get_mapped_reg(nds32, R0, ®_r0);
|
||||
nds32_get_mapped_reg(nds32, R1, ®_r1);
|
||||
nds32_get_mapped_reg(nds32, R2, ®_r2);
|
||||
|
||||
switch (syscall_id) {
|
||||
case NDS32_SYSCALL_EXIT:
|
||||
fileio_info->identifier = malloc(5);
|
||||
sprintf(fileio_info->identifier, "exit");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
break;
|
||||
case NDS32_SYSCALL_OPEN:
|
||||
{
|
||||
uint8_t filename[256];
|
||||
fileio_info->identifier = malloc(5);
|
||||
sprintf(fileio_info->identifier, "open");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of path */
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
|
||||
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_4));
|
||||
fileio_info->param_3 = reg_r1;
|
||||
fileio_info->param_4 = reg_r2;
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r0, 256, filename);
|
||||
fileio_info->param_2 = strlen((char *)filename) + 1;
|
||||
}
|
||||
break;
|
||||
case NDS32_SYSCALL_CLOSE:
|
||||
fileio_info->identifier = malloc(6);
|
||||
sprintf(fileio_info->identifier, "close");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
break;
|
||||
case NDS32_SYSCALL_READ:
|
||||
fileio_info->identifier = malloc(5);
|
||||
sprintf(fileio_info->identifier, "read");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
fileio_info->param_3 = reg_r2;
|
||||
break;
|
||||
case NDS32_SYSCALL_WRITE:
|
||||
fileio_info->identifier = malloc(6);
|
||||
sprintf(fileio_info->identifier, "write");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
fileio_info->param_3 = reg_r2;
|
||||
break;
|
||||
case NDS32_SYSCALL_LSEEK:
|
||||
fileio_info->identifier = malloc(6);
|
||||
sprintf(fileio_info->identifier, "lseek");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
nds32_get_mapped_reg(nds32, R2, &(fileio_info->param_3));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
fileio_info->param_3 = reg_r2;
|
||||
break;
|
||||
case NDS32_SYSCALL_UNLINK:
|
||||
{
|
||||
uint8_t filename[256];
|
||||
fileio_info->identifier = malloc(7);
|
||||
sprintf(fileio_info->identifier, "unlink");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of path */
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r0, 256, filename);
|
||||
fileio_info->param_2 = strlen((char *)filename) + 1;
|
||||
}
|
||||
break;
|
||||
@@ -2404,61 +2407,57 @@ int nds32_get_gdb_fileio_info(struct target *target, struct gdb_fileio_info *fil
|
||||
uint8_t filename[256];
|
||||
fileio_info->identifier = malloc(7);
|
||||
sprintf(fileio_info->identifier, "rename");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of old path */
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
|
||||
fileio_info->param_3 = reg_r1;
|
||||
/* reserve fileio_info->param_4 for length of new path */
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r0, 256, filename);
|
||||
fileio_info->param_2 = strlen((char *)filename) + 1;
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_3,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r1, 256, filename);
|
||||
fileio_info->param_4 = strlen((char *)filename) + 1;
|
||||
}
|
||||
break;
|
||||
case NDS32_SYSCALL_FSTAT:
|
||||
fileio_info->identifier = malloc(6);
|
||||
sprintf(fileio_info->identifier, "fstat");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
break;
|
||||
case NDS32_SYSCALL_STAT:
|
||||
{
|
||||
uint8_t filename[256];
|
||||
fileio_info->identifier = malloc(5);
|
||||
sprintf(fileio_info->identifier, "stat");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of old path */
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_3));
|
||||
fileio_info->param_3 = reg_r1;
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, filename);
|
||||
target->type->read_buffer(target, reg_r0, 256, filename);
|
||||
fileio_info->param_2 = strlen((char *)filename) + 1;
|
||||
}
|
||||
break;
|
||||
case NDS32_SYSCALL_GETTIMEOFDAY:
|
||||
fileio_info->identifier = malloc(13);
|
||||
sprintf(fileio_info->identifier, "gettimeofday");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
nds32_get_mapped_reg(nds32, R1, &(fileio_info->param_2));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
fileio_info->param_2 = reg_r1;
|
||||
break;
|
||||
case NDS32_SYSCALL_ISATTY:
|
||||
fileio_info->identifier = malloc(7);
|
||||
sprintf(fileio_info->identifier, "isatty");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
break;
|
||||
case NDS32_SYSCALL_SYSTEM:
|
||||
{
|
||||
uint8_t command[256];
|
||||
fileio_info->identifier = malloc(7);
|
||||
sprintf(fileio_info->identifier, "system");
|
||||
nds32_get_mapped_reg(nds32, R0, &(fileio_info->param_1));
|
||||
fileio_info->param_1 = reg_r0;
|
||||
/* reserve fileio_info->param_2 for length of old path */
|
||||
|
||||
target->type->read_buffer(target, fileio_info->param_1,
|
||||
256, command);
|
||||
target->type->read_buffer(target, reg_r0, 256, command);
|
||||
fileio_info->param_2 = strlen((char *)command) + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
noinst_LTLIBRARIES += %D%/libriscv.la
|
||||
%C%_libriscv_la_SOURCES = \
|
||||
%D%/asm.h \
|
||||
%D%/batch.h \
|
||||
%D%/debug_defines.h \
|
||||
%D%/encoding.h \
|
||||
%D%/gdb_regs.h \
|
||||
%D%/opcodes.h \
|
||||
%D%/program.h \
|
||||
%D%/riscv.h \
|
||||
%D%/batch.c \
|
||||
%D%/program.c \
|
||||
%D%/riscv-011.c \
|
||||
%D%/riscv-013.c \
|
||||
%D%/riscv.c \
|
||||
%D%/riscv_semihosting.c
|
||||
+220
-164
@@ -229,7 +229,8 @@
|
||||
* Explains why Debug Mode was entered.
|
||||
*
|
||||
* When there are multiple reasons to enter Debug Mode in a single
|
||||
* cycle, the cause with the highest priority is the one written.
|
||||
* cycle, hardware should set \Fcause to the cause with the highest
|
||||
* priority.
|
||||
*
|
||||
* 1: An {\tt ebreak} instruction was executed. (priority 3)
|
||||
*
|
||||
@@ -245,6 +246,25 @@
|
||||
#define CSR_DCSR_CAUSE_LENGTH 3
|
||||
#define CSR_DCSR_CAUSE (0x7U << CSR_DCSR_CAUSE_OFFSET)
|
||||
/*
|
||||
* When 1, \Fmprv in \Rmstatus takes effect during debug mode.
|
||||
* When 0, it is ignored during debug mode.
|
||||
* Implementing this bit is optional.
|
||||
* If not implemented it should be tied to 0.
|
||||
*/
|
||||
#define CSR_DCSR_MPRVEN_OFFSET 4
|
||||
#define CSR_DCSR_MPRVEN_LENGTH 1
|
||||
#define CSR_DCSR_MPRVEN (0x1U << CSR_DCSR_MPRVEN_OFFSET)
|
||||
/*
|
||||
* When set, there is a Non-Maskable-Interrupt (NMI) pending for the hart.
|
||||
*
|
||||
* Since an NMI can indicate a hardware error condition,
|
||||
* reliable debugging may no longer be possible once this bit becomes set.
|
||||
* This is implementation-dependent.
|
||||
*/
|
||||
#define CSR_DCSR_NMIP_OFFSET 3
|
||||
#define CSR_DCSR_NMIP_LENGTH 1
|
||||
#define CSR_DCSR_NMIP (0x1U << CSR_DCSR_NMIP_OFFSET)
|
||||
/*
|
||||
* When set and not in Debug Mode, the hart will only execute a single
|
||||
* instruction and then enter Debug Mode.
|
||||
* If the instruction does not complete due to an exception,
|
||||
@@ -269,14 +289,14 @@
|
||||
#define CSR_DCSR_PRV (0x3U << CSR_DCSR_PRV_OFFSET)
|
||||
#define CSR_DPC 0x7b1
|
||||
#define CSR_DPC_DPC_OFFSET 0
|
||||
#define CSR_DPC_DPC_LENGTH XLEN
|
||||
#define CSR_DPC_DPC (((1L<<XLEN)-1) << CSR_DPC_DPC_OFFSET)
|
||||
#define CSR_DPC_DPC_LENGTH MXLEN
|
||||
#define CSR_DPC_DPC (((1L<<MXLEN)-1) << CSR_DPC_DPC_OFFSET)
|
||||
#define CSR_DSCRATCH0 0x7b2
|
||||
#define CSR_DSCRATCH1 0x7b3
|
||||
#define CSR_TSELECT 0x7a0
|
||||
#define CSR_TSELECT_INDEX_OFFSET 0
|
||||
#define CSR_TSELECT_INDEX_LENGTH XLEN
|
||||
#define CSR_TSELECT_INDEX (((1L<<XLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
|
||||
#define CSR_TSELECT_INDEX_LENGTH MXLEN
|
||||
#define CSR_TSELECT_INDEX (((1L<<MXLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
|
||||
#define CSR_TDATA1 0x7a1
|
||||
/*
|
||||
* 0: There is no trigger at this \Rtselect.
|
||||
@@ -290,12 +310,22 @@
|
||||
* 3: The trigger is an instruction count trigger. The remaining bits
|
||||
* in this register act as described in \Ricount.
|
||||
*
|
||||
* 4: The trigger is an interrupt trigger. The remaining bits
|
||||
* in this register act as described in \Ritrigger.
|
||||
*
|
||||
* 5: The trigger is an exception trigger. The remaining bits
|
||||
* in this register act as described in \Retrigger.
|
||||
*
|
||||
* 15: This trigger exists (so enumeration shouldn't terminate), but
|
||||
* is not currently available.
|
||||
*
|
||||
* Other values are reserved for future use.
|
||||
*
|
||||
* When this field is written to an unsupported value, it takes on its
|
||||
* reset value instead. The reset value is any one of the types
|
||||
* supported by the trigger selected by \Rtselect.
|
||||
*/
|
||||
#define CSR_TDATA1_TYPE_OFFSET (XLEN-4)
|
||||
#define CSR_TDATA1_TYPE_OFFSET (MXLEN-4)
|
||||
#define CSR_TDATA1_TYPE_LENGTH 4
|
||||
#define CSR_TDATA1_TYPE (0xfULL << CSR_TDATA1_TYPE_OFFSET)
|
||||
/*
|
||||
@@ -307,39 +337,57 @@
|
||||
*
|
||||
* This bit is only writable from Debug Mode.
|
||||
*/
|
||||
#define CSR_TDATA1_DMODE_OFFSET (XLEN-5)
|
||||
#define CSR_TDATA1_DMODE_OFFSET (MXLEN-5)
|
||||
#define CSR_TDATA1_DMODE_LENGTH 1
|
||||
#define CSR_TDATA1_DMODE (0x1ULL << CSR_TDATA1_DMODE_OFFSET)
|
||||
/*
|
||||
* Trigger-specific data.
|
||||
*/
|
||||
#define CSR_TDATA1_DATA_OFFSET 0
|
||||
#define CSR_TDATA1_DATA_LENGTH (XLEN - 5)
|
||||
#define CSR_TDATA1_DATA (((1L<<XLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
|
||||
#define CSR_TDATA1_DATA_LENGTH (MXLEN - 5)
|
||||
#define CSR_TDATA1_DATA (((1L<<MXLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
|
||||
#define CSR_TDATA2 0x7a2
|
||||
#define CSR_TDATA2_DATA_OFFSET 0
|
||||
#define CSR_TDATA2_DATA_LENGTH XLEN
|
||||
#define CSR_TDATA2_DATA (((1L<<XLEN)-1) << CSR_TDATA2_DATA_OFFSET)
|
||||
#define CSR_TDATA2_DATA_LENGTH MXLEN
|
||||
#define CSR_TDATA2_DATA (((1L<<MXLEN)-1) << CSR_TDATA2_DATA_OFFSET)
|
||||
#define CSR_TDATA3 0x7a3
|
||||
#define CSR_TDATA3_DATA_OFFSET 0
|
||||
#define CSR_TDATA3_DATA_LENGTH XLEN
|
||||
#define CSR_TDATA3_DATA (((1L<<XLEN)-1) << CSR_TDATA3_DATA_OFFSET)
|
||||
#define CSR_TDATA3_DATA_LENGTH MXLEN
|
||||
#define CSR_TDATA3_DATA (((1L<<MXLEN)-1) << CSR_TDATA3_DATA_OFFSET)
|
||||
#define CSR_TINFO 0x7a4
|
||||
/*
|
||||
* One bit for each possible \Ftype enumerated in \Rtdataone. Bit N
|
||||
* corresponds to type N. If the bit is set, then that type is
|
||||
* supported by the currently selected trigger.
|
||||
*
|
||||
* If the currently selected trigger doesn't exist, this field
|
||||
* contains 1.
|
||||
*
|
||||
* If \Ftype is not writable, this register may be unimplemented, in
|
||||
* which case reading it causes an illegal instruction exception. In
|
||||
* this case the debugger can read the only supported type from
|
||||
* \Rtdataone.
|
||||
*/
|
||||
#define CSR_TINFO_INFO_OFFSET 0
|
||||
#define CSR_TINFO_INFO_LENGTH 16
|
||||
#define CSR_TINFO_INFO (0xffffULL << CSR_TINFO_INFO_OFFSET)
|
||||
#define CSR_MCONTROL 0x7a1
|
||||
#define CSR_MCONTROL_TYPE_OFFSET (XLEN-4)
|
||||
#define CSR_MCONTROL_TYPE_OFFSET (MXLEN-4)
|
||||
#define CSR_MCONTROL_TYPE_LENGTH 4
|
||||
#define CSR_MCONTROL_TYPE (0xfULL << CSR_MCONTROL_TYPE_OFFSET)
|
||||
#define CSR_MCONTROL_DMODE_OFFSET (XLEN-5)
|
||||
#define CSR_MCONTROL_DMODE_OFFSET (MXLEN-5)
|
||||
#define CSR_MCONTROL_DMODE_LENGTH 1
|
||||
#define CSR_MCONTROL_DMODE (0x1ULL << CSR_MCONTROL_DMODE_OFFSET)
|
||||
/*
|
||||
* Specifies the largest naturally aligned powers-of-two (NAPOT) range
|
||||
* supported by the hardware. The value is the logarithm base 2 of the
|
||||
* supported by the hardware when \Fmatch is 1. The value is the
|
||||
* logarithm base 2 of the
|
||||
* number of bytes in that range. A value of 0 indicates that only
|
||||
* exact value matches are supported (one byte range). A value of 63
|
||||
* corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in
|
||||
* size.
|
||||
*/
|
||||
#define CSR_MCONTROL_MASKMAX_OFFSET (XLEN-11)
|
||||
#define CSR_MCONTROL_MASKMAX_OFFSET (MXLEN-11)
|
||||
#define CSR_MCONTROL_MASKMAX_LENGTH 6
|
||||
#define CSR_MCONTROL_MASKMAX (0x3fULL << CSR_MCONTROL_MASKMAX_OFFSET)
|
||||
/*
|
||||
@@ -390,22 +438,8 @@
|
||||
#define CSR_MCONTROL_TIMING_LENGTH 1
|
||||
#define CSR_MCONTROL_TIMING (0x1ULL << CSR_MCONTROL_TIMING_OFFSET)
|
||||
/*
|
||||
* Determines what happens when this trigger matches.
|
||||
*
|
||||
* 0: Raise a breakpoint exception. (Used when software wants to use
|
||||
* the trigger module without an external debugger attached.)
|
||||
*
|
||||
* 1: Enter Debug Mode. (Only supported when \Fdmode is 1.)
|
||||
*
|
||||
* 2: Start tracing.
|
||||
*
|
||||
* 3: Stop tracing.
|
||||
*
|
||||
* 4: Emit trace data for this match. If it is a data access match,
|
||||
* emit appropriate Load/Store Address/Data. If it is an instruction
|
||||
* execution, emit its PC.
|
||||
*
|
||||
* Other values are reserved for future use.
|
||||
* The action to take when the trigger fires. The values are explained
|
||||
* in Table~\ref{tab:action}.
|
||||
*/
|
||||
#define CSR_MCONTROL_ACTION_OFFSET 12
|
||||
#define CSR_MCONTROL_ACTION_LENGTH 6
|
||||
@@ -415,6 +449,18 @@
|
||||
*
|
||||
* 1: While this trigger does not match, it prevents the trigger with
|
||||
* the next index from matching.
|
||||
*
|
||||
* Because \Fchain affects the next trigger, hardware must zero it in
|
||||
* writes to \Rmcontrol that set \Fdmode to 0 if the next trigger has
|
||||
* \Fdmode of 1.
|
||||
* In addition hardware should ignore writes to \Rmcontrol that set
|
||||
* \Fdmode to 1 if the previous trigger has both \Fdmode of 0 and
|
||||
* \Fchain of 1. Debuggers must avoid the latter case by checking
|
||||
* \Fchain on the previous trigger if they're writing \Rmcontrol.
|
||||
*
|
||||
* Implementations that wish to limit the maximum length of a trigger
|
||||
* chain (eg. to meet timing requirements) may do so by zeroing
|
||||
* \Fchain in writes to \Rmcontrol that would make the chain too long.
|
||||
*/
|
||||
#define CSR_MCONTROL_CHAIN_OFFSET 11
|
||||
#define CSR_MCONTROL_CHAIN_LENGTH 1
|
||||
@@ -423,7 +469,7 @@
|
||||
* 0: Matches when the value equals \Rtdatatwo.
|
||||
*
|
||||
* 1: Matches when the top M bits of the value match the top M bits of
|
||||
* \Rtdatatwo. M is XLEN-1 minus the index of the least-significant
|
||||
* \Rtdatatwo. M is MXLEN-1 minus the index of the least-significant
|
||||
* bit containing 0 in \Rtdatatwo.
|
||||
*
|
||||
* 2: Matches when the value is greater than (unsigned) or equal to
|
||||
@@ -482,10 +528,10 @@
|
||||
#define CSR_MCONTROL_LOAD_LENGTH 1
|
||||
#define CSR_MCONTROL_LOAD (0x1ULL << CSR_MCONTROL_LOAD_OFFSET)
|
||||
#define CSR_ICOUNT 0x7a1
|
||||
#define CSR_ICOUNT_TYPE_OFFSET (XLEN-4)
|
||||
#define CSR_ICOUNT_TYPE_OFFSET (MXLEN-4)
|
||||
#define CSR_ICOUNT_TYPE_LENGTH 4
|
||||
#define CSR_ICOUNT_TYPE (0xfULL << CSR_ICOUNT_TYPE_OFFSET)
|
||||
#define CSR_ICOUNT_DMODE_OFFSET (XLEN-5)
|
||||
#define CSR_ICOUNT_DMODE_OFFSET (MXLEN-5)
|
||||
#define CSR_ICOUNT_DMODE_LENGTH 1
|
||||
#define CSR_ICOUNT_DMODE (0x1ULL << CSR_ICOUNT_DMODE_OFFSET)
|
||||
/*
|
||||
@@ -529,26 +575,102 @@
|
||||
#define CSR_ICOUNT_U_LENGTH 1
|
||||
#define CSR_ICOUNT_U (0x1ULL << CSR_ICOUNT_U_OFFSET)
|
||||
/*
|
||||
* Determines what happens when this trigger matches.
|
||||
*
|
||||
* 0: Raise a breakpoint exception. (Used when software wants to use the
|
||||
* trigger module without an external debugger attached.)
|
||||
*
|
||||
* 1: Enter Debug Mode. (Only supported when \Fdmode is 1.)
|
||||
*
|
||||
* 2: Start tracing.
|
||||
*
|
||||
* 3: Stop tracing.
|
||||
*
|
||||
* 4: Emit trace data for this match. If it is a data access match,
|
||||
* emit appropriate Load/Store Address/Data. If it is an instruction
|
||||
* execution, emit its PC.
|
||||
*
|
||||
* Other values are reserved for future use.
|
||||
* The action to take when the trigger fires. The values are explained
|
||||
* in Table~\ref{tab:action}.
|
||||
*/
|
||||
#define CSR_ICOUNT_ACTION_OFFSET 0
|
||||
#define CSR_ICOUNT_ACTION_LENGTH 6
|
||||
#define CSR_ICOUNT_ACTION (0x3fULL << CSR_ICOUNT_ACTION_OFFSET)
|
||||
#define CSR_ITRIGGER 0x7a1
|
||||
#define CSR_ITRIGGER_TYPE_OFFSET (MXLEN-4)
|
||||
#define CSR_ITRIGGER_TYPE_LENGTH 4
|
||||
#define CSR_ITRIGGER_TYPE (0xfULL << CSR_ITRIGGER_TYPE_OFFSET)
|
||||
#define CSR_ITRIGGER_DMODE_OFFSET (MXLEN-5)
|
||||
#define CSR_ITRIGGER_DMODE_LENGTH 1
|
||||
#define CSR_ITRIGGER_DMODE (0x1ULL << CSR_ITRIGGER_DMODE_OFFSET)
|
||||
/*
|
||||
* If this optional bit is implemented, the hardware sets it when this
|
||||
* trigger matches. The trigger's user can set or clear it at any
|
||||
* time. The trigger's user can use this bit to determine which
|
||||
* trigger(s) matched. If the bit is not implemented, it is always 0
|
||||
* and writing it has no effect.
|
||||
*/
|
||||
#define CSR_ITRIGGER_HIT_OFFSET (MXLEN-6)
|
||||
#define CSR_ITRIGGER_HIT_LENGTH 1
|
||||
#define CSR_ITRIGGER_HIT (0x1ULL << CSR_ITRIGGER_HIT_OFFSET)
|
||||
/*
|
||||
* When set, enable this trigger for interrupts that are taken from M
|
||||
* mode.
|
||||
*/
|
||||
#define CSR_ITRIGGER_M_OFFSET 9
|
||||
#define CSR_ITRIGGER_M_LENGTH 1
|
||||
#define CSR_ITRIGGER_M (0x1ULL << CSR_ITRIGGER_M_OFFSET)
|
||||
/*
|
||||
* When set, enable this trigger for interrupts that are taken from S
|
||||
* mode.
|
||||
*/
|
||||
#define CSR_ITRIGGER_S_OFFSET 7
|
||||
#define CSR_ITRIGGER_S_LENGTH 1
|
||||
#define CSR_ITRIGGER_S (0x1ULL << CSR_ITRIGGER_S_OFFSET)
|
||||
/*
|
||||
* When set, enable this trigger for interrupts that are taken from U
|
||||
* mode.
|
||||
*/
|
||||
#define CSR_ITRIGGER_U_OFFSET 6
|
||||
#define CSR_ITRIGGER_U_LENGTH 1
|
||||
#define CSR_ITRIGGER_U (0x1ULL << CSR_ITRIGGER_U_OFFSET)
|
||||
/*
|
||||
* The action to take when the trigger fires. The values are explained
|
||||
* in Table~\ref{tab:action}.
|
||||
*/
|
||||
#define CSR_ITRIGGER_ACTION_OFFSET 0
|
||||
#define CSR_ITRIGGER_ACTION_LENGTH 6
|
||||
#define CSR_ITRIGGER_ACTION (0x3fULL << CSR_ITRIGGER_ACTION_OFFSET)
|
||||
#define CSR_ETRIGGER 0x7a1
|
||||
#define CSR_ETRIGGER_TYPE_OFFSET (MXLEN-4)
|
||||
#define CSR_ETRIGGER_TYPE_LENGTH 4
|
||||
#define CSR_ETRIGGER_TYPE (0xfULL << CSR_ETRIGGER_TYPE_OFFSET)
|
||||
#define CSR_ETRIGGER_DMODE_OFFSET (MXLEN-5)
|
||||
#define CSR_ETRIGGER_DMODE_LENGTH 1
|
||||
#define CSR_ETRIGGER_DMODE (0x1ULL << CSR_ETRIGGER_DMODE_OFFSET)
|
||||
/*
|
||||
* If this optional bit is implemented, the hardware sets it when this
|
||||
* trigger matches. The trigger's user can set or clear it at any
|
||||
* time. The trigger's user can use this bit to determine which
|
||||
* trigger(s) matched. If the bit is not implemented, it is always 0
|
||||
* and writing it has no effect.
|
||||
*/
|
||||
#define CSR_ETRIGGER_HIT_OFFSET (MXLEN-6)
|
||||
#define CSR_ETRIGGER_HIT_LENGTH 1
|
||||
#define CSR_ETRIGGER_HIT (0x1ULL << CSR_ETRIGGER_HIT_OFFSET)
|
||||
/*
|
||||
* When set, enable this trigger for exceptions that are taken from M
|
||||
* mode.
|
||||
*/
|
||||
#define CSR_ETRIGGER_M_OFFSET 9
|
||||
#define CSR_ETRIGGER_M_LENGTH 1
|
||||
#define CSR_ETRIGGER_M (0x1ULL << CSR_ETRIGGER_M_OFFSET)
|
||||
/*
|
||||
* When set, enable this trigger for exceptions that are taken from S
|
||||
* mode.
|
||||
*/
|
||||
#define CSR_ETRIGGER_S_OFFSET 7
|
||||
#define CSR_ETRIGGER_S_LENGTH 1
|
||||
#define CSR_ETRIGGER_S (0x1ULL << CSR_ETRIGGER_S_OFFSET)
|
||||
/*
|
||||
* When set, enable this trigger for exceptions that are taken from U
|
||||
* mode.
|
||||
*/
|
||||
#define CSR_ETRIGGER_U_OFFSET 6
|
||||
#define CSR_ETRIGGER_U_LENGTH 1
|
||||
#define CSR_ETRIGGER_U (0x1ULL << CSR_ETRIGGER_U_OFFSET)
|
||||
/*
|
||||
* The action to take when the trigger fires. The values are explained
|
||||
* in Table~\ref{tab:action}.
|
||||
*/
|
||||
#define CSR_ETRIGGER_ACTION_OFFSET 0
|
||||
#define CSR_ETRIGGER_ACTION_LENGTH 6
|
||||
#define CSR_ETRIGGER_ACTION (0x3fULL << CSR_ETRIGGER_ACTION_OFFSET)
|
||||
#define DMI_DMSTATUS 0x11
|
||||
/*
|
||||
* If 1, then there is an implicit {\tt ebreak} instruction at the
|
||||
@@ -657,6 +779,14 @@
|
||||
#define DMI_DMSTATUS_AUTHBUSY_LENGTH 1
|
||||
#define DMI_DMSTATUS_AUTHBUSY (0x1U << DMI_DMSTATUS_AUTHBUSY_OFFSET)
|
||||
/*
|
||||
* 1 if this Debug Module supports halt-on-reset functionality
|
||||
* controllable by the \Fsetresethaltreq and \Fclrresethaltreq bits.
|
||||
* 0 otherwise.
|
||||
*/
|
||||
#define DMI_DMSTATUS_HASRESETHALTREQ_OFFSET 5
|
||||
#define DMI_DMSTATUS_HASRESETHALTREQ_LENGTH 1
|
||||
#define DMI_DMSTATUS_HASRESETHALTREQ (0x1U << DMI_DMSTATUS_HASRESETHALTREQ_OFFSET)
|
||||
/*
|
||||
* 0: \Rdevtreeaddrzero--\Rdevtreeaddrthree hold information which
|
||||
* is not relevant to the Device Tree.
|
||||
*
|
||||
@@ -762,6 +892,29 @@
|
||||
#define DMI_DMCONTROL_HARTSELHI_LENGTH 10
|
||||
#define DMI_DMCONTROL_HARTSELHI (0x3ffU << DMI_DMCONTROL_HARTSELHI_OFFSET)
|
||||
/*
|
||||
* This optional field writes the halt-on-reset request bit for all
|
||||
* currently selected harts.
|
||||
* When set to 1, each selected hart will halt upon the next deassertion
|
||||
* of its reset. The halt-on-reset request bit is not automatically
|
||||
* cleared. The debugger must write to \Fclrresethaltreq to clear it.
|
||||
*
|
||||
* Writes apply to the new value of \Fhartsel and \Fhasel.
|
||||
*
|
||||
* If \Fhasresethaltreq is 0, this field is not implemented.
|
||||
*/
|
||||
#define DMI_DMCONTROL_SETRESETHALTREQ_OFFSET 3
|
||||
#define DMI_DMCONTROL_SETRESETHALTREQ_LENGTH 1
|
||||
#define DMI_DMCONTROL_SETRESETHALTREQ (0x1U << DMI_DMCONTROL_SETRESETHALTREQ_OFFSET)
|
||||
/*
|
||||
* This optional field clears the halt-on-reset request bit for all
|
||||
* currently selected harts.
|
||||
*
|
||||
* Writes apply to the new value of \Fhartsel and \Fhasel.
|
||||
*/
|
||||
#define DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET 2
|
||||
#define DMI_DMCONTROL_CLRRESETHALTREQ_LENGTH 1
|
||||
#define DMI_DMCONTROL_CLRRESETHALTREQ (0x1U << DMI_DMCONTROL_CLRRESETHALTREQ_OFFSET)
|
||||
/*
|
||||
* This bit controls the reset signal from the DM to the rest of the
|
||||
* system. The signal should reset every part of the system, including
|
||||
* every hart, except for the DM and any logic required to access the
|
||||
@@ -786,7 +939,7 @@
|
||||
* Debug Module after power up, including the platform's system reset
|
||||
* or Debug Transport reset signals.
|
||||
*
|
||||
* A debugger may pulse this bit low to get the debug module into a
|
||||
* A debugger may pulse this bit low to get the Debug Module into a
|
||||
* known state.
|
||||
*
|
||||
* Implementations may use this bit to aid debugging, for example by
|
||||
@@ -808,7 +961,7 @@
|
||||
#define DMI_HARTINFO_NSCRATCH (0xfU << DMI_HARTINFO_NSCRATCH_OFFSET)
|
||||
/*
|
||||
* 0: The {\tt data} registers are shadowed in the hart by CSR
|
||||
* registers. Each CSR register is XLEN bits in size, and corresponds
|
||||
* registers. Each CSR register is MXLEN bits in size, and corresponds
|
||||
* to a single argument, per Table~\ref{tab:datareg}.
|
||||
*
|
||||
* 1: The {\tt data} registers are shadowed in the hart's memory map.
|
||||
@@ -999,7 +1152,7 @@
|
||||
* it's explicitly cleared by the debugger.
|
||||
*
|
||||
* While this field is non-zero, no more system bus accesses can be
|
||||
* initiated by the debug module.
|
||||
* initiated by the Debug Module.
|
||||
*/
|
||||
#define DMI_SBCS_SBBUSYERROR_OFFSET 22
|
||||
#define DMI_SBCS_SBBUSYERROR_LENGTH 1
|
||||
@@ -1010,8 +1163,9 @@
|
||||
* bit goes high immediately when a read or write is requested for any
|
||||
* reason, and does not go low until the access is fully completed.
|
||||
*
|
||||
* To avoid race conditions, debuggers must not try to clear \Fsberror
|
||||
* until they read \Fsbbusy as 0.
|
||||
* Writes to \Rsbcs while \Fsbbusy is high result in undefined
|
||||
* behavior. A debugger must not write to \Rsbcs until it reads
|
||||
* \Fsbbusy as 0.
|
||||
*/
|
||||
#define DMI_SBCS_SBBUSY_OFFSET 21
|
||||
#define DMI_SBCS_SBBUSY_LENGTH 1
|
||||
@@ -1057,11 +1211,13 @@
|
||||
#define DMI_SBCS_SBREADONDATA_LENGTH 1
|
||||
#define DMI_SBCS_SBREADONDATA (0x1U << DMI_SBCS_SBREADONDATA_OFFSET)
|
||||
/*
|
||||
* When the debug module's system bus
|
||||
* When the Debug Module's system bus
|
||||
* master causes a bus error, this field gets set. The bits in this
|
||||
* field remain set until they are cleared by writing 1 to them.
|
||||
* While this field is non-zero, no more system bus accesses can be
|
||||
* initiated by the debug module.
|
||||
* initiated by the Debug Module.
|
||||
*
|
||||
* An implementation may report "Other" (7) for any error condition.
|
||||
*
|
||||
* 0: There was no bus error.
|
||||
*
|
||||
@@ -1069,7 +1225,11 @@
|
||||
*
|
||||
* 2: A bad address was accessed.
|
||||
*
|
||||
* 3: There was some other error (eg. alignment).
|
||||
* 3: There was an alignment error.
|
||||
*
|
||||
* 4: An access of unsupported size was requested.
|
||||
*
|
||||
* 7: Other.
|
||||
*/
|
||||
#define DMI_SBCS_SBERROR_OFFSET 12
|
||||
#define DMI_SBCS_SBERROR_LENGTH 3
|
||||
@@ -1252,107 +1412,3 @@
|
||||
#define VIRT_PRIV_PRV_OFFSET 0
|
||||
#define VIRT_PRIV_PRV_LENGTH 2
|
||||
#define VIRT_PRIV_PRV (0x3U << VIRT_PRIV_PRV_OFFSET)
|
||||
#define DMI_SERCS 0x34
|
||||
/*
|
||||
* Number of supported serial ports.
|
||||
*/
|
||||
#define DMI_SERCS_SERIALCOUNT_OFFSET 28
|
||||
#define DMI_SERCS_SERIALCOUNT_LENGTH 4
|
||||
#define DMI_SERCS_SERIALCOUNT (0xfU << DMI_SERCS_SERIALCOUNT_OFFSET)
|
||||
/*
|
||||
* Select which serial port is accessed by \Rserrx and \Rsertx.
|
||||
*/
|
||||
#define DMI_SERCS_SERIAL_OFFSET 24
|
||||
#define DMI_SERCS_SERIAL_LENGTH 3
|
||||
#define DMI_SERCS_SERIAL (0x7U << DMI_SERCS_SERIAL_OFFSET)
|
||||
#define DMI_SERCS_ERROR7_OFFSET 23
|
||||
#define DMI_SERCS_ERROR7_LENGTH 1
|
||||
#define DMI_SERCS_ERROR7 (0x1U << DMI_SERCS_ERROR7_OFFSET)
|
||||
#define DMI_SERCS_VALID7_OFFSET 22
|
||||
#define DMI_SERCS_VALID7_LENGTH 1
|
||||
#define DMI_SERCS_VALID7 (0x1U << DMI_SERCS_VALID7_OFFSET)
|
||||
#define DMI_SERCS_FULL7_OFFSET 21
|
||||
#define DMI_SERCS_FULL7_LENGTH 1
|
||||
#define DMI_SERCS_FULL7 (0x1U << DMI_SERCS_FULL7_OFFSET)
|
||||
#define DMI_SERCS_ERROR6_OFFSET 20
|
||||
#define DMI_SERCS_ERROR6_LENGTH 1
|
||||
#define DMI_SERCS_ERROR6 (0x1U << DMI_SERCS_ERROR6_OFFSET)
|
||||
#define DMI_SERCS_VALID6_OFFSET 19
|
||||
#define DMI_SERCS_VALID6_LENGTH 1
|
||||
#define DMI_SERCS_VALID6 (0x1U << DMI_SERCS_VALID6_OFFSET)
|
||||
#define DMI_SERCS_FULL6_OFFSET 18
|
||||
#define DMI_SERCS_FULL6_LENGTH 1
|
||||
#define DMI_SERCS_FULL6 (0x1U << DMI_SERCS_FULL6_OFFSET)
|
||||
#define DMI_SERCS_ERROR5_OFFSET 17
|
||||
#define DMI_SERCS_ERROR5_LENGTH 1
|
||||
#define DMI_SERCS_ERROR5 (0x1U << DMI_SERCS_ERROR5_OFFSET)
|
||||
#define DMI_SERCS_VALID5_OFFSET 16
|
||||
#define DMI_SERCS_VALID5_LENGTH 1
|
||||
#define DMI_SERCS_VALID5 (0x1U << DMI_SERCS_VALID5_OFFSET)
|
||||
#define DMI_SERCS_FULL5_OFFSET 15
|
||||
#define DMI_SERCS_FULL5_LENGTH 1
|
||||
#define DMI_SERCS_FULL5 (0x1U << DMI_SERCS_FULL5_OFFSET)
|
||||
#define DMI_SERCS_ERROR4_OFFSET 14
|
||||
#define DMI_SERCS_ERROR4_LENGTH 1
|
||||
#define DMI_SERCS_ERROR4 (0x1U << DMI_SERCS_ERROR4_OFFSET)
|
||||
#define DMI_SERCS_VALID4_OFFSET 13
|
||||
#define DMI_SERCS_VALID4_LENGTH 1
|
||||
#define DMI_SERCS_VALID4 (0x1U << DMI_SERCS_VALID4_OFFSET)
|
||||
#define DMI_SERCS_FULL4_OFFSET 12
|
||||
#define DMI_SERCS_FULL4_LENGTH 1
|
||||
#define DMI_SERCS_FULL4 (0x1U << DMI_SERCS_FULL4_OFFSET)
|
||||
#define DMI_SERCS_ERROR3_OFFSET 11
|
||||
#define DMI_SERCS_ERROR3_LENGTH 1
|
||||
#define DMI_SERCS_ERROR3 (0x1U << DMI_SERCS_ERROR3_OFFSET)
|
||||
#define DMI_SERCS_VALID3_OFFSET 10
|
||||
#define DMI_SERCS_VALID3_LENGTH 1
|
||||
#define DMI_SERCS_VALID3 (0x1U << DMI_SERCS_VALID3_OFFSET)
|
||||
#define DMI_SERCS_FULL3_OFFSET 9
|
||||
#define DMI_SERCS_FULL3_LENGTH 1
|
||||
#define DMI_SERCS_FULL3 (0x1U << DMI_SERCS_FULL3_OFFSET)
|
||||
#define DMI_SERCS_ERROR2_OFFSET 8
|
||||
#define DMI_SERCS_ERROR2_LENGTH 1
|
||||
#define DMI_SERCS_ERROR2 (0x1U << DMI_SERCS_ERROR2_OFFSET)
|
||||
#define DMI_SERCS_VALID2_OFFSET 7
|
||||
#define DMI_SERCS_VALID2_LENGTH 1
|
||||
#define DMI_SERCS_VALID2 (0x1U << DMI_SERCS_VALID2_OFFSET)
|
||||
#define DMI_SERCS_FULL2_OFFSET 6
|
||||
#define DMI_SERCS_FULL2_LENGTH 1
|
||||
#define DMI_SERCS_FULL2 (0x1U << DMI_SERCS_FULL2_OFFSET)
|
||||
#define DMI_SERCS_ERROR1_OFFSET 5
|
||||
#define DMI_SERCS_ERROR1_LENGTH 1
|
||||
#define DMI_SERCS_ERROR1 (0x1U << DMI_SERCS_ERROR1_OFFSET)
|
||||
#define DMI_SERCS_VALID1_OFFSET 4
|
||||
#define DMI_SERCS_VALID1_LENGTH 1
|
||||
#define DMI_SERCS_VALID1 (0x1U << DMI_SERCS_VALID1_OFFSET)
|
||||
#define DMI_SERCS_FULL1_OFFSET 3
|
||||
#define DMI_SERCS_FULL1_LENGTH 1
|
||||
#define DMI_SERCS_FULL1 (0x1U << DMI_SERCS_FULL1_OFFSET)
|
||||
/*
|
||||
* 1 when the debugger-to-core queue for serial port 0 has
|
||||
* over or underflowed. This bit will remain set until it is reset by
|
||||
* writing 1 to this bit.
|
||||
*/
|
||||
#define DMI_SERCS_ERROR0_OFFSET 2
|
||||
#define DMI_SERCS_ERROR0_LENGTH 1
|
||||
#define DMI_SERCS_ERROR0 (0x1U << DMI_SERCS_ERROR0_OFFSET)
|
||||
/*
|
||||
* 1 when the core-to-debugger queue for serial port 0 is not empty.
|
||||
*/
|
||||
#define DMI_SERCS_VALID0_OFFSET 1
|
||||
#define DMI_SERCS_VALID0_LENGTH 1
|
||||
#define DMI_SERCS_VALID0 (0x1U << DMI_SERCS_VALID0_OFFSET)
|
||||
/*
|
||||
* 1 when the debugger-to-core queue for serial port 0 is full.
|
||||
*/
|
||||
#define DMI_SERCS_FULL0_OFFSET 0
|
||||
#define DMI_SERCS_FULL0_LENGTH 1
|
||||
#define DMI_SERCS_FULL0 (0x1U << DMI_SERCS_FULL0_OFFSET)
|
||||
#define DMI_SERTX 0x35
|
||||
#define DMI_SERTX_DATA_OFFSET 0
|
||||
#define DMI_SERTX_DATA_LENGTH 32
|
||||
#define DMI_SERTX_DATA (0xffffffffU << DMI_SERTX_DATA_OFFSET)
|
||||
#define DMI_SERRX 0x36
|
||||
#define DMI_SERRX_DATA_OFFSET 0
|
||||
#define DMI_SERRX_DATA_LENGTH 32
|
||||
#define DMI_SERRX_DATA (0xffffffffU << DMI_SERRX_DATA_OFFSET)
|
||||
|
||||
@@ -240,6 +240,7 @@ static unsigned int slot_offset(const struct target *target, slot_t slot)
|
||||
case SLOT1: return 5;
|
||||
case SLOT_LAST: return info->dramsize-1;
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
switch (slot) {
|
||||
case SLOT0: return 4;
|
||||
@@ -1407,12 +1408,6 @@ static int strict_step(struct target *target, bool announce)
|
||||
|
||||
LOG_DEBUG("enter");
|
||||
|
||||
struct breakpoint *breakpoint = target->breakpoints;
|
||||
while (breakpoint) {
|
||||
riscv_remove_breakpoint(target, breakpoint);
|
||||
breakpoint = breakpoint->next;
|
||||
}
|
||||
|
||||
struct watchpoint *watchpoint = target->watchpoints;
|
||||
while (watchpoint) {
|
||||
riscv_remove_watchpoint(target, watchpoint);
|
||||
@@ -1423,12 +1418,6 @@ static int strict_step(struct target *target, bool announce)
|
||||
if (result != ERROR_OK)
|
||||
return result;
|
||||
|
||||
breakpoint = target->breakpoints;
|
||||
while (breakpoint) {
|
||||
riscv_add_breakpoint(target, breakpoint);
|
||||
breakpoint = breakpoint->next;
|
||||
}
|
||||
|
||||
watchpoint = target->watchpoints;
|
||||
while (watchpoint) {
|
||||
riscv_add_watchpoint(target, watchpoint);
|
||||
@@ -1787,6 +1776,8 @@ static riscv_error_t handle_halt_routine(struct target *target)
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
LOG_ERROR("Got invalid register result %d", result);
|
||||
goto error;
|
||||
}
|
||||
if (riscv_xlen(target) == 32) {
|
||||
reg_cache_set(target, reg, data & 0xffffffff);
|
||||
@@ -1847,7 +1838,7 @@ static int handle_halt(struct target *target, bool announce)
|
||||
target->debug_reason = DBG_REASON_BREAKPOINT;
|
||||
break;
|
||||
case DCSR_CAUSE_HWBP:
|
||||
target->debug_reason = DBG_REASON_WPTANDBKPT;
|
||||
target->debug_reason = DBG_REASON_WATCHPOINT;
|
||||
/* If we halted because of a data trigger, gdb doesn't know to do
|
||||
* the disable-breakpoints-step-enable-breakpoints dance. */
|
||||
info->need_strict_step = true;
|
||||
@@ -1873,6 +1864,12 @@ static int handle_halt(struct target *target, bool announce)
|
||||
riscv_enumerate_triggers(target);
|
||||
}
|
||||
|
||||
if (target->debug_reason == DBG_REASON_BREAKPOINT) {
|
||||
int retval;
|
||||
if (riscv_semihosting(target, &retval) != 0)
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (announce)
|
||||
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||
|
||||
|
||||
@@ -189,8 +189,6 @@ typedef struct {
|
||||
* go low. */
|
||||
unsigned int ac_busy_delay;
|
||||
|
||||
bool need_strict_step;
|
||||
|
||||
bool abstract_read_csr_supported;
|
||||
bool abstract_write_csr_supported;
|
||||
bool abstract_read_fpr_supported;
|
||||
@@ -264,11 +262,18 @@ static dm013_info_t *get_dm(struct target *target)
|
||||
return dm;
|
||||
}
|
||||
|
||||
static uint32_t hartsel_mask(const struct target *target)
|
||||
static uint32_t set_hartsel(uint32_t initial, uint32_t index)
|
||||
{
|
||||
RISCV013_INFO(info);
|
||||
/* TODO: Properly handle hartselhi as well*/
|
||||
return ((1L<<info->hartsellen)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET;
|
||||
initial &= ~DMI_DMCONTROL_HARTSELLO;
|
||||
initial &= ~DMI_DMCONTROL_HARTSELHI;
|
||||
|
||||
uint32_t index_lo = index & ((1 << DMI_DMCONTROL_HARTSELLO_LENGTH) - 1);
|
||||
initial |= index_lo << DMI_DMCONTROL_HARTSELLO_OFFSET;
|
||||
uint32_t index_hi = index >> DMI_DMCONTROL_HARTSELLO_LENGTH;
|
||||
assert(index_hi < 1 << DMI_DMCONTROL_HARTSELHI_LENGTH);
|
||||
initial |= index_hi << DMI_DMCONTROL_HARTSELHI_OFFSET;
|
||||
|
||||
return initial;
|
||||
}
|
||||
|
||||
static void decode_dmi(char *text, unsigned address, unsigned data)
|
||||
@@ -282,8 +287,8 @@ static void decode_dmi(char *text, unsigned address, unsigned data)
|
||||
{ DMI_DMCONTROL, DMI_DMCONTROL_RESUMEREQ, "resumereq" },
|
||||
{ DMI_DMCONTROL, DMI_DMCONTROL_HARTRESET, "hartreset" },
|
||||
{ DMI_DMCONTROL, DMI_DMCONTROL_HASEL, "hasel" },
|
||||
{ DMI_DMCONTROL, ((1L<<10)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET, "hartsello" },
|
||||
/* TODO: hartsellhi */
|
||||
{ DMI_DMCONTROL, DMI_DMCONTROL_HARTSELHI, "hartselhi" },
|
||||
{ DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO, "hartsello" },
|
||||
{ DMI_DMCONTROL, DMI_DMCONTROL_NDMRESET, "ndmreset" },
|
||||
{ DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE, "dmactive" },
|
||||
{ DMI_DMCONTROL, DMI_DMCONTROL_ACKHAVERESET, "ackhavereset" },
|
||||
@@ -741,8 +746,8 @@ static int write_abstract_arg(struct target *target, unsigned index,
|
||||
/**
|
||||
* @size in bits
|
||||
*/
|
||||
static uint32_t access_register_command(uint32_t number, unsigned size,
|
||||
uint32_t flags)
|
||||
static uint32_t access_register_command(struct target *target, uint32_t number,
|
||||
unsigned size, uint32_t flags)
|
||||
{
|
||||
uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0);
|
||||
switch (size) {
|
||||
@@ -765,8 +770,13 @@ static uint32_t access_register_command(uint32_t number, unsigned size,
|
||||
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
|
||||
command = set_field(command, AC_ACCESS_REGISTER_REGNO,
|
||||
number - GDB_REGNO_CSR0);
|
||||
} else {
|
||||
assert(0);
|
||||
} else if (number >= GDB_REGNO_COUNT) {
|
||||
/* Custom register. */
|
||||
assert(target->reg_cache->reg_list[number].arch_info);
|
||||
riscv_reg_info_t *reg_info = target->reg_cache->reg_list[number].arch_info;
|
||||
assert(reg_info);
|
||||
command = set_field(command, AC_ACCESS_REGISTER_REGNO,
|
||||
0xc000 + reg_info->custom_number);
|
||||
}
|
||||
|
||||
command |= flags;
|
||||
@@ -786,7 +796,7 @@ static int register_read_abstract(struct target *target, uint64_t *value,
|
||||
!info->abstract_read_csr_supported)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint32_t command = access_register_command(number, size,
|
||||
uint32_t command = access_register_command(target, number, size,
|
||||
AC_ACCESS_REGISTER_TRANSFER);
|
||||
|
||||
int result = execute_abstract_command(target, command);
|
||||
@@ -821,7 +831,7 @@ static int register_write_abstract(struct target *target, uint32_t number,
|
||||
!info->abstract_write_csr_supported)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint32_t command = access_register_command(number, size,
|
||||
uint32_t command = access_register_command(target, number, size,
|
||||
AC_ACCESS_REGISTER_TRANSFER |
|
||||
AC_ACCESS_REGISTER_WRITE);
|
||||
|
||||
@@ -917,22 +927,25 @@ typedef struct {
|
||||
riscv_addr_t hart_address;
|
||||
/* Memory address to access the scratch memory from the debugger. */
|
||||
riscv_addr_t debug_address;
|
||||
struct working_area *area;
|
||||
} scratch_mem_t;
|
||||
|
||||
/**
|
||||
* Find some scratch memory to be used with the given program.
|
||||
*/
|
||||
static int scratch_find(struct target *target,
|
||||
static int scratch_reserve(struct target *target,
|
||||
scratch_mem_t *scratch,
|
||||
struct riscv_program *program,
|
||||
unsigned size_bytes)
|
||||
{
|
||||
riscv013_info_t *info = get_info(target);
|
||||
|
||||
riscv_addr_t alignment = 1;
|
||||
while (alignment < size_bytes)
|
||||
alignment *= 2;
|
||||
|
||||
scratch->area = NULL;
|
||||
|
||||
riscv013_info_t *info = get_info(target);
|
||||
|
||||
if (info->dataaccess == 1) {
|
||||
/* Sign extend dataaddr. */
|
||||
scratch->hart_address = info->dataaddr;
|
||||
@@ -963,8 +976,9 @@ static int scratch_find(struct target *target,
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
if (riscv_use_scratch_ram) {
|
||||
scratch->hart_address = (riscv_scratch_ram_address + alignment - 1) &
|
||||
if (target_alloc_working_area(target, size_bytes + alignment - 1,
|
||||
&scratch->area) == ERROR_OK) {
|
||||
scratch->hart_address = (scratch->area->address + alignment - 1) &
|
||||
~(alignment - 1);
|
||||
scratch->memory_space = SPACE_DMI_RAM;
|
||||
scratch->debug_address = scratch->hart_address;
|
||||
@@ -972,10 +986,19 @@ static int scratch_find(struct target *target,
|
||||
}
|
||||
|
||||
LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure "
|
||||
"an address with 'riscv set_scratch_ram'.", size_bytes);
|
||||
"a work area with 'configure -work-area-phys'.", size_bytes);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
static int scratch_release(struct target *target,
|
||||
scratch_mem_t *scratch)
|
||||
{
|
||||
if (scratch->area)
|
||||
return target_free_working_area(target, scratch->area);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int scratch_read64(struct target *target, scratch_mem_t *scratch,
|
||||
uint64_t *value)
|
||||
{
|
||||
@@ -1090,23 +1113,29 @@ static int register_write_direct(struct target *target, unsigned number,
|
||||
if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
scratch_mem_t scratch;
|
||||
bool use_scratch = false;
|
||||
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
|
||||
riscv_supports_extension(target, riscv_current_hartid(target), 'D') &&
|
||||
riscv_xlen(target) < 64) {
|
||||
/* There are no instructions to move all the bits from a register, so
|
||||
* we need to use some scratch RAM. */
|
||||
use_scratch = true;
|
||||
riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0));
|
||||
|
||||
scratch_mem_t scratch;
|
||||
if (scratch_find(target, &scratch, &program, 8) != ERROR_OK)
|
||||
if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address)
|
||||
!= ERROR_OK)
|
||||
!= ERROR_OK) {
|
||||
scratch_release(target, &scratch);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (scratch_write64(target, &scratch, value) != ERROR_OK)
|
||||
if (scratch_write64(target, &scratch, value) != ERROR_OK) {
|
||||
scratch_release(target, &scratch);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK)
|
||||
@@ -1133,6 +1162,9 @@ static int register_write_direct(struct target *target, unsigned number,
|
||||
reg->valid = true;
|
||||
}
|
||||
|
||||
if (use_scratch)
|
||||
scratch_release(target, &scratch);
|
||||
|
||||
/* Restore S0. */
|
||||
if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
@@ -1209,13 +1241,15 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
|
||||
riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0,
|
||||
0));
|
||||
|
||||
if (scratch_find(target, &scratch, &program, 8) != ERROR_OK)
|
||||
if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
use_scratch = true;
|
||||
|
||||
if (register_write_direct(target, GDB_REGNO_S0,
|
||||
scratch.hart_address) != ERROR_OK)
|
||||
scratch.hart_address) != ERROR_OK) {
|
||||
scratch_release(target, &scratch);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
} else if (riscv_supports_extension(target,
|
||||
riscv_current_hartid(target), 'D')) {
|
||||
riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
|
||||
@@ -1234,8 +1268,10 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
|
||||
/* Don't message on error. Probably the register doesn't exist. */
|
||||
|
||||
if (use_scratch) {
|
||||
if (scratch_read64(target, &scratch, value) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
result = scratch_read64(target, &scratch, value);
|
||||
scratch_release(target, &scratch);
|
||||
if (result != ERROR_OK)
|
||||
return result;
|
||||
} else {
|
||||
/* Read S0 */
|
||||
if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK)
|
||||
@@ -1290,6 +1326,7 @@ static void deinit_target(struct target *target)
|
||||
LOG_DEBUG("riscv_deinit_target()");
|
||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||
free(info->version_specific);
|
||||
/* TODO: free register arch_info */
|
||||
info->version_specific = NULL;
|
||||
}
|
||||
|
||||
@@ -1336,8 +1373,8 @@ static int examine(struct target *target)
|
||||
dm->was_reset = true;
|
||||
}
|
||||
|
||||
uint32_t max_hartsel_mask = ((1L<<10)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET;
|
||||
dmi_write(target, DMI_DMCONTROL, max_hartsel_mask | DMI_DMCONTROL_DMACTIVE);
|
||||
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO |
|
||||
DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE);
|
||||
uint32_t dmcontrol;
|
||||
if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
@@ -1348,7 +1385,10 @@ static int examine(struct target *target)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
uint32_t hartsel = get_field(dmcontrol, max_hartsel_mask);
|
||||
uint32_t hartsel =
|
||||
(get_field(dmcontrol, DMI_DMCONTROL_HARTSELHI) <<
|
||||
DMI_DMCONTROL_HARTSELLO_LENGTH) |
|
||||
get_field(dmcontrol, DMI_DMCONTROL_HARTSELLO);
|
||||
info->hartsellen = 0;
|
||||
while (hartsel & 1) {
|
||||
info->hartsellen++;
|
||||
@@ -1418,8 +1458,7 @@ static int examine(struct target *target)
|
||||
|
||||
if (get_field(s, DMI_DMSTATUS_ANYHAVERESET))
|
||||
dmi_write(target, DMI_DMCONTROL,
|
||||
set_field(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET,
|
||||
hartsel_mask(target), i));
|
||||
set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i));
|
||||
|
||||
if (!riscv_is_halted(target)) {
|
||||
if (riscv013_halt_current_hart(target) != ERROR_OK) {
|
||||
@@ -1595,7 +1634,7 @@ static int assert_reset(struct target *target)
|
||||
if (!riscv_hart_enabled(target, i))
|
||||
continue;
|
||||
|
||||
control = set_field(control_base, hartsel_mask(target), i);
|
||||
control = set_hartsel(control_base, i);
|
||||
control = set_field(control, DMI_DMCONTROL_HALTREQ,
|
||||
target->reset_halt ? 1 : 0);
|
||||
dmi_write(target, DMI_DMCONTROL, control);
|
||||
@@ -1606,8 +1645,7 @@ static int assert_reset(struct target *target)
|
||||
|
||||
} else {
|
||||
/* Reset just this hart. */
|
||||
uint32_t control = set_field(control_base, hartsel_mask(target),
|
||||
r->current_hartid);
|
||||
uint32_t control = set_hartsel(control_base, r->current_hartid);
|
||||
control = set_field(control, DMI_DMCONTROL_HALTREQ,
|
||||
target->reset_halt ? 1 : 0);
|
||||
control = set_field(control, DMI_DMCONTROL_NDMRESET, 1);
|
||||
@@ -1630,7 +1668,7 @@ static int deassert_reset(struct target *target)
|
||||
control = set_field(control, DMI_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
|
||||
control = set_field(control, DMI_DMCONTROL_DMACTIVE, 1);
|
||||
dmi_write(target, DMI_DMCONTROL,
|
||||
set_field(control, hartsel_mask(target), r->current_hartid));
|
||||
set_hartsel(control, r->current_hartid));
|
||||
|
||||
uint32_t dmstatus;
|
||||
int dmi_busy_delay = info->dmi_busy_delay;
|
||||
@@ -1642,7 +1680,7 @@ static int deassert_reset(struct target *target)
|
||||
if (!riscv_hart_enabled(target, index))
|
||||
continue;
|
||||
dmi_write(target, DMI_DMCONTROL,
|
||||
set_field(control, hartsel_mask(target), index));
|
||||
set_hartsel(control, index));
|
||||
} else {
|
||||
index = r->current_hartid;
|
||||
}
|
||||
@@ -1682,7 +1720,7 @@ static int deassert_reset(struct target *target)
|
||||
if (get_field(dmstatus, DMI_DMSTATUS_ALLHAVERESET)) {
|
||||
/* Ack reset. */
|
||||
dmi_write(target, DMI_DMCONTROL,
|
||||
set_field(control, hartsel_mask(target), index) |
|
||||
set_hartsel(control, index) |
|
||||
DMI_DMCONTROL_ACKHAVERESET);
|
||||
}
|
||||
|
||||
@@ -2042,9 +2080,9 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
|
||||
result = register_write_direct(target, GDB_REGNO_S0, address);
|
||||
if (result != ERROR_OK)
|
||||
goto error;
|
||||
uint32_t command = access_register_command(GDB_REGNO_S1, riscv_xlen(target),
|
||||
AC_ACCESS_REGISTER_TRANSFER |
|
||||
AC_ACCESS_REGISTER_POSTEXEC);
|
||||
uint32_t command = access_register_command(target, GDB_REGNO_S1,
|
||||
riscv_xlen(target),
|
||||
AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
|
||||
result = execute_abstract_command(target, command);
|
||||
if (result != ERROR_OK)
|
||||
goto error;
|
||||
@@ -2530,7 +2568,8 @@ static int write_memory_progbuf(struct target *target, target_addr_t address,
|
||||
|
||||
/* Write and execute command that moves value into S1 and
|
||||
* executes program buffer. */
|
||||
uint32_t command = access_register_command(GDB_REGNO_S1, 32,
|
||||
uint32_t command = access_register_command(target,
|
||||
GDB_REGNO_S1, 32,
|
||||
AC_ACCESS_REGISTER_POSTEXEC |
|
||||
AC_ACCESS_REGISTER_TRANSFER |
|
||||
AC_ACCESS_REGISTER_WRITE);
|
||||
@@ -2723,9 +2762,10 @@ static int riscv013_select_current_hart(struct target *target)
|
||||
return ERROR_OK;
|
||||
|
||||
uint32_t dmcontrol;
|
||||
/* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */
|
||||
if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
|
||||
return ERROR_FAIL;
|
||||
dmcontrol = set_field(dmcontrol, hartsel_mask(target), r->current_hartid);
|
||||
dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
|
||||
int result = dmi_write(target, DMI_DMCONTROL, dmcontrol);
|
||||
dm->current_hartid = r->current_hartid;
|
||||
return result;
|
||||
@@ -2807,7 +2847,7 @@ static bool riscv013_is_halted(struct target *target)
|
||||
/* TODO: Can we make this more obvious to eg. a gdb user? */
|
||||
uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE |
|
||||
DMI_DMCONTROL_ACKHAVERESET;
|
||||
dmcontrol = set_field(dmcontrol, hartsel_mask(target), hartid);
|
||||
dmcontrol = set_hartsel(dmcontrol, hartid);
|
||||
/* If we had been halted when we reset, request another halt. If we
|
||||
* ended up running out of reset, then the user will (hopefully) get a
|
||||
* message that a reset happened, that the target is running, and then
|
||||
@@ -2950,7 +2990,7 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step
|
||||
|
||||
/* Issue the resume command, and then wait for the current hart to resume. */
|
||||
uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE;
|
||||
dmcontrol = set_field(dmcontrol, hartsel_mask(target), r->current_hartid);
|
||||
dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
|
||||
dmi_write(target, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ);
|
||||
|
||||
uint32_t dmstatus;
|
||||
|
||||
+269
-67
@@ -185,18 +185,19 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
|
||||
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
|
||||
int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
|
||||
|
||||
bool riscv_use_scratch_ram;
|
||||
uint64_t riscv_scratch_ram_address;
|
||||
|
||||
bool riscv_prefer_sba;
|
||||
|
||||
typedef struct {
|
||||
uint16_t low, high;
|
||||
} range_t;
|
||||
|
||||
/* In addition to the ones in the standard spec, we'll also expose additional
|
||||
* CSRs in this list.
|
||||
* The list is either NULL, or a series of ranges (inclusive), terminated with
|
||||
* 1,0. */
|
||||
struct {
|
||||
uint16_t low, high;
|
||||
} *expose_csr;
|
||||
range_t *expose_csr;
|
||||
/* Same, but for custom registers. */
|
||||
range_t *expose_custom;
|
||||
|
||||
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
|
||||
{
|
||||
@@ -263,6 +264,8 @@ static int riscv_init_target(struct command_context *cmd_ctx,
|
||||
select_dbus.num_bits = target->tap->ir_length;
|
||||
select_idcode.num_bits = target->tap->ir_length;
|
||||
|
||||
riscv_semihosting_init(target);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
@@ -273,8 +276,16 @@ static void riscv_deinit_target(struct target *target)
|
||||
if (tt) {
|
||||
tt->deinit_target(target);
|
||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||
free(info->reg_names);
|
||||
free(info);
|
||||
}
|
||||
/* Free the shared structure use for most registers. */
|
||||
free(target->reg_cache->reg_list[0].arch_info);
|
||||
/* Free the ones we allocated separately. */
|
||||
for (unsigned i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
|
||||
free(target->reg_cache->reg_list[i].arch_info);
|
||||
free(target->reg_cache->reg_list);
|
||||
free(target->reg_cache);
|
||||
target->arch_info = NULL;
|
||||
}
|
||||
|
||||
@@ -644,6 +655,89 @@ int riscv_remove_watchpoint(struct target *target,
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/* Sets *hit_watchpoint to the first watchpoint identified as causing the
|
||||
* current halt.
|
||||
*
|
||||
* The GDB server uses this information to tell GDB what data address has
|
||||
* been hit, which enables GDB to print the hit variable along with its old
|
||||
* and new value. */
|
||||
int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint)
|
||||
{
|
||||
struct watchpoint *wp = target->watchpoints;
|
||||
|
||||
LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
|
||||
|
||||
/*TODO instead of disassembling the instruction that we think caused the
|
||||
* trigger, check the hit bit of each watchpoint first. The hit bit is
|
||||
* simpler and more reliable to check but as it is optional and relatively
|
||||
* new, not all hardware will implement it */
|
||||
riscv_reg_t dpc;
|
||||
riscv_get_register(target, &dpc, GDB_REGNO_DPC);
|
||||
const uint8_t length = 4;
|
||||
LOG_DEBUG("dpc is 0x%" PRIx64, dpc);
|
||||
|
||||
/* fetch the instruction at dpc */
|
||||
uint8_t buffer[length];
|
||||
if (target_read_buffer(target, dpc, length, buffer) != ERROR_OK) {
|
||||
LOG_ERROR("Failed to read instruction at dpc 0x%" PRIx64, dpc);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
uint32_t instruction = 0;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
LOG_DEBUG("Next byte is %x", buffer[i]);
|
||||
instruction += (buffer[i] << 8 * i);
|
||||
}
|
||||
LOG_DEBUG("Full instruction is %x", instruction);
|
||||
|
||||
/* find out which memory address is accessed by the instruction at dpc */
|
||||
/* opcode is first 7 bits of the instruction */
|
||||
uint8_t opcode = instruction & 0x7F;
|
||||
uint32_t rs1;
|
||||
int16_t imm;
|
||||
riscv_reg_t mem_addr;
|
||||
|
||||
if (opcode == MATCH_LB || opcode == MATCH_SB) {
|
||||
rs1 = (instruction & 0xf8000) >> 15;
|
||||
riscv_get_register(target, &mem_addr, rs1);
|
||||
|
||||
if (opcode == MATCH_SB) {
|
||||
LOG_DEBUG("%x is store instruction", instruction);
|
||||
imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20);
|
||||
} else {
|
||||
LOG_DEBUG("%x is load instruction", instruction);
|
||||
imm = (instruction & 0xfff00000) >> 20;
|
||||
}
|
||||
/* sign extend 12-bit imm to 16-bits */
|
||||
if (imm & (1 << 11))
|
||||
imm |= 0xf000;
|
||||
mem_addr += imm;
|
||||
LOG_DEBUG("memory address=0x%" PRIx64, mem_addr);
|
||||
} else {
|
||||
LOG_DEBUG("%x is not a RV32I load or store", instruction);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
while (wp) {
|
||||
/*TODO support length/mask */
|
||||
if (wp->address == mem_addr) {
|
||||
*hit_watchpoint = wp;
|
||||
LOG_DEBUG("Hit address=%" TARGET_PRIxADDR, wp->address);
|
||||
return ERROR_OK;
|
||||
}
|
||||
wp = wp->next;
|
||||
}
|
||||
|
||||
/* No match found - either we hit a watchpoint caused by an instruction that
|
||||
* this function does not yet disassemble, or we hit a breakpoint.
|
||||
*
|
||||
* OpenOCD will behave as if this function had never been implemented i.e.
|
||||
* report the halt to GDB with no address information. */
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
|
||||
static int oldriscv_step(struct target *target, int current, uint32_t address,
|
||||
int handle_breakpoints)
|
||||
{
|
||||
@@ -802,7 +896,7 @@ static int riscv_get_gdb_reg_list(struct target *target,
|
||||
*reg_list_size = 32;
|
||||
break;
|
||||
case REG_CLASS_ALL:
|
||||
*reg_list_size = GDB_REGNO_COUNT;
|
||||
*reg_list_size = target->reg_cache->num_regs;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unsupported reg_class: %d", reg_class);
|
||||
@@ -1068,9 +1162,17 @@ int riscv_openocd_poll(struct target *target)
|
||||
if (riscv_rtos_enabled(target)) {
|
||||
target->rtos->current_threadid = halted_hart + 1;
|
||||
target->rtos->current_thread = halted_hart + 1;
|
||||
riscv_set_rtos_hartid(target, halted_hart);
|
||||
}
|
||||
|
||||
target->state = TARGET_HALTED;
|
||||
|
||||
if (target->debug_reason == DBG_REASON_BREAKPOINT) {
|
||||
int retval;
|
||||
if (riscv_semihosting(target, &retval) != 0)
|
||||
return retval;
|
||||
}
|
||||
|
||||
target_call_event_callbacks(target, TARGET_EVENT_HALTED);
|
||||
return ERROR_OK;
|
||||
}
|
||||
@@ -1240,31 +1342,6 @@ COMMAND_HANDLER(riscv_test_compliance) {
|
||||
}
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(riscv_set_scratch_ram)
|
||||
{
|
||||
if (CMD_ARGC != 1) {
|
||||
LOG_ERROR("Command takes exactly 1 parameter");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
if (!strcmp(CMD_ARGV[0], "none")) {
|
||||
riscv_use_scratch_ram = false;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/* TODO: use COMMAND_PARSE_NUMBER */
|
||||
long long unsigned int address;
|
||||
int result = sscanf(CMD_ARGV[0], "%llx", &address);
|
||||
if (result != (int) strlen(CMD_ARGV[0])) {
|
||||
LOG_ERROR("%s is not a valid address for command.", CMD_ARGV[0]);
|
||||
riscv_use_scratch_ram = false;
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
riscv_scratch_ram_address = address;
|
||||
riscv_use_scratch_ram = true;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(riscv_set_prefer_sba)
|
||||
{
|
||||
if (CMD_ARGC != 1) {
|
||||
@@ -1288,20 +1365,15 @@ void parse_error(const char *string, char c, unsigned position)
|
||||
LOG_ERROR("%s", buf);
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(riscv_set_expose_csrs)
|
||||
int parse_ranges(range_t **ranges, const char **argv)
|
||||
{
|
||||
if (CMD_ARGC != 1) {
|
||||
LOG_ERROR("Command takes exactly 1 parameter");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
for (unsigned pass = 0; pass < 2; pass++) {
|
||||
unsigned range = 0;
|
||||
unsigned low = 0;
|
||||
bool parse_low = true;
|
||||
unsigned high = 0;
|
||||
for (unsigned i = 0; i == 0 || CMD_ARGV[0][i-1]; i++) {
|
||||
char c = CMD_ARGV[0][i];
|
||||
for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) {
|
||||
char c = argv[0][i];
|
||||
if (isspace(c)) {
|
||||
/* Ignore whitespace. */
|
||||
continue;
|
||||
@@ -1315,13 +1387,13 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
|
||||
parse_low = false;
|
||||
} else if (c == ',' || c == 0) {
|
||||
if (pass == 1) {
|
||||
expose_csr[range].low = low;
|
||||
expose_csr[range].high = low;
|
||||
(*ranges)[range].low = low;
|
||||
(*ranges)[range].high = low;
|
||||
}
|
||||
low = 0;
|
||||
range++;
|
||||
} else {
|
||||
parse_error(CMD_ARGV[0], c, i);
|
||||
parse_error(argv[0], c, i);
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
@@ -1332,31 +1404,52 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
|
||||
} else if (c == ',' || c == 0) {
|
||||
parse_low = true;
|
||||
if (pass == 1) {
|
||||
expose_csr[range].low = low;
|
||||
expose_csr[range].high = high;
|
||||
(*ranges)[range].low = low;
|
||||
(*ranges)[range].high = high;
|
||||
}
|
||||
low = 0;
|
||||
high = 0;
|
||||
range++;
|
||||
} else {
|
||||
parse_error(CMD_ARGV[0], c, i);
|
||||
parse_error(argv[0], c, i);
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pass == 0) {
|
||||
if (expose_csr)
|
||||
free(expose_csr);
|
||||
expose_csr = calloc(range + 2, sizeof(*expose_csr));
|
||||
if (*ranges)
|
||||
free(*ranges);
|
||||
*ranges = calloc(range + 2, sizeof(range_t));
|
||||
} else {
|
||||
expose_csr[range].low = 1;
|
||||
expose_csr[range].high = 0;
|
||||
(*ranges)[range].low = 1;
|
||||
(*ranges)[range].high = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(riscv_set_expose_csrs)
|
||||
{
|
||||
if (CMD_ARGC != 1) {
|
||||
LOG_ERROR("Command takes exactly 1 parameter");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
return parse_ranges(&expose_csr, CMD_ARGV);
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(riscv_set_expose_custom)
|
||||
{
|
||||
if (CMD_ARGC != 1) {
|
||||
LOG_ERROR("Command takes exactly 1 parameter");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
return parse_ranges(&expose_custom, CMD_ARGV);
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(riscv_authdata_read)
|
||||
{
|
||||
if (CMD_ARGC != 0) {
|
||||
@@ -1486,13 +1579,6 @@ static const struct command_registration riscv_exec_command_handlers[] = {
|
||||
.usage = "riscv set_reset_timeout_sec [sec]",
|
||||
.help = "Set the wall-clock timeout (in seconds) after reset is deasserted"
|
||||
},
|
||||
{
|
||||
.name = "set_scratch_ram",
|
||||
.handler = riscv_set_scratch_ram,
|
||||
.mode = COMMAND_ANY,
|
||||
.usage = "riscv set_scratch_ram none|[address]",
|
||||
.help = "Set address of 16 bytes of scratch RAM the debugger can use, or 'none'."
|
||||
},
|
||||
{
|
||||
.name = "set_prefer_sba",
|
||||
.handler = riscv_set_prefer_sba,
|
||||
@@ -1510,6 +1596,15 @@ static const struct command_registration riscv_exec_command_handlers[] = {
|
||||
"addition to the standard ones. This must be executed before "
|
||||
"`init`."
|
||||
},
|
||||
{
|
||||
.name = "expose_custom",
|
||||
.handler = riscv_set_expose_custom,
|
||||
.mode = COMMAND_ANY,
|
||||
.usage = "riscv expose_custom n0[-m0][,n1[-m1]]...",
|
||||
.help = "Configure a list of inclusive ranges for custom registers to "
|
||||
"expose. custom0 is accessed as abstract register number 0xc000, "
|
||||
"etc. This must be executed before `init`."
|
||||
},
|
||||
{
|
||||
.name = "authdata_read",
|
||||
.handler = riscv_authdata_read,
|
||||
@@ -1541,6 +1636,56 @@ static const struct command_registration riscv_exec_command_handlers[] = {
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_command);
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_fileio_command);
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command);
|
||||
extern __COMMAND_HANDLER(handle_common_semihosting_cmdline);
|
||||
|
||||
/*
|
||||
* To be noted that RISC-V targets use the same semihosting commands as
|
||||
* ARM targets.
|
||||
*
|
||||
* The main reason is compatibility with existing tools. For example the
|
||||
* Eclipse OpenOCD/SEGGER J-Link/QEMU plug-ins have several widgets to
|
||||
* configure semihosting, which generate commands like `arm semihosting
|
||||
* enable`.
|
||||
* A secondary reason is the fact that the protocol used is exactly the
|
||||
* one specified by ARM. If RISC-V will ever define its own semihosting
|
||||
* protocol, then a command like `riscv semihosting enable` will make
|
||||
* sense, but for now all semihosting commands are prefixed with `arm`.
|
||||
*/
|
||||
static const struct command_registration arm_exec_command_handlers[] = {
|
||||
{
|
||||
"semihosting",
|
||||
.handler = handle_common_semihosting_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "['enable'|'disable']",
|
||||
.help = "activate support for semihosting operations",
|
||||
},
|
||||
{
|
||||
"semihosting_cmdline",
|
||||
.handler = handle_common_semihosting_cmdline,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "arguments",
|
||||
.help = "command line arguments to be passed to program",
|
||||
},
|
||||
{
|
||||
"semihosting_fileio",
|
||||
.handler = handle_common_semihosting_fileio_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "['enable'|'disable']",
|
||||
.help = "activate support for semihosting fileio operations",
|
||||
},
|
||||
{
|
||||
"semihosting_resexit",
|
||||
.handler = handle_common_semihosting_resumable_exit_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "['enable'|'disable']",
|
||||
.help = "activate support for semihosting resumable exit",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
const struct command_registration riscv_command_handlers[] = {
|
||||
{
|
||||
.name = "riscv",
|
||||
@@ -1549,6 +1694,13 @@ const struct command_registration riscv_command_handlers[] = {
|
||||
.usage = "",
|
||||
.chain = riscv_exec_command_handlers
|
||||
},
|
||||
{
|
||||
.name = "arm",
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "ARM Command Group",
|
||||
.usage = "",
|
||||
.chain = arm_exec_command_handlers
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
@@ -1581,6 +1733,7 @@ struct target_type riscv_target = {
|
||||
|
||||
.add_watchpoint = riscv_add_watchpoint,
|
||||
.remove_watchpoint = riscv_remove_watchpoint,
|
||||
.hit_watchpoint = riscv_hit_watchpoint,
|
||||
|
||||
.arch_state = riscv_arch_state,
|
||||
|
||||
@@ -1760,7 +1913,7 @@ void riscv_invalidate_register_cache(struct target *target)
|
||||
RISCV_INFO(r);
|
||||
|
||||
register_cache_invalidate(target->reg_cache);
|
||||
for (size_t i = 0; i < GDB_REGNO_COUNT; ++i) {
|
||||
for (size_t i = 0; i < target->reg_cache->num_regs; ++i) {
|
||||
struct reg *reg = &target->reg_cache->reg_list[i];
|
||||
reg->valid = false;
|
||||
}
|
||||
@@ -2033,7 +2186,8 @@ const char *gdb_regno_name(enum gdb_regno regno)
|
||||
|
||||
static int register_get(struct reg *reg)
|
||||
{
|
||||
struct target *target = (struct target *) reg->arch_info;
|
||||
riscv_reg_info_t *reg_info = reg->arch_info;
|
||||
struct target *target = reg_info->target;
|
||||
uint64_t value;
|
||||
int result = riscv_get_register(target, &value, reg->number);
|
||||
if (result != ERROR_OK)
|
||||
@@ -2044,7 +2198,8 @@ static int register_get(struct reg *reg)
|
||||
|
||||
static int register_set(struct reg *reg, uint8_t *buf)
|
||||
{
|
||||
struct target *target = (struct target *) reg->arch_info;
|
||||
riscv_reg_info_t *reg_info = reg->arch_info;
|
||||
struct target *target = reg_info->target;
|
||||
|
||||
uint64_t value = buf_get_u64(buf, 0, reg->size);
|
||||
|
||||
@@ -2086,12 +2241,26 @@ int riscv_init_registers(struct target *target)
|
||||
target->reg_cache->name = "RISC-V Registers";
|
||||
target->reg_cache->num_regs = GDB_REGNO_COUNT;
|
||||
|
||||
target->reg_cache->reg_list = calloc(GDB_REGNO_COUNT, sizeof(struct reg));
|
||||
if (expose_custom) {
|
||||
for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) {
|
||||
for (unsigned number = expose_custom[i].low;
|
||||
number <= expose_custom[i].high;
|
||||
number++)
|
||||
target->reg_cache->num_regs++;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("create register cache for %d registers",
|
||||
target->reg_cache->num_regs);
|
||||
|
||||
target->reg_cache->reg_list =
|
||||
calloc(target->reg_cache->num_regs, sizeof(struct reg));
|
||||
|
||||
const unsigned int max_reg_name_len = 12;
|
||||
if (info->reg_names)
|
||||
free(info->reg_names);
|
||||
info->reg_names = calloc(1, GDB_REGNO_COUNT * max_reg_name_len);
|
||||
info->reg_names =
|
||||
calloc(target->reg_cache->num_regs, max_reg_name_len);
|
||||
char *reg_name = info->reg_names;
|
||||
|
||||
static struct reg_feature feature_cpu = {
|
||||
@@ -2106,6 +2275,9 @@ int riscv_init_registers(struct target *target)
|
||||
static struct reg_feature feature_virtual = {
|
||||
.name = "org.gnu.gdb.riscv.virtual"
|
||||
};
|
||||
static struct reg_feature feature_custom = {
|
||||
.name = "org.gnu.gdb.riscv.custom"
|
||||
};
|
||||
|
||||
static struct reg_data_type type_ieee_single = {
|
||||
.type = REG_TYPE_IEEE_SINGLE,
|
||||
@@ -2124,18 +2296,24 @@ int riscv_init_registers(struct target *target)
|
||||
qsort(csr_info, DIM(csr_info), sizeof(*csr_info), cmp_csr_info);
|
||||
unsigned csr_info_index = 0;
|
||||
|
||||
/* When gdb request register N, gdb_get_register_packet() assumes that this
|
||||
unsigned custom_range_index = 0;
|
||||
int custom_within_range = 0;
|
||||
|
||||
riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
|
||||
shared_reg_info->target = target;
|
||||
|
||||
/* When gdb requests register N, gdb_get_register_packet() assumes that this
|
||||
* is register at index N in reg_list. So if there are certain registers
|
||||
* that don't exist, we need to leave holes in the list (or renumber, but
|
||||
* it would be nice not to have yet another set of numbers to translate
|
||||
* between). */
|
||||
for (uint32_t number = 0; number < GDB_REGNO_COUNT; number++) {
|
||||
for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) {
|
||||
struct reg *r = &target->reg_cache->reg_list[number];
|
||||
r->dirty = false;
|
||||
r->valid = false;
|
||||
r->exist = true;
|
||||
r->type = &riscv_reg_arch_type;
|
||||
r->arch_info = target;
|
||||
r->arch_info = shared_reg_info;
|
||||
r->number = number;
|
||||
r->size = riscv_xlen(target);
|
||||
/* r->size is set in riscv_invalidate_register_cache, maybe because the
|
||||
@@ -2495,11 +2673,35 @@ int riscv_init_registers(struct target *target)
|
||||
r->group = "general";
|
||||
r->feature = &feature_virtual;
|
||||
r->size = 8;
|
||||
|
||||
} else {
|
||||
/* Custom registers. */
|
||||
assert(expose_custom);
|
||||
|
||||
range_t *range = &expose_custom[custom_range_index];
|
||||
assert(range->low <= range->high);
|
||||
unsigned custom_number = range->low + custom_within_range;
|
||||
|
||||
r->group = "custom";
|
||||
r->feature = &feature_custom;
|
||||
r->arch_info = calloc(1, sizeof(riscv_reg_info_t));
|
||||
assert(r->arch_info);
|
||||
((riscv_reg_info_t *) r->arch_info)->target = target;
|
||||
((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number;
|
||||
sprintf(reg_name, "custom%d", custom_number);
|
||||
|
||||
custom_within_range++;
|
||||
if (custom_within_range > range->high - range->low) {
|
||||
custom_within_range = 0;
|
||||
custom_range_index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (reg_name[0])
|
||||
r->name = reg_name;
|
||||
reg_name += strlen(reg_name) + 1;
|
||||
assert(reg_name < info->reg_names + GDB_REGNO_COUNT * max_reg_name_len);
|
||||
assert(reg_name < info->reg_names + target->reg_cache->num_regs *
|
||||
max_reg_name_len);
|
||||
r->value = &info->reg_cache_values[number];
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ struct riscv_program;
|
||||
#include "opcodes.h"
|
||||
#include "gdb_regs.h"
|
||||
|
||||
/* The register cache is staticly allocated. */
|
||||
/* The register cache is statically allocated. */
|
||||
#define RISCV_MAX_HARTS 32
|
||||
#define RISCV_MAX_REGISTERS 5000
|
||||
#define RISCV_MAX_TRIGGERS 32
|
||||
@@ -35,6 +35,11 @@ enum riscv_halt_reason {
|
||||
RISCV_HALT_ERROR
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct target *target;
|
||||
unsigned custom_number;
|
||||
} riscv_reg_info_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned dtm_version;
|
||||
|
||||
@@ -58,7 +63,9 @@ typedef struct {
|
||||
uint64_t saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
|
||||
bool valid_saved_registers[RISCV_MAX_HARTS][RISCV_MAX_REGISTERS];
|
||||
|
||||
/* The register cache points into here. */
|
||||
/* OpenOCD's register cache points into here. This is not per-hart because
|
||||
* we just invalidate the entire cache when we change which hart is
|
||||
* selected. */
|
||||
uint64_t reg_cache_values[RISCV_MAX_REGISTERS];
|
||||
|
||||
/* Single buffer that contains all register names, instead of calling
|
||||
@@ -129,9 +136,6 @@ extern int riscv_command_timeout_sec;
|
||||
/* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
|
||||
extern int riscv_reset_timeout_sec;
|
||||
|
||||
extern bool riscv_use_scratch_ram;
|
||||
extern uint64_t riscv_scratch_ram_address;
|
||||
|
||||
extern bool riscv_prefer_sba;
|
||||
|
||||
/* Everything needs the RISC-V specific info structure, so here's a nice macro
|
||||
@@ -257,7 +261,11 @@ int riscv_remove_breakpoint(struct target *target,
|
||||
int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint);
|
||||
int riscv_remove_watchpoint(struct target *target,
|
||||
struct watchpoint *watchpoint);
|
||||
int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_wp_address);
|
||||
|
||||
int riscv_init_registers(struct target *target);
|
||||
|
||||
void riscv_semihosting_init(struct target *target);
|
||||
int riscv_semihosting(struct target *target, int *retval);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* ilg@livius.net *
|
||||
* *
|
||||
* Copyright (C) 2009 by Marvell Technology Group Ltd. *
|
||||
* Written by Nicolas Pitre <nico@marvell.com> *
|
||||
* *
|
||||
* Copyright (C) 2010 by Spencer Oliver *
|
||||
* spen@spen-soft.co.uk *
|
||||
* *
|
||||
* Copyright (C) 2016 by Square, Inc. *
|
||||
* Steven Stallion <stallion@squareup.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. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hold RISC-V semihosting support.
|
||||
*
|
||||
* The RISC-V code is inspired from ARM semihosting.
|
||||
*
|
||||
* Details can be found in chapter 8 of DUI0203I_rvct_developer_guide.pdf
|
||||
* from ARM Ltd.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#include "target/target.h"
|
||||
#include "target/semihosting_common.h"
|
||||
#include "riscv.h"
|
||||
|
||||
static int riscv_semihosting_setup(struct target *target, int enable);
|
||||
static int riscv_semihosting_post_result(struct target *target);
|
||||
|
||||
/**
|
||||
* Initialize RISC-V semihosting. Use common ARM code.
|
||||
*/
|
||||
void riscv_semihosting_init(struct target *target)
|
||||
{
|
||||
semihosting_common_init(target, riscv_semihosting_setup,
|
||||
riscv_semihosting_post_result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for and process a semihosting request using the ARM protocol). This
|
||||
* is meant to be called when the target is stopped due to a debug mode entry.
|
||||
* If the value 0 is returned then there was nothing to process. A non-zero
|
||||
* return value signifies that a request was processed and the target resumed,
|
||||
* or an error was encountered, in which case the caller must return
|
||||
* immediately.
|
||||
*
|
||||
* @param target Pointer to the target to process.
|
||||
* @param retval Pointer to a location where the return code will be stored
|
||||
* @return non-zero value if a request was processed or an error encountered
|
||||
*/
|
||||
int riscv_semihosting(struct target *target, int *retval)
|
||||
{
|
||||
struct semihosting *semihosting = target->semihosting;
|
||||
if (!semihosting)
|
||||
return 0;
|
||||
|
||||
if (!semihosting->is_active)
|
||||
return 0;
|
||||
|
||||
riscv_reg_t dpc;
|
||||
int result = riscv_get_register(target, &dpc, GDB_REGNO_DPC);
|
||||
if (result != ERROR_OK)
|
||||
return 0;
|
||||
|
||||
uint8_t tmp[12];
|
||||
|
||||
/* Read the current instruction, including the bracketing */
|
||||
*retval = target_read_memory(target, dpc - 4, 2, 6, tmp);
|
||||
if (*retval != ERROR_OK)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The instructions that trigger a semihosting call,
|
||||
* always uncompressed, should look like:
|
||||
*
|
||||
* 01f01013 slli zero,zero,0x1f
|
||||
* 00100073 ebreak
|
||||
* 40705013 srai zero,zero,0x7
|
||||
*/
|
||||
uint32_t pre = target_buffer_get_u32(target, tmp);
|
||||
uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
|
||||
uint32_t post = target_buffer_get_u32(target, tmp + 8);
|
||||
LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, dpc);
|
||||
|
||||
if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
|
||||
|
||||
/* Not the magic sequence defining semihosting. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform semihosting call if we are not waiting on a fileio
|
||||
* operation to complete.
|
||||
*/
|
||||
if (!semihosting->hit_fileio) {
|
||||
|
||||
/* RISC-V uses A0 and A1 to pass function arguments */
|
||||
riscv_reg_t r0;
|
||||
riscv_reg_t r1;
|
||||
|
||||
result = riscv_get_register(target, &r0, GDB_REGNO_A0);
|
||||
if (result != ERROR_OK)
|
||||
return 0;
|
||||
|
||||
result = riscv_get_register(target, &r1, GDB_REGNO_A1);
|
||||
if (result != ERROR_OK)
|
||||
return 0;
|
||||
|
||||
semihosting->op = r0;
|
||||
semihosting->param = r1;
|
||||
semihosting->word_size_bytes = riscv_xlen(target) / 8;
|
||||
|
||||
/* Check for ARM operation numbers. */
|
||||
if (0 <= semihosting->op && semihosting->op <= 0x31) {
|
||||
*retval = semihosting_common(target);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed semihosting operation");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* Unknown operation number, not a semihosting call. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resume target if we are not waiting on a fileio
|
||||
* operation to complete.
|
||||
*/
|
||||
if (semihosting->is_resumable && !semihosting->hit_fileio) {
|
||||
/* Resume right after the EBREAK 4 bytes instruction. */
|
||||
*retval = target_resume(target, 0, dpc+4, 0, 0);
|
||||
if (*retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to resume target");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Local functions. */
|
||||
|
||||
/**
|
||||
* Called via semihosting->setup() later, after the target is known,
|
||||
* usually on the first semihosting command.
|
||||
*/
|
||||
static int riscv_semihosting_setup(struct target *target, int enable)
|
||||
{
|
||||
LOG_DEBUG("enable=%d", enable);
|
||||
|
||||
struct semihosting *semihosting = target->semihosting;
|
||||
if (semihosting)
|
||||
semihosting->setup_time = clock();
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int riscv_semihosting_post_result(struct target *target)
|
||||
{
|
||||
struct semihosting *semihosting = target->semihosting;
|
||||
if (!semihosting) {
|
||||
/* If not enabled, silently ignored. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_DEBUG("0x%" PRIx64, semihosting->result);
|
||||
riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,163 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 by Liviu Ionescu *
|
||||
* <ilg@livius.net> *
|
||||
* *
|
||||
* Copyright (C) 2009 by Marvell Technology Group Ltd. *
|
||||
* Written by Nicolas Pitre <nico@marvell.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. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_TARGET_SEMIHOSTING_COMMON_H
|
||||
#define OPENOCD_TARGET_SEMIHOSTING_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* According to:
|
||||
* "Semihosting for AArch32 and AArch64, Release 2.0"
|
||||
* https://static.docs.arm.com/100863/0200/semihosting.pdf
|
||||
* from ARM Ltd.
|
||||
*
|
||||
* The available semihosting operation numbers passed in R0 are allocated
|
||||
* as follows:
|
||||
* - 0x00-0x31 Used by ARM.
|
||||
* - 0x32-0xFF Reserved for future use by ARM.
|
||||
* - 0x100-0x1FF Reserved for user applications. These are not used by ARM.
|
||||
* However, if you are writing your own SVC operations, you are advised
|
||||
* to use a different SVC number rather than using the semihosted
|
||||
* SVC number and these operation type numbers.
|
||||
* - 0x200-0xFFFFFFFF Undefined and currently unused. It is recommended
|
||||
* that you do not use these.
|
||||
*/
|
||||
|
||||
enum semihosting_operation_numbers {
|
||||
/*
|
||||
* ARM semihosting operations, in lexicographic order.
|
||||
*/
|
||||
SEMIHOSTING_ENTER_SVC = 0x17, /* DEPRECATED */
|
||||
|
||||
SEMIHOSTING_SYS_CLOSE = 0x02,
|
||||
SEMIHOSTING_SYS_CLOCK = 0x10,
|
||||
SEMIHOSTING_SYS_ELAPSED = 0x30,
|
||||
SEMIHOSTING_SYS_ERRNO = 0x13,
|
||||
SEMIHOSTING_SYS_EXIT = 0x18,
|
||||
SEMIHOSTING_SYS_EXIT_EXTENDED = 0x20,
|
||||
SEMIHOSTING_SYS_FLEN = 0x0C,
|
||||
SEMIHOSTING_SYS_GET_CMDLINE = 0x15,
|
||||
SEMIHOSTING_SYS_HEAPINFO = 0x16,
|
||||
SEMIHOSTING_SYS_ISERROR = 0x08,
|
||||
SEMIHOSTING_SYS_ISTTY = 0x09,
|
||||
SEMIHOSTING_SYS_OPEN = 0x01,
|
||||
SEMIHOSTING_SYS_READ = 0x06,
|
||||
SEMIHOSTING_SYS_READC = 0x07,
|
||||
SEMIHOSTING_SYS_REMOVE = 0x0E,
|
||||
SEMIHOSTING_SYS_RENAME = 0x0F,
|
||||
SEMIHOSTING_SYS_SEEK = 0x0A,
|
||||
SEMIHOSTING_SYS_SYSTEM = 0x12,
|
||||
SEMIHOSTING_SYS_TICKFREQ = 0x31,
|
||||
SEMIHOSTING_SYS_TIME = 0x11,
|
||||
SEMIHOSTING_SYS_TMPNAM = 0x0D,
|
||||
SEMIHOSTING_SYS_WRITE = 0x05,
|
||||
SEMIHOSTING_SYS_WRITEC = 0x03,
|
||||
SEMIHOSTING_SYS_WRITE0 = 0x04,
|
||||
};
|
||||
|
||||
/*
|
||||
* Codes used by SEMIHOSTING_SYS_EXIT (formerly
|
||||
* SEMIHOSTING_REPORT_EXCEPTION).
|
||||
* On 64-bits, the exit code is passed explicitly.
|
||||
*/
|
||||
enum semihosting_reported_exceptions {
|
||||
/* On 32 bits, use it for exit(0) */
|
||||
ADP_STOPPED_APPLICATION_EXIT = ((2 << 16) + 38),
|
||||
/* On 32 bits, use it for exit(1) */
|
||||
ADP_STOPPED_RUN_TIME_ERROR = ((2 << 16) + 35),
|
||||
};
|
||||
|
||||
struct target;
|
||||
|
||||
/*
|
||||
* A pointer to this structure was added to the target structure.
|
||||
*/
|
||||
struct semihosting {
|
||||
|
||||
/** A flag reporting whether semihosting is active. */
|
||||
bool is_active;
|
||||
|
||||
/** A flag reporting whether semihosting fileio is active. */
|
||||
bool is_fileio;
|
||||
|
||||
/** A flag reporting whether semihosting fileio operation is active. */
|
||||
bool hit_fileio;
|
||||
|
||||
/** Most are resumable, except the two exit calls. */
|
||||
bool is_resumable;
|
||||
|
||||
/**
|
||||
* When SEMIHOSTING_SYS_EXIT is called outside a debug session,
|
||||
* things are simple, the openocd process calls exit() and passes
|
||||
* the value returned by the target.
|
||||
* When SEMIHOSTING_SYS_EXIT is called during a debug session,
|
||||
* by default execution returns to the debugger, leaving the
|
||||
* debugger in a HALT state, similar to the state entered when
|
||||
* encountering a break.
|
||||
* In some use cases, it is useful to have SEMIHOSTING_SYS_EXIT
|
||||
* return normally, as any semihosting call, and do not break
|
||||
* to the debugger.
|
||||
* The standard allows this to happen, but the condition
|
||||
* to trigger it is a bit obscure ("by performing an RDI_Execute
|
||||
* request or equivalent").
|
||||
*
|
||||
* To make the SEMIHOSTING_SYS_EXIT call return normally, enable
|
||||
* this variable via the dedicated command (default: disabled).
|
||||
*/
|
||||
bool has_resumable_exit;
|
||||
|
||||
/** The Target (hart) word size; 8 for 64-bits targets. */
|
||||
size_t word_size_bytes;
|
||||
|
||||
/** The current semihosting operation (R0 on ARM). */
|
||||
int op;
|
||||
|
||||
/** The current semihosting parameter (R1 or ARM). */
|
||||
uint64_t param;
|
||||
|
||||
/**
|
||||
* The current semihosting result to be returned to the application.
|
||||
* Usually 0 for success, -1 for error,
|
||||
* but sometimes a useful value, even a pointer.
|
||||
*/
|
||||
int64_t result;
|
||||
|
||||
/** The value to be returned by semihosting SYS_ERRNO request. */
|
||||
int sys_errno;
|
||||
|
||||
/** The semihosting command line to be passed to the target. */
|
||||
char *cmdline;
|
||||
|
||||
/** The current time when 'execution starts' */
|
||||
clock_t setup_time;
|
||||
|
||||
int (*setup)(struct target *target, int enable);
|
||||
int (*post_result)(struct target *target);
|
||||
};
|
||||
|
||||
int semihosting_common_init(struct target *target, void *setup,
|
||||
void *post_result);
|
||||
int semihosting_common(struct target *target);
|
||||
|
||||
#endif /* OPENOCD_TARGET_SEMIHOSTING_COMMON_H */
|
||||
+7
-5
@@ -1895,6 +1895,9 @@ static void target_destroy(struct target *target)
|
||||
if (target->type->deinit_target)
|
||||
target->type->deinit_target(target);
|
||||
|
||||
if (target->semihosting)
|
||||
free(target->semihosting);
|
||||
|
||||
jtag_unregister_event_callback(jtag_enable_callback, target);
|
||||
|
||||
struct target_event_action *teap = target->event_action;
|
||||
@@ -4143,8 +4146,9 @@ static int target_mem2array(Jim_Interp *interp, struct target *target, int argc,
|
||||
* argv[3] = memory address
|
||||
* argv[4] = count of times to read
|
||||
*/
|
||||
|
||||
if (argc < 4 || argc > 5) {
|
||||
Jim_WrongNumArgs(interp, 1, argv, "varname width addr nelems [phys]");
|
||||
Jim_WrongNumArgs(interp, 0, argv, "varname width addr nelems [phys]");
|
||||
return JIM_ERR;
|
||||
}
|
||||
varname = Jim_GetString(argv[0], &len);
|
||||
@@ -5448,21 +5452,19 @@ static const struct command_registration target_instance_command_handlers[] = {
|
||||
.mode = COMMAND_EXEC,
|
||||
.jim_handler = jim_target_examine,
|
||||
.help = "used internally for reset processing",
|
||||
.usage = "arp_examine ['allow-defer']",
|
||||
.usage = "['allow-defer']",
|
||||
},
|
||||
{
|
||||
.name = "was_examined",
|
||||
.mode = COMMAND_EXEC,
|
||||
.jim_handler = jim_target_was_examined,
|
||||
.help = "used internally for reset processing",
|
||||
.usage = "was_examined",
|
||||
},
|
||||
{
|
||||
.name = "examine_deferred",
|
||||
.mode = COMMAND_EXEC,
|
||||
.jim_handler = jim_target_examine_deferred,
|
||||
.help = "used internally for reset processing",
|
||||
.usage = "examine_deferred",
|
||||
},
|
||||
{
|
||||
.name = "arp_halt_gdb",
|
||||
@@ -6429,7 +6431,7 @@ static const struct command_registration target_exec_command_handlers[] = {
|
||||
.handler = handle_bp_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "list or set hardware or software breakpoint",
|
||||
.usage = "<address> [<asid>]<length> ['hw'|'hw_ctx']",
|
||||
.usage = "<address> [<asid>] <length> ['hw'|'hw_ctx']",
|
||||
},
|
||||
{
|
||||
.name = "rbp",
|
||||
|
||||
+7
-4
@@ -205,6 +205,9 @@ struct target {
|
||||
|
||||
/* file-I/O information for host to do syscall */
|
||||
struct gdb_fileio_info *fileio_info;
|
||||
|
||||
/* The semihosting information, extracted from the target. */
|
||||
struct semihosting *semihosting;
|
||||
};
|
||||
|
||||
struct target_list {
|
||||
@@ -214,10 +217,10 @@ struct target_list {
|
||||
|
||||
struct gdb_fileio_info {
|
||||
char *identifier;
|
||||
uint32_t param_1;
|
||||
uint32_t param_2;
|
||||
uint32_t param_3;
|
||||
uint32_t param_4;
|
||||
uint64_t param_1;
|
||||
uint64_t param_2;
|
||||
uint64_t param_3;
|
||||
uint64_t param_4;
|
||||
};
|
||||
|
||||
/** Returns the instance-specific name of the specified target. */
|
||||
|
||||
Reference in New Issue
Block a user