diff --git a/payloads/libpayload/arch/armv7/cache.c b/payloads/libpayload/arch/armv7/cache.c index 60d5f742b9..9fac442f17 100644 --- a/payloads/libpayload/arch/armv7/cache.c +++ b/payloads/libpayload/arch/armv7/cache.c @@ -26,7 +26,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * cache.c: Low-level cache operations for ARMv7 + * cache.c: Cache maintenance routines for ARMv7-A and ARMv7-R + * + * Reference: ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition */ #include @@ -52,8 +54,8 @@ void tlb_invalidate_all(void) { /* * FIXME: ARMv7 Architecture Ref. Manual claims that the distinction - * instruction vs. data TLBs is deprecated in ARMv7. But that doesn't - * really seem true for Cortex-A15? + * instruction vs. data TLBs is deprecated in ARMv7, however this does + * not seem to be the case as of Cortex-A15. */ tlbiall(); dtlbiall(); @@ -64,7 +66,8 @@ void tlb_invalidate_all(void) void icache_invalidate_all(void) { - /* icache can be entirely invalidated with one operation. + /* + * icache can be entirely invalidated with one operation. * Note: If branch predictors are architecturally-visible, ICIALLU * also performs a BPIALL operation (B2-1283 in arch manual) */ @@ -74,10 +77,17 @@ void icache_invalidate_all(void) enum dcache_op { OP_DCCISW, - OP_DCISW + OP_DCISW, + OP_DCCIMVAC, + OP_DCCMVAC, }; -/* do a dcache operation on entire cache by set/way */ +/* + * Do a dcache operation on entire cache by set/way. This is done for + * portability because mapping of memory address to cache location is + * implementation defined (See note on "Requirements for operations by + * set/way" in arch ref. manual). + */ static void dcache_op_set_way(enum dcache_op op) { uint32_t ccsidr; @@ -101,6 +111,8 @@ static void dcache_op_set_way(enum dcache_op op) /* FIXME: do we need to use CTR.DminLine here? */ linesize_bytes = (1 << ((ccsidr & 0x7) + 2)) * 4; + dsb(); + /* * Set/way operations require an interesting bit packing. See section * B4-35 in the ARMv7 Architecture Reference Manual: @@ -134,8 +146,7 @@ static void dcache_op_set_way(enum dcache_op op) } } } - - dsb(); + isb(); } void dcache_clean_invalidate_all(void) @@ -161,18 +172,116 @@ static unsigned int line_bytes(void) return size; } +/* + * Do a dcache operation by modified virtual address. This is useful for + * maintaining coherency in drivers which do DMA transfers and only need to + * perform cache maintenance on a particular memory range rather than the + * entire cache. + */ +static void dcache_op_mva(unsigned long addr, + unsigned long len, enum dcache_op op) +{ + unsigned long line, linesize; + + linesize = line_bytes(); + line = addr & ~(linesize - 1); + + dsb(); + while (line < addr + len) { + switch(op) { + case OP_DCCIMVAC: + dccimvac(line); + break; + default: + break; + } + line += linesize; + } + isb(); +} + +void dcache_clean_by_mva(unsigned long addr, unsigned long len) +{ + dcache_op_mva(addr, len, OP_DCCMVAC); +} + void dcache_clean_invalidate_by_mva(unsigned long addr, unsigned long len) { - unsigned long line, i; - - line = line_bytes(); - for (i = addr & ~(line - 1); i < addr + len - 1; i += line) - dccimvac(addr); + dcache_op_mva(addr, len, OP_DCCIMVAC); } -/* FIXME: wrapper around imported mmu_setup() for now */ -extern void mmu_setup(unsigned long start, unsigned long size); -void mmu_setup_by_mva(unsigned long start, unsigned long size) +void dcache_mmu_disable(void) { - mmu_setup(start, size); + uint32_t sctlr, csselr; + + /* ensure L1 data/unified cache is selected */ + csselr = read_csselr(); + csselr &= ~0xf; + write_csselr(csselr); + + dcache_clean_invalidate_all(); + + sctlr = read_sctlr(); + sctlr &= ~(SCTLR_C | SCTLR_M); + write_sctlr(sctlr); +} + + +void dcache_mmu_enable(void) +{ + uint32_t sctlr; + + sctlr = read_sctlr(); + dcache_clean_invalidate_all(); + sctlr |= SCTLR_C | SCTLR_M; + write_sctlr(sctlr); +} + +void armv7_invalidate_caches(void) +{ + uint32_t clidr; + int level; + + /* Invalidate branch predictor */ + bpiall(); + + /* Iterate thru each cache identified in CLIDR and invalidate */ + clidr = read_clidr(); + for (level = 0; level < 7; level++) { + unsigned int ctype = (clidr >> (level * 3)) & 0x7; + uint32_t csselr; + + switch(ctype) { + case 0x0: + /* no cache */ + break; + case 0x1: + /* icache only */ + csselr = (level << 1) | 1; + write_csselr(csselr); + icache_invalidate_all(); + break; + case 0x2: + case 0x4: + /* dcache only or unified cache */ + dcache_invalidate_all(); + break; + case 0x3: + /* separate icache and dcache */ + csselr = (level << 1) | 1; + write_csselr(csselr); + icache_invalidate_all(); + + csselr = level < 1; + write_csselr(csselr); + dcache_invalidate_all(); + break; + default: + /* reserved */ + break; + } + } + + /* Invalidate TLB */ + tlb_invalidate_all(); } diff --git a/payloads/libpayload/include/armv7/arch/cache.h b/payloads/libpayload/include/armv7/arch/cache.h index 5125b8c449..f074a3b158 100644 --- a/payloads/libpayload/include/armv7/arch/cache.h +++ b/payloads/libpayload/include/armv7/arch/cache.h @@ -25,6 +25,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * cache.h: Cache maintenance API for ARMv7 */ #ifndef ARMV7_CACHE_H @@ -91,7 +93,7 @@ static inline void isb(void) /* invalidate entire data TLB */ static inline void dtlbiall(void) { - asm volatile ("mcr p15, 0, %0, c8, c6, 0" : : "r" (0)); + asm volatile ("mcr p15, 0, %0, c8, c6, 0" : : "r" (0) : "memory"); } /* invalidate entire instruction TLB */ @@ -103,7 +105,33 @@ static inline void itlbiall(void) /* invalidate entire unified TLB */ static inline void tlbiall(void) { - asm volatile ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0)); + asm volatile ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0) : "memory"); +} + +/* write data access control register (DACR) */ +static inline void write_dacr(uint32_t val) +{ + asm volatile ("mcr p15, 0, %0, c3, c0, 0" : : "r" (val)); +} + +/* write translation table base register 0 (TTBR0) */ +static inline void write_ttbr0(uint32_t val) +{ + asm volatile ("mcr p15, 0, %0, c2, c0, 0" : : "r" (val) : "memory"); +} + +/* read translation table base control register (TTBCR) */ +static inline uint32_t read_ttbcr(void) +{ + uint32_t val = 0; + asm volatile ("mrc p15, 0, %0, c2, c0, 2" : "=r" (val)); + return val; +} + +/* write translation table base control register (TTBCR) */ +static inline void write_ttbcr(uint32_t val) +{ + asm volatile ("mcr p15, 0, %0, c2, c0, 2" : : "r" (val) : "memory"); } /* @@ -119,31 +147,31 @@ static inline void bpiall(void) /* data cache clean and invalidate by MVA to PoC */ static inline void dccimvac(unsigned long mva) { - asm volatile ("mcr p15, 0, %0, c7, c14, 1" : : "r" (mva)); + asm volatile ("mcr p15, 0, %0, c7, c14, 1" : : "r" (mva) : "memory"); } /* data cache invalidate by set/way */ static inline void dccisw(uint32_t val) { - asm volatile ("mcr p15, 0, %0, c7, c14, 2" : : "r" (val)); -} - -/* data cache invalidate by set/way */ -static inline void dcisw(uint32_t val) -{ - asm volatile ("mcr p15, 0, %0, c7, c6, 2" : : "r" (val)); + asm volatile ("mcr p15, 0, %0, c7, c14, 2" : : "r" (val) : "memory"); } /* data cache clean by MVA to PoC */ static inline void dccmvac(unsigned long mva) { - asm volatile ("mcr p15, 0, %0, c7, c10, 1" : : "r" (mva)); + asm volatile ("mcr p15, 0, %0, c7, c10, 1" : : "r" (mva) : "memory"); } /* data cache invalidate by MVA to PoC */ static inline void dcimvac(unsigned long mva) { - asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (mva)); + asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (mva) : "memory"); +} + +/* data cache invalidate by set/way */ +static inline void dcisw(uint32_t val) +{ + asm volatile ("mcr p15, 0, %0, c7, c6, 2" : : "r" (val) : "memory"); } /* instruction cache invalidate all by PoU */ @@ -195,12 +223,12 @@ static inline void write_csselr(uint32_t val) static inline unsigned int read_sctlr(void) { unsigned int val; - asm volatile ("mrc p15, 0, %0, c1, c0, 0" : "=r" (val) : : "cc"); + asm volatile ("mrc p15, 0, %0, c1, c0, 0" : "=r" (val)); return val; } /* write system control register (SCTLR) */ -static inline void write_sctlr(unsigned int val) +static inline void write_sctlr(uint32_t val) { asm volatile ("mcr p15, 0, %0, c1, c0, 0" : : "r" (val) : "cc"); isb(); @@ -210,22 +238,48 @@ static inline void write_sctlr(unsigned int val) * Cache maintenance API */ -/* invalidate all TLBs */ -void tlb_invalidate_all(void); - -/* clean and invalidate entire dcache on current level (given by CCSELR) */ +/* dcache clean and invalidate all (on current level given by CCSELR) */ void dcache_clean_invalidate_all(void); -/* invalidate entire dcache on current level (given by CCSELR) */ -void dcache_invalidate_all(void); +/* dcache clean by modified virtual address to PoC */ +void dcache_clean_by_mva(unsigned long addr, unsigned long len); -/* invalidate and clean dcache by machine virtual address to PoC */ +/* dcache clean and invalidate by modified virtual address to PoC */ void dcache_clean_invalidate_by_mva(unsigned long addr, unsigned long len); -/* invalidate entire icache on current level (given by CSSELR) */ +/* dcache invalidate all (on current level given by CCSELR) */ +void dcache_invalidate_all(void); + +/* dcache and MMU disable */ +void dcache_mmu_disable(void); + +/* dcache and MMU enable */ +void dcache_mmu_enable(void); + +/* icache invalidate all (on current level given by CSSELR) */ void icache_invalidate_all(void); -/* MMU setup by machine virtual address */ -void mmu_setup_by_mva(unsigned long start, unsigned long size); +/* tlb invalidate all */ +void tlb_invalidate_all(void); + +/* + * Generalized setup/init functions + */ + +/* invalidate all caches on ARMv7 */ +void armv7_invalidate_caches(void); + +/* mmu initialization (set page table address, set permissions, etc) */ +void mmu_init(void); + +enum dcache_policy { + DCACHE_OFF, + DCACHE_WRITEBACK, + DCACHE_WRITETHROUGH, +}; + +/* mmu range configuration (set dcache policy) */ +void mmu_config_range(unsigned long start_mb, unsigned long size_mb, + enum dcache_policy policy); #endif /* ARMV7_CACHE_H */