49,6 → 49,29 |
static istate_t istate; |
|
typedef enum { |
/* Branches (conditional) */ |
OP_BCzF, |
OP_BCzFL, |
OP_BCzT, |
OP_BCzTL, |
OP_BEQ, |
OP_BEQL, |
OP_BGEZ, |
OP_BGEZAL, |
OP_BGEZALL, |
OP_BGEZL, |
OP_BGTZ, |
OP_BGTZL, |
OP_BLEZ, |
OP_BLEZL, |
OP_BLTZ, |
OP_BLTZAL, |
OP_BLTZALL, |
OP_BLTZL, |
OP_BNE, |
OP_BNEL, |
|
/* Jumps (unconditional) */ |
OP_J, |
OP_JAL, |
OP_JALR, |
62,15 → 85,50 |
} instr_desc_t; |
|
static instr_desc_t decoding_table[] = { |
{ 0xf3ff0000, 0x41000000, OP_BCzF }, |
{ 0xf3ff0000, 0x41020000, OP_BCzFL }, |
{ 0xf3ff0000, 0x41010000, OP_BCzT }, |
{ 0xf3ff0000, 0x41030000, OP_BCzTL }, |
{ 0xfc000000, 0x10000000, OP_BEQ }, |
{ 0xfc000000, 0x50000000, OP_BEQL }, |
{ 0xfc1f0000, 0x04010000, OP_BGEZ }, |
{ 0xfc1f0000, 0x04110000, OP_BGEZAL }, |
{ 0xfc1f0000, 0x04130000, OP_BGEZALL }, |
{ 0xfc1f0000, 0x04030000, OP_BGEZL }, |
{ 0xfc1f0000, 0x1c000000, OP_BGTZ }, |
{ 0xfc1f0000, 0x5c000000, OP_BGTZL }, |
{ 0xfc1f0000, 0x18000000, OP_BLEZ }, |
{ 0xfc1f0000, 0x58000000, OP_BLEZL }, |
{ 0xfc1f0000, 0x04000000, OP_BLTZ }, |
{ 0xfc1f0000, 0x04100000, OP_BLTZAL }, |
{ 0xfc1f0000, 0x04120000, OP_BLTZALL }, |
{ 0xfc1f0000, 0x04020000, OP_BLTZL }, |
{ 0xfc000000, 0x14000000, OP_BNE }, |
{ 0xfc000000, 0x54000000, OP_BNEL }, |
|
{ 0xfc000000, 0x08000000, OP_J }, |
{ 0xfc000000, 0x0c000000, OP_JAL }, |
{ 0xfc1f07ff, 0x00000009, OP_JALR }, |
{ 0xfc1fffff, 0x00000008, OP_JR }, |
|
{ 0, 0, -1 } |
}; |
|
void arch_dthread_initialize(dthread_t *dt) |
{ |
dt->arch.singlestep = false; |
|
bstore_initialize(&dt->arch.cur); |
bstore_initialize(&dt->arch.next[0]); |
bstore_initialize(&dt->arch.next[1]); |
} |
|
int arch_breakpoint_set(breakpoint_t *b) |
{ |
bstore_initialize(&b->arch.bs); |
bstore_initialize(&b->arch.next_bs[0]); |
bstore_initialize(&b->arch.next_bs[1]); |
|
return bstore_push(&b->arch.bs, b->addr, OPCODE_BREAK); |
} |
|
107,8 → 165,6 |
|
static int get_reg(int reg_no, uint32_t *value) |
{ |
int rc; |
|
cons_printf("get_reg...\n"); |
|
if (reg_no == 0) { |
127,15 → 183,18 |
* |
* Assumptions: addr == PC, *addr is not covered by a BREAK. |
* |
* @param addr Address of an instruction. |
* @return Address of the instruction that will be executed afterwards. |
* @param addr Address of an instruction. |
* @param buffer Buffer for storing up to 2 addresses. |
* @return Number of stored addresses or negative error code. |
*/ |
static int get_next_addr(uintptr_t addr, uintptr_t *next_addr) |
static int get_next_addr(uintptr_t addr, uintptr_t *buffer) |
{ |
/* TODO: J[AL]R, branches and delay slots */ |
uint32_t instr; |
int32_t offset; |
op_t op; |
int rc; |
int n; |
|
rc = islot_read(addr, &instr); |
if (rc != 0) return rc; |
143,23 → 202,54 |
op = instr_decode(instr); |
|
switch (op) { |
case OP_BCzF: |
case OP_BCzFL: |
case OP_BCzT: |
case OP_BCzTL: |
case OP_BEQ: |
case OP_BEQL: |
case OP_BGEZ: |
case OP_BGEZAL: |
case OP_BGEZALL: |
case OP_BGEZL: |
case OP_BGTZ: |
case OP_BGTZL: |
case OP_BLEZ: |
case OP_BLTZ: |
case OP_BLTZAL: |
case OP_BLTZALL: |
case OP_BLTZL: |
case OP_BNE: |
case OP_BNEL: |
/* Branch */ |
offset = (int32_t)(int16_t)(instr & 0x0000ffff) << 2; |
buffer[0] = (addr + 4) + offset; /* taken */ |
buffer[1] = addr + 8; /* not taken */ |
n = 2; |
break; |
|
case OP_J: |
case OP_JAL: |
*next_addr = |
/* Immediate jump */ |
buffer[0] = |
((addr + 4) & 0xf0000000) | |
((instr & 0x03ffffff) << 2); |
n = 1; |
break; |
case OP_JR: |
case OP_JALR: |
rc = get_reg((instr >> 21) & 0x1f, next_addr); |
/* Register jump */ |
rc = get_reg((instr >> 21) & 0x1f, &buffer[0]); |
n = 1; |
break; |
default: |
/* Regular instruction */ |
*next_addr = addr + 4; |
buffer[0] = addr + 4; |
n = 1; |
break; |
} |
|
return 0; |
return n; |
} |
|
static void _ev_breakpoint(thash_t thread_hash) |
166,10 → 256,10 |
{ |
breakpoint_t *b; |
dthread_t *dt; |
int rc; |
int rc, n_next, i; |
uint32_t epc; |
uintptr_t brk_addr; |
uintptr_t next_addr; |
uintptr_t next_addr[2]; |
uint32_t brkp; |
|
brkp = OPCODE_BREAK; |
188,10 → 278,13 |
assert(active_bkpt->arch.bs.address == brk_addr); |
b = active_bkpt; |
|
/* A breakpoint-clearing BRK has been hit */ |
/* A breakpoint-restoring BRK has been hit */ |
cons_printf("restoring breakpoint %d\n", b->id); |
rc = bstore_pop(&b->arch.bs); |
if (rc != 0) return; |
for (i = 0; i < b->arch.n_next; ++i) { |
rc = bstore_pop(&b->arch.next_bs[i]); |
if (rc != 0) return; |
} |
|
rc = bstore_push(&b->arch.bs, b->addr, OPCODE_BREAK); |
if (rc != 0) return; |
active_bkpt = NULL; |
214,8 → 307,8 |
rc = bstore_pop(&b->arch.bs); |
if (rc != 0) return; |
|
rc = get_next_addr(brk_addr, &next_addr); |
if (rc != 0) return; |
n_next = get_next_addr(brk_addr, next_addr); |
if (n_next < 0) return; |
|
/* |
* There could be another breakpoint at next_addr, |
222,8 → 315,12 |
* but that's okay. We'll pop the active breakpoint bs |
* before doing anything else. |
*/ |
rc = bstore_push(&b->arch.bs, next_addr, OPCODE_BREAK); |
if (rc != 0) return; |
for (i = 0; i < n_next; ++i) { |
rc = bstore_push(&b->arch.next_bs[i], next_addr[i], |
OPCODE_BREAK); |
if (rc != 0) return; |
} |
b->arch.n_next = n_next; |
|
active_bkpt = b; |
b->active = true; |
235,7 → 332,7 |
static void _ev_singlestep(thash_t thread_hash) |
{ |
dthread_t *dt; |
int rc; |
int rc, i; |
uint32_t epc; |
int brk_addr; |
uint32_t brkp; |
255,12 → 352,15 |
brk_addr = epc; |
|
if (dt->arch.cur.valid) { |
cons_printf("restore breakpoint BRK\n"); |
cons_printf("restore breakpoint BREAK\n"); |
rc = bstore_pop(&dt->arch.cur); |
} |
|
cons_printf("clear singlestep BRK\n"); |
rc = bstore_pop(&dt->arch.next); |
cons_printf("\nclear singlestep BREAKs\n"); |
for (i = 0; i < dt->arch.n_next; ++i) { |
rc = bstore_pop(&dt->arch.next[i]); |
if (rc != 0) return; |
} |
|
dt->arch.singlestep = false; |
|
293,11 → 393,12 |
|
void arch_singlestep(dthread_t *dt) |
{ |
int rc; |
int rc, i; |
uint32_t epc; |
breakpoint_t *b; |
uint32_t old_instr; |
uintptr_t next_addr; |
uintptr_t next_addr[2]; |
int n_next; |
|
assert(active_bkpt == NULL); |
assert(dt->arch.singlestep == false); |
317,12 → 418,15 |
if (rc < 0) return; |
} |
|
rc = get_next_addr(epc, &next_addr); |
if (rc != 0) return; |
n_next = get_next_addr(epc, next_addr); |
if (n_next < 0) return; |
|
/* Cover next instruction with BREAK */ |
rc = bstore_push(&dt->arch.next, next_addr, OPCODE_BREAK); |
if (rc != 0) return; |
/* Cover next instruction(s) with BREAK */ |
for (i = 0; i < n_next; ++i) { |
rc = bstore_push(&dt->arch.next[i], next_addr[i], OPCODE_BREAK); |
if (rc != 0) return; |
} |
dt->arch.n_next = n_next; |
|
dt->arch.singlestep = true; |
dthread_resume(dt); |