target/xtensa: avoid IHI for writes to non-executable memory 80/8080/2 master
authorIan Thompson <ianst@cadence.com>
Wed, 31 Jan 2024 23:14:25 +0000 (15:14 -0800)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 11 May 2024 11:55:55 +0000 (11:55 +0000)
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>
src/target/xtensa/xtensa.c

index fb7748aa2d10b75016270f2091d201ce5dde208a..f7c82efed4594000b22478ce739c6627ef1a7b68 100644 (file)
 #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) {

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)