This is an automated email from Gerrit. "Erhan Kurubas <erhan.kuru...@espressif.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/6942
-- gerrit commit 6c4a4d21e3954119bcff5d0f944700c29c256bd9 Author: Erhan Kurubas <erhan.kuru...@espressif.com> Date: Thu Apr 21 21:17:04 2022 +0200 target: add Espressif ESP32-C3 basic support Signed-off-by: Erhan Kurubas <erhan.kuru...@espressif.com> Change-Id: Ifb83e698ec5d5c6ec6ba488ee70dbe4b7efe4bc0 diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 49e882fe6c..783652f097 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -17,6 +17,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la $(MIPS32_SRC) \ $(NDS32_SRC) \ $(STM8_SRC) \ + $(ESP32_SRC) \ $(INTEL_IA32_SRC) \ $(ESIRISC_SRC) \ $(ARC_SRC) \ @@ -145,6 +146,11 @@ NDS32_SRC = \ STM8_SRC = \ %D%/stm8.c +ESP32_SRC= \ + %D%/esp32c3.c \ + %D%/esp.c \ + %D%/esp_riscv.c \ + %D%/esp_semihosting.c INTEL_IA32_SRC = \ %D%/quark_x10xx.c \ %D%/quark_d20xx.c \ diff --git a/src/target/esp.c b/src/target/esp.c new file mode 100644 index 0000000000..c29c3fbdee --- /dev/null +++ b/src/target/esp.c @@ -0,0 +1,140 @@ +/*************************************************************************** + * Espressif chips common target API for OpenOCD * + * Copyright (C) 2021 Espressif Systems Ltd. * + * * + * 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/>. * + ***************************************************************************/ + +#include <helper/log.h> +#include <helper/binarybuffer.h> +#include "target.h" +#include "esp.h" + +#define ESP_FLASH_BREAKPOINTS_MAX_NUM 32 + + +int esp_common_init(struct esp_common *esp) +{ + return ERROR_OK; +} + +int esp_dbgstubs_table_read(struct target *target, struct esp_dbg_stubs *dbg_stubs) +{ + int table_size, table_start_id, desc_entry_id, gcov_entry_id; + uint32_t entries[ESP_DBG_STUB_ENTRY_MAX]; + + LOG_DEBUG("%s: Read debug stubs info %" PRIu32 " / %d", target_name(target), + dbg_stubs->base, dbg_stubs->entries_count); + + /* first read 2 entries to get magic num and table size */ + int res = target_read_memory(target, dbg_stubs->base, sizeof(uint32_t), + 2, + (uint8_t *)&entries[0]); + if (res != ERROR_OK) { + LOG_ERROR("%s: Failed to read first debug stub entry!", target_name(target)); + return res; + } + if (entries[0] != ESP_DBG_STUB_MAGIC_NUM_VAL) { + /* idf with the old table entry structure */ + table_size = 2; + table_start_id = desc_entry_id = 0; + gcov_entry_id = 1; + } else { + table_size = entries[1]; + table_start_id = desc_entry_id = ESP_DBG_STUB_TABLE_START; + gcov_entry_id = ESP_DBG_STUB_ENTRY_FIRST; + + if (table_size < 2) { + LOG_ERROR("Invalid stub table entry size (%x)", table_size); + return ERROR_FAIL; + } + /* discard unsupported entries */ + if (table_size > ESP_DBG_STUB_ENTRY_MAX) + table_size = ESP_DBG_STUB_ENTRY_MAX; + + /* now read the remaining entries */ + res = target_read_memory(target, + dbg_stubs->base + 2 * sizeof(uint32_t), + sizeof(uint32_t), + table_size - 2, + (uint8_t *)&entries[2]); + if (res != ERROR_OK) { + LOG_ERROR("%s: Failed to read debug stubs info!", target_name(target)); + return res; + } + dbg_stubs->entries[ESP_DBG_STUB_CAPABILITIES] = + entries[ESP_DBG_STUB_CAPABILITIES]; + } + + dbg_stubs->entries[ESP_DBG_STUB_DESC] = entries[desc_entry_id]; + dbg_stubs->entries[ESP_DBG_STUB_ENTRY_GCOV] = entries[gcov_entry_id]; + + for (enum esp_dbg_stub_id i = ESP_DBG_STUB_DESC; i < ESP_DBG_STUB_ENTRY_MAX; i++) { + LOG_DEBUG("Check dbg stub %d - %x", i, dbg_stubs->entries[i]); + if (dbg_stubs->entries[i]) { + dbg_stubs->entries[i] = buf_get_u32( + (uint8_t *)&dbg_stubs->entries[i], + 0, + 32); + LOG_DEBUG("New dbg stub %d at %x", + dbg_stubs->entries_count, + dbg_stubs->entries[i]); + dbg_stubs->entries_count++; + } + } + if (dbg_stubs->entries_count < + (uint32_t)(table_size - table_start_id)) { + LOG_WARNING("Not full dbg stub table %d of %d", dbg_stubs->entries_count, + (table_size - table_start_id)); + } + + return ERROR_OK; +} + +int esp_common_handle_gdb_detach(struct target *target, struct esp_common *esp_common) +{ + int ret; + + enum target_state old_state = target->state; + if (target->state != TARGET_HALTED) { + ret = target_halt(target); + if (ret != ERROR_OK) { + LOG_ERROR( + "%s: Failed to halt target to remove flash BPs (%d)!", + target_name(target), + ret); + return ret; + } + ret = target_wait_state(target, TARGET_HALTED, 3000); + if (ret != ERROR_OK) { + LOG_ERROR( + "%s: Failed to wait halted target to remove flash BPs (%d)!", + target_name(target), + ret); + return ret; + } + } + + if (old_state == TARGET_RUNNING) { + ret = target_resume(target, 1, 0, 1, 0); + if (ret != ERROR_OK) { + LOG_ERROR( + "%s: Failed to resume target after flash BPs removal (%d)!", + target_name(target), + ret); + return ret; + } + } + return ERROR_OK; +} diff --git a/src/target/esp.h b/src/target/esp.h new file mode 100644 index 0000000000..9652691519 --- /dev/null +++ b/src/target/esp.h @@ -0,0 +1,118 @@ +/*************************************************************************** + * ESP common definitions for OpenOCD * + * Copyright (C) 2020 Espressif Systems Ltd. * + * * + * 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 _ESP_H +#define _ESP_H + +#include <stdint.h> + +/* must be in sync with ESP-IDF version */ +/** Size of the pre-compiled target buffer for stub trampoline. + * @note Must be in sync with ESP-IDF version */ +#define ESP_DBG_STUBS_CODE_BUF_SIZE 32 /* TODO: move this info to esp_dbg_stubs_desc */ +/** Size of the pre-compiled target buffer for stack. + * @note Must be in sync with ESP-IDF version */ +#define ESP_DBG_STUBS_STACK_MIN_SIZE 2048/* TODO: move this info to esp_dbg_stubs_desc */ + +/** + * Debug stubs table entries IDs + * + * @note Must be in sync with ESP-IDF version + */ +enum esp_dbg_stub_id { + ESP_DBG_STUB_ENTRY_MAGIC_NUM, + ESP_DBG_STUB_TABLE_SIZE, + ESP_DBG_STUB_TABLE_START, + ESP_DBG_STUB_DESC = ESP_DBG_STUB_TABLE_START, /*< Stubs descriptor ID */ + ESP_DBG_STUB_ENTRY_FIRST, + ESP_DBG_STUB_ENTRY_GCOV = ESP_DBG_STUB_ENTRY_FIRST, /*< GCOV stub ID */ + ESP_DBG_STUB_CAPABILITIES, + /* add new stub entries here */ + ESP_DBG_STUB_ENTRY_MAX, +}; + +#define ESP_DBG_STUB_MAGIC_NUM_VAL 0xFEEDBEEF +#define ESP_DBG_STUB_CAP_GCOV_THREAD (1 << 0) + +/** + * Debug stubs descriptor. ID: ESP_DBG_STUB_DESC + * + * @note Must be in sync with ESP-IDF version + */ +struct esp_dbg_stubs_desc { + /** Address of pre-compiled target buffer for stub trampoline. Size of the buffer is + * ESP_DBG_STUBS_CODE_BUF_SIZE. */ + uint32_t tramp_addr; + /** Pre-compiled target buffer's addr for stack. The size of the buffer is ESP_DBG_STUBS_STACK_MIN_SIZE. + Target has the buffer which is used for the stack of onboard algorithms. + If stack size required by algorithm exceeds ESP_DBG_STUBS_STACK_MIN_SIZE, + it should be allocated using onboard function pointed by 'data_alloc' and + freed by 'data_free'. They fit to the minimal stack. See below. */ + uint32_t min_stack_addr; + /** Address of malloc-like function to allocate buffer on target. */ + uint32_t data_alloc; + /** Address of free-like function to free buffer allocated with data_alloc. */ + uint32_t data_free; +}; + +/** + * Debug stubs info. + */ +struct esp_dbg_stubs { + /** Address. */ + uint32_t base; + /** Table contents. */ + uint32_t entries[ESP_DBG_STUB_ENTRY_MAX]; + /** Number of table entries. */ + uint32_t entries_count; + /** Debug stubs decsriptor. */ + struct esp_dbg_stubs_desc desc; +}; + +/** + * Semihost calls handling operations. + */ +struct esp_semihost_ops { + /** Callback called before handling semihost call */ + int (*prepare)(struct target *target); +}; + +struct esp_semihost_data { + uint32_t version; /* sending with drvinfo syscall */ + bool need_resume; + struct esp_semihost_ops *ops; +}; + +struct esp_common { + struct esp_dbg_stubs dbg_stubs; +}; + +int esp_common_init(struct esp_common *esp); +int esp_common_flash_breakpoint_add(struct target *target, + struct esp_common *esp, + struct breakpoint *breakpoint); +int esp_common_flash_breakpoint_remove(struct target *target, + struct esp_common *esp, + struct breakpoint *breakpoint); +bool esp_common_flash_breakpoint_exists(struct esp_common *esp, + struct breakpoint *breakpoint); +int esp_common_handle_gdb_detach(struct target *target, struct esp_common *esp_common); + +int esp_dbgstubs_table_read(struct target *target, struct esp_dbg_stubs *dbg_stubs); + +#endif /* _ESP_H */ diff --git a/src/target/esp32c3.c b/src/target/esp32c3.c new file mode 100644 index 0000000000..c837c9efaf --- /dev/null +++ b/src/target/esp32c3.c @@ -0,0 +1,331 @@ +/*************************************************************************** + * ESP32-C3 target for OpenOCD * + * Copyright (C) 2020 Espressif Systems Ltd. * + * * + * 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 "esp32c3.h" +#include <helper/command.h> +#include <helper/bits.h> +#include "target_type.h" +#include "register.h" +#include "semihosting_common.h" +#include "esp_semihosting.h" +#include "riscv/debug_defines.h" +#include "rtos/rtos.h" + +/* ESP32-C3 WDT */ +#define ESP32C3_WDT_WKEY_VALUE 0x50d83aa1 +#define ESP32C3_TIMG0_BASE 0x6001F000 +#define ESP32C3_TIMG1_BASE 0x60020000 +#define ESP32C3_TIMGWDT_CFG0_OFF 0x48 +#define ESP32C3_TIMGWDT_PROTECT_OFF 0x64 +#define ESP32C3_TIMG0WDT_CFG0 (ESP32C3_TIMG0_BASE + ESP32C3_TIMGWDT_CFG0_OFF) +#define ESP32C3_TIMG1WDT_CFG0 (ESP32C3_TIMG1_BASE + ESP32C3_TIMGWDT_CFG0_OFF) +#define ESP32C3_TIMG0WDT_PROTECT (ESP32C3_TIMG0_BASE + ESP32C3_TIMGWDT_PROTECT_OFF) +#define ESP32C3_TIMG1WDT_PROTECT (ESP32C3_TIMG1_BASE + ESP32C3_TIMGWDT_PROTECT_OFF) +#define ESP32C3_RTCCNTL_BASE 0x60008000 +#define ESP32C3_RTCWDT_CFG_OFF 0x90 +#define ESP32C3_RTCWDT_PROTECT_OFF 0xa8 +#define ESP32C3_RTCWDT_CFG (ESP32C3_RTCCNTL_BASE + ESP32C3_RTCWDT_CFG_OFF) +#define ESP32C3_RTCWDT_PROTECT (ESP32C3_RTCCNTL_BASE + ESP32C3_RTCWDT_PROTECT_OFF) + +#define ESP32C3_GPIO_STRAP_REG 0x60004038UL +#define IS_1XXX(v) (((v)&0x08) == 0x08) +#define ESP32C3_IS_FLASH_BOOT(_r_) IS_1XXX(_r_) +#define ESP32C3_FLASH_BOOT_MODE 0x08 + +extern struct target_type riscv_target; +extern const struct command_registration riscv_command_handlers[]; + +static int esp32c3_on_reset(struct target *target); + + +static int esp32c3_wdt_disable(struct target *target) +{ + /* TIMG1 WDT */ + int res = target_write_u32(target, ESP32C3_TIMG0WDT_PROTECT, ESP32C3_WDT_WKEY_VALUE); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write ESP32C3_TIMG0WDT_PROTECT (%d)!", res); + return res; + } + res = target_write_u32(target, ESP32C3_TIMG0WDT_CFG0, 0); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write ESP32C3_TIMG0WDT_CFG0 (%d)!", res); + return res; + } + /* TIMG2 WDT */ + res = target_write_u32(target, ESP32C3_TIMG1WDT_PROTECT, ESP32C3_WDT_WKEY_VALUE); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write ESP32C3_TIMG1WDT_PROTECT (%d)!", res); + return res; + } + res = target_write_u32(target, ESP32C3_TIMG1WDT_CFG0, 0); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write ESP32C3_TIMG1WDT_CFG0 (%d)!", res); + return res; + } + /* RTC WDT */ + res = target_write_u32(target, ESP32C3_RTCWDT_PROTECT, ESP32C3_WDT_WKEY_VALUE); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write ESP32C3_RTCWDT_PROTECT (%d)!", res); + return res; + } + res = target_write_u32(target, ESP32C3_RTCWDT_CFG, 0); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write ESP32C3_RTCWDT_CFG (%d)!", res); + return res; + } + return ERROR_OK; +} + +static const struct esp_semihost_ops esp32c3_semihost_ops = { + .prepare = esp32c3_wdt_disable +}; + +static int esp32c3_handle_target_event(struct target *target, enum target_event event, void *priv) +{ + if (target != priv) + return ERROR_OK; + + LOG_DEBUG("%d", event); + + int ret = esp_riscv_handle_target_event(target, event, priv); + if (ret != ERROR_OK) + return ret; + + return ERROR_OK; +} + +static int esp32c3_target_create(struct target *target, Jim_Interp *interp) +{ + struct esp32c3_common *esp32c3 = calloc(1, sizeof(struct esp32c3_common)); + if (!esp32c3) + return ERROR_FAIL; + + target->arch_info = esp32c3; + + riscv_info_init(target, &esp32c3->esp_riscv.riscv); + + return ERROR_OK; +} + +static int esp32c3_init_target(struct command_context *cmd_ctx, + struct target *target) +{ + int ret = riscv_target.init_target(cmd_ctx, target); + if (ret != ERROR_OK) + return ret; + + struct esp32c3_common *esp32c3 = esp32c3_common(target); + + ret = esp_riscv_init_arch_info(cmd_ctx, + target, + &esp32c3->esp_riscv, + esp32c3_on_reset, + &esp32c3_semihost_ops); + if (ret != ERROR_OK) + return ret; + + ret = target_register_event_callback(esp32c3_handle_target_event, target); + if (ret != ERROR_OK) + return ret; + + return ERROR_OK; +} + +static void esp32c3_deinit_target(struct target *target) +{ + riscv_target.deinit_target(target); +} + +static const char *const s_nonexistent_regs[] = { + "mie", "mip", "tdata3", "uie", "utvt", "utvec", "vcsr", "uscratch", "utval", + "uip", "unxti", "uintstatus", "uscratchcsw", "uscratchcswl", "sedeleg", + "sideleg", "stvt", "snxti", "sintstatus", "sscratchcsw", "sscratchcswl", + "vsstatus", "vsie", "vstvec", "vsscratch", "vsepc", "vscause", "vstval", + "vsip", "vsatp", "mtvt", "mstatush", "mcountinhibit", "mnxti", "mintstatus", + "mscratchcsw", "mscratchcswl", "mtinst", "mtval2", "hstatus", "hedeleg", + "hideleg", "hie", "htimedelta", "hcounteren", "hgeie", "htimedeltah", + "htval", "hip", "hvip", "htinst", "hgatp", "hgeip", "mvendorid", "marchid", + "mimpid", "mhartid", "seed", "mcounteren", "mhpmevent3", "mhpmevent4", "mhpmevent5", + "mhpmevent6", "mhpmevent7", "mhpmevent8", "mhpmevent9", "mhpmevent10", "mhpmevent11", + "mhpmevent12", "mhpmevent13", "mhpmevent14", "mhpmevent15", "mhpmevent16", "mhpmevent17", + "mhpmevent18", "mhpmevent19", "mhpmevent20", "mhpmevent21", "mhpmevent22", "mhpmevent23", + "mhpmevent24", "mhpmevent25", "mhpmevent26", "mhpmevent27", "mhpmevent28", "mhpmevent29", + "mhpmevent30", "mhpmevent31", + "scontext", "hcontext", "tinfo", "mcontext", "mscontext", "mcycle", "minstret", + "mhpmcounter3", "mhpmcounter4", "mhpmcounter5", "mhpmcounter6", "mhpmcounter7", + "mhpmcounter8", "mhpmcounter9", "mhpmcounter10", "mhpmcounter11", "mhpmcounter12", + "mhpmcounter13", "mhpmcounter14", "mhpmcounter15", "mhpmcounter16", "mhpmcounter17", + "mhpmcounter18", "mhpmcounter19", "mhpmcounter20", "mhpmcounter21", "mhpmcounter22", + "mhpmcounter23", "mhpmcounter24", "mhpmcounter25", "mhpmcounter26", "mhpmcounter27", + "mhpmcounter28", "mhpmcounter29", "mhpmcounter30", "mhpmcounter31", "mcycleh", + "minstreth", "mhpmcounter3h", "mhpmcounter4h", "mhpmcounter5h", "mhpmcounter6h", + "mhpmcounter7h", "mhpmcounter8h", "mhpmcounter9h", "mhpmcounter10h", "mhpmcounter11h", + "mhpmcounter12h", "mhpmcounter13h", "mhpmcounter14h", "mhpmcounter15h", "mhpmcounter16h", + "mhpmcounter17h", "mhpmcounter18h", "mhpmcounter19h", "mhpmcounter20h", "mhpmcounter21h", + "mhpmcounter22h", "mhpmcounter23h", "mhpmcounter24h", "mhpmcounter25h", "mhpmcounter26h", + "mhpmcounter27h", "mhpmcounter28h", "mhpmcounter29h", "mhpmcounter30h", "mhpmcounter31h", + "cycle", "time", "instret", "hpmcounter3", "hpmcounter4", "hpmcounter5", "hpmcounter6", + "hpmcounter7", "hpmcounter8", "hpmcounter9", "hpmcounter10", "hpmcounter11", "hpmcounter12", + "hpmcounter13", "hpmcounter14", "hpmcounter15", "hpmcounter17", "hpmcounter18", + "hpmcounter19", + "hpmcounter20", "hpmcounter21", "hpmcounter22", "hpmcounter23", "hpmcounter24", + "hpmcounter25", + "hpmcounter26", "hpmcounter27", "hpmcounter28", "hpmcounter29", "hpmcounter30", + "hpmcounter31", + "cycleh", "timeh", "instreth", "hpmcounter3h", "hpmcounter4h", "hpmcounter5h", + "hpmcounter6h", + "hpmcounter7h", "hpmcounter8h", "hpmcounter9h", "hpmcounter10h", "hpmcounter11h", + "hpmcounter12h", + "hpmcounter13h", "hpmcounter14h", "hpmcounter15h", "hpmcounter17h", "hpmcounter18h", + "hpmcounter19h", + "hpmcounter20h", "hpmcounter21h", "hpmcounter22h", "hpmcounter23h", "hpmcounter24h", + "hpmcounter25h", + "hpmcounter26h", "hpmcounter27h", "hpmcounter28h", "hpmcounter29h", "hpmcounter30h", + "hpmcounter31h", + "hpmcounter16h", "mhpmevent4" +}; + +static int esp32c3_examine(struct target *target) +{ + int ret = riscv_target.examine(target); + if (ret != ERROR_OK) + return ret; + /* RISCV code initializes registers upon target examination. + disable some registers because their reading or writing causes exception. Not supported in ESP32-C3??? */ + for (size_t i = 0; i < sizeof(s_nonexistent_regs)/sizeof(s_nonexistent_regs[0]); i++) { + struct reg *r = register_get_by_name(target->reg_cache, s_nonexistent_regs[i], 1); + if (r) + r->exist = false; + } + return ERROR_OK; +} + +static int esp32c3_on_reset(struct target *target) +{ + LOG_DEBUG("esp32c3_on_reset!"); + struct esp32c3_common *esp32c3 = esp32c3_common(target); + esp32c3->was_reset = true; + return ERROR_OK; +} + +static int esp32c3_poll(struct target *target) +{ + struct esp32c3_common *esp32c3 = esp32c3_common(target); + int res = ERROR_OK; + + RISCV_INFO(r); + if (esp32c3->was_reset && r->dmi_read && r->dmi_write) { + uint32_t dmstatus; + res = r->dmi_read(target, &dmstatus, DM_DMSTATUS); + if (res != ERROR_OK) + LOG_ERROR("Failed to read DMSTATUS (%d)!", res); + else { + uint32_t strap_reg; + LOG_DEBUG("Core is out of reset: dmstatus 0x%x", dmstatus); + esp32c3->was_reset = false; + res = target_read_u32(target, ESP32C3_GPIO_STRAP_REG, &strap_reg); + if (res != ERROR_OK) { + LOG_WARNING("Failed to read ESP32C3_GPIO_STRAP_REG (%d)!", res); + strap_reg = ESP32C3_FLASH_BOOT_MODE; + } + if (ESP32C3_IS_FLASH_BOOT(strap_reg) && + get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0) { + LOG_DEBUG("Halt core"); + res = esp_riscv_core_halt(target); + if (res == ERROR_OK) { + res = esp32c3_wdt_disable(target); + if (res != ERROR_OK) + LOG_ERROR("Failed to disable WDTs (%d)!", res); + } else + LOG_ERROR("Failed to halt core (%d)!", res); + } + if (ESP32C3_IS_FLASH_BOOT(strap_reg)) { + /* enable ebreaks */ + res = esp_riscv_core_ebreaks_enable(target); + if (res != ERROR_OK) + LOG_ERROR("Failed to enable EBREAKS handling (%d)!", res); + if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0) { + LOG_DEBUG("Resume core"); + res = esp_riscv_core_resume(target); + if (res != ERROR_OK) + LOG_ERROR("Failed to resume core (%d)!", res); + LOG_DEBUG("resumed core"); + } + } + } + } + return esp_riscv_poll(target); +} + +static const struct command_registration esp32c3_command_handlers[] = { + { + .usage = "", + .chain = riscv_command_handlers, + }, + { + .name = "esp", + .usage = "", + .chain = esp_riscv_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct target_type esp32c3_target = { + .name = "esp32c3", + + .target_create = esp32c3_target_create, + .init_target = esp32c3_init_target, + .deinit_target = esp32c3_deinit_target, + .examine = esp32c3_examine, + + /* poll current target status */ + .poll = esp32c3_poll, + + .halt = esp_riscv_halt, + .resume = esp_riscv_resume, + .step = esp_riscv_step, + + .assert_reset = esp_riscv_assert_reset, + .deassert_reset = esp_riscv_deassert_reset, + + .read_memory = esp_riscv_read_memory, + .write_memory = esp_riscv_write_memory, + + .checksum_memory = esp_riscv_checksum_memory, + + .get_gdb_arch = esp_riscv_get_gdb_arch, + .get_gdb_reg_list = esp_riscv_get_gdb_reg_list, + .get_gdb_reg_list_noread = esp_riscv_get_gdb_reg_list_noread, + + .add_breakpoint = esp_riscv_breakpoint_add, + .remove_breakpoint = esp_riscv_breakpoint_remove, + + .add_watchpoint = esp_riscv_add_watchpoint, + .remove_watchpoint = esp_riscv_remove_watchpoint, + .hit_watchpoint = esp_riscv_hit_watchpoint, + + .arch_state = esp_riscv_arch_state, + + .commands = esp32c3_command_handlers, + + .address_bits = esp_riscv_address_bits, +}; diff --git a/src/target/esp32c3.h b/src/target/esp32c3.h new file mode 100644 index 0000000000..ec9b7c7c5f --- /dev/null +++ b/src/target/esp32c3.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * ESP32-C3 target for OpenOCD * + * Copyright (C) 2020 Espressif Systems Ltd. * + * * + * 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 _ESP32C3_H +#define _ESP32C3_H + +#include "esp_riscv.h" + +#define ESP32C3_DROM_LOW 0x3C000000 +#define ESP32C3_DROM_HIGH 0x3C800000 +#define ESP32C3_IROM_LOW 0x42000000 +#define ESP32C3_IROM_HIGH 0x42800000 + +struct esp32c3_common { + struct esp_riscv_common esp_riscv; + bool was_reset; +}; + +static inline struct esp32c3_common *esp32c3_common(const struct target *target) +{ + return target->arch_info; +} + +#endif /* _ESP32C3_H */ diff --git a/src/target/esp_riscv.c b/src/target/esp_riscv.c new file mode 100644 index 0000000000..395f01f57d --- /dev/null +++ b/src/target/esp_riscv.c @@ -0,0 +1,439 @@ +/*************************************************************************** + * Espressif RISCV target API for OpenOCD * + * Copyright (C) 2021 Espressif Systems Ltd. * + * * + * 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/>. * + ***************************************************************************/ + +#include <stdbool.h> +#include <stdint.h> +#include "breakpoints.h" +#include "smp.h" +#include "semihosting_common.h" +#include "esp_riscv.h" +#include "target_type.h" +#include "esp_semihosting.h" + +/* Argument indexes for ESP_SEMIHOSTING_SYS_BREAKPOINT_SET */ +enum { + ESP_RISCV_SET_BREAKPOINT_ARG_SET, + ESP_RISCV_SET_BREAKPOINT_ARG_ID, + ESP_RISCV_SET_BREAKPOINT_ARG_ADDR, /* missed if `set` is false */ + ESP_RISCV_SET_BREAKPOINT_ARG_MAX +}; + +/* Argument indexes for ESP_SEMIHOSTING_SYS_WATCHPOINT_SET */ +enum { + ESP_RISCV_SET_WATCHPOINT_ARG_SET, + ESP_RISCV_SET_WATCHPOINT_ARG_ID, + ESP_RISCV_SET_WATCHPOINT_ARG_ADDR, /* missed if `set` is false */ + ESP_RISCV_SET_WATCHPOINT_ARG_SIZE, /* missed if `set` is false */ + ESP_RISCV_SET_WATCHPOINT_ARG_FLAGS, /* missed if `set` is false */ + ESP_RISCV_SET_WATCHPOINT_ARG_MAX +}; + +#define ESP_SEMIHOSTING_WP_FLG_RD (1UL << 0) +#define ESP_SEMIHOSTING_WP_FLG_WR (1UL << 1) + +#define ESP_RISCV_DBGSTUBS_UPDATE_DATA_ENTRY(_e_) \ + do { \ + (_e_) = buf_get_u32((uint8_t *)&(_e_), 0, 32); \ + if ((_e_) == 0) { \ + LOG_WARNING("No valid stub data entry found (0x%x)!", (uint32_t)(_e_)); \ + } \ + } while (0) + +#define ESP_RISCV_DBGSTUBS_UPDATE_CODE_ENTRY(_e_) \ + do { \ + (_e_) = buf_get_u32((uint8_t *)&(_e_), 0, 32); \ + if ((_e_) == 0) { \ + LOG_WARNING("No valid stub code entry found (0x%x)!", (uint32_t)(_e_)); \ + } \ + } while (0) + +extern struct target_type riscv_target; +static int esp_riscv_debug_stubs_info_init(struct target *target, + target_addr_t ctrl_addr); + + +int esp_riscv_semihosting(struct target *target) +{ + int res = ERROR_OK; + struct esp_riscv_common *esp_riscv = target_to_esp_riscv(target); + struct semihosting *semihosting = target->semihosting; + + LOG_DEBUG("op:(%x) param: (%" PRIx64 ")", semihosting->op, semihosting->param); + + if (esp_riscv->semi_ops && esp_riscv->semi_ops->prepare) + esp_riscv->semi_ops->prepare(target); + + switch (semihosting->op) { + case ESP_SEMIHOSTING_SYS_DEBUG_STUBS_INIT: + res = esp_riscv_debug_stubs_info_init(target, semihosting->param); + if (res != ERROR_OK) + return res; + break; + default: + return ERROR_FAIL; + } + + semihosting->result = res == ERROR_OK ? 0 : -1; + semihosting->is_resumable = true; + + return res; +} + +static int esp_riscv_debug_stubs_info_init(struct target *target, + target_addr_t vec_addr) +{ + struct esp_riscv_common *esp_riscv = target_to_esp_riscv(target); + + LOG_INFO("%s: Detected debug stubs @ " TARGET_ADDR_FMT, target_name(target), vec_addr); + + memset(&esp_riscv->esp.dbg_stubs, 0, sizeof(esp_riscv->esp.dbg_stubs)); + + esp_riscv->esp.dbg_stubs.base = vec_addr; + int res = esp_dbgstubs_table_read(target, &esp_riscv->esp.dbg_stubs); + if (res != ERROR_OK) + return res; + if (esp_riscv->esp.dbg_stubs.entries_count == 0) + return ERROR_OK; + + /* read debug stubs descriptor */ + ESP_RISCV_DBGSTUBS_UPDATE_DATA_ENTRY(esp_riscv->esp.dbg_stubs.entries[ESP_DBG_STUB_DESC]); + res = + target_read_buffer(target, esp_riscv->esp.dbg_stubs.entries[ESP_DBG_STUB_DESC], + sizeof(struct esp_dbg_stubs_desc), + (uint8_t *)&esp_riscv->esp.dbg_stubs.desc); + if (res != ERROR_OK) { + LOG_ERROR("Failed to read debug stubs descriptor (%d)!", res); + return res; + } + ESP_RISCV_DBGSTUBS_UPDATE_CODE_ENTRY(esp_riscv->esp.dbg_stubs.desc.tramp_addr); + ESP_RISCV_DBGSTUBS_UPDATE_DATA_ENTRY(esp_riscv->esp.dbg_stubs.desc.min_stack_addr); + ESP_RISCV_DBGSTUBS_UPDATE_CODE_ENTRY(esp_riscv->esp.dbg_stubs.desc.data_alloc); + ESP_RISCV_DBGSTUBS_UPDATE_CODE_ENTRY(esp_riscv->esp.dbg_stubs.desc.data_free); + + return ERROR_OK; +} + +int esp_riscv_breakpoint_add(struct target *target, struct breakpoint *breakpoint) +{ + struct esp_riscv_common *esp_riscv; + + int res = riscv_add_breakpoint(target, breakpoint); + if (res == ERROR_TARGET_RESOURCE_NOT_AVAILABLE && breakpoint->type == BKPT_HARD) { + /* For SMP target return OK if SW flash breakpoint is already set using another + *core; GDB causes call to esp_flash_breakpoint_add() for every core, since it + *treats flash breakpoints as HW ones */ + if (target->smp) { + struct target_list *curr; + foreach_smp_target(curr, target->smp_targets) { + esp_riscv = target_to_esp_riscv(curr->target); + if (esp_common_flash_breakpoint_exists(&esp_riscv->esp, breakpoint)) + return ERROR_OK; + } + } + esp_riscv = target_to_esp_riscv(target); + return esp_common_flash_breakpoint_add(target, &esp_riscv->esp, breakpoint); + } + return res; +} + +int esp_riscv_breakpoint_remove(struct target *target, struct breakpoint *breakpoint) +{ + struct esp_riscv_common *esp_riscv = target_to_esp_riscv(target); + + int res = riscv_remove_breakpoint(target, breakpoint); + if (res == ERROR_TARGET_RESOURCE_NOT_AVAILABLE && breakpoint->type == BKPT_HARD) { + res = esp_common_flash_breakpoint_remove(target, &esp_riscv->esp, breakpoint); + if (res == ERROR_TARGET_RESOURCE_NOT_AVAILABLE && target->smp) { + /* For SMP target return OK always, because SW flash breakpoint are set only + *using one core, but GDB causes call to esp_flash_breakpoint_remove() for + *every core, since it treats flash breakpoints as HW ones */ + return ERROR_OK; + } + } + + return res; +} + +int esp_riscv_handle_target_event(struct target *target, enum target_event event, + void *priv) +{ + int ret; + + if (target != priv) + return ERROR_OK; + + LOG_DEBUG("%d", event); + + switch (event) { + case TARGET_EVENT_GDB_DETACH: + { + struct esp_riscv_common *esp_riscv = target_to_esp_riscv(target); + ret = esp_common_handle_gdb_detach(target, &esp_riscv->esp); + if (ret != ERROR_OK) + return ret; + break; + } + default: + break; + } + return ERROR_OK; +} + +int esp_riscv_read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + /* TODO: find out the widest system bus access size. For now we are assuming it is equal to + *xlen */ + uint32_t sba_access_size = target_data_bits(target) / 8; + + if (size < sba_access_size) { + LOG_DEBUG("Use %d-bit access: size: %d\tcount:%d\tstart address: 0x%08" + TARGET_PRIxADDR, sba_access_size * 8, size, count, address); + target_addr_t al_addr = address & ~(sba_access_size - 1); + uint32_t al_len = (size * count) + address - al_addr; + uint32_t al_cnt = (al_len + sba_access_size - 1) & ~(sba_access_size - 1); + uint8_t al_buf[al_cnt]; + int ret = riscv_target.read_memory(target, + al_addr, + sba_access_size, + al_cnt / sba_access_size, + al_buf); + if (ret == ERROR_OK) + memcpy(buffer, &al_buf[address & (sba_access_size - 1)], size * count); + return ret; + } + + return riscv_target.read_memory(target, address, size, count, buffer); +} + +int esp_riscv_write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + /* TODO: find out the widest system bus access size. For now we are assuming it is equal to + *xlen */ + uint32_t sba_access_size = target_data_bits(target) / 8; + + if (target->state == TARGET_RUNNING || target->state == TARGET_DEBUG_RUNNING) { + /* Emulate using 32-bit SBA access if target is running. + Access via prog_buf or abstartct commands does not work in running state and + fails with abstractcs.cmderr == 4 (halt/resume) */ + if (size < sba_access_size) { + LOG_DEBUG("Use %d-bit access: size: %d\tcount:%d\tstart address: 0x%08" + TARGET_PRIxADDR, sba_access_size * 8, size, count, address); + target_addr_t al_addr = address & ~(sba_access_size - 1); + uint32_t al_len = (size * count) + address - al_addr; + uint32_t al_cnt = (al_len + sba_access_size - 1) & ~(sba_access_size - 1); + uint8_t al_buf[al_cnt]; + int ret = riscv_target.read_memory(target, + al_addr, + sba_access_size, + al_cnt / sba_access_size, + al_buf); + if (ret == ERROR_OK) { + memcpy(&al_buf[address & (sba_access_size - 1)], + buffer, + size * count); + ret = riscv_target.write_memory(target, + address, + sba_access_size, + al_cnt / sba_access_size, + al_buf); + } + return ret; + } + } + return riscv_target.write_memory(target, address, size, count, buffer); +} + +int esp_riscv_poll(struct target *target) +{ + return riscv_target.poll(target); +} + +int esp_riscv_halt(struct target *target) +{ + return riscv_target.halt(target); +} + +int esp_riscv_resume(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution) +{ + return riscv_target.resume(target, current, address, handle_breakpoints, debug_execution); +} + +int esp_riscv_step( + struct target *target, + int current, + target_addr_t address, + int handle_breakpoints) +{ + return riscv_target.step(target, current, address, handle_breakpoints); +} + +int esp_riscv_assert_reset(struct target *target) +{ + return riscv_target.assert_reset(target); +} + +int esp_riscv_deassert_reset(struct target *target) +{ + return riscv_target.deassert_reset(target); +} + +int esp_riscv_checksum_memory(struct target *target, + target_addr_t address, uint32_t count, + uint32_t *checksum) +{ + return riscv_target.checksum_memory(target, address, count, checksum); +} + +int esp_riscv_get_gdb_reg_list_noread(struct target *target, + struct reg **reg_list[], int *reg_list_size, + enum target_register_class reg_class) +{ + return riscv_target.get_gdb_reg_list_noread(target, reg_list, reg_list_size, reg_class); +} + +int esp_riscv_get_gdb_reg_list(struct target *target, + struct reg **reg_list[], int *reg_list_size, + enum target_register_class reg_class) +{ + return riscv_target.get_gdb_reg_list(target, reg_list, reg_list_size, reg_class); +} + +const char *esp_riscv_get_gdb_arch(struct target *target) +{ + return riscv_target.get_gdb_arch(target); +} + +int esp_riscv_arch_state(struct target *target) +{ + return riscv_target.arch_state(target); +} + +int esp_riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint) +{ + return riscv_target.add_watchpoint(target, watchpoint); +} + +int esp_riscv_remove_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + return riscv_target.remove_watchpoint(target, watchpoint); +} + +int esp_riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint) +{ + return riscv_target.hit_watchpoint(target, hit_watchpoint); +} + +unsigned esp_riscv_address_bits(struct target *target) +{ + return riscv_target.address_bits(target); +} + +bool esp_riscv_core_is_halted(struct target *target) +{ + uint32_t dmstatus; + RISCV_INFO(r); + if (r->dmi_read(target, &dmstatus, DM_DMSTATUS) != ERROR_OK) + return false; + return get_field(dmstatus, DM_DMSTATUS_ALLHALTED); +} + +int esp_riscv_core_halt(struct target *target) +{ + RISCV_INFO(r); + + /* Issue the halt command, and then wait for the current hart to halt. */ + uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_HALTREQ; + r->dmi_write(target, DM_DMCONTROL, dmcontrol); + for (size_t i = 0; i < 256; ++i) + if (esp_riscv_core_is_halted(target)) + break; + + if (!esp_riscv_core_is_halted(target)) { + uint32_t dmstatus; + if (r->dmi_read(target, &dmstatus, DM_DMSTATUS) != ERROR_OK) + return ERROR_FAIL; + if (r->dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) + return ERROR_FAIL; + + LOG_ERROR("unable to halt core"); + LOG_ERROR(" dmcontrol=0x%08x", dmcontrol); + LOG_ERROR(" dmstatus =0x%08x", dmstatus); + return ERROR_FAIL; + } + + dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HALTREQ, 0); + r->dmi_write(target, DM_DMCONTROL, dmcontrol); + return ERROR_OK; +} + +int esp_riscv_core_resume(struct target *target) +{ + RISCV_INFO(r); + + /* Issue the resume command, and then wait for the current hart to resume. */ + uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_RESUMEREQ; + r->dmi_write(target, DM_DMCONTROL, dmcontrol); + + dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HASEL, 0); + dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0); + + uint32_t dmstatus; + for (size_t i = 0; i < 256; ++i) { + usleep(10); + int res = r->dmi_read(target, &dmstatus, DM_DMSTATUS); + if (res != ERROR_OK) { + LOG_ERROR("Failed to read dmstatus!"); + return res; + } + if (get_field(dmstatus, DM_DMSTATUS_ALLRESUMEACK) == 0) + continue; + res = r->dmi_write(target, DM_DMCONTROL, dmcontrol); + if (res != ERROR_OK) { + LOG_ERROR("Failed to write dmcontrol!"); + return res; + } + return ERROR_OK; + } + + r->dmi_write(target, DM_DMCONTROL, dmcontrol); + + LOG_ERROR("unable to resume core"); + if (r->dmi_read(target, &dmstatus, DM_DMSTATUS) != ERROR_OK) + return ERROR_FAIL; + LOG_ERROR(" dmstatus =0x%08x", dmstatus); + + return ERROR_FAIL; +} + +int esp_riscv_core_ebreaks_enable(struct target *target) +{ + riscv_reg_t dcsr; + RISCV_INFO(r); + int result = r->get_register(target, &dcsr, GDB_REGNO_DCSR); + if (result != ERROR_OK) + return result; + LOG_DEBUG("DCSR: %" PRIx64, dcsr); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, 1); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, 1); + dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, 1); + return r->set_register(target, GDB_REGNO_DCSR, dcsr); +} diff --git a/src/target/esp_riscv.h b/src/target/esp_riscv.h new file mode 100644 index 0000000000..15d7cf9756 --- /dev/null +++ b/src/target/esp_riscv.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * ESP RISCV common definitions for OpenOCD * + * Copyright (C) 2020 Espressif Systems Ltd. * + * * + * 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 _ESP_RISCV_H +#define _ESP_RISCV_H + +#include "target.h" +#include "riscv/riscv.h" +#include "riscv/debug_defines.h" +#include "esp.h" + +#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) +#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) +#define ESP_RISCV_TARGET_BP_NUM 8 +#define ESP_RISCV_TARGET_WP_NUM 8 + +struct esp_riscv_common { + /* should be first, will be accessed by riscv generic code */ + riscv_info_t riscv; + struct esp_common esp; + struct esp_semihost_data semihost; + struct esp_semihost_ops *semi_ops; + target_addr_t target_bp_addr[ESP_RISCV_TARGET_BP_NUM]; + target_addr_t target_wp_addr[ESP_RISCV_TARGET_WP_NUM]; +}; + +static inline struct esp_riscv_common *target_to_esp_riscv(const struct target *target) +{ + return target->arch_info; +} + +static inline int esp_riscv_init_arch_info(struct command_context *cmd_ctx, struct target *target, + struct esp_riscv_common *esp_riscv, int (*on_reset)(struct target *), + const struct esp_semihost_ops *semi_ops) +{ + int ret = esp_common_init(&esp_riscv->esp); + if (ret != ERROR_OK) + return ret; + + esp_riscv->semi_ops = (struct esp_semihost_ops *)semi_ops; + + return ERROR_OK; +} + +int esp_riscv_semihosting(struct target *target); +int esp_riscv_breakpoint_add(struct target *target, struct breakpoint *breakpoint); +int esp_riscv_breakpoint_remove(struct target *target, struct breakpoint *breakpoint); +int esp_riscv_handle_target_event(struct target *target, enum target_event event, + void *priv); +int esp_riscv_read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer); +int esp_riscv_write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer); +int esp_riscv_poll(struct target *target); +int esp_riscv_halt(struct target *target); +int esp_riscv_resume(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution); +int esp_riscv_step( + struct target *target, + int current, + target_addr_t address, + int handle_breakpoints); +int esp_riscv_assert_reset(struct target *target); +int esp_riscv_deassert_reset(struct target *target); +int esp_riscv_checksum_memory(struct target *target, + target_addr_t address, uint32_t count, + uint32_t *checksum); +int esp_riscv_get_gdb_reg_list_noread(struct target *target, + struct reg **reg_list[], int *reg_list_size, + enum target_register_class reg_class); +int esp_riscv_get_gdb_reg_list(struct target *target, + struct reg **reg_list[], int *reg_list_size, + enum target_register_class reg_class); +const char *esp_riscv_get_gdb_arch(struct target *target); +int esp_riscv_arch_state(struct target *target); +int esp_riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint); +int esp_riscv_remove_watchpoint(struct target *target, + struct watchpoint *watchpoint); +int esp_riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint); +unsigned esp_riscv_address_bits(struct target *target); +bool esp_riscv_core_is_halted(struct target *target); +int esp_riscv_core_halt(struct target *target); +int esp_riscv_core_resume(struct target *target); +int esp_riscv_core_ebreaks_enable(struct target *target); + +extern const struct command_registration esp_riscv_command_handlers[]; + +#endif /* _ESP_RISCV_H */ diff --git a/src/target/esp_semihosting.c b/src/target/esp_semihosting.c new file mode 100644 index 0000000000..862c8d970e --- /dev/null +++ b/src/target/esp_semihosting.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * Semihosting API for Espressif chips * + * Copyright (C) 2022 Espressif Systems Ltd. * + * * + * 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/>. * + ***************************************************************************/ + +#include <helper/log.h> +#include "target.h" +#include "semihosting_common.h" +#include "esp_semihosting.h" +#include "esp_riscv.h" + +struct esp_semihost_data *target_to_esp_semihost_data(struct target *target) +{ + const char *arch = target_get_gdb_arch(target); + if (arch) { + if (strncmp(arch, "riscv", 5) == 0) + return &target_to_esp_riscv(target)->semihost; + } + LOG_ERROR("Unknown target arch!"); + return NULL; +} + +int esp_semihosting_common(struct target *target) +{ + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + /* Silently ignore if the semihosting field was not set. */ + return ERROR_OK; + } + + int retval = ERROR_FAIL; + + LOG_DEBUG("op=0x%x, param=0x%" PRIx64, semihosting->op, + semihosting->param); + + switch (semihosting->op) { + case ESP_SEMIHOSTING_SYS_DRV_INFO: + /* not implemented yet */ + break; + case ESP_SEMIHOSTING_SYS_SEEK: + /* not implemented yet */ + break; + case ESP_SEMIHOSTING_SYS_APPTRACE_INIT: + case ESP_SEMIHOSTING_SYS_DEBUG_STUBS_INIT: + case ESP_SEMIHOSTING_SYS_BREAKPOINT_SET: + case ESP_SEMIHOSTING_SYS_WATCHPOINT_SET: + /* For the time being only riscv chips support these commands */ + return esp_riscv_semihosting(target); + } + + return retval; +} diff --git a/src/target/esp_semihosting.h b/src/target/esp_semihosting.h new file mode 100644 index 0000000000..1ad20db6b6 --- /dev/null +++ b/src/target/esp_semihosting.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Semihosting API for Espressif chips * + * Copyright (C) 2022 Espressif Systems Ltd. * + * * + * 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 _ESP_SEMIHOSTING_H_ +#define _ESP_SEMIHOSTING_H_ + +/* syscalls compatible to ARM standard */ +#define ESP_SEMIHOSTING_SYS_DRV_INFO 0x100 +#define ESP_SEMIHOSTING_SYS_APPTRACE_INIT 0x101 +#define ESP_SEMIHOSTING_SYS_DEBUG_STUBS_INIT 0x102 +#define ESP_SEMIHOSTING_SYS_BREAKPOINT_SET 0x103 +#define ESP_SEMIHOSTING_SYS_WATCHPOINT_SET 0x104 +#define ESP_SEMIHOSTING_SYS_SEEK 0x105 /* custom lseek with whence */ +/* not implemented yet */ +#define ESP_SEMIHOSTING_SYS_MKDIR 0x106 +#define ESP_SEMIHOSTING_SYS_OPENDIR 0x107 +#define ESP_SEMIHOSTING_SYS_READDIR 0x108 +#define ESP_SEMIHOSTING_SYS_READDIR_R 0x109 +#define ESP_SEMIHOSTING_SYS_SEEKDIR 0x10A +#define ESP_SEMIHOSTING_SYS_TELLDIR 0x10B +#define ESP_SEMIHOSTING_SYS_CLOSEDIR 0x10C +#define ESP_SEMIHOSTING_SYS_RMDIR 0x10D +#define ESP_SEMIHOSTING_SYS_ACCESS 0x10E +#define ESP_SEMIHOSTING_SYS_TRUNCATE 0x10F +#define ESP_SEMIHOSTING_SYS_UTIME 0x110 +#define ESP_SEMIHOSTING_SYS_FSTAT 0x111 +#define ESP_SEMIHOSTING_SYS_STAT 0x112 +#define ESP_SEMIHOSTING_SYS_FSYNC 0x113 +#define ESP_SEMIHOSTING_SYS_LINK 0x114 +#define ESP_SEMIHOSTING_SYS_UNLINK 0x115 + +int esp_semihosting_common(struct target *target); + +#endif --