/* Copyright 2019 Espressif Systems (Shanghai) PTE LTD Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* * LoadStoreErrorCause: Occurs when trying to access 32 bit addressable memory region as 8 bit or 16 bit * LoadStoreAlignmentCause: Occurs when trying to access in an unaligned manner * * xxxx xxxx = imm8 field * yyyy = imm4 field * ssss = s field * tttt = t field * * 16 0 * ------------------- * L32I.N yyyy ssss tttt 1000 * S32I.N yyyy ssss tttt 1001 * * 23 0 * ----------------------------- * L8UI xxxx xxxx 0000 ssss tttt 0010 <- LoadStoreError * L16UI xxxx xxxx 0001 ssss tttt 0010 <- LoadStoreError, LoadStoreAlignment * L16SI xxxx xxxx 1001 ssss tttt 0010 <- LoadStoreError, LoadStoreAlignment * L32I xxxx xxxx 0010 ssss tttt 0010 <- LoadStoreAlignment * * S8I xxxx xxxx 0100 ssss tttt 0010 <- LoadStoreError * S16I xxxx xxxx 0101 ssss tttt 0010 <- LoadStoreError, LoadStoreAlignment * S32I xxxx xxxx 0110 ssss tttt 0010 <- LoadStoreAlignment * * ******* UNSUPPORTED ******* * * L32E 0000 1001 rrrr ssss tttt 0000 * S32E 0100 1001 rrrr ssss tttt 0000 * ----------------------------- */ #include "xtensa_rtos.h" #include "sdkconfig.h" #include "soc/soc.h" #define LOADSTORE_HANDLER_STACK_SZ 8 .section .bss, "aw" .balign 16 LoadStoreHandlerStack: .rept LOADSTORE_HANDLER_STACK_SZ .word 0 .endr /* LoadStoreErrorCause handler: * * Completes 8-bit or 16-bit load/store instructions from 32-bit aligned memory region * Called from UserExceptionVector if EXCCAUSE is LoadStoreErrorCause */ .global LoadStoreErrorHandler .section .iram1, "ax" .literal_position .balign 4 LoadStoreErrorHandler: .type LoadStoreErrorHandler, @function wsr a0, depc // Save return address in depc mov a0, sp movi sp, LoadStoreHandlerStack s32i a0, sp, 0x04 // Since a0 contains value of a1 s32i a2, sp, 0x08 s32i a3, sp, 0x0c s32i a4, sp, 0x10 rsr a0, sar // Save SAR in a0 to restore later /* Check whether the address lies in the valid range */ rsr a3, excvaddr movi a4, _iram_text_end // End of code section of IRAM bge a3, a4, 1f movi a4, SOC_CACHE_APP_LOW // Check if in APP cache region blt a3, a4, .LS_wrong_opcode movi a4, SOC_CACHE_APP_HIGH bge a3, a4, .LS_wrong_opcode j 2f 1: movi a4, SOC_IRAM_HIGH // End of IRAM address range bge a3, a4, .LS_wrong_opcode 2: /* Examine the opcode which generated the exception */ /* Note: Instructions are in this order to avoid pipeline stalls. */ rsr a2, epc1 movi a4, ~3 ssa8l a2 // sar is now correct shift for aligned read and a2, a2, a4 // a2 now 4-byte aligned address of instruction l32i a4, a2, 0 l32i a2, a2, 4 src a2, a2, a4 // a2 now instruction that failed bbci a2, 1, .LS_wrong_opcode bbsi a2, 14, .LSE_store_op // Store instruction /* l8/l16ui/l16si */ movi a4, ~3 and a4, a3, a4 // a4 now word aligned read address ssa8l a3 // sar is now shift to extract a3's byte l32i a4, a4, 0 // perform the actual read srl a4, a4 // shift right correct distance extui a3, a2, 12, 4 bnez a3, 1f // l16ui/l16si extui a4, a4, 0, 8 // mask off bits needed for an l8 j 2f 1: extui a4, a4, 0, 16 bbci a2, 15, 2f // l16ui /* Sign adjustment */ slli a4, a4, 16 srai a4, a4, 16 // a4 contains the value 2: /* a4 contains the value */ rsr a3, epc1 addi a3, a3, 3 wsr a3, epc1 wsr a0, sar rsr a0, excsave1 extui a2, a2, 3, 5 blti a2, 10, .LSE_stack_reg movi a3, .LS_jumptable_base addx8 a2, a2, a3 // a2 is now the address to jump to l32i a3, sp, 0x0c jx a2 .LSE_stack_reg: addx2 a2, a2, sp s32i a4, a2, 0 /* Restore all values */ l32i a4, sp, 0x10 l32i a3, sp, 0x0c l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .LSE_store_op: s32i a5, a1, 0x14 s32i a6, a1, 0x18 /* a2 -> instruction that caused the error */ /* a3 -> unaligned address */ extui a4, a2, 4, 4 blti a4, 7, 1f movi a5, .LSE_store_reg addx8 a5, a4, a5 jx a5 1: addx4 a4, a4, sp l32i a4, a4, 0 .LSE_store_data: /* a4 contains the value */ rsr a6, epc1 addi a6, a6, 3 wsr a6, epc1 ssa8b a3 movi a5, -1 bbsi a2, 12, 1f // s16 extui a4, a4, 0, 8 movi a6, 0xff j 2f 1: extui a4, a4, 0, 16 movi a6, 0xffff 2: sll a4, a4 // shift the value to proper offset sll a6, a6 xor a5, a5, a6 // a5 contains the mask movi a6, ~3 and a3, a3, a6 // a3 has the aligned address l32i a6, a3, 0 // a6 contains the data at the aligned address and a6, a6, a5 or a4, a6, a4 s32i a4, a3, 0 /* Restore registers */ wsr a0, sar l32i a6, sp, 0x18 l32i a5, sp, 0x14 l32i a4, sp, 0x10 l32i a3, sp, 0x0c l32i a2, sp, 0x08 l32i a1, sp, 0x04 rsr a0, excsave1 rfe .LSE_store_reg: .org .LSE_store_reg + (7 * 8) mov a4, a7 j .LSE_store_data .org .LSE_store_reg + (8 * 8) mov a4, a8 j .LSE_store_data .org .LSE_store_reg + (9 * 8) mov a4, a9 j .LSE_store_data .org .LSE_store_reg + (10 * 8) mov a4, a10 j .LSE_store_data .org .LSE_store_reg + (11 * 8) mov a4, a11 j .LSE_store_data .org .LSE_store_reg + (12 * 8) mov a4, a12 j .LSE_store_data .org .LSE_store_reg + (13 * 8) mov a4, a13 j .LSE_store_data .org .LSE_store_reg + (14 * 8) mov a4, a14 j .LSE_store_data .org .LSE_store_reg + (15 * 8) mov a4, a15 j .LSE_store_data /* LoadStoreAlignmentCause handler: * * Completes unaligned 16-bit and 32-bit load/store instructions from 32-bit aligned memory region * Called from UserExceptionVector if EXCCAUSE is LoadStoreAlignmentCause */ .global AlignmentErrorHandler .section .iram1, "ax" .literal_position .balign 4 AlignmentErrorHandler: .type AlignmentErrorHandler, @function wsr a0, depc // Save return address in depc mov a0, sp movi sp, LoadStoreHandlerStack s32i a0, sp, 0x04 // Since a0 contains value of a1 s32i a2, sp, 0x08 s32i a3, sp, 0x0c s32i a4, sp, 0x10 rsr a0, sar // Save SAR in a0 to restore later /* Check whether the address lies in the valid range */ rsr a3, excvaddr movi a4, _iram_text_end // End of code section of IRAM bge a3, a4, 1f movi a4, SOC_CACHE_APP_LOW // Check if in APP cache region blt a3, a4, .LS_wrong_opcode movi a4, SOC_CACHE_APP_HIGH bge a3, a4, .LS_wrong_opcode j 2f 1: movi a4, SOC_IRAM_HIGH // End of IRAM address range bge a3, a4, .LS_wrong_opcode 2: /* Examine the opcode which generated the exception */ /* Note: Instructions are in this order to avoid pipeline stalls. */ rsr a2, epc1 movi a4, ~3 ssa8l a2 // sar is now correct shift for aligned read and a2, a2, a4 // a2 now 4-byte aligned address of instruction l32i a4, a2, 0 l32i a2, a2, 4 /* a2 has the instruction that caused the error */ src a2, a2, a4 extui a4, a2, 0, 4 addi a4, a4, -9 beqz a4, .LSA_store_op bbsi a2, 14, .LSA_store_op ssa8l a3 // a3 contains the unaligned address movi a4, ~3 and a4, a3, a4 // a4 has the aligned address l32i a3, a4, 0 l32i a4, a4, 4 src a4, a4, a3 rsr a3, epc1 addi a3, a3, 2 bbsi a2, 3, 1f // l32i.n bbci a2, 1, .LS_wrong_opcode addi a3, a3, 1 bbsi a2, 13, 1f // l32 extui a4, a4, 0, 16 bbci a2, 15, 1f // l16ui /* Sign adjustment */ slli a4, a4, 16 srai a4, a4, 16 // a4 contains the value 1: wsr a3, epc1 wsr a0, sar rsr a0, excsave1 extui a2, a2, 4, 4 blti a2, 5, .LSA_stack_reg // a3 contains the target register movi a3, .LS_jumptable_base slli a2, a2, 4 add a2, a2, a3 // a2 is now the address to jump to l32i a3, sp, 0x0c jx a2 .LSA_stack_reg: addx4 a2, a2, sp s32i a4, a2, 0 /* Restore all values */ l32i a4, sp, 0x10 l32i a3, sp, 0x0c l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe /* Store instruction */ .LSA_store_op: s32i a5, sp, 0x14 s32i a6, sp, 0x18 s32i a7, sp, 0x1c /* a2 -> instruction that caused the error */ /* a3 -> unaligned address */ extui a4, a2, 4, 4 blti a4, 8, 1f movi a5, .LSA_store_reg addx8 a5, a4, a5 jx a5 1: addx4 a4, a4, sp l32i a4, a4, 0 // a4 contains the value .LSA_store_data: movi a6, 0 rsr a7, epc1 addi a7, a7 ,2 bbsi a2, 3, 1f // s32i.n bbci a2, 1, .LS_wrong_opcode addi a7, a7, 1 bbsi a2, 13, 1f // s32i movi a5, -1 extui a4, a4, 0, 16 slli a6, a5, 16 // 0xffff0000 1: wsr a7, epc1 movi a5, ~3 and a5, a3, a5 // a5 has the aligned address ssa8b a3 movi a3, -1 src a7, a6, a3 src a3, a3, a6 /* Store data on lower address */ l32i a6, a5, 0 and a6, a6, a7 sll a7, a4 or a6, a6, a7 s32i a6, a5, 0 /* Store data on higher address */ l32i a7, a5, 4 srl a6, a4 and a3, a7, a3 or a3, a3, a6 s32i a3, a5, 4 /* Restore registers */ wsr a0, sar rsr a0, excsave1 l32i a7, sp, 0x1c l32i a6, sp, 0x18 l32i a5, sp, 0x14 l32i a4, sp, 0x10 l32i a3, sp, 0x0c l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .LSA_store_reg: .org .LSA_store_reg + (8 * 8) mov a4, a8 j .LSA_store_data .org .LSA_store_reg + (9 * 8) mov a4, a9 j .LSA_store_data .org .LSA_store_reg + (10 * 8) mov a4, a10 j .LSA_store_data .org .LSA_store_reg + (11 * 8) mov a4, a11 j .LSA_store_data .org .LSA_store_reg + (12 * 8) mov a4, a12 j .LSA_store_data .org .LSA_store_reg + (13 * 8) mov a4, a13 j .LSA_store_data .org .LSA_store_reg + (14 * 8) mov a4, a14 j .LSA_store_data .org .LSA_store_reg + (15 * 8) mov a4, a15 j .LSA_store_data /* * Common routines for both the exception handlers */ .balign 4 .LS_jumptable: /* The first 5 entries (80 bytes) of this table are unused (registers a0..a4 are handled separately above). Rather than have a whole bunch of wasted space, just pretend that the table starts 80 bytes earlier in memory. */ .set .LS_jumptable_base, .LS_jumptable - (16 * 5) .org .LS_jumptable_base + (16 * 5) mov a5, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 6) mov a6, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 7) mov a7, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 8) mov a8, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 9) mov a9, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 10) mov a10, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 11) mov a11, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 12) mov a12, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 13) mov a13, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 14) mov a14, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .org .LS_jumptable_base + (16 * 15) mov a15, a4 l32i a4, sp, 0x10 l32i a2, sp, 0x08 l32i a1, sp, 0x04 rfe .LS_wrong_opcode: /* Reaches here if the address is in invalid range or the opcode isn't supported. * Restore registers and jump back to _xt_user_exc */ wsr a0, sar l32i a4, sp, 0x10 l32i a3, sp, 0x0c l32i a2, sp, 0x08 l32i a1, sp, 0x04 rsr a0, depc ret // Equivalent to jx a0