From fd9ce0137e644e10e343479ee6ccb7a51a448a2f Mon Sep 17 00:00:00 2001 From: zhuyanlin Date: Wed, 1 Sep 2021 21:37:15 +0800 Subject: [PATCH] arch:xtensa: add xtensa mpu support Add support for Xtensa Memory Protect Unit. Change-Id: I27e2f05daae24429ef7513d843b4f217daeefa0d --- arch/xtensa/src/common/mpu.h | 374 ++++++++++++++++++++++++++++ arch/xtensa/src/common/xtensa_mpu.c | 138 ++++++++++ 2 files changed, 512 insertions(+) create mode 100644 arch/xtensa/src/common/mpu.h create mode 100644 arch/xtensa/src/common/xtensa_mpu.c diff --git a/arch/xtensa/src/common/mpu.h b/arch/xtensa/src/common/mpu.h new file mode 100644 index 0000000000..75d1ae38be --- /dev/null +++ b/arch/xtensa/src/common/mpu.h @@ -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 + +#include + +#ifndef __ASSEMBLY__ +# include +# include +# include +# include +#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 */ diff --git a/arch/xtensa/src/common/xtensa_mpu.c b/arch/xtensa/src/common/xtensa_mpu.c new file mode 100644 index 0000000000..39f2c85f50 --- /dev/null +++ b/arch/xtensa/src/common/xtensa_mpu.c @@ -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 + +#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; +}