diff --git a/MAINTAINERS b/MAINTAINERS index 9d447ea..32f97b2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -230,6 +230,13 @@ F: drivers/core/ F: include/dm/ F: test/dm/ +EFI PAYLOAD +M: Alexander Graf +S: Maintained +F: include/efi_loader.h +F: lib/efi_loader/ +F: cmd/bootefi.c + FLATTENED DEVICE TREE M: Simon Glass S: Maintained diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 8fa57ec..9af6c37 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -122,6 +122,10 @@ ifdef CONFIG_OF_EMBED OBJCOPYFLAGS += -j .dtb.init.rodata endif +ifdef CONFIG_EFI_LOADER +OBJCOPYFLAGS += -j .efi_runtime -j .efi_runtime_rel +endif + ifneq ($(CONFIG_IMX_CONFIG),) ifdef CONFIG_SPL ifndef CONFIG_SPL_BUILD diff --git a/arch/arm/cpu/armv8/cache.S b/arch/arm/cpu/armv8/cache.S index ab8c089..a9f4fec 100644 --- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -10,6 +10,7 @@ #include #include #include +#include #include /* @@ -160,3 +161,56 @@ ENTRY(__asm_flush_l3_cache) ret ENDPROC(__asm_flush_l3_cache) .weak __asm_flush_l3_cache + +/* + * void __asm_switch_ttbr(ulong new_ttbr) + * + * Safely switches to a new page table. + */ +ENTRY(__asm_switch_ttbr) + /* x2 = SCTLR (alive throghout the function) */ + switch_el x4, 3f, 2f, 1f +3: mrs x2, sctlr_el3 + b 0f +2: mrs x2, sctlr_el2 + b 0f +1: mrs x2, sctlr_el1 +0: + + /* Unset CR_M | CR_C | CR_I from SCTLR to disable all caches */ + movn x1, #(CR_M | CR_C | CR_I) + and x1, x2, x1 + switch_el x4, 3f, 2f, 1f +3: msr sctlr_el3, x1 + b 0f +2: msr sctlr_el2, x1 + b 0f +1: msr sctlr_el1, x1 +0: isb + + /* This call only clobbers x30 (lr) and x9 (unused) */ + mov x3, x30 + bl __asm_invalidate_tlb_all + + /* From here on we're running safely with caches disabled */ + + /* Set TTBR to our first argument */ + switch_el x4, 3f, 2f, 1f +3: msr ttbr0_el3, x0 + b 0f +2: msr ttbr0_el2, x0 + b 0f +1: msr ttbr0_el1, x0 +0: isb + + /* Restore original SCTLR and thus enable caches again */ + switch_el x4, 3f, 2f, 1f +3: msr sctlr_el3, x2 + b 0f +2: msr sctlr_el2, x2 + b 0f +1: msr sctlr_el1, x2 +0: isb + + ret x3 +ENDPROC(__asm_switch_ttbr) diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 71f0020..d1bd06b 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 * + * (C) Copyright 2016 + * Alexander Graf + * * SPDX-License-Identifier: GPL-2.0+ */ @@ -13,137 +16,388 @@ DECLARE_GLOBAL_DATA_PTR; #ifndef CONFIG_SYS_DCACHE_OFF -#ifdef CONFIG_SYS_FULL_VA -static void set_ptl1_entry(u64 index, u64 ptl2_entry) +/* + * 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 + */ + +static u64 get_tcr(int el, u64 *pips, u64 *pva_bits) { - u64 *pgd = (u64 *)gd->arch.tlb_addr; - u64 value; + u64 max_addr = 0; + u64 ips, va_bits; + u64 tcr; + int i; + + /* Find the largest address we need to support */ + for (i = 0; mem_map[i].size || mem_map[i].attrs; i++) + max_addr = max(max_addr, mem_map[i].base + mem_map[i].size); + + /* Calculate the maximum physical (and thus virtual) address */ + if (max_addr > (1ULL << 44)) { + ips = 5; + va_bits = 48; + } else if (max_addr > (1ULL << 42)) { + ips = 4; + va_bits = 44; + } else if (max_addr > (1ULL << 40)) { + ips = 3; + va_bits = 42; + } else if (max_addr > (1ULL << 36)) { + ips = 2; + va_bits = 40; + } else if (max_addr > (1ULL << 32)) { + ips = 1; + va_bits = 36; + } else { + ips = 0; + va_bits = 32; + } + + if (el == 1) { + tcr = TCR_EL1_RSVD | (ips << 32) | TCR_EPD1_DISABLE; + } else if (el == 2) { + tcr = TCR_EL2_RSVD | (ips << 16); + } else { + tcr = TCR_EL3_RSVD | (ips << 16); + } - value = ptl2_entry | PTL1_TYPE_TABLE; - pgd[index] = value; + /* PTWs cacheable, inner/outer WBWA and inner shareable */ + tcr |= TCR_TG0_4K | TCR_SHARED_INNER | TCR_ORGN_WBWA | TCR_IRGN_WBWA; + tcr |= TCR_T0SZ(va_bits); + + if (pips) + *pips = ips; + if (pva_bits) + *pva_bits = va_bits; + + return tcr; } -static void set_ptl2_block(u64 ptl1, u64 bfn, u64 address, u64 memory_attrs) +#define MAX_PTE_ENTRIES 512 + +static int pte_type(u64 *pte) { - u64 *pmd = (u64 *)ptl1; - u64 value; + return *pte & PTE_TYPE_MASK; +} - value = address | PTL2_TYPE_BLOCK | PTL2_BLOCK_AF; - value |= memory_attrs; - pmd[bfn] = value; +/* Returns the LSB number for a PTE on level */ +static int level2shift(int level) +{ + /* Page is 12 bits wide, every level translates 9 bits */ + return (12 + 9 * (3 - level)); } -static struct mm_region mem_map[] = CONFIG_SYS_MEM_MAP; +static u64 *find_pte(u64 addr, int level) +{ + int start_level = 0; + u64 *pte; + u64 idx; + u64 va_bits; + int i; + + debug("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; + debug("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); + } -#define PTL1_ENTRIES CONFIG_SYS_PTL1_ENTRIES -#define PTL2_ENTRIES CONFIG_SYS_PTL2_ENTRIES + /* Should never reach here */ + return NULL; +} -static void setup_pgtables(void) +/* Returns and creates a new full table (512 entries) */ +static u64 *create_table(void) { - 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); + 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. " + "Please increase the size in get_page_table_size()", + gd->arch.tlb_fillptr - gd->arch.tlb_addr, + gd->arch.tlb_size); + + /* Mark all entries as invalid */ + memset(new_table, 0, pt_len); + + return new_table; +} + +static void set_pte_table(u64 *pte, u64 *table) +{ + /* Point *pte to the new table */ + debug("Setting %p to addr=%p\n", pte, table); + *pte = PTE_TYPE_TABLE | (ulong)table; +} + +/* Add one mm_region map entry to the page tables */ +static void add_map(struct mm_region *map) +{ + u64 *pte; + u64 addr = map->base; + u64 size = map->size; + u64 attrs = map->attrs | PTE_TYPE_BLOCK | PTE_BLOCK_AF; + u64 blocksize; + int level; + u64 *new_table; + + while (size) { + pte = find_pte(addr, 0); + if (pte && (pte_type(pte) == PTE_TYPE_FAULT)) { + debug("Creating table for addr 0x%llx\n", addr); + new_table = create_table(); + set_pte_table(pte, new_table); + } + + for (level = 1; level < 4; level++) { + pte = find_pte(addr, level); + blocksize = 1ULL << level2shift(level); + debug("Checking if pte fits for addr=%llx size=%llx " + "blocksize=%llx\n", addr, size, blocksize); + if (size >= blocksize && !(addr & (blocksize - 1))) { + /* Page fits, create block PTE */ + debug("Setting PTE %p to block addr=%llx\n", + pte, addr); + *pte = addr | attrs; + addr += blocksize; + size -= blocksize; + break; + } else if ((pte_type(pte) == PTE_TYPE_FAULT)) { + /* Page doesn't fit, create subpages */ + debug("Creating subtable for addr 0x%llx " + "blksize=%llx\n", addr, blocksize); + new_table = create_table(); + set_pte_table(pte, new_table); + } + } + } +} + +/* Splits a block PTE into table with subpages spanning the old block */ +static void split_block(u64 *pte, int level) +{ + u64 old_pte = *pte; + u64 *new_table; + u64 i = 0; + /* level describes the parent level, we need the child ones */ + int levelshift = level2shift(level + 1); + + if (pte_type(pte) != PTE_TYPE_BLOCK) + panic("PTE %p (%llx) is not a block. Some driver code wants to " + "modify dcache settings for an range not covered in " + "mem_map.", pte, old_pte); + + new_table = create_table(); + debug("Splitting pte %p (%llx) into %p\n", pte, old_pte, new_table); + + for (i = 0; i < MAX_PTE_ENTRIES; i++) { + new_table[i] = old_pte | (i << levelshift); + + /* Level 3 block PTEs have the table type */ + if ((level + 1) == 3) + new_table[i] |= PTE_TYPE_TABLE; + + debug("Setting new_table[%lld] = %llx\n", i, new_table[i]); } - /* 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); + /* Set the new table into effect */ + set_pte_table(pte, new_table); +} + +enum pte_type { + PTE_INVAL, + PTE_BLOCK, + PTE_LEVEL, +}; + +/* + * This is a recursively called function to count the number of + * page tables we need to cover a particular PTE range. If you + * call this with level = -1 you basically get the full 48 bit + * coverage. + */ +static int count_required_pts(u64 addr, int level, u64 maxaddr) +{ + int levelshift = level2shift(level); + u64 levelsize = 1ULL << levelshift; + u64 levelmask = levelsize - 1; + u64 levelend = addr + levelsize; + int r = 0; + int i; + enum pte_type pte_type = PTE_INVAL; + + for (i = 0; mem_map[i].size || mem_map[i].attrs; i++) { + struct mm_region *map = &mem_map[i]; + u64 start = map->base; + u64 end = start + map->size; + + /* Check if the PTE would overlap with the map */ + if (max(addr, start) <= min(levelend, end)) { + start = max(addr, start); + end = min(levelend, end); + + /* We need a sub-pt for this level */ + if ((start & levelmask) || (end & levelmask)) { + pte_type = PTE_LEVEL; + break; } - pmd++; - } else { - set_ptl1_entry(l1_e, 0); + /* Lv0 can not do block PTEs, so do levels here too */ + if (level <= 0) { + pte_type = PTE_LEVEL; + break; + } + + /* PTE is active, but fits into a block */ + pte_type = PTE_BLOCK; } } + + /* + * Block PTEs at this level are already covered by the parent page + * table, so we only need to count sub page tables. + */ + if (pte_type == PTE_LEVEL) { + int sublevel = level + 1; + u64 sublevelsize = 1ULL << level2shift(sublevel); + + /* Account for the new sub page table ... */ + r = 1; + + /* ... and for all child page tables that one might have */ + for (i = 0; i < MAX_PTE_ENTRIES; i++) { + r += count_required_pts(addr, sublevel, maxaddr); + addr += sublevelsize; + + if (addr >= maxaddr) { + /* + * We reached the end of address space, no need + * to look any further. + */ + break; + } + } + } + + return r; } -#else +/* Returns the estimated required size of all page tables */ +u64 get_page_table_size(void) +{ + u64 one_pt = MAX_PTE_ENTRIES * sizeof(u64); + u64 size = 0; + u64 va_bits; + int start_level = 0; + + get_tcr(0, NULL, &va_bits); + if (va_bits < 39) + start_level = 1; + + /* Account for all page tables we would need to cover our memory map */ + size = one_pt * count_required_pts(0, start_level - 1, 1ULL << va_bits); + + /* + * We need to duplicate our page table once to have an emergency pt to + * resort to when splitting page tables later on + */ + size *= 2; + + /* + * We may need to split page tables later on if dcache settings change, + * so reserve up to 4 (random pick) page tables for that. + */ + size += one_pt * 4; + + return size; +} -inline void set_pgtable_section(u64 *page_table, u64 index, u64 section, - u64 memory_type, u64 attribute) +static void setup_pgtables(void) { - u64 value; + int i; - value = section | PMD_TYPE_SECT | PMD_SECT_AF; - value |= PMD_ATTRINDX(memory_type); - value |= attribute; - page_table[index] = value; + /* + * Allocate the first level we're on with invalidate entries. + * If the starting level is 0 (va_bits >= 39), then this is our + * Lv0 page table, otherwise it's the entry Lv1 page table. + */ + create_table(); + + /* Now add all MMU table entries one after another to the table */ + for (i = 0; mem_map[i].size || mem_map[i].attrs; i++) + add_map(&mem_map[i]); + + /* Create the same thing once more for our emergency page table */ + create_table(); } -inline void set_pgtable_table(u64 *page_table, u64 index, u64 *table_addr) +static void setup_all_pgtables(void) { - u64 value; + u64 tlb_addr = gd->arch.tlb_addr; + + /* Reset the fill ptr */ + gd->arch.tlb_fillptr = tlb_addr; - value = (u64)table_addr | PMD_TYPE_TABLE; - page_table[index] = value; + /* Create normal system page tables */ + setup_pgtables(); + + /* Create emergency page tables */ + gd->arch.tlb_addr = gd->arch.tlb_fillptr; + setup_pgtables(); + gd->arch.tlb_emerg = gd->arch.tlb_addr; + gd->arch.tlb_addr = tlb_addr; } -#endif /* to activate the MMU we need to set up virtual memory */ __weak void mmu_setup(void) { -#ifndef CONFIG_SYS_FULL_VA - bd_t *bd = gd->bd; - u64 *page_table = (u64 *)gd->arch.tlb_addr, i, j; -#endif int el; -#ifdef CONFIG_SYS_FULL_VA - unsigned long coreid = read_mpidr() & CONFIG_COREID_MASK; - - /* Set up page tables only on BSP */ - if (coreid == BSP_COREID) - setup_pgtables(); -#else - /* Setup an identity-mapping for all spaces */ - for (i = 0; i < (PGTABLE_SIZE >> 3); i++) { - set_pgtable_section(page_table, i, i << SECTION_SHIFT, - MT_DEVICE_NGNRNE, PMD_SECT_NON_SHARE); - } - - /* Setup an identity-mapping for all RAM space */ - for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { - ulong start = bd->bi_dram[i].start; - ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size; - for (j = start >> SECTION_SHIFT; - j < end >> SECTION_SHIFT; j++) { - set_pgtable_section(page_table, j, j << SECTION_SHIFT, - MT_NORMAL, PMD_SECT_NON_SHARE); - } - } + /* Set up page tables only once */ + if (!gd->arch.tlb_fillptr) + setup_all_pgtables(); -#endif - /* load TTBR0 */ el = current_el(); - if (el == 1) { - set_ttbr_tcr_mair(el, gd->arch.tlb_addr, - TCR_EL1_RSVD | TCR_FLAGS | TCR_EL1_IPS_BITS, - MEMORY_ATTRIBUTES); - } else if (el == 2) { - set_ttbr_tcr_mair(el, gd->arch.tlb_addr, - TCR_EL2_RSVD | TCR_FLAGS | TCR_EL2_IPS_BITS, - MEMORY_ATTRIBUTES); - } else { - set_ttbr_tcr_mair(el, gd->arch.tlb_addr, - TCR_EL3_RSVD | TCR_FLAGS | TCR_EL3_IPS_BITS, - MEMORY_ATTRIBUTES); - } + set_ttbr_tcr_mair(el, gd->arch.tlb_addr, get_tcr(el, NULL, NULL), + MEMORY_ATTRIBUTES); + /* enable the mmu */ set_sctlr(get_sctlr() | CR_M); } @@ -228,36 +482,99 @@ u64 *__weak arch_get_page_table(void) { return NULL; } -#ifndef CONFIG_SYS_FULL_VA +static bool is_aligned(u64 addr, u64 size, u64 align) +{ + return !(addr & (align - 1)) && !(size & (align - 1)); +} + +static u64 set_one_region(u64 start, u64 size, u64 attrs, int level) +{ + int levelshift = level2shift(level); + u64 levelsize = 1ULL << levelshift; + u64 *pte = find_pte(start, level); + + /* Can we can just modify the current level block PTE? */ + if (is_aligned(start, size, levelsize)) { + *pte &= ~PMD_ATTRINDX_MASK; + *pte |= attrs; + debug("Set attrs=%llx pte=%p level=%d\n", attrs, pte, level); + + return levelsize; + } + + /* Unaligned or doesn't fit, maybe split block into table */ + debug("addr=%llx level=%d pte=%p (%llx)\n", start, level, pte, *pte); + + /* Maybe we need to split the block into a table */ + if (pte_type(pte) == PTE_TYPE_BLOCK) + split_block(pte, level); + + /* And then double-check it became a table or already is one */ + if (pte_type(pte) != PTE_TYPE_TABLE) + panic("PTE %p (%llx) for addr=%llx should be a table", + pte, *pte, start); + + /* Roll on to the next page table level */ + return 0; +} + void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, enum dcache_option option) { - u64 *page_table = arch_get_page_table(); - u64 upto, end; - - if (page_table == NULL) - return; + u64 attrs = PMD_ATTRINDX(option); + u64 real_start = start; + u64 real_size = size; + + debug("start=%lx size=%lx\n", (ulong)start, (ulong)size); + + /* + * We can not modify page tables that we're currently running on, + * so we first need to switch to the "emergency" page tables where + * we can safely modify our primary page tables and then switch back + */ + __asm_switch_ttbr(gd->arch.tlb_emerg); + + /* + * Loop through the address range until we find a page granule that fits + * our alignment constraints, then set it to the new cache attributes + */ + while (size > 0) { + int level; + u64 r; + + for (level = 1; level < 4; level++) { + r = set_one_region(start, size, attrs, level); + if (r) { + /* PTE successfully replaced */ + size -= r; + start += r; + break; + } + } - end = ALIGN(start + size, (1 << MMU_SECTION_SHIFT)) >> - MMU_SECTION_SHIFT; - start = start >> MMU_SECTION_SHIFT; - for (upto = start; upto < end; upto++) { - page_table[upto] &= ~PMD_ATTRINDX_MASK; - page_table[upto] |= PMD_ATTRINDX(option); } - asm volatile("dsb sy"); - __asm_invalidate_tlb_all(); - asm volatile("dsb sy"); - asm volatile("isb"); - start = start << MMU_SECTION_SHIFT; - end = end << MMU_SECTION_SHIFT; - flush_dcache_range(start, end); - asm volatile("dsb sy"); + + /* We're done modifying page tables, switch back to our primary ones */ + __asm_switch_ttbr(gd->arch.tlb_addr); + + /* + * Make sure there's nothing stale in dcache for a region that might + * have caches off now + */ + flush_dcache_range(real_start, real_start + real_size); } -#endif #else /* CONFIG_SYS_DCACHE_OFF */ +/* + * For SPL builds, we may want to not have dcache enabled. Any real U-Boot + * running however really wants to have dcache and the MMU active. Check that + * everything is sane and give the developer a hint if it isn't. + */ +#ifndef CONFIG_SPL_BUILD +#error Please describe your MMU layout in CONFIG_SYS_MEM_MAP and enable dcache. +#endif + void invalidate_dcache_all(void) { } diff --git a/arch/arm/cpu/armv8/exceptions.S b/arch/arm/cpu/armv8/exceptions.S index baf9401..4f4f526 100644 --- a/arch/arm/cpu/armv8/exceptions.S +++ b/arch/arm/cpu/armv8/exceptions.S @@ -82,31 +82,65 @@ vectors: _do_bad_sync: exception_entry bl do_bad_sync + b exception_exit _do_bad_irq: exception_entry bl do_bad_irq + b exception_exit _do_bad_fiq: exception_entry bl do_bad_fiq + b exception_exit _do_bad_error: exception_entry bl do_bad_error + b exception_exit _do_sync: exception_entry bl do_sync + b exception_exit _do_irq: exception_entry bl do_irq + b exception_exit _do_fiq: exception_entry bl do_fiq + b exception_exit _do_error: exception_entry bl do_error + b exception_exit + +exception_exit: + ldp x2, x0, [sp],#16 + switch_el x11, 3f, 2f, 1f +3: msr elr_el3, x2 + b 0f +2: msr elr_el2, x2 + b 0f +1: msr elr_el1, x2 +0: + ldp x1, x2, [sp],#16 + ldp x3, x4, [sp],#16 + ldp x5, x6, [sp],#16 + ldp x7, x8, [sp],#16 + ldp x9, x10, [sp],#16 + ldp x11, x12, [sp],#16 + ldp x13, x14, [sp],#16 + ldp x15, x16, [sp],#16 + ldp x17, x18, [sp],#16 + ldp x19, x20, [sp],#16 + ldp x21, x22, [sp],#16 + ldp x23, x24, [sp],#16 + ldp x25, x26, [sp],#16 + ldp x27, x28, [sp],#16 + ldp x29, x30, [sp],#16 + eret diff --git a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c index 6ea28ed..7404bd9 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c +++ b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c @@ -26,6 +26,14 @@ DECLARE_GLOBAL_DATA_PTR; +static struct mm_region layerscape_mem_map[] = { + { + /* List terminator */ + 0, + } +}; +struct mm_region *mem_map = layerscape_mem_map; + void cpu_name(char *name) { struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR); @@ -48,6 +56,25 @@ void cpu_name(char *name) } #ifndef CONFIG_SYS_DCACHE_OFF +static void set_pgtable_section(u64 *page_table, u64 index, u64 section, + u64 memory_type, u64 attribute) +{ + u64 value; + + value = section | PTE_TYPE_BLOCK | PTE_BLOCK_AF; + value |= PMD_ATTRINDX(memory_type); + value |= attribute; + page_table[index] = value; +} + +static void set_pgtable_table(u64 *page_table, u64 index, u64 *table_addr) +{ + u64 value; + + value = (u64)table_addr | PTE_TYPE_TABLE; + page_table[index] = value; +} + /* * Set the block entries according to the information of the table. */ @@ -114,10 +141,10 @@ static int find_table(const struct sys_mmu_table *list, temp_base -= block_size; - if ((level_table[index - 1] & PMD_TYPE_MASK) == - PMD_TYPE_TABLE) { + if ((level_table[index - 1] & PTE_TYPE_MASK) == + PTE_TYPE_TABLE) { level_table = (u64 *)(level_table[index - 1] & - ~PMD_TYPE_MASK); + ~PTE_TYPE_MASK); level++; continue; } else { @@ -220,7 +247,7 @@ static inline int final_secure_ddr(u64 *level0_table, struct table_info table = {}; struct sys_mmu_table ddr_entry = { 0, 0, BLOCK_SIZE_L1, MT_NORMAL, - PMD_SECT_OUTER_SHARE | PMD_SECT_NS + PTE_BLOCK_OUTER_SHARE | PTE_BLOCK_NS }; u64 index; @@ -243,7 +270,7 @@ static inline int final_secure_ddr(u64 *level0_table, ddr_entry.virt_addr = phys_addr; ddr_entry.phys_addr = phys_addr; ddr_entry.size = CONFIG_SYS_MEM_RESERVE_SECURE; - ddr_entry.attribute = PMD_SECT_OUTER_SHARE; + ddr_entry.attribute = PTE_BLOCK_OUTER_SHARE; ret = find_table(&ddr_entry, &table, level0_table); if (ret) { printf("MMU error: could not find secure ddr table\n"); diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds index 4c12222..fd15ad5 100644 --- a/arch/arm/cpu/armv8/u-boot.lds +++ b/arch/arm/cpu/armv8/u-boot.lds @@ -42,6 +42,22 @@ SECTIONS . = ALIGN(8); + .efi_runtime : { + __efi_runtime_start = .; + *(efi_runtime_text) + *(efi_runtime_data) + __efi_runtime_stop = .; + } + + .efi_runtime_rel : { + __efi_runtime_rel_start = .; + *(.relaefi_runtime_text) + *(.relaefi_runtime_data) + __efi_runtime_rel_stop = .; + } + + . = ALIGN(8); + .image_copy_end : { *(.__image_copy_end) diff --git a/arch/arm/cpu/armv8/zynqmp/cpu.c b/arch/arm/cpu/armv8/zynqmp/cpu.c index c71f291..5dd3cd8 100644 --- a/arch/arm/cpu/armv8/zynqmp/cpu.c +++ b/arch/arm/cpu/armv8/zynqmp/cpu.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #define ZYNQ_SILICON_VER_MASK 0xF000 @@ -15,6 +16,53 @@ DECLARE_GLOBAL_DATA_PTR; +static struct mm_region zynqmp_mem_map[] = { + { + .base = 0x0UL, + .size = 0x80000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { + .base = 0x80000000UL, + .size = 0x70000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + .base = 0xf8000000UL, + .size = 0x07e00000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + .base = 0xffe00000UL, + .size = 0x00200000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { + .base = 0x400000000UL, + .size = 0x200000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + .base = 0x600000000UL, + .size = 0x800000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { + .base = 0xe00000000UL, + .size = 0xf200000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* List terminator */ + 0, + } +}; +struct mm_region *mem_map = zynqmp_mem_map; + static unsigned int zynqmp_get_silicon_version_secure(void) { u32 ver; @@ -44,172 +92,3 @@ unsigned int zynqmp_get_silicon_version(void) return ZYNQMP_CSU_VERSION_SILICON; } - -#ifndef CONFIG_SYS_DCACHE_OFF -#include - -#define SECTION_SHIFT_L1 30UL -#define SECTION_SHIFT_L2 21UL -#define BLOCK_SIZE_L0 0x8000000000UL -#define BLOCK_SIZE_L1 (1 << SECTION_SHIFT_L1) -#define BLOCK_SIZE_L2 (1 << SECTION_SHIFT_L2) - -#define TCR_TG1_4K (1 << 31) -#define TCR_EPD1_DISABLE (1 << 23) -#define ZYNQMO_VA_BITS 40 -#define ZYNQMP_TCR TCR_TG1_4K | \ - TCR_EPD1_DISABLE | \ - TCR_SHARED_OUTER | \ - TCR_SHARED_INNER | \ - TCR_IRGN_WBWA | \ - TCR_ORGN_WBWA | \ - TCR_T0SZ(ZYNQMO_VA_BITS) - -#define MEMORY_ATTR PMD_SECT_AF | PMD_SECT_INNER_SHARE | \ - PMD_ATTRINDX(MT_NORMAL) | \ - PMD_TYPE_SECT -#define DEVICE_ATTR PMD_SECT_AF | PMD_SECT_PXN | \ - PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_NGNRNE) | \ - PMD_TYPE_SECT - -/* 4K size is required to place 512 entries in each level */ -#define TLB_TABLE_SIZE 0x1000 - -struct attr_tbl { - u32 num; - u64 attr; -}; - -static struct attr_tbl attr_tbll1t0[4] = { {16, 0x0}, - {8, DEVICE_ATTR}, - {32, MEMORY_ATTR}, - {456, DEVICE_ATTR} - }; -static struct attr_tbl attr_tbll2t3[4] = { {0x180, DEVICE_ATTR}, - {0x40, 0x0}, - {0x3F, DEVICE_ATTR}, - {0x1, MEMORY_ATTR} - }; - -/* - * This mmu table looks as below - * Level 0 table contains two entries to 512GB sizes. One is Level1 Table 0 - * and other Level1 Table1. - * Level1 Table0 contains entries for each 1GB from 0 to 511GB. - * Level1 Table1 contains entries for each 1GB from 512GB to 1TB. - * Level2 Table0, Level2 Table1, Level2 Table2 and Level2 Table3 contains - * entries for each 2MB starting from 0GB, 1GB, 2GB and 3GB respectively. - */ -static void zynqmp_mmu_setup(void) -{ - int el; - u32 index_attr; - u64 i, section_l1t0, section_l1t1; - u64 section_l2t0, section_l2t1, section_l2t2, section_l2t3; - u64 *level0_table = (u64 *)gd->arch.tlb_addr; - u64 *level1_table_0 = (u64 *)(gd->arch.tlb_addr + TLB_TABLE_SIZE); - u64 *level1_table_1 = (u64 *)(gd->arch.tlb_addr + (2 * TLB_TABLE_SIZE)); - u64 *level2_table_0 = (u64 *)(gd->arch.tlb_addr + (3 * TLB_TABLE_SIZE)); - u64 *level2_table_1 = (u64 *)(gd->arch.tlb_addr + (4 * TLB_TABLE_SIZE)); - u64 *level2_table_2 = (u64 *)(gd->arch.tlb_addr + (5 * TLB_TABLE_SIZE)); - u64 *level2_table_3 = (u64 *)(gd->arch.tlb_addr + (6 * TLB_TABLE_SIZE)); - - level0_table[0] = - (u64)level1_table_0 | PMD_TYPE_TABLE; - level0_table[1] = - (u64)level1_table_1 | PMD_TYPE_TABLE; - - /* - * set level 1 table 0, covering 0 to 512GB - * set level 1 table 1, covering 512GB to 1TB - */ - section_l1t0 = 0; - section_l1t1 = BLOCK_SIZE_L0; - - index_attr = 0; - for (i = 0; i < 512; i++) { - level1_table_0[i] = section_l1t0; - level1_table_0[i] |= attr_tbll1t0[index_attr].attr; - attr_tbll1t0[index_attr].num--; - if (attr_tbll1t0[index_attr].num == 0) - index_attr++; - level1_table_1[i] = section_l1t1; - level1_table_1[i] |= DEVICE_ATTR; - section_l1t0 += BLOCK_SIZE_L1; - section_l1t1 += BLOCK_SIZE_L1; - } - - level1_table_0[0] = - (u64)level2_table_0 | PMD_TYPE_TABLE; - level1_table_0[1] = - (u64)level2_table_1 | PMD_TYPE_TABLE; - level1_table_0[2] = - (u64)level2_table_2 | PMD_TYPE_TABLE; - level1_table_0[3] = - (u64)level2_table_3 | PMD_TYPE_TABLE; - - section_l2t0 = 0; - section_l2t1 = section_l2t0 + BLOCK_SIZE_L1; /* 1GB */ - section_l2t2 = section_l2t1 + BLOCK_SIZE_L1; /* 2GB */ - section_l2t3 = section_l2t2 + BLOCK_SIZE_L1; /* 3GB */ - - index_attr = 0; - - for (i = 0; i < 512; i++) { - level2_table_0[i] = section_l2t0 | MEMORY_ATTR; - level2_table_1[i] = section_l2t1 | MEMORY_ATTR; - level2_table_2[i] = section_l2t2 | DEVICE_ATTR; - level2_table_3[i] = section_l2t3 | - attr_tbll2t3[index_attr].attr; - attr_tbll2t3[index_attr].num--; - if (attr_tbll2t3[index_attr].num == 0) - index_attr++; - section_l2t0 += BLOCK_SIZE_L2; - section_l2t1 += BLOCK_SIZE_L2; - section_l2t2 += BLOCK_SIZE_L2; - section_l2t3 += BLOCK_SIZE_L2; - } - - /* flush new MMU table */ - flush_dcache_range(gd->arch.tlb_addr, - gd->arch.tlb_addr + gd->arch.tlb_size); - - /* point TTBR to the new table */ - el = current_el(); - set_ttbr_tcr_mair(el, gd->arch.tlb_addr, - ZYNQMP_TCR, MEMORY_ATTRIBUTES); - - set_sctlr(get_sctlr() | CR_M); -} - -int arch_cpu_init(void) -{ - icache_enable(); - __asm_invalidate_dcache_all(); - __asm_invalidate_tlb_all(); - return 0; -} - -/* - * This function is called from lib/board.c. - * It recreates MMU table in main memory. MMU and d-cache are enabled earlier. - * There is no need to disable d-cache for this operation. - */ -void enable_caches(void) -{ - /* The data cache is not active unless the mmu is enabled */ - if (!(get_sctlr() & CR_M)) { - invalidate_dcache_all(); - __asm_invalidate_tlb_all(); - zynqmp_mmu_setup(); - } - puts("Enabling Caches...\n"); - - set_sctlr(get_sctlr() | CR_C); -} - -u64 *arch_get_page_table(void) -{ - return (u64 *)(gd->arch.tlb_addr + 0x3000); -} -#endif diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds index e148ab7..13aa4fa 100644 --- a/arch/arm/cpu/u-boot.lds +++ b/arch/arm/cpu/u-boot.lds @@ -90,6 +90,36 @@ SECTIONS . = ALIGN(4); + .__efi_runtime_start : { + *(.__efi_runtime_start) + } + + .efi_runtime : { + *(efi_runtime_text) + *(efi_runtime_data) + } + + .__efi_runtime_stop : { + *(.__efi_runtime_stop) + } + + .efi_runtime_rel_start : + { + *(.__efi_runtime_rel_start) + } + + .efi_runtime_rel : { + *(.relefi_runtime_text) + *(.relefi_runtime_data) + } + + .efi_runtime_rel_stop : + { + *(.__efi_runtime_rel_stop) + } + + . = ALIGN(4); + .image_copy_end : { *(.__image_copy_end) diff --git a/arch/arm/include/asm/arch-fsl-layerscape/cpu.h b/arch/arm/include/asm/arch-fsl-layerscape/cpu.h index 15ade84..93bbda3 100644 --- a/arch/arm/include/asm/arch-fsl-layerscape/cpu.h +++ b/arch/arm/include/asm/arch-fsl-layerscape/cpu.h @@ -117,48 +117,48 @@ static const struct sys_mmu_table early_mmu_table[] = { #ifdef CONFIG_FSL_LSCH3 { CONFIG_SYS_FSL_CCSR_BASE, CONFIG_SYS_FSL_CCSR_BASE, CONFIG_SYS_FSL_CCSR_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_OCRAM_BASE, CONFIG_SYS_FSL_OCRAM_BASE, - CONFIG_SYS_FSL_OCRAM_SIZE, MT_NORMAL, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_OCRAM_SIZE, MT_NORMAL, PTE_BLOCK_NON_SHARE }, /* For IFC Region #1, only the first 4MB is cache-enabled */ { CONFIG_SYS_FSL_IFC_BASE1, CONFIG_SYS_FSL_IFC_BASE1, - CONFIG_SYS_FSL_IFC_SIZE1_1, MT_NORMAL, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_IFC_SIZE1_1, MT_NORMAL, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_IFC_BASE1 + CONFIG_SYS_FSL_IFC_SIZE1_1, CONFIG_SYS_FSL_IFC_BASE1 + CONFIG_SYS_FSL_IFC_SIZE1_1, CONFIG_SYS_FSL_IFC_SIZE1 - CONFIG_SYS_FSL_IFC_SIZE1_1, - MT_DEVICE_NGNRNE, PMD_SECT_NON_SHARE }, + MT_DEVICE_NGNRNE, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FLASH_BASE, CONFIG_SYS_FSL_IFC_BASE1, - CONFIG_SYS_FSL_IFC_SIZE1, MT_DEVICE_NGNRNE, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_IFC_SIZE1, MT_DEVICE_NGNRNE, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_DRAM_BASE1, CONFIG_SYS_FSL_DRAM_BASE1, CONFIG_SYS_FSL_DRAM_SIZE1, MT_NORMAL, - PMD_SECT_OUTER_SHARE | PMD_SECT_NS }, + PTE_BLOCK_OUTER_SHARE | PTE_BLOCK_NS }, /* Map IFC region #2 up to CONFIG_SYS_FLASH_BASE for NAND boot */ { CONFIG_SYS_FSL_IFC_BASE2, CONFIG_SYS_FSL_IFC_BASE2, CONFIG_SYS_FLASH_BASE - CONFIG_SYS_FSL_IFC_BASE2, - MT_DEVICE_NGNRNE, PMD_SECT_NON_SHARE }, + MT_DEVICE_NGNRNE, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_DCSR_BASE, CONFIG_SYS_FSL_DCSR_BASE, CONFIG_SYS_FSL_DCSR_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_DRAM_BASE2, CONFIG_SYS_FSL_DRAM_BASE2, CONFIG_SYS_FSL_DRAM_SIZE2, MT_NORMAL, - PMD_SECT_OUTER_SHARE | PMD_SECT_NS }, + PTE_BLOCK_OUTER_SHARE | PTE_BLOCK_NS }, #elif defined(CONFIG_FSL_LSCH2) { CONFIG_SYS_FSL_CCSR_BASE, CONFIG_SYS_FSL_CCSR_BASE, CONFIG_SYS_FSL_CCSR_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_OCRAM_BASE, CONFIG_SYS_FSL_OCRAM_BASE, - CONFIG_SYS_FSL_OCRAM_SIZE, MT_NORMAL, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_OCRAM_SIZE, MT_NORMAL, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_DCSR_BASE, CONFIG_SYS_FSL_DCSR_BASE, CONFIG_SYS_FSL_DCSR_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_QSPI_BASE, CONFIG_SYS_FSL_QSPI_BASE, - CONFIG_SYS_FSL_QSPI_SIZE, MT_DEVICE_NGNRNE, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_QSPI_SIZE, MT_DEVICE_NGNRNE, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_IFC_BASE, CONFIG_SYS_FSL_IFC_BASE, - CONFIG_SYS_FSL_IFC_SIZE, MT_DEVICE_NGNRNE, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_IFC_SIZE, MT_DEVICE_NGNRNE, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_DRAM_BASE1, CONFIG_SYS_FSL_DRAM_BASE1, - CONFIG_SYS_FSL_DRAM_SIZE1, MT_NORMAL, PMD_SECT_OUTER_SHARE }, + CONFIG_SYS_FSL_DRAM_SIZE1, MT_NORMAL, PTE_BLOCK_OUTER_SHARE }, { CONFIG_SYS_FSL_DRAM_BASE2, CONFIG_SYS_FSL_DRAM_BASE2, - CONFIG_SYS_FSL_DRAM_SIZE2, MT_NORMAL, PMD_SECT_OUTER_SHARE }, + CONFIG_SYS_FSL_DRAM_SIZE2, MT_NORMAL, PTE_BLOCK_OUTER_SHARE }, #endif }; @@ -166,96 +166,96 @@ static const struct sys_mmu_table final_mmu_table[] = { #ifdef CONFIG_FSL_LSCH3 { CONFIG_SYS_FSL_CCSR_BASE, CONFIG_SYS_FSL_CCSR_BASE, CONFIG_SYS_FSL_CCSR_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_OCRAM_BASE, CONFIG_SYS_FSL_OCRAM_BASE, - CONFIG_SYS_FSL_OCRAM_SIZE, MT_NORMAL, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_OCRAM_SIZE, MT_NORMAL, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_DRAM_BASE1, CONFIG_SYS_FSL_DRAM_BASE1, CONFIG_SYS_FSL_DRAM_SIZE1, MT_NORMAL, - PMD_SECT_OUTER_SHARE | PMD_SECT_NS }, + PTE_BLOCK_OUTER_SHARE | PTE_BLOCK_NS }, { CONFIG_SYS_FSL_QSPI_BASE2, CONFIG_SYS_FSL_QSPI_BASE2, CONFIG_SYS_FSL_QSPI_SIZE2, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_IFC_BASE2, CONFIG_SYS_FSL_IFC_BASE2, - CONFIG_SYS_FSL_IFC_SIZE2, MT_DEVICE_NGNRNE, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_IFC_SIZE2, MT_DEVICE_NGNRNE, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_DCSR_BASE, CONFIG_SYS_FSL_DCSR_BASE, CONFIG_SYS_FSL_DCSR_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_MC_BASE, CONFIG_SYS_FSL_MC_BASE, CONFIG_SYS_FSL_MC_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_NI_BASE, CONFIG_SYS_FSL_NI_BASE, CONFIG_SYS_FSL_NI_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, /* For QBMAN portal, only the first 64MB is cache-enabled */ { CONFIG_SYS_FSL_QBMAN_BASE, CONFIG_SYS_FSL_QBMAN_BASE, CONFIG_SYS_FSL_QBMAN_SIZE_1, MT_NORMAL, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN | PMD_SECT_NS }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN | PTE_BLOCK_NS }, { CONFIG_SYS_FSL_QBMAN_BASE + CONFIG_SYS_FSL_QBMAN_SIZE_1, CONFIG_SYS_FSL_QBMAN_BASE + CONFIG_SYS_FSL_QBMAN_SIZE_1, CONFIG_SYS_FSL_QBMAN_SIZE - CONFIG_SYS_FSL_QBMAN_SIZE_1, - MT_DEVICE_NGNRNE, PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + MT_DEVICE_NGNRNE, PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_PCIE1_PHYS_ADDR, CONFIG_SYS_PCIE1_PHYS_ADDR, CONFIG_SYS_PCIE1_PHYS_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_PCIE2_PHYS_ADDR, CONFIG_SYS_PCIE2_PHYS_ADDR, CONFIG_SYS_PCIE2_PHYS_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_PCIE3_PHYS_ADDR, CONFIG_SYS_PCIE3_PHYS_ADDR, CONFIG_SYS_PCIE3_PHYS_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, #if defined(CONFIG_LS2080A) || defined(CONFIG_LS2085A) { CONFIG_SYS_PCIE4_PHYS_ADDR, CONFIG_SYS_PCIE4_PHYS_ADDR, CONFIG_SYS_PCIE4_PHYS_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, #endif { CONFIG_SYS_FSL_WRIOP1_BASE, CONFIG_SYS_FSL_WRIOP1_BASE, CONFIG_SYS_FSL_WRIOP1_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_AIOP1_BASE, CONFIG_SYS_FSL_AIOP1_BASE, CONFIG_SYS_FSL_AIOP1_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_PEBUF_BASE, CONFIG_SYS_FSL_PEBUF_BASE, CONFIG_SYS_FSL_PEBUF_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_DRAM_BASE2, CONFIG_SYS_FSL_DRAM_BASE2, CONFIG_SYS_FSL_DRAM_SIZE2, MT_NORMAL, - PMD_SECT_OUTER_SHARE | PMD_SECT_NS }, + PTE_BLOCK_OUTER_SHARE | PTE_BLOCK_NS }, #elif defined(CONFIG_FSL_LSCH2) { CONFIG_SYS_FSL_BOOTROM_BASE, CONFIG_SYS_FSL_BOOTROM_BASE, CONFIG_SYS_FSL_BOOTROM_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_CCSR_BASE, CONFIG_SYS_FSL_CCSR_BASE, CONFIG_SYS_FSL_CCSR_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_OCRAM_BASE, CONFIG_SYS_FSL_OCRAM_BASE, - CONFIG_SYS_FSL_OCRAM_SIZE, MT_NORMAL, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_OCRAM_SIZE, MT_NORMAL, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_DCSR_BASE, CONFIG_SYS_FSL_DCSR_BASE, CONFIG_SYS_FSL_DCSR_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_QSPI_BASE, CONFIG_SYS_FSL_QSPI_BASE, CONFIG_SYS_FSL_QSPI_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_IFC_BASE, CONFIG_SYS_FSL_IFC_BASE, - CONFIG_SYS_FSL_IFC_SIZE, MT_DEVICE_NGNRNE, PMD_SECT_NON_SHARE }, + CONFIG_SYS_FSL_IFC_SIZE, MT_DEVICE_NGNRNE, PTE_BLOCK_NON_SHARE }, { CONFIG_SYS_FSL_DRAM_BASE1, CONFIG_SYS_FSL_DRAM_BASE1, CONFIG_SYS_FSL_DRAM_SIZE1, MT_NORMAL, - PMD_SECT_OUTER_SHARE | PMD_SECT_NS }, + PTE_BLOCK_OUTER_SHARE | PTE_BLOCK_NS }, { CONFIG_SYS_FSL_QBMAN_BASE, CONFIG_SYS_FSL_QBMAN_BASE, CONFIG_SYS_FSL_QBMAN_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_DRAM_BASE2, CONFIG_SYS_FSL_DRAM_BASE2, - CONFIG_SYS_FSL_DRAM_SIZE2, MT_NORMAL, PMD_SECT_OUTER_SHARE }, + CONFIG_SYS_FSL_DRAM_SIZE2, MT_NORMAL, PTE_BLOCK_OUTER_SHARE }, { CONFIG_SYS_PCIE1_PHYS_ADDR, CONFIG_SYS_PCIE1_PHYS_ADDR, CONFIG_SYS_PCIE1_PHYS_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_PCIE2_PHYS_ADDR, CONFIG_SYS_PCIE2_PHYS_ADDR, CONFIG_SYS_PCIE2_PHYS_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_PCIE3_PHYS_ADDR, CONFIG_SYS_PCIE3_PHYS_ADDR, CONFIG_SYS_PCIE3_PHYS_SIZE, MT_DEVICE_NGNRNE, - PMD_SECT_NON_SHARE | PMD_SECT_PXN | PMD_SECT_UXN }, + PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN }, { CONFIG_SYS_FSL_DRAM_BASE3, CONFIG_SYS_FSL_DRAM_BASE3, - CONFIG_SYS_FSL_DRAM_SIZE3, MT_NORMAL, PMD_SECT_OUTER_SHARE }, + CONFIG_SYS_FSL_DRAM_SIZE3, MT_NORMAL, PTE_BLOCK_OUTER_SHARE }, #endif }; #endif diff --git a/arch/arm/include/asm/armv8/mmu.h b/arch/arm/include/asm/armv8/mmu.h index 897f010..0d08ed3 100644 --- a/arch/arm/include/asm/armv8/mmu.h +++ b/arch/arm/include/asm/armv8/mmu.h @@ -22,32 +22,19 @@ * calculated specifically. */ -#ifndef CONFIG_SYS_FULL_VA -#define VA_BITS (42) /* 42 bits virtual address */ -#else #define VA_BITS CONFIG_SYS_VA_BITS -#define PTL2_BITS CONFIG_SYS_PTL2_BITS -#endif +#define PTE_BLOCK_BITS CONFIG_SYS_PTL2_BITS + +/* + * block/section address mask and size definitions. + */ /* PAGE_SHIFT determines the page size */ #undef PAGE_SIZE -#define PAGE_SHIFT 16 +#define PAGE_SHIFT 12 #define PAGE_SIZE (1 << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) -/* - * block/section address mask and size definitions. - */ -#ifndef CONFIG_SYS_FULL_VA -#define SECTION_SHIFT 29 -#define SECTION_SIZE (UL(1) << SECTION_SHIFT) -#define SECTION_MASK (~(SECTION_SIZE-1)) -#else -#define BLOCK_SHIFT CONFIG_SYS_BLOCK_SHIFT -#define BLOCK_SIZE (UL(1) << BLOCK_SHIFT) -#define BLOCK_MASK (~(BLOCK_SIZE-1)) -#endif - /***************************************************************/ /* @@ -70,63 +57,28 @@ * */ -#ifdef CONFIG_SYS_FULL_VA -/* - * Level 1 descriptor (PGD). - */ - -#define PTL1_TYPE_MASK (3 << 0) -#define PTL1_TYPE_TABLE (3 << 0) - -#define PTL1_TABLE_PXN (1UL << 59) -#define PTL1_TABLE_XN (1UL << 60) -#define PTL1_TABLE_AP (1UL << 61) -#define PTL1_TABLE_NS (1UL << 63) +#define PTE_TYPE_MASK (3 << 0) +#define PTE_TYPE_FAULT (0 << 0) +#define PTE_TYPE_TABLE (3 << 0) +#define PTE_TYPE_BLOCK (1 << 0) - -/* - * Level 2 descriptor (PMD). - */ - -#define PTL2_TYPE_MASK (3 << 0) -#define PTL2_TYPE_FAULT (0 << 0) -#define PTL2_TYPE_TABLE (3 << 0) -#define PTL2_TYPE_BLOCK (1 << 0) +#define PTE_TABLE_PXN (1UL << 59) +#define PTE_TABLE_XN (1UL << 60) +#define PTE_TABLE_AP (1UL << 61) +#define PTE_TABLE_NS (1UL << 63) /* * Block */ -#define PTL2_MEMTYPE(x) ((x) << 2) -#define PTL2_BLOCK_NON_SHARE (0 << 8) -#define PTL2_BLOCK_OUTER_SHARE (2 << 8) -#define PTL2_BLOCK_INNER_SHARE (3 << 8) -#define PTL2_BLOCK_AF (1 << 10) -#define PTL2_BLOCK_NG (1 << 11) -#define PTL2_BLOCK_PXN (UL(1) << 53) -#define PTL2_BLOCK_UXN (UL(1) << 54) - -#else -/* - * Level 2 descriptor (PMD). - */ -#define PMD_TYPE_MASK (3 << 0) -#define PMD_TYPE_FAULT (0 << 0) -#define PMD_TYPE_TABLE (3 << 0) -#define PMD_TYPE_SECT (1 << 0) - -/* - * Section - */ -#define PMD_SECT_NS (1 << 5) -#define PMD_SECT_NON_SHARE (0 << 8) -#define PMD_SECT_OUTER_SHARE (2 << 8) -#define PMD_SECT_INNER_SHARE (3 << 8) -#define PMD_SECT_AF (1 << 10) -#define PMD_SECT_NG (1 << 11) -#define PMD_SECT_PXN (UL(1) << 53) -#define PMD_SECT_UXN (UL(1) << 54) - -#endif +#define PTE_BLOCK_MEMTYPE(x) ((x) << 2) +#define PTE_BLOCK_NS (1 << 5) +#define PTE_BLOCK_NON_SHARE (0 << 8) +#define PTE_BLOCK_OUTER_SHARE (2 << 8) +#define PTE_BLOCK_INNER_SHARE (3 << 8) +#define PTE_BLOCK_AF (1 << 10) +#define PTE_BLOCK_NG (1 << 11) +#define PTE_BLOCK_PXN (UL(1) << 53) +#define PTE_BLOCK_UXN (UL(1) << 54) /* * AttrIndx[2:0] @@ -154,38 +106,13 @@ #define TCR_TG0_4K (0 << 14) #define TCR_TG0_64K (1 << 14) #define TCR_TG0_16K (2 << 14) - -#ifndef CONFIG_SYS_FULL_VA -#define TCR_EL1_IPS_BITS (UL(3) << 32) /* 42 bits physical address */ -#define TCR_EL2_IPS_BITS (3 << 16) /* 42 bits physical address */ -#define TCR_EL3_IPS_BITS (3 << 16) /* 42 bits physical address */ -#else -#define TCR_EL1_IPS_BITS CONFIG_SYS_TCR_EL1_IPS_BITS -#define TCR_EL2_IPS_BITS CONFIG_SYS_TCR_EL2_IPS_BITS -#define TCR_EL3_IPS_BITS CONFIG_SYS_TCR_EL3_IPS_BITS -#endif - -/* PTWs cacheable, inner/outer WBWA and inner shareable */ -#define TCR_FLAGS (TCR_TG0_64K | \ - TCR_SHARED_INNER | \ - TCR_ORGN_WBWA | \ - TCR_IRGN_WBWA | \ - TCR_T0SZ(VA_BITS)) +#define TCR_EPD1_DISABLE (1 << 23) #define TCR_EL1_RSVD (1 << 31) #define TCR_EL2_RSVD (1 << 31 | 1 << 23) #define TCR_EL3_RSVD (1 << 31 | 1 << 23) #ifndef __ASSEMBLY__ -#ifndef CONFIG_SYS_FULL_VA - -void set_pgtable_section(u64 *page_table, u64 index, - u64 section, u64 memory_type, - u64 attribute); -void set_pgtable_table(u64 *page_table, u64 index, - u64 *table_addr); - -#endif static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr) { asm volatile("dsb sy"); @@ -212,6 +139,8 @@ struct mm_region { u64 size; u64 attrs; }; + +extern struct mm_region *mem_map; #endif #endif /* _ASM_ARMV8_MMU_H_ */ diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h index dcfa098..77d2653 100644 --- a/arch/arm/include/asm/global_data.h +++ b/arch/arm/include/asm/global_data.h @@ -38,10 +38,11 @@ struct arch_global_data { unsigned long long timer_reset_value; #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) unsigned long tlb_addr; -#if defined(CONFIG_SYS_FULL_VA) - unsigned long pmd_addr[CONFIG_SYS_PTL1_ENTRIES]; -#endif unsigned long tlb_size; +#if defined(CONFIG_ARM64) + unsigned long tlb_fillptr; + unsigned long tlb_emerg; +#endif #endif #ifdef CONFIG_OMAP_COMMON diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 026e7ef..ac1173d 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -17,18 +17,15 @@ #define CR_WXN (1 << 19) /* Write Permision Imply XN */ #define CR_EE (1 << 25) /* Exception (Big) Endian */ -#ifndef CONFIG_SYS_FULL_VA -#define PGTABLE_SIZE (0x10000) -#else -#define PGTABLE_SIZE CONFIG_SYS_PGTABLE_SIZE -#endif +#ifndef __ASSEMBLY__ + +u64 get_page_table_size(void); +#define PGTABLE_SIZE get_page_table_size() /* 2MB granularity */ #define MMU_SECTION_SHIFT 21 #define MMU_SECTION_SIZE (1 << MMU_SECTION_SHIFT) -#ifndef __ASSEMBLY__ - enum dcache_option { DCACHE_OFF = 0x3, }; @@ -97,6 +94,7 @@ void __asm_flush_dcache_range(u64 start, u64 end); void __asm_invalidate_tlb_all(void); void __asm_invalidate_icache_all(void); int __asm_flush_l3_cache(void); +void __asm_switch_ttbr(u64 new_ttbr); void armv8_switch_to_el2(void); void armv8_switch_to_el1(void); diff --git a/arch/arm/lib/interrupts_64.c b/arch/arm/lib/interrupts_64.c index b476722..7c9cfce 100644 --- a/arch/arm/lib/interrupts_64.c +++ b/arch/arm/lib/interrupts_64.c @@ -7,6 +7,7 @@ #include #include +#include int interrupt_init(void) @@ -41,6 +42,7 @@ void show_regs(struct pt_regs *regs) */ void do_bad_sync(struct pt_regs *pt_regs, unsigned int esr) { + efi_restore_gd(); printf("Bad mode in \"Synchronous Abort\" handler, esr 0x%08x\n", esr); show_regs(pt_regs); panic("Resetting CPU ...\n"); @@ -51,6 +53,7 @@ void do_bad_sync(struct pt_regs *pt_regs, unsigned int esr) */ void do_bad_irq(struct pt_regs *pt_regs, unsigned int esr) { + efi_restore_gd(); printf("Bad mode in \"Irq\" handler, esr 0x%08x\n", esr); show_regs(pt_regs); panic("Resetting CPU ...\n"); @@ -61,6 +64,7 @@ void do_bad_irq(struct pt_regs *pt_regs, unsigned int esr) */ void do_bad_fiq(struct pt_regs *pt_regs, unsigned int esr) { + efi_restore_gd(); printf("Bad mode in \"Fiq\" handler, esr 0x%08x\n", esr); show_regs(pt_regs); panic("Resetting CPU ...\n"); @@ -71,6 +75,7 @@ void do_bad_fiq(struct pt_regs *pt_regs, unsigned int esr) */ void do_bad_error(struct pt_regs *pt_regs, unsigned int esr) { + efi_restore_gd(); printf("Bad mode in \"Error\" handler, esr 0x%08x\n", esr); show_regs(pt_regs); panic("Resetting CPU ...\n"); @@ -81,6 +86,7 @@ void do_bad_error(struct pt_regs *pt_regs, unsigned int esr) */ void do_sync(struct pt_regs *pt_regs, unsigned int esr) { + efi_restore_gd(); printf("\"Synchronous Abort\" handler, esr 0x%08x\n", esr); show_regs(pt_regs); panic("Resetting CPU ...\n"); @@ -91,6 +97,7 @@ void do_sync(struct pt_regs *pt_regs, unsigned int esr) */ void do_irq(struct pt_regs *pt_regs, unsigned int esr) { + efi_restore_gd(); printf("\"Irq\" handler, esr 0x%08x\n", esr); show_regs(pt_regs); panic("Resetting CPU ...\n"); @@ -101,6 +108,7 @@ void do_irq(struct pt_regs *pt_regs, unsigned int esr) */ void do_fiq(struct pt_regs *pt_regs, unsigned int esr) { + efi_restore_gd(); printf("\"Fiq\" handler, esr 0x%08x\n", esr); show_regs(pt_regs); panic("Resetting CPU ...\n"); @@ -114,6 +122,7 @@ void do_fiq(struct pt_regs *pt_regs, unsigned int esr) */ void __weak do_error(struct pt_regs *pt_regs, unsigned int esr) { + efi_restore_gd(); printf("\"Error\" handler, esr 0x%08x\n", esr); show_regs(pt_regs); panic("Resetting CPU ...\n"); diff --git a/arch/arm/lib/sections.c b/arch/arm/lib/sections.c index a1205c3..6a94522 100644 --- a/arch/arm/lib/sections.c +++ b/arch/arm/lib/sections.c @@ -27,4 +27,8 @@ char __rel_dyn_start[0] __attribute__((section(".__rel_dyn_start"))); char __rel_dyn_end[0] __attribute__((section(".__rel_dyn_end"))); char __secure_start[0] __attribute__((section(".__secure_start"))); char __secure_end[0] __attribute__((section(".__secure_end"))); +char __efi_runtime_start[0] __attribute__((section(".__efi_runtime_start"))); +char __efi_runtime_stop[0] __attribute__((section(".__efi_runtime_stop"))); +char __efi_runtime_rel_start[0] __attribute__((section(".__efi_runtime_rel_start"))); +char __efi_runtime_rel_stop[0] __attribute__((section(".__efi_runtime_rel_stop"))); char _end[0] __attribute__((section(".__end"))); diff --git a/arch/arm/mach-tegra/arm64-mmu.c b/arch/arm/mach-tegra/arm64-mmu.c index c227652..501c4f0 100644 --- a/arch/arm/mach-tegra/arm64-mmu.c +++ b/arch/arm/mach-tegra/arm64-mmu.c @@ -12,120 +12,22 @@ #include #include -DECLARE_GLOBAL_DATA_PTR; - -#define SECTION_SHIFT_L1 30UL -#define SECTION_SHIFT_L2 21UL -#define BLOCK_SIZE_L0 0x8000000000UL -#define BLOCK_SIZE_L1 (1 << SECTION_SHIFT_L1) -#define BLOCK_SIZE_L2 (1 << SECTION_SHIFT_L2) - -#define TCR_TG1_4K (1 << 31) -#define TCR_EPD1_DISABLE (1 << 23) -#define TEGRA_VA_BITS 40 -#define TEGRA_TCR TCR_TG1_4K | \ - TCR_EPD1_DISABLE | \ - TCR_SHARED_OUTER | \ - TCR_SHARED_INNER | \ - TCR_IRGN_WBWA | \ - TCR_ORGN_WBWA | \ - TCR_T0SZ(TEGRA_VA_BITS) - -#define MEMORY_ATTR PMD_SECT_AF | PMD_SECT_INNER_SHARE | \ - PMD_ATTRINDX(MT_NORMAL) | \ - PMD_TYPE_SECT -#define DEVICE_ATTR PMD_SECT_AF | PMD_SECT_PXN | \ - PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_NGNRNE) | \ - PMD_TYPE_SECT - -/* 4K size is required to place 512 entries in each level */ -#define TLB_TABLE_SIZE 0x1000 - -/* - * This mmu table looks as below - * Level 0 table contains two entries to 512GB sizes. One is Level1 Table 0 - * and other Level1 Table1. - * Level1 Table0 contains entries for each 1GB from 0 to 511GB. - * Level1 Table1 contains entries for each 1GB from 512GB to 1TB. - * Level2 Table0, Level2 Table1, Level2 Table2 and Level2 Table3 contains - * entries for each 2MB starting from 0GB, 1GB, 2GB and 3GB respectively. - */ -void mmu_setup(void) -{ - int el; - u64 i, section_l1t0, section_l1t1; - u64 section_l2t0, section_l2t1, section_l2t2, section_l2t3; - u64 *level0_table = (u64 *)gd->arch.tlb_addr; - u64 *level1_table_0 = (u64 *)(gd->arch.tlb_addr + TLB_TABLE_SIZE); - u64 *level1_table_1 = (u64 *)(gd->arch.tlb_addr + (2 * TLB_TABLE_SIZE)); - u64 *level2_table_0 = (u64 *)(gd->arch.tlb_addr + (3 * TLB_TABLE_SIZE)); - u64 *level2_table_1 = (u64 *)(gd->arch.tlb_addr + (4 * TLB_TABLE_SIZE)); - u64 *level2_table_2 = (u64 *)(gd->arch.tlb_addr + (5 * TLB_TABLE_SIZE)); - u64 *level2_table_3 = (u64 *)(gd->arch.tlb_addr + (6 * TLB_TABLE_SIZE)); - - /* Invalidate all table entries */ - memset(level0_table, 0, PGTABLE_SIZE); - - level0_table[0] = - (u64)level1_table_0 | PMD_TYPE_TABLE; - level0_table[1] = - (u64)level1_table_1 | PMD_TYPE_TABLE; - - /* - * set level 1 table 0, covering 0 to 512GB - * set level 1 table 1, covering 512GB to 1TB - */ - section_l1t0 = 0; - section_l1t1 = BLOCK_SIZE_L0; - - for (i = 0; i < 512; i++) { - level1_table_0[i] = section_l1t0; - if (i >= 4) - level1_table_0[i] |= MEMORY_ATTR; - level1_table_1[i] = section_l1t1; - level1_table_1[i] |= MEMORY_ATTR; - section_l1t0 += BLOCK_SIZE_L1; - section_l1t1 += BLOCK_SIZE_L1; +static struct mm_region tegra_mem_map[] = { + { + .base = 0x0UL, + .size = 0x80000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + .base = 0x80000000UL, + .size = 0xff80000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { + /* List terminator */ + 0, } +}; - level1_table_0[0] = - (u64)level2_table_0 | PMD_TYPE_TABLE; - level1_table_0[1] = - (u64)level2_table_1 | PMD_TYPE_TABLE; - level1_table_0[2] = - (u64)level2_table_2 | PMD_TYPE_TABLE; - level1_table_0[3] = - (u64)level2_table_3 | PMD_TYPE_TABLE; - - section_l2t0 = 0; - section_l2t1 = section_l2t0 + BLOCK_SIZE_L1; /* 1GB */ - section_l2t2 = section_l2t1 + BLOCK_SIZE_L1; /* 2GB */ - section_l2t3 = section_l2t2 + BLOCK_SIZE_L1; /* 3GB */ - - for (i = 0; i < 512; i++) { - level2_table_0[i] = section_l2t0 | DEVICE_ATTR; - level2_table_1[i] = section_l2t1 | DEVICE_ATTR; - level2_table_2[i] = section_l2t2 | MEMORY_ATTR; - level2_table_3[i] = section_l2t3 | MEMORY_ATTR; - section_l2t0 += BLOCK_SIZE_L2; - section_l2t1 += BLOCK_SIZE_L2; - section_l2t2 += BLOCK_SIZE_L2; - section_l2t3 += BLOCK_SIZE_L2; - } - - /* flush new MMU table */ - flush_dcache_range(gd->arch.tlb_addr, - gd->arch.tlb_addr + gd->arch.tlb_size); - - /* point TTBR to the new table */ - el = current_el(); - set_ttbr_tcr_mair(el, gd->arch.tlb_addr, - TEGRA_TCR, MEMORY_ATTRIBUTES); - - set_sctlr(get_sctlr() | CR_M); -} - -u64 *arch_get_page_table(void) -{ - return (u64 *)(gd->arch.tlb_addr + (3 * TLB_TABLE_SIZE)); -} +struct mm_region *mem_map = tegra_mem_map; diff --git a/board/armltd/vexpress64/vexpress64.c b/board/armltd/vexpress64/vexpress64.c index 6efc8c1..973b579 100644 --- a/board/armltd/vexpress64/vexpress64.c +++ b/board/armltd/vexpress64/vexpress64.c @@ -14,6 +14,7 @@ #include #include #include "pcie.h" +#include DECLARE_GLOBAL_DATA_PTR; @@ -28,6 +29,26 @@ U_BOOT_DEVICE(vexpress_serials) = { .platdata = &serial_platdata, }; +static struct mm_region vexpress64_mem_map[] = { + { + .base = 0x0UL, + .size = 0x80000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + .base = 0x80000000UL, + .size = 0xff80000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { + /* List terminator */ + 0, + } +}; + +struct mm_region *mem_map = vexpress64_mem_map; + /* This function gets replaced by platforms supporting PCIe. * The replacement function, eg. on Juno, initialises the PCIe bus. */ diff --git a/board/cavium/thunderx/thunderx.c b/board/cavium/thunderx/thunderx.c index b926767..9131a38 100644 --- a/board/cavium/thunderx/thunderx.c +++ b/board/cavium/thunderx/thunderx.c @@ -10,6 +10,7 @@ #include #include +#include #if !CONFIG_IS_ENABLED(OF_CONTROL) #include @@ -42,6 +43,29 @@ U_BOOT_DEVICE(thunderx_serial1) = { DECLARE_GLOBAL_DATA_PTR; +static struct mm_region thunderx_mem_map[] = { + { + .base = 0x000000000000UL, + .size = 0x40000000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_NON_SHARE, + }, { + .base = 0x800000000000UL, + .size = 0x40000000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE, + }, { + .base = 0x840000000000UL, + .size = 0x40000000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE, + }, { + /* List terminator */ + 0, + } +}; + +struct mm_region *mem_map = thunderx_mem_map; + int board_init(void) { return 0; diff --git a/board/hisilicon/hikey/hikey.c b/board/hisilicon/hikey/hikey.c index c4ae40b..1edc807 100644 --- a/board/hisilicon/hikey/hikey.c +++ b/board/hisilicon/hikey/hikey.c @@ -19,6 +19,7 @@ #include #include #include +#include /*TODO drop this table in favour of device tree */ static const struct hikey_gpio_platdata hi6220_gpio[] = { @@ -87,6 +88,26 @@ U_BOOT_DEVICE(hikey_seriala) = { .platdata = &serial_platdata, }; +static struct mm_region hikey_mem_map[] = { + { + .base = 0x0UL, + .size = 0x80000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { + .base = 0x80000000UL, + .size = 0x80000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* List terminator */ + 0, + } +}; + +struct mm_region *mem_map = hikey_mem_map; + #ifdef CONFIG_BOARD_EARLY_INIT_F int board_uart_init(void) { diff --git a/board/ti/am335x/u-boot.lds b/board/ti/am335x/u-boot.lds index 78f294a..a56cc82 100644 --- a/board/ti/am335x/u-boot.lds +++ b/board/ti/am335x/u-boot.lds @@ -59,6 +59,36 @@ SECTIONS . = ALIGN(4); + .__efi_runtime_start : { + *(.__efi_runtime_start) + } + + .efi_runtime : { + *(efi_runtime_text) + *(efi_runtime_data) + } + + .__efi_runtime_stop : { + *(.__efi_runtime_stop) + } + + .efi_runtime_rel_start : + { + *(.__efi_runtime_rel_start) + } + + .efi_runtime_rel : { + *(.relefi_runtime_text) + *(.relefi_runtime_data) + } + + .efi_runtime_rel_stop : + { + *(.__efi_runtime_rel_stop) + } + + . = ALIGN(4); + .image_copy_end : { *(.__image_copy_end) diff --git a/cmd/Kconfig b/cmd/Kconfig index 2ed0263..7cdff04 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -148,6 +148,13 @@ config CMD_BOOTM help Boot an application image from the memory. +config CMD_BOOTEFI + bool "bootefi" + depends on EFI_LOADER + default y + help + Boot an EFI image from memory. + config CMD_ELF bool "bootelf, bootvx" default y diff --git a/cmd/Makefile b/cmd/Makefile index 03f7e0a..7604621 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_CMD_SOURCE) += source.o obj-$(CONFIG_CMD_BDI) += bdinfo.o obj-$(CONFIG_CMD_BEDBUG) += bedbug.o obj-$(CONFIG_CMD_BMP) += bmp.o +obj-$(CONFIG_CMD_BOOTEFI) += bootefi.o obj-$(CONFIG_CMD_BOOTMENU) += bootmenu.o obj-$(CONFIG_CMD_BOOTLDR) += bootldr.o obj-$(CONFIG_CMD_BOOTSTAGE) += bootstage.o diff --git a/cmd/bootefi.c b/cmd/bootefi.c new file mode 100644 index 0000000..e3e51d4 --- /dev/null +++ b/cmd/bootefi.c @@ -0,0 +1,167 @@ +/* + * EFI application loader + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/* + * When booting using the "bootefi" command, we don't know which + * physical device the file came from. So we create a pseudo-device + * called "bootefi" with the device path /bootefi. + * + * In addition to the originating device we also declare the file path + * of "bootefi" based loads to be /bootefi. + */ +static struct efi_device_path_file_path bootefi_dummy_path[] = { + { + .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, + .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, + .dp.length = sizeof(bootefi_dummy_path[0]), + .str = { 'b','o','o','t','e','f','i' }, + }, { + .dp.type = DEVICE_PATH_TYPE_END, + .dp.sub_type = DEVICE_PATH_SUB_TYPE_END, + .dp.length = sizeof(bootefi_dummy_path[0]), + } +}; + +static efi_status_t bootefi_open_dp(void *handle, efi_guid_t *protocol, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + *protocol_interface = bootefi_dummy_path; + return EFI_SUCCESS; +} + +/* The EFI loaded_image interface for the image executed via "bootefi" */ +static struct efi_loaded_image loaded_image_info = { + .device_handle = bootefi_dummy_path, + .file_path = bootefi_dummy_path, +}; + +/* The EFI object struct for the image executed via "bootefi" */ +static struct efi_object loaded_image_info_obj = { + .handle = &loaded_image_info, + .protocols = { + { + /* + * When asking for the loaded_image interface, just + * return handle which points to loaded_image_info + */ + .guid = &efi_guid_loaded_image, + .open = &efi_return_handle, + }, + { + /* + * When asking for the device path interface, return + * bootefi_dummy_path + */ + .guid = &efi_guid_device_path, + .open = &bootefi_open_dp, + }, + }, +}; + +/* The EFI object struct for the device the "bootefi" image was loaded from */ +static struct efi_object bootefi_device_obj = { + .handle = bootefi_dummy_path, + .protocols = { + { + /* When asking for the device path interface, return + * bootefi_dummy_path */ + .guid = &efi_guid_device_path, + .open = &bootefi_open_dp, + } + }, +}; + +/* + * Load an EFI payload into a newly allocated piece of memory, register all + * EFI objects it would want to access and jump to it. + */ +static unsigned long do_bootefi_exec(void *efi) +{ + ulong (*entry)(void *image_handle, struct efi_system_table *st); + + /* + * gd lives in a fixed register which may get clobbered while we execute + * the payload. So save it here and restore it on every callback entry + */ + efi_save_gd(); + + /* Update system table to point to our currently loaded FDT */ + + if (working_fdt) { + systab.tables[0].guid = EFI_FDT_GUID; + systab.tables[0].table = working_fdt; + systab.nr_tables = 1; + } else { + printf("WARNING: No device tree loaded, expect boot to fail\n"); + systab.nr_tables = 0; + } + + /* Load the EFI payload */ + entry = efi_load_pe(efi, &loaded_image_info); + if (!entry) + return -ENOENT; + + /* Initialize and populate EFI object list */ + INIT_LIST_HEAD(&efi_obj_list); + list_add_tail(&loaded_image_info_obj.link, &efi_obj_list); + list_add_tail(&bootefi_device_obj.link, &efi_obj_list); +#ifdef CONFIG_PARTITIONS + efi_disk_register(); +#endif + + /* Call our payload! */ +#ifdef DEBUG_EFI + printf("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry); +#endif + return entry(&loaded_image_info, &systab); +} + + +/* Interpreter command to boot an arbitrary EFI image from memory */ +static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *saddr; + unsigned long addr; + int r = 0; + + if (argc < 2) + return 1; + saddr = argv[1]; + + addr = simple_strtoul(saddr, NULL, 16); + + printf("## Starting EFI application at 0x%08lx ...\n", addr); + r = do_bootefi_exec((void *)addr); + printf("## Application terminated, r = %d\n", r); + + if (r != 0) + r = 1; + + return r; +} + +static char bootefi_help_text[] = + "\n" + " - boot EFI payload stored at address \n" + "\n" + "Since most EFI payloads want to have a device tree provided, please\n" + "make sure you load a device tree using the fdt addr command before\n" + "executing bootefi.\n"; + +U_BOOT_CMD( + bootefi, 2, 0, do_bootefi, + "Boots an EFI payload from memory\n", + bootefi_help_text +); diff --git a/common/board_r.c b/common/board_r.c index 6c23865..63837e9 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -64,6 +64,7 @@ #ifdef CONFIG_AVR32 #include #endif +#include DECLARE_GLOBAL_DATA_PTR; @@ -176,6 +177,9 @@ static int initr_reloc_global_data(void) */ gd->fdt_blob += gd->reloc_off; #endif +#ifdef CONFIG_EFI_LOADER + efi_runtime_relocate(gd->relocaddr, NULL); +#endif return 0; } @@ -781,6 +785,9 @@ init_fnc_t init_sequence_r[] = { #ifdef CONFIG_CLOCKS set_cpu_clk_info, /* Setup clock information */ #endif +#ifdef CONFIG_EFI_LOADER + efi_memory_init, +#endif stdio_init_tables, initr_serial, initr_announce, diff --git a/disk/part.c b/disk/part.c index 1935b28..d265c2b 100644 --- a/disk/part.c +++ b/disk/part.c @@ -20,13 +20,8 @@ #define PRINTF(fmt,args...) #endif -struct block_drvr { - char *name; - block_dev_desc_t* (*get_dev)(int dev); - int (*select_hwpart)(int dev_num, int hwpart); -}; -static const struct block_drvr block_drvr[] = { +const struct block_drvr block_drvr[] = { #if defined(CONFIG_CMD_IDE) { .name = "ide", .get_dev = ide_get_dev, }, #endif diff --git a/doc/README.arm64 b/doc/README.arm64 index de669cb..f658fa2 100644 --- a/doc/README.arm64 +++ b/doc/README.arm64 @@ -36,26 +36,6 @@ Notes 6. CONFIG_ARM64 instead of CONFIG_ARMV8 is used to distinguish aarch64 and aarch32 specific codes. -7. CONFIG_SYS_FULL_VA is used to enable 2-level page tables. For cores - supporting 64k pages it allows usage of full 48+ virtual/physical addresses - - Enabling this option requires the following ones to be defined: - - CONFIG_SYS_MEM_MAP - an array of 'struct mm_region' describing the - system memory map (start, length, attributes) - - CONFIG_SYS_MEM_MAP_SIZE - number of entries in CONFIG_SYS_MEM_MAP - - CONFIG_SYS_PTL1_ENTRIES - number of 1st level page table entries - - CONFIG_SYS_PTL2_ENTRIES - number of 1nd level page table entries - for the largest CONFIG_SYS_MEM_MAP entry - - CONFIG_COREID_MASK - the mask value used to get the core from the - MPIDR_EL1 register - - CONFIG_SYS_PTL2_BITS - number of bits addressed by the 2nd level - page tables - - CONFIG_SYS_BLOCK_SHIFT - number of bits addressed by a single block - entry from L2 page tables - - CONFIG_SYS_PGTABLE_SIZE - total size of the page table - - CONFIG_SYS_TCR_EL{1,2,3}_IPS_BITS - the IPS field of the TCR_EL{1,2,3} - - Contributor diff --git a/doc/README.efi b/doc/README.efi index 23a3cdd..1fd3f00 100644 --- a/doc/README.efi +++ b/doc/README.efi @@ -4,6 +4,28 @@ # SPDX-License-Identifier: GPL-2.0+ # +=========== Table of Contents =========== + + 1 U-Boot on EFI + 1.1 In God's Name, Why? + 1.2 Status + 1.3 Build Instructions + 1.4 Trying it out + 1.5 Inner workings + 1.6 EFI Application + 1.7 EFI Payload + 1.8 Tables + 1.9 Interrupts + 1.10 32/64-bit + 1.11 Future work + 1.12 Where is the code? + + 2 EFI on U-Boot + 2.1 In God's Name, Why? + 2.2 How do I get it? + 2.3 Status + 2.4 Future work + U-Boot on EFI ============= This document provides information about U-Boot running on top of EFI, either @@ -234,7 +256,6 @@ board/efi/efi-x86/efi.c common/cmd_efi.c the 'efi' command - -- Ben Stoltz, Simon Glass Google, Inc @@ -242,3 +263,63 @@ July 2015 [1] http://www.qemu.org [2] http://www.tianocore.org/ovmf/ + +------------------------------------------------------------------------------- + +EFI on U-Boot +============= + +In addition to support for running U-Boot as a UEFI application, U-Boot itself +can also expose the UEFI interfaces and thus allow UEFI payloads to run under +it. + +In God's Name, Why? +------------------- + +With this support in place, you can run any UEFI payload (such as the Linux +kernel, grub2 or gummiboot) on U-Boot. This dramatically simplifies boot loader +configuration, as U-Boot based systems now look and feel (almost) the same way +as TianoCore based systems. + +How do I get it? +---------------- + +EFI support for 32bit ARM and AArch64 is already included in U-Boot. All you +need to do is enable + + CONFIG_CMD_BOOTEFI=y + CONFIG_EFI_LOADER=y + +in your .config file and you will automatically get a bootefi command to run +an efi application as well as snippet in the default distro boot script that +scans for removable media efi binaries as fallback. + +Status +------ + +I am successfully able to run grub2 and Linux EFI binaries with this code on +ARMv7 as well as AArch64 systems. + +When enabled, the resulting U-Boot binary only grows by ~10KB, so it's very +light weight. + +All storage devices are directly accessible from the uEFI payload + +Removable media booting (search for /efi/boot/boota{a64,arm}.efi) is supported. + +Simple use cases like "Plug this SD card into my ARM device and it just +boots into grub which boots into Linux", work very well. + +Future work +----------- + +Of course, there are still a few things one could do on top: + + - Improve disk media detection (don't scan, use what information we +have) + - Add EFI variable support using NVRAM + - Add GFX support + - Make EFI Shell work + - Network device support + - Support for payload exit + - Payload Watchdog support diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 909e3ca..7329f40 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -189,7 +189,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, data ? DIV_ROUND_UP(data->blocks, 8) : 0); int ret = 0, flags = 0, i; unsigned int timeout = 100000; - u32 retry = 10000; + u32 retry = 100000; u32 mask, ctrl; ulong start = get_timer(0); struct bounce_buffer bbstate; diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index 37c6b43..c19f1b0 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -90,6 +90,48 @@ BOOT_TARGET_DEVICES_references_UBIFS_without_CONFIG_CMD_UBIFS #endif +#ifdef CONFIG_EFI_LOADER +#if defined(CONFIG_ARM64) +#define BOOTEFI_NAME "bootaa64.efi" +#elif defined(CONFIG_ARM) +#define BOOTEFI_NAME "bootarm.efi" +#endif +#endif + +#ifdef BOOTEFI_NAME +#define BOOTENV_SHARED_EFI \ + "boot_efi_binary=" \ + "load ${devtype} ${devnum}:${distro_bootpart} " \ + "${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; " \ + "bootefi ${kernel_addr_r}\0" \ + \ + "load_efi_dtb=" \ + "load ${devtype} ${devnum}:${distro_bootpart} " \ + "${fdt_addr_r} ${prefix}${fdt_name}; " \ + "fdt addr ${fdt_addr_r}\0" \ + \ + "efi_dtb_prefixes=/ /dtb/ /dtb/current/\0" \ + "scan_dev_for_efi=" \ + "for prefix in ${efi_dtb_prefixes}; do " \ + "if test -e ${devtype} " \ + "${devnum}:${distro_bootpart} " \ + "${prefix}${fdt_name}; then " \ + "run load_efi_dtb; " \ + "fi;" \ + "done;" \ + "if test -e ${devtype} ${devnum}:${distro_bootpart} " \ + "efi/boot/"BOOTEFI_NAME"; then " \ + "echo Found EFI removable media binary " \ + "efi/boot/"BOOTEFI_NAME"; " \ + "run boot_efi_binary; " \ + "echo EFI LOAD FAILED: continuing...; " \ + "fi; " +#define SCAN_DEV_FOR_EFI "run scan_dev_for_efi;" +#else +#define BOOTENV_SHARED_EFI +#define SCAN_DEV_FOR_EFI +#endif + #ifdef CONFIG_CMD_SATA #define BOOTENV_SHARED_SATA BOOTENV_SHARED_BLKDEV(sata) #define BOOTENV_DEV_SATA BOOTENV_DEV_BLKDEV @@ -217,6 +259,7 @@ BOOTENV_SHARED_SCSI \ BOOTENV_SHARED_IDE \ BOOTENV_SHARED_UBIFS \ + BOOTENV_SHARED_EFI \ "boot_prefixes=/ /boot/\0" \ "boot_scripts=boot.scr.uimg boot.scr\0" \ "boot_script_dhcp=boot.scr.uimg\0" \ @@ -258,7 +301,9 @@ "for prefix in ${boot_prefixes}; do " \ "run scan_dev_for_extlinux; " \ "run scan_dev_for_scripts; " \ - "done\0" \ + "done;" \ + SCAN_DEV_FOR_EFI \ + "\0" \ \ "scan_dev_for_boot_part=" \ "part list ${devtype} ${devnum} -bootable devplist; " \ diff --git a/include/configs/hikey.h b/include/configs/hikey.h index 796861e..2d9ace9 100644 --- a/include/configs/hikey.h +++ b/include/configs/hikey.h @@ -21,8 +21,8 @@ #define CONFIG_SUPPORT_RAW_INITRD -/* Cache Definitions */ -#define CONFIG_SYS_DCACHE_OFF +/* MMU Definitions */ +#define CONFIG_SYS_CACHELINE_SIZE 64 #define CONFIG_IDENT_STRING "hikey" diff --git a/include/configs/thunderx_88xx.h b/include/configs/thunderx_88xx.h index cece4dd..736d0a5 100644 --- a/include/configs/thunderx_88xx.h +++ b/include/configs/thunderx_88xx.h @@ -22,38 +22,8 @@ #define MEM_BASE 0x00500000 -#define CONFIG_COREID_MASK 0xffffff - -#define CONFIG_SYS_FULL_VA - #define CONFIG_SYS_LOWMEM_BASE MEM_BASE -#define CONFIG_SYS_MEM_MAP {{0x000000000000UL, 0x40000000000UL, \ - PTL2_MEMTYPE(MT_NORMAL) | \ - PTL2_BLOCK_NON_SHARE}, \ - {0x800000000000UL, 0x40000000000UL, \ - PTL2_MEMTYPE(MT_DEVICE_NGNRNE) | \ - PTL2_BLOCK_NON_SHARE}, \ - {0x840000000000UL, 0x40000000000UL, \ - PTL2_MEMTYPE(MT_DEVICE_NGNRNE) | \ - PTL2_BLOCK_NON_SHARE}, \ - } - -#define CONFIG_SYS_MEM_MAP_SIZE 3 - -#define CONFIG_SYS_VA_BITS 48 -#define CONFIG_SYS_PTL2_BITS 42 -#define CONFIG_SYS_BLOCK_SHIFT 29 -#define CONFIG_SYS_PTL1_ENTRIES 64 -#define CONFIG_SYS_PTL2_ENTRIES 8192 - -#define CONFIG_SYS_PGTABLE_SIZE \ - ((CONFIG_SYS_PTL1_ENTRIES + \ - CONFIG_SYS_MEM_MAP_SIZE * CONFIG_SYS_PTL2_ENTRIES) * 8) -#define CONFIG_SYS_TCR_EL1_IPS_BITS (5UL << 32) -#define CONFIG_SYS_TCR_EL2_IPS_BITS (5 << 16) -#define CONFIG_SYS_TCR_EL3_IPS_BITS (5 << 16) - /* Link Definitions */ #define CONFIG_SYS_TEXT_BASE 0x00500000 #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x7fff0) diff --git a/include/configs/vexpress_aemv8a.h b/include/configs/vexpress_aemv8a.h index 133041b..2949170 100644 --- a/include/configs/vexpress_aemv8a.h +++ b/include/configs/vexpress_aemv8a.h @@ -19,9 +19,8 @@ #define CONFIG_SUPPORT_RAW_INITRD -/* Cache Definitions */ -#define CONFIG_SYS_DCACHE_OFF -#define CONFIG_SYS_ICACHE_OFF +/* MMU Definitions */ +#define CONFIG_SYS_CACHELINE_SIZE 64 #define CONFIG_IDENT_STRING " vexpress_aemv8a" #define CONFIG_BOOTP_VCI_STRING "U-Boot.armv8.vexpress_aemv8a" diff --git a/include/efi.h b/include/efi.h index fcafda0..1dbc3b7 100644 --- a/include/efi.h +++ b/include/efi.h @@ -38,6 +38,7 @@ struct efi_device_path; #define EFI_WRITE_PROTECTED (8 | (1UL << (BITS_PER_LONG - 1))) #define EFI_OUT_OF_RESOURCES (9 | (1UL << (BITS_PER_LONG - 1))) #define EFI_NOT_FOUND (14 | (1UL << (BITS_PER_LONG - 1))) +#define EFI_ACCESS_DENIED (15 | (1UL << (BITS_PER_LONG - 1))) #define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG - 1))) typedef unsigned long efi_status_t; @@ -139,6 +140,7 @@ enum { #define EFI_PAGE_SHIFT 12 #define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) +#define EFI_PAGE_MASK (EFI_PAGE_SIZE - 1) struct efi_mem_desc { u32 type; diff --git a/include/efi_api.h b/include/efi_api.h index 4fd17d6..03f6687 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -17,11 +17,18 @@ #include +/* Types and defines for EFI CreateEvent */ +enum efi_event_type { + EFI_TIMER_STOP = 0, + EFI_TIMER_PERIODIC = 1, + EFI_TIMER_RELATIVE = 2 +}; + /* EFI Boot Services table */ struct efi_boot_services { struct efi_table_hdr hdr; - void *raise_tpl; - void *restore_tpl; + efi_status_t (EFIAPI *raise_tpl)(unsigned long new_tpl); + void (EFIAPI *restore_tpl)(unsigned long old_tpl); efi_status_t (EFIAPI *allocate_pages)(int, int, unsigned long, efi_physical_addr_t *); @@ -32,21 +39,33 @@ struct efi_boot_services { efi_status_t (EFIAPI *allocate_pool)(int, unsigned long, void **); efi_status_t (EFIAPI *free_pool)(void *); - void *create_event; - void *set_timer; - efi_status_t(EFIAPI *wait_for_event)(unsigned long number_of_events, - void *event, unsigned long *index); - void *signal_event; - void *close_event; - void *check_event; - - void *install_protocol_interface; - void *reinstall_protocol_interface; - void *uninstall_protocol_interface; + efi_status_t (EFIAPI *create_event)(enum efi_event_type type, + unsigned long notify_tpl, + void (EFIAPI *notify_function) (void *event, + void *context), + void *notify_context, void **event); + efi_status_t (EFIAPI *set_timer)(void *event, int type, + uint64_t trigger_time); + efi_status_t (EFIAPI *wait_for_event)(unsigned long number_of_events, + void *event, unsigned long *index); + efi_status_t (EFIAPI *signal_event)(void *event); + efi_status_t (EFIAPI *close_event)(void *event); + efi_status_t (EFIAPI *check_event)(void *event); + + efi_status_t (EFIAPI *install_protocol_interface)( + void **handle, efi_guid_t *protocol, + int protocol_interface_type, void *protocol_interface); + efi_status_t (EFIAPI *reinstall_protocol_interface)( + void *handle, efi_guid_t *protocol, + void *old_interface, void *new_interface); + efi_status_t (EFIAPI *uninstall_protocol_interface)(void *handle, + efi_guid_t *protocol, void *protocol_interface); efi_status_t (EFIAPI *handle_protocol)(efi_handle_t, efi_guid_t *, void **); void *reserved; - void *register_protocol_notify; + efi_status_t (EFIAPI *register_protocol_notify)( + efi_guid_t *protocol, void *event, + void **registration); efi_status_t (EFIAPI *locate_handle)( enum efi_locate_search_type search_type, efi_guid_t *protocol, void *search_key, @@ -54,7 +73,8 @@ struct efi_boot_services { efi_status_t (EFIAPI *locate_device_path)(efi_guid_t *protocol, struct efi_device_path **device_path, efi_handle_t *device); - void *install_configuration_table; + efi_status_t (EFIAPI *install_configuration_table)( + efi_guid_t *guid, void *table); efi_status_t (EFIAPI *load_image)(bool boot_policiy, efi_handle_t parent_image, @@ -66,17 +86,20 @@ struct efi_boot_services { efi_status_t (EFIAPI *exit)(efi_handle_t handle, efi_status_t exit_status, unsigned long exitdata_size, s16 *exitdata); - void *unload_image; + efi_status_t (EFIAPI *unload_image)(void *image_handle); efi_status_t (EFIAPI *exit_boot_services)(efi_handle_t, unsigned long); efi_status_t (EFIAPI *get_next_monotonic_count)(u64 *count); efi_status_t (EFIAPI *stall)(unsigned long usecs); - void *set_watchdog_timer; + efi_status_t (EFIAPI *set_watchdog_timer)(unsigned long timeout, + uint64_t watchdog_code, unsigned long data_size, + uint16_t *watchdog_data); efi_status_t(EFIAPI *connect_controller)(efi_handle_t controller_handle, efi_handle_t *driver_image_handle, struct efi_device_path *remaining_device_path, bool recursive); - void *disconnect_controller; + efi_status_t (EFIAPI *disconnect_controller)(void *controller_handle, + void *driver_image_handle, void *child_handle); #define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001 #define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002 #define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004 @@ -87,7 +110,9 @@ struct efi_boot_services { efi_guid_t *protocol, void **interface, efi_handle_t agent_handle, efi_handle_t controller_handle, u32 attributes); - void *close_protocol; + efi_status_t (EFIAPI *close_protocol)(void *handle, + efi_guid_t *protocol, void *agent_handle, + void *controller_handle); efi_status_t(EFIAPI *open_protocol_information)(efi_handle_t handle, efi_guid_t *protocol, struct efi_open_protocol_info_entry **entry_buffer, @@ -99,12 +124,18 @@ struct efi_boot_services { enum efi_locate_search_type search_type, efi_guid_t *protocol, void *search_key, unsigned long *no_handles, efi_handle_t **buffer); - void *locate_protocol; - void *install_multiple_protocol_interfaces; - void *uninstall_multiple_protocol_interfaces; - void *calculate_crc32; - void *copy_mem; - void *set_mem; + efi_status_t (EFIAPI *locate_protocol)(efi_guid_t *protocol, + void *registration, void **protocol_interface); + efi_status_t (EFIAPI *install_multiple_protocol_interfaces)( + void **handle, ...); + efi_status_t (EFIAPI *uninstall_multiple_protocol_interfaces)( + void *handle, ...); + efi_status_t (EFIAPI *calculate_crc32)(void *data, + unsigned long data_size, uint32_t *crc32); + void (EFIAPI *copy_mem)(void *destination, void *source, + unsigned long length); + void (EFIAPI *set_mem)(void *buffer, unsigned long size, + uint8_t value); void *create_event_ex; }; @@ -121,12 +152,19 @@ enum efi_reset_type { struct efi_runtime_services { struct efi_table_hdr hdr; - void *get_time; - void *set_time; - void *get_wakeup_time; - void *set_wakeup_time; - void *set_virtual_address_map; - void *convert_pointer; + efi_status_t (EFIAPI *get_time)(struct efi_time *time, + struct efi_time_cap *capabilities); + efi_status_t (EFIAPI *set_time)(struct efi_time *time); + efi_status_t (EFIAPI *get_wakeup_time)(char *enabled, char *pending, + struct efi_time *time); + efi_status_t (EFIAPI *set_wakeup_time)(char enabled, + struct efi_time *time); + efi_status_t (EFIAPI *set_virtual_address_map)( + unsigned long memory_map_size, + unsigned long descriptor_size, + uint32_t descriptor_version, + struct efi_mem_desc *virtmap); + efi_status_t (*convert_pointer)(unsigned long dbg, void **address); efi_status_t (EFIAPI *get_variable)(s16 *variable_name, efi_guid_t *vendor, u32 *attributes, unsigned long *data_size, void *data); @@ -136,7 +174,8 @@ struct efi_runtime_services { efi_status_t (EFIAPI *set_variable)(s16 *variable_name, efi_guid_t *vendor, u32 attributes, unsigned long data_size, void *data); - void *get_next_high_mono_count; + efi_status_t (EFIAPI *get_next_high_mono_count)( + uint32_t *high_count); void (EFIAPI *reset_system)(enum efi_reset_type reset_type, efi_status_t reset_status, unsigned long data_size, void *reset_data); @@ -154,6 +193,18 @@ struct efi_runtime_services { EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, \ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define EFI_FDT_GUID \ + EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, \ + 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0) + +struct efi_configuration_table +{ + efi_guid_t guid; + void *table; +}; + +#define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL) + struct efi_system_table { struct efi_table_hdr hdr; unsigned long fw_vendor; /* physical addr of wchar_t vendor string */ @@ -163,13 +214,17 @@ struct efi_system_table { unsigned long con_out_handle; struct efi_simple_text_output_protocol *con_out; unsigned long stderr_handle; - unsigned long std_err; + struct efi_simple_text_output_protocol *std_err; struct efi_runtime_services *runtime; struct efi_boot_services *boottime; unsigned long nr_tables; - unsigned long tables; + struct efi_configuration_table *tables; }; +#define LOADED_IMAGE_GUID \ + EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, \ + 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) + struct efi_loaded_image { u32 revision; void *parent_handle; @@ -186,12 +241,60 @@ struct efi_loaded_image { unsigned long unload; }; +#define DEVICE_PATH_GUID \ + EFI_GUID(0x09576e91, 0x6d3f, 0x11d2, \ + 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) + +#define DEVICE_PATH_TYPE_END 0x7f +# define DEVICE_PATH_SUB_TYPE_END 0xff + struct efi_device_path { u8 type; u8 sub_type; u16 length; }; +#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 +# define DEVICE_PATH_SUB_TYPE_FILE_PATH 0x04 + +struct efi_device_path_file_path { + struct efi_device_path dp; + u16 str[16]; +}; + +#define BLOCK_IO_GUID \ + EFI_GUID(0x964e5b21, 0x6459, 0x11d2, \ + 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) + +struct efi_block_io_media +{ + u32 media_id; + char removable_media; + char media_present; + char logical_partition; + char read_only; + char write_caching; + u8 pad[3]; + u32 block_size; + u32 io_align; + u8 pad2[4]; + u64 last_block; +}; + +struct efi_block_io { + u64 revision; + struct efi_block_io_media *media; + efi_status_t (EFIAPI *reset)(struct efi_block_io *this, + char extended_verification); + efi_status_t (EFIAPI *read_blocks)(struct efi_block_io *this, + u32 media_id, u64 lba, unsigned long buffer_size, + void *buffer); + efi_status_t (EFIAPI *write_blocks)(struct efi_block_io *this, + u32 media_id, u64 lba, unsigned long buffer_size, + void *buffer); + efi_status_t (EFIAPI *flush_blocks)(struct efi_block_io *this); +}; + struct simple_text_output_mode { s32 max_mode; s32 mode; @@ -206,8 +309,9 @@ struct efi_simple_text_output_protocol { efi_status_t (EFIAPI *output_string)( struct efi_simple_text_output_protocol *this, const unsigned short *str); - void *test_string; - + efi_status_t (EFIAPI *test_string)( + struct efi_simple_text_output_protocol *this, + const unsigned short *str); efi_status_t(EFIAPI *query_mode)( struct efi_simple_text_output_protocol *this, unsigned long mode_number, unsigned long *columns, @@ -223,7 +327,9 @@ struct efi_simple_text_output_protocol { efi_status_t(EFIAPI *set_cursor_position) ( struct efi_simple_text_output_protocol *this, unsigned long column, unsigned long row); - efi_status_t(EFIAPI *enable_cursor)(void *, bool enable); + efi_status_t(EFIAPI *enable_cursor)( + struct efi_simple_text_output_protocol *this, + bool enable); struct simple_text_output_mode *mode; }; @@ -241,4 +347,22 @@ struct efi_simple_input_interface { void *wait_for_key; }; +#define CONSOLE_CONTROL_GUID \ + EFI_GUID(0xf42f7782, 0x12e, 0x4c12, \ + 0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21) +#define EFI_CONSOLE_MODE_TEXT 0 +#define EFI_CONSOLE_MODE_GFX 1 + +struct efi_console_control_protocol +{ + efi_status_t (EFIAPI *get_mode)( + struct efi_console_control_protocol *this, int *mode, + char *uga_exists, char *std_in_locked); + efi_status_t (EFIAPI *set_mode)( + struct efi_console_control_protocol *this, int mode); + efi_status_t (EFIAPI *lock_std_in)( + struct efi_console_control_protocol *this, + uint16_t *password); +}; + #endif diff --git a/include/efi_loader.h b/include/efi_loader.h new file mode 100644 index 0000000..e344566 --- /dev/null +++ b/include/efi_loader.h @@ -0,0 +1,147 @@ +/* + * EFI application loader + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#ifdef CONFIG_EFI_LOADER + +#include + +/* #define DEBUG_EFI */ + +#ifdef DEBUG_EFI +#define EFI_ENTRY(format, ...) do { \ + efi_restore_gd(); \ + printf("EFI: Entry %s(" format ")\n", __func__, ##__VA_ARGS__); \ + } while(0) +#else +#define EFI_ENTRY(format, ...) do { \ + efi_restore_gd(); \ + } while(0) +#endif + +#define EFI_EXIT(ret) efi_exit_func(ret); + +extern struct efi_runtime_services efi_runtime_services; +extern struct efi_system_table systab; + +extern const struct efi_simple_text_output_protocol efi_con_out; +extern const struct efi_simple_input_interface efi_con_in; +extern const struct efi_console_control_protocol efi_console_control; + +extern const efi_guid_t efi_guid_console_control; +extern const efi_guid_t efi_guid_device_path; +extern const efi_guid_t efi_guid_loaded_image; + +extern unsigned int __efi_runtime_start, __efi_runtime_stop; +extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; + +/* + * While UEFI objects can have callbacks, you can also call functions on + * protocols (classes) themselves. This struct maps a protocol GUID to its + * interface (usually a struct with callback functions). + */ +struct efi_class_map { + const efi_guid_t *guid; + const void *interface; +}; + +/* + * When the UEFI payload wants to open a protocol on an object to get its + * interface (usually a struct with callback functions), this struct maps the + * protocol GUID to the respective protocol handler open function for that + * object protocol combination. + */ +struct efi_handler { + const efi_guid_t *guid; + efi_status_t (EFIAPI *open)(void *handle, + efi_guid_t *protocol, void **protocol_interface, + void *agent_handle, void *controller_handle, + uint32_t attributes); +}; + +/* + * UEFI has a poor man's OO model where one "object" can be polymorphic and have + * multiple different protocols (classes) attached to it. + * + * This struct is the parent struct for all of our actual implementation objects + * that can include it to make themselves an EFI object + */ +struct efi_object { + /* Every UEFI object is part of a global object list */ + struct list_head link; + /* We support up to 4 "protocols" an object can be accessed through */ + struct efi_handler protocols[4]; + /* The object spawner can either use this for data or as identifier */ + void *handle; +}; + +/* This list contains all UEFI objects we know of */ +extern struct list_head efi_obj_list; + +/* Called by bootefi to make all disk storage accessible as EFI objects */ +int efi_disk_register(void); +/* + * Stub implementation for a protocol opener that just returns the handle as + * interface + */ +efi_status_t efi_return_handle(void *handle, + efi_guid_t *protocol, void **protocol_interface, + void *agent_handle, void *controller_handle, + uint32_t attributes); +/* Called from places to check whether a timer expired */ +void efi_timer_check(void); +/* PE loader implementation */ +void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info); +/* Called once to store the pristine gd pointer */ +void efi_save_gd(void); +/* Called from EFI_ENTRY on callback entry to put gd into the gd register */ +void efi_restore_gd(void); +/* Called from EFI_EXIT on callback exit to restore the gd register */ +efi_status_t efi_exit_func(efi_status_t ret); +/* Call this to relocate the runtime section to an address space */ +void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map); + +/* Generic EFI memory allocator, call this to get memory */ +void *efi_alloc(uint64_t len, int memory_type); +/* More specific EFI memory allocator, called by EFI payloads */ +efi_status_t efi_allocate_pages(int type, int memory_type, unsigned long pages, + uint64_t *memory); +/* EFI memory free function. Not implemented today */ +efi_status_t efi_free_pages(uint64_t memory, unsigned long pages); +/* Returns the EFI memory map */ +efi_status_t efi_get_memory_map(unsigned long *memory_map_size, + struct efi_mem_desc *memory_map, + unsigned long *map_key, + unsigned long *descriptor_size, + uint32_t *descriptor_version); +/* Adds a range into the EFI memory map */ +uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, + bool overlap_only_ram); +/* Called by board init to initialize the EFI memory map */ +int efi_memory_init(void); + +/* + * Use these to indicate that your code / data should go into the EFI runtime + * section and thus still be available when the OS is running + */ +#define EFI_RUNTIME_DATA __attribute__ ((section ("efi_runtime_data"))) +#define EFI_RUNTIME_TEXT __attribute__ ((section ("efi_runtime_text"))) + +#else /* defined(EFI_LOADER) */ + +/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ +#define EFI_RUNTIME_DATA +#define EFI_RUNTIME_TEXT + +/* No loader configured, stub out EFI_ENTRY */ +static inline void efi_restore_gd(void) { } + +#endif diff --git a/include/part.h b/include/part.h index dc23949..6e6205b 100644 --- a/include/part.h +++ b/include/part.h @@ -42,6 +42,12 @@ struct block_dev_desc { void *priv; /* driver private struct pointer */ }; +struct block_drvr { + char *name; + block_dev_desc_t* (*get_dev)(int dev); + int (*select_hwpart)(int dev_num, int hwpart); +}; + #define BLOCK_CNT(size, block_dev_desc) (PAD_COUNT(size, block_dev_desc->blksz)) #define PAD_TO_BLOCKSIZE(size, block_dev_desc) \ (PAD_SIZE(size, block_dev_desc->blksz)) @@ -123,6 +129,8 @@ int get_device(const char *ifname, const char *dev_str, int get_device_and_partition(const char *ifname, const char *dev_part_str, block_dev_desc_t **dev_desc, disk_partition_t *info, int allow_whole_dev); + +extern const struct block_drvr block_drvr[]; #else static inline block_dev_desc_t *get_dev(const char *ifname, int dev) { return NULL; } diff --git a/include/pe.h b/include/pe.h new file mode 100644 index 0000000..deb35a0 --- /dev/null +++ b/include/pe.h @@ -0,0 +1,263 @@ +/* + * Portable Executable binary format structures + * + * Copyright (c) 2016 Alexander Graf + * + * Based on wine code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _PE_H +#define _PE_H + +typedef struct _IMAGE_DOS_HEADER { + uint16_t e_magic; /* 00: MZ Header signature */ + uint16_t e_cblp; /* 02: Bytes on last page of file */ + uint16_t e_cp; /* 04: Pages in file */ + uint16_t e_crlc; /* 06: Relocations */ + uint16_t e_cparhdr; /* 08: Size of header in paragraphs */ + uint16_t e_minalloc; /* 0a: Minimum extra paragraphs needed */ + uint16_t e_maxalloc; /* 0c: Maximum extra paragraphs needed */ + uint16_t e_ss; /* 0e: Initial (relative) SS value */ + uint16_t e_sp; /* 10: Initial SP value */ + uint16_t e_csum; /* 12: Checksum */ + uint16_t e_ip; /* 14: Initial IP value */ + uint16_t e_cs; /* 16: Initial (relative) CS value */ + uint16_t e_lfarlc; /* 18: File address of relocation table */ + uint16_t e_ovno; /* 1a: Overlay number */ + uint16_t e_res[4]; /* 1c: Reserved words */ + uint16_t e_oemid; /* 24: OEM identifier (for e_oeminfo) */ + uint16_t e_oeminfo; /* 26: OEM information; e_oemid specific */ + uint16_t e_res2[10]; /* 28: Reserved words */ + uint32_t e_lfanew; /* 3c: Offset to extended header */ +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + +#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ +#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ + +#define IMAGE_FILE_MACHINE_ARM 0x01c0 +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 +#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_ARM64 0xaa64 +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b +#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 + +typedef struct _IMAGE_FILE_HEADER { + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + +typedef struct _IMAGE_DATA_DIRECTORY { + uint32_t VirtualAddress; + uint32_t Size; +} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; + +#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 + +typedef struct _IMAGE_OPTIONAL_HEADER64 { + uint16_t Magic; /* 0x20b */ + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; +} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; + +typedef struct _IMAGE_NT_HEADERS64 { + uint32_t Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; + +typedef struct _IMAGE_OPTIONAL_HEADER { + + /* Standard fields */ + + uint16_t Magic; /* 0x10b or 0x107 */ /* 0x00 */ + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; /* 0x10 */ + uint32_t BaseOfCode; + uint32_t BaseOfData; + + /* NT additional fields */ + + uint32_t ImageBase; + uint32_t SectionAlignment; /* 0x20 */ + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; /* 0x30 */ + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; /* 0x40 */ + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint32_t SizeOfStackReserve; + uint32_t SizeOfStackCommit; + uint32_t SizeOfHeapReserve; /* 0x50 */ + uint32_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */ + /* 0xE0 */ +} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; + +typedef struct _IMAGE_NT_HEADERS { + uint32_t Signature; /* "PE"\0\0 */ /* 0x00 */ + IMAGE_FILE_HEADER FileHeader; /* 0x04 */ + IMAGE_OPTIONAL_HEADER32 OptionalHeader; /* 0x18 */ +} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; + +#define IMAGE_SIZEOF_SHORT_NAME 8 + +typedef struct _IMAGE_SECTION_HEADER { + uint8_t Name[IMAGE_SIZEOF_SHORT_NAME]; + union { + uint32_t PhysicalAddress; + uint32_t VirtualSize; + } Misc; + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; +} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; + +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 + +typedef struct _IMAGE_BASE_RELOCATION +{ + uint32_t VirtualAddress; + uint32_t SizeOfBlock; + /* WORD TypeOffset[1]; */ +} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION; + +typedef struct _IMAGE_RELOCATION +{ + union { + uint32_t VirtualAddress; + uint32_t RelocCount; + } DUMMYUNIONNAME; + uint32_t SymbolTableIndex; + uint16_t Type; +} IMAGE_RELOCATION, *PIMAGE_RELOCATION; + +#define IMAGE_SIZEOF_RELOCATION 10 + +/* generic relocation types */ +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGH 1 +#define IMAGE_REL_BASED_LOW 2 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_HIGHADJ 4 +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define IMAGE_REL_BASED_ARM_MOV32A 5 /* yes, 5 too */ +#define IMAGE_REL_BASED_ARM_MOV32 5 /* yes, 5 too */ +#define IMAGE_REL_BASED_SECTION 6 +#define IMAGE_REL_BASED_REL 7 +#define IMAGE_REL_BASED_ARM_MOV32T 7 /* yes, 7 too */ +#define IMAGE_REL_BASED_THUMB_MOV32 7 /* yes, 7 too */ +#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 +#define IMAGE_REL_BASED_IA64_IMM64 9 /* yes, 9 too */ +#define IMAGE_REL_BASED_DIR64 10 +#define IMAGE_REL_BASED_HIGH3ADJ 11 + +/* ARM relocation types */ +#define IMAGE_REL_ARM_ABSOLUTE 0x0000 +#define IMAGE_REL_ARM_ADDR 0x0001 +#define IMAGE_REL_ARM_ADDR32NB 0x0002 +#define IMAGE_REL_ARM_BRANCH24 0x0003 +#define IMAGE_REL_ARM_BRANCH11 0x0004 +#define IMAGE_REL_ARM_TOKEN 0x0005 +#define IMAGE_REL_ARM_GPREL12 0x0006 +#define IMAGE_REL_ARM_GPREL7 0x0007 +#define IMAGE_REL_ARM_BLX24 0x0008 +#define IMAGE_REL_ARM_BLX11 0x0009 +#define IMAGE_REL_ARM_SECTION 0x000E +#define IMAGE_REL_ARM_SECREL 0x000F +#define IMAGE_REL_ARM_MOV32A 0x0010 +#define IMAGE_REL_ARM_MOV32T 0x0011 +#define IMAGE_REL_ARM_BRANCH20T 0x0012 +#define IMAGE_REL_ARM_BRANCH24T 0x0014 +#define IMAGE_REL_ARM_BLX23T 0x0015 + +/* ARM64 relocation types */ +#define IMAGE_REL_ARM64_ABSOLUTE 0x0000 +#define IMAGE_REL_ARM64_ADDR32 0x0001 +#define IMAGE_REL_ARM64_ADDR32NB 0x0002 +#define IMAGE_REL_ARM64_BRANCH26 0x0003 +#define IMAGE_REL_ARM64_PAGEBASE_REL21 0x0004 +#define IMAGE_REL_ARM64_REL21 0x0005 +#define IMAGE_REL_ARM64_PAGEOFFSET_12A 0x0006 +#define IMAGE_REL_ARM64_PAGEOFFSET_12L 0x0007 +#define IMAGE_REL_ARM64_SECREL 0x0008 +#define IMAGE_REL_ARM64_SECREL_LOW12A 0x0009 +#define IMAGE_REL_ARM64_SECREL_HIGH12A 0x000A +#define IMAGE_REL_ARM64_SECREL_LOW12L 0x000B +#define IMAGE_REL_ARM64_TOKEN 0x000C +#define IMAGE_REL_ARM64_SECTION 0x000D +#define IMAGE_REL_ARM64_ADDR64 0x000E + +/* AMD64 relocation types */ +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 +#define IMAGE_REL_AMD64_ADDR64 0x0001 +#define IMAGE_REL_AMD64_ADDR32 0x0002 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +#define IMAGE_REL_AMD64_REL32 0x0004 +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000A +#define IMAGE_REL_AMD64_SECREL 0x000B +#define IMAGE_REL_AMD64_SECREL7 0x000C +#define IMAGE_REL_AMD64_TOKEN 0x000D +#define IMAGE_REL_AMD64_SREL32 0x000E +#define IMAGE_REL_AMD64_PAIR 0x000F +#define IMAGE_REL_AMD64_SSPAN32 0x0010 + +#endif /* _PE_H */ diff --git a/lib/Kconfig b/lib/Kconfig index c7eab46..a67df3c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -129,5 +129,6 @@ config ERRNO_STR - if errno is negative - a pointer to errno related message source lib/efi/Kconfig +source lib/efi_loader/Kconfig endmenu diff --git a/lib/Makefile b/lib/Makefile index 1e21bcc..4aaa2ea 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -8,6 +8,7 @@ ifndef CONFIG_SPL_BUILD obj-$(CONFIG_EFI) += efi/ +obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_RSA) += rsa/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_LZO) += lzo/ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig new file mode 100644 index 0000000..6da1c7f --- /dev/null +++ b/lib/efi_loader/Kconfig @@ -0,0 +1,9 @@ +config EFI_LOADER + bool "Support running EFI Applications in U-Boot" + depends on ARM64 || ARM + default y + help + Select this option if you want to run EFI applications (like grub2) + on top of U-Boot. If this option is enabled, U-Boot will expose EFI + interfaces to a loaded EFI application, enabling it to reuse U-Boot's + device drivers. diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile new file mode 100644 index 0000000..28725a2 --- /dev/null +++ b/lib/efi_loader/Makefile @@ -0,0 +1,12 @@ +# +# (C) Copyright 2016 Alexander Graf +# +# SPDX-License-Identifier: GPL-2.0+ +# + +# This file only gets included with CONFIG_EFI_LOADER set, so all +# object inclusion implicitly depends on it + +obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o +obj-y += efi_memory.o +obj-$(CONFIG_PARTITIONS) += efi_disk.o diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c new file mode 100644 index 0000000..87400de --- /dev/null +++ b/lib/efi_loader/efi_boottime.c @@ -0,0 +1,781 @@ +/* + * EFI application boot time services + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG_EFI */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* This list contains all the EFI objects our payload has access to */ +LIST_HEAD(efi_obj_list); + +/* + * If we're running on nasty systems (32bit ARM booting into non-EFI Linux) + * we need to do trickery with caches. Since we don't want to break the EFI + * aware boot path, only apply hacks when loading exiting directly (breaking + * direct Linux EFI booting along the way - oh well). + */ +static bool efi_is_direct_boot = true; + +/* + * EFI can pass arbitrary additional "tables" containing vendor specific + * information to the payload. One such table is the FDT table which contains + * a pointer to a flattened device tree blob. + * + * In most cases we want to pass an FDT to the payload, so reserve one slot of + * config table space for it. The pointer gets populated by do_bootefi_exec(). + */ +static struct efi_configuration_table EFI_RUNTIME_DATA efi_conf_table[1]; + +/* + * The "gd" pointer lives in a register on ARM and AArch64 that we declare + * fixed when compiling U-Boot. However, the payload does not know about that + * restriction so we need to manually swap its and our view of that register on + * EFI callback entry/exit. + */ +static volatile void *efi_gd, *app_gd; + +/* Called from do_bootefi_exec() */ +void efi_save_gd(void) +{ + efi_gd = gd; +} + +/* Called on every callback entry */ +void efi_restore_gd(void) +{ + /* Only restore if we're already in EFI context */ + if (!efi_gd) + return; + + if (gd != efi_gd) + app_gd = gd; + gd = efi_gd; +} + +/* Called on every callback exit */ +efi_status_t efi_exit_func(efi_status_t ret) +{ + gd = app_gd; + return ret; +} + +static efi_status_t efi_unsupported(const char *funcname) +{ +#ifdef DEBUG_EFI + printf("EFI: App called into unimplemented function %s\n", funcname); +#endif + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2) +{ + return memcmp(g1, g2, sizeof(efi_guid_t)); +} + +static unsigned long EFIAPI efi_raise_tpl(unsigned long new_tpl) +{ + EFI_ENTRY("0x%lx", new_tpl); + return EFI_EXIT(0); +} + +static void EFIAPI efi_restore_tpl(unsigned long old_tpl) +{ + EFI_ENTRY("0x%lx", old_tpl); + EFI_EXIT(efi_unsupported(__func__)); +} + +efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type, + unsigned long pages, + uint64_t *memory) +{ + efi_status_t r; + + EFI_ENTRY("%d, %d, 0x%lx, %p", type, memory_type, pages, memory); + r = efi_allocate_pages(type, memory_type, pages, memory); + return EFI_EXIT(r); +} + +efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory, unsigned long pages) +{ + efi_status_t r; + + EFI_ENTRY("%"PRIx64", 0x%lx", memory, pages); + r = efi_free_pages(memory, pages); + return EFI_EXIT(r); +} + +efi_status_t EFIAPI efi_get_memory_map_ext(unsigned long *memory_map_size, + struct efi_mem_desc *memory_map, + unsigned long *map_key, + unsigned long *descriptor_size, + uint32_t *descriptor_version) +{ + efi_status_t r; + + EFI_ENTRY("%p, %p, %p, %p, %p", memory_map_size, memory_map, + map_key, descriptor_size, descriptor_version); + r = efi_get_memory_map(memory_map_size, memory_map, map_key, + descriptor_size, descriptor_version); + return EFI_EXIT(r); +} + +static efi_status_t EFIAPI efi_allocate_pool(int pool_type, unsigned long size, + void **buffer) +{ + return efi_allocate_pages(0, pool_type, (size + 0xfff) >> 12, (void*)buffer); +} + +static efi_status_t EFIAPI efi_free_pool(void *buffer) +{ + return efi_free_pages((ulong)buffer, 0); +} + +/* + * Our event capabilities are very limited. Only support a single + * event to exist, so we don't need to maintain lists. + */ +static struct { + enum efi_event_type type; + u32 trigger_type; + u32 trigger_time; + u64 trigger_next; + unsigned long notify_tpl; + void (*notify_function) (void *event, void *context); + void *notify_context; +} efi_event = { + /* Disable timers on bootup */ + .trigger_next = -1ULL, +}; + +static efi_status_t EFIAPI efi_create_event( + enum efi_event_type type, ulong notify_tpl, + void (*notify_function) (void *event, void *context), + void *notify_context, void **event) +{ + EFI_ENTRY("%d, 0x%lx, %p, %p", type, notify_tpl, notify_function, + notify_context); + if (efi_event.notify_function) { + /* We only support one event at a time */ + return EFI_EXIT(EFI_OUT_OF_RESOURCES); + } + + efi_event.type = type; + efi_event.notify_tpl = notify_tpl; + efi_event.notify_function = notify_function; + efi_event.notify_context = notify_context; + *event = &efi_event; + + return EFI_EXIT(EFI_SUCCESS); +} + +/* + * Our timers have to work without interrupts, so we check whenever keyboard + * input or disk accesses happen if enough time elapsed for it to fire. + */ +void efi_timer_check(void) +{ + u64 now = timer_get_us(); + + if (now >= efi_event.trigger_next) { + /* Triggering! */ + if (efi_event.trigger_type == EFI_TIMER_PERIODIC) + efi_event.trigger_next += efi_event.trigger_time / 10; + efi_event.notify_function(&efi_event, efi_event.notify_context); + } + + WATCHDOG_RESET(); +} + +static efi_status_t EFIAPI efi_set_timer(void *event, int type, + uint64_t trigger_time) +{ + /* We don't have 64bit division available everywhere, so limit timer + * distances to 32bit bits. */ + u32 trigger32 = trigger_time; + + EFI_ENTRY("%p, %d, %"PRIx64, event, type, trigger_time); + + if (trigger32 < trigger_time) { + printf("WARNING: Truncating timer from %"PRIx64" to %x\n", + trigger_time, trigger32); + } + + if (event != &efi_event) { + /* We only support one event at a time */ + return EFI_EXIT(EFI_INVALID_PARAMETER); + } + + switch (type) { + case EFI_TIMER_STOP: + efi_event.trigger_next = -1ULL; + break; + case EFI_TIMER_PERIODIC: + case EFI_TIMER_RELATIVE: + efi_event.trigger_next = timer_get_us() + (trigger32 / 10); + break; + default: + return EFI_EXIT(EFI_INVALID_PARAMETER); + } + efi_event.trigger_type = type; + efi_event.trigger_time = trigger_time; + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events, + void *event, unsigned long *index) +{ + u64 now; + + EFI_ENTRY("%ld, %p, %p", num_events, event, index); + + now = timer_get_us(); + while (now < efi_event.trigger_next) { } + efi_timer_check(); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_signal_event(void *event) +{ + EFI_ENTRY("%p", event); + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_close_event(void *event) +{ + EFI_ENTRY("%p", event); + efi_event.trigger_next = -1ULL; + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_check_event(void *event) +{ + EFI_ENTRY("%p", event); + return EFI_EXIT(EFI_NOT_READY); +} + +static efi_status_t EFIAPI efi_install_protocol_interface(void **handle, + efi_guid_t *protocol, int protocol_interface_type, + void *protocol_interface) +{ + EFI_ENTRY("%p, %p, %d, %p", handle, protocol, protocol_interface_type, + protocol_interface); + return EFI_EXIT(EFI_OUT_OF_RESOURCES); +} +static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle, + efi_guid_t *protocol, void *old_interface, + void *new_interface) +{ + EFI_ENTRY("%p, %p, %p, %p", handle, protocol, old_interface, + new_interface); + return EFI_EXIT(EFI_ACCESS_DENIED); +} + +static efi_status_t EFIAPI efi_uninstall_protocol_interface(void *handle, + efi_guid_t *protocol, void *protocol_interface) +{ + EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface); + return EFI_EXIT(EFI_NOT_FOUND); +} + +static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol, + void *event, + void **registration) +{ + EFI_ENTRY("%p, %p, %p", protocol, event, registration); + return EFI_EXIT(EFI_OUT_OF_RESOURCES); +} + +static int efi_search(enum efi_locate_search_type search_type, + efi_guid_t *protocol, void *search_key, + struct efi_object *efiobj) +{ + int i; + + switch (search_type) { + case all_handles: + return 0; + case by_register_notify: + return -1; + case by_protocol: + for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) { + const efi_guid_t *guid = efiobj->protocols[i].guid; + if (guid && !guidcmp(guid, protocol)) + return 0; + } + return -1; + } + + return -1; +} + +static efi_status_t EFIAPI efi_locate_handle( + enum efi_locate_search_type search_type, + efi_guid_t *protocol, void *search_key, + unsigned long *buffer_size, efi_handle_t *buffer) +{ + struct list_head *lhandle; + unsigned long size = 0; + + EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key, + buffer_size, buffer); + + /* Count how much space we need */ + list_for_each(lhandle, &efi_obj_list) { + struct efi_object *efiobj; + efiobj = list_entry(lhandle, struct efi_object, link); + if (!efi_search(search_type, protocol, search_key, efiobj)) { + size += sizeof(void*); + } + } + + if (*buffer_size < size) { + *buffer_size = size; + return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + } + + /* Then fill the array */ + list_for_each(lhandle, &efi_obj_list) { + struct efi_object *efiobj; + efiobj = list_entry(lhandle, struct efi_object, link); + if (!efi_search(search_type, protocol, search_key, efiobj)) { + *(buffer++) = efiobj->handle; + } + } + + *buffer_size = size; + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol, + struct efi_device_path **device_path, + efi_handle_t *device) +{ + EFI_ENTRY("%p, %p, %p", protocol, device_path, device); + return EFI_EXIT(EFI_NOT_FOUND); +} + +static efi_status_t EFIAPI efi_install_configuration_table(efi_guid_t *guid, + void *table) +{ + int i; + + EFI_ENTRY("%p, %p", guid, table); + + /* Check for guid override */ + for (i = 0; i < systab.nr_tables; i++) { + if (!guidcmp(guid, &efi_conf_table[i].guid)) { + efi_conf_table[i].table = table; + return EFI_EXIT(EFI_SUCCESS); + } + } + + /* No override, check for overflow */ + if (i >= ARRAY_SIZE(efi_conf_table)) + return EFI_EXIT(EFI_OUT_OF_RESOURCES); + + /* Add a new entry */ + memcpy(&efi_conf_table[i].guid, guid, sizeof(*guid)); + efi_conf_table[i].table = table; + systab.nr_tables = i; + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_load_image(bool boot_policy, + efi_handle_t parent_image, + struct efi_device_path *file_path, + void *source_buffer, + unsigned long source_size, + efi_handle_t *image_handle) +{ + static struct efi_object loaded_image_info_obj = { + .protocols = { + { + .guid = &efi_guid_loaded_image, + .open = &efi_return_handle, + }, + }, + }; + struct efi_loaded_image *info; + struct efi_object *obj; + + EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image, + file_path, source_buffer, source_size, image_handle); + info = malloc(sizeof(*info)); + obj = malloc(sizeof(loaded_image_info_obj)); + memset(info, 0, sizeof(*info)); + memcpy(obj, &loaded_image_info_obj, sizeof(loaded_image_info_obj)); + obj->handle = info; + info->file_path = file_path; + info->reserved = efi_load_pe(source_buffer, info); + if (!info->reserved) { + free(info); + free(obj); + return EFI_EXIT(EFI_UNSUPPORTED); + } + + *image_handle = info; + list_add_tail(&obj->link, &efi_obj_list); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, + unsigned long *exit_data_size, + s16 **exit_data) +{ + ulong (*entry)(void *image_handle, struct efi_system_table *st); + struct efi_loaded_image *info = image_handle; + + EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); + entry = info->reserved; + + efi_is_direct_boot = false; + + /* call the image! */ + entry(image_handle, &systab); + + /* Should usually never get here */ + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_exit(void *image_handle, long exit_status, + unsigned long exit_data_size, + uint16_t *exit_data) +{ + EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status, + exit_data_size, exit_data); + return EFI_EXIT(efi_unsupported(__func__)); +} + +static struct efi_object *efi_search_obj(void *handle) +{ + struct list_head *lhandle; + + list_for_each(lhandle, &efi_obj_list) { + struct efi_object *efiobj; + efiobj = list_entry(lhandle, struct efi_object, link); + if (efiobj->handle == handle) + return efiobj; + } + + return NULL; +} + +static efi_status_t EFIAPI efi_unload_image(void *image_handle) +{ + struct efi_object *efiobj; + + EFI_ENTRY("%p", image_handle); + efiobj = efi_search_obj(image_handle); + if (efiobj) + list_del(&efiobj->link); + + return EFI_EXIT(EFI_SUCCESS); +} + +static void efi_exit_caches(void) +{ +#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64) + /* + * Grub on 32bit ARM needs to have caches disabled before jumping into + * a zImage, but does not know of all cache layers. Give it a hand. + */ + if (efi_is_direct_boot) + cleanup_before_linux(); +#endif +} + +static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, + unsigned long map_key) +{ + EFI_ENTRY("%p, %ld", image_handle, map_key); + + /* Fix up caches for EFI payloads if necessary */ + efi_exit_caches(); + + /* This stops all lingering devices */ + bootm_disable_interrupts(); + + /* Give the payload some time to boot */ + WATCHDOG_RESET(); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count) +{ + static uint64_t mono = 0; + EFI_ENTRY("%p", count); + *count = mono++; + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_stall(unsigned long microseconds) +{ + EFI_ENTRY("%ld", microseconds); + udelay(microseconds); + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, + uint64_t watchdog_code, + unsigned long data_size, + uint16_t *watchdog_data) +{ + EFI_ENTRY("%ld, 0x%"PRIx64", %ld, %p", timeout, watchdog_code, + data_size, watchdog_data); + return EFI_EXIT(efi_unsupported(__func__)); +} + +static efi_status_t EFIAPI efi_connect_controller( + efi_handle_t controller_handle, + efi_handle_t *driver_image_handle, + struct efi_device_path *remain_device_path, + bool recursive) +{ + EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle, + remain_device_path, recursive); + return EFI_EXIT(EFI_NOT_FOUND); +} + +static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle, + void *driver_image_handle, + void *child_handle) +{ + EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle, + child_handle); + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + +static efi_status_t EFIAPI efi_close_protocol(void *handle, + efi_guid_t *protocol, + void *agent_handle, + void *controller_handle) +{ + EFI_ENTRY("%p, %p, %p, %p", handle, protocol, agent_handle, + controller_handle); + return EFI_EXIT(EFI_NOT_FOUND); +} + +static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle, + efi_guid_t *protocol, + struct efi_open_protocol_info_entry **entry_buffer, + unsigned long *entry_count) +{ + EFI_ENTRY("%p, %p, %p, %p", handle, protocol, entry_buffer, + entry_count); + return EFI_EXIT(EFI_NOT_FOUND); +} + +static efi_status_t EFIAPI efi_protocols_per_handle(void *handle, + efi_guid_t ***protocol_buffer, + unsigned long *protocol_buffer_count) +{ + EFI_ENTRY("%p, %p, %p", handle, protocol_buffer, + protocol_buffer_count); + return EFI_EXIT(EFI_OUT_OF_RESOURCES); +} + +static efi_status_t EFIAPI efi_locate_handle_buffer( + enum efi_locate_search_type search_type, + efi_guid_t *protocol, void *search_key, + unsigned long *no_handles, efi_handle_t **buffer) +{ + EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key, + no_handles, buffer); + return EFI_EXIT(EFI_NOT_FOUND); +} + +static struct efi_class_map efi_class_maps[] = { + { + .guid = &efi_guid_console_control, + .interface = &efi_console_control + }, +}; + +static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol, + void *registration, + void **protocol_interface) +{ + int i; + + EFI_ENTRY("%p, %p, %p", protocol, registration, protocol_interface); + for (i = 0; i < ARRAY_SIZE(efi_class_maps); i++) { + struct efi_class_map *curmap = &efi_class_maps[i]; + if (!guidcmp(protocol, curmap->guid)) { + *protocol_interface = (void*)curmap->interface; + return EFI_EXIT(EFI_SUCCESS); + } + } + + return EFI_EXIT(EFI_NOT_FOUND); +} + +static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( + void **handle, ...) +{ + EFI_ENTRY("%p", handle); + return EFI_EXIT(EFI_OUT_OF_RESOURCES); +} + +static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( + void *handle, ...) +{ + EFI_ENTRY("%p", handle); + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + +static efi_status_t EFIAPI efi_calculate_crc32(void *data, + unsigned long data_size, + uint32_t *crc32_p) +{ + EFI_ENTRY("%p, %ld", data, data_size); + *crc32_p = crc32(0, data, data_size); + return EFI_EXIT(EFI_SUCCESS); +} + +static void EFIAPI efi_copy_mem(void *destination, void *source, + unsigned long length) +{ + EFI_ENTRY("%p, %p, %ld", destination, source, length); + memcpy(destination, source, length); +} + +static void EFIAPI efi_set_mem(void *buffer, unsigned long size, uint8_t value) +{ + EFI_ENTRY("%p, %ld, 0x%x", buffer, size, value); + memset(buffer, value, size); +} + +static efi_status_t EFIAPI efi_open_protocol( + void *handle, efi_guid_t *protocol, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + struct list_head *lhandle; + int i; + efi_status_t r = EFI_UNSUPPORTED; + + EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol, + protocol_interface, agent_handle, controller_handle, + attributes); + list_for_each(lhandle, &efi_obj_list) { + struct efi_object *efiobj; + efiobj = list_entry(lhandle, struct efi_object, link); + + if (efiobj->handle != handle) + continue; + + for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) { + struct efi_handler *handler = &efiobj->protocols[i]; + const efi_guid_t *hprotocol = handler->guid; + if (!hprotocol) + break; + if (!guidcmp(hprotocol, protocol)) { + r = handler->open(handle, protocol, + protocol_interface, agent_handle, + controller_handle, attributes); + goto out; + } + } + } + +out: + return EFI_EXIT(r); +} + +static efi_status_t EFIAPI efi_handle_protocol(void *handle, + efi_guid_t *protocol, + void **protocol_interface) +{ + EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface); + return efi_open_protocol(handle, protocol, protocol_interface, + NULL, NULL, 0); +} + +static const struct efi_boot_services efi_boot_services = { + .hdr = { + .headersize = sizeof(struct efi_table_hdr), + }, + .raise_tpl = efi_raise_tpl, + .restore_tpl = efi_restore_tpl, + .allocate_pages = efi_allocate_pages_ext, + .free_pages = efi_free_pages_ext, + .get_memory_map = efi_get_memory_map_ext, + .allocate_pool = efi_allocate_pool, + .free_pool = efi_free_pool, + .create_event = efi_create_event, + .set_timer = efi_set_timer, + .wait_for_event = efi_wait_for_event, + .signal_event = efi_signal_event, + .close_event = efi_close_event, + .check_event = efi_check_event, + .install_protocol_interface = efi_install_protocol_interface, + .reinstall_protocol_interface = efi_reinstall_protocol_interface, + .uninstall_protocol_interface = efi_uninstall_protocol_interface, + .handle_protocol = efi_handle_protocol, + .reserved = NULL, + .register_protocol_notify = efi_register_protocol_notify, + .locate_handle = efi_locate_handle, + .locate_device_path = efi_locate_device_path, + .install_configuration_table = efi_install_configuration_table, + .load_image = efi_load_image, + .start_image = efi_start_image, + .exit = (void*)efi_exit, + .unload_image = efi_unload_image, + .exit_boot_services = efi_exit_boot_services, + .get_next_monotonic_count = efi_get_next_monotonic_count, + .stall = efi_stall, + .set_watchdog_timer = efi_set_watchdog_timer, + .connect_controller = efi_connect_controller, + .disconnect_controller = efi_disconnect_controller, + .open_protocol = efi_open_protocol, + .close_protocol = efi_close_protocol, + .open_protocol_information = efi_open_protocol_information, + .protocols_per_handle = efi_protocols_per_handle, + .locate_handle_buffer = efi_locate_handle_buffer, + .locate_protocol = efi_locate_protocol, + .install_multiple_protocol_interfaces = efi_install_multiple_protocol_interfaces, + .uninstall_multiple_protocol_interfaces = efi_uninstall_multiple_protocol_interfaces, + .calculate_crc32 = efi_calculate_crc32, + .copy_mem = efi_copy_mem, + .set_mem = efi_set_mem, +}; + + +static uint16_t EFI_RUNTIME_DATA firmware_vendor[] = + { 'D','a','s',' ','U','-','b','o','o','t',0 }; + +struct efi_system_table EFI_RUNTIME_DATA systab = { + .hdr = { + .signature = EFI_SYSTEM_TABLE_SIGNATURE, + .revision = 0x20005, /* 2.5 */ + .headersize = sizeof(struct efi_table_hdr), + }, + .fw_vendor = (long)firmware_vendor, + .con_in = (void*)&efi_con_in, + .con_out = (void*)&efi_con_out, + .std_err = (void*)&efi_con_out, + .runtime = (void*)&efi_runtime_services, + .boottime = (void*)&efi_boot_services, + .nr_tables = 0, + .tables = (void*)efi_conf_table, +}; diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c new file mode 100644 index 0000000..2e0228c --- /dev/null +++ b/lib/efi_loader/efi_console.c @@ -0,0 +1,360 @@ +/* + * EFI application console interface + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +/* If we can't determine the console size, default to 80x24 */ +static int console_columns = 80; +static int console_rows = 24; +static bool console_size_queried; + +const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID; + +#define cESC '\x1b' +#define ESC "\x1b" + +static efi_status_t EFIAPI efi_cin_get_mode( + struct efi_console_control_protocol *this, + int *mode, char *uga_exists, char *std_in_locked) +{ + EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked); + + if (mode) + *mode = EFI_CONSOLE_MODE_TEXT; + if (uga_exists) + *uga_exists = 0; + if (std_in_locked) + *std_in_locked = 0; + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_cin_set_mode( + struct efi_console_control_protocol *this, int mode) +{ + EFI_ENTRY("%p, %d", this, mode); + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static efi_status_t EFIAPI efi_cin_lock_std_in( + struct efi_console_control_protocol *this, + uint16_t *password) +{ + EFI_ENTRY("%p, %p", this, password); + return EFI_EXIT(EFI_UNSUPPORTED); +} + +const struct efi_console_control_protocol efi_console_control = { + .get_mode = efi_cin_get_mode, + .set_mode = efi_cin_set_mode, + .lock_std_in = efi_cin_lock_std_in, +}; + +static struct simple_text_output_mode efi_con_mode = { + .max_mode = 0, + .mode = 0, + .attribute = 0, + .cursor_column = 0, + .cursor_row = 0, + .cursor_visible = 1, +}; + +static int term_read_reply(int *n, int maxnum, char end_char) +{ + char c; + int i = 0; + + c = getc(); + if (c != cESC) + return -1; + c = getc(); + if (c != '[') + return -1; + + n[0] = 0; + while (1) { + c = getc(); + if (c == ';') { + i++; + if (i >= maxnum) + return -1; + n[i] = 0; + continue; + } else if (c == end_char) { + break; + } else if (c > '9' || c < '0') { + return -1; + } + + /* Read one more decimal position */ + n[i] *= 10; + n[i] += c - '0'; + } + + return 0; +} + +static efi_status_t EFIAPI efi_cout_reset( + struct efi_simple_text_output_protocol *this, + char extended_verification) +{ + EFI_ENTRY("%p, %d", this, extended_verification); + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static void print_unicode_in_utf8(u16 c) +{ + char utf8[4] = { 0 }; + char *b = utf8; + + if (c < 0x80) { + *(b++) = c; + } else if (c < 0x800) { + *(b++) = 192 + c / 64; + *(b++) = 128 + c % 64; + } else { + *(b++) = 224 + c / 4096; + *(b++) = 128 + c / 64 % 64; + *(b++) = 128 + c % 64; + } + + puts(utf8); +} + +static efi_status_t EFIAPI efi_cout_output_string( + struct efi_simple_text_output_protocol *this, + const unsigned short *string) +{ + u16 ch; + + EFI_ENTRY("%p, %p", this, string); + for (;(ch = *string); string++) { + print_unicode_in_utf8(ch); + efi_con_mode.cursor_column++; + if (ch == '\n') { + efi_con_mode.cursor_column = 1; + efi_con_mode.cursor_row++; + } else if (efi_con_mode.cursor_column > console_columns) { + efi_con_mode.cursor_column = 1; + efi_con_mode.cursor_row++; + } + if (efi_con_mode.cursor_row > console_rows) { + efi_con_mode.cursor_row = console_rows; + } + } + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_cout_test_string( + struct efi_simple_text_output_protocol *this, + const unsigned short *string) +{ + EFI_ENTRY("%p, %p", this, string); + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_cout_query_mode( + struct efi_simple_text_output_protocol *this, + unsigned long mode_number, unsigned long *columns, + unsigned long *rows) +{ + EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows); + + if (!console_size_queried) { + /* Ask the terminal about its size */ + int n[3]; + u64 timeout; + + console_size_queried = true; + + /* Empty input buffer */ + while (tstc()) + getc(); + + printf(ESC"[18t"); + + /* Check if we have a terminal that understands */ + timeout = timer_get_us() + 1000000; + while (!tstc()) + if (timer_get_us() > timeout) + goto out; + + /* Read {depth,rows,cols} */ + if (term_read_reply(n, 3, 't')) { + goto out; + } + + console_columns = n[2]; + console_rows = n[1]; + } + +out: + if (columns) + *columns = console_columns; + if (rows) + *rows = console_rows; + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_cout_set_mode( + struct efi_simple_text_output_protocol *this, + unsigned long mode_number) +{ + EFI_ENTRY("%p, %ld", this, mode_number); + + /* We only support text output for now */ + if (mode_number == EFI_CONSOLE_MODE_TEXT) + return EFI_EXIT(EFI_SUCCESS); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static efi_status_t EFIAPI efi_cout_set_attribute( + struct efi_simple_text_output_protocol *this, + unsigned long attribute) +{ + EFI_ENTRY("%p, %lx", this, attribute); + + /* Just ignore attributes (colors) for now */ + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static efi_status_t EFIAPI efi_cout_clear_screen( + struct efi_simple_text_output_protocol *this) +{ + EFI_ENTRY("%p", this); + + printf(ESC"[2J"); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_cout_set_cursor_position( + struct efi_simple_text_output_protocol *this, + unsigned long column, unsigned long row) +{ + EFI_ENTRY("%p, %ld, %ld", this, column, row); + + printf(ESC"[%d;%df", (int)row, (int)column); + efi_con_mode.cursor_column = column; + efi_con_mode.cursor_row = row; + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_cout_enable_cursor( + struct efi_simple_text_output_protocol *this, + bool enable) +{ + EFI_ENTRY("%p, %d", this, enable); + + printf(ESC"[?25%c", enable ? 'h' : 'l'); + + return EFI_EXIT(EFI_SUCCESS); +} + +const struct efi_simple_text_output_protocol efi_con_out = { + .reset = efi_cout_reset, + .output_string = efi_cout_output_string, + .test_string = efi_cout_test_string, + .query_mode = efi_cout_query_mode, + .set_mode = efi_cout_set_mode, + .set_attribute = efi_cout_set_attribute, + .clear_screen = efi_cout_clear_screen, + .set_cursor_position = efi_cout_set_cursor_position, + .enable_cursor = efi_cout_enable_cursor, + .mode = (void*)&efi_con_mode, +}; + +static efi_status_t EFIAPI efi_cin_reset( + struct efi_simple_input_interface *this, + bool extended_verification) +{ + EFI_ENTRY("%p, %d", this, extended_verification); + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static efi_status_t EFIAPI efi_cin_read_key_stroke( + struct efi_simple_input_interface *this, + struct efi_input_key *key) +{ + struct efi_input_key pressed_key = { + .scan_code = 0, + .unicode_char = 0, + }; + char ch; + + EFI_ENTRY("%p, %p", this, key); + + /* We don't do interrupts, so check for timers cooperatively */ + efi_timer_check(); + + if (!tstc()) { + /* No key pressed */ + return EFI_EXIT(EFI_NOT_READY); + } + + ch = getc(); + if (ch == cESC) { + /* Escape Sequence */ + ch = getc(); + switch (ch) { + case cESC: /* ESC */ + pressed_key.scan_code = 23; + break; + case 'O': /* F1 - F4 */ + pressed_key.scan_code = getc() - 'P' + 11; + break; + case 'a'...'z': + ch = ch - 'a'; + break; + case '[': + ch = getc(); + switch (ch) { + case 'A'...'D': /* up, down right, left */ + pressed_key.scan_code = ch - 'A' + 1; + break; + case 'F': /* End */ + pressed_key.scan_code = 6; + break; + case 'H': /* Home */ + pressed_key.scan_code = 5; + break; + case '1': /* F5 - F8 */ + pressed_key.scan_code = getc() - '0' + 11; + getc(); + break; + case '2': /* F9 - F12 */ + pressed_key.scan_code = getc() - '0' + 19; + getc(); + break; + case '3': /* DEL */ + pressed_key.scan_code = 8; + getc(); + break; + } + break; + } + } else if (ch == 0x7f) { + /* Backspace */ + ch = 0x08; + } + pressed_key.unicode_char = ch; + *key = pressed_key; + + return EFI_EXIT(EFI_SUCCESS); +} + +const struct efi_simple_input_interface efi_con_in = { + .reset = efi_cin_reset, + .read_key_stroke = efi_cin_read_key_stroke, + .wait_for_key = NULL, +}; diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c new file mode 100644 index 0000000..f93fcb2 --- /dev/null +++ b/lib/efi_loader/efi_disk.c @@ -0,0 +1,218 @@ +/* + * EFI application disk support + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID; + +struct efi_disk_obj { + /* Generic EFI object parent class data */ + struct efi_object parent; + /* EFI Interface callback struct for block I/O */ + struct efi_block_io ops; + /* U-Boot ifname for block device */ + const char *ifname; + /* U-Boot dev_index for block device */ + int dev_index; + /* EFI Interface Media descriptor struct, referenced by ops */ + struct efi_block_io_media media; + /* EFI device path to this block device */ + struct efi_device_path_file_path *dp; +}; + +static void ascii2unicode(u16 *unicode, char *ascii) +{ + while (*ascii) + *(unicode++) = *(ascii++); +} + +static efi_status_t efi_disk_open_block(void *handle, efi_guid_t *protocol, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + struct efi_disk_obj *diskobj = handle; + + *protocol_interface = &diskobj->ops; + + return EFI_SUCCESS; +} + +static efi_status_t efi_disk_open_dp(void *handle, efi_guid_t *protocol, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + struct efi_disk_obj *diskobj = handle; + + *protocol_interface = diskobj->dp; + + return EFI_SUCCESS; +} + +static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this, + char extended_verification) +{ + EFI_ENTRY("%p, %x", this, extended_verification); + return EFI_EXIT(EFI_DEVICE_ERROR); +} + +enum efi_disk_direction { + EFI_DISK_READ, + EFI_DISK_WRITE, +}; + +static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this, + u32 media_id, u64 lba, unsigned long buffer_size, + void *buffer, enum efi_disk_direction direction) +{ + struct efi_disk_obj *diskobj; + struct block_dev_desc *desc; + int blksz; + int blocks; + unsigned long n; + + EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba, + buffer_size, buffer); + + diskobj = container_of(this, struct efi_disk_obj, ops); + if (!(desc = get_dev(diskobj->ifname, diskobj->dev_index))) + return EFI_EXIT(EFI_DEVICE_ERROR); + blksz = desc->blksz; + blocks = buffer_size / blksz; + +#ifdef DEBUG_EFI + printf("EFI: %s:%d blocks=%x lba=%"PRIx64" blksz=%x dir=%d\n", __func__, + __LINE__, blocks, lba, blksz, direction); +#endif + + /* We only support full block access */ + if (buffer_size & (blksz - 1)) + return EFI_EXIT(EFI_DEVICE_ERROR); + + if (direction == EFI_DISK_READ) + n = desc->block_read(desc, lba, blocks, buffer); + else + n = desc->block_write(desc, lba, blocks, buffer); + + /* We don't do interrupts, so check for timers cooperatively */ + efi_timer_check(); + +#ifdef DEBUG_EFI + printf("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks); +#endif + if (n != blocks) + return EFI_EXIT(EFI_DEVICE_ERROR); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t efi_disk_read_blocks(struct efi_block_io *this, + u32 media_id, u64 lba, unsigned long buffer_size, + void *buffer) +{ + return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer, + EFI_DISK_READ); +} + +static efi_status_t efi_disk_write_blocks(struct efi_block_io *this, + u32 media_id, u64 lba, unsigned long buffer_size, + void *buffer) +{ + return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer, + EFI_DISK_WRITE); +} + +static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this) +{ + /* We always write synchronously */ + EFI_ENTRY("%p", this); + return EFI_EXIT(EFI_SUCCESS); +} + +static const struct efi_block_io block_io_disk_template = { + .reset = &efi_disk_reset, + .read_blocks = &efi_disk_read_blocks, + .write_blocks = &efi_disk_write_blocks, + .flush_blocks = &efi_disk_flush_blocks, +}; + +/* + * U-Boot doesn't have a list of all online disk devices. So when running our + * EFI payload, we scan through all of the potentially available ones and + * store them in our object pool. + * + * This gets called from do_bootefi_exec(). + */ +int efi_disk_register(void) +{ + const struct block_drvr *cur_drvr; + int i; + int disks = 0; + + /* Search for all available disk devices */ + for (cur_drvr = block_drvr; cur_drvr->name; cur_drvr++) { + printf("Scanning disks on %s...\n", cur_drvr->name); + for (i = 0; i < 4; i++) { + block_dev_desc_t *desc; + struct efi_disk_obj *diskobj; + struct efi_device_path_file_path *dp; + int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2); + char devname[16] = { 0 }; /* dp->str is u16[16] long */ + + desc = get_dev(cur_drvr->name, i); + if (!desc) + continue; + if (desc->type == DEV_TYPE_UNKNOWN) + continue; + + diskobj = calloc(1, objlen); + + /* Fill in object data */ + diskobj->parent.protocols[0].guid = &efi_block_io_guid; + diskobj->parent.protocols[0].open = efi_disk_open_block; + diskobj->parent.protocols[1].guid = &efi_guid_device_path; + diskobj->parent.protocols[1].open = efi_disk_open_dp; + diskobj->parent.handle = diskobj; + diskobj->ops = block_io_disk_template; + diskobj->ifname = cur_drvr->name; + diskobj->dev_index = i; + + /* Fill in EFI IO Media info (for read/write callbacks) */ + diskobj->media.removable_media = desc->removable; + diskobj->media.media_present = 1; + diskobj->media.block_size = desc->blksz; + diskobj->media.io_align = desc->blksz; + diskobj->media.last_block = desc->lba; + diskobj->ops.media = &diskobj->media; + + /* Fill in device path */ + dp = (void*)&diskobj[1]; + diskobj->dp = dp; + dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; + dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; + dp[0].dp.length = sizeof(*dp); + snprintf(devname, sizeof(devname), "%s%d", + cur_drvr->name, i); + ascii2unicode(dp[0].str, devname); + + dp[1].dp.type = DEVICE_PATH_TYPE_END; + dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END; + dp[1].dp.length = sizeof(*dp); + + /* Hook up to the device list */ + list_add_tail(&diskobj->parent.link, &efi_obj_list); + disks++; + } + } + printf("Found %d disks\n", disks); + + return 0; +} diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c new file mode 100644 index 0000000..4479726 --- /dev/null +++ b/lib/efi_loader/efi_image_loader.c @@ -0,0 +1,183 @@ +/* + * EFI image loader + * + * based partly on wine code + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID; +const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID; + +efi_status_t EFIAPI efi_return_handle(void *handle, efi_guid_t *protocol, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol, + protocol_interface, agent_handle, controller_handle, + attributes); + *protocol_interface = handle; + return EFI_EXIT(EFI_SUCCESS); +} + +static void efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel, + unsigned long rel_size, void *efi_reloc) +{ + const IMAGE_BASE_RELOCATION *end; + int i; + + end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size); + while (rel < end - 1 && rel->SizeOfBlock) { + const uint16_t *relocs = (const uint16_t *)(rel + 1); + i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t); + while (i--) { + uint16_t offset = (*relocs & 0xfff) + + rel->VirtualAddress; + int type = *relocs >> EFI_PAGE_SHIFT; + unsigned long delta = (unsigned long)efi_reloc; + uint64_t *x64 = efi_reloc + offset; + uint32_t *x32 = efi_reloc + offset; + uint16_t *x16 = efi_reloc + offset; + + switch (type) { + case IMAGE_REL_BASED_ABSOLUTE: + break; + case IMAGE_REL_BASED_HIGH: + *x16 += ((uint32_t)delta) >> 16; + break; + case IMAGE_REL_BASED_LOW: + *x16 += (uint16_t)delta; + break; + case IMAGE_REL_BASED_HIGHLOW: + *x32 += (uint32_t)delta; + break; + case IMAGE_REL_BASED_DIR64: + *x64 += (uint64_t)delta; + break; + default: + printf("Unknown Relocation off %x type %x\n", + offset, type); + } + relocs++; + } + rel = (const IMAGE_BASE_RELOCATION *)relocs; + } +} + +/* + * This function loads all sections from a PE binary into a newly reserved + * piece of memory. On successful load it then returns the entry point for + * the binary. Otherwise NULL. + */ +void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info) +{ + IMAGE_NT_HEADERS32 *nt; + IMAGE_DOS_HEADER *dos; + IMAGE_SECTION_HEADER *sections; + int num_sections; + void *efi_reloc; + int i; + const IMAGE_BASE_RELOCATION *rel; + unsigned long rel_size; + int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC; + void *entry; + uint64_t image_size; + unsigned long virt_size = 0; + bool can_run_nt64 = true; + bool can_run_nt32 = true; + +#if defined(CONFIG_ARM64) + can_run_nt32 = false; +#elif defined(CONFIG_ARM) + can_run_nt64 = false; +#endif + + dos = efi; + if (dos->e_magic != IMAGE_DOS_SIGNATURE) { + printf("%s: Invalid DOS Signature\n", __func__); + return NULL; + } + + nt = (void *) ((char *)efi + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) { + printf("%s: Invalid NT Signature\n", __func__); + return NULL; + } + + /* Calculate upper virtual address boundary */ + num_sections = nt->FileHeader.NumberOfSections; + sections = (void *)&nt->OptionalHeader + + nt->FileHeader.SizeOfOptionalHeader; + + for (i = num_sections - 1; i >= 0; i--) { + IMAGE_SECTION_HEADER *sec = §ions[i]; + virt_size = max_t(unsigned long, virt_size, + sec->VirtualAddress + sec->Misc.VirtualSize); + } + + /* Read 32/64bit specific header bits */ + if (can_run_nt64 && + (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)) { + IMAGE_NT_HEADERS64 *nt64 = (void *)nt; + IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader; + image_size = opt->SizeOfImage; + efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA); + if (!efi_reloc) { + printf("%s: Could not allocate %ld bytes\n", + __func__, virt_size); + return NULL; + } + entry = efi_reloc + opt->AddressOfEntryPoint; + rel_size = opt->DataDirectory[rel_idx].Size; + rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress; + } else if (can_run_nt32 && + (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) { + IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader; + image_size = opt->SizeOfImage; + efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA); + if (!efi_reloc) { + printf("%s: Could not allocate %ld bytes\n", + __func__, virt_size); + return NULL; + } + entry = efi_reloc + opt->AddressOfEntryPoint; + rel_size = opt->DataDirectory[rel_idx].Size; + rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress; + } else { + printf("%s: Invalid optional header magic %x\n", __func__, + nt->OptionalHeader.Magic); + return NULL; + } + + /* Load sections into RAM */ + for (i = num_sections - 1; i >= 0; i--) { + IMAGE_SECTION_HEADER *sec = §ions[i]; + memset(efi_reloc + sec->VirtualAddress, 0, + sec->Misc.VirtualSize); + memcpy(efi_reloc + sec->VirtualAddress, + efi + sec->PointerToRawData, + sec->SizeOfRawData); + } + + /* Run through relocations */ + efi_loader_relocate(rel, rel_size, efi_reloc); + + /* Flush cache */ + flush_cache((ulong)efi_reloc, virt_size); + invalidate_icache_all(); + + /* Populate the loaded image interface bits */ + loaded_image_info->image_base = efi; + loaded_image_info->image_size = image_size; + + return entry; +} diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c new file mode 100644 index 0000000..c82b53f --- /dev/null +++ b/lib/efi_loader/efi_memory.c @@ -0,0 +1,319 @@ +/* + * EFI application memory management + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* #define DEBUG_EFI */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct efi_mem_list { + struct list_head link; + struct efi_mem_desc desc; +}; + +/* This list contains all memory map items */ +LIST_HEAD(efi_mem); + +/* + * Unmaps all memory occupied by the carve_desc region from the + * list entry pointed to by map. + * + * Returns 1 if carving was performed or 0 if the regions don't overlap. + * Returns -1 if it would affect non-RAM regions but overlap_only_ram is set. + * Carving is only guaranteed to complete when all regions return 0. + */ +static int efi_mem_carve_out(struct efi_mem_list *map, + struct efi_mem_desc *carve_desc, + bool overlap_only_ram) +{ + struct efi_mem_list *newmap; + struct efi_mem_desc *map_desc = &map->desc; + uint64_t map_start = map_desc->physical_start; + uint64_t map_end = map_start + (map_desc->num_pages << EFI_PAGE_SHIFT); + uint64_t carve_start = carve_desc->physical_start; + uint64_t carve_end = carve_start + + (carve_desc->num_pages << EFI_PAGE_SHIFT); + + /* check whether we're overlapping */ + if ((carve_end <= map_start) || (carve_start >= map_end)) + return 0; + + /* We're overlapping with non-RAM, warn the caller if desired */ + if (overlap_only_ram && (map_desc->type != EFI_CONVENTIONAL_MEMORY)) + return -1; + + /* Sanitize carve_start and carve_end to lie within our bounds */ + carve_start = max(carve_start, map_start); + carve_end = min(carve_end, map_end); + + /* Carving at the beginning of our map? Just move it! */ + if (carve_start == map_start) { + if (map_end == carve_end) { + /* Full overlap, just remove map */ + list_del(&map->link); + } + + map_desc->physical_start = carve_end; + map_desc->num_pages = (map_end - carve_end) >> EFI_PAGE_SHIFT; + return 1; + } + + /* + * Overlapping maps, just split the list map at carve_start, + * it will get moved or removed in the next iteration. + * + * [ map_desc |__carve_start__| newmap ] + */ + + /* Create a new map from [ carve_start ... map_end ] */ + newmap = calloc(1, sizeof(*newmap)); + newmap->desc = map->desc; + newmap->desc.physical_start = carve_start; + newmap->desc.num_pages = (map_end - carve_start) >> EFI_PAGE_SHIFT; + list_add_tail(&newmap->link, &efi_mem); + + /* Shrink the map to [ map_start ... carve_start ] */ + map_desc->num_pages = (carve_start - map_start) >> EFI_PAGE_SHIFT; + + return 1; +} + +uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, + bool overlap_only_ram) +{ + struct list_head *lhandle; + struct efi_mem_list *newlist; + bool do_carving; + + if (!pages) + return start; + + newlist = calloc(1, sizeof(*newlist)); + newlist->desc.type = memory_type; + newlist->desc.physical_start = start; + newlist->desc.virtual_start = start; + newlist->desc.num_pages = pages; + + switch (memory_type) { + case EFI_RUNTIME_SERVICES_CODE: + case EFI_RUNTIME_SERVICES_DATA: + newlist->desc.attribute = (1 << EFI_MEMORY_WB_SHIFT) | + (1ULL << EFI_MEMORY_RUNTIME_SHIFT); + break; + case EFI_MMAP_IO: + newlist->desc.attribute = 1ULL << EFI_MEMORY_RUNTIME_SHIFT; + break; + default: + newlist->desc.attribute = 1 << EFI_MEMORY_WB_SHIFT; + break; + } + + /* Add our new map */ + do { + do_carving = false; + list_for_each(lhandle, &efi_mem) { + struct efi_mem_list *lmem; + int r; + + lmem = list_entry(lhandle, struct efi_mem_list, link); + r = efi_mem_carve_out(lmem, &newlist->desc, + overlap_only_ram); + if (r < 0) { + return 0; + } else if (r) { + do_carving = true; + break; + } + } + } while (do_carving); + + /* Add our new map */ + list_add_tail(&newlist->link, &efi_mem); + + return start; +} + +static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr) +{ + struct list_head *lhandle; + + list_for_each(lhandle, &efi_mem) { + struct efi_mem_list *lmem = list_entry(lhandle, + struct efi_mem_list, link); + struct efi_mem_desc *desc = &lmem->desc; + uint64_t desc_len = desc->num_pages << EFI_PAGE_SHIFT; + uint64_t desc_end = desc->physical_start + desc_len; + uint64_t curmax = min(max_addr, desc_end); + uint64_t ret = curmax - len; + + /* We only take memory from free RAM */ + if (desc->type != EFI_CONVENTIONAL_MEMORY) + continue; + + /* Out of bounds for max_addr */ + if ((ret + len) > max_addr) + continue; + + /* Out of bounds for upper map limit */ + if ((ret + len) > desc_end) + continue; + + /* Out of bounds for lower map limit */ + if (ret < desc->physical_start) + continue; + + /* Return the highest address in this map within bounds */ + return ret; + } + + return 0; +} + +efi_status_t efi_allocate_pages(int type, int memory_type, + unsigned long pages, uint64_t *memory) +{ + u64 len = pages << EFI_PAGE_SHIFT; + efi_status_t r = EFI_SUCCESS; + uint64_t addr; + + switch (type) { + case 0: + /* Any page */ + addr = efi_find_free_memory(len, gd->ram_top); + if (!addr) { + r = EFI_NOT_FOUND; + break; + } + break; + case 1: + /* Max address */ + addr = efi_find_free_memory(len, *memory); + if (!addr) { + r = EFI_NOT_FOUND; + break; + } + break; + case 2: + /* Exact address, reserve it. The addr is already in *memory. */ + addr = *memory; + break; + default: + /* UEFI doesn't specify other allocation types */ + r = EFI_INVALID_PARAMETER; + break; + } + + if (r == EFI_SUCCESS) { + uint64_t ret; + + /* Reserve that map in our memory maps */ + ret = efi_add_memory_map(addr, pages, memory_type, true); + if (ret == addr) { + *memory = addr; + } else { + /* Map would overlap, bail out */ + r = EFI_OUT_OF_RESOURCES; + } + } + + return r; +} + +void *efi_alloc(uint64_t len, int memory_type) +{ + uint64_t ret = 0; + uint64_t pages = (len + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; + efi_status_t r; + + r = efi_allocate_pages(0, memory_type, pages, &ret); + if (r == EFI_SUCCESS) + return (void*)(uintptr_t)ret; + + return NULL; +} + +efi_status_t efi_free_pages(uint64_t memory, unsigned long pages) +{ + /* We don't free, let's cross our fingers we have plenty RAM */ + return EFI_SUCCESS; +} + +efi_status_t efi_get_memory_map(unsigned long *memory_map_size, + struct efi_mem_desc *memory_map, + unsigned long *map_key, + unsigned long *descriptor_size, + uint32_t *descriptor_version) +{ + ulong map_size = 0; + struct list_head *lhandle; + + list_for_each(lhandle, &efi_mem) + map_size += sizeof(struct efi_mem_desc); + + *memory_map_size = map_size; + + if (descriptor_size) + *descriptor_size = sizeof(struct efi_mem_desc); + + if (*memory_map_size < map_size) + return EFI_BUFFER_TOO_SMALL; + + /* Copy list into array */ + if (memory_map) { + list_for_each(lhandle, &efi_mem) { + struct efi_mem_list *lmem; + + lmem = list_entry(lhandle, struct efi_mem_list, link); + *memory_map = lmem->desc; + memory_map++; + } + } + + return EFI_SUCCESS; +} + +int efi_memory_init(void) +{ + uint64_t runtime_start, runtime_end, runtime_pages; + uint64_t uboot_start, uboot_pages; + uint64_t uboot_stack_size = 16 * 1024 * 1024; + int i; + + /* Add RAM */ + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + u64 ram_start = gd->bd->bi_dram[i].start; + u64 ram_size = gd->bd->bi_dram[i].size; + u64 start = (ram_start + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + u64 pages = (ram_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT; + + efi_add_memory_map(start, pages, EFI_CONVENTIONAL_MEMORY, + false); + } + + /* Add U-Boot */ + uboot_start = (gd->start_addr_sp - uboot_stack_size) & ~EFI_PAGE_MASK; + uboot_pages = (gd->ram_top - uboot_start) >> EFI_PAGE_SHIFT; + efi_add_memory_map(uboot_start, uboot_pages, EFI_LOADER_DATA, false); + + /* Add Runtime Services */ + runtime_start = (ulong)&__efi_runtime_start & ~EFI_PAGE_MASK; + runtime_end = (ulong)&__efi_runtime_stop; + runtime_end = (runtime_end + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; + runtime_pages = (runtime_end - runtime_start) >> EFI_PAGE_SHIFT; + efi_add_memory_map(runtime_start, runtime_pages, + EFI_RUNTIME_SERVICES_CODE, false); + + return 0; +} diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c new file mode 100644 index 0000000..22bcd08 --- /dev/null +++ b/lib/efi_loader/efi_runtime.c @@ -0,0 +1,290 @@ +/* + * EFI application runtime services + * + * Copyright (c) 2016 Alexander Graf + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +/* For manual relocation support */ +DECLARE_GLOBAL_DATA_PTR; + +static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void); +static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void); +static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void); + +#if defined(CONFIG_ARM64) +#define R_RELATIVE 1027 +#define R_MASK 0xffffffffULL +#define IS_RELA 1 +#elif defined(CONFIG_ARM) +#define R_RELATIVE 23 +#define R_MASK 0xffULL +#else +#error Need to add relocation awareness +#endif + +struct elf_rel { + ulong *offset; + ulong info; +}; + +struct elf_rela { + ulong *offset; + ulong info; + long addend; +}; + +/* + * EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI + * payload are running concurrently at the same time. In this mode, we can + * handle a good number of runtime callbacks + */ + +static void EFIAPI efi_reset_system(enum efi_reset_type reset_type, + efi_status_t reset_status, + unsigned long data_size, void *reset_data) +{ + EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size, + reset_data); + + switch (reset_type) { + case EFI_RESET_COLD: + case EFI_RESET_WARM: + do_reset(NULL, 0, 0, NULL); + break; + case EFI_RESET_SHUTDOWN: + /* We don't have anything to map this to */ + break; + } + + EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_get_time(struct efi_time *time, + struct efi_time_cap *capabilities) +{ +#if defined(CONFIG_CMD_DATE) && defined(CONFIG_DM_RTC) + struct rtc_time tm; + int r; + struct udevice *dev; + + EFI_ENTRY("%p %p", time, capabilities); + + r = uclass_get_device(UCLASS_RTC, 0, &dev); + if (r) + return EFI_EXIT(EFI_DEVICE_ERROR); + + r = dm_rtc_get(dev, &tm); + if (r) + return EFI_EXIT(EFI_DEVICE_ERROR); + + memset(time, 0, sizeof(*time)); + time->year = tm.tm_year; + time->month = tm.tm_mon; + time->day = tm.tm_mday; + time->hour = tm.tm_hour; + time->minute = tm.tm_min; + time->daylight = tm.tm_isdst; + + return EFI_EXIT(EFI_SUCCESS); +#else + return EFI_DEVICE_ERROR; +#endif +} + +struct efi_runtime_detach_list_struct { + void *ptr; + void *patchto; +}; + +static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = { + { + /* do_reset is gone */ + .ptr = &efi_runtime_services.reset_system, + .patchto = NULL, + }, { + /* invalidate_*cache_all are gone */ + .ptr = &efi_runtime_services.set_virtual_address_map, + .patchto = &efi_invalid_parameter, + }, { + /* RTC accessors are gone */ + .ptr = &efi_runtime_services.get_time, + .patchto = &efi_device_error, + }, +}; + +static bool efi_runtime_tobedetached(void *p) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) + if (efi_runtime_detach_list[i].ptr == p) + return true; + + return false; +} + +static void efi_runtime_detach(ulong offset) +{ + int i; + ulong patchoff = offset - (ulong)gd->relocaddr; + + for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) { + ulong patchto = (ulong)efi_runtime_detach_list[i].patchto; + ulong *p = efi_runtime_detach_list[i].ptr; + ulong newaddr = patchto ? (patchto + patchoff) : 0; + +#ifdef DEBUG_EFI + printf("%s: Setting %p to %lx\n", __func__, p, newaddr); +#endif + *p = newaddr; + } +} + +/* Relocate EFI runtime to uboot_reloc_base = offset */ +void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map) +{ +#ifdef IS_RELA + struct elf_rela *rel = (void*)&__efi_runtime_rel_start; +#else + struct elf_rel *rel = (void*)&__efi_runtime_rel_start; + static ulong lastoff = CONFIG_SYS_TEXT_BASE; +#endif + +#ifdef DEBUG_EFI + printf("%s: Relocating to offset=%lx\n", __func__, offset); +#endif + + for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) { + ulong base = CONFIG_SYS_TEXT_BASE; + ulong *p; + ulong newaddr; + + p = (void*)((ulong)rel->offset - base) + gd->relocaddr; + + if ((rel->info & R_MASK) != R_RELATIVE) { + continue; + } + +#ifdef IS_RELA + newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE; +#else + newaddr = *p - lastoff + offset; +#endif + + /* Check if the relocation is inside bounds */ + if (map && ((newaddr < map->virtual_start) || + newaddr > (map->virtual_start + (map->num_pages << 12)))) { + if (!efi_runtime_tobedetached(p)) + printf("U-Boot EFI: Relocation at %p is out of " + "range (%lx)\n", p, newaddr); + continue; + } + +#ifdef DEBUG_EFI + printf("%s: Setting %p to %lx\n", __func__, p, newaddr); +#endif + + *p = newaddr; + flush_dcache_range((ulong)p, (ulong)&p[1]); + } + +#ifndef IS_RELA + lastoff = offset; +#endif + + invalidate_icache_all(); +} + +static efi_status_t EFIAPI efi_set_virtual_address_map( + unsigned long memory_map_size, + unsigned long descriptor_size, + uint32_t descriptor_version, + struct efi_mem_desc *virtmap) +{ + ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL; + int n = memory_map_size / descriptor_size; + int i; + + EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size, + descriptor_version, virtmap); + + for (i = 0; i < n; i++) { + struct efi_mem_desc *map; + + map = (void*)virtmap + (descriptor_size * i); + if (map->type == EFI_RUNTIME_SERVICES_CODE) { + ulong new_offset = map->virtual_start - (runtime_start - gd->relocaddr); + + efi_runtime_relocate(new_offset, map); + /* Once we're virtual, we can no longer handle + complex callbacks */ + efi_runtime_detach(new_offset); + return EFI_EXIT(EFI_SUCCESS); + } + } + + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + +/* + * In the second stage, U-Boot has disappeared. To isolate our runtime code + * that at this point still exists from the rest, we put it into a special + * section. + * + * !!WARNING!! + * + * This means that we can not rely on any code outside of this file in any + * function or variable below this line. + * + * Please keep everything fully self-contained and annotated with + * EFI_RUNTIME_TEXT and EFI_RUNTIME_DATA markers. + */ + +/* + * Relocate the EFI runtime stub to a different place. We need to call this + * the first time we expose the runtime interface to a user and on set virtual + * address map calls. + */ + +static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_unimplemented(void) +{ + return EFI_UNSUPPORTED; +} + +static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_device_error(void) +{ + return EFI_DEVICE_ERROR; +} + +static efi_status_t EFI_RUNTIME_TEXT EFIAPI efi_invalid_parameter(void) +{ + return EFI_INVALID_PARAMETER; +} + +struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = { + .hdr = { + .signature = EFI_RUNTIME_SERVICES_SIGNATURE, + .revision = EFI_RUNTIME_SERVICES_REVISION, + .headersize = sizeof(struct efi_table_hdr), + }, + .get_time = &efi_get_time, + .set_time = (void *)&efi_device_error, + .get_wakeup_time = (void *)&efi_unimplemented, + .set_wakeup_time = (void *)&efi_unimplemented, + .set_virtual_address_map = &efi_set_virtual_address_map, + .convert_pointer = (void *)&efi_invalid_parameter, + .get_variable = (void *)&efi_device_error, + .get_next_variable = (void *)&efi_device_error, + .set_variable = (void *)&efi_device_error, + .get_next_high_mono_count = (void *)&efi_device_error, + .reset_system = &efi_reset_system, +};