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 <david.ung.42@gmail.com> Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>gitignore-build
parent
84a0bb4a3c
commit
d376f7f518
|
@ -26,6 +26,10 @@ noinst_LTLIBRARIES += %D%/libtarget.la
|
||||||
%D%/dsp5680xx.c \
|
%D%/dsp5680xx.c \
|
||||||
%D%/hla_target.c
|
%D%/hla_target.c
|
||||||
|
|
||||||
|
if TARGET64
|
||||||
|
%C%_libtarget_la_SOURCES +=$(ARMV8_SRC)
|
||||||
|
endif
|
||||||
|
|
||||||
TARGET_CORE_SRC = \
|
TARGET_CORE_SRC = \
|
||||||
%D%/algorithm.c \
|
%D%/algorithm.c \
|
||||||
%D%/register.c \
|
%D%/register.c \
|
||||||
|
@ -69,6 +73,10 @@ ARMV7_SRC = \
|
||||||
%D%/cortex_a.c \
|
%D%/cortex_a.c \
|
||||||
%D%/ls1_sap.c
|
%D%/ls1_sap.c
|
||||||
|
|
||||||
|
ARMV8_SRC = \
|
||||||
|
%D%/aarch64.c \
|
||||||
|
%D%/armv8.c
|
||||||
|
|
||||||
ARM_DEBUG_SRC = \
|
ARM_DEBUG_SRC = \
|
||||||
%D%/arm_dpm.c \
|
%D%/arm_dpm.c \
|
||||||
%D%/arm_jtag.c \
|
%D%/arm_jtag.c \
|
||||||
|
@ -146,6 +154,7 @@ INTEL_IA32_SRC = \
|
||||||
%D%/armv7a.h \
|
%D%/armv7a.h \
|
||||||
%D%/armv7m.h \
|
%D%/armv7m.h \
|
||||||
%D%/armv7m_trace.h \
|
%D%/armv7m_trace.h \
|
||||||
|
%D%/armv8.h \
|
||||||
%D%/avrt.h \
|
%D%/avrt.h \
|
||||||
%D%/dsp563xx.h \
|
%D%/dsp563xx.h \
|
||||||
%D%/dsp563xx_once.h \
|
%D%/dsp563xx_once.h \
|
||||||
|
@ -153,6 +162,7 @@ INTEL_IA32_SRC = \
|
||||||
%D%/breakpoints.h \
|
%D%/breakpoints.h \
|
||||||
%D%/cortex_m.h \
|
%D%/cortex_m.h \
|
||||||
%D%/cortex_a.h \
|
%D%/cortex_a.h \
|
||||||
|
%D%/aarch64.h \
|
||||||
%D%/embeddedice.h \
|
%D%/embeddedice.h \
|
||||||
%D%/etb.h \
|
%D%/etb.h \
|
||||||
%D%/etm.h \
|
%D%/etm.h \
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -218,6 +218,7 @@ struct arm_reg {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct reg_cache *arm_build_reg_cache(struct target *target, struct arm *arm);
|
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[];
|
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,
|
int arm_get_gdb_reg_list(struct target *target,
|
||||||
struct reg **reg_list[], int *reg_list_size,
|
struct reg **reg_list[], int *reg_list_size,
|
||||||
enum target_register_class reg_class);
|
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);
|
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);
|
void arm_set_cpsr(struct arm *arm, uint32_t cpsr);
|
||||||
struct reg *arm_reg_current(struct arm *arm, unsigned regnum);
|
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_fp_reg;
|
||||||
extern struct reg arm_gdb_dummy_fps_reg;
|
extern struct reg arm_gdb_dummy_fps_reg;
|
||||||
|
|
|
@ -299,10 +299,15 @@ static int dpm_write_reg64(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
|
||||||
switch (regnum) {
|
switch (regnum) {
|
||||||
case 0 ... 30:
|
case 0 ... 30:
|
||||||
i = 0xd5330400 + regnum;
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
LOG_DEBUG("register %s (%16.16llx) not defined", r->name,
|
||||||
|
(unsigned long long)value);
|
||||||
break;
|
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 */
|
/* update core mode and state, plus shadow mapping for R8..R14 */
|
||||||
arm_set_cpsr(arm, cpsr);
|
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;
|
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 */
|
/* 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;
|
struct arm_reg *r;
|
||||||
unsigned regnum;
|
unsigned regnum;
|
||||||
|
|
||||||
|
@ -1119,7 +1127,7 @@ int arm_dpm_setup(struct arm_dpm *dpm)
|
||||||
{
|
{
|
||||||
struct arm *arm = dpm->arm;
|
struct arm *arm = dpm->arm;
|
||||||
struct target *target = arm->target;
|
struct target *target = arm->target;
|
||||||
struct reg_cache *cache;
|
struct reg_cache *cache = 0;
|
||||||
|
|
||||||
arm->dpm = dpm;
|
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->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;
|
arm->write_core_reg = arm->write_core_reg ? : arm_dpm_write_core_reg;
|
||||||
|
|
||||||
/* avoid duplicating the register cache */
|
if (arm->core_cache != NULL) {
|
||||||
if (arm->core_cache == NULL) {
|
if (arm->core_state == ARM_STATE_AARCH64) {
|
||||||
cache = arm_build_reg_cache(target, arm);
|
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)
|
if (!cache)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
|
||||||
*register_get_last_cache_p(&target->reg_cache) = cache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* coprocessor access setup */
|
/* coprocessor access setup */
|
||||||
|
|
|
@ -73,10 +73,17 @@ struct arm_dpm {
|
||||||
int (*instr_write_data_dcc)(struct arm_dpm *,
|
int (*instr_write_data_dcc)(struct arm_dpm *,
|
||||||
uint32_t opcode, uint32_t data);
|
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. */
|
/** Runs one instruction, writing data to R0 before execution. */
|
||||||
int (*instr_write_data_r0)(struct arm_dpm *,
|
int (*instr_write_data_r0)(struct arm_dpm *,
|
||||||
uint32_t opcode, uint32_t data);
|
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. */
|
/** Optional core-specific operation invoked after CPSR writes. */
|
||||||
int (*instr_cpsr_sync)(struct arm_dpm *dpm);
|
int (*instr_cpsr_sync)(struct arm_dpm *dpm);
|
||||||
|
|
||||||
|
|
|
@ -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 <helper/replacements.h>
|
||||||
|
|
||||||
|
#include "armv8.h"
|
||||||
|
#include "arm_disassembler.h"
|
||||||
|
|
||||||
|
#include "register.h"
|
||||||
|
#include <helper/binarybuffer.h>
|
||||||
|
#include <helper/command.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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,<Rt>,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,<Rt>,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,<Rd>,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,<Rd>, 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,<Rd>, 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,<RT>,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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -88,6 +88,7 @@ extern struct target_type dragonite_target;
|
||||||
extern struct target_type xscale_target;
|
extern struct target_type xscale_target;
|
||||||
extern struct target_type cortexm_target;
|
extern struct target_type cortexm_target;
|
||||||
extern struct target_type cortexa_target;
|
extern struct target_type cortexa_target;
|
||||||
|
extern struct target_type aarch64_target;
|
||||||
extern struct target_type cortexr4_target;
|
extern struct target_type cortexr4_target;
|
||||||
extern struct target_type arm11_target;
|
extern struct target_type arm11_target;
|
||||||
extern struct target_type ls1_sap_target;
|
extern struct target_type ls1_sap_target;
|
||||||
|
@ -135,6 +136,9 @@ static struct target_type *target_types[] = {
|
||||||
&or1k_target,
|
&or1k_target,
|
||||||
&quark_x10xx_target,
|
&quark_x10xx_target,
|
||||||
&quark_d20xx_target,
|
&quark_d20xx_target,
|
||||||
|
#if BUILD_TARGET64
|
||||||
|
&aarch64_target,
|
||||||
|
#endif
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue