diff options
author | Hsiangkai Wang <hsiangkai@gmail.com> | 2013-01-02 12:02:00 +0800 |
---|---|---|
committer | Spencer Oliver <spen@spen-soft.co.uk> | 2013-08-07 21:01:25 +0000 |
commit | 0a4c8990c29e61fd0c2796486519cdb256b8da3b (patch) | |
tree | 56a4d7b47b392b7da4fa1ad08a4d5261c3619a0e /src/target/nds32_v3_common.c | |
parent | 80d412bafc03ce9a0418a2b98de2668b0f8de0e6 (diff) |
gdb_server: support File-I/O Remote Protocol Extension
The File I/O remote protocol extension allows the target to use the
host's file system and console I/O to perform various system calls.
To use the function, targets need to prepare two callback functions:
* get_gdb_finish_info: to get file I/O parameters from target
* gdb_fileio_end: pass file I/O response to target
As target is halted, gdb_server will try to get file-I/O information
from target through target_get_gdb_fileio_info(). If the callback function
returns ERROR_OK, gdb_server will initiate a file-I/O request to gdb.
After gdb finishes system call, gdb will pass response of the system call
to target through target_gdb_fileio_end() and continue to run(continue or step).
To implement the function, I add a new data structure in struct target,
called struct gdb_fileio_info, to record file I/O name and parameters.
Details refer to GDB manual "File-I/O Remote Protocol Extension"
Change-Id: I7f4d45e7c9e967b6d898dc79ba01d86bc46315d3
Signed-off-by: Hsiangkai Wang <hsiangkai@gmail.com>
Reviewed-on: http://openocd.zylin.com/1102
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Diffstat (limited to 'src/target/nds32_v3_common.c')
-rw-r--r-- | src/target/nds32_v3_common.c | 155 |
1 files changed, 154 insertions, 1 deletions
diff --git a/src/target/nds32_v3_common.c b/src/target/nds32_v3_common.c index 2fbd1a37..f0cd77d2 100644 --- a/src/target/nds32_v3_common.c +++ b/src/target/nds32_v3_common.c @@ -29,6 +29,18 @@ #include "nds32_aice.h" #include "nds32_v3_common.h" +static struct breakpoint syscall_breakpoint = { + 0x80, + 0, + 4, + BKPT_SOFT, + 0, + NULL, + NULL, + 0x515CA11, + 0, +}; + static struct nds32_v3_common_callback *v3_common_callback; static int nds32_v3_register_mapping(struct nds32 *nds32, int reg_no) @@ -80,6 +92,22 @@ static int nds32_v3_debug_entry(struct nds32 *nds32, bool enable_watchpoint) if (enable_watchpoint) CHECK_RETVAL(v3_common_callback->deactivate_hardware_watchpoint(nds32->target)); + if (nds32->virtual_hosting) { + if (syscall_breakpoint.set) { + /** disable virtual hosting */ + + /* remove breakpoint at syscall entry */ + target_remove_breakpoint(nds32->target, &syscall_breakpoint); + syscall_breakpoint.set = 0; + + uint32_t value_pc; + nds32_get_mapped_reg(nds32, PC, &value_pc); + if (value_pc == syscall_breakpoint.address) + /** process syscall for virtual hosting */ + nds32->hit_syscall = true; + } + } + if (ERROR_OK != nds32_examine_debug_reason(nds32)) { nds32->target->state = backup_state; @@ -132,6 +160,74 @@ static int nds32_v3_leave_debug_state(struct nds32 *nds32, bool enable_watchpoin */ CHECK_RETVAL(nds32_restore_context(target)); + if (nds32->virtual_hosting) { + /** enable virtual hosting */ + uint32_t value_ir3; + uint32_t entry_size; + uint32_t syscall_address; + + /* get syscall entry address */ + nds32_get_mapped_reg(nds32, IR3, &value_ir3); + entry_size = 0x4 << (((value_ir3 >> 14) & 0x3) << 1); + syscall_address = (value_ir3 & 0xFFFF0000) + entry_size * 8; /* The index of SYSCALL is 8 */ + + if (nds32->hit_syscall) { + /* single step to skip syscall entry */ + /* use IRET to skip syscall */ + struct aice_port_s *aice = target_to_aice(target); + uint32_t value_ir9; + uint32_t value_ir6; + uint32_t syscall_id; + + nds32_get_mapped_reg(nds32, IR6, &value_ir6); + syscall_id = (value_ir6 >> 16) & 0x7FFF; + + if (syscall_id == NDS32_SYSCALL_EXIT) { + /* If target hits exit syscall, do not use IRET to skip handler. */ + aice_step(aice); + } else { + /* use api->read/write_reg to skip nds32 register cache */ + uint32_t value_dimbr; + aice_read_debug_reg(aice, NDS_EDM_SR_DIMBR, &value_dimbr); + aice_write_register(aice, IR11, value_dimbr + 0xC); + + aice_read_register(aice, IR9, &value_ir9); + value_ir9 += 4; /* syscall is always 4 bytes */ + aice_write_register(aice, IR9, value_ir9); + + /* backup hardware breakpoint 0 */ + uint32_t backup_bpa, backup_bpam, backup_bpc; + aice_read_debug_reg(aice, NDS_EDM_SR_BPA0, &backup_bpa); + aice_read_debug_reg(aice, NDS_EDM_SR_BPAM0, &backup_bpam); + aice_read_debug_reg(aice, NDS_EDM_SR_BPC0, &backup_bpc); + + /* use hardware breakpoint 0 to stop cpu after skipping syscall */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPA0, value_ir9); + aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0, 0); + aice_write_debug_reg(aice, NDS_EDM_SR_BPC0, 0xA); + + /* Execute two IRET. + * First IRET is used to quit debug mode. + * Second IRET is used to quit current syscall. */ + uint32_t dim_inst[4] = {NOP, NOP, IRET, IRET}; + aice_execute(aice, dim_inst, 4); + + /* restore origin hardware breakpoint 0 */ + aice_write_debug_reg(aice, NDS_EDM_SR_BPA0, backup_bpa); + aice_write_debug_reg(aice, NDS_EDM_SR_BPAM0, backup_bpam); + aice_write_debug_reg(aice, NDS_EDM_SR_BPC0, backup_bpc); + } + + nds32->hit_syscall = false; + } + + /* insert breakpoint at syscall entry */ + syscall_breakpoint.address = syscall_address; + syscall_breakpoint.type = BKPT_SOFT; + syscall_breakpoint.set = 1; + target_add_breakpoint(target, &syscall_breakpoint); + } + /* enable polling */ jtag_poll_set_enabled(true); @@ -398,7 +494,27 @@ int nds32_v3_read_buffer(struct target *target, uint32_t address, else return ERROR_FAIL; - return nds32_read_buffer(target, address, size, buffer); + int result; + struct aice_port_s *aice = target_to_aice(target); + /* give arbitrary initial value to avoid warning messages */ + enum nds_memory_access origin_access_channel = NDS_MEMORY_ACC_CPU; + + if (nds32->hit_syscall) { + /* Use bus mode to access memory during virtual hosting */ + origin_access_channel = memory->access_channel; + memory->access_channel = NDS_MEMORY_ACC_BUS; + aice_memory_access(aice, NDS_MEMORY_ACC_BUS); + } + + result = nds32_read_buffer(target, address, size, buffer); + + if (nds32->hit_syscall) { + /* Restore access_channel after virtual hosting */ + memory->access_channel = origin_access_channel; + aice_memory_access(aice, origin_access_channel); + } + + return result; } int nds32_v3_write_buffer(struct target *target, uint32_t address, @@ -436,6 +552,24 @@ int nds32_v3_write_buffer(struct target *target, uint32_t address, else return ERROR_FAIL; + if (nds32->hit_syscall) { + /* Use bus mode to access memory during virtual hosting */ + struct aice_port_s *aice = target_to_aice(target); + enum nds_memory_access origin_access_channel; + int result; + + origin_access_channel = memory->access_channel; + memory->access_channel = NDS_MEMORY_ACC_BUS; + aice_memory_access(aice, NDS_MEMORY_ACC_BUS); + + result = nds32_gdb_fileio_write_memory(nds32, address, size, buffer); + + memory->access_channel = origin_access_channel; + aice_memory_access(aice, origin_access_channel); + + return result; + } + return nds32_write_buffer(target, address, size, buffer); } @@ -474,10 +608,26 @@ int nds32_v3_read_memory(struct target *target, uint32_t address, else return ERROR_FAIL; + struct aice_port_s *aice = target_to_aice(target); + /* give arbitrary initial value to avoid warning messages */ + enum nds_memory_access origin_access_channel = NDS_MEMORY_ACC_CPU; int result; + if (nds32->hit_syscall) { + /* Use bus mode to access memory during virtual hosting */ + origin_access_channel = memory->access_channel; + memory->access_channel = NDS_MEMORY_ACC_BUS; + aice_memory_access(aice, NDS_MEMORY_ACC_BUS); + } + result = nds32_read_memory(target, address, size, count, buffer); + if (nds32->hit_syscall) { + /* Restore access_channel after virtual hosting */ + memory->access_channel = origin_access_channel; + aice_memory_access(aice, origin_access_channel); + } + return result; } @@ -527,5 +677,8 @@ int nds32_v3_init_target(struct command_context *cmd_ctx, nds32_init(nds32); + target->fileio_info = malloc(sizeof(struct gdb_fileio_info)); + target->fileio_info->identifier = NULL; + return ERROR_OK; } |