fix(esp32): Fix access to MALLOC_CAP_IRAM_8BIT byte array in loop

The Xtensa load/store handler did not properly handle 8/16-bit
memory access to IRAM regions configured with MALLOC_CAP_IRAM_8BIT
(and CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY=y) from a loop
(LBEG/LEND/LCOUNT) context. This caused the loop to exit after
the first access, instead of continuing to iterate as intended.

Closes https://github.com/espressif/esp-idf/issues/14127
This commit is contained in:
Konstantin Kondrashov
2026-05-01 12:46:26 +03:00
parent fb93ff749e
commit 5ea2823c7c
4 changed files with 124 additions and 16 deletions
@@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Zero-overhead loop adjustment helpers for Xtensa assembly code.
*
* Used by exception handlers to emulate loop hardware when an exception
* occurs at LEND so execution resumes at LBEG with LCOUNT decremented.
*/
#ifndef XTENSA_ZOL_MACROS_H
#define XTENSA_ZOL_MACROS_H
#if __XTENSA__
#include "xtensa/config/core-isa.h"
#include "xtensa/config/xt_specreg.h"
/**
* Adjust loop counter and return address for exceptions at LEND.
*
* When an exception occurs on instruction at LEND address of a zero-overhead loop,
* we must decrement LCOUNT and set EPC back to LBEG so the loop continues
* iterating after the exception is handled. Otherwise, the loop exits prematurely.
*
* if (EPC == LEND && LCOUNT != 0) {
* LCOUNT--;
* EPC = LBEG;
* }
*
* param[in/out] epc_reg - register containing EPC, updated to LBEG if needed
* param[in/out] tmp_reg - temporary register for intermediate values
*
* Return: Use epc_reg to set corrected EPC value.
*/
.macro XT_ZOL_EPC_LCOUNT_RESTORE epc_reg tmp_reg
#if XCHAL_HAVE_LOOPS
rsr \tmp_reg, XT_REG_LEND
bne \epc_reg, \tmp_reg, 1f
rsr \tmp_reg, XT_REG_LCOUNT
beqz \tmp_reg, 1f
addi \tmp_reg, \tmp_reg, -1
wsr \tmp_reg, XT_REG_LCOUNT
rsr \epc_reg, XT_REG_LBEG
1:
#endif
.endm
#endif /* __XTENSA__ */
#endif /* XTENSA_ZOL_MACROS_H */
+8 -3
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -37,6 +37,7 @@
*/
#include "xtensa_rtos.h"
#include "xtensa/xtensa_zol_macros.h"
#include "sdkconfig.h"
#include "soc/soc.h"
@@ -124,10 +125,11 @@ LoadStoreErrorHandler:
2:
/* a4 contains the value */
wsr a0, sar
rsr a3, epc1
addi a3, a3, 3
XT_ZOL_EPC_LCOUNT_RESTORE a3, a0 // If faulted at LEND, rewinds loop and leaves adjusted PC in a3
wsr a3, epc1
wsr a0, sar
rsr a0, excsave1
extui a2, a2, 3, 5
@@ -169,6 +171,7 @@ LoadStoreErrorHandler:
/* a4 contains the value */
rsr a6, epc1
addi a6, a6, 3
XT_ZOL_EPC_LCOUNT_RESTORE a6, a5 // If faulted at LEND, rewinds loop and leaves adjusted PC in a6
wsr a6, epc1
ssa8b a3
@@ -321,8 +324,9 @@ AlignmentErrorHandler:
srai a4, a4, 16 // a4 contains the value
1:
wsr a3, epc1
wsr a0, sar
XT_ZOL_EPC_LCOUNT_RESTORE a3, a0 // If faulted at LEND, rewinds loop and leaves adjusted PC in a3
wsr a3, epc1
rsr a0, excsave1
extui a2, a2, 4, 4
@@ -379,6 +383,7 @@ AlignmentErrorHandler:
slli a6, a5, 16 // 0xffff0000
1:
XT_ZOL_EPC_LCOUNT_RESTORE a7, a5 // If faulted at LEND, rewinds loop and leaves adjusted PC in a7
wsr a7, epc1
movi a5, ~3
and a5, a3, a5 // a5 has the aligned address
+5 -13
View File
@@ -99,6 +99,7 @@
*/
#include "xtensa_rtos.h"
#include "xtensa/xtensa_zol_macros.h"
#include "esp_private/panic_reason.h"
#include "sdkconfig.h"
#include "soc/soc.h"
@@ -904,22 +905,13 @@ _xt_syscall_exc:
#endif
/*
Grab the interruptee's PC and skip over the 'syscall' instruction.
If it's at the end of a zero-overhead loop and it's not on the last
iteration, decrement loop counter and skip to beginning of loop.
Adjust EPC to point to next instruction, so when we return to user code
it will be at the instruction after the 'syscall' that caused the exception.
*/
rsr a2, EPC_1 /* a2 = PC of 'syscall' */
addi a3, a2, 3 /* ++PC */
#if XCHAL_HAVE_LOOPS
rsr a0, LEND /* if (PC == LEND */
bne a3, a0, 1f
rsr a0, LCOUNT /* && LCOUNT != 0) */
beqz a0, 1f /* { */
addi a0, a0, -1 /* --LCOUNT */
rsr a3, LBEG /* PC = LBEG */
wsr a0, LCOUNT /* } */
#endif
1: wsr a3, EPC_1 /* update PC */
XT_ZOL_EPC_LCOUNT_RESTORE a3, a0 /* If faulted at LEND, rewinds loop and leaves adjusted PC in a3 */
wsr a3, EPC_1 /* update PC to next instruction */
/* Restore interruptee's context and return from exception. */
#ifdef __XTENSA_CALL0_ABI__