blob: 051dcfdbcf058c7ea3635a43c907dd4341549ce5 [file] [log] [blame]
/****************************************************************************
* 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/xtensa_corebits.h>
#include <sys/types.h>
#include <assert.h>
#include <debug.h>
#include "xtensa.h"
#include "esp32_spicache.h"
/****************************************************************************
* Public Data
****************************************************************************/
#ifdef CONFIG_ARCH_USE_TEXT_HEAP
extern uint8_t _siramheap[];
extern uint8_t _eiramheap[];
#endif
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
#ifdef CONFIG_ARCH_USE_TEXT_HEAP
#ifdef CONFIG_ENDIAN_BIG
#error not implemented
#endif
#ifndef CONFIG_BUILD_FLAT
#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_s16i
*
* Description:
* Decode S16I instruction using 32-bit aligned access.
* Return non-zero on successful decoding.
*
****************************************************************************/
static int decode_s16i(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 1| 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) == 0x50)
{
*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: decode_l16ui
*
* Description:
* Decode L16UI instruction using 32-bit aligned access.
* Return non-zero on successful decoding.
*
****************************************************************************/
static int decode_l16ui(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 1| 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) == 0x10)
{
*t = b0 >> 4;
*s = b1 & 0xf;
*imm8 = load_uint8(p + 2);
return 1;
}
return 0;
}
/****************************************************************************
* Name: decode_l16si
*
* Description:
* Decode L16SI instruction using 32-bit aligned access.
* Return non-zero on successful decoding.
*
****************************************************************************/
static int decode_l16si(const uint8_t *p, uint8_t *imm8, uint8_t *s,
uint8_t *t)
{
/* 23 16 15 12 11 8 7 4 3 0
* | imm8 |1 0 0 1| 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) == 0x90)
{
*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;
#if XCHAL_HAVE_LOOPS != 0
/* 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_ESP32_EXCEPTION_ENABLE_CACHE
if (!spi_flash_cache_enabled())
{
spi_enable_cache(0);
#ifdef CONFIG_SMP
spi_enable_cache(1);
#endif
_err("\nERROR: Cache was disabled and re-enabled\n");
}
#endif
#ifdef CONFIG_ARCH_USE_TEXT_HEAP
/* 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 == EXCCAUSE_LOAD_STORE_ERROR &&
(uintptr_t)_siramheap <= regs[REG_EXCVADDR] &&
(uintptr_t)_eiramheap > regs[REG_EXCVADDR])
{
uint8_t *pc = (uint8_t *)regs[REG_PC];
uint8_t imm8;
uint8_t s;
uint8_t t;
binfo("EXCCAUSE_LOAD_STORE_ERROR at %p, pc=%p\n",
(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_s16i(pc, &imm8, &s, &t))
{
binfo("Emulating S16I 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]);
uintptr_t va = regs[REG_A0 + s] + (imm8 << 1);
DEBUGASSERT(va == regs[REG_EXCVADDR]);
store_uint8((uint8_t *)va, regs[REG_A0 + t]);
store_uint8((uint8_t *)va + 1, regs[REG_A0 + t] >> 8);
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;
}
else if (decode_l16si(pc, &imm8, &s, &t))
{
binfo("Emulating L16SI 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]);
uintptr_t va = regs[REG_A0 + s] + (imm8 << 1);
DEBUGASSERT(va == regs[REG_EXCVADDR]);
uint8_t lo = load_uint8((uint8_t *)va);
uint8_t hi = load_uint8((uint8_t *)va + 1);
regs[REG_A0 + t] = (int16_t)((hi << 8) | lo);
advance_pc(regs, 3);
return regs;
}
else if (decode_l16ui(pc, &imm8, &s, &t))
{
binfo("Emulating L16UI 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]);
uintptr_t va = regs[REG_A0 + s] + (imm8 << 1);
DEBUGASSERT(va == regs[REG_EXCVADDR]);
uint8_t lo = load_uint8((uint8_t *)va);
uint8_t hi = load_uint8((uint8_t *)va + 1);
regs[REG_A0 + t] = (hi << 8) | lo;
advance_pc(regs, 3);
return regs;
}
}
#endif
/* xtensa_user_panic never returns. */
xtensa_user_panic(exccause, regs);
while (1)
{
}
}