diff options
author | drath <drath@b42882b7-edfa-0310-969c-e2dbd0fdcd60> | 2007-03-28 16:31:55 +0000 |
---|---|---|
committer | drath <drath@b42882b7-edfa-0310-969c-e2dbd0fdcd60> | 2007-03-28 16:31:55 +0000 |
commit | 4a5f45e87d593d6911da680cb92ffbc9b43c9486 (patch) | |
tree | d41ddb8594b5fab03c12075cbc4d3888d59ed027 /src/target | |
parent | 5ed126c4f90948fbf53d186dc4ef49018fb5ecfc (diff) | |
download | openocd_libswd-4a5f45e87d593d6911da680cb92ffbc9b43c9486.tar.gz openocd_libswd-4a5f45e87d593d6911da680cb92ffbc9b43c9486.tar.bz2 openocd_libswd-4a5f45e87d593d6911da680cb92ffbc9b43c9486.tar.xz openocd_libswd-4a5f45e87d593d6911da680cb92ffbc9b43c9486.zip |
- merged XScale branch back into trunk
- fixed some compiler warnigns in amt_jtagaccel.c, bitbang.c, parport.c
- free working area and register stuff if str7x block write algorithm failed
- check PC after exiting a target algorithm in armv4_5.c
git-svn-id: svn://svn.berlios.de/openocd/trunk@135 b42882b7-edfa-0310-969c-e2dbd0fdcd60
Diffstat (limited to 'src/target')
-rw-r--r-- | src/target/Makefile.am | 4 | ||||
-rw-r--r-- | src/target/arm_disassembler.c | 35 | ||||
-rw-r--r-- | src/target/arm_disassembler.h | 36 | ||||
-rw-r--r-- | src/target/arm_simulator.c | 684 | ||||
-rw-r--r-- | src/target/arm_simulator.h | 31 | ||||
-rw-r--r-- | src/target/armv4_5.c | 9 | ||||
-rw-r--r-- | src/target/armv4_5.h | 10 | ||||
-rw-r--r-- | src/target/target.c | 87 | ||||
-rw-r--r-- | src/target/target.h | 14 | ||||
-rw-r--r-- | src/target/xscale.c | 3175 | ||||
-rw-r--r-- | src/target/xscale.h | 145 | ||||
-rw-r--r-- | src/target/xscale/build.sh | 7 | ||||
-rw-r--r-- | src/target/xscale/debug_handler.S | 718 | ||||
-rw-r--r-- | src/target/xscale/debug_handler.cmd | 49 | ||||
-rw-r--r-- | src/target/xscale/protocol.h | 70 |
15 files changed, 5018 insertions, 56 deletions
diff --git a/src/target/Makefile.am b/src/target/Makefile.am index 3e2b39d3..cf1955a0 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -3,7 +3,7 @@ METASOURCES = AUTO noinst_LIBRARIES = libtarget.a libtarget_a_SOURCES = target.c register.c breakpoints.c armv4_5.c embeddedice.c etm.c arm7tdmi.c arm9tdmi.c \ arm_jtag.c arm7_9_common.c algorithm.c arm920t.c arm720t.c armv4_5_mmu.c armv4_5_cache.c arm_disassembler.c \ - arm966e.c arm926ejs.c etb.c + arm966e.c arm926ejs.c etb.c xscale.c arm_simulator.c noinst_HEADERS = target.h register.h armv4_5.h embeddedice.h etm.h arm7tdmi.h arm9tdmi.h \ arm_jtag.h arm7_9_common.h arm920t.h arm720t.h armv4_5_mmu.h armv4_5_cache.h breakpoints.h algorithm.h \ - arm_disassembler.h arm966e.h arm926ejs.h etb.h + arm_disassembler.h arm966e.h arm926ejs.h etb.h xscale.h arm_simulator.h diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c index f9e0227a..dd779282 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -835,6 +835,9 @@ int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction) snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tBLX%s r%i", address, opcode, COND(opcode), Rm); + + instruction->info.b_bl_bx_blx.reg_operand = Rm; + instruction->info.b_bl_bx_blx.target_address = -1; } /* Enhanced DSP add/subtracts */ @@ -1078,6 +1081,18 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) instruction->info.data_proc.shifter_operand.immediate_shift.shift_imm = shift_imm; instruction->info.data_proc.shifter_operand.immediate_shift.shift = shift; + /* LSR encodes a shift by 32 bit as 0x0 */ + if ((shift == 0x1) && (shift_imm == 0x0)) + shift_imm = 0x20; + + /* ASR encodes a shift by 32 bit as 0x0 */ + if ((shift == 0x2) && (shift_imm == 0x0)) + shift_imm = 0x20; + + /* ROR by 32 bit is actually a RRX */ + if ((shift == 0x3) && (shift_imm == 0x0)) + shift = 0x4; + if ((shift_imm == 0x0) && (shift == 0x0)) { snprintf(shifter_operand, 32, "r%i", Rm); @@ -1090,22 +1105,19 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) } else if (shift == 0x1) /* LSR */ { - if (shift_imm == 0x0) - shift_imm = 0x32; snprintf(shifter_operand, 32, "r%i, LSR #0x%x", Rm, shift_imm); } else if (shift == 0x2) /* ASR */ { - if (shift_imm == 0x0) - shift_imm = 0x32; snprintf(shifter_operand, 32, "r%i, ASR #0x%x", Rm, shift_imm); } - else if (shift == 0x3) /* ROR or RRX */ + else if (shift == 0x3) /* ROR */ + { + snprintf(shifter_operand, 32, "r%i, ROR #0x%x", Rm, shift_imm); + } + else if (shift == 0x4) /* RRX */ { - if (shift_imm == 0x0) /* RRX */ - snprintf(shifter_operand, 32, "r%i, RRX", Rm); - else - snprintf(shifter_operand, 32, "r%i, ROR #0x%x", Rm, shift_imm); + snprintf(shifter_operand, 32, "r%i, RRX", Rm); } } } @@ -1130,7 +1142,7 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) { snprintf(shifter_operand, 32, "r%i, ASR r%i", Rm, Rs); } - else if (shift == 0x3) /* ROR or RRX */ + else if (shift == 0x3) /* ROR */ { snprintf(shifter_operand, 32, "r%i, ROR r%i", Rm, Rs); } @@ -1159,7 +1171,7 @@ int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction) return ERROR_OK; } -int evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction) +int arm_evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction) { /* clear fields, to avoid confusion */ memset(instruction, 0, sizeof(arm_instruction_t)); @@ -1302,3 +1314,4 @@ int evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction) ERROR("should never reach this point"); return -1; } + diff --git a/src/target/arm_disassembler.h b/src/target/arm_disassembler.h index c6dc6967..b55c8855 100644 --- a/src/target/arm_disassembler.h +++ b/src/target/arm_disassembler.h @@ -126,28 +126,30 @@ typedef struct arm_b_bl_bx_blx_instr_s u32 target_address; } arm_b_bl_bx_blx_instr_t; +union arm_shifter_operand +{ + struct { + u32 immediate; + } immediate; + struct { + u8 Rm; + u8 shift; + u8 shift_imm; + } immediate_shift; + struct { + u8 Rm; + u8 shift; + u8 Rs; + } register_shift; +}; + typedef struct arm_data_proc_instr_s { int variant; /* 0: immediate, 1: immediate_shift, 2: register_shift */ u8 S; u8 Rn; u8 Rd; - union - { - struct { - u8 immediate; - } immediate; - struct { - u8 Rm; - u8 shift; - u8 shift_imm; - } immediate_shift; - struct { - u8 Rm; - u8 shift; - u8 Rs; - } register_shift; - } shifter_operand; + union arm_shifter_operand shifter_operand; } arm_data_proc_instr_t; typedef struct arm_load_store_instr_s @@ -192,7 +194,7 @@ typedef struct arm_instruction_s } arm_instruction_t; -extern int evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction); +extern int arm_evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction); #define COND(opcode) (arm_condition_strings[(opcode & 0xf0000000)>>28]) diff --git a/src/target/arm_simulator.c b/src/target/arm_simulator.c new file mode 100644 index 00000000..fd0b309c --- /dev/null +++ b/src/target/arm_simulator.c @@ -0,0 +1,684 @@ +/*************************************************************************** + * Copyright (C) 2006 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "target.h" +#include "armv4_5.h" +#include "arm_disassembler.h" +#include "arm_simulator.h" +#include "log.h" +#include "binarybuffer.h" + +#include <string.h> + +u32 arm_shift(u8 shift, u32 Rm, u32 shift_amount, u8 *carry) +{ + u32 return_value; + shift_amount &= 0xff; + + if (shift == 0x0) /* LSL */ + { + if ((shift_amount > 0) && (shift_amount <= 32)) + { + return_value = Rm << shift_amount; + *carry = Rm >> (32 - shift_amount); + } + else if (shift_amount > 32) + { + return_value = 0x0; + *carry = 0x0; + } + else /* (shift_amount == 0) */ + { + return_value = Rm; + } + } + else if (shift == 0x1) /* LSR */ + { + if ((shift_amount > 0) && (shift_amount <= 32)) + { + return_value = Rm >> shift_amount; + *carry = (Rm >> (shift_amount - 1)) & 1; + } + else if (shift_amount > 32) + { + return_value = 0x0; + *carry = 0x0; + } + else /* (shift_amount == 0) */ + { + return_value = Rm; + } + } + else if (shift == 0x2) /* ASR */ + { + if ((shift_amount > 0) && (shift_amount <= 32)) + { + /* right shifts of unsigned values are guaranteed to be logical (shift in zeroes) + * simulate an arithmetic shift (shift in signed-bit) by adding the signed-bit manually */ + return_value = Rm >> shift_amount; + if (Rm & 0x80000000) + return_value |= 0xffffffff << (32 - shift_amount); + } + else if (shift_amount > 32) + { + if (Rm & 0x80000000) + { + return_value = 0xffffffff; + *carry = 0x1; + } + else + { + return_value = 0x0; + *carry = 0x0; + } + } + else /* (shift_amount == 0) */ + { + return_value = Rm; + } + } + else if (shift == 0x3) /* ROR */ + { + if (shift_amount == 0) + { + return_value = Rm; + } + else + { + shift_amount = shift_amount % 32; + return_value = (Rm >> shift_amount) | (Rm << (32 - shift_amount)); + *carry = (return_value >> 31) & 0x1; + } + } + else if (shift == 0x4) /* RRX */ + { + return_value = Rm >> 1; + if (*carry) + Rm |= 0x80000000; + *carry = Rm & 0x1; + } + + return return_value; +} + +u32 arm_shifter_operand(armv4_5_common_t *armv4_5, int variant, union arm_shifter_operand shifter_operand, u8 *shifter_carry_out) +{ + u32 return_value; + int instruction_size; + + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + instruction_size = 4; + else + instruction_size = 2; + + *shifter_carry_out = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 29, 1); + + if (variant == 0) /* 32-bit immediate */ + { + return_value = shifter_operand.immediate.immediate; + } + else if (variant == 1) /* immediate shift */ + { + u32 Rm = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, shifter_operand.immediate_shift.Rm).value, 0, 32); + + /* adjust RM in case the PC is being read */ + if (shifter_operand.immediate_shift.Rm == 15) + Rm += 2 * instruction_size; + + return_value = arm_shift(shifter_operand.immediate_shift.shift, Rm, shifter_operand.immediate_shift.shift_imm, shifter_carry_out); + } + else if (variant == 2) /* register shift */ + { + u32 Rm = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, shifter_operand.register_shift.Rm).value, 0, 32); + u32 Rs = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, shifter_operand.register_shift.Rs).value, 0, 32); + + /* adjust RM in case the PC is being read */ + if (shifter_operand.register_shift.Rm == 15) + Rm += 2 * instruction_size; + + return_value = arm_shift(shifter_operand.immediate_shift.shift, Rm, Rs, shifter_carry_out); + } + else + { + ERROR("BUG: shifter_operand.variant not 0, 1 or 2"); + return_value = 0xffffffff; + } + + return return_value; +} + +int pass_condition(u32 cpsr, u32 opcode) +{ + switch ((opcode & 0xf0000000) >> 28) + { + case 0x0: /* EQ */ + if (cpsr & 0x40000000) + return 1; + else + return 0; + case 0x1: /* NE */ + if (!(cpsr & 0x40000000)) + return 1; + else + return 0; + case 0x2: /* CS */ + if (cpsr & 0x20000000) + return 1; + else + return 0; + case 0x3: /* CC */ + if (!(cpsr & 0x20000000)) + return 1; + else + return 0; + case 0x4: /* MI */ + if (cpsr & 0x80000000) + return 1; + else + return 0; + case 0x5: /* PL */ + if (!(cpsr & 0x80000000)) + return 1; + else + return 0; + case 0x6: /* VS */ + if (cpsr & 0x10000000) + return 1; + else + return 0; + case 0x7: /* VC */ + if (!(cpsr & 0x10000000)) + return 1; + else + return 0; + case 0x8: /* HI */ + if ((cpsr & 0x20000000) && !(cpsr & 0x40000000)) + return 1; + else + return 0; + case 0x9: /* LS */ + if (!(cpsr & 0x20000000) || (cpsr & 0x40000000)) + return 1; + else + return 0; + case 0xa: /* GE */ + if (((cpsr & 0x80000000) && (cpsr & 0x10000000)) + || (!(cpsr & 0x80000000) && !(cpsr & 0x10000000))) + return 1; + else + return 0; + case 0xb: /* LT */ + if (((cpsr & 0x80000000) && !(cpsr & 0x10000000)) + || (!(cpsr & 0x80000000) && (cpsr & 0x10000000))) + return 1; + else + return 0; + case 0xc: /* GT */ + if (!(cpsr & 0x40000000) && + (((cpsr & 0x80000000) && (cpsr & 0x10000000)) + || (!(cpsr & 0x80000000) && !(cpsr & 0x10000000)))) + return 1; + else + return 0; + case 0xd: /* LE */ + if ((cpsr & 0x40000000) && + (((cpsr & 0x80000000) && !(cpsr & 0x10000000)) + || (!(cpsr & 0x80000000) && (cpsr & 0x10000000)))) + return 1; + else + return 0; + case 0xe: + case 0xf: + return 1; + + } + + ERROR("BUG: should never get here"); + return 0; +} + +/* simulate a single step (if possible) + * if the dry_run_pc argument is provided, no state is changed, + * but the new pc is stored in the variable pointed at by the argument + */ +int arm_simulate_step(target_t *target, u32 *dry_run_pc) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + u32 opcode; + u32 current_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + arm_instruction_t instruction; + int instruction_size; + + if (armv4_5->core_state == ARMV4_5_STATE_ARM) + { + /* get current instruction, and identify it */ + target_read_u32(target, current_pc, &opcode); + arm_evaluate_opcode(opcode, current_pc, &instruction); + instruction_size = 4; + } + else + { + /* TODO: add support for Thumb instruction set */ + instruction_size = 2; + } + + /* check condition code */ + if (!pass_condition(buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), opcode)) + { + if (dry_run_pc) + { + *dry_run_pc = current_pc + instruction_size; + } + else + { + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, current_pc + instruction_size); + } + + return ERROR_OK; + } + + /* examine instruction type */ + + /* branch instructions */ + if ((instruction.type >= ARM_B) && (instruction.type <= ARM_BLX)) + { + u32 target; + + if (instruction.info.b_bl_bx_blx.reg_operand == -1) + { + target = instruction.info.b_bl_bx_blx.target_address; + } + else + { + target = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.b_bl_bx_blx.reg_operand).value, 0, 32); + } + + if (dry_run_pc) + { + *dry_run_pc = target; + return ERROR_OK; + } + else + { + if (instruction.type == ARM_B) + { + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, target); + } + else if (instruction.type == ARM_BL) + { + u32 old_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 14).value, 0, 32, old_pc + 4); + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, target); + } + else if (instruction.type == ARM_BX) + { + if (target & 0x1) + { + armv4_5->core_state = ARMV4_5_STATE_THUMB; + } + else + { + armv4_5->core_state = ARMV4_5_STATE_ARM; + } + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, target & 0xfffffffe); + } + else if (instruction.type == ARM_BLX) + { + u32 old_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 14).value, 0, 32, old_pc + 4); + + if (target & 0x1) + { + armv4_5->core_state = ARMV4_5_STATE_THUMB; + } + else + { + armv4_5->core_state = ARMV4_5_STATE_ARM; + } + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, target & 0xfffffffe); + } + + return ERROR_OK; + } + } + /* data processing instructions, except compare instructions (CMP, CMN, TST, TEQ) */ + else if (((instruction.type >= ARM_AND) && (instruction.type <= ARM_RSC)) + || ((instruction.type >= ARM_ORR) && (instruction.type <= ARM_MVN))) + { + u32 Rd, Rn, shifter_operand; + u8 C = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 29, 1); + u8 carry_out; + + Rd = 0x0; + Rn = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.data_proc.Rn).value, 0, 32); + shifter_operand = arm_shifter_operand(armv4_5, instruction.info.data_proc.variant, instruction.info.data_proc.shifter_operand, &carry_out); + + /* adjust Rn in case the PC is being read */ + if (instruction.info.data_proc.Rn == 15) + Rn += 2 * instruction_size; + + if (instruction.type == ARM_AND) + Rd = Rn & shifter_operand; + else if (instruction.type == ARM_EOR) + Rd = Rn ^ shifter_operand; + else if (instruction.type == ARM_SUB) + Rd = Rn - shifter_operand; + else if (instruction.type == ARM_RSB) + Rd = shifter_operand - Rn; + else if (instruction.type == ARM_ADD) + Rd = Rn + shifter_operand; + else if (instruction.type == ARM_ADC) + Rd = Rn + shifter_operand + (C & 1); + else if (instruction.type == ARM_SBC) + Rd = Rn - shifter_operand - (C & 1) ? 0 : 1; + else if (instruction.type == ARM_RSC) + Rd = shifter_operand - Rn - (C & 1) ? 0 : 1; + else if (instruction.type == ARM_ORR) + Rd = Rn | shifter_operand; + else if (instruction.type == ARM_BIC) + Rd = Rn & ~(shifter_operand); + else if (instruction.type == ARM_MOV) + Rd = shifter_operand; + else if (instruction.type == ARM_MVN) + Rd = ~shifter_operand; + + if (dry_run_pc) + { + if (instruction.info.data_proc.Rd == 15) + { + *dry_run_pc = Rd; + return ERROR_OK; + } + else + { + *dry_run_pc = current_pc + instruction_size; + } + + return ERROR_OK; + } + else + { + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.data_proc.Rd).value, 0, 32, Rd); + WARNING("no updating of flags yet"); + + if (instruction.info.data_proc.Rd == 15) + return ERROR_OK; + } + } + /* compare instructions (CMP, CMN, TST, TEQ) */ + else if ((instruction.type >= ARM_TST) && (instruction.type <= ARM_CMN)) + { + if (dry_run_pc) + { + *dry_run_pc = current_pc + instruction_size; + return ERROR_OK; + } + else + { + WARNING("no updating of flags yet"); + } + } + /* load register instructions */ + else if ((instruction.type >= ARM_LDR) && (instruction.type <= ARM_LDRSH)) + { + u32 load_address, modified_address, load_value; + u32 Rn = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store.Rn).value, 0, 32); + + /* adjust Rn in case the PC is being read */ + if (instruction.info.load_store.Rn == 15) + Rn += 2 * instruction_size; + + if (instruction.info.load_store.offset_mode == 0) + { + if (instruction.info.load_store.U) + modified_address = Rn + instruction.info.load_store.offset.offset; + else + modified_address = Rn - instruction.info.load_store.offset.offset; + } + else if (instruction.info.load_store.offset_mode == 1) + { + u32 offset; + u32 Rm = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store.offset.reg.Rm).value, 0, 32); + u8 shift = instruction.info.load_store.offset.reg.shift; + u8 shift_imm = instruction.info.load_store.offset.reg.shift_imm; + u8 carry = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 29, 1); + + offset = arm_shift(shift, Rm, shift_imm, &carry); + + if (instruction.info.load_store.U) + modified_address = Rn + offset; + else + modified_address = Rn - offset; + } + else + { + ERROR("BUG: offset_mode neither 0 (offset) nor 1 (scaled register)"); + } + + if (instruction.info.load_store.index_mode == 0) + { + /* offset mode + * we load from the modified address, but don't change the base address register */ + load_address = modified_address; + modified_address = Rn; + } + else if (instruction.info.load_store.index_mode == 1) + { + /* pre-indexed mode + * we load from the modified address, and write it back to the base address register */ + load_address = modified_address; + } + else if (instruction.info.load_store.index_mode == 2) + { + /* post-indexed mode + * we load from the unmodified address, and write the modified address back */ + load_address = Rn; + } + + target_read_u32(target, load_address, &load_value); + + if (dry_run_pc) + { + if (instruction.info.load_store.Rd == 15) + { + *dry_run_pc = load_value; + return ERROR_OK; + } + else + { + *dry_run_pc = current_pc + instruction_size; + } + + return ERROR_OK; + } + else + { + if ((instruction.info.load_store.index_mode == 1) || + (instruction.info.load_store.index_mode == 2)) + { + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store.Rn).value, 0, 32, modified_address); + } + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store.Rd).value, 0, 32, load_value); + + if (instruction.info.load_store.Rd == 15) + return ERROR_OK; + } + } + /* load multiple instruction */ + else if (instruction.type == ARM_LDM) + { + int i; + u32 Rn = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store_multiple.Rn).value, 0, 32); + u32 load_values[16]; + int bits_set = 0; + + for (i = 0; i < 16; i++) + { + if (instruction.info.load_store_multiple.register_list & (1 << i)) + bits_set++; + } + + switch (instruction.info.load_store_multiple.addressing_mode) + { + case 0: /* Increment after */ + Rn = Rn; + break; + case 1: /* Increment before */ + Rn = Rn + 4; + break; + case 2: /* Decrement after */ + Rn = Rn - (bits_set * 4) + 4; + break; + case 3: /* Decrement before */ + Rn = Rn - (bits_set * 4); + break; + } + + for (i = 0; i < 16; i++) + { + if (instruction.info.load_store_multiple.register_list & (1 << i)) + { + target_read_u32(target, Rn, &load_values[i]); + Rn += 4; + } + } + + if (dry_run_pc) + { + if (instruction.info.load_store_multiple.register_list & 0x8000) + { + *dry_run_pc = load_values[15]; + return ERROR_OK; + } + } + else + { + enum armv4_5_mode mode = armv4_5->core_mode; + int update_cpsr = 0; + + if (instruction.info.load_store_multiple.S) + { + if (instruction.info.load_store_multiple.register_list & 0x8000) + update_cpsr = 1; + else + mode = ARMV4_5_MODE_USR; + } + + for (i = 0; i < 16; i++) + { + if (instruction.info.load_store_multiple.register_list & (1 << i)) + { + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, i).value, 0, 32, load_values[i]); + } + } + + if (update_cpsr) + { + u32 spsr = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).value, 0, 32); + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, spsr); + } + + /* base register writeback */ + if (instruction.info.load_store_multiple.W) + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store_multiple.Rn).value, 0, 32, Rn); + + if (instruction.info.load_store_multiple.register_list & 0x8000) + return ERROR_OK; + } + } + /* store multiple instruction */ + else if (instruction.type == ARM_STM) + { + int i; + + if (dry_run_pc) + { + /* STM wont affect PC (advance by instruction size */ + } + else + { + u32 Rn = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store_multiple.Rn).value, 0, 32); + int bits_set = 0; + enum armv4_5_mode mode = armv4_5->core_mode; + + for (i = 0; i < 16; i++) + { + if (instruction.info.load_store_multiple.register_list & (1 << i)) + bits_set++; + } + + if (instruction.info.load_store_multiple.S) + { + mode = ARMV4_5_MODE_USR; + } + + switch (instruction.info.load_store_multiple.addressing_mode) + { + case 0: /* Increment after */ + Rn = Rn; + break; + case 1: /* Increment before */ + Rn = Rn + 4; + break; + case 2: /* Decrement after */ + Rn = Rn - (bits_set * 4) + 4; + break; + case 3: /* Decrement before */ + Rn = Rn - (bits_set * 4); + break; + } + + for (i = 0; i < 16; i++) + { + if (instruction.info.load_store_multiple.register_list & (1 << i)) + { + target_write_u32(target, Rn, buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).value, 0, 32)); + Rn += 4; + } + } + + /* base register writeback */ + if (instruction.info.load_store_multiple.W) + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store_multiple.Rn).value, 0, 32, Rn); + + } + } + else if (!dry_run_pc) + { + /* the instruction wasn't handled, but we're supposed to simulate it + */ + return ERROR_ARM_SIMULATOR_NOT_IMPLEMENTED; + } + + if (dry_run_pc) + { + *dry_run_pc = current_pc + instruction_size; + return ERROR_OK; + } + else + { + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, current_pc + instruction_size); + return ERROR_OK; + } + +} diff --git a/src/target/arm_simulator.h b/src/target/arm_simulator.h new file mode 100644 index 00000000..9c28a086 --- /dev/null +++ b/src/target/arm_simulator.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2006 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef ARM_SIMULATOR_H +#define ARM_SIMULATOR_H + +#include "target.h" +#include "types.h" + +extern int arm_simulate_step(target_t *target, u32 *dry_run_pc); + + +#define ERROR_ARM_SIMULATOR_NOT_IMPLEMENTED (-1000) + +#endif /* ARM_SIMULATOR_H */ diff --git a/src/target/armv4_5.c b/src/target/armv4_5.c index 3eab0335..dc42d1c0 100644 --- a/src/target/armv4_5.c +++ b/src/target/armv4_5.c @@ -442,7 +442,7 @@ int handle_armv4_5_disassemble_command(struct command_context_s *cmd_ctx, char * for (i = 0; i < count; i++) { target_read_u32(target, address, &opcode); - evaluate_opcode(opcode, address, &cur_instruction); + arm_evaluate_opcode(opcode, address, &cur_instruction); command_print(cmd_ctx, "%s", cur_instruction.text); address += (thumb) ? 2 : 4; } @@ -598,6 +598,13 @@ int armv4_5_run_algorithm(struct target_s *target, int num_mem_params, mem_param } } + if ((retval != ERROR_TARGET_TIMEOUT) && + (buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32) != exit_point)) + { + WARNING("target reentered debug state, but not at the desired exit point: 0x%4.4x", + buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + } + breakpoint_remove(target, exit_point); for (i = 0; i < num_mem_params; i++) diff --git a/src/target/armv4_5.h b/src/target/armv4_5.h index 47996de4..0ba94ff7 100644 --- a/src/target/armv4_5.h +++ b/src/target/armv4_5.h @@ -213,6 +213,11 @@ extern int armv4_5_invalidate_core_regs(target_t *target); */ #define ARMV4_5_MCR(CP, op1, Rd, CRn, CRm, op2) (0xee000010 | (CRm) | ((op2) << 5) | ((CP) << 8) | ((Rd) << 12) | ((CRn) << 16) | ((op1) << 21)) +/* Breakpoint instruction (ARMv5) + * Im: 16-bit immediate + */ +#define ARMV5_BKPT(Im) (0xe1200070 | ((Im & 0xfff0) << 8) | (Im & 0xf)) + /* Thumb mode instructions */ @@ -266,4 +271,9 @@ extern int armv4_5_invalidate_core_regs(target_t *target); */ #define ARMV4_5_T_B(Imm) ((0xe000 | (Imm)) | ((0xe000 | (Imm)) << 16)) +/* Breakpoint instruction (ARMv5) (Thumb state) + * Im: 8-bit immediate + */ +#define ARMV5_T_BKPT(Im) ((0xbe00 | Im) | ((0xbe00 | Im) << 16)) + #endif /* ARMV4_5_H */ diff --git a/src/target/target.c b/src/target/target.c index 376fc52f..548ea0c3 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -79,6 +79,7 @@ extern target_type_t arm9tdmi_target; extern target_type_t arm920t_target; extern target_type_t arm966e_target; extern target_type_t arm926ejs_target; +extern target_type_t xscale_target; target_type_t *target_types[] = { @@ -88,6 +89,7 @@ target_type_t *target_types[] = &arm720t_target, &arm966e_target, &arm926ejs_target, + &xscale_target, NULL, }; @@ -727,60 +729,107 @@ int target_read_buffer(struct target_s *target, u32 address, u32 size, u8 *buffe return ERROR_OK; } -void target_read_u32(struct target_s *target, u32 address, u32 *value) +int target_read_u32(struct target_s *target, u32 address, u32 *value) { u8 value_buf[4]; + + int retval = target->type->read_memory(target, address, 4, 1, value_buf); - target->type->read_memory(target, address, 4, 1, value_buf); + if (retval == ERROR_OK) + { + *value = target_buffer_get_u32(target, value_buf); + DEBUG("address: 0x%8.8x, value: 0x%8.8x", address, *value); + } + else + { + *value = 0x0; + DEBUG("address: 0x%8.8x failed", address); + } - *value = target_buffer_get_u32(target, value_buf); - - DEBUG("address: 0x%8.8x, value: 0x%8.8x", address, *value); + return retval; } -void target_read_u16(struct target_s *target, u32 address, u16 *value) +int target_read_u16(struct target_s *target, u32 address, u16 *value) { u8 value_buf[2]; - target->type->read_memory(target, address, 2, 1, value_buf); + int retval = target->type->read_memory(target, address, 2, 1, value_buf); - *value = target_buffer_get_u16(target, value_buf); - - DEBUG("address: 0x%8.8x, value: 0x%4.4x", address, *value); + if (retval == ERROR_OK) + { + *value = target_buffer_get_u16(target, value_buf); + DEBUG("address: 0x%8.8x, value: 0x%4.4x", address, *value); + } + else + { + *value = 0x0; + DEBUG("address: 0x%8.8x failed", address); + } + + return retval; } -void target_read_u8(struct target_s *target, u32 address, u8 *value) +int target_read_u8(struct target_s *target, u32 address, u8 *value) { - target->type->read_memory(target, address, 1, 1, value); + int retval = target->type->read_memory(target, address, 1, 1, value); - DEBUG("address: 0x%8.8x, value: 0x%2.2x", address, *value); + if (retval == ERROR_OK) + { + DEBUG("address: 0x%8.8x, value: 0x%2.2x", address, *value); + } + else + { + *value = 0x0; + DEBUG("address: 0x%8.8x failed", address); + } + + return retval; } -void target_write_u32(struct target_s *target, u32 address, u32 value) +int target_write_u32(struct target_s *target, u32 address, u32 value) { + int retval; u8 value_buf[4]; DEBUG("address: 0x%8.8x, value: 0x%8.8x", address, value); target_buffer_set_u32(target, value_buf, value); - target->type->write_memory(target, address, 4, 1, value_buf); + if ((retval = target->type->write_memory(target, address, 4, 1, value_buf)) != ERROR_OK) + { + DEBUG("failed: %i", retval); + } + + return retval; } -void target_write_u16(struct target_s *target, u32 address, u16 value) +int target_write_u16(struct target_s *target, u32 address, u16 value) { + int retval; u8 value_buf[2]; DEBUG("address: 0x%8.8x, value: 0x%8.8x", address, value); target_buffer_set_u16(target, value_buf, value); - target->type->write_memory(target, address, 2, 1, value_buf); + if ((retval = target->type->write_memory(target, address, 2, 1, value_buf)) != ERROR_OK) + { + DEBUG("failed: %i", retval); + } + + return retval; } -void target_write_u8(struct target_s *target, u32 address, u8 value) +int target_write_u8(struct target_s *target, u32 address, u8 value) { + int retval; + DEBUG("address: 0x%8.8x, value: 0x%2.2x", address, value); - target->type->read_memory(target, address, 1, 1, &value); + if ((retval = target->type->read_memory(target, address, 1, 1, &value)) != ERROR_OK) + { + DEBUG("failed: %i", retval); + } + + return retval; } int target_register_user_commands(struct command_context_s *cmd_ctx) diff --git a/src/target/target.h b/src/target/target.h index 04157fde..d340a778 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -50,6 +50,8 @@ enum daemon_startup_mode DAEMON_RESET, /* reset target (behaviour defined by reset_mode */ }; +extern enum daemon_startup_mode startup_mode; + enum target_reset_mode { RESET_RUN = 0, /* reset and let target run */ @@ -222,12 +224,12 @@ extern u16 target_buffer_get_u16(target_t *target, u8 *buffer); extern void target_buffer_set_u32(target_t *target, u8 *buffer, u32 value); extern void target_buffer_set_u16(target_t *target, u8 *buffer, u16 value); -void target_read_u32(struct target_s *target, u32 address, u32 *value); -void target_read_u16(struct target_s *target, u32 address, u16 *value); -void target_read_u8(struct target_s *target, u32 address, u8 *value); -void target_write_u32(struct target_s *target, u32 address, u32 value); -void target_write_u16(struct target_s *target, u32 address, u16 value); -void target_write_u8(struct target_s *target, u32 address, u8 value); +int target_read_u32(struct target_s *target, u32 address, u32 *value); +int target_read_u16(struct target_s *target, u32 address, u16 *value); +int target_read_u8(struct target_s *target, u32 address, u8 *value); +int target_write_u32(struct target_s *target, u32 address, u32 value); +int target_write_u16(struct target_s *target, u32 address, u16 value); +int target_write_u8(struct target_s *target, u32 address, u8 value); #define ERROR_TARGET_INVALID (-300) #define ERROR_TARGET_INIT_FAILED (-301) diff --git a/src/target/xscale.c b/src/target/xscale.c new file mode 100644 index 00000000..d6c9f215 --- /dev/null +++ b/src/target/xscale.c @@ -0,0 +1,3175 @@ +/*************************************************************************** + * Copyright (C) 2006 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "config.h" + +#include "xscale.h" + +#include "register.h" +#include "target.h" +#include "armv4_5.h" +#include "arm_simulator.h" +#include "log.h" +#include "jtag.h" +#include "binarybuffer.h" +#include "time_support.h" +#include "breakpoints.h" + +#include <stdlib.h> +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +/* cli handling */ +int xscale_register_commands(struct command_context_s *cmd_ctx); + +/* forward declarations */ +int xscale_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target); +int xscale_init_target(struct command_context_s *cmd_ctx, struct target_s *target); +int xscale_quit(); + +int xscale_arch_state(struct target_s *target, char *buf, int buf_size); +enum target_state xscale_poll(target_t *target); +int xscale_halt(target_t *target); +int xscale_resume(struct target_s *target, int current, u32 address, int handle_breakpoints, int debug_execution); +int xscale_step(struct target_s *target, int current, u32 address, int handle_breakpoints); +int xscale_debug_entry(target_t *target); +int xscale_restore_context(target_t *target); + +int xscale_assert_reset(target_t *target); +int xscale_deassert_reset(target_t *target); +int xscale_soft_reset_halt(struct target_s *target); + +int xscale_set_reg_u32(reg_t *reg, u32 value); + +int xscale_read_core_reg(struct target_s *target, int num, enum armv4_5_mode mode); +int xscale_write_core_reg(struct target_s *target, int num, enum armv4_5_mode mode, u32 value); + +int xscale_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int xscale_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer); +int xscale_bulk_write_memory(target_t *target, u32 address, u32 count, u8 *buffer); + +int xscale_add_breakpoint(struct target_s *target, breakpoint_t *breakpoint); +int xscale_remove_breakpoint(struct target_s *target, breakpoint_t *breakpoint); +int xscale_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint); +int xscale_unset_breakpoint(struct target_s *target, breakpoint_t *breakpoint); +int xscale_add_watchpoint(struct target_s *target, watchpoint_t *watchpoint); +int xscale_remove_watchpoint(struct target_s *target, watchpoint_t *watchpoint); +void xscale_enable_watchpoints(struct target_s *target); +void xscale_enable_breakpoints(struct target_s *target); + +target_type_t xscale_target = +{ + .name = "xscale", + + .poll = xscale_poll, + .arch_state = xscale_arch_state, + + .halt = xscale_halt, + .resume = xscale_resume, + .step = xscale_step, + + .assert_reset = xscale_assert_reset, + .deassert_reset = xscale_deassert_reset, + .soft_reset_halt = xscale_soft_reset_halt, + + .get_gdb_reg_list = armv4_5_get_gdb_reg_list, + + .read_memory = xscale_read_memory, + .write_memory = xscale_write_memory, + .bulk_write_memory = xscale_bulk_write_memory, + + .run_algorithm = armv4_5_run_algorithm, + + .add_breakpoint = xscale_add_breakpoint, + .remove_breakpoint = xscale_remove_breakpoint, + .add_watchpoint = xscale_add_watchpoint, + .remove_watchpoint = xscale_remove_watchpoint, + + .register_commands = xscale_register_commands, + .target_command = xscale_target_command, + .init_target = xscale_init_target, + .quit = xscale_quit +}; + +char* xscale_reg_list[] = +{ + "XSCALE_MAINID", /* 0 */ + "XSCALE_CACHETYPE", + "XSCALE_CTRL", + "XSCALE_AUXCTRL", + "XSCALE_TTB", + "XSCALE_DAC", + "XSCALE_FSR", + "XSCALE_FAR", + "XSCALE_PID", + "XSCALE_CPACCESS", + "XSCALE_IBCR0", /* 10 */ + "XSCALE_IBCR1", + "XSCALE_DBR0", + "XSCALE_DBR1", + "XSCALE_DBCON", + "XSCALE_TBREG", + "XSCALE_CHKPT0", + "XSCALE_CHKPT1", + "XSCALE_DCSR", + "XSCALE_TX", + "XSCALE_RX", /* 20 */ + "XSCALE_TXRXCTRL", +}; + +xscale_reg_t xscale_reg_arch_info[] = +{ + {XSCALE_MAINID, NULL}, + {XSCALE_CACHETYPE, NULL}, + {XSCALE_CTRL, NULL}, + {XSCALE_AUXCTRL, NULL}, + {XSCALE_TTB, NULL}, + {XSCALE_DAC, NULL}, + {XSCALE_FSR, NULL}, + {XSCALE_FAR, NULL}, + {XSCALE_PID, NULL}, + {XSCALE_CPACCESS, NULL}, + {XSCALE_IBCR0, NULL}, + {XSCALE_IBCR1, NULL}, + {XSCALE_DBR0, NULL}, + {XSCALE_DBR1, NULL}, + {XSCALE_DBCON, NULL}, + {XSCALE_TBREG, NULL}, + {XSCALE_CHKPT0, NULL}, + {XSCALE_CHKPT1, NULL}, + {XSCALE_DCSR, NULL}, /* DCSR accessed via JTAG or SW */ + {-1, NULL}, /* TX accessed via JTAG */ + {-1, NULL}, /* RX accessed via JTAG */ + {-1, NULL}, /* TXRXCTRL implicit access via JTAG */ +}; + +int xscale_reg_arch_type = -1; + +int xscale_get_reg(reg_t *reg); +int xscale_set_reg(reg_t *reg, u8 *buf); + +int xscale_get_arch_pointers(target_t *target, armv4_5_common_t **armv4_5_p, xscale_common_t **xscale_p) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + return -1; + } + + if (xscale->common_magic != XSCALE_COMMON_MAGIC) + { + return -1; + } + + *armv4_5_p = armv4_5; + *xscale_p = xscale; + + return ERROR_OK; +} + +int xscale_jtag_set_instr(int chain_pos, u32 new_instr) +{ + jtag_device_t *device = jtag_get_device(chain_pos); + + if (buf_get_u32(device->cur_instr, 0, device->ir_length) != new_instr) + { + scan_field_t field; + + field.device = chain_pos; + field.num_bits = device->ir_length; + field.out_value = calloc(CEIL(field.num_bits, 8), 1); + buf_set_u32(field.out_value, 0, field.num_bits, new_instr); + field.out_mask = NULL; + field.in_value = NULL; + field.in_check_value = device->expected; + field.in_check_mask = device->expected_mask; + field.in_handler = NULL; + field.in_handler_priv = NULL; + + jtag_add_ir_scan(1, &field, -1); + + free(field.out_value); + } + + return ERROR_OK; +} + +int xscale_jtag_callback(enum jtag_event event, void *priv) +{ + switch (event) + { + case JTAG_TRST_ASSERTED: + break; + case JTAG_TRST_RELEASED: + break; + case JTAG_SRST_ASSERTED: + break; + case JTAG_SRST_RELEASED: + break; + default: + WARNING("unhandled JTAG event"); + } + + return ERROR_OK; +} + +int xscale_read_dcsr(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + int retval; + + scan_field_t fields[3]; + u8 field0 = 0x0; + u8 field0_check_value = 0x2; + u8 field0_check_mask = 0x7; + u8 field2 = 0x0; + u8 field2_check_value = 0x0; + u8 field2_check_mask = 0x1; + + jtag_add_end_state(TAP_PD); + xscale_jtag_set_instr(xscale->jtag_info.chain_pos, xscale->jtag_info.dcsr); + + buf_set_u32(&field0, 1, 1, xscale->hold_rst); + buf_set_u32(&field0, 2, 1, xscale->external_debug_break); + + fields[0].device = xscale->jtag_info.chain_pos; + fields[0].num_bits = 3; + fields[0].out_value = &field0; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = &field0_check_value; + fields[0].in_check_mask = &field0_check_mask; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = xscale->jtag_info.chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = xscale->reg_cache->reg_list[XSCALE_DCSR].value; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + fields[2].device = xscale->jtag_info.chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = &field2; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = &field2_check_value; + fields[2].in_check_mask = &field2_check_mask; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while reading DCSR"); + exit(-1); + } + + xscale->reg_cache->reg_list[XSCALE_DCSR].dirty = 0; + xscale->reg_cache->reg_list[XSCALE_DCSR].valid = 1; + + /* write the register with the value we just read + * on this second pass, only the first bit of field0 is guaranteed to be 0) + */ + field0_check_mask = 0x1; + fields[1].out_value = xscale->reg_cache->reg_list[XSCALE_DCSR].value; + fields[1].in_value = NULL; + + jtag_add_end_state(TAP_RTI); + + jtag_add_dr_scan(3, fields, -1); + + return ERROR_OK; +} + +int xscale_receive(target_t *target, u32 *buffer, int num_words) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + enum tap_state path[3]; + scan_field_t fields[3]; + + u8 *field0 = malloc(num_words * 1); + u8 field0_check_value = 0x2; + u8 field0_check_mask = 0x6; + u32 *field1 = malloc(num_words * 4); + u8 field2_check_value = 0x0; + u8 field2_check_mask = 0x1; + int words_done = 0; + int words_scheduled = 0; + + int i; + int retval; + + path[0] = TAP_SDS; + path[1] = TAP_CD; + path[2] = TAP_SD; + + fields[0].device = xscale->jtag_info.chain_pos; + fields[0].num_bits = 3; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + /* fields[0].in_value = field0; */ + fields[0].in_check_value = &field0_check_value; + fields[0].in_check_mask = &field0_check_mask; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = xscale->jtag_info.chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + fields[2].device = xscale->jtag_info.chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = NULL; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = &field2_check_value; + fields[2].in_check_mask = &field2_check_mask; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_end_state(TAP_RTI); + xscale_jtag_set_instr(xscale->jtag_info.chain_pos, xscale->jtag_info.dbgtx); + jtag_add_runtest(1, -1); + + /* repeat until all words have been collected */ + while (words_done < num_words) + { + /* schedule reads */ + words_scheduled = 0; + for (i = words_done; i < num_words; i++) + { + fields[0].in_value = &field0[i]; + fields[1].in_handler = buf_to_u32_handler; + fields[1].in_handler_priv = (u8*)&field1[i]; + + jtag_add_pathmove(3, path); + jtag_add_dr_scan(3, fields, TAP_RTI); + words_scheduled++; + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while receiving data from debug handler"); + exit(-1); + } + + /* examine results */ + for (i = words_done; i < num_words; i++) + { + if (!(field0[0] & 1)) + { + /* move backwards if necessary */ + int j; + for (j = i; j < num_words - 1; j++) + { + field0[j] = field0[j+1]; + field1[j] = field1[j+1]; + } + words_scheduled--; + } + } + words_done += words_scheduled; + } + + for (i = 0; i < num_words; i++) + *(buffer++) = buf_get_u32((u8*)&field1[i], 0, 32); + + free(field1); + + return ERROR_OK; +} + +int xscale_read_tx(target_t *target, int consume) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + enum tap_state path[3]; + + int retval; + struct timeval timeout, now; + + scan_field_t fields[3]; + u8 field0_in = 0x0; + u8 field0_check_value = 0x2; + u8 field0_check_mask = 0x6; + u8 field2_check_value = 0x0; + u8 field2_check_mask = 0x1; + + jtag_add_end_state(TAP_RTI); + + xscale_jtag_set_instr(xscale->jtag_info.chain_pos, xscale->jtag_info.dbgtx); + + path[0] = TAP_SDS; + path[1] = TAP_CD; + path[2] = TAP_SD; + + fields[0].device = xscale->jtag_info.chain_pos; + fields[0].num_bits = 3; + fields[0].out_value = NULL; + fields[0].out_mask = NULL; + fields[0].in_value = &field0_in; + fields[0].in_check_value = &field0_check_value; + fields[0].in_check_mask = &field0_check_mask; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = xscale->jtag_info.chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = NULL; + fields[1].out_mask = NULL; + fields[1].in_value = xscale->reg_cache->reg_list[XSCALE_TX].value; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + fields[2].device = xscale->jtag_info.chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = NULL; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = &field2_check_value; + fields[2].in_check_mask = &field2_check_mask; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + gettimeofday(&timeout, NULL); + timeval_add_time(&timeout, 5, 0); + + do + { + /* if we want to consume the register content (i.e. clear TX_READY), + * we have to go straight from Capture-DR to Shift-DR + * otherwise, we go from Capture-DR to Exit1-DR to Pause-DR + */ + if (consume) + jtag_add_pathmove(3, path); + else + jtag_add_statemove(TAP_PD); + + jtag_add_dr_scan(3, fields, TAP_RTI); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while reading TX"); + exit(-1); + } + + gettimeofday(&now, NULL); + if ((now.tv_sec > timeout.tv_sec) && (now.tv_usec > timeout.tv_usec)) + { + ERROR("time out reading TX register"); + return ERROR_TARGET_TIMEOUT; + } + } while ((!(field0_in & 1)) && consume); + + if (!(field0_in & 1)) + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + + return ERROR_OK; +} + +int xscale_write_rx(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + int retval; + struct timeval timeout, now; + + scan_field_t fields[3]; + u8 field0_out = 0x0; + u8 field0_in = 0x0; + u8 field0_check_value = 0x2; + u8 field0_check_mask = 0x6; + u8 field2 = 0x0; + u8 field2_check_value = 0x0; + u8 field2_check_mask = 0x1; + + jtag_add_end_state(TAP_RTI); + + xscale_jtag_set_instr(xscale->jtag_info.chain_pos, xscale->jtag_info.dbgrx); + + fields[0].device = xscale->jtag_info.chain_pos; + fields[0].num_bits = 3; + fields[0].out_value = &field0_out; + fields[0].out_mask = NULL; + fields[0].in_value = &field0_in; + fields[0].in_check_value = &field0_check_value; + fields[0].in_check_mask = &field0_check_mask; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = xscale->jtag_info.chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = xscale->reg_cache->reg_list[XSCALE_RX].value; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + fields[2].device = xscale->jtag_info.chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = &field2; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = &field2_check_value; + fields[2].in_check_mask = &field2_check_mask; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + gettimeofday(&timeout, NULL); + timeval_add_time(&timeout, 5, 0); + + /* poll until rx_read is low */ + do + { + DEBUG("polling RX"); + jtag_add_dr_scan(3, fields, TAP_RTI); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while writing RX"); + exit(-1); + } + + gettimeofday(&now, NULL); + if ((now.tv_sec > timeout.tv_sec) && (now.tv_usec > timeout.tv_usec)) + { + ERROR("time out writing RX register"); + return ERROR_TARGET_TIMEOUT; + } + } while (field0_in & 1); + + /* set rx_valid */ + field2 = 0x1; + jtag_add_dr_scan(3, fields, TAP_RTI); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while writing RX"); + exit(-1); + } + + return ERROR_OK; +} + +/* send count elements of size byte to the debug handler */ +int xscale_send(target_t *target, u8 *buffer, int count, int size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + int retval; + + int done_count = 0; + u8 output[4] = {0, 0, 0, 0}; + + scan_field_t fields[3]; + u8 field0_out = 0x0; + u8 field0_in = 0x0; + u8 field0_check_value = 0x2; + u8 field0_check_mask = 0x6; + u8 field2 = 0x1; + u8 field2_check_value = 0x0; + u8 field2_check_mask = 0x1; + + jtag_add_end_state(TAP_RTI); + + xscale_jtag_set_instr(xscale->jtag_info.chain_pos, xscale->jtag_info.dbgrx); + + fields[0].device = xscale->jtag_info.chain_pos; + fields[0].num_bits = 3; + fields[0].out_value = &field0_out; + fields[0].out_mask = NULL; + fields[0].in_value = &field0_in; + fields[0].in_check_value = &field0_check_value; + fields[0].in_check_mask = &field0_check_mask; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = xscale->jtag_info.chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = output; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + fields[2].device = xscale->jtag_info.chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = &field2; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = &field2_check_value; + fields[2].in_check_mask = &field2_check_mask; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + while (done_count++ < count) + { + /* extract sized element from target-endian buffer, and put it + * into little-endian output buffer + */ + switch (size) + { + case 4: + buf_set_u32(output, 0, 32, target_buffer_get_u32(target, buffer)); + break; + case 2: + buf_set_u32(output, 0, 32, target_buffer_get_u16(target, buffer)); + break; + case 1: + output[0] = *buffer; + break; + default: + ERROR("BUG: size neither 4, 2 nor 1"); + exit(-1); + } + + jtag_add_dr_scan(3, fields, TAP_RTI); + buffer += size; + } + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while sending data to debug handler"); + exit(-1); + } + + return ERROR_OK; +} + +int xscale_send_u32(target_t *target, u32 value) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + buf_set_u32(xscale->reg_cache->reg_list[XSCALE_RX].value, 0, 32, value); + return xscale_write_rx(target); +} + +int xscale_write_dcsr(target_t *target, int hold_rst, int ext_dbg_brk) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + int retval; + + scan_field_t fields[3]; + u8 field0 = 0x0; + u8 field0_check_value = 0x2; + u8 field0_check_mask = 0x7; + u8 field2 = 0x0; + u8 field2_check_value = 0x0; + u8 field2_check_mask = 0x1; + + if (hold_rst != -1) + xscale->hold_rst = hold_rst; + + if (ext_dbg_brk != -1) + xscale->external_debug_break = ext_dbg_brk; + + jtag_add_end_state(TAP_RTI); + xscale_jtag_set_instr(xscale->jtag_info.chain_pos, xscale->jtag_info.dcsr); + + buf_set_u32(&field0, 1, 1, xscale->hold_rst); + buf_set_u32(&field0, 2, 1, xscale->external_debug_break); + + fields[0].device = xscale->jtag_info.chain_pos; + fields[0].num_bits = 3; + fields[0].out_value = &field0; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = &field0_check_value; + fields[0].in_check_mask = &field0_check_mask; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = xscale->jtag_info.chain_pos; + fields[1].num_bits = 32; + fields[1].out_value = xscale->reg_cache->reg_list[XSCALE_DCSR].value; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + + fields[2].device = xscale->jtag_info.chain_pos; + fields[2].num_bits = 1; + fields[2].out_value = &field2; + fields[2].out_mask = NULL; + fields[2].in_value = NULL; + fields[2].in_check_value = &field2_check_value; + fields[2].in_check_mask = &field2_check_mask; + fields[2].in_handler = NULL; + fields[2].in_handler_priv = NULL; + + jtag_add_dr_scan(3, fields, -1); + + if ((retval = jtag_execute_queue()) != ERROR_OK) + { + ERROR("JTAG error while writing DCSR"); + exit(-1); + } + + xscale->reg_cache->reg_list[XSCALE_DCSR].dirty = 0; + xscale->reg_cache->reg_list[XSCALE_DCSR].valid = 1; + + return ERROR_OK; +} + +/* parity of the number of bits 0 if even; 1 if odd. for 32 bit words */ +unsigned int parity (unsigned int v) +{ + unsigned int ov = v; + v ^= v >> 16; + v ^= v >> 8; + v ^= v >> 4; + v &= 0xf; + DEBUG("parity of 0x%x is %i", ov, (0x6996 >> v) & 1); + return (0x6996 >> v) & 1; +} + +int xscale_load_ic(target_t *target, int mini, u32 va, u32 buffer[8]) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + u8 packet[4]; + u8 cmd; + int word; + + scan_field_t fields[2]; + + DEBUG("loading miniIC at 0x%8.8x", va); + + jtag_add_end_state(TAP_RTI); + xscale_jtag_set_instr(xscale->jtag_info.chain_pos, xscale->jtag_info.ldic); /* LDIC */ + + /* CMD is b010 for Main IC and b011 for Mini IC */ + if (mini) + buf_set_u32(&cmd, 0, 3, 0x3); + else + buf_set_u32(&cmd, 0, 3, 0x2); + + buf_set_u32(&cmd, 3, 3, 0x0); + + /* virtual address of desired cache line */ + buf_set_u32(packet, 0, 27, va >> 5); + + fields[0].device = xscale->jtag_info.chain_pos; + fields[0].num_bits = 6; + fields[0].out_value = &cmd; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = xscale->jtag_info.chain_pos; + fields[1].num_bits = 27; + fields[1].out_value = packet; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + jtag_add_dr_scan(2, fields, -1); + + fields[0].num_bits = 32; + fields[0].out_value = packet; + + fields[1].num_bits = 1; + fields[1].out_value = &cmd; + + for (word = 0; word < 8; word++) + { + buf_set_u32(packet, 0, 32, buffer[word]); + cmd = parity(*((u32*)packet)); + jtag_add_dr_scan(2, fields, -1); + } + + jtag_execute_queue(); + + return ERROR_OK; +} + +int xscale_invalidate_ic_line(target_t *target, u32 va) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + u8 packet[4]; + u8 cmd; + + scan_field_t fields[2]; + + jtag_add_end_state(TAP_RTI); + xscale_jtag_set_instr(xscale->jtag_info.chain_pos, xscale->jtag_info.ldic); /* LDIC */ + + /* CMD for invalidate IC line b000, bits [6:4] b000 */ + buf_set_u32(&cmd, 0, 6, 0x0); + + /* virtual address of desired cache line */ + buf_set_u32(packet, 0, 27, va >> 5); + + fields[0].device = xscale->jtag_info.chain_pos; + fields[0].num_bits = 6; + fields[0].out_value = &cmd; + fields[0].out_mask = NULL; + fields[0].in_value = NULL; + fields[0].in_check_value = NULL; + fields[0].in_check_mask = NULL; + fields[0].in_handler = NULL; + fields[0].in_handler_priv = NULL; + + fields[1].device = xscale->jtag_info.chain_pos; + fields[1].num_bits = 27; + fields[1].out_value = packet; + fields[1].out_mask = NULL; + fields[1].in_value = NULL; + fields[1].in_check_value = NULL; + fields[1].in_check_mask = NULL; + fields[1].in_handler = NULL; + fields[1].in_handler_priv = NULL; + + jtag_add_dr_scan(2, fields, -1); + + return ERROR_OK; +} + +int xscale_update_vectors(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + int i; + + u32 low_reset_branch, high_reset_branch; + + for (i = 1; i < 8; i++) + { + /* if there's a static vector specified for this exception, override */ + if (xscale->static_high_vectors_set & (1 << i)) + { + xscale->high_vectors[i] = xscale->static_high_vectors[i]; + } + else + { + if (target_read_u32(target, 0xffff0000 + 4*i, &xscale->high_vectors[i]) != ERROR_OK) + { + xscale->high_vectors[i] = ARMV4_5_B(0xfffffe, 0); + } + } + } + + for (i = 1; i < 8; i++) + { + if (xscale->static_low_vectors_set & (1 << i)) + { + xscale->low_vectors[i] = xscale->static_low_vectors[i]; + } + else + { + if (target_read_u32(target, 0x0 + 4*i, &xscale->low_vectors[i]) != ERROR_OK) + { + xscale->low_vectors[i] = ARMV4_5_B(0xfffffe, 0); + } + } + } + + /* calculate branches to debug handler */ + low_reset_branch = (xscale->handler_address + 0x20 - 0x0 - 0x8) >> 2; + high_reset_branch = (xscale->handler_address + 0x20 - 0xffff0000 - 0x8) >> 2; + + xscale->low_vectors[0] = ARMV4_5_B((low_reset_branch & 0xffffff), 0); + xscale->high_vectors[0] = ARMV4_5_B((high_reset_branch & 0xffffff), 0); + + /* invalidate and load exception vectors in mini i-cache */ + xscale_invalidate_ic_line(target, 0x0); + xscale_invalidate_ic_line(target, 0xffff0000); + + xscale_load_ic(target, 1, 0x0, xscale->low_vectors); + xscale_load_ic(target, 1, 0xffff0000, xscale->high_vectors); + + return ERROR_OK; +} + +int xscale_arch_state(struct target_s *target, char *buf, int buf_size) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + char *state[] = + { + "disabled", "enabled" + }; + + char *arch_dbg_reason[] = + { + "", "\n(processor reset)", "\n(trace buffer full)" + }; + + if (armv4_5->common_magic != ARMV4_5_COMMON_MAGIC) + { + ERROR("BUG: called for a non-ARMv4/5 target"); + exit(-1); + } + + snprintf(buf, buf_size, + "target halted in %s state due to %s, current mode: %s\n" + "cpsr: 0x%8.8x pc: 0x%8.8x\n" + "MMU: %s, D-Cache: %s, I-Cache: %s" + "%s", + armv4_5_state_strings[armv4_5->core_state], + target_debug_reason_strings[target->debug_reason], + armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)], + buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), + buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32), + state[xscale->armv4_5_mmu.mmu_enabled], + state[xscale->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled], + state[xscale->armv4_5_mmu.armv4_5_cache.i_cache_enabled], + arch_dbg_reason[xscale->arch_debug_reason]); + + return ERROR_OK; +} + +enum target_state xscale_poll(target_t *target) +{ + int retval; + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + if ((target->state == TARGET_RUNNING) || (target->state == TARGET_DEBUG_RUNNING)) + { + if ((retval = xscale_read_tx(target, 0)) == ERROR_OK) + { + /* there's data to read from the tx register, we entered debug state */ + xscale->handler_running = 1; + + /* process debug entry, fetching current mode regs */ + if ((retval = xscale_debug_entry(target)) != ERROR_OK) + return retval; + + /* if target was running, signal that we halted + * otherwise we reentered from debug execution */ + if (target->state == TARGET_RUNNING) + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + else + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED); + + target->state = TARGET_HALTED; + } + else if (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE) + { + ERROR("error while polling TX register"); + exit(-1); + } + } + + return target->state; +} + +int xscale_debug_entry(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + u32 pc; + u32 *buffer = malloc(4 * 10); + int i; + + u32 moe; + + /* clear external dbg break (will be written on next DCSR read) */ + xscale->external_debug_break = 0; + xscale_read_dcsr(target); + + /* get r0, pc, r1 to r7 and cpsr */ + xscale_receive(target, buffer, 10); + + /* move r0 from buffer to register cache */ + buf_set_u32(armv4_5->core_cache->reg_list[0].value, 0, 32, buffer[0]); + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_cache->reg_list[15].valid = 1; + DEBUG("r0: 0x%8.8x", buffer[0]); + + /* move pc from buffer to register cache */ + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, buffer[1]); + armv4_5->core_cache->reg_list[15].dirty = 1; + armv4_5->core_cache->reg_list[15].valid = 1; + DEBUG("pc: 0x%8.8x", buffer[1]); + + /* move data from buffer to register cache */ + for (i = 1; i <= 7; i++) + { + buf_set_u32(armv4_5->core_cache->reg_list[i].value, 0, 32, buffer[1 + i]); + armv4_5->core_cache->reg_list[i].dirty = 1; + armv4_5->core_cache->reg_list[i].valid = 1; + DEBUG("r%i: 0x%8.8x", i, buffer[i + 1]); + } + + buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, buffer[9]); + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].dirty = 1; + armv4_5->core_cache->reg_list[ARMV4_5_CPSR].valid = 1; + DEBUG("cpsr: 0x%8.8x", buffer[9]); + + armv4_5->core_mode = buffer[9] & 0x1f; + if (armv4_5_mode_to_number(armv4_5->core_mode) == -1) + { + target->state = TARGET_UNKNOWN; + ERROR("cpsr contains invalid mode value - communication failure"); + return ERROR_TARGET_FAILURE; + } + DEBUG("target entered debug state in %s mode", armv4_5_mode_strings[armv4_5_mode_to_number(armv4_5->core_mode)]); + + if (buffer[9] & 0x20) + armv4_5->core_state = ARMV4_5_STATE_THUMB; + else + armv4_5->core_state = ARMV4_5_STATE_ARM; + + /* get banked registers, r8 to r14, and spsr if not in USR/SYS mode */ + if ((armv4_5->core_mode != ARMV4_5_MODE_USR) && (armv4_5->core_mode != ARMV4_5_MODE_SYS)) + { + xscale_receive(target, buffer, 8); + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).value, 0, 32, buffer[7]); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).dirty = 0; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).valid = 1; + } + else + { + /* r8 to r14, but no spsr */ + xscale_receive(target, buffer, 7); + } + + /* move data from buffer to register cache */ + for (i = 8; i <= 14; i++) + { + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).value, 0, 32, buffer[i - 8]); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).dirty = 0; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).valid = 1; + } + + /* examine debug reason */ + xscale_read_dcsr(target); + moe = buf_get_u32(xscale->reg_cache->reg_list[XSCALE_DCSR].value, 2, 3); + + /* stored PC (for calculating fixup) */ + pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + + switch (moe) + { + case 0x0: /* Processor reset */ + target->debug_reason = DBG_REASON_DBGRQ; + xscale->arch_debug_reason = XSCALE_DBG_REASON_RESET; + pc -= 4; + break; + case 0x1: /* Instruction breakpoint hit */ + target->debug_reason = DBG_REASON_BREAKPOINT; + xscale->arch_debug_reason = XSCALE_DBG_REASON_GENERIC; + pc -= 4; + break; + case 0x2: /* Data breakpoint hit */ + target->debug_reason = DBG_REASON_WATCHPOINT; + xscale->arch_debug_reason = XSCALE_DBG_REASON_GENERIC; + pc -= 4; + break; + case 0x3: /* BKPT instruction executed */ + target->debug_reason = DBG_REASON_BREAKPOINT; + xscale->arch_debug_reason = XSCALE_DBG_REASON_GENERIC; + pc -= 4; + break; + case 0x4: /* Ext. debug event */ + target->debug_reason = DBG_REASON_DBGRQ; + xscale->arch_debug_reason = XSCALE_DBG_REASON_GENERIC; + pc -= 4; + break; + case 0x5: /* Vector trap occured */ + target->debug_reason = DBG_REASON_BREAKPOINT; + xscale->arch_debug_reason = XSCALE_DBG_REASON_GENERIC; + pc -= 4; + break; + case 0x6: /* Trace buffer full break */ + target->debug_reason = DBG_REASON_DBGRQ; + xscale->arch_debug_reason = XSCALE_DBG_REASON_TB_FULL; + pc -= 4; + break; + case 0x7: /* Reserved */ + default: + ERROR("Method of Entry is 'Reserved'"); + exit(-1); + break; + } + + /* apply PC fixup */ + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, pc); + + /* on the first debug entry, identify cache type */ + if (xscale->armv4_5_mmu.armv4_5_cache.ctype == -1) + { + u32 cache_type_reg; + + /* read cp15 cache type register */ + xscale_get_reg(&xscale->reg_cache->reg_list[XSCALE_CACHETYPE]); + cache_type_reg = buf_get_u32(xscale->reg_cache->reg_list[XSCALE_CACHETYPE].value, 0, 32); + + armv4_5_identify_cache(cache_type_reg, &xscale->armv4_5_mmu.armv4_5_cache); + } + + /* examine MMU and Cache settings */ + /* read cp15 control register */ + xscale_get_reg(&xscale->reg_cache->reg_list[XSCALE_CTRL]); + xscale->cp15_control_reg = buf_get_u32(xscale->reg_cache->reg_list[XSCALE_CTRL].value, 0, 32); + xscale->armv4_5_mmu.mmu_enabled = (xscale->cp15_control_reg & 0x1U) ? 1 : 0; + xscale->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = (xscale->cp15_control_reg & 0x4U) ? 1 : 0; + xscale->armv4_5_mmu.armv4_5_cache.i_cache_enabled = (xscale->cp15_control_reg & 0x1000U) ? 1 : 0; + + return ERROR_OK; +} + +int xscale_halt(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + DEBUG("target->state: %s", target_state_strings[target->state]); + + if (target->state == TARGET_HALTED) + { + WARNING("target was already halted"); + return ERROR_TARGET_ALREADY_HALTED; + } + else if (target->state == TARGET_UNKNOWN) + { + /* this must not happen for a xscale target */ + ERROR("target was in unknown state when halt was requested"); + exit(-1); + } + else if (target->state == TARGET_RESET) + { + DEBUG("target->state == TARGET_RESET"); + + /* clear TRST */ + jtag_add_reset(0, -1); + } + else + { + /* assert external dbg break */ + xscale->external_debug_break = 1; + xscale_read_dcsr(target); + + target->debug_reason = DBG_REASON_DBGRQ; + } + + return ERROR_OK; +} + +int xscale_enable_single_step(struct target_s *target, u32 next_pc) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale= armv4_5->arch_info; + reg_t *ibcr0 = &xscale->reg_cache->reg_list[XSCALE_IBCR0]; + + if (xscale->ibcr0_used) + { + breakpoint_t *ibcr0_bp = breakpoint_find(target, buf_get_u32(ibcr0->value, 0, 32) & 0xfffffffe); + + if (ibcr0_bp) + { + xscale_unset_breakpoint(target, ibcr0_bp); + } + else + { + ERROR("BUG: xscale->ibcr0_used is set, but no breakpoint with that address found"); + exit(-1); + } + } + + xscale_set_reg_u32(ibcr0, next_pc | 0x1); + + return ERROR_OK; +} + +int xscale_disable_single_step(struct target_s *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale= armv4_5->arch_info; + reg_t *ibcr0 = &xscale->reg_cache->reg_list[XSCALE_IBCR0]; + + xscale_set_reg_u32(ibcr0, 0x0); + + return ERROR_OK; +} + +int xscale_resume(struct target_s *target, int current, u32 address, int handle_breakpoints, int debug_execution) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale= armv4_5->arch_info; + breakpoint_t *breakpoint = target->breakpoints; + + u32 current_pc; + + int retval; + int i; + + DEBUG("-"); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!debug_execution) + { + target_free_all_working_areas(target); + } + + /* update vector tables */ + xscale_update_vectors(target); + + /* current = 1: continue on current pc, otherwise continue at <address> */ + if (!current) + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, address); + + current_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + + /* if we're at the reset vector, we have to simulate the branch */ + if (current_pc == 0x0) + { + arm_simulate_step(target, NULL); + current_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + } + + /* the front-end may request us not to handle breakpoints */ + if (handle_breakpoints) + { + if ((breakpoint = breakpoint_find(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)))) + { + u32 next_pc; + + /* there's a breakpoint at the current PC, we have to step over it */ + DEBUG("unset breakpoint at 0x%8.8x", breakpoint->address); + xscale_unset_breakpoint(target, breakpoint); + + /* calculate PC of next instruction */ + if ((retval = arm_simulate_step(target, &next_pc)) != ERROR_OK) + { + u32 current_opcode; + target_read_u32(target, current_pc, ¤t_opcode); + ERROR("BUG: couldn't calculate PC of next instruction, current opcode was 0x%8.8x", current_opcode); + } + + DEBUG("enable single-step"); + xscale_enable_single_step(target, next_pc); + + /* restore banked registers */ + xscale_restore_context(target); + + /* send resume request (command 0x30 or 0x31) + * clean the trace buffer if it is to be enabled (0x62) */ + if (xscale->trace_buffer_enabled) + { + xscale_send_u32(target, 0x62); + xscale_send_u32(target, 0x31); + } + else + xscale_send_u32(target, 0x30); + + /* send CPSR */ + xscale_send_u32(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32)); + DEBUG("writing cpsr with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32)); + + for (i = 7; i >= 0; i--) + { + /* send register */ + xscale_send_u32(target, buf_get_u32(armv4_5->core_cache->reg_list[i].value, 0, 32)); + DEBUG("writing r%i with value 0x%8.8x", i, buf_get_u32(armv4_5->core_cache->reg_list[i].value, 0, 32)); + } + + /* send PC */ + xscale_send_u32(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + DEBUG("writing PC with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + + /* wait for and process debug entry */ + xscale_debug_entry(target); + + DEBUG("disable single-step"); + xscale_disable_single_step(target); + + DEBUG("set breakpoint at 0x%8.8x", breakpoint->address); + xscale_set_breakpoint(target, breakpoint); + } + } + + /* enable any pending breakpoints and watchpoints */ + xscale_enable_breakpoints(target); + xscale_enable_watchpoints(target); + + /* restore banked registers */ + xscale_restore_context(target); + + /* send resume request (command 0x30 or 0x31) + * clean the trace buffer if it is to be enabled (0x62) */ + if (xscale->trace_buffer_enabled) + { + xscale_send_u32(target, 0x62); + xscale_send_u32(target, 0x31); + } + else + xscale_send_u32(target, 0x30); + + /* send CPSR */ + xscale_send_u32(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32)); + DEBUG("writing cpsr with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32)); + + for (i = 7; i >= 0; i--) + { + /* send register */ + xscale_send_u32(target, buf_get_u32(armv4_5->core_cache->reg_list[i].value, 0, 32)); + DEBUG("writing r%i with value 0x%8.8x", i, buf_get_u32(armv4_5->core_cache->reg_list[i].value, 0, 32)); + } + + /* send PC */ + xscale_send_u32(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + DEBUG("writing PC with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + + target->debug_reason = DBG_REASON_NOTHALTED; + + if (!debug_execution) + { + /* registers are now invalid */ + armv4_5_invalidate_core_regs(target); + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + } + else + { + target->state = TARGET_DEBUG_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED); + } + + DEBUG("target resumed"); + + xscale->handler_running = 1; + + return ERROR_OK; +} + +int xscale_step(struct target_s *target, int current, u32 address, int handle_breakpoints) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + breakpoint_t *breakpoint = target->breakpoints; + + u32 current_pc, next_pc; + int i; + int retval; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* current = 1: continue on current pc, otherwise continue at <address> */ + if (!current) + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, address); + + current_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + + /* if we're at the reset vector, we have to simulate the step */ + if (current_pc == 0x0) + { + arm_simulate_step(target, NULL); + current_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32); + + target->debug_reason = DBG_REASON_SINGLESTEP; + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + return ERROR_OK; + } + + /* the front-end may request us not to handle breakpoints */ + if (handle_breakpoints) + if ((breakpoint = breakpoint_find(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)))) + { + xscale_unset_breakpoint(target, breakpoint); + } + + target->debug_reason = DBG_REASON_SINGLESTEP; + + /* calculate PC of next instruction */ + if ((retval = arm_simulate_step(target, &next_pc)) != ERROR_OK) + { + u32 current_opcode; + target_read_u32(target, current_pc, ¤t_opcode); + ERROR("BUG: couldn't calculate PC of next instruction, current opcode was 0x%8.8x", current_opcode); + } + + DEBUG("enable single-step"); + xscale_enable_single_step(target, next_pc); + + /* restore banked registers */ + xscale_restore_context(target); + + /* send resume request (command 0x30 or 0x31) + * clean the trace buffer if it is to be enabled (0x62) */ + if (xscale->trace_buffer_enabled) + { + xscale_send_u32(target, 0x62); + xscale_send_u32(target, 0x31); + } + else + xscale_send_u32(target, 0x30); + + /* send CPSR */ + xscale_send_u32(target, buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32)); + DEBUG("writing cpsr with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32)); + + for (i = 7; i >= 0; i--) + { + /* send register */ + xscale_send_u32(target, buf_get_u32(armv4_5->core_cache->reg_list[i].value, 0, 32)); + DEBUG("writing r%i with value 0x%8.8x", i, buf_get_u32(armv4_5->core_cache->reg_list[i].value, 0, 32)); + } + + /* send PC */ + xscale_send_u32(target, buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + DEBUG("writing PC with value 0x%8.8x", buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32)); + + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + + /* registers are now invalid */ + armv4_5_invalidate_core_regs(target); + + /* wait for and process debug entry */ + xscale_debug_entry(target); + + DEBUG("disable single-step"); + xscale_disable_single_step(target); + + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + + if (breakpoint) + { + xscale_set_breakpoint(target, breakpoint); + } + + DEBUG("target stepped"); + + return ERROR_OK; + +} + +int xscale_assert_reset(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + DEBUG("target->state: %s", target_state_strings[target->state]); + + /* if the handler isn't installed yet, we have to assert TRST, too */ + if (!xscale->handler_installed) + { + jtag_add_reset(1, 1); + } + else + jtag_add_reset(-1, 1); + + /* sleep 1ms, to be sure we fulfill any requirements */ + jtag_add_sleep(1000); + + target->state = TARGET_RESET; + + return ERROR_OK; + +} + +int xscale_deassert_reset(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + FILE *binary; + u32 address; + struct stat binary_stat; + u32 binary_size; + + u32 buffer[8]; + u32 buf_cnt; + int i; + + breakpoint_t *breakpoint = target->breakpoints; + + xscale->ibcr_available = 2; + xscale->ibcr0_used = 0; + xscale->ibcr1_used = 0; + + xscale->dbr_available = 2; + xscale->dbr0_used = 0; + xscale->dbr1_used = 0; + + /* mark all hardware breakpoints as unset */ + while (breakpoint) + { + if (breakpoint->type == BKPT_HARD) + { + breakpoint->set = 0; + } + breakpoint = breakpoint->next; + } + + if (!xscale->handler_installed) + { + /* release TRST */ + jtag_add_reset(0, -1); + jtag_add_sleep(100000); + + /* set Hold reset, Halt mode and Trap Reset */ + buf_set_u32(xscale->reg_cache->reg_list[XSCALE_DCSR].value, 30, 1, 0x1); + buf_set_u32(xscale->reg_cache->reg_list[XSCALE_DCSR].value, 16, 1, 0x1); + xscale_write_dcsr(target, 1, 0); + jtag_add_runtest(100, TAP_RTI); + jtag_execute_queue(); + + /* release SRST */ + jtag_add_reset(0, 0); + /* wait 150ms; 100ms were not enough */ + jtag_add_sleep(150000); + + jtag_add_runtest(2030, TAP_RTI); + jtag_execute_queue(); + + xscale_write_dcsr(target, 1, 0); + jtag_execute_queue(); + + /* TODO: load debug handler */ + if (stat("target/xscale/debug_handler.bin", &binary_stat) == -1) + { + ERROR("couldn't stat() target/xscale/debug_handler.bin: %s", strerror(errno)); + return ERROR_OK; + } + + if (!(binary = fopen("target/xscale/debug_handler.bin", "r"))) + { + ERROR("couldn't open target/xscale/debug_handler.bin: %s", strerror(errno)); + return ERROR_OK; + } + + if ((binary_size = binary_stat.st_size) % 4) + { + ERROR("debug_handler.bin: size not a multiple of 4"); + exit(-1); + } + + if (binary_size > 0x800) + { + ERROR("debug_handler.bin: larger than 2kb"); + exit(-1); + } + + binary_size = CEIL(binary_size, 32) * 32; + + address = xscale->handler_address; + while (binary_size > 0) + { + buf_cnt = fread(buffer, 4, 8, binary); + + for (i = 0; i < buf_cnt; i++) + { + /* convert LE buffer to host-endian u32 */ + buffer[i] = buf_get_u32((u8*)(&buffer[i]), 0, 32); + } + + if (buf_cnt < 8) + { + for (; buf_cnt < 8; buf_cnt++) + { + buffer[buf_cnt] = 0xe1a08008; + } + } + + /* only load addresses other than the reset vectors */ + if ((address % 0x400) != 0x0) + { + xscale_load_ic(target, 1, address, buffer); + } + + address += buf_cnt * 4; + binary_size -= buf_cnt * 4; + }; + + xscale_load_ic(target, 1, 0x0, xscale->low_vectors); + xscale_load_ic(target, 1, 0xffff0000, xscale->high_vectors); + + jtag_add_runtest(30, TAP_RTI); + + /* let the target run (should enter debug handler) */ + xscale_write_dcsr(target, 0, 0); + target->state = TARGET_RUNNING; + + if ((target->reset_mode != RESET_HALT) && (target->reset_mode != RESET_INIT)) + { + jtag_add_sleep(10000); + + /* we should have entered debug now */ + xscale_debug_entry(target); + target->state = TARGET_HALTED; + + /* the PC is now at 0x0 */ + buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, 0x0); + } + } + else + { + jtag_add_reset(0, 0); + } + + + return ERROR_OK; +} + +int xscale_soft_reset_halt(struct target_s *target) +{ + + return ERROR_OK; +} + +int xscale_read_core_reg(struct target_s *target, int num, enum armv4_5_mode mode) +{ + + return ERROR_OK; +} + +int xscale_write_core_reg(struct target_s *target, int num, enum armv4_5_mode mode, u32 value) +{ + + return ERROR_OK; +} + +int xscale_full_context(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + + u32 *buffer; + + int i, j; + + DEBUG("-"); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + buffer = malloc(4 * 8); + + /* iterate through processor modes (FIQ, IRQ, SVC, ABT, UND and SYS) + * we can't enter User mode on an XScale (unpredictable), + * but User shares registers with SYS + */ + for(i = 1; i < 7; i++) + { + int valid = 1; + + /* check if there are invalid registers in the current mode + */ + for (j = 0; j <= 16; j++) + { + if (ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).valid == 0) + valid = 0; + } + + if (!valid) + { + u32 tmp_cpsr; + + /* request banked registers */ + xscale_send_u32(target, 0x0); + + tmp_cpsr = 0x0; + tmp_cpsr |= armv4_5_number_to_mode(i); + tmp_cpsr |= 0xc0; /* I/F bits */ + + /* send CPSR for desired mode */ + xscale_send_u32(target, tmp_cpsr); + + /* get banked registers, r8 to r14, and spsr if not in USR/SYS mode */ + if ((armv4_5_number_to_mode(i) != ARMV4_5_MODE_USR) && (armv4_5_number_to_mode(i) != ARMV4_5_MODE_SYS)) + { + xscale_receive(target, buffer, 8); + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).value, 0, 32, buffer[7]); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).dirty = 0; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).valid = 1; + } + else + { + xscale_receive(target, buffer, 7); + } + + /* move data from buffer to register cache */ + for (j = 8; j <= 14; j++) + { + buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).value, 0, 32, buffer[j - 8]); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).dirty = 0; + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).valid = 1; + } + } + } + + free(buffer); + + return ERROR_OK; +} + +int xscale_restore_context(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + + int i, j; + + DEBUG("-"); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* iterate through processor modes (FIQ, IRQ, SVC, ABT, UND and SYS) + * we can't enter User mode on an XScale (unpredictable), + * but User shares registers with SYS + */ + for(i = 1; i < 7; i++) + { + int dirty = 0; + + /* check if there are invalid registers in the current mode + */ + for (j = 8; j <= 14; j++) + { + if (ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).dirty == 1) + dirty = 1; + } + + /* if not USR/SYS, check if the SPSR needs to be written */ + if ((armv4_5_number_to_mode(i) != ARMV4_5_MODE_USR) && (armv4_5_number_to_mode(i) != ARMV4_5_MODE_SYS)) + { + if (ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).dirty == 1) + dirty = 1; + } + + if (dirty) + { + u32 tmp_cpsr; + + /* send banked registers */ + xscale_send_u32(target, 0x1); + + tmp_cpsr = 0x0; + tmp_cpsr |= armv4_5_number_to_mode(i); + tmp_cpsr |= 0xc0; /* I/F bits */ + + /* send CPSR for desired mode */ + xscale_send_u32(target, tmp_cpsr); + + /* send banked registers, r8 to r14, and spsr if not in USR/SYS mode */ + for (j = 8; j <= 14; j++) + { + xscale_send_u32(target, buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, j).value, 0, 32)); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), j).dirty = 0; + } + + if ((armv4_5_number_to_mode(i) != ARMV4_5_MODE_USR) && (armv4_5_number_to_mode(i) != ARMV4_5_MODE_SYS)) + { + xscale_send_u32(target, buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).value, 0, 32)); + ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5_number_to_mode(i), 16).dirty = 0; + } + } + } + + return ERROR_OK; +} + +int xscale_read_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + u32 *buf32; + int i; + + DEBUG("address: 0x%8.8x, size: 0x%8.8x, count: 0x%8.8x", address, size, count); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* sanitize arguments */ + if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer)) + return ERROR_INVALID_ARGUMENTS; + + if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) + return ERROR_TARGET_UNALIGNED_ACCESS; + + /* send memory read request (command 0x1n, n: access size) */ + xscale_send_u32(target, 0x10 | size); + + /* send base address for read request */ + xscale_send_u32(target, address); + + /* send number of requested data words */ + xscale_send_u32(target, count); + + /* receive data from target (count times 32-bit words in host endianness) */ + buf32 = malloc(4 * count); + xscale_receive(target, buf32, count); + + /* extract data from host-endian buffer into byte stream */ + for (i = 0; i < count; i++) + { + switch (size) + { + case 4: + target_buffer_set_u32(target, buffer, buf32[i]); + buffer += 4; + break; + case 2: + target_buffer_set_u16(target, buffer, buf32[i] & 0xffff); + buffer += 2; + break; + case 1: + *buffer++ = buf32[i] & 0xff; + break; + default: + ERROR("should never get here"); + exit(-1); + } + } + + free(buf32); + + /* examine DCSR, to see if Sticky Abort (SA) got set */ + xscale_read_dcsr(target); + if (buf_get_u32(xscale->reg_cache->reg_list[XSCALE_DCSR].value, 5, 1) == 1) + { + /* clear SA bit */ + xscale_send_u32(target, 0x60); + + return ERROR_TARGET_DATA_ABORT; + } + + return ERROR_OK; +} + +int xscale_write_memory(struct target_s *target, u32 address, u32 size, u32 count, u8 *buffer) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + DEBUG("address: 0x%8.8x, size: 0x%8.8x, count: 0x%8.8x", address, size, count); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* sanitize arguments */ + if (((size != 4) && (size != 2) && (size != 1)) || (count == 0) || !(buffer)) + return ERROR_INVALID_ARGUMENTS; + + if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u))) + return ERROR_TARGET_UNALIGNED_ACCESS; + + /* send memory write request (command 0x2n, n: access size) */ + xscale_send_u32(target, 0x20 | size); + + /* send base address for read request */ + xscale_send_u32(target, address); + + /* send number of requested data words to be written*/ + xscale_send_u32(target, count); + + /* extract data from host-endian buffer into byte stream */ +#if 0 + for (i = 0; i < count; i++) + { + switch (size) + { + case 4: + value = target_buffer_get_u32(target, buffer); + xscale_send_u32(target, value); + buffer += 4; + break; + case 2: + value = target_buffer_get_u16(target, buffer); + xscale_send_u32(target, value); + buffer += 2; + break; + case 1: + value = *buffer; + xscale_send_u32(target, value); + buffer += 1; + break; + default: + ERROR("should never get here"); + exit(-1); + } + } +#endif + xscale_send(target, buffer, count, size); + + /* examine DCSR, to see if Sticky Abort (SA) got set */ + xscale_read_dcsr(target); + if (buf_get_u32(xscale->reg_cache->reg_list[XSCALE_DCSR].value, 5, 1) == 1) + { + /* clear SA bit */ + xscale_send_u32(target, 0x60); + + return ERROR_TARGET_DATA_ABORT; + } + + return ERROR_OK; +} + +int xscale_bulk_write_memory(target_t *target, u32 address, u32 count, u8 *buffer) +{ + xscale_write_memory(target, address, 4, count, buffer); + + return ERROR_OK; +} + +u32 xscale_get_ttb(target_t *target) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + u32 ttb; + + xscale_get_reg(&xscale->reg_cache->reg_list[XSCALE_TTB]); + ttb = buf_get_u32(xscale->reg_cache->reg_list[XSCALE_TTB].value, 0, 32); + + return ttb; +} + +void xscale_disable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + u32 cp15_control; + + /* read cp15 control register */ + xscale_get_reg(&xscale->reg_cache->reg_list[XSCALE_CTRL]); + cp15_control = buf_get_u32(xscale->reg_cache->reg_list[XSCALE_CTRL].value, 0, 32); + + if (mmu) + cp15_control &= ~0x1U; + + if (d_u_cache) + { + /* clean DCache */ + xscale_send_u32(target, 0x50); + xscale_send_u32(target, xscale->cache_clean_address); + + /* invalidate DCache */ + xscale_send_u32(target, 0x51); + + cp15_control &= ~0x4U; + } + + if (i_cache) + { + /* invalidate ICache */ + xscale_send_u32(target, 0x52); + cp15_control &= ~0x1000U; + } + + /* write new cp15 control register */ + xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_CTRL], cp15_control); + + /* execute cpwait to ensure outstanding operations complete */ + xscale_send_u32(target, 0x53); +} + +void xscale_enable_mmu_caches(target_t *target, int mmu, int d_u_cache, int i_cache) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + u32 cp15_control; + + /* read cp15 control register */ + xscale_get_reg(&xscale->reg_cache->reg_list[XSCALE_CTRL]); + cp15_control = buf_get_u32(xscale->reg_cache->reg_list[XSCALE_CTRL].value, 0, 32); + + if (mmu) + cp15_control |= 0x1U; + + if (d_u_cache) + cp15_control |= 0x4U; + + if (i_cache) + cp15_control |= 0x1000U; + + /* write new cp15 control register */ + xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_CTRL], cp15_control); + + /* execute cpwait to ensure outstanding operations complete */ + xscale_send_u32(target, 0x53); +} + +int xscale_set_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (xscale->force_hw_bkpts) + breakpoint->type = BKPT_HARD; + + if (breakpoint->set) + { + WARNING("breakpoint already set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_HARD) + { + u32 value = breakpoint->address | 1; + if (!xscale->ibcr0_used) + { + xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_IBCR0], value); + xscale->ibcr0_used = 1; + breakpoint->set = 1; /* breakpoint set on first breakpoint register */ + } + else if (!xscale->ibcr1_used) + { + xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_IBCR1], value); + xscale->ibcr1_used = 1; + breakpoint->set = 2; /* breakpoint set on second breakpoint register */ + } + else + { + ERROR("BUG: no hardware comparator available"); + return ERROR_OK; + } + } + else if (breakpoint->type == BKPT_SOFT) + { + if (breakpoint->length == 4) + { + /* keep the original instruction in target endianness */ + target->type->read_memory(target, breakpoint->address, 4, 1, breakpoint->orig_instr); + /* write the original instruction in target endianness (arm7_9->arm_bkpt is host endian) */ + target_write_u32(target, breakpoint->address, xscale->arm_bkpt); + } + else + { + /* keep the original instruction in target endianness */ + target->type->read_memory(target, breakpoint->address, 2, 1, breakpoint->orig_instr); + /* write the original instruction in target endianness (arm7_9->arm_bkpt is host endian) */ + target_write_u32(target, breakpoint->address, xscale->thumb_bkpt); + } + breakpoint->set = 1; + } + + return ERROR_OK; + +} + +int xscale_add_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (xscale->force_hw_bkpts) + { + DEBUG("forcing use of hardware breakpoint at address 0x%8.8x", breakpoint->address); + breakpoint->type = BKPT_HARD; + } + + if ((breakpoint->type == BKPT_HARD) && (xscale->ibcr_available < 1)) + { + INFO("no breakpoint unit available for hardware breakpoint"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + else + { + xscale->ibcr_available--; + } + + if ((breakpoint->length != 2) && (breakpoint->length != 4)) + { + INFO("only breakpoints of two (Thumb) or four (ARM) bytes length supported"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + return ERROR_OK; +} + +int xscale_unset_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!breakpoint->set) + { + WARNING("breakpoint not set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_HARD) + { + if (breakpoint->set == 1) + { + xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_IBCR0], 0x0); + xscale->ibcr0_used = 0; + } + else if (breakpoint->set == 2) + { + xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_IBCR1], 0x0); + xscale->ibcr1_used = 0; + } + breakpoint->set = 0; + } + else + { + /* restore original instruction (kept in target endianness) */ + if (breakpoint->length == 4) + { + target->type->write_memory(target, breakpoint->address, 4, 1, breakpoint->orig_instr); + } + else + { + target->type->write_memory(target, breakpoint->address, 2, 1, breakpoint->orig_instr); + } + breakpoint->set = 0; + } + + return ERROR_OK; +} + +int xscale_remove_breakpoint(struct target_s *target, breakpoint_t *breakpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (breakpoint->set) + { + xscale_unset_breakpoint(target, breakpoint); + } + + if (breakpoint->type == BKPT_HARD) + xscale->ibcr_available++; + + return ERROR_OK; +} + +int xscale_set_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + u8 enable; + reg_t *dbcon = &xscale->reg_cache->reg_list[XSCALE_DBCON]; + u32 dbcon_value = buf_get_u32(dbcon->value, 0, 32); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + xscale_get_reg(dbcon); + + switch (watchpoint->rw) + { + case WPT_READ: + enable = 0x3; + break; + case WPT_ACCESS: + enable = 0x2; + break; + case WPT_WRITE: + enable = 0x1; + break; + default: + ERROR("BUG: watchpoint->rw neither read, write nor access"); + } + + if (!xscale->dbr0_used) + { + xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_DBR0], watchpoint->address); + dbcon_value |= enable; + xscale_set_reg_u32(dbcon, dbcon_value); + watchpoint->set = 1; + xscale->dbr0_used = 1; + } + else if (!xscale->dbr1_used) + { + xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_DBR1], watchpoint->address); + dbcon_value |= enable << 2; + xscale_set_reg_u32(dbcon, dbcon_value); + watchpoint->set = 2; + xscale->dbr1_used = 1; + } + else + { + ERROR("BUG: no hardware comparator available"); + return ERROR_OK; + } + + return ERROR_OK; +} + +int xscale_add_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (xscale->dbr_available < 1) + { + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if ((watchpoint->length != 1) && (watchpoint->length != 2) && (watchpoint->length != 4)) + { + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + xscale->dbr_available--; + + return ERROR_OK; +} + +int xscale_unset_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + reg_t *dbcon = &xscale->reg_cache->reg_list[XSCALE_DBCON]; + u32 dbcon_value = buf_get_u32(dbcon->value, 0, 32); + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (!watchpoint->set) + { + WARNING("breakpoint not set"); + return ERROR_OK; + } + + if (watchpoint->set == 1) + { + dbcon_value &= ~0x3; + xscale_set_reg_u32(dbcon, dbcon_value); + xscale->dbr0_used = 0; + } + else if (watchpoint->set == 2) + { + dbcon_value &= ~0xc; + xscale_set_reg_u32(dbcon, dbcon_value); + xscale->dbr1_used = 0; + } + watchpoint->set = 0; + + return ERROR_OK; +} + +int xscale_remove_watchpoint(struct target_s *target, watchpoint_t *watchpoint) +{ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + if (target->state != TARGET_HALTED) + { + WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (watchpoint->set) + { + xscale_unset_watchpoint(target, watchpoint); + } + + xscale->dbr_available++; + + return ERROR_OK; +} + +void xscale_enable_watchpoints(struct target_s *target) +{ + watchpoint_t *watchpoint = target->watchpoints; + + while (watchpoint) + { + if (watchpoint->set == 0) + xscale_set_watchpoint(target, watchpoint); + watchpoint = watchpoint->next; + } +} + +void xscale_enable_breakpoints(struct target_s *target) +{ + breakpoint_t *breakpoint = target->breakpoints; + + /* set any pending breakpoints */ + while (breakpoint) + { + if (breakpoint->set == 0) + xscale_set_breakpoint(target, breakpoint); + breakpoint = breakpoint->next; + } +} + +int xscale_get_reg(reg_t *reg) +{ + xscale_reg_t *arch_info = reg->arch_info; + target_t *target = arch_info->target; + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + /* DCSR, TX and RX are accessible via JTAG */ + if (strcmp(reg->name, "XSCALE_DCSR") == 0) + { + return xscale_read_dcsr(arch_info->target); + } + else if (strcmp(reg->name, "XSCALE_TX") == 0) + { + /* 1 = consume register content */ + return xscale_read_tx(arch_info->target, 1); + } + else if (strcmp(reg->name, "XSCALE_RX") == 0) + { + /* can't read from RX register (host -> debug handler) */ + return ERROR_OK; + } + else if (strcmp(reg->name, "XSCALE_TXRXCTRL") == 0) + { + /* can't (explicitly) read from TXRXCTRL register */ + return ERROR_OK; + } + else /* Other DBG registers have to be transfered by the debug handler */ + { + /* send CP read request (command 0x40) */ + xscale_send_u32(target, 0x40); + + /* send CP register number */ + xscale_send_u32(target, arch_info->dbg_handler_number); + + /* read register value */ + xscale_read_tx(target, 1); + buf_cpy(xscale->reg_cache->reg_list[XSCALE_TX].value, reg->value, 32); + + reg->dirty = 0; + reg->valid = 1; + } + + return ERROR_OK; +} + +int xscale_set_reg(reg_t *reg, u8* buf) +{ + xscale_reg_t *arch_info = reg->arch_info; + target_t *target = arch_info->target; + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + u32 value = buf_get_u32(buf, 0, 32); + + /* DCSR, TX and RX are accessible via JTAG */ + if (strcmp(reg->name, "XSCALE_DCSR") == 0) + { + buf_set_u32(xscale->reg_cache->reg_list[XSCALE_DCSR].value, 0, 32, value); + return xscale_write_dcsr(arch_info->target, -1, -1); + } + else if (strcmp(reg->name, "XSCALE_RX") == 0) + { + buf_set_u32(xscale->reg_cache->reg_list[XSCALE_RX].value, 0, 32, value); + return xscale_write_rx(arch_info->target); + } + else if (strcmp(reg->name, "XSCALE_TX") == 0) + { + /* can't write to TX register (debug-handler -> host) */ + return ERROR_OK; + } + else if (strcmp(reg->name, "XSCALE_TXRXCTRL") == 0) + { + /* can't (explicitly) write to TXRXCTRL register */ + return ERROR_OK; + } + else /* Other DBG registers have to be transfered by the debug handler */ + { + /* send CP write request (command 0x41) */ + xscale_send_u32(target, 0x41); + + /* send CP register number */ + xscale_send_u32(target, arch_info->dbg_handler_number); + + /* send CP register value */ + xscale_send_u32(target, value); + buf_set_u32(reg->value, 0, 32, value); + } + + return ERROR_OK; +} + +/* convenience wrapper to access XScale specific registers */ +int xscale_set_reg_u32(reg_t *reg, u32 value) +{ + u8 buf[4]; + + buf_set_u32(buf, 0, 32, value); + + return xscale_set_reg(reg, buf); +} + +int xscale_write_dcsr_sw(target_t *target, u32 value) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + reg_t *dcsr = &xscale->reg_cache->reg_list[XSCALE_DCSR]; + xscale_reg_t *dcsr_arch_info = dcsr->arch_info; + + /* send CP write request (command 0x41) */ + xscale_send_u32(target, 0x41); + + /* send CP register number */ + xscale_send_u32(target, dcsr_arch_info->dbg_handler_number); + + /* send CP register value */ + xscale_send_u32(target, value); + buf_set_u32(dcsr->value, 0, 32, value); + + return ERROR_OK; +} + +void xscale_build_reg_cache(target_t *target) +{ + /* get pointers to arch-specific information */ + armv4_5_common_t *armv4_5 = target->arch_info; + xscale_common_t *xscale = armv4_5->arch_info; + + reg_cache_t **cache_p = register_get_last_cache_p(&target->reg_cache); + xscale_reg_t *arch_info = malloc(sizeof(xscale_reg_arch_info)); + int i; + int num_regs = sizeof(xscale_reg_arch_info) / sizeof(xscale_reg_t); + + (*cache_p) = armv4_5_build_reg_cache(target, armv4_5); + armv4_5->core_cache = (*cache_p); + + /* register a register arch-type for XScale dbg registers only once */ + if (xscale_reg_arch_type == -1) + xscale_reg_arch_type = register_reg_arch_type(xscale_get_reg, xscale_set_reg); + + (*cache_p)->next = malloc(sizeof(reg_cache_t)); + cache_p = &(*cache_p)->next; + + /* fill in values for the xscale reg cache */ + (*cache_p)->name = "XScale registers"; + (*cache_p)->next = NULL; + (*cache_p)->reg_list = malloc(num_regs * sizeof(reg_t)); + (*cache_p)->num_regs = num_regs; + + for (i = 0; i < num_regs; i++) + { + (*cache_p)->reg_list[i].name = xscale_reg_list[i]; + (*cache_p)->reg_list[i].value = calloc(4, 1); + (*cache_p)->reg_list[i].dirty = 0; + (*cache_p)->reg_list[i].valid = 0; + (*cache_p)->reg_list[i].size = 32; + (*cache_p)->reg_list[i].bitfield_desc = NULL; + (*cache_p)->reg_list[i].num_bitfields = 0; + (*cache_p)->reg_list[i].arch_info = &arch_info[i]; + (*cache_p)->reg_list[i].arch_type = xscale_reg_arch_type; + arch_info[i] = xscale_reg_arch_info[i]; + arch_info[i].target = target; + } + + xscale->reg_cache = (*cache_p); +} + +int xscale_init_target(struct command_context_s *cmd_ctx, struct target_s *target) +{ + if (startup_mode != DAEMON_RESET) + { + ERROR("XScale target requires a reset"); + ERROR("Reset target to enable debug"); + } + + return ERROR_OK; +} + +int xscale_quit() +{ + + return ERROR_OK; +} + +int xscale_init_arch_info(target_t *target, xscale_common_t *xscale, int chain_pos, char *variant) +{ + armv4_5_common_t *armv4_5; + u32 high_reset_branch, low_reset_branch; + int i; + + armv4_5 = &xscale->armv4_5_common; + + /* store architecture specfic data (none so far) */ + xscale->arch_info = NULL; + xscale->common_magic = XSCALE_COMMON_MAGIC; + + /* remember the variant (PXA25x, PXA27x, IXP42x, ...) */ + xscale->variant = strdup(variant); + + /* prepare JTAG information for the new target */ + xscale->jtag_info.chain_pos = chain_pos; + jtag_register_event_callback(xscale_jtag_callback, target); + + xscale->jtag_info.dbgrx = 0x02; + xscale->jtag_info.dbgtx = 0x10; + xscale->jtag_info.dcsr = 0x09; + xscale->jtag_info.ldic = 0x07; + + if ((strcmp(xscale->variant, "pxa250") == 0) || + (strcmp(xscale->variant, "pxa255") == 0) || + (strcmp(xscale->variant, "pxa26x") == 0)) + { + xscale->jtag_info.ir_length = 5; + } + else if ((strcmp(xscale->variant, "pxa27x") == 0) || + (strcmp(xscale->variant, "ixp42x") == 0) || + (strcmp(xscale->variant, "ixp45x") == 0) || + (strcmp(xscale->variant, "ixp46x") == 0)) + { + xscale->jtag_info.ir_length = 7; + } + + /* the debug handler isn't installed (and thus not running) at this time */ + xscale->handler_installed = 0; + xscale->handler_running = 0; + xscale->handler_address = 0xfe000800; + + /* clear the vectors we keep locally for reference */ + memset(xscale->low_vectors, 0, sizeof(xscale->low_vectors)); + memset(xscale->high_vectors, 0, sizeof(xscale->high_vectors)); + + /* no user-specified vectors have been configured yet */ + xscale->static_low_vectors_set = 0x0; + xscale->static_high_vectors_set = 0x0; + + /* calculate branches to debug handler */ + low_reset_branch = (xscale->handler_address + 0x20 - 0x0 - 0x8) >> 2; + high_reset_branch = (xscale->handler_address + 0x20 - 0xffff0000 - 0x8) >> 2; + + xscale->low_vectors[0] = ARMV4_5_B((low_reset_branch & 0xffffff), 0); + xscale->high_vectors[0] = ARMV4_5_B((high_reset_branch & 0xffffff), 0); + + for (i = 1; i <= 7; i++) + { + xscale->low_vectors[i] = ARMV4_5_B(0xfffffe, 0); + xscale->high_vectors[i] = ARMV4_5_B(0xfffffe, 0); + } + + /* 64kB aligned region used for DCache cleaning */ + xscale->cache_clean_address = 0xfffe0000; + + xscale->hold_rst = 0; + xscale->external_debug_break = 0; + + xscale->force_hw_bkpts = 1; + + xscale->ibcr_available = 2; + xscale->ibcr0_used = 0; + xscale->ibcr1_used = 0; + + xscale->dbr_available = 2; + xscale->dbr0_used = 0; + xscale->dbr1_used = 0; + + xscale->arm_bkpt = ARMV5_BKPT(0x0); + xscale->thumb_bkpt = ARMV5_T_BKPT(0x0) & 0xffff; + + xscale->vector_catch = 0x1; + + xscale->trace_buffer_enabled = 0; + xscale->trace_buffer_fill = 0; + + /* prepare ARMv4/5 specific information */ + armv4_5->arch_info = xscale; + armv4_5->read_core_reg = xscale_read_core_reg; + armv4_5->write_core_reg = xscale_write_core_reg; + armv4_5->full_context = xscale_full_context; + + armv4_5_init_arch_info(target, armv4_5); + + xscale->armv4_5_mmu.armv4_5_cache.ctype = -1; + xscale->armv4_5_mmu.get_ttb = xscale_get_ttb; + xscale->armv4_5_mmu.read_memory = xscale_read_memory; + xscale->armv4_5_mmu.write_memory = xscale_write_memory; + xscale->armv4_5_mmu.disable_mmu_caches = xscale_disable_mmu_caches; + xscale->armv4_5_mmu.enable_mmu_caches = xscale_enable_mmu_caches; + xscale->armv4_5_mmu.has_tiny_pages = 1; + xscale->armv4_5_mmu.mmu_enabled = 0; + + return ERROR_OK; +} + +/* target xscale <endianess> <startup_mode> <chain_pos> <variant> */ +int xscale_target_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct target_s *target) +{ + int chain_pos; + char *variant = NULL; + xscale_common_t *xscale = malloc(sizeof(xscale_common_t)); + + if (argc < 5) + { + ERROR("'target xscale' requires four arguments: <endianess> <startup_mode> <chain_pos> <variant>"); + exit(-1); + } + + chain_pos = strtoul(args[3], NULL, 0); + + variant = args[4]; + + xscale_init_arch_info(target, xscale, chain_pos, variant); + xscale_build_reg_cache(target); + + return ERROR_OK; +} + +int xscale_handle_debug_handler_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = NULL; + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + + u32 handler_address; + + if (argc < 2) + { + ERROR("'xscale debug_handler <target#> <address>' command takes two required operands"); + return ERROR_OK; + } + + if ((target = get_target_by_num(strtoul(args[0], NULL, 0))) == NULL) + { + ERROR("no target '%s' configured", args[0]); + return ERROR_OK; + } + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an ARM920t target"); + return ERROR_OK; + } + + handler_address = strtoul(args[1], NULL, 0); + + if (((handler_address >= 0x800) && (handler_address <= 0x1fef800)) || + ((handler_address >= 0xfe000800) && (handler_address <= 0xfffff800))) + { + xscale->handler_address = handler_address; + } + else + { + ERROR("xscale debug_handler <address> must be between 0x800 and 0x1fef800 or between 0xfe000800 and 0xfffff800"); + } + + return ERROR_OK; +} + +int xscale_handle_cache_clean_address_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = NULL; + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + + u32 cache_clean_address; + + if (argc < 2) + { + ERROR("'xscale cache_clean_address <target#> <address>' command takes two required operands"); + return ERROR_OK; + } + + if ((target = get_target_by_num(strtoul(args[0], NULL, 0))) == NULL) + { + ERROR("no target '%s' configured", args[0]); + return ERROR_OK; + } + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an XScale target"); + return ERROR_OK; + } + + cache_clean_address = strtoul(args[1], NULL, 0); + + if (cache_clean_address & 0xffff) + { + ERROR("xscale cache_clean_address <address> must be 64kb aligned"); + } + else + { + xscale->cache_clean_address = cache_clean_address; + } + + return ERROR_OK; +} + +int xscale_handle_cache_info_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an XScale target"); + return ERROR_OK; + } + + return armv4_5_handle_cache_info_command(cmd_ctx, &xscale->armv4_5_mmu.armv4_5_cache); +} + +int xscale_handle_virt2phys_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an XScale target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + return armv4_5_mmu_handle_virt2phys_command(cmd_ctx, cmd, args, argc, target, &xscale->armv4_5_mmu); +} + +int xscale_handle_mmu_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an XScale target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + if (argc >= 1) + { + if (strcmp("enable", args[0]) == 0) + { + xscale_enable_mmu_caches(target, 1, 0, 0); + xscale->armv4_5_mmu.mmu_enabled = 1; + } + else if (strcmp("disable", args[0]) == 0) + { + xscale_disable_mmu_caches(target, 1, 0, 0); + xscale->armv4_5_mmu.mmu_enabled = 0; + } + } + + command_print(cmd_ctx, "mmu %s", (xscale->armv4_5_mmu.mmu_enabled) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int xscale_handle_idcache_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + int icache = 0, dcache = 0; + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an XScale target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + if (strcmp(cmd, "icache") == 0) + icache = 1; + else if (strcmp(cmd, "dcache") == 0) + dcache = 1; + + if (argc >= 1) + { + if (strcmp("enable", args[0]) == 0) + { + xscale_enable_mmu_caches(target, 0, dcache, icache); + + if (icache) + xscale->armv4_5_mmu.armv4_5_cache.i_cache_enabled = 1; + else if (dcache) + xscale->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = 1; + } + else if (strcmp("disable", args[0]) == 0) + { + xscale_disable_mmu_caches(target, 0, dcache, icache); + + if (icache) + xscale->armv4_5_mmu.armv4_5_cache.i_cache_enabled = 0; + else if (dcache) + xscale->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled = 0; + } + } + + if (icache) + command_print(cmd_ctx, "icache %s", (xscale->armv4_5_mmu.armv4_5_cache.i_cache_enabled) ? "enabled" : "disabled"); + + if (dcache) + command_print(cmd_ctx, "dcache %s", (xscale->armv4_5_mmu.armv4_5_cache.d_u_cache_enabled) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int xscale_handle_vector_catch_command(command_context_t *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an XScale target"); + return ERROR_OK; + } + + if (argc < 1) + { + command_print(cmd_ctx, "usage: xscale vector_catch [mask]"); + } + else + { + xscale->vector_catch = strtoul(args[0], NULL, 0); + buf_set_u32(xscale->reg_cache->reg_list[XSCALE_DCSR].value, 16, 8, xscale->vector_catch); + xscale_write_dcsr(target, -1, -1); + } + + command_print(cmd_ctx, "vector catch mask: 0x%2.2x", xscale->vector_catch); + + return ERROR_OK; +} + +int xscale_handle_force_hw_bkpts_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an XScale target"); + return ERROR_OK; + } + + if ((argc >= 1) && (strcmp("enable", args[0]) == 0)) + { + xscale->force_hw_bkpts = 1; + } + else if ((argc >= 1) && (strcmp("disable", args[0]) == 0)) + { + xscale->force_hw_bkpts = 0; + } + else + { + command_print(cmd_ctx, "usage: xscale force_hw_bkpts <enable|disable>"); + } + + command_print(cmd_ctx, "force hardware breakpoints %s", (xscale->force_hw_bkpts) ? "enabled" : "disabled"); + + return ERROR_OK; +} + +int xscale_handle_trace_buffer_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + u32 dcsr_value; + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an XScale target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + if ((argc >= 1) && (strcmp("enable", args[0]) == 0)) + { + xscale->trace_buffer_enabled = 1; + } + else if ((argc >= 1) && (strcmp("disable", args[0]) == 0)) + { + xscale->trace_buffer_enabled = 0; + } + + if ((argc >= 2) && (strcmp("fill", args[1]) == 0)) + { + xscale->trace_buffer_fill = 1; + } + else if ((argc >= 2) && (strcmp("wrap", args[1]) == 0)) + { + xscale->trace_buffer_fill = 0; + } + + command_print(cmd_ctx, "trace buffer %s (%s)", + (xscale->trace_buffer_enabled) ? "enabled" : "disabled", + (xscale->trace_buffer_fill) ? "fill" : "wrap"); + + dcsr_value = buf_get_u32(xscale->reg_cache->reg_list[XSCALE_DCSR].value, 0, 32); + if (xscale->trace_buffer_fill) + xscale_write_dcsr_sw(target, (dcsr_value & 0xfffffffc) | 2); + else + xscale_write_dcsr_sw(target, dcsr_value & 0xfffffffc); + + return ERROR_OK; +} + +int xscale_handle_dump_trace_buffer_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + target_t *target = get_current_target(cmd_ctx); + armv4_5_common_t *armv4_5; + xscale_common_t *xscale; + u32 trace_buffer[258]; + int is_address[256]; + int i; + + if (xscale_get_arch_pointers(target, &armv4_5, &xscale) != ERROR_OK) + { + command_print(cmd_ctx, "target isn't an XScale target"); + return ERROR_OK; + } + + if (target->state != TARGET_HALTED) + { + command_print(cmd_ctx, "target must be stopped for \"%s\" command", cmd); + return ERROR_OK; + } + + /* send read trace buffer command (command 0x61) */ + xscale_send_u32(target, 0x61); + + /* receive trace buffer content */ + xscale_receive(target, trace_buffer, 258); + + for (i = 255; i >= 0; i--) + { + is_address[i] = 0; + if (((trace_buffer[i] & 0xf0) == 0x90) || + ((trace_buffer[i] & 0xf0) == 0xd0)) + { + if (i >= 4) + is_address[--i] = 1; + if (i >= 3) + is_address[--i] = 1; + if (i >= 2) + is_address[--i] = 1; + if (i >= 1) + is_address[--i] = 1; + } + } + + for (i = 0; i < 256; i++) + { +#if 0 + command_print(cmd_ctx, "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x", + trace_buffer[i + 0], trace_buffer[i + 1], trace_buffer[i + 2], trace_buffer[i + 3], + trace_buffer[i + 4], trace_buffer[i + 5], trace_buffer[i + 6], trace_buffer[i + 6] + ); + i += 8; +#endif + if (is_address[i]) + { + command_print(cmd_ctx, "address: 0x%2.2x%2.2x%2.2x%2.2x", trace_buffer[i], trace_buffer[i+1], trace_buffer[i+2], trace_buffer[i+3]); + i += 3; + } + else + { + switch ((trace_buffer[i] & 0xf0) >> 4) + { + case 0: + command_print(cmd_ctx, "0x%2.2x: reset exception", trace_buffer[i]); + break; + case 1: + command_print(cmd_ctx, "0x%2.2x: undef exception", trace_buffer[i]); + break; + case 2: + command_print(cmd_ctx, "0x%2.2x: swi exception", trace_buffer[i]); + break; + case 3: + command_print(cmd_ctx, "0x%2.2x: pabort exception", trace_buffer[i]); + break; + case 4: + command_print(cmd_ctx, "0x%2.2x: dabort exception", trace_buffer[i]); + break; + case 5: + command_print(cmd_ctx, "0x%2.2x: invalid", trace_buffer[i]); + break; + case 6: + command_print(cmd_ctx, "0x%2.2x: irq exception", trace_buffer[i]); + break; + case 7: + command_print(cmd_ctx, "0x%2.2x: fiq exception", trace_buffer[i]); + break; + case 0x8: + command_print(cmd_ctx, "0x%2.2x: direct branch", trace_buffer[i]); + break; + case 0x9: + command_print(cmd_ctx, "0x%2.2x: indirect branch", trace_buffer[i]); + break; + case 0xa: + command_print(cmd_ctx, "0x%2.2x: invalid", trace_buffer[i]); + break; + case 0xb: + command_print(cmd_ctx, "0x%2.2x: invalid", trace_buffer[i]); + break; + case 0xc: + command_print(cmd_ctx, "0x%2.2x: checkpointed direct branch", trace_buffer[i]); + break; + case 0xd: + command_print(cmd_ctx, "0x%2.2x: checkpointed indirect branch", trace_buffer[i]); + break; + case 0xe: + command_print(cmd_ctx, "0x%2.2x: invalid", trace_buffer[i]); + break; + case 0xf: + command_print(cmd_ctx, "0x%2.2x: rollover", trace_buffer[i]); + break; + } + } + } + + command_print(cmd_ctx, "chkpt0: 0x%8.8x, chkpt1: 0x%8.8x", trace_buffer[256], trace_buffer[257]); + + return ERROR_OK; +} + +int xscale_register_commands(struct command_context_s *cmd_ctx) +{ + command_t *xscale_cmd; + + xscale_cmd = register_command(cmd_ctx, NULL, "xscale", NULL, COMMAND_ANY, "xscale specific commands"); + + register_command(cmd_ctx, xscale_cmd, "debug_handler", xscale_handle_debug_handler_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, xscale_cmd, "cache_clean_address", xscale_handle_cache_clean_address_command, COMMAND_ANY, NULL); + + register_command(cmd_ctx, xscale_cmd, "cache_info", xscale_handle_cache_info_command, COMMAND_EXEC, NULL); + register_command(cmd_ctx, xscale_cmd, "virt2phys", xscale_handle_virt2phys_command, COMMAND_EXEC, NULL); + register_command(cmd_ctx, xscale_cmd, "mmu", xscale_handle_mmu_command, COMMAND_EXEC, "['enable'|'disable'] the MMU"); + register_command(cmd_ctx, xscale_cmd, "icache", xscale_handle_idcache_command, COMMAND_EXEC, "['enable'|'disable'] the ICache"); + register_command(cmd_ctx, xscale_cmd, "dcache", xscale_handle_idcache_command, COMMAND_EXEC, "['enable'|'disable'] the DCache"); + + register_command(cmd_ctx, xscale_cmd, "vector_catch", xscale_handle_idcache_command, COMMAND_EXEC, "<mask> of vectors that should be catched"); + + register_command(cmd_ctx, xscale_cmd, "trace_buffer", xscale_handle_trace_buffer_command, COMMAND_EXEC, "<enable|disable> ['fill'|'wrap']"); + + register_command(cmd_ctx, xscale_cmd, "dump_trace_buffer", xscale_handle_dump_trace_buffer_command, COMMAND_EXEC, "dump content of trace buffer"); + + armv4_5_register_commands(cmd_ctx); + + return ERROR_OK; +} diff --git a/src/target/xscale.h b/src/target/xscale.h new file mode 100644 index 00000000..9fcb265e --- /dev/null +++ b/src/target/xscale.h @@ -0,0 +1,145 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef XSCALE_H +#define XSCALE_H + +#include "target.h" +#include "register.h" +#include "armv4_5.h" +#include "armv4_5_mmu.h" + +#define XSCALE_COMMON_MAGIC 0x58534341 + +typedef struct xscale_jtag_s +{ + /* position in JTAG scan chain */ + int chain_pos; + + /* IR length and instructions */ + int ir_length; + u32 dbgrx; + u32 dbgtx; + u32 ldic; + u32 dcsr; +} xscale_jtag_t; + +enum xscale_debug_reason +{ + XSCALE_DBG_REASON_GENERIC, + XSCALE_DBG_REASON_RESET, + XSCALE_DBG_REASON_TB_FULL, +}; + +typedef struct xscale_common_s +{ + int common_magic; + + /* XScale registers (CP15, DBG) */ + reg_cache_t *reg_cache; + + /* pxa250, pxa255, pxa27x, ixp42x, ... */ + char *variant; + + xscale_jtag_t jtag_info; + + /* current state of the debug handler */ + int handler_installed; + int handler_running; + u32 handler_address; + + /* target-endian buffers with exception vectors */ + u32 low_vectors[8]; + u32 high_vectors[8]; + + /* static low vectors */ + u8 static_low_vectors_set; /* bit field with static vectors set by the user */ + u8 static_high_vectors_set; /* bit field with static vectors set by the user */ + u32 static_low_vectors[8]; + u32 static_high_vectors[8]; + + /* DCache cleaning */ + u32 cache_clean_address; + + /* whether hold_rst and ext_dbg_break should be set */ + int hold_rst; + int external_debug_break; + + /* breakpoint / watchpoint handling */ + int force_hw_bkpts; + int dbr_available; + int dbr0_used; + int dbr1_used; + int ibcr_available; + int ibcr0_used; + int ibcr1_used; + u32 arm_bkpt; + u16 thumb_bkpt; + + u8 vector_catch; + + int trace_buffer_enabled; + int trace_buffer_fill; + + int arch_debug_reason; + + /* armv4/5 common stuff */ + armv4_5_common_t armv4_5_common; + + /* MMU/Caches */ + armv4_5_mmu_common_t armv4_5_mmu; + u32 cp15_control_reg; + + /* possible future enhancements that go beyond XScale common stuff */ + void *arch_info; +} xscale_common_t; + +typedef struct xscale_reg_s +{ + int dbg_handler_number; + target_t *target; +} xscale_reg_t; + +enum +{ + XSCALE_MAINID, /* 0 */ + XSCALE_CACHETYPE, + XSCALE_CTRL, + XSCALE_AUXCTRL, + XSCALE_TTB, + XSCALE_DAC, + XSCALE_FSR, + XSCALE_FAR, + XSCALE_PID, + XSCALE_CPACCESS, + XSCALE_IBCR0, /* 10 */ + XSCALE_IBCR1, + XSCALE_DBR0, + XSCALE_DBR1, + XSCALE_DBCON, + XSCALE_TBREG, + XSCALE_CHKPT0, + XSCALE_CHKPT1, + XSCALE_DCSR, + XSCALE_TX, + XSCALE_RX, /* 20 */ + XSCALE_TXRXCTRL, +}; + +#endif /* XSCALE_H */ diff --git a/src/target/xscale/build.sh b/src/target/xscale/build.sh new file mode 100644 index 00000000..fc828b2c --- /dev/null +++ b/src/target/xscale/build.sh @@ -0,0 +1,7 @@ +arm-none-eabi-gcc -c debug_handler.S -o debug_handler.o +arm-none-eabi-ld -EL -n -Tdebug_handler.cmd debug_handler.o -o debug_handler.out +arm-none-eabi-objcopy -O binary debug_handler.out debug_handler.bin + +#arm-none-eabi-gcc -mbig-endian -c debug_handler.S -o debug_handler_be.o +#arm-none-eabi-ld -EB -n -Tdebug_handler.cmd debug_handler_be.o -o debug_handler_be.out +#arm-none-eabi-objcopy -O binary debug_handler_be.out debug_handler_be.bin diff --git a/src/target/xscale/debug_handler.S b/src/target/xscale/debug_handler.S new file mode 100644 index 00000000..6d9b1cd3 --- /dev/null +++ b/src/target/xscale/debug_handler.S @@ -0,0 +1,718 @@ +/*************************************************************************** + * Copyright (C) 2006 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "protocol.h" + + .text + .align 4 + +@ Disable thumb mode + .code 32 + +@ send word to debugger +.macro m_send_to_debugger reg +1: + mrc p14, 0, r15, c14, c0, 0 + bvs 1b + mcr p14, 0, \reg, c8, c0, 0 +.endm + +@ receive word from debugger +.macro m_receive_from_debugger reg +1: + mrc p14, 0, r15, c14, c0, 0 + bpl 1b + mrc p14, 0, \reg, c9, c0, 0 +.endm + +@ save register on debugger, small +.macro m_small_save_reg reg + mov r0, \reg + bl send_to_debugger +.endm + +@ save status register on debugger, small +.macro m_small_save_psr + mrs r0, spsr + bl send_to_debugger +.endm + +@ wait for all outstanding coprocessor accesses to complete +.macro m_cpwait + mrc p15, 0, r0, c2, c0, 0 + mov r0, r0 + sub pc, pc, #4 +.endm + +.global reset_handler +.global undef_handler +.global swi_handler +.global prefetch_abort_handler +.global data_abort_handler +.global irq_handler +.global fiq_handler + +.section .part1 , "ax" + +reset_handler: + @ read DCSR + mrc p14, 0, r13, c10, c0 + @ check if global enable bit (GE) is set + ands r13, r13, #0x80000000 + + bne debug_handler + + @ set global enable bit (GE) + mov r13, #0xc0000000 + mcr p14, 0, r13, c10, c0 + +debug_handler: + + @ save r0 without modifying other registers + m_send_to_debugger r0 + + @ save lr (program PC) without branching (use macro) + m_send_to_debugger r14 + + @ save non-banked registers and spsr (program CPSR) + m_small_save_reg r1 + m_small_save_reg r2 + m_small_save_reg r3 + m_small_save_reg r4 + m_small_save_reg r5 + m_small_save_reg r6 + m_small_save_reg r7 + m_small_save_psr + + mrs r0, spsr + + @ prepare program PSR for debug use (clear Thumb, set I/F to disable interrupts) + bic r0, r0, #PSR_T + orr r0, r0, #(PSR_I | PSR_F) + + @ examine mode bits + and r1, r0, #MODE_MASK + cmp r1, #MODE_USR + + bne not_user_mode + + @ replace USR mode with SYS + bic r0, r0, #MODE_MASK + orr r0, r0, #MODE_SYS + +not_user_mode: + + b save_banked_registers + +@ command loop +@ wait for command from debugger, than execute desired function +get_command: + bl receive_from_debugger + + @ 0x0n - register access + cmp r0, #0x0 + beq get_banked_registers + + cmp r0, #0x1 + beq set_banked_registers + + @ 0x1n - read memory + cmp r0, #0x11 + beq read_byte + + cmp r0, #0x12 + beq read_half_word + + cmp r0, #0x14 + beq read_word + + @ 0x2n - write memory + cmp r0, #0x21 + beq write_byte + + cmp r0, #0x22 + beq write_half_word + + cmp r0, #0x24 + beq write_word + + @ 0x3n - program execution + cmp r0, #0x30 + beq resume + + cmp r0, #0x31 + beq resume_w_trace + + @ 0x4n - coprocessor access + cmp r0, #0x40 + beq read_cp_reg + + cmp r0, #0x41 + beq write_cp_reg + + @ 0x5n - cache and mmu functions + cmp r0, #0x50 + beq clean_d_cache + + cmp r0, #0x51 + beq invalidate_d_cache + + cmp r0, #0x52 + beq invalidate_i_cache + + cmp r0, #0x53 + beq cpwait + + @ 0x6n - misc functions + cmp r0, #0x60 + beq clear_sa + + cmp r0, #0x61 + beq read_trace_buffer + + cmp r0, #0x62 + beq clean_trace_buffer + + @ return (back to get_command) + b get_command + +@ ---- + +@ resume program execution +resume: + @ restore CPSR (SPSR_dbg) + bl receive_from_debugger + msr spsr, r0 + + @ restore registers (r7 - r0) + bl receive_from_debugger @ r7 + mov r7, r0 + bl receive_from_debugger @ r6 + mov r6, r0 + bl receive_from_debugger @ r5 + mov r5, r0 + bl receive_from_debugger @ r4 + mov r4, r0 + bl receive_from_debugger @ r3 + mov r3, r0 + bl receive_from_debugger @ r2 + mov r2, r0 + bl receive_from_debugger @ r1 + mov r1, r0 + bl receive_from_debugger @ r0 + + @ resume addresss + m_receive_from_debugger lr + + @ branch back to application code, restoring CPSR + subs pc, lr, #0 + +@ get banked registers +@ receive mode bits from host, then run into save_banked_registers to + +get_banked_registers: + bl receive_from_debugger + +@ save banked registers +@ r0[4:0]: desired mode bits +save_banked_registers: + @ backup CPSR + mrs r7, cpsr + msr cpsr_c, r0 + nop + + @ keep current mode bits in r1 for later use + and r1, r0, #MODE_MASK + + @ backup banked registers + m_send_to_debugger r8 + m_send_to_debugger r9 + m_send_to_debugger r10 + m_send_to_debugger r11 + m_send_to_debugger r12 + m_send_to_debugger r13 + m_send_to_debugger r14 + + @ if not in SYS mode (or USR, which we replaced with SYS before) + cmp r1, #MODE_SYS + + beq no_spsr_to_save + + @ backup SPSR + mrs r0, spsr + m_send_to_debugger r0 + +no_spsr_to_save: + + @ restore CPSR for SDS + msr cpsr_c, r7 + nop + + @ return + b get_command + +@ ---- + + +@ set banked registers +@ receive mode bits from host, then run into save_banked_registers to + +set_banked_registers: + bl receive_from_debugger + +@ restore banked registers +@ r0[4:0]: desired mode bits +restore_banked_registers: + @ backup CPSR + mrs r7, cpsr + msr cpsr_c, r0 + nop + + @ keep current mode bits in r1 for later use + and r1, r0, #MODE_MASK + + @ set banked registers + m_receive_from_debugger r8 + m_receive_from_debugger r9 + m_receive_from_debugger r10 + m_receive_from_debugger r11 + m_receive_from_debugger r12 + m_receive_from_debugger r13 + m_receive_from_debugger r14 + + @ if not in SYS mode (or USR, which we replaced with SYS before) + cmp r1, #MODE_SYS + + beq no_spsr_to_restore + + @ set SPSR + m_receive_from_debugger r0 + msr spsr, r0 + +no_spsr_to_restore: + + @ restore CPSR for SDS + msr cpsr_c, r7 + nop + + @ return + b get_command + +@ ---- + +read_byte: + @ r2: address + bl receive_from_debugger + mov r2, r0 + + @ r1: count + bl receive_from_debugger + mov r1, r0 + +rb_loop: + ldrb r0, [r2], #1 + + @ drain write- (and fill-) buffer to work around XScale errata + mcr p15, 0, r8, c7, c10, 4 + + bl send_to_debugger + + subs r1, r1, #1 + bne rb_loop + + @ return + b get_command + +@ ---- + +read_half_word: + @ r2: address + bl receive_from_debugger + mov r2, r0 + + @ r1: count + bl receive_from_debugger + mov r1, r0 + +rh_loop: + ldrh r0, [r2], #2 + + @ drain write- (and fill-) buffer to work around XScale errata + mcr p15, 0, r8, c7, c10, 4 + + bl send_to_debugger + + subs r1, r1, #1 + bne rh_loop + + @ return + b get_command + +@ ---- + +read_word: + @ r2: address + bl receive_from_debugger + mov r2, r0 + + @ r1: count + bl receive_from_debugger + mov r1, r0 + +rw_loop: + ldr r0, [r2], #4 + + @ drain write- (and fill-) buffer to work around XScale errata + mcr p15, 0, r8, c7, c10, 4 + + bl send_to_debugger + + subs r1, r1, #1 + bne rw_loop + + @ return + b get_command + +@ ---- + +write_byte: + @ r2: address + bl receive_from_debugger + mov r2, r0 + + @ r1: count + bl receive_from_debugger + mov r1, r0 + +wb_loop: + bl receive_from_debugger + strb r0, [r2], #1 + + @ drain write- (and fill-) buffer to work around XScale errata + mcr p15, 0, r8, c7, c10, 4 + + subs r1, r1, #1 + bne wb_loop + + @ return + b get_command + +@ ---- + +write_half_word: + @ r2: address + bl receive_from_debugger + mov r2, r0 + + @ r1: count + bl receive_from_debugger + mov r1, r0 + +wh_loop: + bl receive_from_debugger + strh r0, [r2], #2 + + @ drain write- (and fill-) buffer to work around XScale errata + mcr p15, 0, r8, c7, c10, 4 + + subs r1, r1, #1 + bne wh_loop + + @ return + b get_command + +@ ---- + +write_word: + @ r2: address + bl receive_from_debugger + mov r2, r0 + + @ r1: count + bl receive_from_debugger + mov r1, r0 + +ww_loop: + bl receive_from_debugger + str r0, [r2], #4 + + @ drain write- (and fill-) buffer to work around XScale errata + mcr p15, 0, r8, c7, c10, 4 + + subs r1, r1, #1 + bne ww_loop + + @ return + b get_command + +@ ---- + +clear_sa: + @ read DCSR + mrc p14, 0, r0, c10, c0 + + @ clear SA bit + bic r0, r0, #0x20 + + @ write DCSR + mcr p14, 0, r0, c10, c0 + + @ return + b get_command + +@ ---- + +clean_d_cache: + @ r0: cache clean area + bl receive_from_debugger + + mov r1, #1024 +clean_loop: + mcr p15, 0, r0, c7, c2, 5 + add r0, r0, #32 + subs r1, r1, #1 + bne clean_loop + + @ return + b get_command + +@ ---- + +invalidate_d_cache: + mcr p15, 0, r0, c7, c6, 0 + + @ return + b get_command + +@ ---- + +invalidate_i_cache: + mcr p15, 0, r0, c7, c5, 0 + + @ return + b get_command + +@ ---- + +cpwait: + m_cpwait + + @return + b get_command + +@ ---- + +.section .part2 , "ax" + +read_cp_reg: + @ requested cp register + bl receive_from_debugger + + adr r1, read_cp_table + add pc, r1, r0, lsl #3 + +read_cp_table: + mrc p15, 0, r0, c0, c0, 0 @ XSCALE_MAINID + b read_cp_reg_reply + mrc p15, 0, r0, c0, c0, 1 @ XSCALE_CACHETYPE + b read_cp_reg_reply + mrc p15, 0, r0, c1, c0, 0 @ XSCALE_CTRL + b read_cp_reg_reply + mrc p15, 0, r0, c1, c0, 1 @ XSCALE_AUXCTRL + b read_cp_reg_reply + mrc p15, 0, r0, c2, c0, 0 @ XSCALE_TTB + b read_cp_reg_reply + mrc p15, 0, r0, c3, c0, 0 @ XSCALE_DAC + b read_cp_reg_reply + mrc p15, 0, r0, c5, c0, 0 @ XSCALE_FSR + b read_cp_reg_reply + mrc p15, 0, r0, c6, c0, 0 @ XSCALE_FAR + b read_cp_reg_reply + mrc p15, 0, r0, c13, c0, 0 @ XSCALE_PID + b read_cp_reg_reply + mrc p15, 0, r0, c15, c0, 0 @ XSCALE_CP_ACCESS + b read_cp_reg_reply + mrc p15, 0, r0, c14, c8, 0 @ XSCALE_IBCR0 + b read_cp_reg_reply + mrc p15, 0, r0, c14, c9, 0 @ XSCALE_IBCR1 + b read_cp_reg_reply + mrc p15, 0, r0, c14, c0, 0 @ XSCALE_DBR0 + b read_cp_reg_reply + mrc p15, 0, r0, c14, c3, 0 @ XSCALE_DBR1 + b read_cp_reg_reply + mrc p15, 0, r0, c14, c4, 0 @ XSCALE_DBCON + b read_cp_reg_reply + mrc p14, 0, r0, c11, c0, 0 @ XSCALE_TBREG + b read_cp_reg_reply + mrc p14, 0, r0, c12, c0, 0 @ XSCALE_CHKPT0 + b read_cp_reg_reply + mrc p14, 0, r0, c13, c0, 0 @ XSCALE_CHKPT1 + b read_cp_reg_reply + mrc p14, 0, r0, c10, c0, 0 @ XSCALE_DCSR + b read_cp_reg_reply + +read_cp_reg_reply: + bl send_to_debugger + + @ return + b get_command + +@ ---- + +write_cp_reg: + @ requested cp register + bl receive_from_debugger + mov r1, r0 + + @ value to be written + bl receive_from_debugger + + adr r2, write_cp_table + add pc, r2, r1, lsl #3 + +write_cp_table: + mcr p15, 0, r0, c0, c0, 0 @ XSCALE_MAINID (0x0) + b get_command + mcr p15, 0, r0, c0, c0, 1 @ XSCALE_CACHETYPE (0x1) + b get_command + mcr p15, 0, r0, c1, c0, 0 @ XSCALE_CTRL (0x2) + b get_command + mcr p15, 0, r0, c1, c0, 1 @ XSCALE_AUXCTRL (0x3) + b get_command + mcr p15, 0, r0, c2, c0, 0 @ XSCALE_TTB (0x4) + b get_command + mcr p15, 0, r0, c3, c0, 0 @ XSCALE_DAC (0x5) + b get_command + mcr p15, 0, r0, c5, c0, 0 @ XSCALE_FSR (0x6) + b get_command + mcr p15, 0, r0, c6, c0, 0 @ XSCALE_FAR (0x7) + b get_command + mcr p15, 0, r0, c13, c0, 0 @ XSCALE_PID (0x8) + b get_command + mcr p15, 0, r0, c15, c0, 0 @ XSCALE_CP_ACCESS (0x9) + b get_command + mcr p15, 0, r0, c14, c8, 0 @ XSCALE_IBCR0 (0xa) + b get_command + mcr p15, 0, r0, c14, c9, 0 @ XSCALE_IBCR1 (0xb) + b get_command + mcr p15, 0, r0, c14, c0, 0 @ XSCALE_DBR0 (0xc) + b get_command + mcr p15, 0, r0, c14, c3, 0 @ XSCALE_DBR1 (0xd) + b get_command + mcr p15, 0, r0, c14, c4, 0 @ XSCALE_DBCON (0xe) + b get_command + mcr p14, 0, r0, c11, c0, 0 @ XSCALE_TBREG (0xf) + b get_command + mcr p14, 0, r0, c12, c0, 0 @ XSCALE_CHKPT0 (0x10) + b get_command + mcr p14, 0, r0, c13, c0, 0 @ XSCALE_CHKPT1 (0x11) + b get_command + mcr p14, 0, r0, c10, c0, 0 @ XSCALE_DCSR (0x12) + b get_command + +@ ---- + +read_trace_buffer: + + @ dump 256 entries from trace buffer + mov r1, #256 +read_tb_loop: + mrc p14, 0, r0, c11, c0, 0 @ XSCALE_TBREG + bl send_to_debugger + subs r1, r1, #1 + bne read_tb_loop + + @ dump checkpoint register 0 + mrc p14, 0, r0, c12, c0, 0 @ XSCALE_CHKPT0 (0x10) + bl send_to_debugger + + @ dump checkpoint register 1 + mrc p14, 0, r0, c13, c0, 0 @ XSCALE_CHKPT1 (0x11) + bl send_to_debugger + + @ return + b get_command + +@ ---- + +clean_trace_buffer: + + @ clean 256 entries from trace buffer + mov r1, #256 +clean_tb_loop: + mrc p14, 0, r0, c11, c0, 0 @ XSCALE_TBREG + subs r1, r1, #1 + bne clean_tb_loop + + @ return + b get_command + +@ ---- + + +@ resume program execution with trace buffer enabled +resume_w_trace: + @ restore CPSR (SPSR_dbg) + bl receive_from_debugger + msr spsr, r0 + + @ restore registers (r7 - r0) + bl receive_from_debugger @ r7 + mov r7, r0 + bl receive_from_debugger @ r6 + mov r6, r0 + bl receive_from_debugger @ r5 + mov r5, r0 + bl receive_from_debugger @ r4 + mov r4, r0 + bl receive_from_debugger @ r3 + mov r3, r0 + bl receive_from_debugger @ r2 + mov r2, r0 + bl receive_from_debugger @ r1 + mov r1, r0 + bl receive_from_debugger @ r0 + + @ resume addresss + m_receive_from_debugger lr + + mrc p14, 0, r13, c10, c0, 0 @ XSCALE_DCSR + orr r13, r13, #1 + mcr p14, 0, r13, c10, c0, 0 @ XSCALE_DCSR + + @ branch back to application code, restoring CPSR + subs pc, lr, #0 + +undef_handler: +swi_handler: +prefetch_abort_handler: +data_abort_handler: +irq_handler: +fiq_handler: +1: + b 1b + +send_to_debugger: + m_send_to_debugger r0 + mov pc, lr + +receive_from_debugger: + m_receive_from_debugger r0 + mov pc, lr + diff --git a/src/target/xscale/debug_handler.cmd b/src/target/xscale/debug_handler.cmd new file mode 100644 index 00000000..183c2020 --- /dev/null +++ b/src/target/xscale/debug_handler.cmd @@ -0,0 +1,49 @@ +/* identify the Entry Point */ +ENTRY(reset_handler) + +/* specify the mini-ICache memory areas */ +MEMORY +{ + mini_icache_0 (x) : ORIGIN = 0x0, LENGTH = 1024 /* first part of mini icache (sets 0-31) */ + mini_icache_1 (x) : ORIGIN = 0x400, LENGTH = 1024 /* second part of mini icache (sets 0-31) */ +} + +/* now define the output sections */ +SECTIONS +{ + .part1 : + { + LONG(0) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + *(.part1) + } >mini_icache_0 + + .part2 : + { + LONG(0) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + LONG(0) + *(.part2) + FILL(0x0) + } >mini_icache_1 + + /DISCARD/ : + { + *(.text) + *(.glue_7) + *(.glue_7t) + *(.data) + *(.bss) + } +} diff --git a/src/target/xscale/protocol.h b/src/target/xscale/protocol.h new file mode 100644 index 00000000..525db31d --- /dev/null +++ b/src/target/xscale/protocol.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2006 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define REG_R0 0 +#define REG_R1 1 +#define REG_R2 2 +#define REG_R3 3 +#define REG_R4 4 +#define REG_R5 5 +#define REG_R6 6 +#define REG_R7 7 +#define REG_R8 8 +#define REG_R9 9 +#define REG_R10 10 +#define REG_R11 11 +#define REG_R12 12 +#define REG_R13 13 +#define REG_R14 14 +#define REG_R15 15 +#define REG_CPSR 16 +#define REG_SPSR 17 + +#define MODE_USR 0x10 +#define MODE_FIQ 0x11 +#define MODE_IRQ 0x12 +#define MODE_SVC 0x13 +#define MODE_ABT 0x17 +#define MODE_UND 0x1b +#define MODE_SYS 0x1f + +#define MODE_ANY 0x40 +#define MODE_CURRENT 0x80 + +#define MODE_MASK 0x1f +#define PSR_I 0x80 +#define PSR_F 0x40 +#define PSR_T 0x20 + +#define XSCALE_DBG_MAINID 0x0 +#define XSCALE_DBG_CACHETYPE 0x1 +#define XSCALE_DBG_CTRL 0x2 +#define XSCALE_DBG_AUXCTRL 0x3 +#define XSCALE_DBG_TTB 0x4 +#define XSCALE_DBG_DAC 0x5 +#define XSCALE_DBG_FSR 0x6 +#define XSCALE_DBG_FAR 0x7 +#define XSCALE_DBG_PID 0x8 +#define XSCALE_DBG_CPACCESS 0x9 +#define XSCALE_DBG_IBCR0 0xa +#define XSCALE_DBG_IBCR1 0xb +#define XSCALE_DBG_DBR0 0xc +#define XSCALE_DBG_DBR1 0xd +#define XSCALE_DBG_DBCON 0xe |