aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIan Thompson <ianst@cadence.com>2024-01-31 15:14:25 -0800
committerAntonio Borneo <borneo.antonio@gmail.com>2024-05-11 11:55:55 +0000
commit2c8376b79d104a855bd3a559e59edf330309bcad (patch)
tree6e8f3cc6de524e48e8bd3ff285b62e50656210ed /src
parent22ddf62d759b10a89e9c4c3f3929a66c9d72572d (diff)
downloadriscv-openocd-2c8376b79d104a855bd3a559e59edf330309bcad.zip
riscv-openocd-2c8376b79d104a855bd3a559e59edf330309bcad.tar.gz
riscv-openocd-2c8376b79d104a855bd3a559e59edf330309bcad.tar.bz2
target/xtensa: avoid IHI for writes to non-executable memory
For MPU configs, determine memory access rights by probing protection TLB. Issuing IHI without execute permissions can trigger an exception. No new clang static analyzer warnings. Change-Id: Iea8eab5c2113df3f954285c3b9a79e96d41aa941 Signed-off-by: Ian Thompson <ianst@cadence.com> Reviewed-on: https://review.openocd.org/c/openocd/+/8080 Reviewed-by: Erhan Kurubas <erhan.kurubas@espressif.com> Tested-by: jenkins Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/target/xtensa/xtensa.c89
1 files changed, 81 insertions, 8 deletions
diff --git a/src/target/xtensa/xtensa.c b/src/target/xtensa/xtensa.c
index fb7748a..f7c82ef 100644
--- a/src/target/xtensa/xtensa.c
+++ b/src/target/xtensa/xtensa.c
@@ -158,6 +158,12 @@
#define XT_INS_RFWU(X) (XT_ISBE(X) ? 0x005300 << 8 : 0x003500)
#define XT_INS_RFWO_RFWU_MASK(X) (XT_ISBE(X) ? 0xFFFFFF << 8 : 0xFFFFFF)
+/* Read Protection TLB Entry Info */
+#define XT_INS_PPTLB(X, S, T) _XT_INS_FORMAT_RRR(X, 0x500000, ((S) << 4) | (T), 0xD)
+
+#define XT_TLB1_ACC_SHIFT 8
+#define XT_TLB1_ACC_MSK 0xF
+
#define XT_WATCHPOINTS_NUM_MAX 2
/* Special register number macro for DDR, PS, WB, A3, A4 registers.
@@ -298,6 +304,27 @@ enum xtensa_mem_region_type {
XTENSA_MEM_REGS_NUM
};
+/**
+ * Types of access rights for MPU option
+ * The first block is kernel RWX ARs; the second block is user rwx ARs.
+ */
+enum xtensa_mpu_access_type {
+ XTENSA_ACC_00X_000 = 0x2,
+ XTENSA_ACC_000_00X,
+ XTENSA_ACC_R00_000,
+ XTENSA_ACC_R0X_000,
+ XTENSA_ACC_RW0_000,
+ XTENSA_ACC_RWX_000,
+ XTENSA_ACC_0W0_0W0,
+ XTENSA_ACC_RW0_RWX,
+ XTENSA_ACC_RW0_R00,
+ XTENSA_ACC_RWX_R0X,
+ XTENSA_ACC_R00_R00,
+ XTENSA_ACC_R0X_R0X,
+ XTENSA_ACC_RW0_RW0,
+ XTENSA_ACC_RWX_RWX
+};
+
/* Register definition as union for list allocation */
union xtensa_reg_val_u {
xtensa_reg_val_t val;
@@ -521,6 +548,44 @@ static void xtensa_queue_exec_ins_wide(struct xtensa *xtensa, uint8_t *ops, uint
}
}
+/* NOTE: Assumes A3 has already been saved and marked dirty; A3 will be clobbered */
+static inline bool xtensa_region_ar_exec(struct target *target, target_addr_t start, target_addr_t end)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ if (xtensa->core_config->mpu.enabled) {
+ /* For cores with the MPU option, issue PPTLB on start and end addresses.
+ * Parse access rights field, and confirm both have execute permissions.
+ */
+ for (int i = 0; i <= 1; i++) {
+ uint32_t at, acc;
+ uint8_t at_buf[4];
+ bool exec_acc;
+ target_addr_t addr = i ? end : start;
+ xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, addr);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_PPTLB(xtensa, XT_REG_A3, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_WSR(xtensa, XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, XDMREG_DDR, at_buf);
+ int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
+ if (res != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Error queuing PPTLB: %d", res);
+ res = xtensa_core_status_check(target);
+ if (res != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Error issuing PPTLB: %d", res);
+ at = buf_get_u32(at_buf, 0, 32);
+ acc = (at >> XT_TLB1_ACC_SHIFT) & XT_TLB1_ACC_MSK;
+ exec_acc = ((acc == XTENSA_ACC_00X_000) || (acc == XTENSA_ACC_R0X_000) ||
+ (acc == XTENSA_ACC_RWX_000) || (acc == XTENSA_ACC_RWX_R0X) ||
+ (acc == XTENSA_ACC_R0X_R0X) || (acc == XTENSA_ACC_RWX_RWX));
+ LOG_TARGET_DEBUG(target, "PPTLB(" TARGET_ADDR_FMT ") -> 0x%08" PRIx32 " exec_acc %d",
+ addr, at, exec_acc);
+ if (!exec_acc)
+ return false;
+ }
+ }
+ return true;
+}
+
static int xtensa_queue_pwr_reg_write(struct xtensa *xtensa, unsigned int reg, uint32_t data)
{
struct xtensa_debug_module *dm = &xtensa->dbg_mod;
@@ -2176,11 +2241,13 @@ int xtensa_write_memory(struct target *target,
}
} else {
/* Invalidate ICACHE, writeback DCACHE if present */
- uint32_t issue_ihi = xtensa_is_icacheable(xtensa, address);
- uint32_t issue_dhwb = xtensa_is_dcacheable(xtensa, address);
- if (issue_ihi || issue_dhwb) {
+ bool issue_ihi = xtensa_is_icacheable(xtensa, address) &&
+ xtensa_region_ar_exec(target, addrstart_al, addrend_al);
+ bool issue_dhwbi = xtensa_is_dcacheable(xtensa, address);
+ LOG_TARGET_DEBUG(target, "Cache OPs: IHI %d, DHWBI %d", issue_ihi, issue_dhwbi);
+ if (issue_ihi || issue_dhwbi) {
uint32_t ilinesize = issue_ihi ? xtensa->core_config->icache.line_size : UINT32_MAX;
- uint32_t dlinesize = issue_dhwb ? xtensa->core_config->dcache.line_size : UINT32_MAX;
+ uint32_t dlinesize = issue_dhwbi ? xtensa->core_config->dcache.line_size : UINT32_MAX;
uint32_t linesize = MIN(ilinesize, dlinesize);
uint32_t off = 0;
adr = addrstart_al;
@@ -2193,7 +2260,7 @@ int xtensa_write_memory(struct target *target,
}
if (issue_ihi)
xtensa_queue_exec_ins(xtensa, XT_INS_IHI(xtensa, XT_REG_A3, off));
- if (issue_dhwb)
+ if (issue_dhwbi)
xtensa_queue_exec_ins(xtensa, XT_INS_DHWBI(xtensa, XT_REG_A3, off));
off += linesize;
if (off > 1020) {
@@ -2205,7 +2272,11 @@ int xtensa_write_memory(struct target *target,
/* Execute cache WB/INV instructions */
res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
- xtensa_core_status_check(target);
+ if (res != ERROR_OK)
+ LOG_TARGET_ERROR(target,
+ "Error queuing cache writeback/invaldate instruction(s): %d",
+ res);
+ res = xtensa_core_status_check(target);
if (res != ERROR_OK)
LOG_TARGET_ERROR(target,
"Error issuing cache writeback/invaldate instruction(s): %d",
@@ -2367,7 +2438,8 @@ int xtensa_poll(struct target *target)
static int xtensa_update_instruction(struct target *target, target_addr_t address, uint32_t size, const uint8_t *buffer)
{
struct xtensa *xtensa = target_to_xtensa(target);
- unsigned int issue_ihi = xtensa_is_icacheable(xtensa, address);
+ unsigned int issue_ihi = xtensa_is_icacheable(xtensa, address) &&
+ xtensa_region_ar_exec(target, address, address + size);
unsigned int issue_dhwbi = xtensa_is_dcacheable(xtensa, address);
uint32_t icache_line_size = issue_ihi ? xtensa->core_config->icache.line_size : UINT32_MAX;
uint32_t dcache_line_size = issue_dhwbi ? xtensa->core_config->dcache.line_size : UINT32_MAX;
@@ -2385,7 +2457,8 @@ static int xtensa_update_instruction(struct target *target, target_addr_t addres
/* Write start address to A3 and invalidate */
xtensa_queue_dbg_reg_write(xtensa, XDMREG_DDR, address);
xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa, XT_SR_DDR, XT_REG_A3));
- LOG_TARGET_DEBUG(target, "DHWBI, IHI for address "TARGET_ADDR_FMT, address);
+ LOG_TARGET_DEBUG(target, "IHI %d, DHWBI %d for address " TARGET_ADDR_FMT,
+ issue_ihi, issue_dhwbi, address);
if (issue_dhwbi) {
xtensa_queue_exec_ins(xtensa, XT_INS_DHWBI(xtensa, XT_REG_A3, 0));
if (!same_dc_line) {