diff --git a/src/target/flashstub/riscv32_flashstub.h b/src/target/flashstub/riscv32_flashstub.h new file mode 100644 index 00000000000..36a1d0b9539 --- /dev/null +++ b/src/target/flashstub/riscv32_flashstub.h @@ -0,0 +1,34 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2015 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TARGET_RISCV_FLASHSTUB_STUB_H +#define TARGET_RISCV_FLASHSTUB_STUB_H + + +/* + * exit stub, 0 means OK, else error code + */ +static inline void __attribute__((always_inline)) riscv_stub_exit(const int code) +{ + __asm__("li a0, %0 \n" + "ebreak" ::"i"(code)); +} + +#endif diff --git a/src/target/riscv32.c b/src/target/riscv32.c index c15ce02cded..2b1119769ce 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -30,7 +30,6 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "general.h" #include "target.h" #include "target_internal.h" @@ -670,3 +669,80 @@ static int riscv32_breakwatch_clear(target_s *const target, breakwatch_s *const const uint32_t address = 0; return riscv_config_trigger(hart, breakwatch->reserved[0], RISCV_TRIGGER_MODE_UNUSED, &config, &address) ? 0 : -1; } + +/* + * Small helper function to translate target to hart and simplify parameters + * We assume it is 32 bits + */ +static inline bool riscv32_target_csr_write(target_s *target, const uint16_t reg, uint32_t val) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + return riscv_csr_write(hart, reg, &val); +} + +/* + * Small helper function to translate target to hart and simplify parameters + * We assume it is 32 bits + */ +static inline bool riscv32_target_csr_read(target_s *target, const uint16_t reg, uint32_t *val) +{ + riscv_hart_s *const hart = riscv_hart_struct(target); + return riscv_csr_read(hart, reg, val); +} + +/* + * Execute code on the target with the signature void function(a,b,c,d) + * - codexec is the address the code to tun is located at + * - param1/2/3/4 will end up as the 4 parameters of the stub function + * + * The flashstub must not use the stack at all. + * It returns true on success, false on error + * There is a built-in timeout of 10 seconds + */ +bool riscv32_run_stub( + target_s *target, uint32_t loadaddr, uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4) +{ + bool ret = false; + uint32_t pc = 0; + uint32_t mie = 0; + uint32_t zero = 0; + // save PC & MIE + target->reg_read(target, RISCV_REG_PC, &pc, 4); + riscv32_target_csr_read(target, RV_CSR_MIE, &mie); + riscv32_target_csr_write(target, RV_CSR_MIE, zero); // disable interrupt + target->reg_write(target, RISCV_REG_A0, ¶m1, 4); + target->reg_write(target, RISCV_REG_A1, ¶m2, 4); + target->reg_write(target, RISCV_REG_A2, ¶m3, 4); + target->reg_write(target, RISCV_REG_A3, ¶m4, 4); + target->reg_write(target, RISCV_REG_PC, &loadaddr, 4); + + target->halt_resume(target, false); // go! + platform_timeout_s timeout; + platform_timeout_set(&timeout, 10000); + target_halt_reason_e reason = TARGET_HALT_RUNNING; + while (reason == TARGET_HALT_RUNNING) { + if (platform_timeout_is_expired(&timeout)) { + reason = TARGET_HALT_ERROR; + break; + } + reason = target->halt_poll(target, NULL); + } + switch (reason) { + case TARGET_HALT_REQUEST: + ret = true; + break; + case TARGET_HALT_ERROR: // room left here for more detailed handling + default: + break; + } + target->halt_request(target); + if (ret) { + uint32_t a0; + target->reg_read(target, RISCV_REG_A0, &a0, 4); + ret = (a0 == 0); + } + // restore PC & MIE + riscv32_target_csr_write(target, RV_CSR_MIE, mie); // put back MIE + target->reg_write(target, RISCV_REG_PC, &pc, 4); + return ret; +} diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index bdbc4f89150..e235bead50c 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -221,6 +221,14 @@ typedef struct riscv_hart { #define RV_FPU_GDB_OFFSET 33 #define RV_FPU_GDB_CSR_OFFSET 66 +// General purpose registers +#define RISCV_REG_A0 10 +#define RISCV_REG_A1 11 +#define RISCV_REG_A2 12 +#define RISCV_REG_A3 13 +#define RISCV_REG_PC 32 +#define RISCV_REG_SP 2 + void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); riscv_hart_s *riscv_hart_struct(target_s *target); @@ -241,4 +249,15 @@ uint32_t riscv32_pack_data(const void *src, uint8_t access_width); void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); void riscv32_mem_write(target_s *target, target_addr_t dest, const void *src, size_t len); +/* + * Execute code on the target with the signature void stub_function(a,b,c,d) + * - codexec is the address the code to run is located at + * - param1/2/3/4 will end up as the 4 parameters of the stub function + * + * The flashstub must not use the stack at all. + * The flashstub must return 0 on success, not 0 on error + * There is a built-in timeout of 10 seconds + */ +bool riscv32_run_stub(target_s *target, uint32_t loadaddr, uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3); + #endif /*TARGET_RISCV_DEBUG_H*/