Permalink
Fetching contributors…
Cannot retrieve contributors at this time
251 lines (225 sloc) 7.7 KB
/**
* Copyright (c) 2012 Anup Patel.
* All rights reserved.
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* @file mmu_lpae_entry_ttbl.c
* @author Anup Patel (anup@brainfault.org)
* @author Jean-Christophe Dubois (jcd@tribudubois.net)
* @brief Initial translation table setup at reset time
*/
#include <vmm_types.h>
#include <cpu_mmu_lpae.h>
#include <mmu_lpae.h>
struct mmu_lpae_entry_ctrl {
u32 ttbl_count;
int *ttbl_tree;
u64 *next_ttbl;
virtual_addr_t ttbl_base;
};
extern u8 def_ttbl[];
extern int def_ttbl_tree[];
#ifdef CONFIG_DEFTERM_EARLY_PRINT
extern u8 defterm_early_base[];
#endif
void __attribute__ ((section(".entry")))
__setup_initial_ttbl(struct mmu_lpae_entry_ctrl *lpae_entry,
virtual_addr_t map_start, virtual_addr_t map_end,
virtual_addr_t pa_start, u32 aindex, bool writeable)
{
u32 i, index;
u64 *ttbl;
virtual_addr_t page_addr;
/* align start addresses */
map_start &= TTBL_L3_MAP_MASK;
pa_start &= TTBL_L3_MAP_MASK;
page_addr = map_start;
while (page_addr < map_end) {
/* Setup level1 table */
ttbl = (u64 *) lpae_entry->ttbl_base;
index = (page_addr & TTBL_L1_INDEX_MASK) >> TTBL_L1_INDEX_SHIFT;
if (ttbl[index] & TTBL_VALID_MASK) {
/* Find level2 table */
ttbl =
(u64 *) (unsigned long)(ttbl[index] &
TTBL_OUTADDR_MASK);
} else {
/* Allocate new level2 table */
if (lpae_entry->ttbl_count == TTBL_INITIAL_TABLE_COUNT) {
while (1) ; /* No initial table available */
}
for (i = 0; i < TTBL_TABLE_ENTCNT; i++) {
cpu_mmu_clean_invalidate(
&lpae_entry->next_ttbl[i]);
lpae_entry->next_ttbl[i] = 0x0ULL;
}
lpae_entry->ttbl_tree[lpae_entry->ttbl_count] =
((virtual_addr_t) ttbl -
lpae_entry->ttbl_base) >> TTBL_TABLE_SIZE_SHIFT;
lpae_entry->ttbl_count++;
ttbl[index] |=
(((virtual_addr_t) lpae_entry->next_ttbl) &
TTBL_OUTADDR_MASK);
ttbl[index] |= (TTBL_TABLE_MASK | TTBL_VALID_MASK);
ttbl = lpae_entry->next_ttbl;
lpae_entry->next_ttbl += TTBL_TABLE_ENTCNT;
}
/* Setup level2 table */
index = (page_addr & TTBL_L2_INDEX_MASK) >> TTBL_L2_INDEX_SHIFT;
if (ttbl[index] & TTBL_VALID_MASK) {
/* Find level3 table */
ttbl =
(u64 *) (unsigned long)(ttbl[index] &
TTBL_OUTADDR_MASK);
} else {
/* Allocate new level3 table */
if (lpae_entry->ttbl_count == TTBL_INITIAL_TABLE_COUNT) {
while (1) ; /* No initial table available */
}
for (i = 0; i < TTBL_TABLE_ENTCNT; i++) {
cpu_mmu_clean_invalidate(
&lpae_entry->next_ttbl[i]);
lpae_entry->next_ttbl[i] = 0x0ULL;
}
lpae_entry->ttbl_tree[lpae_entry->ttbl_count] =
((virtual_addr_t) ttbl -
lpae_entry->ttbl_base) >> TTBL_TABLE_SIZE_SHIFT;
lpae_entry->ttbl_count++;
ttbl[index] |=
(((virtual_addr_t) lpae_entry->next_ttbl) &
TTBL_OUTADDR_MASK);
ttbl[index] |= (TTBL_TABLE_MASK | TTBL_VALID_MASK);
ttbl = lpae_entry->next_ttbl;
lpae_entry->next_ttbl += TTBL_TABLE_ENTCNT;
}
/* Setup level3 table */
index = (page_addr & TTBL_L3_INDEX_MASK) >> TTBL_L3_INDEX_SHIFT;
if (!(ttbl[index] & TTBL_VALID_MASK)) {
/* Update level3 table */
ttbl[index] =
(((page_addr - map_start) + pa_start) &
TTBL_OUTADDR_MASK);
ttbl[index] |= TTBL_STAGE1_LOWER_AF_MASK;
ttbl[index] |= (writeable) ?
(TTBL_AP_SRW_U << TTBL_STAGE1_LOWER_AP_SHIFT) :
(TTBL_AP_SR_U << TTBL_STAGE1_LOWER_AP_SHIFT);
ttbl[index] |=
(aindex << TTBL_STAGE1_LOWER_AINDEX_SHIFT)
& TTBL_STAGE1_LOWER_AINDEX_MASK;
ttbl[index] |= TTBL_STAGE1_LOWER_NS_MASK;
ttbl[index] |= (TTBL_SH_INNER_SHAREABLE
<< TTBL_STAGE1_LOWER_SH_SHIFT);
ttbl[index] |= (TTBL_TABLE_MASK | TTBL_VALID_MASK);
}
/* Point to next page */
page_addr += TTBL_L3_BLOCK_SIZE;
}
}
/* Note: This functions must be called with MMU disabled from
* primary CPU only.
* Note: This functions cannot refer to any global variable &
* functions to ensure that it can execute from anywhere.
*/
#define to_load_pa(va) ({ \
virtual_addr_t _tva = (va); \
if (exec_start <= _tva && _tva < exec_end) { \
_tva = _tva - exec_start + load_start; \
} \
_tva; \
})
#define to_exec_va(va) ({ \
virtual_addr_t _tva = (va); \
if (load_start <= _tva && _tva < load_end) { \
_tva = _tva - load_start + exec_start; \
} \
_tva; \
})
#define SECTION_START(SECTION) _ ## SECTION ## _start
#define SECTION_END(SECTION) _ ## SECTION ## _end
#define SECTION_ADDR_START(SECTION) (virtual_addr_t)&SECTION_START(SECTION)
#define SECTION_ADDR_END(SECTION) (virtual_addr_t)&SECTION_END(SECTION)
#define DECLARE_SECTION(SECTION) \
extern virtual_addr_t SECTION_START(SECTION); \
extern virtual_addr_t SECTION_END(SECTION)
DECLARE_SECTION(text);
DECLARE_SECTION(cpuinit);
DECLARE_SECTION(spinlock);
DECLARE_SECTION(init);
DECLARE_SECTION(rodata);
#define SETUP_RO_SECTION(ENTRY, SECTION) \
__setup_initial_ttbl(&(ENTRY), \
to_exec_va(SECTION_ADDR_START(SECTION)), \
to_exec_va(SECTION_ADDR_END(SECTION)), \
to_load_pa(SECTION_ADDR_START(SECTION)), \
AINDEX_NORMAL_WB, \
FALSE)
void __attribute__ ((section(".entry")))
_setup_initial_ttbl(virtual_addr_t load_start, virtual_addr_t load_end,
virtual_addr_t exec_start, virtual_addr_t exec_end)
{
u32 i;
#ifdef CONFIG_DEFTERM_EARLY_PRINT
virtual_addr_t defterm_early_va;
#endif
struct mmu_lpae_entry_ctrl lpae_entry = { 0, NULL, NULL, 0 };
/* Init ttbl_base, ttbl_tree, and next_ttbl */
lpae_entry.ttbl_tree =
(int *)to_load_pa((virtual_addr_t)&def_ttbl_tree);
for (i = 0; i < TTBL_INITIAL_TABLE_COUNT; i++) {
cpu_mmu_clean_invalidate(&lpae_entry.ttbl_tree[i]);
lpae_entry.ttbl_tree[i] = -1;
}
lpae_entry.ttbl_base = to_load_pa((virtual_addr_t)&def_ttbl);
lpae_entry.next_ttbl = (u64 *)lpae_entry.ttbl_base;
/* Init first ttbl */
for (i = 0; i < TTBL_TABLE_ENTCNT; i++) {
cpu_mmu_clean_invalidate(&lpae_entry.next_ttbl[i]);
lpae_entry.next_ttbl[i] = 0x0ULL;
}
lpae_entry.ttbl_count++;
lpae_entry.next_ttbl += TTBL_TABLE_ENTCNT;
#ifdef CONFIG_DEFTERM_EARLY_PRINT
/* Map UART for early defterm
* Note: This is for early debug purpose
*/
defterm_early_va = to_exec_va((virtual_addr_t)&defterm_early_base);
__setup_initial_ttbl(&lpae_entry,
defterm_early_va,
defterm_early_va + TTBL_L3_BLOCK_SIZE,
(virtual_addr_t)CONFIG_DEFTERM_EARLY_BASE_PA,
AINDEX_DEVICE_nGnRE, TRUE);
#endif
/* Map physical = logical
* Note: This mapping is using at boot time only
*/
__setup_initial_ttbl(&lpae_entry, load_start, load_end, load_start,
AINDEX_NORMAL_WB, TRUE);
/* Map to logical addresses which are
* covered by read-only linker sections
* Note: This mapping is used at runtime
*/
SETUP_RO_SECTION(lpae_entry, text);
SETUP_RO_SECTION(lpae_entry, init);
SETUP_RO_SECTION(lpae_entry, cpuinit);
SETUP_RO_SECTION(lpae_entry, spinlock);
SETUP_RO_SECTION(lpae_entry, rodata);
/* Map rest of logical addresses which are
* not covered by read-only linker sections
* Note: This mapping is used at runtime
*/
__setup_initial_ttbl(&lpae_entry, exec_start, exec_end, load_start,
AINDEX_NORMAL_WB, TRUE);
}