This is an automated email from Gerrit. Daniel Goehring (dgoeh...@os.amperecomputing.com) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/5572
-- gerrit commit 4b8c1d609316af15c7cde7d26a3fc1f8013af2c6 Author: Kevin Burke <kev...@os.amperecomputing.com> Date: Fri Apr 3 20:28:36 2020 -0400 target|aarch64: Add MSR/MRS TCL/UI support Aarch64 has a new class of system registers that are accessed via the MRS/MSR assembly instructions. This update allows the user to read or write these registers regardless of exception level. Tested on an Ampere eMAG8180 and Quicksilver silicon Change-Id: I99cfc37ac756294fd9779c84e840037529ef304f Signed-off-by: Kevin Burke <kev...@os.amperecomputing.com> Signed-off-by: Daniel Goehring <dgoeh...@os.amperecomputing.com> diff --git a/src/target/aarch64.c b/src/target/aarch64.c index 87176f6..527e994 100644 --- a/src/target/aarch64.c +++ b/src/target/aarch64.c @@ -1,6 +1,8 @@ /*************************************************************************** * Copyright (C) 2015 by David Ung * * * + * Copyright (C) 2019-2020, Ampere Computing LLC * + * * * 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 * @@ -2738,6 +2740,140 @@ static int jim_mcrmrc(Jim_Interp *interp, int argc, Jim_Obj * const *argv) return JIM_OK; } +COMMAND_HANDLER(aarch64_msr_mrs) +{ + struct target *target = get_current_target(CMD_CTX); + struct arm *arm; + int retval; + bool is_msr; + unsigned int arg_cnt; + uint32_t op0; + uint32_t op1; + uint32_t op2; + uint32_t CRn; + uint32_t CRm; + uint64_t value; + uint32_t ns_requested = MSRMRS_NOOPTION; + int index = 0; + + if (!strcmp(CMD_NAME, "mrs")) { + is_msr = false; + arg_cnt = 5; + } else { + is_msr = true; + arg_cnt = 6; + } + + /* check to see if user is specifying a security preference */ + if (CMD_ARGC > 0) { + if (!strcmp(CMD_ARGV[0], "sec")) { + ns_requested = MSRMRS_SECURE; + arg_cnt++; + index++; + } else if (!strcmp(CMD_ARGV[0], "nsec")) { + ns_requested = MSRMRS_NONSECURE; + arg_cnt++; + index++; + } else if (!strcmp(CMD_ARGV[0], "asis")) { + ns_requested = MSRMRS_ASIS; + arg_cnt++; + index++; + } + } + + if (CMD_ARGC != arg_cnt) { + LOG_ERROR("%s command failed: wrong number of arguments", CMD_NAME); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (target == NULL) { + LOG_ERROR("%s: target not found", CMD_NAME); + return ERROR_FAIL; + } + + if (!target_was_examined(target)) { + LOG_ERROR("%s: not yet examined", target_name(target)); + return ERROR_FAIL; + } + + arm = target_to_arm(target); + if (!is_arm(arm)) { + LOG_ERROR("%s: not an ARM", target_name(target)); + return ERROR_FAIL; + } + + if (target->state != TARGET_HALTED) { + LOG_ERROR("%s: not halted", target_name(target)); + return ERROR_TARGET_NOT_HALTED; + } + + if (arm->core_state != ARM_STATE_AARCH64) { + LOG_ERROR("%s: not 64-bit arm target", target_name(target)); + return ERROR_FAIL; + } + + /* NOTE: parameter sequence matches ARM instruction set usage: + * MSR <sec/nsec> op0, op1, rX, CRn, CRm, op2 ; write System Reg from rX + * MRS <sec/nsec> op0, op1, rX, CRn, CRm, op2 ; read System Reg into rX + * The "rX" is necessarily omitted; it uses Tcl mechanisms. + */ + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], op0); + if (op0 & ~0x3) { + LOG_ERROR("%s: %s %d out of range", CMD_NAME, + "op0", (int) op0); + return ERROR_FAIL; + } + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], op1); + if (op1 & ~0x7) { + LOG_ERROR("%s: %s %d out of range", CMD_NAME, + "op1", (int) op1); + return ERROR_FAIL; + } + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], CRn); + if (CRn & ~0xf) { + LOG_ERROR("%s: %s %d out of range", CMD_NAME, + "CRn", (int) CRn); + return ERROR_FAIL; + } + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], CRm); + if (CRm & ~0xf) { + LOG_ERROR("%s: %s %d out of range", CMD_NAME, + "CRm", (int) CRm); + return ERROR_FAIL; + } + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], op2); + if (op2 & ~0x7) { + LOG_ERROR("%s: %s %d out of range", CMD_NAME, + "op2", (int) op2); + return ERROR_FAIL; + } + + value = 0; + + if (is_msr == true) { + COMMAND_PARSE_NUMBER(u64, CMD_ARGV[index], value); + /* NOTE: parameters reordered! */ + /* ARMV8_MSR(ns, op0, op1, 0, CRn, CRm, op2) */ + retval = arm->msr(target, ns_requested, op0, op1, op2, CRn, CRm, value); + if (retval != ERROR_OK) + LOG_ERROR("%s: error encountered writing the designated register", CMD_NAME); + } else { + /* NOTE: parameters reordered! */ + /* ARMV8_MRS(ns, op0, op1, 0, CRn, CRm, op2) */ + retval = arm->mrs(target, ns_requested, op0, op1, op2, CRn, CRm, &value); + if (retval == ERROR_OK) + command_print(CMD, "0x%16.16" PRIx64, value); + else + LOG_ERROR("%s: error encountered reading the designated register", CMD_NAME); + } + + return retval; +} + static const struct command_registration aarch64_exec_command_handlers[] = { { .name = "cache_info", @@ -2775,6 +2911,20 @@ static const struct command_registration aarch64_exec_command_handlers[] = { .usage = "cpnum op1 CRn CRm op2", }, { + .name = "msr", + .mode = COMMAND_EXEC, + .handler = aarch64_msr_mrs, + .help = "write system register", + .usage = "['sec' | 'nsec' | ' ' | 'asis'] op0 op1 CRn CRm op2 value", + }, + { + .name = "mrs", + .mode = COMMAND_EXEC, + .handler = aarch64_msr_mrs, + .help = "read system register", + .usage = "['sec' | 'nsec' | ' ' | 'asis'] op0 op1 CRn CRm op2", + }, + { .chain = smp_command_handlers, }, diff --git a/src/target/arm.h b/src/target/arm.h index b399574..7461f88 100644 --- a/src/target/arm.h +++ b/src/target/arm.h @@ -11,6 +11,8 @@ * Copyright (C) 2018 by Liviu Ionescu * <i...@livius.net> * + * Copyright (C) 2019-2020, Ampere Computing LLC + * * 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 @@ -234,6 +236,18 @@ struct arm { uint32_t CRn, uint32_t CRm, uint32_t value); + /** Read system register. */ + int (*mrs)(struct target *target, uint32_t ns_requested, uint32_t op0, + uint32_t op1, uint32_t op2, + uint32_t CRn, uint32_t CRm, + uint64_t *value); + + /** Write system register. */ + int (*msr)(struct target *target, uint32_t ns_requested, uint32_t op0, + uint32_t op1, uint32_t op2, + uint32_t CRn, uint32_t CRm, + uint64_t value); + void *arch_info; /** For targets conforming to ARM Debug Interface v5, diff --git a/src/target/armv8.h b/src/target/armv8.h index 1a61145..abf8d25 100644 --- a/src/target/armv8.h +++ b/src/target/armv8.h @@ -1,6 +1,8 @@ /*************************************************************************** * Copyright (C) 2015 by David Ung * * * + * Copyright (C) 2020, Ampere Computing LLC * + * * * 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 * @@ -199,6 +201,8 @@ struct armv8_common { uint32_t debug_base; struct adiv5_ap *debug_ap; + enum arm_mode max_aarch64_el; + const uint32_t *opcodes; /* mdir */ @@ -246,9 +250,16 @@ static inline bool is_armv8(struct armv8_common *armv8) return armv8->common_magic == ARMV8_COMMON_MAGIC; } +/* msr/mrs command options */ +#define MSRMRS_SECURE 0 +#define MSRMRS_NONSECURE 1 +#define MSRMRS_ASIS 2 +#define MSRMRS_NOOPTION 3 + /* register offsets from armv8.debug_base */ #define CPUV8_DBG_MAINID0 0xD00 #define CPUV8_DBG_CPUFEATURE0 0xD20 +#define CPUV8_DBG_PFR 0xD20 #define CPUV8_DBG_DBGFEATURE0 0xD28 #define CPUV8_DBG_MEMFEATURE0 0xD38 diff --git a/src/target/armv8_dpm.c b/src/target/armv8_dpm.c index 5be5272..a350591 100644 --- a/src/target/armv8_dpm.c +++ b/src/target/armv8_dpm.c @@ -1,6 +1,8 @@ /* * Copyright (C) 2009 by David Brownell * + * Copyright (C) 2019-2020, Ampere Computing LLC + * * 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 @@ -539,6 +541,220 @@ static int dpmv8_mcr(struct target *target, int cpnum, return retval; } +/* determine highest Exception Level for this target */ +static int dpmv8_maximum_el(struct arm_dpm *dpm, enum arm_mode *highest_mode) +{ + int retval; + uint32_t edpfr_lower; + + struct armv8_common *armv8 = (struct armv8_common *) dpm->arm->arch_info; + + if (armv8->max_aarch64_el == ARMV8_64_EL0T) { + /* read EDPFR register to see what ELs exist in AARCH64 state */ + retval = mem_ap_read_atomic_u32(armv8->debug_ap, + armv8->debug_base + CPUV8_DBG_PFR, + &edpfr_lower); + if (retval != ERROR_OK) + return retval; + if ((edpfr_lower >> 12) & 0xf) /* aarch64 EL3 present */ + *highest_mode = ARMV8_64_EL3H; + else if ((edpfr_lower >> 8) & 0xf) /* EL2 present bits */ + *highest_mode = ARMV8_64_EL2H; + else + *highest_mode = ARMV8_64_EL1H; + + armv8->max_aarch64_el = *highest_mode; + } else + *highest_mode = armv8->max_aarch64_el; + + return ERROR_OK; +} + +/* + * System register support + */ + +/* Read system register */ +static int dpmv8_mrs(struct target *target, uint32_t ns_requested, uint32_t op0, + uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm, + uint64_t *value) +{ + struct arm *arm = target_to_arm(target); + struct arm_dpm *dpm = arm->dpm; + int retval; + enum arm_mode highest_mode; + uint32_t target_el, highest_el; + uint64_t scr_value; + + target_el = 0; + highest_el = target_el; /* set default to run with current excetion level */ + scr_value = ns_requested; /* set default not to restore the scr register */ + + LOG_DEBUG("MRS %d, %d, x0, c%d, c%d, %d", (int) op0, + (int) op1, (int) CRn, + (int) CRm, (int) op2); + + retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + return retval; + + if (ns_requested != MSRMRS_ASIS) { + /* user wants to execute the command at the highest exception level */ + retval = dpmv8_maximum_el(dpm, &highest_mode); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to determine highest EL level to use on MSR operation"); + goto fail; + } + + target_el = ((buf_get_u32(dpm->arm->cpsr->value, 0, 32) >> 2) & 3); /* current el */ + highest_el = ((uint32_t) highest_mode) >> 2; + if (target_el < highest_el) { + retval = armv8_dpm_modeswitch(dpm, highest_mode); /* all accesses at highest EL */ + if (retval != ERROR_OK) { + LOG_ERROR("Unable to switch to highest exception level on MSR operation"); + highest_el = target_el; /* no need to try to restore exception level */ + goto fail; + } + } + if (ns_requested != MSRMRS_NOOPTION) { /* user specified a specific security setting */ + if (ns_requested == MSRMRS_SECURE && highest_mode != ARMV8_64_EL3H) { + LOG_ERROR("MSR %d, %d, x0, c%d, c%d, %d secure does not exist", + (int) op0, (int) op1, (int) CRn, + (int) CRm, (int) op2); + goto fail; /* may need to restore target_el */ + } else if (highest_mode == ARMV8_64_EL3H) { + /* Need to see if EL3's NS bit is set correctly */ + /* by reading SCR_EL3 */ + retval = dpm->instr_read_data_r0_64(dpm, + ARMV8_MRS_INSTR(3, 6, 0, 1, 1, 0), + &scr_value); + if (retval != ERROR_OK) { + scr_value = ns_requested; /* no need to restore ns bit */ + goto fail; + } + if (ns_requested != (scr_value & 0x1)) { + retval = dpm->instr_write_data_r0_64(dpm, + ARMV8_MSR_INSTR(3, 6, 0, 1, 1, 0), + ((scr_value & 0xFFFFFFFFFFFFFFFE) | ns_requested)); + if (retval != ERROR_OK) { + scr_value = ns_requested; /* no need to restore ns bit */ + goto fail; + } + } + } + } + } + + /* read system register into x0; return via DCC */ + retval = dpm->instr_read_data_r0_64(dpm, + ARMV8_MRS_INSTR(op0, op1, 0, CRn, CRm, op2), value); +fail: + if (ns_requested != (scr_value & 0x1)) { + /* need to restore scr_el3.ns before leaving */ + retval += dpm->instr_write_data_r0_64(dpm, + ARMV8_MSR_INSTR(3, 6, 0, 1, 1, 0), + scr_value); + } + if (target_el < highest_el) { + /* need to restore exception level */ + retval += armv8_dpm_modeswitch(dpm, ARM_MODE_ANY); /* return to previous access */ + } + + /* (void) */ dpm->finish(dpm); + return retval; +} + +/* Write system register */ +static int dpmv8_msr(struct target *target, uint32_t ns_requested, uint32_t op0, + uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm, + uint64_t value) +{ + struct arm *arm = target_to_arm(target); + struct arm_dpm *dpm = arm->dpm; + int retval; + enum arm_mode highest_mode; + uint32_t target_el, highest_el; + uint64_t scr_value; + + target_el = 0; + highest_el = target_el; /* set default not to restore exception level */ + scr_value = ns_requested; /* set default not to restore the scr register */ + + LOG_DEBUG("MSR %d, %d, x0, c%d, c%d, %d", (int) op0, + (int) op1, (int) CRn, + (int) CRm, (int) op2); + + retval = dpm->prepare(dpm); + if (retval != ERROR_OK) + return retval; + + if (ns_requested != MSRMRS_ASIS) { + /* user wants to execute the command at the highest exception level */ + retval = dpmv8_maximum_el(dpm, &highest_mode); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to determine highest EL level to use on MSR operation"); + goto fail; + } + + target_el = ((buf_get_u32(dpm->arm->cpsr->value, 0, 32) >> 2) & 3); /* current el */ + highest_el = ((uint32_t) highest_mode) >> 2; + if (target_el < highest_el) { + retval = armv8_dpm_modeswitch(dpm, highest_mode); /* all accesses at highest EL */ + if (retval != ERROR_OK) { + LOG_ERROR("Unable to switch to highest exception level on MSR operation"); + highest_el = target_el; /* no need to try to restore exception level */ + goto fail; + } + } + if (ns_requested != MSRMRS_NOOPTION) { /* user specified a specific security setting */ + if (ns_requested == MSRMRS_SECURE && highest_mode != ARMV8_64_EL3H) { + LOG_ERROR("MSR %d, %d, x0, c%d, c%d, %d secure does not exist", + (int) op0, (int) op1, (int) CRn, + (int) CRm, (int) op2); + goto fail; /* may need to restore target_el */ + } else if (highest_mode == ARMV8_64_EL3H) { + /* Need to see if EL3's NS bit is set correctly */ + /* by reading SCR_EL3 */ + retval = dpm->instr_read_data_r0_64(dpm, + ARMV8_MRS_INSTR(3, 6, 0, 1, 1, 0), + &scr_value); + if (retval != ERROR_OK) { + scr_value = ns_requested; /* no need to restore ns bit */ + goto fail; + } + if (ns_requested != (scr_value & 0x1)) { + retval = dpm->instr_write_data_r0_64(dpm, + ARMV8_MSR_INSTR(3, 6, 0, 1, 1, 0), + ((scr_value & 0xFFFFFFFFFFFFFFFE) | ns_requested)); + if (retval != ERROR_OK) { + scr_value = ns_requested; /* no need to restore ns bit */ + goto fail; + } + } + } + } + } + + /* read DCC into x0; then write system register from R0 */ + retval = dpm->instr_write_data_r0_64(dpm, + ARMV8_MSR_INSTR(op0, op1, 0, CRn, CRm, op2), value); + +fail: + if (ns_requested != (scr_value & 0x1)) { + /* need to restore scr_el3.ns before leaving */ + retval += dpm->instr_write_data_r0_64(dpm, + ARMV8_MSR_INSTR(3, 6, 0, 1, 1, 0), + scr_value); + } + if (target_el < highest_el) { + /* need to restore exception level */ + retval += armv8_dpm_modeswitch(dpm, ARM_MODE_ANY); /* return to previous access */ + } + + /* (void) */ dpm->finish(dpm); + return retval; +} + /*----------------------------------------------------------------------*/ /* @@ -1438,6 +1654,10 @@ int armv8_dpm_setup(struct arm_dpm *dpm) arm->mrc = dpmv8_mrc; arm->mcr = dpmv8_mcr; + /* system register setup */ + arm->mrs = dpmv8_mrs; + arm->msr = dpmv8_msr; + dpm->prepare = dpmv8_dpm_prepare; dpm->finish = dpmv8_dpm_finish; diff --git a/src/target/armv8_opcodes.h b/src/target/armv8_opcodes.h index 239c4c5..78426cf 100644 --- a/src/target/armv8_opcodes.h +++ b/src/target/armv8_opcodes.h @@ -1,6 +1,9 @@ /* * Copyright (C) 2015 by pierrr kuo * vichy....@gmail.com + * + * Copyright (C) 2019, Ampere Computing LLC + * * 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 @@ -137,6 +140,30 @@ #define ARMV8_ISB 0xd5033fdf #define ARMV8_ISB_SY_T1 0xf3bf8f6f +/* Move to ARM register from system register + * op0: first system register opcode + * op1: second system register opcode + * CRn: first system register operand + * CRm: second system register operand + * op2: third system register opcode + * Rd: destination register + */ +#define ARMV8_MRS_INSTR(op0, op1, Rd, CRn, CRm, op2) \ + (0xd5300000 | (Rd) | ((op2) << 5) | ((CRm) << 8) \ + | ((CRn) << 12) | ((op1) << 16) | ((op0) << 19)) + +/* Move to system register from ARM register + * op0: first system register opcode + * op1: second system register opcode + * CRn: first system register operand + * CRm: second system register operand + * op2: third system register opcode + * Rd: destination register + */ +#define ARMV8_MSR_INSTR(op0, op1, Rd, CRn, CRm, op2) \ + (0xd5100000 | (Rd) | ((op2) << 5) | ((CRm) << 8) \ + | ((CRn) << 12) | ((op1) << 16) | ((op0) << 19)) + #define ARMV8_MRS(System, Rt) (0xd5300000 | ((System) << 5) | (Rt)) /* ARM V8 Move to system register. */ #define ARMV8_MSR_GP(System, Rt) \ -- _______________________________________________ OpenOCD-devel mailing list OpenOCD-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openocd-devel