From f9be756b559ccc567e5412c85b5ded98f19617e7 Mon Sep 17 00:00:00 2001 From: David Hendricks Date: Thu, 21 Mar 2013 21:58:50 -0700 Subject: armv7: add new dcache and MMU setup functions This adds new MMU setup code. Most notably, this version uses cbmem_add() to determine the translation table base address, which in turn is necessary to ensure payloads which wipe memory can tell which regions to wipe out. TODOs: - Finish cleaning up references to old cache/MMU stuff - Add L2 setup (from exynos_cache.c) - Set up ranges dynamically rather than in ramstage's main(). Change-Id: Iba5295a801e8058a3694e4ec5b94bbe9a69d3ee6 Signed-off-by: David Hendricks Reviewed-on: http://review.coreboot.org/2877 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich --- src/arch/armv7/bootblock_simple.c | 6 +- src/arch/armv7/include/arch/cache.h | 46 ++++- src/arch/armv7/include/system.h | 83 --------- src/arch/armv7/lib/Makefile.inc | 7 +- src/arch/armv7/lib/cache-cp15.c | 221 ----------------------- src/arch/armv7/lib/cache.c | 29 ++- src/arch/armv7/lib/cache_v7.c | 339 ------------------------------------ src/arch/armv7/lib/mmu.c | 132 ++++++++++++++ 8 files changed, 203 insertions(+), 660 deletions(-) delete mode 100644 src/arch/armv7/lib/cache-cp15.c delete mode 100644 src/arch/armv7/lib/cache_v7.c create mode 100644 src/arch/armv7/lib/mmu.c (limited to 'src/arch') diff --git a/src/arch/armv7/bootblock_simple.c b/src/arch/armv7/bootblock_simple.c index ad25b41316..f6134f75fb 100644 --- a/src/arch/armv7/bootblock_simple.c +++ b/src/arch/armv7/bootblock_simple.c @@ -53,12 +53,14 @@ void main(void) armv7_invalidate_caches(); /* - * Re-enable caches and branch prediction. MMU will be set up later. + * Re-enable icache and branch prediction. MMU and dcache will be + * set up later. + * * Note: If booting from USB, we need to disable branch prediction * before copying from USB into RAM (FIXME: why?) */ sctlr = read_sctlr(); - sctlr |= SCTLR_C | SCTLR_Z | SCTLR_I; + sctlr |= SCTLR_Z | SCTLR_I; write_sctlr(sctlr); if (boot_cpu()) { diff --git a/src/arch/armv7/include/arch/cache.h b/src/arch/armv7/include/arch/cache.h index c00325629a..92c0f2be0d 100644 --- a/src/arch/armv7/include/arch/cache.h +++ b/src/arch/armv7/include/arch/cache.h @@ -108,6 +108,32 @@ static inline void tlbiall(void) asm volatile ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0)); } +/* 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"); +} + /* * Low-level cache maintenance operations */ @@ -224,6 +250,12 @@ void dcache_clean_invalidate_by_mva(unsigned long addr, unsigned long len); /* 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); @@ -237,7 +269,17 @@ void tlb_invalidate_all(void); /* invalidate all caches on ARMv7 */ void armv7_invalidate_caches(void); -/* MMU setup by modified virtual address */ -void mmu_setup_by_mva(unsigned long start, unsigned long size); +/* 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 */ diff --git a/src/arch/armv7/include/system.h b/src/arch/armv7/include/system.h index eda0bc1806..0643852f76 100644 --- a/src/arch/armv7/include/system.h +++ b/src/arch/armv7/include/system.h @@ -2,37 +2,6 @@ #ifndef SYSTEM_H_ #define SYSTEM_H_ -/* - * CR1 bits (CP#15 CR1) - */ -#define CR_M (1 << 0) /* MMU enable */ -#define CR_A (1 << 1) /* Alignment abort enable */ -#define CR_C (1 << 2) /* Dcache enable */ -#define CR_W (1 << 3) /* Write buffer enable */ -#define CR_P (1 << 4) /* 32-bit exception handler */ -#define CR_D (1 << 5) /* 32-bit data address range */ -#define CR_L (1 << 6) /* Implementation defined */ -#define CR_B (1 << 7) /* Big endian */ -#define CR_S (1 << 8) /* System MMU protection */ -#define CR_R (1 << 9) /* ROM MMU protection */ -#define CR_F (1 << 10) /* Implementation defined */ -#define CR_Z (1 << 11) /* Implementation defined */ -#define CR_I (1 << 12) /* Icache enable */ -#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */ -#define CR_RR (1 << 14) /* Round Robin cache replacement */ -#define CR_L4 (1 << 15) /* LDR pc can set T bit */ -#define CR_DT (1 << 16) -#define CR_IT (1 << 18) -#define CR_ST (1 << 19) -#define CR_FI (1 << 21) /* Fast interrupt (lower latency mode) */ -#define CR_U (1 << 22) /* Unaligned access operation */ -#define CR_XP (1 << 23) /* Extended page tables */ -#define CR_VE (1 << 24) /* Vectored interrupts */ -#define CR_EE (1 << 25) /* Exception (Big) Endian */ -#define CR_TRE (1 << 28) /* TEX remap enable */ -#define CR_AFE (1 << 29) /* Access flag enable */ -#define CR_TE (1 << 30) /* Thumb exception enable */ - /* * This is used to ensure the compiler did actually allocate the register we * asked it for some inline assembly sequences. Apparently we can't trust @@ -48,58 +17,6 @@ #define arch_align_stack(x) (x) #ifndef __ASSEMBLER__ -#include /* for isb() */ -static inline unsigned int get_cr(void) -{ - unsigned int val; - asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc"); - return val; -} - -static inline void set_cr(unsigned int val) -{ - asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" - : : "r" (val) : "cc"); - isb(); -} - -/* options available for data cache on each page */ -enum dcache_option { - DCACHE_OFF, - DCACHE_WRITETHROUGH, - DCACHE_WRITEBACK, -}; - -/* Size of an MMU section */ -enum { - MMU_SECTION_SHIFT = 20, - MMU_SECTION_SIZE = 1 << MMU_SECTION_SHIFT, -}; - -/** - * Change the cache settings for a region. - * - * \param start start address of memory region to change - * \param size size of memory region to change - * \param option dcache option to select - */ -void mmu_set_region_dcache(unsigned long start, int size, - enum dcache_option option); - -/** - * Register an update to the page tables, and flush the TLB - * - * \param start start address of update in page table - * \param stop stop address of update in page table - */ -void mmu_page_table_flush(unsigned long start, unsigned long stop); - -void mmu_setup(unsigned long start, unsigned long size); - -void v7_inval_tlb(void); - -void arm_init_before_mmu(void); - /* * FIXME: sdelay originally came from arch/arm/cpu/armv7/exynos5/setup.h in * u-boot but does not seem specific to exynos5... diff --git a/src/arch/armv7/lib/Makefile.inc b/src/arch/armv7/lib/Makefile.inc index c248b9ea93..56a1bb72aa 100644 --- a/src/arch/armv7/lib/Makefile.inc +++ b/src/arch/armv7/lib/Makefile.inc @@ -3,12 +3,8 @@ bootblock-y += syslib.c bootblock-$(CONFIG_EARLY_CONSOLE) += early_console.c bootblock-y += cache.c -bootblock-y += cache_v7.c -bootblock-y += cache-cp15.c romstage-y += cache.c -romstage-y += cache_v7.c -romstage-y += cache-cp15.c romstage-y += div0.c romstage-y += syslib.c romstage-$(CONFIG_EARLY_CONSOLE) += early_console.c @@ -19,8 +15,7 @@ ramstage-y += div0.c #ramstage-y += memset.S ramstage-y += syslib.c ramstage-y += cache.c -ramstage-y += cache_v7.c -ramstage-y += cache-cp15.c +ramstage-y += mmu.c #FIXME(dhendrix): should this be a config option? romstage-y += eabi_compat.c diff --git a/src/arch/armv7/lib/cache-cp15.c b/src/arch/armv7/lib/cache-cp15.c deleted file mode 100644 index 32f3c79932..0000000000 --- a/src/arch/armv7/lib/cache-cp15.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * (C) Copyright 2002 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#include -#include -#include - -static unsigned int tlb_addr; - -static void cp_delay (void) -{ - volatile int i; - - /* copro seems to need some delay between reading and writing */ - for (i = 0; i < 100; i++) - nop(); - asm volatile("" : : : "memory"); -} - -static void set_section_dcache(int section, enum dcache_option option) -{ - u32 value = section << MMU_SECTION_SHIFT | (3 << 10); - u32 *page_table; - unsigned int tlb_size = 4096 * 4; - - /* - * FIXME(dhendrix): This calculation is from arch/arm/lib/board.c - * in u-boot. We may need to subtract more due to logging. - * FIXME(rminnich) - * The cast avoids an incorrect overflow diagnostic. - * We really need to start adding ULL to constants that are - * intrinsically unsigned. - */ - tlb_addr = ((u32)CONFIG_SYS_SDRAM_BASE + (CONFIG_DRAM_SIZE_MB << 20UL)); - tlb_addr -= tlb_size; - /* round down to next 64KB limit */ - tlb_addr &= ~(0x10000 - 1); - page_table = (u32 *)tlb_addr; - - switch (option) { - case DCACHE_WRITETHROUGH: - value |= 0x1a; - break; - - case DCACHE_WRITEBACK: - value |= 0x1e; - break; - - case DCACHE_OFF: - value |= 0x12; - break; - } - - page_table[section] = value; -} - -#if 0 -void __mmu_page_table_flush(unsigned long start, unsigned long stop) -{ - debug("%s: Warning: not implemented\n", __func__); -} -#endif - -void mmu_set_region_dcache(unsigned long start, int size, enum dcache_option option) -{ - u32 *page_table = &tlb_addr; - u32 upto, end; - - end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT; - start = start >> MMU_SECTION_SHIFT; - debug("mmu_set_region_dcache start=%x, size=%x, option=%d\n", - start, size, option); - for (upto = start; upto < end; upto++) - set_section_dcache(upto, option); - mmu_page_table_flush((u32)&page_table[start], (u32)&page_table[end]); -} - -/** - * dram_bank_mmu_set - set up the data cache policy for a given dram bank - * - * @start: virtual address start of bank - * @size: size of bank (in bytes) - */ -static inline void dram_bank_mmu_setup(unsigned long start, unsigned long size) -{ - int i; - - debug("%s: bank: %d\n", __func__, bank); - for (i = start >> 20; i < (start + size) >> 20; i++) { -#if defined(CONFIG_ARM_DCACHE_POLICY_WRITEBACK) - set_section_dcache(i, DCACHE_WRITEBACK); -#elif defined(CONFIG_ARM_DCACHE_POLICY_WRITETHROUGH) - set_section_dcache(i, DCACHE_WRITETHROUGH); -#else -#error "Must define dcache policy." -#endif - } -} - -/* to activate the MMU we need to set up virtual memory: use 1M areas */ -inline void mmu_setup(unsigned long start, unsigned long size_mb) -{ - int i; - u32 reg; - -// arm_init_before_mmu(); - /* Set up an identity-mapping for all 4GB, rw for everyone */ - for (i = 0; i < 4096; i++) - set_section_dcache(i, DCACHE_OFF); - - dram_bank_mmu_setup(start, size_mb << 20); - - /* Copy the page table address to cp15 */ - asm volatile("mcr p15, 0, %0, c2, c0, 0" - : : "r" (tlb_addr) : "memory"); - /* Set the access control to all-supervisor */ - asm volatile("mcr p15, 0, %0, c3, c0, 0" - : : "r" (~0)); - /* and enable the mmu */ - reg = get_cr(); /* get control reg. */ - cp_delay(); - set_cr(reg | CR_M); -} - -static int mmu_enabled(void) -{ - return get_cr() & CR_M; -} - -/* cache_bit must be either CR_I or CR_C */ -static void cache_enable(unsigned long start, unsigned long size, uint32_t cache_bit) -{ - uint32_t reg; - - /* The data cache is not active unless the mmu is enabled too */ - if ((cache_bit == CR_C) && !mmu_enabled()) - mmu_setup(start, size); - reg = get_cr(); /* get control reg. */ - cp_delay(); - set_cr(reg | cache_bit); -} - -/* - * Big hack warning! - * - * Devs like to compile with -O0 to get a nice debugging illusion. But this - * function does not survive that since -O0 causes the compiler to read the - * PC back from the stack after the dcache flush. Might it be possible to fix - * this by flushing the write buffer? - */ -static void cache_disable(uint32_t cache_bit) __attribute__ ((optimize(2))); - -/* cache_bit must be either CR_I or CR_C */ -static void cache_disable(uint32_t cache_bit) -{ - uint32_t reg; - - if (cache_bit == CR_C) { - /* if cache isn;t enabled no need to disable */ - reg = get_cr(); - if ((reg & CR_C) != CR_C) - return; - /* if disabling data cache, disable mmu too */ - cache_bit |= CR_M; - } - reg = get_cr(); - cp_delay(); - if (cache_bit == (CR_C | CR_M)) - flush_dcache_all(); - set_cr(reg & ~cache_bit); -} - -void icache_enable(unsigned long start, unsigned long size) -{ - cache_enable(start, size, CR_I); -} - -void icache_disable(void) -{ - cache_disable(CR_I); -} - -int icache_status(void) -{ - return (get_cr() & CR_I) != 0; -} - -void dcache_enable(unsigned long start, unsigned long size) -{ - cache_enable(start, size, CR_C); -} - -void dcache_disable(void) -{ - cache_disable(CR_C); -} - -int dcache_status(void) -{ - return (get_cr() & CR_C) != 0; -} diff --git a/src/arch/armv7/lib/cache.c b/src/arch/armv7/lib/cache.c index 63e406c03f..2686db7cf9 100644 --- a/src/arch/armv7/lib/cache.c +++ b/src/arch/armv7/lib/cache.c @@ -204,6 +204,28 @@ void dcache_clean_invalidate_by_mva(unsigned long addr, unsigned long len) dcache_op_mva(addr, len, OP_DCCIMVAC); } + +void dcache_mmu_disable(void) +{ + uint32_t sctlr; + + sctlr = read_sctlr(); + dcache_clean_invalidate_all(); + 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; @@ -252,10 +274,3 @@ void armv7_invalidate_caches(void) /* Invalidate TLB */ tlb_invalidate_all(); } - -/* 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) -{ - mmu_setup(start, size); -} diff --git a/src/arch/armv7/lib/cache_v7.c b/src/arch/armv7/lib/cache_v7.c deleted file mode 100644 index 1764351115..0000000000 --- a/src/arch/armv7/lib/cache_v7.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * (C) Copyright 2010 - * Texas Instruments, - * Aneesh V - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ -#include -#include -#include -#include -#include - -#define ARMV7_DCACHE_INVAL_ALL 1 -#define ARMV7_DCACHE_CLEAN_INVAL_ALL 2 -#define ARMV7_DCACHE_INVAL_RANGE 3 -#define ARMV7_DCACHE_CLEAN_INVAL_RANGE 4 - -/* - * Write the level and type you want to Cache Size Selection Register(CSSELR) - * to get size details from Current Cache Size ID Register(CCSIDR) - */ -static void set_csselr(u32 level, u32 type) -{ u32 csselr = level << 1 | type; - - /* Write to Cache Size Selection Register(CSSELR) */ - asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr)); -} - -static u32 get_ccsidr(void) -{ - u32 ccsidr; - - /* Read current CP15 Cache Size ID Register */ - asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr)); - return ccsidr; -} - -static u32 get_clidr(void) -{ - u32 clidr; - - /* Read current CP15 Cache Level ID Register */ - asm volatile ("mrc p15,1,%0,c0,c0,1" : "=r" (clidr)); - return clidr; -} - -static void v7_inval_dcache_level_setway(u32 level, u32 num_sets, - u32 num_ways, u32 way_shift, - u32 log2_line_len) -{ - int way, set, setway; - - /* - * For optimal assembly code: - * a. count down - * b. have bigger loop inside - */ - for (way = num_ways - 1; way >= 0 ; way--) { - for (set = num_sets - 1; set >= 0; set--) { - setway = (level << 1) | (set << log2_line_len) | - (way << way_shift); - /* Invalidate data/unified cache line by set/way */ - asm volatile (" mcr p15, 0, %0, c7, c6, 2" - : : "r" (setway)); - } - } - /* DSB to make sure the operation is complete */ - CP15DSB; -} - -static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets, - u32 num_ways, u32 way_shift, - u32 log2_line_len) -{ - int way, set, setway; - - /* - * For optimal assembly code: - * a. count down - * b. have bigger loop inside - */ - for (way = num_ways - 1; way >= 0 ; way--) { - for (set = num_sets - 1; set >= 0; set--) { - setway = (level << 1) | (set << log2_line_len) | - (way << way_shift); - /* - * Clean & Invalidate data/unified - * cache line by set/way - */ - asm volatile (" mcr p15, 0, %0, c7, c14, 2" - : : "r" (setway)); - } - } - /* DSB to make sure the operation is complete */ - CP15DSB; -} - -static void v7_maint_dcache_level_setway(u32 level, u32 operation) -{ - u32 ccsidr; - u32 num_sets, num_ways, log2_line_len, log2_num_ways; - u32 way_shift; - - set_csselr(level, ARMV7_CSSELR_IND_DATA_UNIFIED); - - ccsidr = get_ccsidr(); - - log2_line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >> - CCSIDR_LINE_SIZE_OFFSET) + 2; - /* Converting from words to bytes */ - log2_line_len += 2; - - num_ways = ((ccsidr & CCSIDR_ASSOCIATIVITY_MASK) >> - CCSIDR_ASSOCIATIVITY_OFFSET) + 1; - num_sets = ((ccsidr & CCSIDR_NUM_SETS_MASK) >> - CCSIDR_NUM_SETS_OFFSET) + 1; - /* - * According to ARMv7 ARM number of sets and number of ways need - * not be a power of 2 - */ - log2_num_ways = log_2_n_round_up(num_ways); - - way_shift = (32 - log2_num_ways); - if (operation == ARMV7_DCACHE_INVAL_ALL) { - v7_inval_dcache_level_setway(level, num_sets, num_ways, - way_shift, log2_line_len); - } else if (operation == ARMV7_DCACHE_CLEAN_INVAL_ALL) { - v7_clean_inval_dcache_level_setway(level, num_sets, num_ways, - way_shift, log2_line_len); - } -} - -static void v7_maint_dcache_all(u32 operation) -{ - u32 level, cache_type, level_start_bit = 0; - - u32 clidr = get_clidr(); - - for (level = 0; level < 7; level++) { - cache_type = (clidr >> level_start_bit) & 0x7; - if ((cache_type == ARMV7_CLIDR_CTYPE_DATA_ONLY) || - (cache_type == ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA) || - (cache_type == ARMV7_CLIDR_CTYPE_UNIFIED)) - v7_maint_dcache_level_setway(level, operation); - level_start_bit += 3; - } -} - -static void v7_dcache_clean_inval_range(u32 start, - u32 stop, u32 line_len) -{ - u32 mva; - - /* Align start to cache line boundary */ - start &= ~(line_len - 1); - for (mva = start; mva < stop; mva = mva + line_len) { - /* DCCIMVAC - Clean & Invalidate data cache by MVA to PoC */ - asm volatile ("mcr p15, 0, %0, c7, c14, 1" : : "r" (mva)); - } -} - -static void v7_dcache_inval_range(u32 start, u32 stop, u32 line_len) -{ - u32 mva; - - /* - * If start address is not aligned to cache-line do not - * invalidate the first cache-line - */ - if (start & (line_len - 1)) { - printk(BIOS_ERR, "%s - start address is not aligned - 0x%08x\n", - __func__, start); - /* move to next cache line */ - start = (start + line_len - 1) & ~(line_len - 1); - } - - /* - * If stop address is not aligned to cache-line do not - * invalidate the last cache-line - */ - if (stop & (line_len - 1)) { - printk(BIOS_ERR, "%s - stop address is not aligned - 0x%08x\n", - __func__, stop); - /* align to the beginning of this cache line */ - stop &= ~(line_len - 1); - } - - for (mva = start; mva < stop; mva = mva + line_len) { - /* DCIMVAC - Invalidate data cache by MVA to PoC */ - asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (mva)); - } -} - -static void v7_dcache_maint_range(u32 start, u32 stop, u32 range_op) -{ - u32 line_len = dcache_get_line_size(); - - switch (range_op) { - case ARMV7_DCACHE_CLEAN_INVAL_RANGE: - v7_dcache_clean_inval_range(start, stop, line_len); - break; - case ARMV7_DCACHE_INVAL_RANGE: - v7_dcache_inval_range(start, stop, line_len); - break; - } - - /* DSB to make sure the operation is complete */ - CP15DSB; -} - -/* Invalidate TLB */ -void v7_inval_tlb(void) -{ - /* Invalidate entire unified TLB */ - asm volatile ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0)); - /* Invalidate entire data TLB */ - asm volatile ("mcr p15, 0, %0, c8, c6, 0" : : "r" (0)); - /* Invalidate entire instruction TLB */ - asm volatile ("mcr p15, 0, %0, c8, c5, 0" : : "r" (0)); - /* Full system DSB - make sure that the invalidation is complete */ - CP15DSB; - /* Full system ISB - make sure the instruction stream sees it */ - CP15ISB; -} - -unsigned long dcache_get_line_size(void) -{ - u32 line_len, ccsidr; - - ccsidr = get_ccsidr(); - line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >> - CCSIDR_LINE_SIZE_OFFSET) + 2; - /* Converting from words to bytes */ - line_len += 2; - /* converting from log2(linelen) to linelen */ - line_len = 1 << line_len; - - return line_len; -} - -void invalidate_dcache_all(void) -{ - v7_maint_dcache_all(ARMV7_DCACHE_INVAL_ALL); - - v7_outer_cache_inval_all(); -} - -/* - * Performs a clean & invalidation of the entire data cache - * at all levels - */ -void flush_dcache_all(void) -{ - v7_maint_dcache_all(ARMV7_DCACHE_CLEAN_INVAL_ALL); - - v7_outer_cache_flush_all(); -} - -/* - * Invalidates range in all levels of D-cache/unified cache used: - * Affects the range [start, stop - 1] - */ -void invalidate_dcache_range(unsigned long start, unsigned long stop) -{ - - v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE); - - v7_outer_cache_inval_range(start, stop); -} - -/* - * Flush range(clean & invalidate) from all levels of D-cache/unified - * cache used: - * Affects the range [start, stop - 1] - */ -void flush_dcache_range(unsigned long start, unsigned long stop) -{ - v7_dcache_maint_range(start, stop, ARMV7_DCACHE_CLEAN_INVAL_RANGE); - - v7_outer_cache_flush_range(start, stop); -} - -void arm_init_before_mmu(void) -{ - v7_outer_cache_enable(); - invalidate_dcache_all(); - v7_inval_tlb(); -} - -void mmu_page_table_flush(unsigned long start, unsigned long stop) -{ - flush_dcache_range(start, stop); - v7_inval_tlb(); -} - -/* - * Flush range from all levels of d-cache/unified-cache used: - * Affects the range [start, start + size - 1] - */ -void flush_cache(unsigned long start, unsigned long size) -{ - flush_dcache_range(start, start + size); -} - -/* Invalidate entire I-cache and branch predictor array */ -void invalidate_icache_all(void) -{ - /* - * Invalidate all instruction caches to PoU. - * Also flushes branch target cache. - */ - asm volatile ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); - - /* Invalidate entire branch predictor array */ - asm volatile ("mcr p15, 0, %0, c7, c5, 6" : : "r" (0)); - - /* Full system DSB - make sure that the invalidation is complete */ - CP15DSB; - - /* ISB - make sure the instruction stream sees it */ - CP15ISB; -} diff --git a/src/arch/armv7/lib/mmu.c b/src/arch/armv7/lib/mmu.c new file mode 100644 index 0000000000..34a484f300 --- /dev/null +++ b/src/arch/armv7/lib/mmu.c @@ -0,0 +1,132 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * 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. + */ + +#include +#include + +#include +#include + +#include + +#define L1_TLB_ENTRIES 4096 /* 1 entry for each 1MB address space */ + +static uintptr_t ttb_addr; + +void mmu_config_range(unsigned long start_mb, unsigned long size_mb, + enum dcache_policy policy) +{ + unsigned int i; + uint32_t attr; + uint32_t *ttb_entry = (uint32_t *)ttb_addr; + const char *str = NULL; + + /* + * Section entry bits: + * 31:20 - section base address + * 18 - 0 to indicate normal section (versus supersection) + * 17 - nG, 0 to indicate page is global + * 16 - S, 0 for non-shareable (?) + * 15 - APX, 0 for full access + * 14:12 - TEX, 0b000 for outer and inner write-back + * 11:10 - AP, 0b11 for full access + * 9 - P, ? (FIXME: not described or possibly obsolete?) + * 8: 5 - Domain + * 4 - XN, 1 to set execute-never (and also avoid prefetches) + * 3 - C, 1 for cacheable + * 2 - B, 1 for bufferable + * 1: 0 - 0b10 to indicate section entry + */ + + switch(policy) { + case DCACHE_OFF: + /* XN set to avoid prefetches to uncached/unbuffered regions */ + attr = (0x3 << 10) | (1 << 4) | 0x2; + str = "off"; + break; + case DCACHE_WRITEBACK: + attr = (0x3 << 10) | (1 << 3) | (1 << 2) | 0x2; + str = "writeback"; + break; + case DCACHE_WRITETHROUGH: + attr = (0x3 << 10) | (1 << 3) | (1 << 2) | 0x2; + str = "writethrough"; + break; + default: + printk(BIOS_ERR, "unknown dcache policy: %02x\n", policy); + return; + } + + printk(BIOS_DEBUG, "Setting dcache policy: 0x%08lx:0x%08lx [%s]\n", + start_mb << 20, ((start_mb + size_mb) << 20) - 1, str); + + for (i = start_mb; i < start_mb + size_mb; i++) + ttb_entry[i] = (i << 20) | attr; +} + +void mmu_init(void) +{ + unsigned int ttb_size; + uint32_t ttbcr; + + /* + * For coreboot's purposes, we will create a simple L1 page table + * in RAM with 1MB section translation entries over the 4GB address + * space. + * (ref: section 10.2 and example 15-4 in Cortex-A series + * programmer's guide) + * + * FIXME: TLB needs to be aligned to 16KB, but cbmem_add() aligns to + * 512 bytes. So add double the space in cbmem and fix-up the pointer. + */ + ttb_size = L1_TLB_ENTRIES * sizeof(unsigned long); + ttb_addr = (uintptr_t)cbmem_add(CBMEM_ID_GDT, ttb_size * 2); + ttb_addr = ALIGN(ttb_addr + ttb_size, ttb_size); + printk(BIOS_DEBUG, "Translation table is @ 0x%08x\n", ttb_addr); + + /* + * Disable TTBR1 by setting TTBCR.N to 0b000, which means the TTBR0 + * table size is 16KB and has indices VA[31:20]. + * + * ref: Arch Ref. Manual for ARMv7-A, B3.5.4, + */ + ttbcr = read_ttbcr(); + ttbcr &= ~(0x3); + write_ttbcr(ttbcr); + + /* + * Translation table base 0 address is in bits 31:14-N, where N is given + * by bits 2:0 in TTBCR (which we set to 0). All lower bits in this + * register should be zero for coreboot. + */ + write_ttbr0(ttb_addr); + + /* disable domain-level checking of permissions */ + write_dacr(~0); +} -- cgit v1.2.3