esp32: emulate byte access for module text

Tested on ESP-EYE.
This commit is contained in:
YAMAMOTO Takashi 2020-03-16 15:07:36 +09:00 committed by patacongo
parent df44909b30
commit f4e7845b85
7 changed files with 459 additions and 8 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -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();

View File

@ -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 <nuttx/config.h>
#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

View File

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

View File

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

View File

@ -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 <nuttx/config.h>
#include <nuttx/arch.h>
#include <arch/loadstore.h>
#include <arch/xtensa/core.h>
#include <sys/types.h>
#include <debug.h>
#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)
{
}
}