0,0 → 1,269 |
/* |
* Copyright (c) 2008 Jiri Svoboda |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* |
* - Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* - Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* - The name of the author may not be used to endorse or promote products |
* derived from this software without specific prior written permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 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. |
*/ |
|
/** @addtogroup debug |
* @{ |
*/ |
/** @file |
*/ |
|
#include <stdio.h> |
#include <stdlib.h> |
#include <assert.h> |
#include <sys/types.h> |
#include <errno.h> |
#include <udebug.h> |
|
#include "../../../cons.h" |
#include "../../../main.h" |
#include "../../../breakpoint.h" |
#include "../../../include/arch.h" |
#include "../../../include/arch/arch.h" |
#include "../../../genarch/idec/idec.h" |
|
static istate_t istate; |
|
typedef enum { |
/* Branch */ |
OP_B, |
OP_BL, |
OP_BLX1, |
OP_BLX2, |
OP_BX |
} op_t; |
|
typedef struct { |
uint32_t mask; |
uint32_t value; |
op_t op; |
} instr_desc_t; |
|
static instr_desc_t decoding_table[] = { |
/* Unconditional branch (link) and exchange */ |
{ 0xfe000000, 0xfa000000, OP_BLX1 }, |
{ 0x0ffffff0, 0x012fff30, OP_BLX2 }, |
{ 0x0ffffff0, 0x012fff10, OP_BX }, |
|
/* |
* Order is significant here, as the condition code for B, BL |
* (the top 4 bits) must not be 0xf, which is caught by BLX, BX |
*/ |
|
/* Branch (and link) */ |
{ 0x0f000000, 0x0a000000, OP_B }, |
{ 0x0f000000, 0x0b000000, OP_BL }, |
|
{ 0, 0, -1 } |
}; |
|
/** Sign-extend a value to 32 bits. |
* |
* @param val A signed value (of limited width) |
* @param bits Bit-width of value. |
* @return The value extended to a 32-bit signed integer. |
*/ |
#define EXTS(val, bits) ((int32_t)(val) << (32 - (bits)) >> (32 - (bits))) |
|
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) |
{ |
int rc; |
|
rc = idec_breakpoint_set(b); |
if (rc != 0) return rc; |
|
return 0; |
} |
|
int arch_breakpoint_remove(breakpoint_t *b) |
{ |
return idec_breakpoint_remove(b); |
} |
|
static int islot_read(uintptr_t addr, uint32_t *instr) |
{ |
int rc; |
|
rc = udebug_mem_read(app_phone, instr, addr, sizeof(uint32_t)); |
if (rc != EOK) { |
cons_printf("Error reading memory address 0x%zx\n", addr); |
} |
|
return rc; |
} |
|
static int get_reg(dthread_t *dt, int reg_no, uint32_t *value) |
{ |
int rc; |
|
cons_printf("get_reg...\n"); |
|
if (reg_no == 0) { |
*value = 0; |
return 0; |
} |
|
rc = udebug_regs_read(app_phone, dt->hash, &istate); |
if (rc < 0) return rc; |
|
switch (reg_no) { |
|
case 0: *value = istate.r0; break; |
case 1: *value = istate.r1; break; |
case 2: *value = istate.r2; break; |
case 3: *value = istate.r3; break; |
case 4: *value = istate.r4; break; |
case 5: *value = istate.r5; break; |
case 6: *value = istate.r6; break; |
case 7: *value = istate.r7; break; |
case 8: *value = istate.r8; break; |
case 9: *value = istate.r9; break; |
case 10: *value = istate.r10; break; |
case 11: *value = istate.r11; break; |
case 12: *value = istate.r12; break; |
case 13: *value = istate.sp; break; |
case 14: *value = istate.lr; break; |
case 15: *value = istate.pc; break; |
|
} |
printf("get_reg ok (0x%08x)\n", *value); |
|
return 0; |
} |
|
static op_t instr_decode(uint32_t instr) |
{ |
instr_desc_t *idesc; |
|
idesc = &decoding_table[0]; |
while (idesc->op >= 0) { |
if ((instr & idesc->mask) == idesc->value) |
return idesc->op; |
++idesc; |
} |
|
return -1; |
} |
|
/** Get address of the instruction that will be executed after the current one. |
* |
* Assumptions: addr == PC, *addr is not covered by a BREAK. |
* |
* @param dt Dthread on which to operate. |
* @param addr Address of an instruction. |
* @param buffer Buffer for storing up to 2 addresses. |
* @return Number of stored addresses or negative error code. |
*/ |
int get_next_addr(dthread_t *dt, uintptr_t addr, uintptr_t *buffer) |
{ |
uint32_t instr; |
int32_t imm, h; |
uint32_t regv; |
op_t op; |
int rc; |
int n; |
|
rc = islot_read(addr, &instr); |
if (rc != 0) return rc; |
|
op = instr_decode(instr); |
|
switch (op) { |
/* Branch (and link) */ |
case OP_B: |
case OP_BL: |
/* imm is a 24-bit signed integer */ |
imm = EXTS(instr & 0x00ffffff, 24); |
buffer[0] = (addr + 8) + (imm << 2); |
buffer[1] = addr + 4; |
n = 2; |
break; |
|
/* Unconditional branch, link and exchange */ |
case OP_BLX1: |
/* imm is a 24-bit signed integer */ |
imm = EXTS(instr & 0x00ffffff, 24); |
h = (instr & 0x01000000) ? 1 : 0; |
buffer[0] = (addr + 8) + (imm << 2) + (h << 1); |
n = 1; |
break; |
|
case OP_BLX2: |
case OP_BX: |
/* BLX (2), BX */ |
rc = get_reg(dt, instr & 0xf, ®v); |
if (rc != 0) return rc; |
|
buffer[0] = regv & ~0x1; |
buffer[1] = addr + 4; |
n = 2; |
break; |
|
/* TODO: handle instructions writing r15 */ |
|
default: |
/* Regular instruction */ |
buffer[0] = addr + 4; |
n = 1; |
break; |
} |
|
return n; |
} |
|
void arch_event_breakpoint(thash_t thread_hash) |
{ |
idec_event_breakpoint(thread_hash); |
} |
|
void arch_event_trap(dthread_t *dt) |
{ |
/* Unused */ |
(void)dt; |
} |
|
void arch_dump_regs(thash_t thash) |
{ |
/* TODO */ |
} |
|
void arch_singlestep(dthread_t *dt) |
{ |
idec_singlestep(dt); |
} |
|
/** @} |
*/ |