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>
This commit is contained in:
Ian Thompson 2024-01-31 15:14:25 -08:00 committed by Antonio Borneo
parent 22ddf62d75
commit 2c8376b79d
1 changed files with 81 additions and 8 deletions

View File

@ -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) {