arch:xtensa: add xtensa mpu support

Add support for Xtensa Memory Protect Unit.

Change-Id: I27e2f05daae24429ef7513d843b4f217daeefa0d
This commit is contained in:
zhuyanlin 2021-09-01 21:37:15 +08:00 committed by Alan Carvalho de Assis
parent 9d870e1502
commit fd9ce0137e
2 changed files with 512 additions and 0 deletions

View File

@ -0,0 +1,374 @@
/****************************************************************************
* arch/xtensa/src/common/mpu.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_SRC_COMMON_XTENSA_MPU_H
#define __ARCH_XTENSA_SRC_COMMON_XTENSA_MPU_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <arch/chip/core-isa.h>
#ifndef __ASSEMBLY__
# include <sys/types.h>
# include <stdint.h>
# include <stdbool.h>
# include <assert.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MPU_VADDR_MASK ~(XCHAL_MPU_ALIGN - (1))
#define MPU_ENTRY_AS(vaddr, valid) \
(((vaddr) & MPU_VADDR_MASK) | \
((valid) & (0x1)))
#define MPU_ENTRY_AR(access, memtype) \
(((ENCODE_MEMORY_TYPE(memtype)) << (12)) | \
(((access) & (0xf)) << (8)))
/****************************************************************************
* MPU access rights constants
****************************************************************************/
#define MPU_AR_NONE (0) /* no access */
#define MPU_AR_R (4) /* Kernel read, User no access*/
#define MPU_AR_RX (5) /* Kernel read/execute, User no access */
#define MPU_AR_RW (6) /* Kernel read/write, User no access */
#define MPU_AR_RWX (7) /* Kernel read/write/execute, User no access */
#define MPU_AR_Ww (8) /* Kernel write, User write */
#define MPU_AR_RWrwx (9) /* Kernel read/write , User read/write/execute */
#define MPU_AR_RWr (10) /* Kernel read/write, User read */
#define MPU_AR_RWXrx (11) /* Kernel read/write/execute, User read/execute */
#define MPU_AR_Rr (12) /* Kernel read, User read */
#define MPU_AR_RXrx (13) /* Kernel read/execute, User read/execute */
#define MPU_AR_RWrw (14) /* Kernel read/write, User read/write */
#define MPU_AR_RWXrwx (15) /* Kernel read/write/execute, User read/write/execute */
#define MPU_AR_WIDTH 4 /* Bits used to encode access rights */
/****************************************************************************
* MPU access memtype constants
****************************************************************************/
#define MPU_MEM_DEVICE (0x00008000)
#define MPU_MEM_NON_CACHEABLE (0x00090000)
#define MPU_MEM_WRITETHRU_NOALLOC (0x00080000)
#define MPU_MEM_WRITETHRU (0x00040000)
#define MPU_MEM_WRITETHRU_WRITEALLOC (0x00060000)
#define MPU_MEM_WRITEBACK_NOALLOC (0x00050000)
#define MPU_MEM_WRITEBACK (0x00070000)
#define MPU_MEM_INTERRUPTIBLE (0x08000000)
#define MPU_MEM_BUFFERABLE (0x01000000)
#define MPU_MEM_NON_SHAREABLE (0x00000000)
#define MPU_MEM_INNER_SHAREABLE (0x02000000)
#define MPU_MEM_OUTER_SHAREABLE (0x04000000)
#define MPU_MEM_SYSTEM_SHAREABLE (0x06000000)
/****************************************************************************
* Layout of the MPU memory type specifier for: ENCODE_MEMORY_TYPE()
*
* Bits 0-3 - reserved for pass through of accessRights
* Bits 4-12 - reserved for pass through of memoryType bits
* Bit 13 - indicates to use existing access rights of region
* Bit 14 - indicates to use existing memory type of region
* Bit 15 - indicates device
* Bit 16-19- system cache properties
* Bit 20-23- local cache properties
* Bit 24 - indicates bufferable
* Bit 25-26- encodes shareability (1=inner, 2=outer, 3=system)
* Bit 27 - indicates interruptible
* Bits 28-31- reserved for future use
****************************************************************************/
#define SYSTEM_CACHE_BITS (0x000f0000)
#define LOCAL_CACHE_BITS (0x00f00000)
#define SYSTEM_RWC_MASK (0x00070000)
#define LOCAL_RWC_MASK (0x00700000)
#define SHIFT_RWC 16
#define MEM_ANY_SHAREABLE(x) (((x & MPU_MEM_SYSTEM_SHAREABLE) \
!= (0)) ? (1) : (0))
#define MEM_INNER_SHAREABLE(x) (((x & MPU_MEM_SYSTEM_SHAREABLE) \
== MPU_MEM_INNER_SHAREABLE) ? (1) : (0))
#define MEM_IS_BUFFERABLE(x) (((x & MPU_MEM_BUFFERABLE) != (0)) ? \
(1) : (0))
#define MEM_IS_DEVICE(x) (((x & MPU_MEM_DEVICE) != (0)) ? (1) : (0))
#define NON_CACHEABLE_DOMAIN(x) \
((MEM_IS_DEVICE(x) != (0)) || \
(MEM_ANY_SHAREABLE(x) != (0)) ? (0x3) : (0))
#define CACHEABLE_DOMAIN(x) ((MEM_ANY_SHAREABLE(x) != (0)) ? \
(0x3) : (0x1))
#define MEM_CACHE_MASK(x) (x & SYSTEM_CACHE_BITS)
#define IS_SYSTEM_NONCACHEABLE(x) \
(((MEM_CACHE_MASK(x) & MPU_MEM_NON_CACHEABLE) == \
MPU_MEM_NON_CACHEABLE) ? (1) : (0))
#define ENCODE_DEVICE(x) \
(((((x & MPU_MEM_INTERRUPTIBLE) != (0)) \
? (1) : (0)) << 3) | \
((NON_CACHEABLE_DOMAIN(x) << 1) | MEM_IS_BUFFERABLE(x)))
#define ENCODE_SYSTEM_NONCACHEABLE(x) \
(((x & LOCAL_CACHE_BITS) != (0)) && \
(((x & LOCAL_CACHE_BITS) >> 4) != MPU_MEM_NON_CACHEABLE)) ? \
((0x89) | (((x) & LOCAL_CACHE_BITS) >> SHIFT_RWC)) :\
((((0x18) | (NON_CACHEABLE_DOMAIN(x) << 1)) \
| MEM_IS_BUFFERABLE(x)))
#define ENCODE_SYSTEM_CACHEABLE(x) \
(((((x & LOCAL_CACHE_BITS) >> 4) & MPU_MEM_NON_CACHEABLE) == \
MPU_MEM_NON_CACHEABLE) ? \
(CACHEABLE_DOMAIN(x) << 4) : \
ENCODE_SYSTEM_CACHEABLE_LOCAL_CACHEABLE(x)) | \
((MEM_INNER_SHAREABLE(x) << 3) | \
((MEM_CACHE_MASK(x) & SYSTEM_RWC_MASK) \
>> SHIFT_RWC))
#define ENCODE_SYSTEM_CACHEABLE_LOCAL_CACHEABLE(x) \
((CACHEABLE_DOMAIN(x) << 7) | (((((x & LOCAL_CACHE_BITS) != (0)) ? \
(x & LOCAL_CACHE_BITS) : \
((MEM_CACHE_MASK(x) << 4)) \
& (LOCAL_RWC_MASK)) >> SHIFT_RWC)))
#define ENCODE_MEMORY_TYPE(x) \
(((x & (0xffffe000)) != (0)) ? \
((MEM_IS_DEVICE((x)) != (0)) ? ENCODE_DEVICE((x)) : \
((IS_SYSTEM_NONCACHEABLE((x)) != (0)) ? \
ENCODE_SYSTEM_NONCACHEABLE((x)) : \
ENCODE_SYSTEM_CACHEABLE((x)))) : x)
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
#ifndef __ASSEMBLY__
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Name: mpu_allocregion
*
* Description:
* Allocate the next region
*
****************************************************************************/
unsigned int mpu_allocregion(void);
/****************************************************************************
* Name: mpu_control
*
* Description:
* Configure and enable (or disable) the MPU
*
****************************************************************************/
void mpu_control(bool enable);
/****************************************************************************
* Name: mpu_configure_region
*
* Description:
* Configure a region for privileged, strongly ordered memory
*
****************************************************************************/
void mpu_configure_region(uintptr_t base, size_t size,
uint8_t acc, uint16_t memtype);
/****************************************************************************
* Name: mpu_priv_stronglyordered
*
* Description:
* Configure a region for privileged, strongly ordered memory
*
****************************************************************************/
#define mpu_priv_stronglyordered(base, size) \
do \
{ \
/* The configure the region */ \
mpu_configure_region(base, size, \
MPU_AR_RWX, \
MPU_MEM_DEVICE); \
} while (0)
/****************************************************************************
* Name: mpu_user_flash
*
* Description:
* Configure a region for user program flash
*
****************************************************************************/
#define mpu_user_flash(base, size) \
do \
{ \
/* The configure the region */ \
mpu_configure_region(base, size, \
MPU_AR_RXrx, \
MPU_MEM_WRITEBACK);\
} while (0)
/****************************************************************************
* Name: mpu_priv_flash
*
* Description:
* Configure a region for privileged program flash
*
****************************************************************************/
#define mpu_priv_flash(base, size) \
do \
{ \
/* The configure the region */ \
mpu_configure_region(base, size, \
MPU_AR_RX, \
MPU_MEM_WRITEBACK);\
} while (0)
/****************************************************************************
* Name: mpu_user_intsram
*
* Description:
* Configure a region as user internal SRAM
*
****************************************************************************/
#define mpu_user_intsram(base, size) \
do \
{ \
/* The configure the region */ \
mpu_configure_region(base, size, \
MPU_AR_RWXrwx, \
MPU_MEM_WRITEBACK);\
} while (0)
/****************************************************************************
* Name: mpu_priv_intsram
*
* Description:
* Configure a region as privileged internal SRAM
*
****************************************************************************/
#define mpu_priv_intsram(base, size) \
do \
{ \
/* The configure the region */ \
mpu_configure_region(base, size,\
MPU_AR_RWX, \
MPU_MEM_WRITETHRU);\
} while (0)
/****************************************************************************
* Name: mpu_user_extsram
*
* Description:
* Configure a region as user external SRAM
*
****************************************************************************/
#define mpu_user_extsram(base, size) \
do \
{ \
/* The configure the region */ \
mpu_configure_region(base, size, \
MPU_AR_RWXrwx, \
MPU_MEM_WRITETHRU);\
} while (0)
/****************************************************************************
* Name: mpu_priv_extsram
*
* Description:
* Configure a region as privileged external SRAM
*
****************************************************************************/
#define mpu_priv_extsram(base, size) \
do \
{ \
/* The configure the region */ \
mpu_configure_region(base, size, \
MPU_AR_RWX, \
MPU_MEM_WRITETHRU);\
} while (0)
/****************************************************************************
* Name: mpu_peripheral
*
* Description:
* Configure a region as privileged peripheral address space
*
****************************************************************************/
#define mpu_peripheral(base, size) \
do \
{ \
/* Then configure the region */ \
mpu_configure_region(base, size, \
MPU_AR_RW, \
MPU_MEM_DEVICE);\
} while (0)
/****************************************************************************
* Name: mpu_user_peripheral
*
* Description:
* Configure a region as user peripheral address space
*
****************************************************************************/
#define mpu_user_peripheral(base, size) \
do \
{ \
/* Then configure the region */ \
mpu_configure_region(base, size, \
MPU_AR_RWrw, \
MPU_MEM_DEVICE);\
} while (0)
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_XTENSA_SRC_COMMON_XTENSA_MPU_H */

View File

@ -0,0 +1,138 @@
/****************************************************************************
* arch/xtensa/src/common/xtensa_mpu.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 "mpu.h"
/****************************************************************************
* Private Data
****************************************************************************/
/* Cache for mpu base address in entry */
static uint32_t xtensa_mpu_base[XCHAL_MPU_ENTRIES];
/* The next available region number in decreasing way */
static uint8_t g_region = XCHAL_MPU_ENTRIES;
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mpu_allocregion
*
* Description:
* Allocate the next region
*
* Assumptions:
* - Regions are never deallocated
* - Regions are only allocated early in initialization, so no special
* protection against re-entrancy is required;
*
****************************************************************************/
unsigned int mpu_allocregion(void)
{
DEBUGASSERT((unsigned int)g_region >= 0);
return (unsigned int)--g_region;
}
/****************************************************************************
* Name: mpu_control
*
* Description:
* Configure and enable (or disable) the MPU
*
****************************************************************************/
void mpu_control(bool enable)
{
uint32_t regval = 0;
if (enable)
{
uint32_t i;
for (i = XCHAL_MPU_ENTRIES - 1; i >= g_region; i--)
{
regval |= (1 << i);
}
}
__asm__ __volatile__ ("wsr %0, mpuenb\n" : : "r"(regval));
}
/****************************************************************************
* Name: mpu_configure_region
*
* Description:
* Configure a region
*
****************************************************************************/
void mpu_configure_region(uintptr_t base, size_t size,
uint8_t acc, uint16_t memtype)
{
unsigned int region = mpu_allocregion();
uint32_t at;
uint32_t as;
/* Ensure address not overlay
*
* Xtensa ISA Reference Manual B4.6.5.3:
* The lowest address of each foregound segment
* must be no smaller than the lowest address of the
* preceding foregroud segment in numerical order.
*/
if (region < (XCHAL_MPU_ENTRIES - 1))
{
DEBUGASSERT(base < xtensa_mpu_base[region + 1]);
}
/* Now only setup MPU entry, enable mpu in mpu_control */
as = MPU_ENTRY_AS(base, 0);
at = MPU_ENTRY_AR(acc, memtype);
/* Set segment bits */
at |= region;
/* update mpu entry .. requires an memw on same cache line */
__asm__ __volatile__
(
"\tj 1f\n"
"\t.align 16\n"
"\t1: memw\n"
"\twptlb %0, %1\n"
:
: "a" (at), "a"(as)
);
xtensa_mpu_base[region] = base;
}