/* * 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 #include #include #include #include /** * @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_ */