From: David Ung Date: Fri, 16 Jan 2015 01:22:20 +0000 (-0800) Subject: aarch64: Add ARMv8 AARCH64 support files X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=d376f7f51831ca8816bb4aca00076b0668462775;p=openocd aarch64: Add ARMv8 AARCH64 support files Add new AARCH64 target and ARMv8 support files. This is an instantiation from the cortex_a files but modified to support 64bit ARMv8. Not all features are complete, notably breakpts and single stepping are not yet implemented. Currently it lets you halt of the processors, resume, dump cpu registers, read/write memory and getting a stack trace with gdb. > halt invalid mode value encountered 5 target state: halted unrecognized psr mode: 0x5 target halted in ARM state due to debug-request, current mode: UNRECOGNIZED cpsr: 0x600001c5 pc: 0x00093528 MMU: disabled, D-Cache: disabled, I-Cache: disabled > targets TargetName Type Endian TapName State -- ------------------ ---------- ------ ------------------ ------------ 0* cpu0 aarch64 little cpu.dap halted > reg ===== arm v8 registers (0) r0 (/64): 0x00000000FFFFFFED (dirty) (1) r1 (/64): 0x00000000F76E4000 (2) r2 (/64): 0x0000000000000000 (3) r3 (/64): 0x0000000000010000 (4) r4 (/64): 0xFFFFFFC06E2939E1 (5) r5 (/64): 0x0000000000000018 (6) r6 (/64): 0x003A699CFB3C8480 (7) r7 (/64): 0x0000000053555555 (8) r8 (/64): 0x00FFFFFFFFFFFFFF (9) r9 (/64): 0x000000001FFEF992 (10) r10 (/64): 0x0000000000000001 (11) r11 (/64): 0x0000000000000000 (12) r12 (/64): 0x00000000000000F0 (13) r13 (/64): 0x00000000EFDFEAC8 (14) r14 (/64): 0x00000000F6DDA659 (15) r15 (/64): 0x0000000000000000 (16) r16 (/64): 0xFFFFFFC0000F9094 (17) r17 (/64): 0x0000000000000000 (18) r18 (/64): 0x0000000000000000 (19) r19 (/64): 0xFFFFFFC00087C000 (20) r20 (/64): 0x0000000000000002 (21) r21 (/64): 0xFFFFFFC000867C28 (22) r22 (/64): 0xFFFFFFC000916A52 (23) r23 (/64): 0xFFFFFFC00116D8B0 (24) r24 (/64): 0xFFFFFFC000774A0C (25) r25 (/64): 0x000000008007B000 (26) r26 (/64): 0x000000008007D000 (27) r27 (/64): 0xFFFFFFC000080450 (28) r28 (/64): 0x0000004080000000 (29) r29 (/64): 0xFFFFFFC00087FF20 (30) r30 (/64): 0xFFFFFFC000085114 (31) sp (/64): 0xFFFFFFC00087FF20 (32) pc (/64): 0xFFFFFFC000093528 (33) xPSR (/64): 0x00000000600001C5 And from gdb (gdb) bt #0 cpu_do_idle () at /mnt/host/source/src/third_party/kernel/3.14/arch/arm64/mm/proc.S:87 #1 0xffffffc000085114 in arch_cpu_idle () at /mnt/host/source/src/third_party/kernel/3.14/arch/arm64/kernel/process.c:107 #2 0x0000000000000000 in ?? () Change-Id: Iccb1d15c7d8ace7b9e811dac3c9757ced4d0f618 Signed-off-by: David Ung Signed-off-by: Matthias Welwarsky --- diff --git a/src/target/Makefile.am b/src/target/Makefile.am index eb0d62ed..a17496cd 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -26,6 +26,10 @@ noinst_LTLIBRARIES += %D%/libtarget.la %D%/dsp5680xx.c \ %D%/hla_target.c +if TARGET64 +%C%_libtarget_la_SOURCES +=$(ARMV8_SRC) +endif + TARGET_CORE_SRC = \ %D%/algorithm.c \ %D%/register.c \ @@ -69,6 +73,10 @@ ARMV7_SRC = \ %D%/cortex_a.c \ %D%/ls1_sap.c +ARMV8_SRC = \ + %D%/aarch64.c \ + %D%/armv8.c + ARM_DEBUG_SRC = \ %D%/arm_dpm.c \ %D%/arm_jtag.c \ @@ -146,6 +154,7 @@ INTEL_IA32_SRC = \ %D%/armv7a.h \ %D%/armv7m.h \ %D%/armv7m_trace.h \ + %D%/armv8.h \ %D%/avrt.h \ %D%/dsp563xx.h \ %D%/dsp563xx_once.h \ @@ -153,6 +162,7 @@ INTEL_IA32_SRC = \ %D%/breakpoints.h \ %D%/cortex_m.h \ %D%/cortex_a.h \ + %D%/aarch64.h \ %D%/embeddedice.h \ %D%/etb.h \ %D%/etm.h \ diff --git a/src/target/aarch64.c b/src/target/aarch64.c new file mode 100644 index 00000000..8ac64eb4 --- /dev/null +++ b/src/target/aarch64.c @@ -0,0 +1,2601 @@ +/*************************************************************************** + * Copyright (C) 2015 by David Ung * + * * + * 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., * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "breakpoints.h" +#include "aarch64.h" +#include "register.h" +#include "target_request.h" +#include "target_type.h" +#include "arm_opcodes.h" +#include + +static int aarch64_poll(struct target *target); +static int aarch64_debug_entry(struct target *target); +static int aarch64_restore_context(struct target *target, bool bpwp); +static int aarch64_set_breakpoint(struct target *target, + struct breakpoint *breakpoint, uint8_t matchmode); +static int aarch64_set_context_breakpoint(struct target *target, + struct breakpoint *breakpoint, uint8_t matchmode); +static int aarch64_set_hybrid_breakpoint(struct target *target, + struct breakpoint *breakpoint); +static int aarch64_unset_breakpoint(struct target *target, + struct breakpoint *breakpoint); +static int aarch64_mmu(struct target *target, int *enabled); +static int aarch64_virt2phys(struct target *target, + target_addr_t virt, target_addr_t *phys); +static int aarch64_read_apb_ab_memory(struct target *target, + uint64_t address, uint32_t size, uint32_t count, uint8_t *buffer); +static int aarch64_instr_write_data_r0(struct arm_dpm *dpm, + uint32_t opcode, uint32_t data); + +static int aarch64_restore_system_control_reg(struct target *target) +{ + int retval = ERROR_OK; + + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = target_to_armv8(target); + + if (aarch64->system_control_reg != aarch64->system_control_reg_curr) { + aarch64->system_control_reg_curr = aarch64->system_control_reg; + retval = aarch64_instr_write_data_r0(armv8->arm.dpm, + 0xd5181000, + aarch64->system_control_reg); + } + + return retval; +} + +/* check address before aarch64_apb read write access with mmu on + * remove apb predictible data abort */ +static int aarch64_check_address(struct target *target, uint32_t address) +{ + /* TODO */ + return ERROR_OK; +} +/* modify system_control_reg in order to enable or disable mmu for : + * - virt2phys address conversion + * - read or write memory in phys or virt address */ +static int aarch64_mmu_modify(struct target *target, int enable) +{ + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = &aarch64->armv8_common; + int retval = ERROR_OK; + + if (enable) { + /* if mmu enabled at target stop and mmu not enable */ + if (!(aarch64->system_control_reg & 0x1U)) { + LOG_ERROR("trying to enable mmu on target stopped with mmu disable"); + return ERROR_FAIL; + } + if (!(aarch64->system_control_reg_curr & 0x1U)) { + aarch64->system_control_reg_curr |= 0x1U; + retval = aarch64_instr_write_data_r0(armv8->arm.dpm, + 0xd5181000, + aarch64->system_control_reg_curr); + } + } else { + if (aarch64->system_control_reg_curr & 0x4U) { + /* data cache is active */ + aarch64->system_control_reg_curr &= ~0x4U; + /* flush data cache armv7 function to be called */ + if (armv8->armv8_mmu.armv8_cache.flush_all_data_cache) + armv8->armv8_mmu.armv8_cache.flush_all_data_cache(target); + } + if ((aarch64->system_control_reg_curr & 0x1U)) { + aarch64->system_control_reg_curr &= ~0x1U; + retval = aarch64_instr_write_data_r0(armv8->arm.dpm, + 0xd5181000, + aarch64->system_control_reg_curr); + } + } + return retval; +} + +/* + * Basic debug access, very low level assumes state is saved + */ +static int aarch64_init_debug_access(struct target *target) +{ + struct armv8_common *armv8 = target_to_armv8(target); + int retval; + uint32_t dummy; + + LOG_DEBUG(" "); + + /* Unlocking the debug registers for modification + * The debugport might be uninitialised so try twice */ + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_LOCKACCESS, 0xC5ACCE55); + if (retval != ERROR_OK) { + /* try again */ + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_LOCKACCESS, 0xC5ACCE55); + if (retval == ERROR_OK) + LOG_USER("Locking debug access failed on first, but succeeded on second try."); + } + if (retval != ERROR_OK) + return retval; + /* Clear Sticky Power Down status Bit in PRSR to enable access to + the registers in the Core Power Domain */ + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_PRSR, &dummy); + if (retval != ERROR_OK) + return retval; + + /* Enabling of instruction execution in debug mode is done in debug_entry code */ + + /* Resync breakpoint registers */ + + /* Since this is likely called from init or reset, update target state information*/ + return aarch64_poll(target); +} + +/* To reduce needless round-trips, pass in a pointer to the current + * DSCR value. Initialize it to zero if you just need to know the + * value on return from this function; or DSCR_INSTR_COMP if you + * happen to know that no instruction is pending. + */ +static int aarch64_exec_opcode(struct target *target, + uint32_t opcode, uint32_t *dscr_p) +{ + uint32_t dscr; + int retval; + struct armv8_common *armv8 = target_to_armv8(target); + dscr = dscr_p ? *dscr_p : 0; + + LOG_DEBUG("exec opcode 0x%08" PRIx32, opcode); + + /* Wait for InstrCompl bit to be set */ + long long then = timeval_ms(); + while ((dscr & DSCR_INSTR_COMP) == 0) { + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read DSCR register, opcode = 0x%08" PRIx32, opcode); + return retval; + } + if (timeval_ms() > then + 1000) { + LOG_ERROR("Timeout waiting for aarch64_exec_opcode"); + return ERROR_FAIL; + } + } + + retval = mem_ap_write_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_ITR, opcode); + if (retval != ERROR_OK) + return retval; + + then = timeval_ms(); + do { + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read DSCR register"); + return retval; + } + if (timeval_ms() > then + 1000) { + LOG_ERROR("Timeout waiting for aarch64_exec_opcode"); + return ERROR_FAIL; + } + } while ((dscr & DSCR_INSTR_COMP) == 0); /* Wait for InstrCompl bit to be set */ + + if (dscr_p) + *dscr_p = dscr; + + return retval; +} + +/* Write to memory mapped registers directly with no cache or mmu handling */ +static int aarch64_dap_write_memap_register_u32(struct target *target, + uint32_t address, + uint32_t value) +{ + int retval; + struct armv8_common *armv8 = target_to_armv8(target); + + retval = mem_ap_write_atomic_u32(armv8->debug_ap, address, value); + + return retval; +} + +/* + * AARCH64 implementation of Debug Programmer's Model + * + * NOTE the invariant: these routines return with DSCR_INSTR_COMP set, + * so there's no need to poll for it before executing an instruction. + * + * NOTE that in several of these cases the "stall" mode might be useful. + * It'd let us queue a few operations together... prepare/finish might + * be the places to enable/disable that mode. + */ + +static inline struct aarch64_common *dpm_to_a8(struct arm_dpm *dpm) +{ + return container_of(dpm, struct aarch64_common, armv8_common.dpm); +} + +static int aarch64_write_dcc(struct aarch64_common *a8, uint32_t data) +{ + LOG_DEBUG("write DCC 0x%08" PRIx32, data); + return mem_ap_write_u32(a8->armv8_common.debug_ap, + a8->armv8_common.debug_base + CPUDBG_DTRRX, data); +} + +static int aarch64_write_dcc_64(struct aarch64_common *a8, uint64_t data) +{ + int ret; + LOG_DEBUG("write DCC 0x%08" PRIx32, (unsigned)data); + LOG_DEBUG("write DCC 0x%08" PRIx32, (unsigned)(data >> 32)); + ret = mem_ap_write_u32(a8->armv8_common.debug_ap, + a8->armv8_common.debug_base + CPUDBG_DTRRX, data); + ret += mem_ap_write_u32(a8->armv8_common.debug_ap, + a8->armv8_common.debug_base + CPUDBG_DTRTX, data >> 32); + return ret; +} + +static int aarch64_read_dcc(struct aarch64_common *a8, uint32_t *data, + uint32_t *dscr_p) +{ + uint32_t dscr = DSCR_INSTR_COMP; + int retval; + + if (dscr_p) + dscr = *dscr_p; + + /* Wait for DTRRXfull */ + long long then = timeval_ms(); + while ((dscr & DSCR_DTR_TX_FULL) == 0) { + retval = mem_ap_read_atomic_u32(a8->armv8_common.debug_ap, + a8->armv8_common.debug_base + CPUDBG_DSCR, + &dscr); + if (retval != ERROR_OK) + return retval; + if (timeval_ms() > then + 1000) { + LOG_ERROR("Timeout waiting for read dcc"); + return ERROR_FAIL; + } + } + + retval = mem_ap_read_atomic_u32(a8->armv8_common.debug_ap, + a8->armv8_common.debug_base + CPUDBG_DTRTX, + data); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("read DCC 0x%08" PRIx32, *data); + + if (dscr_p) + *dscr_p = dscr; + + return retval; +} +static int aarch64_read_dcc_64(struct aarch64_common *a8, uint64_t *data, + uint32_t *dscr_p) +{ + uint32_t dscr = DSCR_INSTR_COMP; + uint32_t higher; + int retval; + + if (dscr_p) + dscr = *dscr_p; + + /* Wait for DTRRXfull */ + long long then = timeval_ms(); + while ((dscr & DSCR_DTR_TX_FULL) == 0) { + retval = mem_ap_read_atomic_u32(a8->armv8_common.debug_ap, + a8->armv8_common.debug_base + CPUDBG_DSCR, + &dscr); + if (retval != ERROR_OK) + return retval; + if (timeval_ms() > then + 1000) { + LOG_ERROR("Timeout waiting for read dcc"); + return ERROR_FAIL; + } + } + + retval = mem_ap_read_atomic_u32(a8->armv8_common.debug_ap, + a8->armv8_common.debug_base + CPUDBG_DTRTX, + (uint32_t *)data); + if (retval != ERROR_OK) + return retval; + + retval = mem_ap_read_atomic_u32(a8->armv8_common.debug_ap, + a8->armv8_common.debug_base + CPUDBG_DTRRX, + &higher); + if (retval != ERROR_OK) + return retval; + + *data = *(uint32_t *)data | (uint64_t)higher << 32; + LOG_DEBUG("read DCC 0x%16.16" PRIx64, *data); + + if (dscr_p) + *dscr_p = dscr; + + return retval; +} + +static int aarch64_dpm_prepare(struct arm_dpm *dpm) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + uint32_t dscr; + int retval; + + /* set up invariant: INSTR_COMP is set after ever DPM operation */ + long long then = timeval_ms(); + for (;; ) { + retval = mem_ap_read_atomic_u32(a8->armv8_common.debug_ap, + a8->armv8_common.debug_base + CPUDBG_DSCR, + &dscr); + if (retval != ERROR_OK) + return retval; + if ((dscr & DSCR_INSTR_COMP) != 0) + break; + if (timeval_ms() > then + 1000) { + LOG_ERROR("Timeout waiting for dpm prepare"); + return ERROR_FAIL; + } + } + + /* this "should never happen" ... */ + if (dscr & DSCR_DTR_RX_FULL) { + LOG_ERROR("DSCR_DTR_RX_FULL, dscr 0x%08" PRIx32, dscr); + /* Clear DCCRX */ + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + 0xd5130400, + &dscr); + if (retval != ERROR_OK) + return retval; + } + + return retval; +} + +static int aarch64_dpm_finish(struct arm_dpm *dpm) +{ + /* REVISIT what could be done here? */ + return ERROR_OK; +} + +static int aarch64_instr_write_data_dcc(struct arm_dpm *dpm, + uint32_t opcode, uint32_t data) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + int retval; + uint32_t dscr = DSCR_INSTR_COMP; + + retval = aarch64_write_dcc(a8, data); + if (retval != ERROR_OK) + return retval; + + return aarch64_exec_opcode( + a8->armv8_common.arm.target, + opcode, + &dscr); +} + +static int aarch64_instr_write_data_dcc_64(struct arm_dpm *dpm, + uint32_t opcode, uint64_t data) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + int retval; + uint32_t dscr = DSCR_INSTR_COMP; + + retval = aarch64_write_dcc_64(a8, data); + if (retval != ERROR_OK) + return retval; + + return aarch64_exec_opcode( + a8->armv8_common.arm.target, + opcode, + &dscr); +} + +static int aarch64_instr_write_data_r0(struct arm_dpm *dpm, + uint32_t opcode, uint32_t data) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + uint32_t dscr = DSCR_INSTR_COMP; + int retval; + + retval = aarch64_write_dcc(a8, data); + if (retval != ERROR_OK) + return retval; + + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + 0xd5330500, + &dscr); + if (retval != ERROR_OK) + return retval; + + /* then the opcode, taking data from R0 */ + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + opcode, + &dscr); + + return retval; +} + +static int aarch64_instr_write_data_r0_64(struct arm_dpm *dpm, + uint32_t opcode, uint64_t data) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + uint32_t dscr = DSCR_INSTR_COMP; + int retval; + + retval = aarch64_write_dcc_64(a8, data); + if (retval != ERROR_OK) + return retval; + + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + 0xd5330400, + &dscr); + if (retval != ERROR_OK) + return retval; + + /* then the opcode, taking data from R0 */ + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + opcode, + &dscr); + + return retval; +} + +static int aarch64_instr_cpsr_sync(struct arm_dpm *dpm) +{ + struct target *target = dpm->arm->target; + uint32_t dscr = DSCR_INSTR_COMP; + + /* "Prefetch flush" after modifying execution status in CPSR */ + return aarch64_exec_opcode(target, + ARMV4_5_MCR(15, 0, 0, 7, 5, 4), + &dscr); +} + +static int aarch64_instr_read_data_dcc(struct arm_dpm *dpm, + uint32_t opcode, uint32_t *data) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + int retval; + uint32_t dscr = DSCR_INSTR_COMP; + + /* the opcode, writing data to DCC */ + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + opcode, + &dscr); + if (retval != ERROR_OK) + return retval; + + return aarch64_read_dcc(a8, data, &dscr); +} + +static int aarch64_instr_read_data_dcc_64(struct arm_dpm *dpm, + uint32_t opcode, uint64_t *data) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + int retval; + uint32_t dscr = DSCR_INSTR_COMP; + + /* the opcode, writing data to DCC */ + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + opcode, + &dscr); + if (retval != ERROR_OK) + return retval; + + return aarch64_read_dcc_64(a8, data, &dscr); +} + +static int aarch64_instr_read_data_r0(struct arm_dpm *dpm, + uint32_t opcode, uint32_t *data) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + uint32_t dscr = DSCR_INSTR_COMP; + int retval; + + /* the opcode, writing data to R0 */ + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + opcode, + &dscr); + if (retval != ERROR_OK) + return retval; + + /* write R0 to DCC */ + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + 0xd5130400, /* msr dbgdtr_el0, x0 */ + &dscr); + if (retval != ERROR_OK) + return retval; + + return aarch64_read_dcc(a8, data, &dscr); +} + +static int aarch64_instr_read_data_r0_64(struct arm_dpm *dpm, + uint32_t opcode, uint64_t *data) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + uint32_t dscr = DSCR_INSTR_COMP; + int retval; + + /* the opcode, writing data to R0 */ + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + opcode, + &dscr); + if (retval != ERROR_OK) + return retval; + + /* write R0 to DCC */ + retval = aarch64_exec_opcode( + a8->armv8_common.arm.target, + 0xd5130400, /* msr dbgdtr_el0, x0 */ + &dscr); + if (retval != ERROR_OK) + return retval; + + return aarch64_read_dcc_64(a8, data, &dscr); +} + +static int aarch64_bpwp_enable(struct arm_dpm *dpm, unsigned index_t, + uint32_t addr, uint32_t control) +{ + struct aarch64_common *a8 = dpm_to_a8(dpm); + uint32_t vr = a8->armv8_common.debug_base; + uint32_t cr = a8->armv8_common.debug_base; + int retval; + + switch (index_t) { + case 0 ... 15: /* breakpoints */ + vr += CPUDBG_BVR_BASE; + cr += CPUDBG_BCR_BASE; + break; + case 16 ... 31: /* watchpoints */ + vr += CPUDBG_WVR_BASE; + cr += CPUDBG_WCR_BASE; + index_t -= 16; + break; + default: + return ERROR_FAIL; + } + vr += 4 * index_t; + cr += 4 * index_t; + + LOG_DEBUG("A8: bpwp enable, vr %08x cr %08x", + (unsigned) vr, (unsigned) cr); + + retval = aarch64_dap_write_memap_register_u32(dpm->arm->target, + vr, addr); + if (retval != ERROR_OK) + return retval; + retval = aarch64_dap_write_memap_register_u32(dpm->arm->target, + cr, control); + return retval; +} + +static int aarch64_bpwp_disable(struct arm_dpm *dpm, unsigned index_t) +{ + return ERROR_OK; + +#if 0 + struct aarch64_common *a8 = dpm_to_a8(dpm); + uint32_t cr; + + switch (index_t) { + case 0 ... 15: + cr = a8->armv8_common.debug_base + CPUDBG_BCR_BASE; + break; + case 16 ... 31: + cr = a8->armv8_common.debug_base + CPUDBG_WCR_BASE; + index_t -= 16; + break; + default: + return ERROR_FAIL; + } + cr += 4 * index_t; + + LOG_DEBUG("A8: bpwp disable, cr %08x", (unsigned) cr); + + /* clear control register */ + return aarch64_dap_write_memap_register_u32(dpm->arm->target, cr, 0); +#endif +} + +static int aarch64_dpm_setup(struct aarch64_common *a8, uint32_t debug) +{ + struct arm_dpm *dpm = &a8->armv8_common.dpm; + int retval; + + dpm->arm = &a8->armv8_common.arm; + dpm->didr = debug; + + dpm->prepare = aarch64_dpm_prepare; + dpm->finish = aarch64_dpm_finish; + + dpm->instr_write_data_dcc = aarch64_instr_write_data_dcc; + dpm->instr_write_data_dcc_64 = aarch64_instr_write_data_dcc_64; + dpm->instr_write_data_r0 = aarch64_instr_write_data_r0; + dpm->instr_write_data_r0_64 = aarch64_instr_write_data_r0_64; + dpm->instr_cpsr_sync = aarch64_instr_cpsr_sync; + + dpm->instr_read_data_dcc = aarch64_instr_read_data_dcc; + dpm->instr_read_data_dcc_64 = aarch64_instr_read_data_dcc_64; + dpm->instr_read_data_r0 = aarch64_instr_read_data_r0; + dpm->instr_read_data_r0_64 = aarch64_instr_read_data_r0_64; + + dpm->arm_reg_current = armv8_reg_current; + + dpm->bpwp_enable = aarch64_bpwp_enable; + dpm->bpwp_disable = aarch64_bpwp_disable; + + retval = arm_dpm_setup(dpm); + if (retval == ERROR_OK) + retval = arm_dpm_initialize(dpm); + + return retval; +} +static struct target *get_aarch64(struct target *target, int32_t coreid) +{ + struct target_list *head; + struct target *curr; + + head = target->head; + while (head != (struct target_list *)NULL) { + curr = head->target; + if ((curr->coreid == coreid) && (curr->state == TARGET_HALTED)) + return curr; + head = head->next; + } + return target; +} +static int aarch64_halt(struct target *target); + +static int aarch64_halt_smp(struct target *target) +{ + int retval = 0; + struct target_list *head; + struct target *curr; + head = target->head; + while (head != (struct target_list *)NULL) { + curr = head->target; + if ((curr != target) && (curr->state != TARGET_HALTED)) + retval += aarch64_halt(curr); + head = head->next; + } + return retval; +} + +static int update_halt_gdb(struct target *target) +{ + int retval = 0; + if (target->gdb_service && target->gdb_service->core[0] == -1) { + target->gdb_service->target = target; + target->gdb_service->core[0] = target->coreid; + retval += aarch64_halt_smp(target); + } + return retval; +} + +/* + * Cortex-A8 Run control + */ + +static int aarch64_poll(struct target *target) +{ + int retval = ERROR_OK; + uint32_t dscr; + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = &aarch64->armv8_common; + enum target_state prev_target_state = target->state; + /* toggle to another core is done by gdb as follow */ + /* maint packet J core_id */ + /* continue */ + /* the next polling trigger an halt event sent to gdb */ + if ((target->state == TARGET_HALTED) && (target->smp) && + (target->gdb_service) && + (target->gdb_service->target == NULL)) { + target->gdb_service->target = + get_aarch64(target, target->gdb_service->core[1]); + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + return retval; + } + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) + return retval; + aarch64->cpudbg_dscr = dscr; + + if (DSCR_RUN_MODE(dscr) == (DSCR_CORE_HALTED | DSCR_CORE_RESTARTED)) { + if (prev_target_state != TARGET_HALTED) { + /* We have a halting debug event */ + LOG_DEBUG("Target halted"); + target->state = TARGET_HALTED; + if ((prev_target_state == TARGET_RUNNING) + || (prev_target_state == TARGET_UNKNOWN) + || (prev_target_state == TARGET_RESET)) { + retval = aarch64_debug_entry(target); + if (retval != ERROR_OK) + return retval; + if (target->smp) { + retval = update_halt_gdb(target); + if (retval != ERROR_OK) + return retval; + } + target_call_event_callbacks(target, + TARGET_EVENT_HALTED); + } + if (prev_target_state == TARGET_DEBUG_RUNNING) { + LOG_DEBUG(" "); + + retval = aarch64_debug_entry(target); + if (retval != ERROR_OK) + return retval; + if (target->smp) { + retval = update_halt_gdb(target); + if (retval != ERROR_OK) + return retval; + } + + target_call_event_callbacks(target, + TARGET_EVENT_DEBUG_HALTED); + } + } + } else if (DSCR_RUN_MODE(dscr) == DSCR_CORE_RESTARTED) + target->state = TARGET_RUNNING; + else { + LOG_DEBUG("Unknown target state dscr = 0x%08" PRIx32, dscr); + target->state = TARGET_UNKNOWN; + } + + return retval; +} + +static int aarch64_halt(struct target *target) +{ + int retval = ERROR_OK; + uint32_t dscr; + struct armv8_common *armv8 = target_to_armv8(target); + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0, &dscr); + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0, 1); + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0, &dscr); + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x140, &dscr); + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x140, 6); + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x140, &dscr); + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0xa0, &dscr); + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0xa0, 5); + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0xa0, &dscr); + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0xa4, &dscr); + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0xa4, 2); + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0xa4, &dscr); + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x20, &dscr); + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x20, 4); + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x20, &dscr); + + /* + * enter halting debug mode + */ + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) + return retval; + +# /* STATUS */ + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x134, &dscr); + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x1c, &dscr); + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x1c, 1); + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x1c, &dscr); + + + long long then = timeval_ms(); + for (;; ) { + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) + return retval; + if ((dscr & DSCR_CORE_HALTED) != 0) + break; + if (timeval_ms() > then + 1000) { + LOG_ERROR("Timeout waiting for halt"); + return ERROR_FAIL; + } + } + + target->debug_reason = DBG_REASON_DBGRQ; + + return ERROR_OK; +} + +static int aarch64_internal_restore(struct target *target, int current, + uint64_t *address, int handle_breakpoints, int debug_execution) +{ + struct armv8_common *armv8 = target_to_armv8(target); + struct arm *arm = &armv8->arm; + int retval; + uint64_t resume_pc; + + if (!debug_execution) + target_free_all_working_areas(target); + + /* current = 1: continue on current pc, otherwise continue at
*/ + resume_pc = buf_get_u64(arm->pc->value, 0, 64); + if (!current) + resume_pc = *address; + else + *address = resume_pc; + + /* Make sure that the Armv7 gdb thumb fixups does not + * kill the return address + */ + switch (arm->core_state) { + case ARM_STATE_ARM: + case ARM_STATE_AARCH64: + resume_pc &= 0xFFFFFFFFFFFFFFFC; + break; + case ARM_STATE_THUMB: + case ARM_STATE_THUMB_EE: + /* When the return address is loaded into PC + * bit 0 must be 1 to stay in Thumb state + */ + resume_pc |= 0x1; + break; + case ARM_STATE_JAZELLE: + LOG_ERROR("How do I resume into Jazelle state??"); + return ERROR_FAIL; + } + LOG_DEBUG("resume pc = 0x%16" PRIx64, resume_pc); + buf_set_u64(arm->pc->value, 0, 64, resume_pc); + arm->pc->dirty = 1; + arm->pc->valid = 1; +#if 0 + /* restore dpm_mode at system halt */ + dpm_modeswitch(&armv8->dpm, ARM_MODE_ANY); +#endif + /* called it now before restoring context because it uses cpu + * register r0 for restoring system control register */ + retval = aarch64_restore_system_control_reg(target); + if (retval != ERROR_OK) + return retval; + retval = aarch64_restore_context(target, handle_breakpoints); + if (retval != ERROR_OK) + return retval; + target->debug_reason = DBG_REASON_NOTHALTED; + target->state = TARGET_RUNNING; + + /* registers are now invalid */ + register_cache_invalidate(arm->core_cache); + +#if 0 + /* the front-end may request us not to handle breakpoints */ + if (handle_breakpoints) { + /* Single step past breakpoint at current address */ + breakpoint = breakpoint_find(target, resume_pc); + if (breakpoint) { + LOG_DEBUG("unset breakpoint at 0x%8.8x", breakpoint->address); + cortex_m3_unset_breakpoint(target, breakpoint); + cortex_m3_single_step_core(target); + cortex_m3_set_breakpoint(target, breakpoint); + } + } +#endif + + return retval; +} + +static int aarch64_internal_restart(struct target *target) +{ + struct armv8_common *armv8 = target_to_armv8(target); + struct arm *arm = &armv8->arm; + int retval; + uint32_t dscr; + /* + * * Restart core and wait for it to be started. Clear ITRen and sticky + * * exception flags: see ARMv7 ARM, C5.9. + * + * REVISIT: for single stepping, we probably want to + * disable IRQs by default, with optional override... + */ + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) + return retval; + + if ((dscr & DSCR_INSTR_COMP) == 0) + LOG_ERROR("DSCR InstrCompl must be set before leaving debug!"); + + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, dscr & ~DSCR_ITR_EN); + if (retval != ERROR_OK) + return retval; + + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DRCR, DRCR_RESTART | + DRCR_CLEAR_EXCEPTIONS); + if (retval != ERROR_OK) + return retval; + + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x10, 1); + if (retval != ERROR_OK) + return retval; + + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x10000 + 0x1c, 2); + if (retval != ERROR_OK) + return retval; + + long long then = timeval_ms(); + for (;; ) { + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) + return retval; + if ((dscr & DSCR_CORE_RESTARTED) != 0) + break; + if (timeval_ms() > then + 1000) { + LOG_ERROR("Timeout waiting for resume"); + return ERROR_FAIL; + } + } + + target->debug_reason = DBG_REASON_NOTHALTED; + target->state = TARGET_RUNNING; + + /* registers are now invalid */ + register_cache_invalidate(arm->core_cache); + + return ERROR_OK; +} + +static int aarch64_restore_smp(struct target *target, int handle_breakpoints) +{ + int retval = 0; + struct target_list *head; + struct target *curr; + uint64_t address; + head = target->head; + while (head != (struct target_list *)NULL) { + curr = head->target; + if ((curr != target) && (curr->state != TARGET_RUNNING)) { + /* resume current address , not in step mode */ + retval += aarch64_internal_restore(curr, 1, &address, + handle_breakpoints, 0); + retval += aarch64_internal_restart(curr); + } + head = head->next; + + } + return retval; +} + +static int aarch64_resume(struct target *target, int current, + target_addr_t address, int handle_breakpoints, int debug_execution) +{ + int retval = 0; + uint64_t resume_addr; + + if (address) { + LOG_DEBUG("resuming with custom address not supported"); + return ERROR_FAIL; + } + + /* dummy resume for smp toggle in order to reduce gdb impact */ + if ((target->smp) && (target->gdb_service->core[1] != -1)) { + /* simulate a start and halt of target */ + target->gdb_service->target = NULL; + target->gdb_service->core[0] = target->gdb_service->core[1]; + /* fake resume at next poll we play the target core[1], see poll*/ + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + return 0; + } + aarch64_internal_restore(target, current, &resume_addr, handle_breakpoints, debug_execution); + if (target->smp) { + target->gdb_service->core[0] = -1; + retval = aarch64_restore_smp(target, handle_breakpoints); + if (retval != ERROR_OK) + return retval; + } + aarch64_internal_restart(target); + + if (!debug_execution) { + target->state = TARGET_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_RESUMED); + LOG_DEBUG("target resumed at 0x%" PRIx64, resume_addr); + } else { + target->state = TARGET_DEBUG_RUNNING; + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED); + LOG_DEBUG("target debug resumed at 0x%" PRIx64, resume_addr); + } + + return ERROR_OK; +} + +static int aarch64_debug_entry(struct target *target) +{ + uint32_t dscr; + int retval = ERROR_OK; + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = target_to_armv8(target); + + LOG_DEBUG("dscr = 0x%08" PRIx32, aarch64->cpudbg_dscr); + + /* REVISIT surely we should not re-read DSCR !! */ + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) + return retval; + + /* REVISIT see A8 TRM 12.11.4 steps 2..3 -- make sure that any + * imprecise data aborts get discarded by issuing a Data + * Synchronization Barrier: ARMV4_5_MCR(15, 0, 0, 7, 10, 4). + */ + + /* Enable the ITR execution once we are in debug mode */ + dscr |= DSCR_ITR_EN; + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, dscr); + if (retval != ERROR_OK) + return retval; + + /* Examine debug reason */ + arm_dpm_report_dscr(&armv8->dpm, aarch64->cpudbg_dscr); + + /* save address of instruction that triggered the watchpoint? */ + if (target->debug_reason == DBG_REASON_WATCHPOINT) { + uint32_t wfar; + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_WFAR, + &wfar); + if (retval != ERROR_OK) + return retval; + arm_dpm_report_wfar(&armv8->dpm, wfar); + } + + retval = arm_dpm_read_current_registers_64(&armv8->dpm); + + if (armv8->post_debug_entry) { + retval = armv8->post_debug_entry(target); + if (retval != ERROR_OK) + return retval; + } + + return retval; +} + +static int aarch64_post_debug_entry(struct target *target) +{ + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = &aarch64->armv8_common; + struct armv8_mmu_common *armv8_mmu = &armv8->armv8_mmu; + uint32_t sctlr_el1 = 0; + int retval; + + mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DRCR, 1<<2); + retval = aarch64_instr_read_data_r0(armv8->arm.dpm, + 0xd5381000, &sctlr_el1); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("sctlr_el1 = %#8.8x", sctlr_el1); + aarch64->system_control_reg = sctlr_el1; + aarch64->system_control_reg_curr = sctlr_el1; + aarch64->curr_mode = armv8->arm.core_mode; + + armv8_mmu->mmu_enabled = sctlr_el1 & 0x1U ? 1 : 0; + armv8_mmu->armv8_cache.d_u_cache_enabled = sctlr_el1 & 0x4U ? 1 : 0; + armv8_mmu->armv8_cache.i_cache_enabled = sctlr_el1 & 0x1000U ? 1 : 0; + +#if 0 + if (armv8->armv8_mmu.armv8_cache.ctype == -1) + armv8_identify_cache(target); +#endif + + return ERROR_OK; +} + +static int aarch64_step(struct target *target, int current, target_addr_t address, + int handle_breakpoints) +{ + struct armv8_common *armv8 = target_to_armv8(target); + struct arm *arm = &armv8->arm; + struct breakpoint *breakpoint = NULL; + struct breakpoint stepbreakpoint; + struct reg *r; + int retval; + + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* current = 1: continue on current pc, otherwise continue at
*/ + r = arm->pc; + if (!current) + buf_set_u64(r->value, 0, 64, address); + else + address = buf_get_u64(r->value, 0, 64); + + /* The front-end may request us not to handle breakpoints. + * But since Cortex-A8 uses breakpoint for single step, + * we MUST handle breakpoints. + */ + handle_breakpoints = 1; + if (handle_breakpoints) { + breakpoint = breakpoint_find(target, address); + if (breakpoint) + aarch64_unset_breakpoint(target, breakpoint); + } + + /* Setup single step breakpoint */ + stepbreakpoint.address = address; + stepbreakpoint.length = 4; + stepbreakpoint.type = BKPT_HARD; + stepbreakpoint.set = 0; + + /* Break on IVA mismatch */ + aarch64_set_breakpoint(target, &stepbreakpoint, 0x04); + + target->debug_reason = DBG_REASON_SINGLESTEP; + + retval = aarch64_resume(target, 1, address, 0, 0); + if (retval != ERROR_OK) + return retval; + + long long then = timeval_ms(); + while (target->state != TARGET_HALTED) { + retval = aarch64_poll(target); + if (retval != ERROR_OK) + return retval; + if (timeval_ms() > then + 1000) { + LOG_ERROR("timeout waiting for target halt"); + return ERROR_FAIL; + } + } + + aarch64_unset_breakpoint(target, &stepbreakpoint); + + target->debug_reason = DBG_REASON_BREAKPOINT; + + if (breakpoint) + aarch64_set_breakpoint(target, breakpoint, 0); + + if (target->state != TARGET_HALTED) + LOG_DEBUG("target stepped"); + + return ERROR_OK; +} + +static int aarch64_restore_context(struct target *target, bool bpwp) +{ + struct armv8_common *armv8 = target_to_armv8(target); + + LOG_DEBUG(" "); + + if (armv8->pre_restore_context) + armv8->pre_restore_context(target); + + return arm_dpm_write_dirty_registers(&armv8->dpm, bpwp); + + return ERROR_OK; +} + +/* + * Cortex-A8 Breakpoint and watchpoint functions + */ + +/* Setup hardware Breakpoint Register Pair */ +static int aarch64_set_breakpoint(struct target *target, + struct breakpoint *breakpoint, uint8_t matchmode) +{ + int retval; + int brp_i = 0; + uint32_t control; + uint8_t byte_addr_select = 0x0F; + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = &aarch64->armv8_common; + struct aarch64_brp *brp_list = aarch64->brp_list; + + if (breakpoint->set) { + LOG_WARNING("breakpoint already set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_HARD) { + while (brp_list[brp_i].used && (brp_i < aarch64->brp_num)) + brp_i++; + if (brp_i >= aarch64->brp_num) { + LOG_ERROR("ERROR Can not find free Breakpoint Register Pair"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + breakpoint->set = brp_i + 1; + if (breakpoint->length == 2) + byte_addr_select = (3 << (breakpoint->address & 0x02)); + control = ((matchmode & 0x7) << 20) + | (byte_addr_select << 5) + | (3 << 1) | 1; + brp_list[brp_i].used = 1; + brp_list[brp_i].value = (breakpoint->address & 0xFFFFFFFC); + brp_list[brp_i].control = control; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BVR_BASE + 4 * brp_list[brp_i].BRPn, + brp_list[brp_i].value); + if (retval != ERROR_OK) + return retval; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BCR_BASE + 4 * brp_list[brp_i].BRPn, + brp_list[brp_i].control); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("brp %i control 0x%0" PRIx32 " value 0x%" TARGET_PRIxADDR, brp_i, + brp_list[brp_i].control, + brp_list[brp_i].value); + } else if (breakpoint->type == BKPT_SOFT) { + uint8_t code[4]; + if (breakpoint->length == 2) + buf_set_u32(code, 0, 32, ARMV5_T_BKPT(0x11)); + else + buf_set_u32(code, 0, 32, ARMV5_BKPT(0x11)); + retval = target_read_memory(target, + breakpoint->address & 0xFFFFFFFE, + breakpoint->length, 1, + breakpoint->orig_instr); + if (retval != ERROR_OK) + return retval; + retval = target_write_memory(target, + breakpoint->address & 0xFFFFFFFE, + breakpoint->length, 1, code); + if (retval != ERROR_OK) + return retval; + breakpoint->set = 0x11; /* Any nice value but 0 */ + } + + return ERROR_OK; +} + +static int aarch64_set_context_breakpoint(struct target *target, + struct breakpoint *breakpoint, uint8_t matchmode) +{ + int retval = ERROR_FAIL; + int brp_i = 0; + uint32_t control; + uint8_t byte_addr_select = 0x0F; + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = &aarch64->armv8_common; + struct aarch64_brp *brp_list = aarch64->brp_list; + + if (breakpoint->set) { + LOG_WARNING("breakpoint already set"); + return retval; + } + /*check available context BRPs*/ + while ((brp_list[brp_i].used || + (brp_list[brp_i].type != BRP_CONTEXT)) && (brp_i < aarch64->brp_num)) + brp_i++; + + if (brp_i >= aarch64->brp_num) { + LOG_ERROR("ERROR Can not find free Breakpoint Register Pair"); + return ERROR_FAIL; + } + + breakpoint->set = brp_i + 1; + control = ((matchmode & 0x7) << 20) + | (byte_addr_select << 5) + | (3 << 1) | 1; + brp_list[brp_i].used = 1; + brp_list[brp_i].value = (breakpoint->asid); + brp_list[brp_i].control = control; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BVR_BASE + 4 * brp_list[brp_i].BRPn, + brp_list[brp_i].value); + if (retval != ERROR_OK) + return retval; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BCR_BASE + 4 * brp_list[brp_i].BRPn, + brp_list[brp_i].control); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("brp %i control 0x%0" PRIx32 " value 0x%" TARGET_PRIxADDR, brp_i, + brp_list[brp_i].control, + brp_list[brp_i].value); + return ERROR_OK; + +} + +static int aarch64_set_hybrid_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + int retval = ERROR_FAIL; + int brp_1 = 0; /* holds the contextID pair */ + int brp_2 = 0; /* holds the IVA pair */ + uint32_t control_CTX, control_IVA; + uint8_t CTX_byte_addr_select = 0x0F; + uint8_t IVA_byte_addr_select = 0x0F; + uint8_t CTX_machmode = 0x03; + uint8_t IVA_machmode = 0x01; + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = &aarch64->armv8_common; + struct aarch64_brp *brp_list = aarch64->brp_list; + + if (breakpoint->set) { + LOG_WARNING("breakpoint already set"); + return retval; + } + /*check available context BRPs*/ + while ((brp_list[brp_1].used || + (brp_list[brp_1].type != BRP_CONTEXT)) && (brp_1 < aarch64->brp_num)) + brp_1++; + + printf("brp(CTX) found num: %d\n", brp_1); + if (brp_1 >= aarch64->brp_num) { + LOG_ERROR("ERROR Can not find free Breakpoint Register Pair"); + return ERROR_FAIL; + } + + while ((brp_list[brp_2].used || + (brp_list[brp_2].type != BRP_NORMAL)) && (brp_2 < aarch64->brp_num)) + brp_2++; + + printf("brp(IVA) found num: %d\n", brp_2); + if (brp_2 >= aarch64->brp_num) { + LOG_ERROR("ERROR Can not find free Breakpoint Register Pair"); + return ERROR_FAIL; + } + + breakpoint->set = brp_1 + 1; + breakpoint->linked_BRP = brp_2; + control_CTX = ((CTX_machmode & 0x7) << 20) + | (brp_2 << 16) + | (0 << 14) + | (CTX_byte_addr_select << 5) + | (3 << 1) | 1; + brp_list[brp_1].used = 1; + brp_list[brp_1].value = (breakpoint->asid); + brp_list[brp_1].control = control_CTX; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BVR_BASE + 4 * brp_list[brp_1].BRPn, + brp_list[brp_1].value); + if (retval != ERROR_OK) + return retval; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BCR_BASE + 4 * brp_list[brp_1].BRPn, + brp_list[brp_1].control); + if (retval != ERROR_OK) + return retval; + + control_IVA = ((IVA_machmode & 0x7) << 20) + | (brp_1 << 16) + | (IVA_byte_addr_select << 5) + | (3 << 1) | 1; + brp_list[brp_2].used = 1; + brp_list[brp_2].value = (breakpoint->address & 0xFFFFFFFC); + brp_list[brp_2].control = control_IVA; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BVR_BASE + 4 * brp_list[brp_2].BRPn, + brp_list[brp_2].value); + if (retval != ERROR_OK) + return retval; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BCR_BASE + 4 * brp_list[brp_2].BRPn, + brp_list[brp_2].control); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int aarch64_unset_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + int retval; + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = &aarch64->armv8_common; + struct aarch64_brp *brp_list = aarch64->brp_list; + + if (!breakpoint->set) { + LOG_WARNING("breakpoint not set"); + return ERROR_OK; + } + + if (breakpoint->type == BKPT_HARD) { + if ((breakpoint->address != 0) && (breakpoint->asid != 0)) { + int brp_i = breakpoint->set - 1; + int brp_j = breakpoint->linked_BRP; + if ((brp_i < 0) || (brp_i >= aarch64->brp_num)) { + LOG_DEBUG("Invalid BRP number in breakpoint"); + return ERROR_OK; + } + LOG_DEBUG("rbp %i control 0x%0" PRIx32 " value 0x%" TARGET_PRIxADDR, brp_i, + brp_list[brp_i].control, brp_list[brp_i].value); + brp_list[brp_i].used = 0; + brp_list[brp_i].value = 0; + brp_list[brp_i].control = 0; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BCR_BASE + 16 * brp_list[brp_i].BRPn, + brp_list[brp_i].control); + if (retval != ERROR_OK) + return retval; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BVR_BASE + 16 * brp_list[brp_i].BRPn, + brp_list[brp_i].value); + if (retval != ERROR_OK) + return retval; + if ((brp_j < 0) || (brp_j >= aarch64->brp_num)) { + LOG_DEBUG("Invalid BRP number in breakpoint"); + return ERROR_OK; + } + LOG_DEBUG("rbp %i control 0x%0" PRIx32 " value 0x%0" PRIx64, brp_j, + brp_list[brp_j].control, brp_list[brp_j].value); + brp_list[brp_j].used = 0; + brp_list[brp_j].value = 0; + brp_list[brp_j].control = 0; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BCR_BASE + 16 * brp_list[brp_j].BRPn, + brp_list[brp_j].control); + if (retval != ERROR_OK) + return retval; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BVR_BASE + 16 * brp_list[brp_j].BRPn, + brp_list[brp_j].value); + if (retval != ERROR_OK) + return retval; + breakpoint->linked_BRP = 0; + breakpoint->set = 0; + return ERROR_OK; + + } else { + int brp_i = breakpoint->set - 1; + if ((brp_i < 0) || (brp_i >= aarch64->brp_num)) { + LOG_DEBUG("Invalid BRP number in breakpoint"); + return ERROR_OK; + } + LOG_DEBUG("rbp %i control 0x%0" PRIx32 " value 0x%0" PRIx64, brp_i, + brp_list[brp_i].control, brp_list[brp_i].value); + brp_list[brp_i].used = 0; + brp_list[brp_i].value = 0; + brp_list[brp_i].control = 0; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BCR_BASE + 4 * brp_list[brp_i].BRPn, + brp_list[brp_i].control); + if (retval != ERROR_OK) + return retval; + retval = aarch64_dap_write_memap_register_u32(target, armv8->debug_base + + CPUDBG_BVR_BASE + 4 * brp_list[brp_i].BRPn, + brp_list[brp_i].value); + if (retval != ERROR_OK) + return retval; + breakpoint->set = 0; + return ERROR_OK; + } + } else { + /* restore original instruction (kept in target endianness) */ + if (breakpoint->length == 4) { + retval = target_write_memory(target, + breakpoint->address & 0xFFFFFFFFFFFFFFFE, + 4, 1, breakpoint->orig_instr); + if (retval != ERROR_OK) + return retval; + } else { + retval = target_write_memory(target, + breakpoint->address & 0xFFFFFFFFFFFFFFFE, + 2, 1, breakpoint->orig_instr); + if (retval != ERROR_OK) + return retval; + } + } + breakpoint->set = 0; + + return ERROR_OK; +} + +static int aarch64_add_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + struct aarch64_common *aarch64 = target_to_aarch64(target); + + if ((breakpoint->type == BKPT_HARD) && (aarch64->brp_num_available < 1)) { + LOG_INFO("no hardware breakpoint available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (breakpoint->type == BKPT_HARD) + aarch64->brp_num_available--; + + return aarch64_set_breakpoint(target, breakpoint, 0x00); /* Exact match */ +} + +static int aarch64_add_context_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + struct aarch64_common *aarch64 = target_to_aarch64(target); + + if ((breakpoint->type == BKPT_HARD) && (aarch64->brp_num_available < 1)) { + LOG_INFO("no hardware breakpoint available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (breakpoint->type == BKPT_HARD) + aarch64->brp_num_available--; + + return aarch64_set_context_breakpoint(target, breakpoint, 0x02); /* asid match */ +} + +static int aarch64_add_hybrid_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + struct aarch64_common *aarch64 = target_to_aarch64(target); + + if ((breakpoint->type == BKPT_HARD) && (aarch64->brp_num_available < 1)) { + LOG_INFO("no hardware breakpoint available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + if (breakpoint->type == BKPT_HARD) + aarch64->brp_num_available--; + + return aarch64_set_hybrid_breakpoint(target, breakpoint); /* ??? */ +} + + +static int aarch64_remove_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + struct aarch64_common *aarch64 = target_to_aarch64(target); + +#if 0 +/* It is perfectly possible to remove breakpoints while the target is running */ + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } +#endif + + if (breakpoint->set) { + aarch64_unset_breakpoint(target, breakpoint); + if (breakpoint->type == BKPT_HARD) + aarch64->brp_num_available++; + } + + return ERROR_OK; +} + +/* + * Cortex-A8 Reset functions + */ + +static int aarch64_assert_reset(struct target *target) +{ + struct armv8_common *armv8 = target_to_armv8(target); + + LOG_DEBUG(" "); + + /* FIXME when halt is requested, make it work somehow... */ + + /* Issue some kind of warm reset. */ + if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) + target_handle_event(target, TARGET_EVENT_RESET_ASSERT); + else if (jtag_get_reset_config() & RESET_HAS_SRST) { + /* REVISIT handle "pulls" cases, if there's + * hardware that needs them to work. + */ + jtag_add_reset(0, 1); + } else { + LOG_ERROR("%s: how to reset?", target_name(target)); + return ERROR_FAIL; + } + + /* registers are now invalid */ + register_cache_invalidate(armv8->arm.core_cache); + + target->state = TARGET_RESET; + + return ERROR_OK; +} + +static int aarch64_deassert_reset(struct target *target) +{ + int retval; + + LOG_DEBUG(" "); + + /* be certain SRST is off */ + jtag_add_reset(0, 0); + + retval = aarch64_poll(target); + if (retval != ERROR_OK) + return retval; + + if (target->reset_halt) { + if (target->state != TARGET_HALTED) { + LOG_WARNING("%s: ran after reset and before halt ...", + target_name(target)); + retval = target_halt(target); + if (retval != ERROR_OK) + return retval; + } + } + + return ERROR_OK; +} + +static int aarch64_write_apb_ab_memory(struct target *target, + uint64_t address, uint32_t size, + uint32_t count, const uint8_t *buffer) +{ + /* write memory through APB-AP */ + int retval = ERROR_COMMAND_SYNTAX_ERROR; + struct armv8_common *armv8 = target_to_armv8(target); + struct arm *arm = &armv8->arm; + int total_bytes = count * size; + int total_u32; + int start_byte = address & 0x3; + int end_byte = (address + total_bytes) & 0x3; + struct reg *reg; + uint32_t dscr; + uint8_t *tmp_buff = NULL; + uint32_t i = 0; + + LOG_DEBUG("Writing APB-AP memory address 0x%" PRIx64 " size %" PRIu32 " count%" PRIu32, + address, size, count); + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + total_u32 = DIV_ROUND_UP((address & 3) + total_bytes, 4); + + /* Mark register R0 as dirty, as it will be used + * for transferring the data. + * It will be restored automatically when exiting + * debug mode + */ + reg = armv8_reg_current(arm, 1); + reg->dirty = true; + + reg = armv8_reg_current(arm, 0); + reg->dirty = true; + + /* clear any abort */ + retval = mem_ap_write_atomic_u32(armv8->debug_ap, armv8->debug_base + CPUDBG_DRCR, 1<<2); + if (retval != ERROR_OK) + return retval; + + /* This algorithm comes from either : + * Cortex-A8 TRM Example 12-25 + * Cortex-R4 TRM Example 11-26 + * (slight differences) + */ + + /* The algorithm only copies 32 bit words, so the buffer + * should be expanded to include the words at either end. + * The first and last words will be read first to avoid + * corruption if needed. + */ + tmp_buff = malloc(total_u32 * 4); + + if ((start_byte != 0) && (total_u32 > 1)) { + /* First bytes not aligned - read the 32 bit word to avoid corrupting + * the other bytes in the word. + */ + retval = aarch64_read_apb_ab_memory(target, (address & ~0x3), 4, 1, tmp_buff); + if (retval != ERROR_OK) + goto error_free_buff_w; + } + + /* If end of write is not aligned, or the write is less than 4 bytes */ + if ((end_byte != 0) || + ((total_u32 == 1) && (total_bytes != 4))) { + + /* Read the last word to avoid corruption during 32 bit write */ + int mem_offset = (total_u32-1) * 4; + retval = aarch64_read_apb_ab_memory(target, (address & ~0x3) + mem_offset, 4, 1, &tmp_buff[mem_offset]); + if (retval != ERROR_OK) + goto error_free_buff_w; + } + + /* Copy the write buffer over the top of the temporary buffer */ + memcpy(&tmp_buff[start_byte], buffer, total_bytes); + + /* We now have a 32 bit aligned buffer that can be written */ + + /* Read DSCR */ + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) + goto error_free_buff_w; + + /* Set DTR mode to Normal*/ + dscr = (dscr & ~DSCR_EXT_DCC_MASK) | DSCR_EXT_DCC_NON_BLOCKING; + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, dscr); + if (retval != ERROR_OK) + goto error_free_buff_w; + + if (size > 4) { + LOG_WARNING("reading size >4 bytes not yet supported"); + goto error_unset_dtr_w; + } + + retval = aarch64_instr_write_data_dcc_64(arm->dpm, 0xd5330401, address+4); + if (retval != ERROR_OK) + goto error_unset_dtr_w; + + dscr = DSCR_INSTR_COMP; + while (i < count * size) { + uint32_t val; + + memcpy(&val, &buffer[i], size); + retval = aarch64_instr_write_data_dcc(arm->dpm, 0xd5330500, val); + if (retval != ERROR_OK) + goto error_unset_dtr_w; + + retval = aarch64_exec_opcode(target, 0xb81fc020, &dscr); + if (retval != ERROR_OK) + goto error_unset_dtr_w; + + retval = aarch64_exec_opcode(target, 0x91001021, &dscr); + if (retval != ERROR_OK) + goto error_unset_dtr_w; + + i += 4; + } + + /* Check for sticky abort flags in the DSCR */ + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) + goto error_free_buff_w; + if (dscr & (DSCR_STICKY_ABORT_PRECISE | DSCR_STICKY_ABORT_IMPRECISE)) { + /* Abort occurred - clear it and exit */ + LOG_ERROR("abort occurred - dscr = 0x%08" PRIx32, dscr); + mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DRCR, 1<<2); + goto error_free_buff_w; + } + + /* Done */ + free(tmp_buff); + return ERROR_OK; + +error_unset_dtr_w: + /* Unset DTR mode */ + mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + dscr = (dscr & ~DSCR_EXT_DCC_MASK) | DSCR_EXT_DCC_NON_BLOCKING; + mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, dscr); +error_free_buff_w: + LOG_ERROR("error"); + free(tmp_buff); + return ERROR_FAIL; +} + +static int aarch64_read_apb_ab_memory(struct target *target, + target_addr_t address, uint32_t size, + uint32_t count, uint8_t *buffer) +{ + /* read memory through APB-AP */ + + int retval = ERROR_COMMAND_SYNTAX_ERROR; + struct armv8_common *armv8 = target_to_armv8(target); + struct arm *arm = &armv8->arm; + struct reg *reg; + uint32_t dscr, val; + uint8_t *tmp_buff = NULL; + uint32_t i = 0; + + LOG_DEBUG("Reading APB-AP memory address 0x%" TARGET_PRIxADDR " size %" PRIu32 " count%" PRIu32, + address, size, count); + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Mark register R0 as dirty, as it will be used + * for transferring the data. + * It will be restored automatically when exiting + * debug mode + */ + reg = armv8_reg_current(arm, 0); + reg->dirty = true; + + /* clear any abort */ + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DRCR, 1<<2); + if (retval != ERROR_OK) + goto error_free_buff_r; + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + if (retval != ERROR_OK) + goto error_unset_dtr_r; + + if (size > 4) { + LOG_WARNING("reading size >4 bytes not yet supported"); + goto error_unset_dtr_r; + } + + while (i < count * size) { + + retval = aarch64_instr_write_data_dcc_64(arm->dpm, 0xd5330400, address+4); + if (retval != ERROR_OK) + goto error_unset_dtr_r; + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + + dscr = DSCR_INSTR_COMP; + retval = aarch64_exec_opcode(target, 0xb85fc000, &dscr); + if (retval != ERROR_OK) + goto error_unset_dtr_r; + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + + retval = aarch64_instr_read_data_dcc(arm->dpm, 0xd5130400, &val); + if (retval != ERROR_OK) + goto error_unset_dtr_r; + memcpy(&buffer[i], &val, size); + i += 4; + address += 4; + } + + /* Clear any sticky error */ + mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DRCR, 1<<2); + + /* Done */ + return ERROR_OK; + +error_unset_dtr_r: + LOG_WARNING("DSCR = 0x%" PRIx32, dscr); + /* Todo: Unset DTR mode */ + +error_free_buff_r: + LOG_ERROR("error"); + free(tmp_buff); + + /* Clear any sticky error */ + mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DRCR, 1<<2); + + return ERROR_FAIL; +} + +static int aarch64_read_phys_memory(struct target *target, + target_addr_t address, uint32_t size, + uint32_t count, uint8_t *buffer) +{ + struct armv8_common *armv8 = target_to_armv8(target); + int retval = ERROR_COMMAND_SYNTAX_ERROR; + struct adiv5_dap *swjdp = armv8->arm.dap; + uint8_t apsel = swjdp->apsel; + LOG_DEBUG("Reading memory at real address 0x%" TARGET_PRIxADDR "; size %" PRId32 "; count %" PRId32, + address, size, count); + + if (count && buffer) { + + if (armv8->memory_ap_available && (apsel == armv8->memory_ap->ap_num)) { + + /* read memory through AHB-AP */ + retval = mem_ap_read_buf(armv8->memory_ap, buffer, size, count, address); + } else { + /* read memory through APB-AP */ + retval = aarch64_mmu_modify(target, 0); + if (retval != ERROR_OK) + return retval; + retval = aarch64_read_apb_ab_memory(target, address, size, count, buffer); + } + } + return retval; +} + +static int aarch64_read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + int mmu_enabled = 0; + target_addr_t virt, phys; + int retval; + struct armv8_common *armv8 = target_to_armv8(target); + struct adiv5_dap *swjdp = armv8->arm.dap; + uint8_t apsel = swjdp->apsel; + + /* aarch64 handles unaligned memory access */ + LOG_DEBUG("Reading memory at address 0x%" TARGET_PRIxADDR "; size %" PRId32 "; count %" PRId32, address, + size, count); + + /* determine if MMU was enabled on target stop */ + if (!armv8->is_armv7r) { + retval = aarch64_mmu(target, &mmu_enabled); + if (retval != ERROR_OK) + return retval; + } + + if (armv8->memory_ap_available && (apsel == armv8->memory_ap->ap_num)) { + if (mmu_enabled) { + virt = address; + retval = aarch64_virt2phys(target, virt, &phys); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("Reading at virtual address. Translating v:0x%" TARGET_PRIxADDR " to r:0x%" TARGET_PRIxADDR, + virt, phys); + address = phys; + } + retval = aarch64_read_phys_memory(target, address, size, count, + buffer); + } else { + if (mmu_enabled) { + retval = aarch64_check_address(target, address); + if (retval != ERROR_OK) + return retval; + /* enable MMU as we could have disabled it for phys + access */ + retval = aarch64_mmu_modify(target, 1); + if (retval != ERROR_OK) + return retval; + } + retval = aarch64_read_apb_ab_memory(target, address, size, + count, buffer); + } + return retval; +} + +static int aarch64_write_phys_memory(struct target *target, + target_addr_t address, uint32_t size, + uint32_t count, const uint8_t *buffer) +{ + struct armv8_common *armv8 = target_to_armv8(target); + struct adiv5_dap *swjdp = armv8->arm.dap; + int retval = ERROR_COMMAND_SYNTAX_ERROR; + uint8_t apsel = swjdp->apsel; + + LOG_DEBUG("Writing memory to real address 0x%" TARGET_PRIxADDR "; size %" PRId32 "; count %" PRId32, address, + size, count); + + if (count && buffer) { + + if (armv8->memory_ap_available && (apsel == armv8->memory_ap->ap_num)) { + + /* write memory through AHB-AP */ + retval = mem_ap_write_buf(armv8->memory_ap, buffer, size, count, address); + } else { + + /* write memory through APB-AP */ + if (!armv8->is_armv7r) { + retval = aarch64_mmu_modify(target, 0); + if (retval != ERROR_OK) + return retval; + } + return aarch64_write_apb_ab_memory(target, address, size, count, buffer); + } + } + + + /* REVISIT this op is generic ARMv7-A/R stuff */ + if (retval == ERROR_OK && target->state == TARGET_HALTED) { + struct arm_dpm *dpm = armv8->arm.dpm; + + retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + return retval; + + /* The Cache handling will NOT work with MMU active, the + * wrong addresses will be invalidated! + * + * For both ICache and DCache, walk all cache lines in the + * address range. Cortex-A8 has fixed 64 byte line length. + * + * REVISIT per ARMv7, these may trigger watchpoints ... + */ + + /* invalidate I-Cache */ + if (armv8->armv8_mmu.armv8_cache.i_cache_enabled) { + /* ICIMVAU - Invalidate Cache single entry + * with MVA to PoU + * MCR p15, 0, r0, c7, c5, 1 + */ + for (uint32_t cacheline = address; + cacheline < address + size * count; + cacheline += 64) { + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_MCR(15, 0, 0, 7, 5, 1), + cacheline); + if (retval != ERROR_OK) + return retval; + } + } + + /* invalidate D-Cache */ + if (armv8->armv8_mmu.armv8_cache.d_u_cache_enabled) { + /* DCIMVAC - Invalidate data Cache line + * with MVA to PoC + * MCR p15, 0, r0, c7, c6, 1 + */ + for (uint32_t cacheline = address; + cacheline < address + size * count; + cacheline += 64) { + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_MCR(15, 0, 0, 7, 6, 1), + cacheline); + if (retval != ERROR_OK) + return retval; + } + } + + /* (void) */ dpm->finish(dpm); + } + + return retval; +} + +static int aarch64_write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + int mmu_enabled = 0; + target_addr_t virt, phys; + int retval; + struct armv8_common *armv8 = target_to_armv8(target); + struct adiv5_dap *swjdp = armv8->arm.dap; + uint8_t apsel = swjdp->apsel; + + /* aarch64 handles unaligned memory access */ + LOG_DEBUG("Writing memory at address 0x%" TARGET_PRIxADDR "; size %" PRId32 + "; count %" PRId32, address, size, count); + + /* determine if MMU was enabled on target stop */ + if (!armv8->is_armv7r) { + retval = aarch64_mmu(target, &mmu_enabled); + if (retval != ERROR_OK) + return retval; + } + + if (armv8->memory_ap_available && (apsel == armv8->memory_ap->ap_num)) { + LOG_DEBUG("Writing memory to address 0x%" TARGET_PRIxADDR "; size %" + PRId32 "; count %" PRId32, address, size, count); + if (mmu_enabled) { + virt = address; + retval = aarch64_virt2phys(target, virt, &phys); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("Writing to virtual address. Translating v:0x%" + TARGET_PRIxADDR " to r:0x%" TARGET_PRIxADDR, virt, phys); + address = phys; + } + retval = aarch64_write_phys_memory(target, address, size, + count, buffer); + } else { + if (mmu_enabled) { + retval = aarch64_check_address(target, address); + if (retval != ERROR_OK) + return retval; + /* enable MMU as we could have disabled it for phys access */ + retval = aarch64_mmu_modify(target, 1); + if (retval != ERROR_OK) + return retval; + } + retval = aarch64_write_apb_ab_memory(target, address, size, count, buffer); + } + return retval; +} + +static int aarch64_handle_target_request(void *priv) +{ + struct target *target = priv; + struct armv8_common *armv8 = target_to_armv8(target); + int retval; + + if (!target_was_examined(target)) + return ERROR_OK; + if (!target->dbg_msg_enabled) + return ERROR_OK; + + if (target->state == TARGET_RUNNING) { + uint32_t request; + uint32_t dscr; + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + + /* check if we have data */ + while ((dscr & DSCR_DTR_TX_FULL) && (retval == ERROR_OK)) { + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DTRTX, &request); + if (retval == ERROR_OK) { + target_request(target, request); + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_DSCR, &dscr); + } + } + } + + return ERROR_OK; +} + +static int aarch64_examine_first(struct target *target) +{ + struct aarch64_common *aarch64 = target_to_aarch64(target); + struct armv8_common *armv8 = &aarch64->armv8_common; + struct adiv5_dap *swjdp = armv8->arm.dap; + int retval = ERROR_OK; + uint32_t pfr, debug, ctypr, ttypr, cpuid; + int i; + + /* We do one extra read to ensure DAP is configured, + * we call ahbap_debugport_init(swjdp) instead + */ + retval = dap_dp_init(swjdp); + if (retval != ERROR_OK) + return retval; + + /* Search for the APB-AB - it is needed for access to debug registers */ + retval = dap_find_ap(swjdp, AP_TYPE_APB_AP, &armv8->debug_ap); + if (retval != ERROR_OK) { + LOG_ERROR("Could not find APB-AP for debug access"); + return retval; + } + + retval = mem_ap_init(armv8->debug_ap); + if (retval != ERROR_OK) { + LOG_ERROR("Could not initialize the APB-AP"); + return retval; + } + + armv8->debug_ap->memaccess_tck = 80; + + /* Search for the AHB-AB */ + armv8->memory_ap_available = false; + retval = dap_find_ap(swjdp, AP_TYPE_AHB_AP, &armv8->memory_ap); + if (retval == ERROR_OK) { + retval = mem_ap_init(armv8->memory_ap); + if (retval == ERROR_OK) + armv8->memory_ap_available = true; + } + if (retval != ERROR_OK) { + /* AHB-AP not found or unavailable - use the CPU */ + LOG_DEBUG("No AHB-AP available for memory access"); + } + + + if (!target->dbgbase_set) { + uint32_t dbgbase; + /* Get ROM Table base */ + uint32_t apid; + int32_t coreidx = target->coreid; + retval = dap_get_debugbase(armv8->debug_ap, &dbgbase, &apid); + if (retval != ERROR_OK) + return retval; + /* Lookup 0x15 -- Processor DAP */ + retval = dap_lookup_cs_component(armv8->debug_ap, dbgbase, 0x15, + &armv8->debug_base, &coreidx); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("Detected core %" PRId32 " dbgbase: %08" PRIx32, + coreidx, armv8->debug_base); + } else + armv8->debug_base = target->dbgbase; + + retval = mem_ap_write_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x300, 0); + if (retval != ERROR_OK) { + LOG_DEBUG("Examine %s failed", "oslock"); + return retval; + } + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x88, &cpuid); + LOG_DEBUG("0x88 = %x", cpuid); + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x314, &cpuid); + LOG_DEBUG("0x314 = %x", cpuid); + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + 0x310, &cpuid); + LOG_DEBUG("0x310 = %x", cpuid); + if (retval != ERROR_OK) + return retval; + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_CPUID, &cpuid); + if (retval != ERROR_OK) { + LOG_DEBUG("Examine %s failed", "CPUID"); + return retval; + } + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_CTYPR, &ctypr); + if (retval != ERROR_OK) { + LOG_DEBUG("Examine %s failed", "CTYPR"); + return retval; + } + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUDBG_TTYPR, &ttypr); + if (retval != ERROR_OK) { + LOG_DEBUG("Examine %s failed", "TTYPR"); + return retval; + } + + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + ID_AA64PFR0_EL1, &pfr); + if (retval != ERROR_OK) { + LOG_DEBUG("Examine %s failed", "ID_AA64DFR0_EL1"); + return retval; + } + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + ID_AA64DFR0_EL1, &debug); + if (retval != ERROR_OK) { + LOG_DEBUG("Examine %s failed", "ID_AA64DFR0_EL1"); + return retval; + } + + LOG_DEBUG("cpuid = 0x%08" PRIx32, cpuid); + LOG_DEBUG("ctypr = 0x%08" PRIx32, ctypr); + LOG_DEBUG("ttypr = 0x%08" PRIx32, ttypr); + LOG_DEBUG("ID_AA64PFR0_EL1 = 0x%08" PRIx32, pfr); + LOG_DEBUG("ID_AA64DFR0_EL1 = 0x%08" PRIx32, debug); + + armv8->arm.core_type = ARM_MODE_MON; + armv8->arm.core_state = ARM_STATE_AARCH64; + retval = aarch64_dpm_setup(aarch64, debug); + if (retval != ERROR_OK) + return retval; + + /* Setup Breakpoint Register Pairs */ + aarch64->brp_num = ((debug >> 12) & 0x0F) + 1; + aarch64->brp_num_context = ((debug >> 28) & 0x0F) + 1; + + /* hack - no context bpt support yet */ + aarch64->brp_num_context = 0; + + aarch64->brp_num_available = aarch64->brp_num; + aarch64->brp_list = calloc(aarch64->brp_num, sizeof(struct aarch64_brp)); + for (i = 0; i < aarch64->brp_num; i++) { + aarch64->brp_list[i].used = 0; + if (i < (aarch64->brp_num-aarch64->brp_num_context)) + aarch64->brp_list[i].type = BRP_NORMAL; + else + aarch64->brp_list[i].type = BRP_CONTEXT; + aarch64->brp_list[i].value = 0; + aarch64->brp_list[i].control = 0; + aarch64->brp_list[i].BRPn = i; + } + + LOG_DEBUG("Configured %i hw breakpoints", aarch64->brp_num); + + target_set_examined(target); + return ERROR_OK; +} + +static int aarch64_examine(struct target *target) +{ + int retval = ERROR_OK; + + /* don't re-probe hardware after each reset */ + if (!target_was_examined(target)) + retval = aarch64_examine_first(target); + + /* Configure core debug access */ + if (retval == ERROR_OK) + retval = aarch64_init_debug_access(target); + + return retval; +} + +/* + * Cortex-A8 target creation and initialization + */ + +static int aarch64_init_target(struct command_context *cmd_ctx, + struct target *target) +{ + /* examine_first() does a bunch of this */ + return ERROR_OK; +} + +static int aarch64_init_arch_info(struct target *target, + struct aarch64_common *aarch64, struct jtag_tap *tap) +{ + struct armv8_common *armv8 = &aarch64->armv8_common; + struct adiv5_dap *dap = armv8->arm.dap; + + armv8->arm.dap = dap; + + /* Setup struct aarch64_common */ + aarch64->common_magic = AARCH64_COMMON_MAGIC; + /* tap has no dap initialized */ + if (!tap->dap) { + tap->dap = dap_init(); + + /* Leave (only) generic DAP stuff for debugport_init() */ + tap->dap->tap = tap; + } + + armv8->arm.dap = tap->dap; + + aarch64->fast_reg_read = 0; + + /* register arch-specific functions */ + armv8->examine_debug_reason = NULL; + + armv8->post_debug_entry = aarch64_post_debug_entry; + + armv8->pre_restore_context = NULL; + + armv8->armv8_mmu.read_physical_memory = aarch64_read_phys_memory; + + /* REVISIT v7a setup should be in a v7a-specific routine */ + armv8_init_arch_info(target, armv8); + target_register_timer_callback(aarch64_handle_target_request, 1, 1, target); + + return ERROR_OK; +} + +static int aarch64_target_create(struct target *target, Jim_Interp *interp) +{ + struct aarch64_common *aarch64 = calloc(1, sizeof(struct aarch64_common)); + + aarch64->armv8_common.is_armv7r = false; + + return aarch64_init_arch_info(target, aarch64, target->tap); +} + +static int aarch64_mmu(struct target *target, int *enabled) +{ + if (target->state != TARGET_HALTED) { + LOG_ERROR("%s: target not halted", __func__); + return ERROR_TARGET_INVALID; + } + + *enabled = target_to_aarch64(target)->armv8_common.armv8_mmu.mmu_enabled; + return ERROR_OK; +} + +static int aarch64_virt2phys(struct target *target, target_addr_t virt, + target_addr_t *phys) +{ + int retval = ERROR_FAIL; + struct armv8_common *armv8 = target_to_armv8(target); + struct adiv5_dap *swjdp = armv8->arm.dap; + uint8_t apsel = swjdp->apsel; + if (armv8->memory_ap_available && (apsel == armv8->memory_ap->ap_num)) { + uint32_t ret; + retval = armv8_mmu_translate_va(target, + virt, &ret); + if (retval != ERROR_OK) + goto done; + *phys = ret; + } else {/* use this method if armv8->memory_ap not selected + * mmu must be enable in order to get a correct translation */ + retval = aarch64_mmu_modify(target, 1); + if (retval != ERROR_OK) + goto done; + retval = armv8_mmu_translate_va_pa(target, virt, phys, 1); + } +done: + return retval; +} + +COMMAND_HANDLER(aarch64_handle_cache_info_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct armv8_common *armv8 = target_to_armv8(target); + + return armv8_handle_cache_info_command(CMD_CTX, + &armv8->armv8_mmu.armv8_cache); +} + + +COMMAND_HANDLER(aarch64_handle_dbginit_command) +{ + struct target *target = get_current_target(CMD_CTX); + if (!target_was_examined(target)) { + LOG_ERROR("target not examined yet"); + return ERROR_FAIL; + } + + return aarch64_init_debug_access(target); +} +COMMAND_HANDLER(aarch64_handle_smp_off_command) +{ + struct target *target = get_current_target(CMD_CTX); + /* check target is an smp target */ + struct target_list *head; + struct target *curr; + head = target->head; + target->smp = 0; + if (head != (struct target_list *)NULL) { + while (head != (struct target_list *)NULL) { + curr = head->target; + curr->smp = 0; + head = head->next; + } + /* fixes the target display to the debugger */ + target->gdb_service->target = target; + } + return ERROR_OK; +} + +COMMAND_HANDLER(aarch64_handle_smp_on_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct target_list *head; + struct target *curr; + head = target->head; + if (head != (struct target_list *)NULL) { + target->smp = 1; + while (head != (struct target_list *)NULL) { + curr = head->target; + curr->smp = 1; + head = head->next; + } + } + return ERROR_OK; +} + +COMMAND_HANDLER(aarch64_handle_smp_gdb_command) +{ + struct target *target = get_current_target(CMD_CTX); + int retval = ERROR_OK; + struct target_list *head; + head = target->head; + if (head != (struct target_list *)NULL) { + if (CMD_ARGC == 1) { + int coreid = 0; + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], coreid); + if (ERROR_OK != retval) + return retval; + target->gdb_service->core[1] = coreid; + + } + command_print(CMD_CTX, "gdb coreid %" PRId32 " -> %" PRId32, target->gdb_service->core[0] + , target->gdb_service->core[1]); + } + return ERROR_OK; +} + +static const struct command_registration aarch64_exec_command_handlers[] = { + { + .name = "cache_info", + .handler = aarch64_handle_cache_info_command, + .mode = COMMAND_EXEC, + .help = "display information about target caches", + .usage = "", + }, + { + .name = "dbginit", + .handler = aarch64_handle_dbginit_command, + .mode = COMMAND_EXEC, + .help = "Initialize core debug", + .usage = "", + }, + { .name = "smp_off", + .handler = aarch64_handle_smp_off_command, + .mode = COMMAND_EXEC, + .help = "Stop smp handling", + .usage = "", + }, + { + .name = "smp_on", + .handler = aarch64_handle_smp_on_command, + .mode = COMMAND_EXEC, + .help = "Restart smp handling", + .usage = "", + }, + { + .name = "smp_gdb", + .handler = aarch64_handle_smp_gdb_command, + .mode = COMMAND_EXEC, + .help = "display/fix current core played to gdb", + .usage = "", + }, + + + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration aarch64_command_handlers[] = { + { + .chain = arm_command_handlers, + }, + { + .chain = armv8_command_handlers, + }, + { + .name = "cortex_a", + .mode = COMMAND_ANY, + .help = "Cortex-A command group", + .usage = "", + .chain = aarch64_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct target_type aarch64_target = { + .name = "aarch64", + + .poll = aarch64_poll, + .arch_state = armv8_arch_state, + + .halt = aarch64_halt, + .resume = aarch64_resume, + .step = aarch64_step, + + .assert_reset = aarch64_assert_reset, + .deassert_reset = aarch64_deassert_reset, + + /* REVISIT allow exporting VFP3 registers ... */ + .get_gdb_reg_list = armv8_get_gdb_reg_list, + + .read_memory = aarch64_read_memory, + .write_memory = aarch64_write_memory, + + .checksum_memory = arm_checksum_memory, + .blank_check_memory = arm_blank_check_memory, + + .run_algorithm = armv4_5_run_algorithm, + + .add_breakpoint = aarch64_add_breakpoint, + .add_context_breakpoint = aarch64_add_context_breakpoint, + .add_hybrid_breakpoint = aarch64_add_hybrid_breakpoint, + .remove_breakpoint = aarch64_remove_breakpoint, + .add_watchpoint = NULL, + .remove_watchpoint = NULL, + + .commands = aarch64_command_handlers, + .target_create = aarch64_target_create, + .init_target = aarch64_init_target, + .examine = aarch64_examine, + + .read_phys_memory = aarch64_read_phys_memory, + .write_phys_memory = aarch64_write_phys_memory, + .mmu = aarch64_mmu, + .virt2phys = aarch64_virt2phys, +}; diff --git a/src/target/aarch64.h b/src/target/aarch64.h new file mode 100644 index 00000000..513f06d1 --- /dev/null +++ b/src/target/aarch64.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2015 by David Ung * + * * + * 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., * + ***************************************************************************/ + +#ifndef AARCH64_H +#define AARCH64_H + +#include "armv8.h" + +#define AARCH64_COMMON_MAGIC 0x411fc082 + +#define CPUDBG_CPUID 0xD00 +#define CPUDBG_CTYPR 0xD04 +#define CPUDBG_TTYPR 0xD0C +#define ID_AA64PFR0_EL1 0xD20 +#define ID_AA64DFR0_EL1 0xD28 +#define CPUDBG_LOCKACCESS 0xFB0 +#define CPUDBG_LOCKSTATUS 0xFB4 + +#define BRP_NORMAL 0 +#define BRP_CONTEXT 1 + +#define AARCH64_PADDRDBG_CPU_SHIFT 13 + +struct aarch64_brp { + int used; + int type; + target_addr_t value; + uint32_t control; + uint8_t BRPn; +}; + +struct aarch64_common { + int common_magic; + + /* Context information */ + uint32_t cpudbg_dscr; + + uint32_t system_control_reg; + uint32_t system_control_reg_curr; + + enum arm_mode curr_mode; + + + /* Breakpoint register pairs */ + int brp_num_context; + int brp_num; + int brp_num_available; + struct aarch64_brp *brp_list; + + /* Use aarch64_read_regs_through_mem for fast register reads */ + int fast_reg_read; + + struct armv8_common armv8_common; + +}; + +static inline struct aarch64_common * +target_to_aarch64(struct target *target) +{ + return container_of(target->arch_info, struct aarch64_common, armv8_common.arm); +} + +#endif /* AARCH64_H */ diff --git a/src/target/arm.h b/src/target/arm.h index 5713fc08..e4f20383 100644 --- a/src/target/arm.h +++ b/src/target/arm.h @@ -218,6 +218,7 @@ struct arm_reg { }; struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm); +struct reg_cache *armv8_build_reg_cache(struct target *target); extern const struct command_registration arm_command_handlers[]; @@ -225,6 +226,9 @@ int arm_arch_state(struct target *target); int arm_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size, enum target_register_class reg_class); +int armv8_get_gdb_reg_list(struct target *target, + struct reg **reg_list[], int *reg_list_size, + enum target_register_class reg_class); int arm_init_arch_info(struct target *target, struct arm *arm); @@ -249,6 +253,7 @@ int arm_blank_check_memory(struct target *target, void arm_set_cpsr(struct arm *arm, uint32_t cpsr); struct reg *arm_reg_current(struct arm *arm, unsigned regnum); +struct reg *armv8_reg_current(struct arm *arm, unsigned regnum); extern struct reg arm_gdb_dummy_fp_reg; extern struct reg arm_gdb_dummy_fps_reg; diff --git a/src/target/arm_dpm.c b/src/target/arm_dpm.c index 0c84be54..55f253e4 100644 --- a/src/target/arm_dpm.c +++ b/src/target/arm_dpm.c @@ -299,10 +299,15 @@ static int dpm_write_reg64(struct arm_dpm *dpm, struct reg *r, unsigned regnum) switch (regnum) { case 0 ... 30: i = 0xd5330400 + regnum; - retval = dpm->instr_write_data_dcc(dpm, i, value); + retval = dpm->instr_write_data_dcc_64(dpm, i, value); + break; + case 32: /* PC */ + i = 0xd51b4520; + retval = dpm->instr_write_data_r0_64(dpm, i, value); break; - default: + LOG_DEBUG("register %s (%16.16llx) not defined", r->name, + (unsigned long long)value); break; } @@ -354,6 +359,9 @@ static int arm_dpm_read_current_registers_i(struct arm_dpm *dpm) /* update core mode and state, plus shadow mapping for R8..R14 */ arm_set_cpsr(arm, cpsr); + if (core_state == ARM_STATE_AARCH64) + /* arm_set_cpsr changes core_state, restore it for now */ + arm->core_state = ARM_STATE_AARCH64; core_regs = arm->core_cache->num_regs; @@ -573,7 +581,7 @@ static int arm_dpm_write_dirty_registers_64(struct arm_dpm *dpm) */ /* check everything except our scratch register R0 */ - for (unsigned i = 1; i < 32; i++) { + for (unsigned i = 1; i <= 32; i++) { struct arm_reg *r; unsigned regnum; @@ -1119,7 +1127,7 @@ int arm_dpm_setup(struct arm_dpm *dpm) { struct arm *arm = dpm->arm; struct target *target = arm->target; - struct reg_cache *cache; + struct reg_cache *cache = 0; arm->dpm = dpm; @@ -1128,13 +1136,17 @@ int arm_dpm_setup(struct arm_dpm *dpm) arm->read_core_reg = arm->read_core_reg ? : arm_dpm_read_core_reg; arm->write_core_reg = arm->write_core_reg ? : arm_dpm_write_core_reg; - /* avoid duplicating the register cache */ - if (arm->core_cache == NULL) { - cache = arm_build_reg_cache(target, arm); + if (arm->core_cache != NULL) { + if (arm->core_state == ARM_STATE_AARCH64) { + cache = armv8_build_reg_cache(target); + target->reg_cache = cache; + } else { + cache = arm_build_reg_cache(target, arm); + *register_get_last_cache_p(&target->reg_cache) = cache; + } + if (!cache) return ERROR_FAIL; - - *register_get_last_cache_p(&target->reg_cache) = cache; } /* coprocessor access setup */ diff --git a/src/target/arm_dpm.h b/src/target/arm_dpm.h index 9cc0304a..5b16e648 100644 --- a/src/target/arm_dpm.h +++ b/src/target/arm_dpm.h @@ -73,10 +73,17 @@ struct arm_dpm { int (*instr_write_data_dcc)(struct arm_dpm *, uint32_t opcode, uint32_t data); + int (*instr_write_data_dcc_64)(struct arm_dpm *, + uint32_t opcode, uint64_t data); + /** Runs one instruction, writing data to R0 before execution. */ int (*instr_write_data_r0)(struct arm_dpm *, uint32_t opcode, uint32_t data); + /** Runs one instruction, writing data to R0 before execution. */ + int (*instr_write_data_r0_64)(struct arm_dpm *, + uint32_t opcode, uint64_t data); + /** Optional core-specific operation invoked after CPSR writes. */ int (*instr_cpsr_sync)(struct arm_dpm *dpm); diff --git a/src/target/armv8.c b/src/target/armv8.c new file mode 100644 index 00000000..902d2901 --- /dev/null +++ b/src/target/armv8.c @@ -0,0 +1,774 @@ +/*************************************************************************** + * Copyright (C) 2015 by David Ung * + * * + * 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., * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "armv8.h" +#include "arm_disassembler.h" + +#include "register.h" +#include +#include + +#include +#include +#include + +#include "arm_opcodes.h" +#include "target.h" +#include "target_type.h" + +static int armv8_read_core_reg(struct target *target, struct reg *r, + int num, enum arm_mode mode) +{ + uint64_t reg_value; + int retval; + struct arm_reg *armv8_core_reg; + struct armv8_common *armv8 = target_to_armv8(target); + + assert(num < (int)armv8->arm.core_cache->num_regs); + + armv8_core_reg = armv8->arm.core_cache->reg_list[num].arch_info; + retval = armv8->load_core_reg_u64(target, + armv8_core_reg->num, ®_value); + + buf_set_u64(armv8->arm.core_cache->reg_list[num].value, 0, 64, reg_value); + armv8->arm.core_cache->reg_list[num].valid = 1; + armv8->arm.core_cache->reg_list[num].dirty = 0; + + return retval; +} + +#if 0 +static int armv8_write_core_reg(struct target *target, struct reg *r, + int num, enum arm_mode mode, target_addr_t value) +{ + int retval; + struct arm_reg *armv8_core_reg; + struct armv8_common *armv8 = target_to_armv8(target); + + assert(num < (int)armv8->arm.core_cache->num_regs); + + armv8_core_reg = armv8->arm.core_cache->reg_list[num].arch_info; + retval = armv8->store_core_reg_u64(target, + armv8_core_reg->num, + value); + if (retval != ERROR_OK) { + LOG_ERROR("JTAG failure"); + armv8->arm.core_cache->reg_list[num].dirty = armv8->arm.core_cache->reg_list[num].valid; + return ERROR_JTAG_DEVICE_ERROR; + } + + LOG_DEBUG("write core reg %i value 0x%" PRIx64 "", num, value); + armv8->arm.core_cache->reg_list[num].valid = 1; + armv8->arm.core_cache->reg_list[num].dirty = 0; + + return ERROR_OK; +} +#endif + +static void armv8_show_fault_registers(struct target *target) +{ + /* TODO */ +} + +static int armv8_read_ttbcr(struct target *target) +{ + struct armv8_common *armv8 = target_to_armv8(target); + struct arm_dpm *dpm = armv8->arm.dpm; + uint32_t ttbcr; + int retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + goto done; + /* MRC p15,0,,c2,c0,2 ; Read CP15 Translation Table Base Control Register*/ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 0, 0, 2, 0, 2), + &ttbcr); + if (retval != ERROR_OK) + goto done; + armv8->armv8_mmu.ttbr1_used = ((ttbcr & 0x7) != 0) ? 1 : 0; + armv8->armv8_mmu.ttbr0_mask = 7 << (32 - ((ttbcr & 0x7))); +#if 0 + LOG_INFO("ttb1 %s ,ttb0_mask %x", + armv8->armv8_mmu.ttbr1_used ? "used" : "not used", + armv8->armv8_mmu.ttbr0_mask); +#endif + if (armv8->armv8_mmu.ttbr1_used == 1) { + LOG_INFO("SVC access above %" PRIx32, + (uint32_t)(0xffffffff & armv8->armv8_mmu.ttbr0_mask)); + armv8->armv8_mmu.os_border = 0xffffffff & armv8->armv8_mmu.ttbr0_mask; + } else { + /* fix me , default is hard coded LINUX border */ + armv8->armv8_mmu.os_border = 0xc0000000; + } +done: + dpm->finish(dpm); + return retval; +} + + +/* method adapted to cortex A : reused arm v4 v5 method*/ +int armv8_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val) +{ + uint32_t first_lvl_descriptor = 0x0; + uint32_t second_lvl_descriptor = 0x0; + int retval; + struct armv8_common *armv8 = target_to_armv8(target); + struct arm_dpm *dpm = armv8->arm.dpm; + uint32_t ttb = 0; /* default ttb0 */ + if (armv8->armv8_mmu.ttbr1_used == -1) + armv8_read_ttbcr(target); + if ((armv8->armv8_mmu.ttbr1_used) && + (va > (0xffffffff & armv8->armv8_mmu.ttbr0_mask))) { + /* select ttb 1 */ + ttb = 1; + } + retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + goto done; + + /* MRC p15,0,,c2,c0,ttb */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 0, 0, 2, 0, ttb), + &ttb); + if (retval != ERROR_OK) + return retval; + retval = armv8->armv8_mmu.read_physical_memory(target, + (ttb & 0xffffc000) | ((va & 0xfff00000) >> 18), + 4, 1, (uint8_t *)&first_lvl_descriptor); + if (retval != ERROR_OK) + return retval; + first_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) + &first_lvl_descriptor); + /* reuse armv4_5 piece of code, specific armv8 changes may come later */ + LOG_DEBUG("1st lvl desc: %8.8" PRIx32 "", first_lvl_descriptor); + + if ((first_lvl_descriptor & 0x3) == 0) { + LOG_ERROR("Address translation failure"); + return ERROR_TARGET_TRANSLATION_FAULT; + } + + + if ((first_lvl_descriptor & 0x3) == 2) { + /* section descriptor */ + *val = (first_lvl_descriptor & 0xfff00000) | (va & 0x000fffff); + return ERROR_OK; + } + + if ((first_lvl_descriptor & 0x3) == 1) { + /* coarse page table */ + retval = armv8->armv8_mmu.read_physical_memory(target, + (first_lvl_descriptor & 0xfffffc00) | ((va & 0x000ff000) >> 10), + 4, 1, (uint8_t *)&second_lvl_descriptor); + if (retval != ERROR_OK) + return retval; + } else if ((first_lvl_descriptor & 0x3) == 3) { + /* fine page table */ + retval = armv8->armv8_mmu.read_physical_memory(target, + (first_lvl_descriptor & 0xfffff000) | ((va & 0x000ffc00) >> 8), + 4, 1, (uint8_t *)&second_lvl_descriptor); + if (retval != ERROR_OK) + return retval; + } + + second_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *) + &second_lvl_descriptor); + + LOG_DEBUG("2nd lvl desc: %8.8" PRIx32 "", second_lvl_descriptor); + + if ((second_lvl_descriptor & 0x3) == 0) { + LOG_ERROR("Address translation failure"); + return ERROR_TARGET_TRANSLATION_FAULT; + } + + if ((second_lvl_descriptor & 0x3) == 1) { + /* large page descriptor */ + *val = (second_lvl_descriptor & 0xffff0000) | (va & 0x0000ffff); + return ERROR_OK; + } + + if ((second_lvl_descriptor & 0x3) == 2) { + /* small page descriptor */ + *val = (second_lvl_descriptor & 0xfffff000) | (va & 0x00000fff); + return ERROR_OK; + } + + if ((second_lvl_descriptor & 0x3) == 3) { + *val = (second_lvl_descriptor & 0xfffffc00) | (va & 0x000003ff); + return ERROR_OK; + } + + /* should not happen */ + LOG_ERROR("Address translation failure"); + return ERROR_TARGET_TRANSLATION_FAULT; + +done: + return retval; +} + +/* V8 method VA TO PA */ +int armv8_mmu_translate_va_pa(struct target *target, target_addr_t va, + target_addr_t *val, int meminfo) +{ + return ERROR_OK; +} + +static int armv8_handle_inner_cache_info_command(struct command_context *cmd_ctx, + struct armv8_cache_common *armv8_cache) +{ + if (armv8_cache->ctype == -1) { + command_print(cmd_ctx, "cache not yet identified"); + return ERROR_OK; + } + + command_print(cmd_ctx, + "D-Cache: linelen %" PRIi32 ", associativity %" PRIi32 ", nsets %" PRIi32 ", cachesize %" PRId32 " KBytes", + armv8_cache->d_u_size.linelen, + armv8_cache->d_u_size.associativity, + armv8_cache->d_u_size.nsets, + armv8_cache->d_u_size.cachesize); + + command_print(cmd_ctx, + "I-Cache: linelen %" PRIi32 ", associativity %" PRIi32 ", nsets %" PRIi32 ", cachesize %" PRId32 " KBytes", + armv8_cache->i_size.linelen, + armv8_cache->i_size.associativity, + armv8_cache->i_size.nsets, + armv8_cache->i_size.cachesize); + + return ERROR_OK; +} + +static int _armv8_flush_all_data(struct target *target) +{ + struct armv8_common *armv8 = target_to_armv8(target); + struct arm_dpm *dpm = armv8->arm.dpm; + struct armv8_cachesize *d_u_size = + &(armv8->armv8_mmu.armv8_cache.d_u_size); + int32_t c_way, c_index = d_u_size->index; + int retval; + /* check that cache data is on at target halt */ + if (!armv8->armv8_mmu.armv8_cache.d_u_cache_enabled) { + LOG_INFO("flushed not performed :cache not on at target halt"); + return ERROR_OK; + } + retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + goto done; + do { + c_way = d_u_size->way; + do { + uint32_t value = (c_index << d_u_size->index_shift) + | (c_way << d_u_size->way_shift); + /* DCCISW */ + /* LOG_INFO ("%d %d %x",c_way,c_index,value); */ + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_MCR(15, 0, 0, 7, 14, 2), + value); + if (retval != ERROR_OK) + goto done; + c_way -= 1; + } while (c_way >= 0); + c_index -= 1; + } while (c_index >= 0); + return retval; +done: + LOG_ERROR("flushed failed"); + dpm->finish(dpm); + return retval; +} + +static int armv8_flush_all_data(struct target *target) +{ + int retval = ERROR_FAIL; + /* check that armv8_cache is correctly identify */ + struct armv8_common *armv8 = target_to_armv8(target); + if (armv8->armv8_mmu.armv8_cache.ctype == -1) { + LOG_ERROR("trying to flush un-identified cache"); + return retval; + } + + if (target->smp) { + /* look if all the other target have been flushed in order to flush level + * 2 */ + struct target_list *head; + struct target *curr; + head = target->head; + while (head != (struct target_list *)NULL) { + curr = head->target; + if (curr->state == TARGET_HALTED) { + LOG_INFO("Wait flushing data l1 on core %" PRId32, curr->coreid); + retval = _armv8_flush_all_data(curr); + } + head = head->next; + } + } else + retval = _armv8_flush_all_data(target); + return retval; +} + +int armv8_handle_cache_info_command(struct command_context *cmd_ctx, + struct armv8_cache_common *armv8_cache) +{ + if (armv8_cache->ctype == -1) { + command_print(cmd_ctx, "cache not yet identified"); + return ERROR_OK; + } + + if (armv8_cache->display_cache_info) + armv8_cache->display_cache_info(cmd_ctx, armv8_cache); + return ERROR_OK; +} + +/* retrieve core id cluster id */ +static int armv8_read_mpidr(struct target *target) +{ + int retval = ERROR_FAIL; + struct armv8_common *armv8 = target_to_armv8(target); + struct arm_dpm *dpm = armv8->arm.dpm; + uint32_t mpidr; + retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + goto done; + /* MRC p15,0,,c0,c0,5; read Multiprocessor ID register*/ + + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 0, 0, 0, 0, 5), + &mpidr); + if (retval != ERROR_OK) + goto done; + if (mpidr & 1<<31) { + armv8->multi_processor_system = (mpidr >> 30) & 1; + armv8->cluster_id = (mpidr >> 8) & 0xf; + armv8->cpu_id = mpidr & 0x3; + LOG_INFO("%s cluster %x core %x %s", target_name(target), + armv8->cluster_id, + armv8->cpu_id, + armv8->multi_processor_system == 0 ? "multi core" : "mono core"); + + } else + LOG_ERROR("mpdir not in multiprocessor format"); + +done: + dpm->finish(dpm); + return retval; + + +} + +int armv8_identify_cache(struct target *target) +{ + /* read cache descriptor */ + int retval = ERROR_FAIL; + struct armv8_common *armv8 = target_to_armv8(target); + struct arm_dpm *dpm = armv8->arm.dpm; + uint32_t cache_selected, clidr; + uint32_t cache_i_reg, cache_d_reg; + struct armv8_cache_common *cache = &(armv8->armv8_mmu.armv8_cache); + if (!armv8->is_armv7r) + armv8_read_ttbcr(target); + retval = dpm->prepare(dpm); + + if (retval != ERROR_OK) + goto done; + /* retrieve CLIDR + * mrc p15, 1, r0, c0, c0, 1 @ read clidr */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 1, 0, 0, 0, 1), + &clidr); + if (retval != ERROR_OK) + goto done; + clidr = (clidr & 0x7000000) >> 23; + LOG_INFO("number of cache level %" PRIx32, (uint32_t)(clidr / 2)); + if ((clidr / 2) > 1) { + /* FIXME not supported present in cortex A8 and later */ + /* in cortex A7, A15 */ + LOG_ERROR("cache l2 present :not supported"); + } + /* retrieve selected cache + * MRC p15, 2,, c0, c0, 0; Read CSSELR */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 2, 0, 0, 0, 0), + &cache_selected); + if (retval != ERROR_OK) + goto done; + + retval = armv8->arm.mrc(target, 15, + 2, 0, /* op1, op2 */ + 0, 0, /* CRn, CRm */ + &cache_selected); + if (retval != ERROR_OK) + goto done; + /* select instruction cache + * MCR p15, 2,, c0, c0, 0; Write CSSELR + * [0] : 1 instruction cache selection , 0 data cache selection */ + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_MRC(15, 2, 0, 0, 0, 0), + 1); + if (retval != ERROR_OK) + goto done; + + /* read CCSIDR + * MRC P15,1,,C0, C0,0 ;on cortex A9 read CCSIDR + * [2:0] line size 001 eight word per line + * [27:13] NumSet 0x7f 16KB, 0xff 32Kbytes, 0x1ff 64Kbytes */ + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 1, 0, 0, 0, 0), + &cache_i_reg); + if (retval != ERROR_OK) + goto done; + + /* select data cache*/ + retval = dpm->instr_write_data_r0(dpm, + ARMV4_5_MRC(15, 2, 0, 0, 0, 0), + 0); + if (retval != ERROR_OK) + goto done; + + retval = dpm->instr_read_data_r0(dpm, + ARMV4_5_MRC(15, 1, 0, 0, 0, 0), + &cache_d_reg); + if (retval != ERROR_OK) + goto done; + + /* restore selected cache */ + dpm->instr_write_data_r0(dpm, + ARMV4_5_MRC(15, 2, 0, 0, 0, 0), + cache_selected); + + if (retval != ERROR_OK) + goto done; + dpm->finish(dpm); + + /* put fake type */ + cache->d_u_size.linelen = 16 << (cache_d_reg & 0x7); + cache->d_u_size.cachesize = (((cache_d_reg >> 13) & 0x7fff)+1)/8; + cache->d_u_size.nsets = (cache_d_reg >> 13) & 0x7fff; + cache->d_u_size.associativity = ((cache_d_reg >> 3) & 0x3ff) + 1; + /* compute info for set way operation on cache */ + cache->d_u_size.index_shift = (cache_d_reg & 0x7) + 4; + cache->d_u_size.index = (cache_d_reg >> 13) & 0x7fff; + cache->d_u_size.way = ((cache_d_reg >> 3) & 0x3ff); + cache->d_u_size.way_shift = cache->d_u_size.way + 1; + { + int i = 0; + while (((cache->d_u_size.way_shift >> i) & 1) != 1) + i++; + cache->d_u_size.way_shift = 32-i; + } +#if 0 + LOG_INFO("data cache index %d << %d, way %d << %d", + cache->d_u_size.index, cache->d_u_size.index_shift, + cache->d_u_size.way, + cache->d_u_size.way_shift); + + LOG_INFO("data cache %d bytes %d KBytes asso %d ways", + cache->d_u_size.linelen, + cache->d_u_size.cachesize, + cache->d_u_size.associativity); +#endif + cache->i_size.linelen = 16 << (cache_i_reg & 0x7); + cache->i_size.associativity = ((cache_i_reg >> 3) & 0x3ff) + 1; + cache->i_size.nsets = (cache_i_reg >> 13) & 0x7fff; + cache->i_size.cachesize = (((cache_i_reg >> 13) & 0x7fff)+1)/8; + /* compute info for set way operation on cache */ + cache->i_size.index_shift = (cache_i_reg & 0x7) + 4; + cache->i_size.index = (cache_i_reg >> 13) & 0x7fff; + cache->i_size.way = ((cache_i_reg >> 3) & 0x3ff); + cache->i_size.way_shift = cache->i_size.way + 1; + { + int i = 0; + while (((cache->i_size.way_shift >> i) & 1) != 1) + i++; + cache->i_size.way_shift = 32-i; + } +#if 0 + LOG_INFO("instruction cache index %d << %d, way %d << %d", + cache->i_size.index, cache->i_size.index_shift, + cache->i_size.way, cache->i_size.way_shift); + + LOG_INFO("instruction cache %d bytes %d KBytes asso %d ways", + cache->i_size.linelen, + cache->i_size.cachesize, + cache->i_size.associativity); +#endif + /* if no l2 cache initialize l1 data cache flush function function */ + if (armv8->armv8_mmu.armv8_cache.flush_all_data_cache == NULL) { + armv8->armv8_mmu.armv8_cache.display_cache_info = + armv8_handle_inner_cache_info_command; + armv8->armv8_mmu.armv8_cache.flush_all_data_cache = + armv8_flush_all_data; + } + armv8->armv8_mmu.armv8_cache.ctype = 0; + +done: + dpm->finish(dpm); + armv8_read_mpidr(target); + return retval; + +} + +int armv8_init_arch_info(struct target *target, struct armv8_common *armv8) +{ + struct arm *arm = &armv8->arm; + arm->arch_info = armv8; + target->arch_info = &armv8->arm; + /* target is useful in all function arm v4 5 compatible */ + armv8->arm.target = target; + armv8->arm.common_magic = ARM_COMMON_MAGIC; + armv8->common_magic = ARMV8_COMMON_MAGIC; + + arm->read_core_reg = armv8_read_core_reg; +#if 0 + arm->write_core_reg = armv8_write_core_reg; +#endif + + armv8->armv8_mmu.armv8_cache.l2_cache = NULL; + armv8->armv8_mmu.armv8_cache.ctype = -1; + armv8->armv8_mmu.armv8_cache.flush_all_data_cache = NULL; + armv8->armv8_mmu.armv8_cache.display_cache_info = NULL; + return ERROR_OK; +} + +int armv8_arch_state(struct target *target) +{ + static const char * const state[] = { + "disabled", "enabled" + }; + + struct armv8_common *armv8 = target_to_armv8(target); + struct arm *arm = &armv8->arm; + + if (armv8->common_magic != ARMV8_COMMON_MAGIC) { + LOG_ERROR("BUG: called for a non-Armv8 target"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + arm_arch_state(target); + + if (armv8->is_armv7r) { + LOG_USER("D-Cache: %s, I-Cache: %s", + state[armv8->armv8_mmu.armv8_cache.d_u_cache_enabled], + state[armv8->armv8_mmu.armv8_cache.i_cache_enabled]); + } else { + LOG_USER("MMU: %s, D-Cache: %s, I-Cache: %s", + state[armv8->armv8_mmu.mmu_enabled], + state[armv8->armv8_mmu.armv8_cache.d_u_cache_enabled], + state[armv8->armv8_mmu.armv8_cache.i_cache_enabled]); + } + + if (arm->core_mode == ARM_MODE_ABT) + armv8_show_fault_registers(target); + if (target->debug_reason == DBG_REASON_WATCHPOINT) + LOG_USER("Watchpoint triggered at PC %#08x", + (unsigned) armv8->dpm.wp_pc); + + return ERROR_OK; +} + +static const struct { + unsigned id; + const char *name; + unsigned bits; + enum reg_type type; + const char *group; + const char *feature; +} armv8_regs[] = { + { ARMV8_R0, "x0", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R1, "x1", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R2, "x2", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R3, "x3", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R4, "x4", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R5, "x5", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R6, "x6", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R7, "x7", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R8, "x8", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R9, "x9", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R10, "x10", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R11, "x11", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R12, "x12", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R13, "x13", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R14, "x14", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R15, "x15", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R16, "x16", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R17, "x17", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R18, "x18", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R19, "x19", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R20, "x20", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R21, "x21", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R22, "x22", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R23, "x23", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R24, "x24", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R25, "x25", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R26, "x26", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R27, "x27", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R28, "x28", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R29, "x29", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_R30, "x30", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, + + { ARMV8_R31, "sp", 64, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.aarch64.core" }, + { ARMV8_PC, "pc", 64, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.aarch64.core" }, + + { ARMV8_xPSR, "CPSR", 64, REG_TYPE_INT, "general", "org.gnu.gdb.aarch64.core" }, +}; + +#define ARMV8_NUM_REGS ARRAY_SIZE(armv8_regs) + + +static int armv8_get_core_reg(struct reg *reg) +{ + int retval; + struct arm_reg *armv8_reg = reg->arch_info; + struct target *target = armv8_reg->target; + struct arm *arm = target_to_arm(target); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + retval = arm->read_core_reg(target, reg, armv8_reg->num, arm->core_mode); + + return retval; +} + +static int armv8_set_core_reg(struct reg *reg, uint8_t *buf) +{ + struct arm_reg *armv8_reg = reg->arch_info; + struct target *target = armv8_reg->target; + uint64_t value = buf_get_u64(buf, 0, 64); + + if (target->state != TARGET_HALTED) + return ERROR_TARGET_NOT_HALTED; + + buf_set_u64(reg->value, 0, 64, value); + reg->dirty = 1; + reg->valid = 1; + + return ERROR_OK; +} + +static const struct reg_arch_type armv8_reg_type = { + .get = armv8_get_core_reg, + .set = armv8_set_core_reg, +}; + +/** Builds cache of architecturally defined registers. */ +struct reg_cache *armv8_build_reg_cache(struct target *target) +{ + struct armv8_common *armv8 = target_to_armv8(target); + struct arm *arm = &armv8->arm; + int num_regs = ARMV8_NUM_REGS; + struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache); + struct reg_cache *cache = malloc(sizeof(struct reg_cache)); + struct reg *reg_list = calloc(num_regs, sizeof(struct reg)); + struct arm_reg *arch_info = calloc(num_regs, sizeof(struct arm_reg)); + struct reg_feature *feature; + int i; + + /* Build the process context cache */ + cache->name = "arm v8 registers"; + cache->next = NULL; + cache->reg_list = reg_list; + cache->num_regs = num_regs; + (*cache_p) = cache; + + for (i = 0; i < num_regs; i++) { + arch_info[i].num = armv8_regs[i].id; + arch_info[i].target = target; + arch_info[i].arm = arm; + + reg_list[i].name = armv8_regs[i].name; + reg_list[i].size = armv8_regs[i].bits; + reg_list[i].value = calloc(1, 4); + reg_list[i].dirty = 0; + reg_list[i].valid = 0; + reg_list[i].type = &armv8_reg_type; + reg_list[i].arch_info = &arch_info[i]; + + reg_list[i].group = armv8_regs[i].group; + reg_list[i].number = i; + reg_list[i].exist = true; + reg_list[i].caller_save = true; /* gdb defaults to true */ + + feature = calloc(1, sizeof(struct reg_feature)); + if (feature) { + feature->name = armv8_regs[i].feature; + reg_list[i].feature = feature; + } else + LOG_ERROR("unable to allocate feature list"); + + reg_list[i].reg_data_type = calloc(1, sizeof(struct reg_data_type)); + if (reg_list[i].reg_data_type) + reg_list[i].reg_data_type->type = armv8_regs[i].type; + else + LOG_ERROR("unable to allocate reg type list"); + } + + arm->cpsr = reg_list + ARMV8_xPSR; + arm->pc = reg_list + ARMV8_PC; + arm->core_cache = cache; + + return cache; +} + +struct reg *armv8_reg_current(struct arm *arm, unsigned regnum) +{ + struct reg *r; + + if (regnum > 33) + return NULL; + + r = arm->core_cache->reg_list + regnum; + return r; +} + +const struct command_registration armv8_command_handlers[] = { + { + .chain = dap_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + + +int armv8_get_gdb_reg_list(struct target *target, + struct reg **reg_list[], int *reg_list_size, + enum target_register_class reg_class) +{ + struct arm *arm = target_to_arm(target); + int i; + + switch (reg_class) { + case REG_CLASS_GENERAL: + case REG_CLASS_ALL: + *reg_list_size = 34; + *reg_list = malloc(sizeof(struct reg *) * (*reg_list_size)); + + for (i = 0; i < *reg_list_size; i++) + (*reg_list)[i] = armv8_reg_current(arm, i); + + return ERROR_OK; + break; + + default: + LOG_ERROR("not a valid register class type in query."); + return ERROR_FAIL; + break; + } +} diff --git a/src/target/armv8.h b/src/target/armv8.h new file mode 100644 index 00000000..080237d4 --- /dev/null +++ b/src/target/armv8.h @@ -0,0 +1,201 @@ +/*************************************************************************** + * Copyright (C) 2015 by David Ung * + * * + * 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., * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ARMV8_H +#define OPENOCD_TARGET_ARMV8_H + +#include "arm_adi_v5.h" +#include "arm.h" +#include "armv4_5_mmu.h" +#include "armv4_5_cache.h" +#include "arm_dpm.h" + +enum { + ARMV8_R0, + ARMV8_R1, + ARMV8_R2, + ARMV8_R3, + ARMV8_R4, + ARMV8_R5, + ARMV8_R6, + ARMV8_R7, + ARMV8_R8, + ARMV8_R9, + ARMV8_R10, + ARMV8_R11, + ARMV8_R12, + ARMV8_R13, + ARMV8_R14, + ARMV8_R15, + ARMV8_R16, + ARMV8_R17, + ARMV8_R18, + ARMV8_R19, + ARMV8_R20, + ARMV8_R21, + ARMV8_R22, + ARMV8_R23, + ARMV8_R24, + ARMV8_R25, + ARMV8_R26, + ARMV8_R27, + ARMV8_R28, + ARMV8_R29, + ARMV8_R30, + ARMV8_R31, + + ARMV8_PC = 32, + ARMV8_xPSR = 33, + + ARMV8_LAST_REG, +}; + + +#define ARMV8_COMMON_MAGIC 0x0A450AAA + +/* VA to PA translation operations opc2 values*/ +#define V2PCWPR 0 +#define V2PCWPW 1 +#define V2PCWUR 2 +#define V2PCWUW 3 +#define V2POWPR 4 +#define V2POWPW 5 +#define V2POWUR 6 +#define V2POWUW 7 +/* L210/L220 cache controller support */ +struct armv8_l2x_cache { + uint32_t base; + uint32_t way; +}; + +struct armv8_cachesize { + uint32_t level_num; + /* cache dimensionning */ + uint32_t linelen; + uint32_t associativity; + uint32_t nsets; + uint32_t cachesize; + /* info for set way operation on cache */ + uint32_t index; + uint32_t index_shift; + uint32_t way; + uint32_t way_shift; +}; + +struct armv8_cache_common { + int ctype; + struct armv8_cachesize d_u_size; /* data cache */ + struct armv8_cachesize i_size; /* instruction cache */ + int i_cache_enabled; + int d_u_cache_enabled; + /* l2 external unified cache if some */ + void *l2_cache; + int (*flush_all_data_cache)(struct target *target); + int (*display_cache_info)(struct command_context *cmd_ctx, + struct armv8_cache_common *armv8_cache); +}; + +struct armv8_mmu_common { + /* following field mmu working way */ + int32_t ttbr1_used; /* -1 not initialized, 0 no ttbr1 1 ttbr1 used and */ + uint32_t ttbr0_mask;/* masked to be used */ + uint32_t os_border; + + int (*read_physical_memory)(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer); + struct armv8_cache_common armv8_cache; + uint32_t mmu_enabled; +}; + +struct armv8_common { + struct arm arm; + int common_magic; + struct reg_cache *core_cache; + + /* Core Debug Unit */ + struct arm_dpm dpm; + uint32_t debug_base; + struct adiv5_ap *debug_ap; + struct adiv5_ap *memory_ap; + bool memory_ap_available; + /* mdir */ + uint8_t multi_processor_system; + uint8_t cluster_id; + uint8_t cpu_id; + bool is_armv7r; + + /* cache specific to V7 Memory Management Unit compatible with v4_5*/ + struct armv8_mmu_common armv8_mmu; + + /* Direct processor core register read and writes */ + int (*load_core_reg_u64)(struct target *target, uint32_t num, uint64_t *value); + int (*store_core_reg_u64)(struct target *target, uint32_t num, uint64_t value); + + int (*examine_debug_reason)(struct target *target); + int (*post_debug_entry)(struct target *target); + + void (*pre_restore_context)(struct target *target); +}; + +static inline struct armv8_common * +target_to_armv8(struct target *target) +{ + return container_of(target->arch_info, struct armv8_common, arm); +} + +/* register offsets from armv8.debug_base */ + +#define CPUDBG_WFAR 0x018 +/* PCSR at 0x084 -or- 0x0a0 -or- both ... based on flags in DIDR */ +#define CPUDBG_DSCR 0x088 +#define CPUDBG_DRCR 0x090 +#define CPUDBG_PRCR 0x310 +#define CPUDBG_PRSR 0x314 + +#define CPUDBG_DTRRX 0x080 +#define CPUDBG_ITR 0x084 +#define CPUDBG_DTRTX 0x08c + +#define CPUDBG_BVR_BASE 0x400 +#define CPUDBG_BCR_BASE 0x408 +#define CPUDBG_WVR_BASE 0x180 +#define CPUDBG_WCR_BASE 0x1C0 +#define CPUDBG_VCR 0x01C + +#define CPUDBG_OSLAR 0x300 +#define CPUDBG_OSLSR 0x304 +#define CPUDBG_OSSRR 0x308 +#define CPUDBG_ECR 0x024 + +#define CPUDBG_DSCCR 0x028 + +#define CPUDBG_AUTHSTATUS 0xFB8 + +int armv8_arch_state(struct target *target); +int armv8_identify_cache(struct target *target); +int armv8_init_arch_info(struct target *target, struct armv8_common *armv8); +int armv8_mmu_translate_va_pa(struct target *target, target_addr_t va, + target_addr_t *val, int meminfo); +int armv8_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val); + +int armv8_handle_cache_info_command(struct command_context *cmd_ctx, + struct armv8_cache_common *armv8_cache); + +extern const struct command_registration armv8_command_handlers[]; + +#endif diff --git a/src/target/target.c b/src/target/target.c index 40baf8ed..5c2164f7 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -88,6 +88,7 @@ extern struct target_type dragonite_target; extern struct target_type xscale_target; extern struct target_type cortexm_target; extern struct target_type cortexa_target; +extern struct target_type aarch64_target; extern struct target_type cortexr4_target; extern struct target_type arm11_target; extern struct target_type ls1_sap_target; @@ -135,6 +136,9 @@ static struct target_type *target_types[] = { &or1k_target, &quark_x10xx_target, &quark_d20xx_target, +#if BUILD_TARGET64 + &aarch64_target, +#endif NULL, };