[U-Boot] [PATCH 2/9] arm64: Make full va map code more dynamic
Alexander Graf
agraf at suse.de
Wed Feb 24 11:55:39 CET 2016
On 23.02.16 14:17, Simon Glass wrote:
> Hi Alex,
>
> On 21 February 2016 at 18:57, Alexander Graf <agraf at suse.de> wrote:
>> The idea to generate our pages tables from an array of memory ranges
>> is very sound. However, instead of hard coding the code to create up
>> to 2 levels of 64k granule page tables, we really should just create
>> normal 4k page tables that allow us to set caching attributes on 2M
>> or 4k level later on.
>>
>> So this patch moves the full_va mapping code to 4k page size and
>> makes it fully flexible to dynamically create as many levels as
>> necessary for a map (including dynamic 1G/2M pages). It also adds
>> support to dynamically split a large map into smaller ones when
>> some code wants to set dcache attributes.
>>
>> With all this in place, there is very little reason to create your
>> own page tables in board specific files.
>>
>> Signed-off-by: Alexander Graf <agraf at suse.de>
>> ---
>> arch/arm/cpu/armv8/cache_v8.c | 346 +++++++++++++++++++++++++++++++------
>> arch/arm/include/asm/armv8/mmu.h | 68 ++++----
>> arch/arm/include/asm/global_data.h | 4 +-
>> arch/arm/include/asm/system.h | 3 +-
>> include/configs/thunderx_88xx.h | 14 +-
>> 5 files changed, 332 insertions(+), 103 deletions(-)
>>
>
> Should the change to the thunderx file go in a separate patch?
We're changing semantics for some defines from "this define acts in
L1/L2 page table entries" to "this define is for level/block type PTEs".
So it's tied to this patch :).
>
>> diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c
>> index 9229532..4369a83 100644
>> --- a/arch/arm/cpu/armv8/cache_v8.c
>> +++ b/arch/arm/cpu/armv8/cache_v8.c
>> @@ -2,6 +2,9 @@
>> * (C) Copyright 2013
>> * David Feng <fenghua at phytium.com.cn>
>> *
>> + * (C) Copyright 2016
>> + * Alexander Graf <agraf at suse.de>
>> + *
>> * SPDX-License-Identifier: GPL-2.0+
>> */
>>
>> @@ -9,35 +12,40 @@
>> #include <asm/system.h>
>> #include <asm/armv8/mmu.h>
>>
>> -DECLARE_GLOBAL_DATA_PTR;
>> -
>> -#ifndef CONFIG_SYS_DCACHE_OFF
>> +/* #define DEBUG_MMU */
>>
>> -#ifdef CONFIG_SYS_FULL_VA
>> -static void set_ptl1_entry(u64 index, u64 ptl2_entry)
>> -{
>> - u64 *pgd = (u64 *)gd->arch.tlb_addr;
>> - u64 value;
>> +#ifdef DEBUG_MMU
>> +#define DPRINTF(a, ...) printf("%s:%d: " a, __func__, __LINE__, __VA_ARGS__)
>> +#else
>> +#define DPRINTF(a, ...) do { } while(0)
>> +#endif
>
> Can you use the normal DEBUG and debug()?
Uh, I guess so, yeah.
>
>>
>> - value = ptl2_entry | PTL1_TYPE_TABLE;
>> - pgd[index] = value;
>> -}
>> +DECLARE_GLOBAL_DATA_PTR;
>>
>> -static void set_ptl2_block(u64 ptl1, u64 bfn, u64 address, u64 memory_attrs)
>> -{
>> - u64 *pmd = (u64 *)ptl1;
>> - u64 value;
>> +#ifndef CONFIG_SYS_DCACHE_OFF
>>
>> - value = address | PTL2_TYPE_BLOCK | PTL2_BLOCK_AF;
>> - value |= memory_attrs;
>> - pmd[bfn] = value;
>> -}
>> +/*
>> + * With 4k page granule, a virtual address is split into 4 lookup parts
>> + * spanning 9 bits each:
>> + *
>> + * _______________________________________________
>> + * | | | | | | |
>> + * | 0 | Lv0 | Lv1 | Lv2 | Lv3 | off |
>> + * |_______|_______|_______|_______|_______|_______|
>> + * 63-48 47-39 38-30 29-21 20-12 11-00
>> + *
>> + * mask page size
>> + *
>> + * Lv0: FF8000000000 --
>> + * Lv1: 7FC0000000 1G
>> + * Lv2: 3FE00000 2M
>> + * Lv3: 1FF000 4K
>> + * off: FFF
>> + */
>>
>> +#ifdef CONFIG_SYS_FULL_VA
>> static struct mm_region mem_map[] = CONFIG_SYS_MEM_MAP;
>
> I am not ken on the idea of using a big #define table on these boards.
> Is there not a device-tree binding for this that we can use? It is
> just a data table, We are moving to Kconfig and eventually want to
> drop the config files.
I'll move this into board files which then can do whatever they like
with it - take if from a #define in a header, populate it dynamically
from device tree, do something halfway in between, whatever fits your
needs best :).
Btw, if you want to use dt for it, you don't need to add any new
bindings at all. Simply take all of your device regs/ranges and memory
ranges and gobble them into the memory map.
>
>>
>> -#define PTL1_ENTRIES CONFIG_SYS_PTL1_ENTRIES
>> -#define PTL2_ENTRIES CONFIG_SYS_PTL2_ENTRIES
>> -
>> static u64 get_tcr(int el, u64 *pips, u64 *pva_bits)
>> {
>> u64 max_addr = 0;
>> @@ -79,8 +87,8 @@ static u64 get_tcr(int el, u64 *pips, u64 *pva_bits)
>> }
>>
>> /* PTWs cacheable, inner/outer WBWA and inner shareable */
>> - tcr |= TCR_TG0_64K | TCR_SHARED_INNER | TCR_ORGN_WBWA | TCR_IRGN_WBWA;
>> - tcr |= TCR_T0SZ(VA_BITS);
>> + tcr |= TCR_TG0_4K | TCR_SHARED_INNER | TCR_ORGN_WBWA | TCR_IRGN_WBWA;
>> + tcr |= TCR_T0SZ(va_bits);
>>
>> if (pips)
>> *pips = ips;
>> @@ -90,39 +98,196 @@ static u64 get_tcr(int el, u64 *pips, u64 *pva_bits)
>> return tcr;
>> }
>>
>> -static void setup_pgtables(void)
>> +#define MAX_PTE_ENTRIES 512
>> +
>> +static int pte_type(u64 *pte)
>> +{
>> + return *pte & PTE_TYPE_MASK;
>> +}
>> +
>> +/* Returns the LSB number for a PTE on level <level> */
>> +static int level2shift(int level)
>> {
>> - int l1_e, l2_e;
>> - unsigned long pmd = 0;
>> - unsigned long address;
>> -
>> - /* Setup the PMD pointers */
>> - for (l1_e = 0; l1_e < CONFIG_SYS_MEM_MAP_SIZE; l1_e++) {
>> - gd->arch.pmd_addr[l1_e] = gd->arch.tlb_addr +
>> - PTL1_ENTRIES * sizeof(u64);
>> - gd->arch.pmd_addr[l1_e] += PTL2_ENTRIES * sizeof(u64) * l1_e;
>> - gd->arch.pmd_addr[l1_e] = ALIGN(gd->arch.pmd_addr[l1_e],
>> - 0x10000UL);
>> + /* Page is 12 bits wide, every level translates 9 bits */
>> + return (12 + 9 * (3 - level));
>> +}
>> +
>> +static u64 *find_pte(u64 addr, int level)
>> +{
>> + int start_level = 0;
>> + u64 *pte;
>> + u64 idx;
>> + u64 va_bits;
>> + int i;
>> +
>> + DPRINTF("addr=%llx level=%d\n", addr, level);
>> +
>> + get_tcr(0, NULL, &va_bits);
>> + if (va_bits < 39)
>> + start_level = 1;
>> +
>> + if (level < start_level)
>> + return NULL;
>> +
>> + /* Walk through all page table levels to find our PTE */
>> + pte = (u64*)gd->arch.tlb_addr;
>> + for (i = start_level; i < 4; i++) {
>> + idx = (addr >> level2shift(i)) & 0x1FF;
>> + pte += idx;
>> + DPRINTF("idx=%llx PTE %p at level %d: %llx\n", idx, pte, i, *pte);
>> +
>> + /* Found it */
>> + if (i == level)
>> + return pte;
>> + /* PTE is no table (either invalid or block), can't traverse */
>> + if (pte_type(pte) != PTE_TYPE_TABLE)
>> + return NULL;
>> + /* Off to the next level */
>> + pte = (u64*)(*pte & 0x0000fffffffff000ULL);
>> }
>>
>> - /* Setup the page tables */
>> - for (l1_e = 0; l1_e < PTL1_ENTRIES; l1_e++) {
>> - if (mem_map[pmd].base ==
>> - (uintptr_t)l1_e << PTL2_BITS) {
>> - set_ptl1_entry(l1_e, gd->arch.pmd_addr[pmd]);
>> -
>> - for (l2_e = 0; l2_e < PTL2_ENTRIES; l2_e++) {
>> - address = mem_map[pmd].base
>> - + (uintptr_t)l2_e * BLOCK_SIZE;
>> - set_ptl2_block(gd->arch.pmd_addr[pmd], l2_e,
>> - address, mem_map[pmd].attrs);
>> - }
>> + /* Should never reach here */
>> + return NULL;
>> +}
>> +
>> +/* Creates a new full table (512 entries) and sets *pte to refer to it */
>> +static u64 *create_table(void)
>> +{
>> + u64 *new_table = (u64*)gd->arch.tlb_fillptr;
>> + u64 pt_len = MAX_PTE_ENTRIES * sizeof(u64);
>> +
>> + /* Allocate MAX_PTE_ENTRIES pte entries */
>> + gd->arch.tlb_fillptr += pt_len;
>> +
>> + if (gd->arch.tlb_fillptr - gd->arch.tlb_addr > gd->arch.tlb_size)
>> + panic("Insufficient RAM for page table: 0x%lx > 0x%lx",
>> + gd->arch.tlb_fillptr - gd->arch.tlb_addr,
>> + gd->arch.tlb_size);
>
> For each of these panic() calls can you please add a comment as to
> what the user should do? It needs to be very clear what action should
> be taken to resolve the problem.
Good idea. The only case where I can't think of a good text is at
if (pte_type(pte) != PTE_TYPE_TABLE)
panic("PTE %p (%llx) for addr=%llx should be a table",
pte, *pte, start);
because this really is more of an assert(). We should never reach it.
Thanks a bunch for the reviews,
Alex
More information about the U-Boot
mailing list