diff --git a/arch/xtensa/include/loadstore.h b/arch/xtensa/include/loadstore.h new file mode 100644 index 00000000000..69d219e8054 --- /dev/null +++ b/arch/xtensa/include/loadstore.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * arch/xtensa/include/loadstore.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +#ifndef __ARCH_XTENSA_INCLUDE_LOADSTORE_H +#define __ARCH_XTENSA_INCLUDE_LOADSTORE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Inline functions + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +uint32_t l32i(const uint32_t *p); +void s32i(uint32_t *p, uint32_t value); + +#endif /* __ARCH_XTENSA_INCLUDE_LOADSTORE_H */ diff --git a/arch/xtensa/src/common/xtensa.h b/arch/xtensa/src/common/xtensa.h index ab6c1fbe56d..bff7093e7bf 100644 --- a/arch/xtensa/src/common/xtensa.h +++ b/arch/xtensa/src/common/xtensa.h @@ -267,7 +267,8 @@ uint32_t *xtensa_irq_dispatch(int irq, uint32_t *regs); uint32_t xtensa_enable_cpuint(uint32_t *shadow, uint32_t intmask); uint32_t xtensa_disable_cpuint(uint32_t *shadow, uint32_t intmask); void xtensa_panic(int xptcode, uint32_t *regs) noreturn_function; -void xtensa_user(int exccause, uint32_t *regs) noreturn_function; +void xtensa_user_panic(int exccause, uint32_t *regs) noreturn_function; +uint32_t *xtensa_user(int exccause, uint32_t *regs); /* Software interrupt handler */ diff --git a/arch/xtensa/src/common/xtensa_assert.c b/arch/xtensa/src/common/xtensa_assert.c index e18838b1b37..dcf9382bcf5 100644 --- a/arch/xtensa/src/common/xtensa_assert.c +++ b/arch/xtensa/src/common/xtensa_assert.c @@ -314,7 +314,7 @@ void xtensa_panic(int xptcode, uint32_t *regs) * ****************************************************************************/ -void xtensa_user(int exccause, uint32_t *regs) +void xtensa_user_panic(int exccause, uint32_t *regs) { #if CONFIG_TASK_NAME_SIZE > 0 && defined(CONFIG_DEBUG_ALERT) struct tcb_s *rtcb = running_task(); diff --git a/arch/xtensa/src/common/xtensa_loadstore.S b/arch/xtensa/src/common/xtensa_loadstore.S new file mode 100644 index 00000000000..b14fdfef94f --- /dev/null +++ b/arch/xtensa/src/common/xtensa_loadstore.S @@ -0,0 +1,76 @@ +/**************************************************************************** + * arch/xtensa/src/common/xtensa_loadstore.S + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "xtensa_abi.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: l32i + * + * Description: + * Execute a L32I instruction + * + * Entry Conditions: + * A2 - a pointer + * + ****************************************************************************/ + + .global l32i + .type l32i, @function + .align 4 + +l32i: + ENTRY0 + + l32i a2, a2, 0 + + RET0 + +/**************************************************************************** + * Name: s32i + * + * Description: + * Execute a S32I instruction + * + * Entry Conditions: + * A2 - a pointer + * A3 - a value to store + * + ****************************************************************************/ + + .global s32i + .type s32i, @function + .align 4 + +s32i: + ENTRY0 + + s32i a3, a2, 0 + + RET0 diff --git a/arch/xtensa/src/common/xtensa_user_handler.S b/arch/xtensa/src/common/xtensa_user_handler.S index b8e9adb3cee..9e7b44cd732 100644 --- a/arch/xtensa/src/common/xtensa_user_handler.S +++ b/arch/xtensa/src/common/xtensa_user_handler.S @@ -238,11 +238,30 @@ _xtensa_user_handler: rsr a6, EXCCAUSE /* Argument 1 (a6) = EXCCAUSE */ mov a7, sp /* Argument 2 (a7) = pointer to register save area */ call4 xtensa_user /* Call xtensa_user */ + mov a2, a6 #endif - /* xtensa_user should not return */ + /* Restore registers in preparation to return from interrupt */ -1: j 1b + call0 _xtensa_context_restore /* (Preserves a2) */ + + /* Restore only level-specific regs (the rest were already restored) */ + + l32i a0, a2, (4 * REG_PS) /* Retrieve interruptee's PS */ + wsr a0, PS + l32i a0, a2, (4 * REG_PC) /* Retrieve interruptee's PC */ + wsr a0, EPC_1 + l32i a0, a2, (4 * REG_A0) /* Retrieve interruptee's A0 */ + l32i sp, a2, (4 * REG_A1) /* Remove interrupt stack frame */ + l32i a2, a2, (4 * REG_A2) /* Retrieve interruptee's A2 */ + rsync /* Ensure PS and EPC written */ + + /* Return from exception. RFE returns from either the UserExceptionVector + * or the KernelExceptionVector. RFE sets PS.EXCM back to 0, and then + * jumps to the address in EPC[1]. PS.UM and PS.WOE are left unchanged. + */ + + rfe /**************************************************************************** * Name: _xtensa_syscall_handler @@ -464,21 +483,21 @@ _xtensa_coproc_handler: #endif wsr a0, PS - /* Call xtensa_user, passing both the EXCCAUSE and a pointer to the + /* Call xtensa_user_panic, passing both the EXCCAUSE and a pointer to the * beginning of the register save area. */ #ifdef __XTENSA_CALL0_ABI__ rsr a2, EXCCAUSE /* Argument 1 (a2) = EXCCAUSE */ mov a3, sp /* Argument 2 (a2) = pointer to register save area */ - calx0 xtensa_user /* Call xtensa_user */ + calx0 xtensa_user_panic /* Call xtensa_user_panic */ #else rsr a6, EXCCAUSE /* Argument 1 (a2) = EXCCAUSE */ mov a7, sp /* Argument 2 (a2) = pointer to register save area */ - call4 xtensa_user /* Call xtensa_user */ + call4 xtensa_user_panic /* Call xtensa_user_panic */ #endif - /* xtensa_user should not return */ + /* xtensa_user_panic should not return */ 1: j 1b diff --git a/arch/xtensa/src/esp32/Make.defs b/arch/xtensa/src/esp32/Make.defs index d8c71d87497..3c4a0369b32 100644 --- a/arch/xtensa/src/esp32/Make.defs +++ b/arch/xtensa/src/esp32/Make.defs @@ -95,6 +95,7 @@ CHIP_ASRCS = CHIP_CSRCS = esp32_allocateheap.c esp32_clockconfig.c esp32_cpuint.c CHIP_CSRCS += esp32_gpio.c esp32_intdecode.c esp32_irq.c esp32_region.c CHIP_CSRCS += esp32_timerisr.c +CHIP_CSRCS += esp32_user.c # Configuration-dependent ESP32 files @@ -109,4 +110,5 @@ endif ifeq ($(CONFIG_ARCH_USE_MODULE_TEXT),y) CHIP_CSRCS += esp32_modtext.c +CMN_ASRCS += xtensa_loadstore.S endif diff --git a/arch/xtensa/src/esp32/esp32_user.c b/arch/xtensa/src/esp32/esp32_user.c new file mode 100644 index 00000000000..5de680c1411 --- /dev/null +++ b/arch/xtensa/src/esp32/esp32_user.c @@ -0,0 +1,302 @@ +/**************************************************************************** + * arch/xtensa/src/esp32/esp32_user.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#include +#include + +#include "xtensa.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef CONFIG_ARCH_USE_MODULE_TEXT +extern uint32_t _smodtext; +extern uint32_t _emodtext; +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef CONFIG_ARCH_USE_MODULE_TEXT +#ifdef CONFIG_ENDIAN_BIG +#error not implemented +#endif +#if defined(CONFIG_BUILD_PROTECTED) || defined (CONFIG_BUILD_KERNEL) +#error permission check not implemented +#endif + +/**************************************************************************** + * Name: load_uint8 + * + * Description: + * Fetch a byte using 32-bit aligned access. + * + ****************************************************************************/ + +static uint8_t load_uint8(const uint8_t *p) +{ + const uint32_t *aligned; + uint32_t value; + unsigned int offset; + + aligned = (const uint32_t *)(((uintptr_t)p) & ~3); + value = l32i(aligned); + offset = ((uintptr_t)p) & 3; + switch (offset) + { + case 0: + return value & 0xff; + case 1: + return (value >> 8) & 0xff; + case 2: + return (value >> 16) & 0xff; + case 3: + return (value >> 24) & 0xff; + } + + /* not reached */ + + PANIC(); +} + +/**************************************************************************** + * Name: store_uint8 + * + * Description: + * Store a byte using 32-bit aligned access. + * + ****************************************************************************/ + +static void store_uint8(uint8_t *p, uint8_t v) +{ + uint32_t *aligned; + uint32_t value; + unsigned int offset; + + aligned = (uint32_t *)(((uintptr_t)p) & ~3); + value = l32i(aligned); + offset = ((uintptr_t)p) & 3; + switch (offset) + { + case 0: + value = (value & 0xffffff00) | v; + break; + case 1: + value = (value & 0xffff00ff) | (v << 8); + break; + case 2: + value = (value & 0xff00ffff) | (v << 16); + break; + case 3: + value = (value & 0x00ffffff) | (v << 24); + break; + } + + s32i(aligned, value); +} + +/**************************************************************************** + * Name: decode_s8i + * + * Description: + * Decode S8I instruction using 32-bit aligned access. + * Return non-zero on successful decoding. + * + ****************************************************************************/ + +static int decode_s8i(const uint8_t *p, uint8_t *imm8, uint8_t *s, + uint8_t *t) +{ + /* 23 16 15 12 11 8 7 4 3 0 + * | imm8 |0 1 0 0| s | t |0 0 1 0| + */ + + uint8_t b0 = load_uint8(p); + uint8_t b1 = load_uint8(p + 1); + + if ((b0 & 0xf) == 2 && (b1 & 0xf0) == 0x40) + { + *t = b0 >> 4; + *s = b1 & 0xf; + *imm8 = load_uint8(p + 2); + return 1; + } + + return 0; +} + +/**************************************************************************** + * Name: decode_l8ui + * + * Description: + * Decode L8UI instruction using 32-bit aligned access. + * Return non-zero on successful decoding. + * + ****************************************************************************/ + +static int decode_l8ui(const uint8_t *p, uint8_t *imm8, uint8_t *s, + uint8_t *t) +{ + /* 23 16 15 12 11 8 7 4 3 0 + * | imm8 |0 0 0 0| s | t |0 0 1 0| + */ + + uint8_t b0 = load_uint8(p); + uint8_t b1 = load_uint8(p + 1); + + if ((b0 & 0xf) == 2 && (b1 & 0xf0) == 0) + { + *t = b0 >> 4; + *s = b1 & 0xf; + *imm8 = load_uint8(p + 2); + return 1; + } + + return 0; +} + +/**************************************************************************** + * Name: advance_pc + * + * Description: + * Advance PC register by the given value. + * + ****************************************************************************/ + +static void advance_pc(uint32_t *regs, int diff) +{ + uint32_t nextpc; + + /* Advance to the next instruction. */ + + nextpc = regs[REG_PC] + diff; +#ifdef XCHAL_HAVE_LOOPS + /* See Xtensa ISA 4.3.2.4 Loopback Semantics */ + + if (regs[REG_LCOUNT] != 0 && nextpc == regs[REG_LEND]) + { + regs[REG_LCOUNT]--; + nextpc = regs[REG_LBEG]; + } + +#endif + regs[REG_PC] = nextpc; +} + +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xtensa_user + * + * Description: + * ESP32-specific user exception handler. + * + ****************************************************************************/ + +uint32_t *xtensa_user(int exccause, uint32_t *regs) +{ +#ifdef CONFIG_ARCH_USE_MODULE_TEXT + /* Emulate byte access for module text. + * + * ESP32 only allows word-aligned accesses to the instruction memory + * regions. A non-aligned access raises a LoadStoreErrorCause exception. + * We catch those exception and emulate byte access here because it's + * necessary in a few places during dynamic code loading: + * + * - memcpy as a part of read(2) when loading code from a file system. + * - relocation needs to inspect and modify text. + * + * (thus binfo() is used below) + */ + + if (exccause == XCHAL_EXCCAUSE_LOAD_STORE_ERROR && + (uintptr_t)&_smodtext <= regs[REG_EXCVADDR] && + (uintptr_t)&_emodtext > regs[REG_EXCVADDR]) + { + uint8_t *pc = (uint8_t *)regs[REG_PC]; + uint8_t imm8; + uint8_t s; + uint8_t t; + + binfo("XCHAL_EXCCAUSE_LOAD_STORE_ERROR at %p, pc=%p\n", + (FAR void *)regs[REG_EXCVADDR], + pc); + + if (decode_s8i(pc, &imm8, &s, &t)) + { + binfo("Emulating S8I imm8=%u, s=%u (%p), t=%u (%p)\n", + (unsigned int)imm8, + (unsigned int)s, + (void *)regs[REG_A0 + s], + (unsigned int)t, + (void *)regs[REG_A0 + t]); + + DEBUGASSERT(regs[REG_A0 + s] + imm8 == regs[REG_EXCVADDR]); + store_uint8(((uint8_t *)regs[REG_A0 + s]) + imm8, + regs[REG_A0 + t]); + advance_pc(regs, 3); + return regs; + } + else if (decode_l8ui(pc, &imm8, &s, &t)) + { + binfo("Emulating L8UI imm8=%u, s=%u (%p), t=%u (%p)\n", + (unsigned int)imm8, + (unsigned int)s, + (void *)regs[REG_A0 + s], + (unsigned int)t, + (void *)regs[REG_A0 + t]); + + DEBUGASSERT(regs[REG_A0 + s] + imm8 == regs[REG_EXCVADDR]); + regs[REG_A0 + t] = load_uint8(((uint8_t *)regs[REG_A0 + s]) + + imm8); + advance_pc(regs, 3); + return regs; + } + } + +#endif + /* xtensa_user_panic never returns. */ + + xtensa_user_panic(exccause, regs); + + while (1) + { + } +}