This adds a new function xtensa_mem_kernel_has_access() to determine if a memory region can be accessed by kernel threads. This allows checking for valid mapped memory before accessing them to avoid relying on page faults to detect invalid access. Also fixed an issue with arch_buffer_validate() on MPU where it may return okay even if the incoming memory region has no corresponding entry in the MPU table. Signed-off-by: Daniel Leung <daniel.leung@intel.com>
458 lines
11 KiB
C
458 lines
11 KiB
C
/*
|
|
* Copyright (c) 2023 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifndef ZEPHYR_ARCH_XTENSA_XTENSA_MPU_PRIV_H_
|
|
#define ZEPHYR_ARCH_XTENSA_XTENSA_MPU_PRIV_H_
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <zephyr/toolchain.h>
|
|
#include <zephyr/arch/xtensa/mpu.h>
|
|
#include <zephyr/sys/util_macro.h>
|
|
|
|
#include <xtensa/config/core-isa.h>
|
|
|
|
/**
|
|
* @defgroup xtensa_mpu_internal_apis Xtensa Memory Protection Unit (MPU) Internal APIs
|
|
* @ingroup xtensa_mpu_apis
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @name Bit shifts and masks for MPU entry registers.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Number of bits to shift for start address in MPU entry register.
|
|
*
|
|
* This is only used for aligning the value to the MPU entry register,
|
|
* and is different than the hardware alignment requirement.
|
|
*/
|
|
#define XTENSA_MPU_ENTRY_REG_START_ADDR_SHIFT 5U
|
|
|
|
/**
|
|
* Bit mask of start address in MPU entry register.
|
|
*
|
|
* This is only used for aligning the value to the MPU entry register,
|
|
* and is different than the hardware alignment requirement.
|
|
*/
|
|
#define XTENSA_MPU_ENTRY_REG_START_ADDR_MASK 0xFFFFFFE0U
|
|
|
|
/** Number of bits to shift for enable bit in MPU entry register. */
|
|
#define XTENSA_MPU_ENTRY_REG_ENABLE_SHIFT 0U
|
|
|
|
/** Bit mask of enable bit in MPU entry register. */
|
|
#define XTENSA_MPU_ENTRY_REG_ENABLE_MASK BIT(XTENSA_MPU_ENTRY_ENABLE_SHIFT)
|
|
|
|
/** Number of bits to shift for lock bit in MPU entry register. */
|
|
#define XTENSA_MPU_ENTRY_REG_LOCK_SHIFT 1U
|
|
|
|
/** Bit mask of lock bit in MPU entry register. */
|
|
#define XTENSA_MPU_ENTRY_REG_LOCK_MASK BIT(XTENSA_MPU_ENTRY_LOCK_SHIFT)
|
|
|
|
/** Number of bits to shift for access rights in MPU entry register. */
|
|
#define XTENSA_MPU_ENTRY_REG_ACCESS_RIGHTS_SHIFT 8U
|
|
|
|
/** Bit mask of access rights in MPU entry register. */
|
|
#define XTENSA_MPU_ENTRY_REG_ACCESS_RIGHTS_MASK \
|
|
(0xFU << XTENSA_MPU_ENTRY_REG_ACCESS_RIGHTS_SHIFT)
|
|
|
|
/** Number of bits to shift for memory type in MPU entry register. */
|
|
#define XTENSA_MPU_ENTRY_REG_MEMORY_TYPE_SHIFT 12U
|
|
|
|
/** Bit mask of memory type in MPU entry register. */
|
|
#define XTENSA_MPU_ENTRY_REG_MEMORY_TYPE_MASK \
|
|
(0x1FFU << XTENSA_MPU_ENTRY_REG_MEMORY_TYPE_SHIFT)
|
|
|
|
/** Bit mask for foreground entry returned by probing. */
|
|
#define XTENSA_MPU_PROBE_IS_FG_ENTRY_MASK BIT(31)
|
|
|
|
/** Bit mask for background entry returned by probing. */
|
|
#define XTENSA_MPU_PROBE_IS_BG_ENTRY_MASK BIT(30)
|
|
|
|
/** Bit mask used to determine if entry is valid returned by probing. */
|
|
#define XTENSA_MPU_PROBE_VALID_ENTRY_MASK \
|
|
(XTENSA_MPU_PROBE_IS_FG_ENTRY_MASK | XTENSA_MPU_PROBE_IS_BG_ENTRY_MASK)
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @name Bit shifts and masks for MPU PPTLB return value.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/** Bit shift for segment value. */
|
|
#define XTENSA_MPU_PPTLB_ACCESS_RIGHTS_SHIFT 8U
|
|
|
|
/** Mask for segment value. */
|
|
#define XTENSA_MPU_PPTLB_ACCESS_RIGHTS_MASK 0x00000F00U
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
|
|
/**
|
|
* Define one MPU entry of type struct xtensa_mpu_entry.
|
|
*
|
|
* @note This needs a comma at the end if used in array declaration.
|
|
*
|
|
* @param saddr Start address.
|
|
* @param en Enable bit
|
|
* @param rights Access rights.
|
|
* @param memtype Memory type.
|
|
*/
|
|
#define XTENSA_MPU_ENTRY(saddr, en, rights, memtype) \
|
|
{ \
|
|
.as.p.enable = en, \
|
|
.as.p.lock = 0, \
|
|
.as.p.mbz = 0, \
|
|
.as.p.start_addr = (saddr >> XTENSA_MPU_ENTRY_START_ADDR_SHIFT), \
|
|
.at.p.segment = 0, \
|
|
.at.p.mbz1 = 0, \
|
|
.at.p.access_rights = rights, \
|
|
.at.p.memory_type = memtype, \
|
|
.at.p.mbz2 = 0, \
|
|
}
|
|
|
|
/**
|
|
* @brief Read MPUCFG register.
|
|
*
|
|
* This returns the bitmask of enabled MPU entries (foreground segments).
|
|
*
|
|
* @return Value of MPUCFG register.
|
|
*/
|
|
static ALWAYS_INLINE uint32_t xtensa_mpu_mpucfg_read(void)
|
|
{
|
|
uint32_t mpucfg;
|
|
|
|
__asm__ __volatile__("rsr.mpucfg %0" : "=a" (mpucfg));
|
|
|
|
return mpucfg;
|
|
}
|
|
|
|
/**
|
|
* @brief Read MPUENB register.
|
|
*
|
|
* This returns the enable bits for MPU entries.
|
|
*
|
|
* @return Value of MPUENB register.
|
|
*/
|
|
static ALWAYS_INLINE uint32_t xtensa_mpu_mpuenb_read(void)
|
|
{
|
|
uint32_t mpuenb;
|
|
|
|
__asm__ __volatile__("rsr.mpuenb %0" : "=a" (mpuenb));
|
|
|
|
return mpuenb;
|
|
}
|
|
|
|
/**
|
|
* @brief Write MPUENB register.
|
|
*
|
|
* This writes the enable bits for MPU entries.
|
|
*
|
|
* @param mpuenb Value to be written.
|
|
*/
|
|
static ALWAYS_INLINE void xtensa_mpu_mpuenb_write(uint32_t mpuenb)
|
|
{
|
|
__asm__ __volatile__("wsr.mpuenb %0" : : "a"(mpuenb));
|
|
}
|
|
|
|
/**
|
|
* @brief Probe for protection TLB entry from an address.
|
|
*
|
|
* @param addr Probe address.
|
|
*
|
|
* @return Return of the PPTLB instruction.
|
|
*/
|
|
static ALWAYS_INLINE uint32_t xtensa_pptlb_probe(uintptr_t addr)
|
|
{
|
|
uint32_t ret;
|
|
|
|
__asm__ __volatile__("pptlb %0, %1\n\t" : "=a"(ret) : "a"(addr));
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @name MPU entry internal helper functions.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Return the start address encoded in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
*
|
|
* @return Start address.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
uintptr_t xtensa_mpu_entry_start_address_get(const struct xtensa_mpu_entry *entry)
|
|
{
|
|
return (entry->as.p.start_addr << XTENSA_MPU_ENTRY_REG_START_ADDR_SHIFT);
|
|
}
|
|
|
|
/**
|
|
* @brief Set the start address encoded in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
* @param addr Start address.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
void xtensa_mpu_entry_start_address_set(struct xtensa_mpu_entry *entry, uintptr_t addr)
|
|
{
|
|
entry->as.p.start_addr = addr >> XTENSA_MPU_ENTRY_REG_START_ADDR_SHIFT;
|
|
}
|
|
|
|
/**
|
|
* @brief Return the lock bit encoded in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
*
|
|
* @retval True Lock bit is set.
|
|
* @retval False Lock bit is not set.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
bool xtensa_mpu_entry_lock_get(const struct xtensa_mpu_entry *entry)
|
|
{
|
|
return entry->as.p.lock != 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the lock bit encoded in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
* @param lock True if to lock the MPU entry.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
void xtensa_mpu_entry_lock_set(struct xtensa_mpu_entry *entry, bool lock)
|
|
{
|
|
entry->as.p.lock = lock ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Return the enable bit encoded in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
*
|
|
* @retval True Enable bit is set.
|
|
* @retval False Enable bit is not set.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
bool xtensa_mpu_entry_enable_get(const struct xtensa_mpu_entry *entry)
|
|
{
|
|
return entry->as.p.enable != 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the enable bit encoded in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
* @param en True if to enable the MPU entry.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
void xtensa_mpu_entry_enable_set(struct xtensa_mpu_entry *entry, bool en)
|
|
{
|
|
entry->as.p.enable = en ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Return the access rights encoded in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
*
|
|
* @return Access right value.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
uint8_t xtensa_mpu_entry_access_rights_get(const struct xtensa_mpu_entry *entry)
|
|
{
|
|
return entry->at.p.access_rights;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the lock bit encoded in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
* @param access_rights Access rights to be set.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
void xtensa_mpu_entry_access_rights_set(struct xtensa_mpu_entry *entry, uint8_t access_rights)
|
|
{
|
|
entry->at.p.access_rights = access_rights;
|
|
}
|
|
|
|
/**
|
|
* @brief Return the memory type encoded in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
*
|
|
* @return Memory type value.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
uint16_t xtensa_mpu_entry_memory_type_get(const struct xtensa_mpu_entry *entry)
|
|
{
|
|
return entry->at.p.memory_type;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the memory type in the MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
* @param memory_type Memory type to be set.
|
|
*/
|
|
static ALWAYS_INLINE
|
|
void xtensa_mpu_entry_memory_type_set(struct xtensa_mpu_entry *entry, uint16_t memory_type)
|
|
{
|
|
entry->at.p.memory_type = memory_type;
|
|
}
|
|
|
|
/**
|
|
* @brief Set both access rights and memory type of a MPU entry.
|
|
*
|
|
* @param entry Pointer to the MPU entry.
|
|
* @param access_rights Access rights value.
|
|
* @param memory_type Memory type value.
|
|
*/
|
|
static inline
|
|
void xtensa_mpu_entry_attributes_set(struct xtensa_mpu_entry *entry,
|
|
uint8_t access_rights, uint16_t memory_type)
|
|
{
|
|
xtensa_mpu_entry_access_rights_set(entry, access_rights);
|
|
xtensa_mpu_entry_memory_type_set(entry, memory_type);
|
|
}
|
|
|
|
/**
|
|
* @brief Set fields in MPU entry so it will be functional.
|
|
*
|
|
* This sets the starting address, enable bit, access rights and memory type
|
|
* of an entry.
|
|
*
|
|
* Note that this preserves the valud of the segment field.
|
|
*
|
|
* @param entry Pointer to the entry to be manipulated.
|
|
* @param start_address Start address to be set.
|
|
* @param enable Whether this entry should be enabled.
|
|
* @param access_rights Access rights for the entry.
|
|
* @param memory_type Memory type for the entry.
|
|
*/
|
|
static inline
|
|
void xtensa_mpu_entry_set(struct xtensa_mpu_entry *entry, uintptr_t start_address,
|
|
bool enable, uint8_t access_rights, uint16_t memory_type)
|
|
{
|
|
uint8_t segment = entry->at.p.segment;
|
|
|
|
/* Clear out the fields, and make sure MBZ fields are zero. */
|
|
entry->as.raw = 0;
|
|
entry->at.raw = 0;
|
|
|
|
xtensa_mpu_entry_start_address_set(entry, start_address);
|
|
xtensa_mpu_entry_enable_set(entry, enable);
|
|
xtensa_mpu_entry_access_rights_set(entry, access_rights);
|
|
xtensa_mpu_entry_memory_type_set(entry, memory_type);
|
|
|
|
entry->at.p.segment = segment;
|
|
}
|
|
|
|
/**
|
|
* @brief Test if two MPU entries have same access rights.
|
|
*
|
|
* @param entry1 MPU entry #1
|
|
* @param entry2 MPU entry #2.
|
|
*
|
|
* @return True if access rights are the same, false otherwise.
|
|
*/
|
|
static inline
|
|
bool xtensa_mpu_entries_has_same_access_rights(const struct xtensa_mpu_entry *entry1,
|
|
const struct xtensa_mpu_entry *entry2)
|
|
{
|
|
return entry1->at.p.access_rights == entry2->at.p.access_rights;
|
|
}
|
|
|
|
/**
|
|
* @brief Test if two MPU entries have same memory types.
|
|
*
|
|
* @param entry1 MPU entry #1.
|
|
* @param entry2 MPU entry #2.
|
|
*
|
|
* @return True if memory types are the same, false otherwise.
|
|
*/
|
|
static inline
|
|
bool xtensa_mpu_entries_has_same_memory_type(const struct xtensa_mpu_entry *entry1,
|
|
const struct xtensa_mpu_entry *entry2)
|
|
{
|
|
return entry1->at.p.memory_type == entry2->at.p.memory_type;
|
|
}
|
|
|
|
/**
|
|
* @brief Test if two MPU entries have same access rights and memory types.
|
|
*
|
|
* @param entry1 MPU entry #1.
|
|
* @param entry2 MPU entry #2.
|
|
*
|
|
* @return True if access rights and memory types are the same, false otherwise.
|
|
*/
|
|
static inline
|
|
bool xtensa_mpu_entries_has_same_attributes(const struct xtensa_mpu_entry *entry1,
|
|
const struct xtensa_mpu_entry *entry2)
|
|
{
|
|
return xtensa_mpu_entries_has_same_access_rights(entry1, entry2) &&
|
|
xtensa_mpu_entries_has_same_memory_type(entry1, entry2);
|
|
}
|
|
|
|
/**
|
|
* @brief Test if two entries has the same addresses.
|
|
*
|
|
* @param entry1 MPU entry #1.
|
|
* @param entry2 MPU entry #2.
|
|
*
|
|
* @return True if they have the same address, false otherwise.
|
|
*/
|
|
static inline
|
|
bool xtensa_mpu_entries_has_same_address(const struct xtensa_mpu_entry *entry1,
|
|
const struct xtensa_mpu_entry *entry2)
|
|
{
|
|
return xtensa_mpu_entry_start_address_get(entry1)
|
|
== xtensa_mpu_entry_start_address_get(entry2);
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @name MPU access rights helper functions.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Test if the access rights is valid.
|
|
*
|
|
* @param access_rights Access rights value.
|
|
*
|
|
* @return True if access rights is valid, false otherwise.
|
|
*/
|
|
static ALWAYS_INLINE bool xtensa_mpu_access_rights_is_valid(uint8_t access_rights)
|
|
{
|
|
return (access_rights != 1) && (access_rights <= 15);
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
#endif /* ZEPHYR_ARCH_XTENSA_XTENSA_MPU_PRIV_H_ */
|