/* * 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); }