TCF Agent: fixed MicroBlaze stepping and registers access
diff --git a/agent/machine/microblaze/tcf/cpudefs-mdep.c b/agent/machine/microblaze/tcf/cpudefs-mdep.c
index 90781f9..3273fa0 100644
--- a/agent/machine/microblaze/tcf/cpudefs-mdep.c
+++ b/agent/machine/microblaze/tcf/cpudefs-mdep.c
@@ -23,6 +23,7 @@
#include <tcf/framework/cpudefs.h>
#include <tcf/framework/context.h>
#include <tcf/framework/myalloc.h>
+#include <tcf/services/runctrl.h>
#include <machine/microblaze/tcf/stack-crawl-microblaze.h>
#include <machine/microblaze/tcf/disassembler-microblaze.h>
#if ENABLE_ContextMux
@@ -30,21 +31,30 @@
#endif
#include <tcf/cpudefs-mdep.h>
+typedef struct ContextExtensionMicroBlaze {
+ int sw_stepping;
+ char opcode[sizeof(BREAK_INST)];
+ ContextAddress addr;
+} ContextExtensionMicroBlaze;
+
+static size_t context_extension_offset = 0;
+
+#define EXT(ctx) ((ContextExtensionMicroBlaze *)((char *)(ctx) + context_extension_offset))
+
RegisterDefinition * regs_index = NULL;
unsigned char BREAK_INST[] = { 0, 0, 0, 0 };
static RegisterDefinition * reg_pc = NULL;
static unsigned regs_cnt = 0;
static unsigned regs_max = 0;
-static size_t regs_offs = 0;
+
+#define REG_OFFSET(name) offsetof(REG_SET, name)
static RegisterDefinition * alloc_reg(size_t size) {
assert(regs_cnt < regs_max - 1);
- regs_index[regs_cnt].offset = regs_offs;
regs_index[regs_cnt].size = size;
regs_index[regs_cnt].dwarf_id = -1;
regs_index[regs_cnt].eh_frame_id = -1;
- regs_offs += size;
return regs_index + regs_cnt++;
}
@@ -56,30 +66,30 @@
return grp;
}
-static RegisterDefinition * alloc_spr(RegisterDefinition * grp, const char * name, size_t size, int id, const char * desc) {
+static RegisterDefinition * alloc_spr(RegisterDefinition * grp, const char * name, size_t offset, size_t size, int id, const char * desc) {
RegisterDefinition * reg = alloc_reg(size);
reg->parent = grp;
reg->name = loc_strdup(name);
reg->description = loc_strdup(desc);
reg->dwarf_id = (int16_t)id;
reg->eh_frame_id = (int16_t)id;
+ reg->offset = offset;
return reg;
}
static void microblaze_create_reg_definitions(void) {
unsigned i = 0;
+ RegisterDefinition * pvr = NULL;
- regs_offs = 0;
regs_cnt = 0;
regs_max = 128;
regs_index = (RegisterDefinition *)loc_alloc_zero(sizeof(RegisterDefinition) * regs_max);
for (i = 0; i < 32; i++) {
- char name[32];
RegisterDefinition * r = alloc_reg(4);
- snprintf(name, sizeof(name), "r%d", i);
- r->name = loc_strdup(name);
+ r->name = loc_printf("r%d", i);
r->dwarf_id = r->eh_frame_id = (int16_t)i;
+ r->offset = REG_OFFSET(user.regs.gpr) + i * 4;
switch (i) {
case 0: r->no_write = 1; break;
case 1: r->role = "SP"; break;
@@ -87,27 +97,22 @@
}
}
- reg_pc = alloc_spr(NULL, "pc", 4, 32, "Program Control Register");
+ reg_pc = alloc_spr(NULL, "pc", REG_OFFSET(user.regs.pc), 4, 32, "Program Control Register");
reg_pc->role = "PC";
- alloc_spr(NULL, "msr", 4, 33, "Machine Status Register");
- alloc_spr(NULL, "ear", 4, 34, "Exception Address Register");
- alloc_spr(NULL, "esr", 4, 35, "Exception Status Register");
- alloc_spr(NULL, "fsr", 4, 36, "Floating Point Unit Status Register");
- alloc_spr(NULL, "btr", 4, 37, "Exception Branch Taken Register");
+ alloc_spr(NULL, "msr", REG_OFFSET(user.regs.msr), 4, 33, "Machine Status Register");
+ alloc_spr(NULL, "ear", REG_OFFSET(user.regs.ear), 4, 34, "Exception Address Register");
+ alloc_spr(NULL, "esr", REG_OFFSET(user.regs.esr), 4, 35, "Exception Status Register");
+ alloc_spr(NULL, "fsr", REG_OFFSET(user.regs.fsr), 4, 36, "Floating Point Unit Status Register");
+ alloc_spr(NULL, "btr", REG_OFFSET(user.regs.btr), 4, 37, "Exception Branch Taken Register");
- /* TODO: check if CPU configured with MMU */
- {
- RegisterDefinition * grp = alloc_group("mmu");
- alloc_spr(grp, "pid", 4, 51, "Process Identifier Register");
- alloc_spr(grp, "zpr", 4, 52, "Zone Protection Register");
- alloc_spr(grp, "tlbx", 4, 53, "Translation Look-Aside Buffer Index Register");
- alloc_spr(grp, "tlbsx", 4, 54, "Translation Look-Aside Buffer Search Index Register");
- alloc_spr(grp, "tlblo", 4, 55, "Translation Look-Aside Buffer Low Register");
- alloc_spr(grp, "tlbhi", 4, 56, "Translation Look-Aside Buffer High Register");
+ pvr = alloc_group("pvr");
+ for (i = 0; i < 12; i++) {
+ RegisterDefinition * r = alloc_reg(4);
+ r->name = loc_printf("pvr%d", i);
+ r->offset = REG_OFFSET(user.regs.pvr) + i * 4;
+ r->parent = pvr;
}
- alloc_spr(NULL, "slr", 4, 57, "Stack protection - Low pointer");
- alloc_spr(NULL, "shr", 4, 58, "Stack protection - High pointer");
}
RegisterDefinition * get_PC_definition(Context * ctx) {
@@ -125,9 +130,186 @@
}
#endif
+#if ENABLE_external_stepping_mode
+static int read_reg(Context * ctx, RegisterDefinition * def, size_t size, ContextAddress * addr) {
+ size_t i;
+ uint8_t buf[8];
+ uint64_t n = 0;
+ *addr = 0;
+ if (def->dwarf_id == 0) return 0;
+ assert(!def->big_endian);
+ assert(size <= def->size);
+ assert(size <= sizeof(buf));
+ if (context_read_reg(ctx, def, 0, size, buf) < 0) return -1;
+ for (i = 0; i < size; i++) n |= (uint64_t)buf[i] << (i * 8);
+ *addr = (ContextAddress)n;
+ return 0;
+}
+
+static int read_mem(Context * ctx, ContextAddress addr, uint32_t * data) {
+ size_t i;
+ uint8_t buf[4];
+ uint32_t v = 0;
+ if (context_read_mem(ctx, addr, &buf, 4) < 0) return -1;
+ for (i = 0; i < 4; i++) v |= (uint32_t)buf[i] << (big_endian_host() ? 3 - i : i) * 8;
+ *data = v;
+ return 0;
+}
+
+static int br_condition(uint32_t instr, ContextAddress data) {
+ uint32_t ra32 = (uint32_t)data;
+ uint64_t ra64 = (uint64_t)data;
+ switch ((instr >> 21) & 0xf) {
+ case 0: return ra32 == 0;
+ case 1: return ra32 != 0;
+ case 2: return ra32 < 0;
+ case 3: return ra32 <= 0;
+ case 4: return ra32 > 0;
+ case 5: return ra32 >= 0;
+ case 8: return ra64 == 0;
+ case 9: return ra64 != 0;
+ case 10: return ra64 < 0;
+ case 11: return ra64 <= 0;
+ case 12: return ra64 > 0;
+ case 13: return ra64 >= 0;
+ }
+ return 0;
+}
+
+static int get_next_address(Context * ctx, ContextAddress * next_addr) {
+ uint32_t instr = 0;
+ uint64_t imm = 0;
+ unsigned imm_bits = 0;
+ ContextAddress addr = 0;
+ ContextAddress instr_addr = 0;
+
+ /* Read opcode at PC */
+ if (read_reg(ctx, reg_pc, reg_pc->size, &addr) < 0) return -1;
+ if (read_mem(ctx, addr, &instr) < 0) return -1;
+ instr_addr = addr;
+ addr += 4;
+
+ /* Check for IMM and IMML instructions */
+ if ((instr & 0xffff0000) == 0xb0000000) {
+ imm_bits = 16;
+ imm = instr & 0xffff;
+ if (read_mem(ctx, addr, &instr) < 0) return -1;
+ addr += 4;
+ }
+ else if ((instr & 0xff000000) == 0xb2000000) {
+ imm_bits = 24;
+ imm = instr & 0xffffff;
+ if (read_mem(ctx, addr, &instr) < 0) return -1;
+ addr += 4;
+ }
+
+ /* Check for branch and return instructions */
+ if ((instr & 0xfc0007ff) == 0x98000000) {
+ /* BR .. BRK */
+ ContextAddress rb_data = 0;
+ RegisterDefinition * rb_def = regs_index + ((instr >> 11) & 0x1f);
+ if (read_reg(ctx, rb_def, rb_def->size, &rb_data) < 0) return -1;
+ if (instr & (1 << 19)) {
+ addr = rb_data;
+ }
+ else {
+ addr = instr_addr + rb_data;
+ }
+ }
+ else if ((instr & 0xfc0007ff) == 0x9c000000) {
+ /* BEQ .. BGED */
+ ContextAddress ra_data = 0;
+ RegisterDefinition * ra_def = regs_index + ((instr >> 16) & 0x1f);
+ if (read_reg(ctx, ra_def, ra_def->size, &ra_data) < 0) return -1;
+ if (br_condition(instr, ra_data)) {
+ ContextAddress rb_data = 0;
+ RegisterDefinition * rb_def = regs_index + ((instr >> 11) & 0x1f);
+ if (read_reg(ctx, rb_def, rb_def->size, &rb_data) < 0) return -1;
+ addr = instr_addr + rb_data;
+ }
+ else if (instr & (1 << 25)) {
+ addr += 4;
+ }
+ }
+ else if ((instr & 0xfc000000) == 0xb4000000) {
+ /* RTSD .. RTED */
+ ContextAddress ra_data = 0;
+ RegisterDefinition * ra_def = regs_index + ((instr >> 16) & 0x1f);
+ if (read_reg(ctx, ra_def, ra_def->size, &ra_data) < 0) return -1;
+ imm = (imm << 16) | (instr & 0xffff);
+ imm_bits += 16;
+ if (imm & ((uint64_t)1 << (imm_bits - 1))) {
+ imm |= ~(((uint64_t)1 << imm_bits) - 1);
+ }
+ addr = ra_data + imm;
+ }
+ else if ((instr & 0xfc000000) == 0xb8000000) {
+ /* BRI .. BRKI */
+ imm = (imm << 16) | (instr & 0xffff);
+ imm_bits += 16;
+ if (imm & ((uint64_t)1 << (imm_bits - 1))) {
+ imm |= ~(((uint64_t)1 << imm_bits) - 1);
+ }
+ if (instr & (1 << 19)) {
+ addr = imm;
+ }
+ else {
+ addr = instr_addr + imm;
+ }
+ }
+ else if ((instr & 0xfc000000) == 0xbc000000) {
+ /* BEQI .. BGEID */
+ ContextAddress ra_data = 0;
+ RegisterDefinition * ra_def = regs_index + ((instr >> 16) & 0x1f);
+ if (read_reg(ctx, ra_def, ra_def->size, &ra_data) < 0) return -1;
+ if (br_condition(instr, ra_data)) {
+ imm = (imm << 16) | (instr & 0xffff);
+ imm_bits += 16;
+ if (imm & ((uint64_t)1 << (imm_bits - 1))) {
+ imm |= ~(((uint64_t)1 << imm_bits) - 1);
+ }
+ addr = instr_addr + imm;
+ }
+ else if (instr & (1 << 25)) {
+ addr += 4;
+ }
+ }
+
+ *next_addr = addr;
+ return 0;
+}
+
+int cpu_enable_stepping_mode(Context * ctx, uint32_t * is_cont) {
+ Context * grp = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+ ContextExtensionMicroBlaze * ext = EXT(grp);
+ assert(!grp->exited);
+ assert(!ext->sw_stepping);
+ if (get_next_address(ctx, &ext->addr) < 0) return -1;
+ if (context_read_mem(grp, ext->addr, ext->opcode, sizeof(BREAK_INST)) < 0) return -1;
+ if (context_write_mem(grp, ext->addr, BREAK_INST, sizeof(BREAK_INST)) < 0) return -1;
+ ext->sw_stepping = 1;
+ run_ctrl_lock();
+ *is_cont = 1;
+ return 0;
+}
+
+int cpu_disable_stepping_mode(Context * ctx) {
+ Context * grp = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+ ContextExtensionMicroBlaze * ext = EXT(grp);
+ if (ext->sw_stepping) {
+ run_ctrl_unlock();
+ ext->sw_stepping = 0;
+ if (grp->exited) return 0;
+ return context_write_mem(grp, ext->addr, ext->opcode, sizeof(BREAK_INST));
+ }
+ return 0;
+}
+#endif
+
void ini_cpudefs_mdep(void) {
static uint8_t bkpt_le[4] = { 0x18, 0x00, 0x0c, 0xba };
static uint8_t bkpt_be[4] = { 0xba, 0x0c, 0x00, 0x18 };
+ context_extension_offset = context_extension(sizeof(ContextExtensionMicroBlaze));
memcpy(BREAK_INST, big_endian_host() ? bkpt_be : bkpt_le, 4);
microblaze_create_reg_definitions();
}
diff --git a/agent/machine/microblaze/tcf/cpudefs-mdep.h b/agent/machine/microblaze/tcf/cpudefs-mdep.h
index 6fcc999..d5084c4 100644
--- a/agent/machine/microblaze/tcf/cpudefs-mdep.h
+++ b/agent/machine/microblaze/tcf/cpudefs-mdep.h
@@ -20,6 +20,10 @@
extern void add_cpudefs_disassembler(Context * cpu_ctx);
#endif
+#if !defined(ENABLE_external_stepping_mode)
+#define ENABLE_external_stepping_mode 1
+#endif
+
extern RegisterDefinition * regs_index;
extern unsigned char BREAK_INST[4];
diff --git a/agent/machine/microblaze/tcf/disassembler-microblaze.c b/agent/machine/microblaze/tcf/disassembler-microblaze.c
index 1a03a3a..0fc5e06 100644
--- a/agent/machine/microblaze/tcf/disassembler-microblaze.c
+++ b/agent/machine/microblaze/tcf/disassembler-microblaze.c
@@ -1497,14 +1497,14 @@
return &dr;
}
-DisassemblyResult * disassemble_microblaze(uint8_t * buf,
+DisassemblyResult * disassemble_microblaze(uint8_t * code,
ContextAddress addr, ContextAddress size, DisassemblerParams * params) {
en_64_bit = 0;
- return disassemble_instr(buf, addr, size, params);
+ return disassemble_instr(code, addr, size, params);
}
-DisassemblyResult * disassemble_microblaze_x(uint8_t * buf,
+DisassemblyResult * disassemble_microblaze_x(uint8_t * code,
ContextAddress addr, ContextAddress size, DisassemblerParams * params) {
en_64_bit = 1;
- return disassemble_instr(buf, addr, size, params);
+ return disassemble_instr(code, addr, size, params);
}
diff --git a/agent/machine/microblaze/tcf/regset-mdep.h b/agent/machine/microblaze/tcf/regset-mdep.h
index b31ecc0..e209b19 100644
--- a/agent/machine/microblaze/tcf/regset-mdep.h
+++ b/agent/machine/microblaze/tcf/regset-mdep.h
@@ -12,3 +12,6 @@
* Contributors:
* Xilinx - initial API and implementation
*******************************************************************************/
+
+/* offset to be applied to the PC after a software trap */
+#define TRAP_OFFSET 0
diff --git a/agent/msvc/agent-vc2015.vcxproj b/agent/msvc/agent-vc2015.vcxproj
index 5cfd82b..5e1d219 100644
--- a/agent/msvc/agent-vc2015.vcxproj
+++ b/agent/msvc/agent-vc2015.vcxproj
@@ -360,6 +360,7 @@
<ClInclude Include="..\machine\a64\tcf\cpu-regs-gdb.h" />
<ClInclude Include="..\machine\arm\tcf\cpu-regs-gdb.h" />
<ClInclude Include="..\machine\i386\tcf\cpu-regs-gdb.h" />
+ <ClInclude Include="..\machine\microblazex\tcf\cpu-regs-gdb.h" />
<ClInclude Include="..\machine\microblaze\tcf\cpu-regs-gdb.h" />
<ClInclude Include="..\machine\microblaze\tcf\cpudefs-mdep.h" />
<ClInclude Include="..\machine\microblaze\tcf\disassembler-microblaze.h" />
diff --git a/agent/msvc/agent-vc2015.vcxproj.filters b/agent/msvc/agent-vc2015.vcxproj.filters
index d081654..778182a 100644
--- a/agent/msvc/agent-vc2015.vcxproj.filters
+++ b/agent/msvc/agent-vc2015.vcxproj.filters
@@ -64,6 +64,9 @@
<Filter Include="machine\powerpc">
<UniqueIdentifier>{ff538629-61f5-41bb-907c-975bcb6161f1}</UniqueIdentifier>
</Filter>
+ <Filter Include="machine\microblazex">
+ <UniqueIdentifier>{4e8ce030-4562-4d31-8bf8-3f970be0c354}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\tcf\framework\asyncreq.c">
@@ -837,5 +840,8 @@
<ClInclude Include="..\machine\microblaze\tcf\disassembler-microblaze.h">
<Filter>machine\microblaze</Filter>
</ClInclude>
+ <ClInclude Include="..\machine\microblazex\tcf\cpu-regs-gdb.h">
+ <Filter>machine\microblazex</Filter>
+ </ClInclude>
</ItemGroup>
</Project>
\ No newline at end of file
diff --git a/agent/system/GNU/Linux/tcf/context-linux.c b/agent/system/GNU/Linux/tcf/context-linux.c
index 1462d46..79d6fe8 100644
--- a/agent/system/GNU/Linux/tcf/context-linux.c
+++ b/agent/system/GNU/Linux/tcf/context-linux.c
@@ -921,12 +921,12 @@
}
#else
if (i >= offsetof(REG_SET, user.regs) && i < offsetof(REG_SET, user.regs) + sizeof(ext->regs->user.regs)) {
- if (ptrace(PTRACE_GETREGS, ext->pid, 0, &ext->regs->user.regs) < 0 && errno != ESRCH) {
- err = errno;
- break;
+ /* Try to read all registers at once */
+ if (ptrace(PTRACE_GETREGS, ext->pid, 0, &ext->regs->user.regs) == 0) {
+ memset(ext->regs_valid + offsetof(REG_SET, user.regs), 0xff, sizeof(ext->regs->user.regs));
+ continue;
}
- memset(ext->regs_valid + offsetof(REG_SET, user.regs), 0xff, sizeof(ext->regs->user.regs));
- continue;
+ /* Did not work, use PTRACE_PEEKUSER to get one register at a time */
}
if (i >= offsetof(REG_SET, fp) && i < offsetof(REG_SET, fp) + sizeof(ext->regs->fp)) {
#if defined(__arm__) || defined(__aarch64__)