mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
Merge branch 'devel-stable' into for-linus
This commit is contained in:
commit
6660800fb7
@ -20,6 +20,7 @@ config ARM
|
||||
select GENERIC_ALLOCATOR
|
||||
select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI)
|
||||
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
|
||||
select GENERIC_EARLY_IOREMAP
|
||||
select GENERIC_IDLE_POLL_SETUP
|
||||
select GENERIC_IRQ_PROBE
|
||||
select GENERIC_IRQ_SHOW
|
||||
@ -2060,6 +2061,25 @@ config AUTO_ZRELADDR
|
||||
0xf8000000. This assumes the zImage being placed in the first 128MB
|
||||
from start of memory.
|
||||
|
||||
config EFI_STUB
|
||||
bool
|
||||
|
||||
config EFI
|
||||
bool "UEFI runtime support"
|
||||
depends on OF && !CPU_BIG_ENDIAN && MMU && AUTO_ZRELADDR && !XIP_KERNEL
|
||||
select UCS2_STRING
|
||||
select EFI_PARAMS_FROM_FDT
|
||||
select EFI_STUB
|
||||
select EFI_ARMSTUB
|
||||
select EFI_RUNTIME_WRAPPERS
|
||||
---help---
|
||||
This option provides support for runtime services provided
|
||||
by UEFI firmware (such as non-volatile variables, realtime
|
||||
clock, and platform reset). A UEFI stub is also provided to
|
||||
allow the kernel to be booted as an EFI application. This
|
||||
is only useful for kernels that may run on systems that have
|
||||
UEFI firmware.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "CPU Power Management"
|
||||
|
@ -167,9 +167,11 @@ if [ $(words $(ZRELADDR)) -gt 1 -a "$(CONFIG_AUTO_ZRELADDR)" = "" ]; then \
|
||||
false; \
|
||||
fi
|
||||
|
||||
efi-obj-$(CONFIG_EFI_STUB) := $(objtree)/drivers/firmware/efi/libstub/lib.a
|
||||
|
||||
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \
|
||||
$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \
|
||||
$(bswapsdi2) FORCE
|
||||
$(bswapsdi2) $(efi-obj-y) FORCE
|
||||
@$(check_for_multiple_zreladdr)
|
||||
$(call if_changed,ld)
|
||||
@$(check_for_bad_syms)
|
||||
|
130
arch/arm/boot/compressed/efi-header.S
Normal file
130
arch/arm/boot/compressed/efi-header.S
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2015 Linaro Ltd
|
||||
* Authors: Roy Franz <roy.franz@linaro.org>
|
||||
* Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
.macro __nop
|
||||
#ifdef CONFIG_EFI_STUB
|
||||
@ This is almost but not quite a NOP, since it does clobber the
|
||||
@ condition flags. But it is the best we can do for EFI, since
|
||||
@ PE/COFF expects the magic string "MZ" at offset 0, while the
|
||||
@ ARM/Linux boot protocol expects an executable instruction
|
||||
@ there.
|
||||
.inst 'M' | ('Z' << 8) | (0x1310 << 16) @ tstne r0, #0x4d000
|
||||
#else
|
||||
mov r0, r0
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.macro __EFI_HEADER
|
||||
#ifdef CONFIG_EFI_STUB
|
||||
b __efi_start
|
||||
|
||||
.set start_offset, __efi_start - start
|
||||
.org start + 0x3c
|
||||
@
|
||||
@ The PE header can be anywhere in the file, but for
|
||||
@ simplicity we keep it together with the MSDOS header
|
||||
@ The offset to the PE/COFF header needs to be at offset
|
||||
@ 0x3C in the MSDOS header.
|
||||
@ The only 2 fields of the MSDOS header that are used are this
|
||||
@ PE/COFF offset, and the "MZ" bytes at offset 0x0.
|
||||
@
|
||||
.long pe_header - start @ Offset to the PE header.
|
||||
|
||||
pe_header:
|
||||
.ascii "PE\0\0"
|
||||
|
||||
coff_header:
|
||||
.short 0x01c2 @ ARM or Thumb
|
||||
.short 2 @ nr_sections
|
||||
.long 0 @ TimeDateStamp
|
||||
.long 0 @ PointerToSymbolTable
|
||||
.long 1 @ NumberOfSymbols
|
||||
.short section_table - optional_header
|
||||
@ SizeOfOptionalHeader
|
||||
.short 0x306 @ Characteristics.
|
||||
@ IMAGE_FILE_32BIT_MACHINE |
|
||||
@ IMAGE_FILE_DEBUG_STRIPPED |
|
||||
@ IMAGE_FILE_EXECUTABLE_IMAGE |
|
||||
@ IMAGE_FILE_LINE_NUMS_STRIPPED
|
||||
|
||||
optional_header:
|
||||
.short 0x10b @ PE32 format
|
||||
.byte 0x02 @ MajorLinkerVersion
|
||||
.byte 0x14 @ MinorLinkerVersion
|
||||
.long _end - __efi_start @ SizeOfCode
|
||||
.long 0 @ SizeOfInitializedData
|
||||
.long 0 @ SizeOfUninitializedData
|
||||
.long efi_stub_entry - start @ AddressOfEntryPoint
|
||||
.long start_offset @ BaseOfCode
|
||||
.long 0 @ data
|
||||
|
||||
extra_header_fields:
|
||||
.long 0 @ ImageBase
|
||||
.long 0x200 @ SectionAlignment
|
||||
.long 0x200 @ FileAlignment
|
||||
.short 0 @ MajorOperatingSystemVersion
|
||||
.short 0 @ MinorOperatingSystemVersion
|
||||
.short 0 @ MajorImageVersion
|
||||
.short 0 @ MinorImageVersion
|
||||
.short 0 @ MajorSubsystemVersion
|
||||
.short 0 @ MinorSubsystemVersion
|
||||
.long 0 @ Win32VersionValue
|
||||
|
||||
.long _end - start @ SizeOfImage
|
||||
.long start_offset @ SizeOfHeaders
|
||||
.long 0 @ CheckSum
|
||||
.short 0xa @ Subsystem (EFI application)
|
||||
.short 0 @ DllCharacteristics
|
||||
.long 0 @ SizeOfStackReserve
|
||||
.long 0 @ SizeOfStackCommit
|
||||
.long 0 @ SizeOfHeapReserve
|
||||
.long 0 @ SizeOfHeapCommit
|
||||
.long 0 @ LoaderFlags
|
||||
.long 0x6 @ NumberOfRvaAndSizes
|
||||
|
||||
.quad 0 @ ExportTable
|
||||
.quad 0 @ ImportTable
|
||||
.quad 0 @ ResourceTable
|
||||
.quad 0 @ ExceptionTable
|
||||
.quad 0 @ CertificationTable
|
||||
.quad 0 @ BaseRelocationTable
|
||||
|
||||
section_table:
|
||||
@
|
||||
@ The EFI application loader requires a relocation section
|
||||
@ because EFI applications must be relocatable. This is a
|
||||
@ dummy section as far as we are concerned.
|
||||
@
|
||||
.ascii ".reloc\0\0"
|
||||
.long 0 @ VirtualSize
|
||||
.long 0 @ VirtualAddress
|
||||
.long 0 @ SizeOfRawData
|
||||
.long 0 @ PointerToRawData
|
||||
.long 0 @ PointerToRelocations
|
||||
.long 0 @ PointerToLineNumbers
|
||||
.short 0 @ NumberOfRelocations
|
||||
.short 0 @ NumberOfLineNumbers
|
||||
.long 0x42100040 @ Characteristics
|
||||
|
||||
.ascii ".text\0\0\0"
|
||||
.long _end - __efi_start @ VirtualSize
|
||||
.long __efi_start @ VirtualAddress
|
||||
.long _edata - __efi_start @ SizeOfRawData
|
||||
.long __efi_start @ PointerToRawData
|
||||
.long 0 @ PointerToRelocations
|
||||
.long 0 @ PointerToLineNumbers
|
||||
.short 0 @ NumberOfRelocations
|
||||
.short 0 @ NumberOfLineNumbers
|
||||
.long 0xe0500020 @ Characteristics
|
||||
|
||||
.align 9
|
||||
__efi_start:
|
||||
#endif
|
||||
.endm
|
@ -12,6 +12,8 @@
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/v7m.h>
|
||||
|
||||
#include "efi-header.S"
|
||||
|
||||
AR_CLASS( .arch armv7-a )
|
||||
M_CLASS( .arch armv7-m )
|
||||
|
||||
@ -126,7 +128,7 @@
|
||||
start:
|
||||
.type start,#function
|
||||
.rept 7
|
||||
mov r0, r0
|
||||
__nop
|
||||
.endr
|
||||
ARM( mov r0, r0 )
|
||||
ARM( b 1f )
|
||||
@ -139,7 +141,8 @@ start:
|
||||
.word 0x04030201 @ endianness flag
|
||||
|
||||
THUMB( .thumb )
|
||||
1:
|
||||
1: __EFI_HEADER
|
||||
|
||||
ARM_BE8( setend be ) @ go BE8 if compiled for BE8
|
||||
AR_CLASS( mrs r9, cpsr )
|
||||
#ifdef CONFIG_ARM_VIRT_EXT
|
||||
@ -1353,6 +1356,53 @@ __enter_kernel:
|
||||
|
||||
reloc_code_end:
|
||||
|
||||
#ifdef CONFIG_EFI_STUB
|
||||
.align 2
|
||||
_start: .long start - .
|
||||
|
||||
ENTRY(efi_stub_entry)
|
||||
@ allocate space on stack for passing current zImage address
|
||||
@ and for the EFI stub to return of new entry point of
|
||||
@ zImage, as EFI stub may copy the kernel. Pointer address
|
||||
@ is passed in r2. r0 and r1 are passed through from the
|
||||
@ EFI firmware to efi_entry
|
||||
adr ip, _start
|
||||
ldr r3, [ip]
|
||||
add r3, r3, ip
|
||||
stmfd sp!, {r3, lr}
|
||||
mov r2, sp @ pass zImage address in r2
|
||||
bl efi_entry
|
||||
|
||||
@ Check for error return from EFI stub. r0 has FDT address
|
||||
@ or error code.
|
||||
cmn r0, #1
|
||||
beq efi_load_fail
|
||||
|
||||
@ Preserve return value of efi_entry() in r4
|
||||
mov r4, r0
|
||||
bl cache_clean_flush
|
||||
bl cache_off
|
||||
|
||||
@ Set parameters for booting zImage according to boot protocol
|
||||
@ put FDT address in r2, it was returned by efi_entry()
|
||||
@ r1 is the machine type, and r0 needs to be 0
|
||||
mov r0, #0
|
||||
mov r1, #0xFFFFFFFF
|
||||
mov r2, r4
|
||||
|
||||
@ Branch to (possibly) relocated zImage that is in [sp]
|
||||
ldr lr, [sp]
|
||||
ldr ip, =start_offset
|
||||
add lr, lr, ip
|
||||
mov pc, lr @ no mode switch
|
||||
|
||||
efi_load_fail:
|
||||
@ Return EFI_LOAD_ERROR to EFI firmware on error.
|
||||
ldr r0, =0x80000001
|
||||
ldmfd sp!, {ip, pc}
|
||||
ENDPROC(efi_stub_entry)
|
||||
#endif
|
||||
|
||||
.align
|
||||
.section ".stack", "aw", %nobits
|
||||
.L_user_stack: .space 4096
|
||||
|
@ -48,6 +48,13 @@ SECTIONS
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
}
|
||||
.data : {
|
||||
/*
|
||||
* The EFI stub always executes from RAM, and runs strictly before the
|
||||
* decompressor, so we can make an exception for its r/w data, and keep it
|
||||
*/
|
||||
*(.data.efistub)
|
||||
}
|
||||
.piggydata : {
|
||||
*(.piggydata)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
generic-y += bitsperlong.h
|
||||
generic-y += cputime.h
|
||||
generic-y += current.h
|
||||
generic-y += early_ioremap.h
|
||||
generic-y += emergency-restart.h
|
||||
generic-y += errno.h
|
||||
generic-y += exec.h
|
||||
|
83
arch/arm/include/asm/efi.h
Normal file
83
arch/arm/include/asm/efi.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARM_EFI_H
|
||||
#define __ASM_ARM_EFI_H
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cachetype.h>
|
||||
#include <asm/early_ioremap.h>
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/highmem.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
#ifdef CONFIG_EFI
|
||||
void efi_init(void);
|
||||
|
||||
int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
|
||||
|
||||
#define efi_call_virt(f, ...) \
|
||||
({ \
|
||||
efi_##f##_t *__f; \
|
||||
efi_status_t __s; \
|
||||
\
|
||||
efi_virtmap_load(); \
|
||||
__f = efi.systab->runtime->f; \
|
||||
__s = __f(__VA_ARGS__); \
|
||||
efi_virtmap_unload(); \
|
||||
__s; \
|
||||
})
|
||||
|
||||
#define __efi_call_virt(f, ...) \
|
||||
({ \
|
||||
efi_##f##_t *__f; \
|
||||
\
|
||||
efi_virtmap_load(); \
|
||||
__f = efi.systab->runtime->f; \
|
||||
__f(__VA_ARGS__); \
|
||||
efi_virtmap_unload(); \
|
||||
})
|
||||
|
||||
static inline void efi_set_pgd(struct mm_struct *mm)
|
||||
{
|
||||
check_and_switch_context(mm, NULL);
|
||||
}
|
||||
|
||||
void efi_virtmap_load(void);
|
||||
void efi_virtmap_unload(void);
|
||||
|
||||
#else
|
||||
#define efi_init()
|
||||
#endif /* CONFIG_EFI */
|
||||
|
||||
/* arch specific definitions used by the stub code */
|
||||
|
||||
#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* A reasonable upper bound for the uncompressed kernel size is 32 MBytes,
|
||||
* so we will reserve that amount of memory. We have no easy way to tell what
|
||||
* the actuall size of code + data the uncompressed kernel will use.
|
||||
* If this is insufficient, the decompressor will relocate itself out of the
|
||||
* way before performing the decompression.
|
||||
*/
|
||||
#define MAX_UNCOMP_KERNEL_SIZE SZ_32M
|
||||
|
||||
/*
|
||||
* The kernel zImage should preferably be located between 32 MB and 128 MB
|
||||
* from the base of DRAM. The min address leaves space for a maximal size
|
||||
* uncompressed image, and the max address is due to how the zImage decompressor
|
||||
* picks a destination address.
|
||||
*/
|
||||
#define ZIMAGE_OFFSET_LIMIT SZ_128M
|
||||
#define MIN_ZIMAGE_OFFSET MAX_UNCOMP_KERNEL_SIZE
|
||||
#define MAX_FDT_OFFSET ZIMAGE_OFFSET_LIMIT
|
||||
|
||||
#endif /* _ASM_ARM_EFI_H */
|
@ -19,20 +19,47 @@ enum fixed_addresses {
|
||||
FIX_TEXT_POKE0,
|
||||
FIX_TEXT_POKE1,
|
||||
|
||||
__end_of_fixed_addresses
|
||||
__end_of_fixmap_region,
|
||||
|
||||
/*
|
||||
* Share the kmap() region with early_ioremap(): this is guaranteed
|
||||
* not to clash since early_ioremap() is only available before
|
||||
* paging_init(), and kmap() only after.
|
||||
*/
|
||||
#define NR_FIX_BTMAPS 32
|
||||
#define FIX_BTMAPS_SLOTS 7
|
||||
#define TOTAL_FIX_BTMAPS (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS)
|
||||
|
||||
FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
|
||||
FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,
|
||||
__end_of_early_ioremap_region
|
||||
};
|
||||
|
||||
static const enum fixed_addresses __end_of_fixed_addresses =
|
||||
__end_of_fixmap_region > __end_of_early_ioremap_region ?
|
||||
__end_of_fixmap_region : __end_of_early_ioremap_region;
|
||||
|
||||
#define FIXMAP_PAGE_COMMON (L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY)
|
||||
|
||||
#define FIXMAP_PAGE_NORMAL (FIXMAP_PAGE_COMMON | L_PTE_MT_WRITEBACK)
|
||||
#define FIXMAP_PAGE_RO (FIXMAP_PAGE_NORMAL | L_PTE_RDONLY)
|
||||
|
||||
/* Used by set_fixmap_(io|nocache), both meant for mapping a device */
|
||||
#define FIXMAP_PAGE_IO (FIXMAP_PAGE_COMMON | L_PTE_MT_DEV_SHARED | L_PTE_SHARED)
|
||||
#define FIXMAP_PAGE_NOCACHE FIXMAP_PAGE_IO
|
||||
|
||||
#define __early_set_fixmap __set_fixmap
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
|
||||
void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot);
|
||||
void __init early_fixmap_init(void);
|
||||
|
||||
#include <asm-generic/fixmap.h>
|
||||
|
||||
#else
|
||||
|
||||
static inline void early_fixmap_init(void) { }
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -42,6 +42,8 @@ enum {
|
||||
extern void iotable_init(struct map_desc *, int);
|
||||
extern void vm_reserve_area_early(unsigned long addr, unsigned long size,
|
||||
void *caller);
|
||||
extern void create_mapping_late(struct mm_struct *mm, struct map_desc *md,
|
||||
bool ng);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LL
|
||||
extern void debug_ll_addr(unsigned long *paddr, unsigned long *vaddr);
|
||||
|
@ -26,7 +26,7 @@ void __check_vmalloc_seq(struct mm_struct *mm);
|
||||
#ifdef CONFIG_CPU_HAS_ASID
|
||||
|
||||
void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk);
|
||||
#define init_new_context(tsk,mm) ({ atomic64_set(&mm->context.id, 0); 0; })
|
||||
#define init_new_context(tsk,mm) ({ atomic64_set(&(mm)->context.id, 0); 0; })
|
||||
|
||||
#ifdef CONFIG_ARM_ERRATA_798181
|
||||
void a15_erratum_get_cpumask(int this_cpu, struct mm_struct *mm,
|
||||
|
@ -76,6 +76,7 @@ obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_xscale.o perf_event_v6.o \
|
||||
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
|
||||
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
|
||||
obj-$(CONFIG_VDSO) += vdso.o
|
||||
obj-$(CONFIG_EFI) += efi.o
|
||||
|
||||
ifneq ($(CONFIG_ARCH_EBSA110),y)
|
||||
obj-y += io.o
|
||||
|
38
arch/arm/kernel/efi.c
Normal file
38
arch/arm/kernel/efi.c
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <asm/efi.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mmu_context.h>
|
||||
|
||||
int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
|
||||
{
|
||||
struct map_desc desc = {
|
||||
.virtual = md->virt_addr,
|
||||
.pfn = __phys_to_pfn(md->phys_addr),
|
||||
.length = md->num_pages * EFI_PAGE_SIZE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Order is important here: memory regions may have all of the
|
||||
* bits below set (and usually do), so we check them in order of
|
||||
* preference.
|
||||
*/
|
||||
if (md->attribute & EFI_MEMORY_WB)
|
||||
desc.type = MT_MEMORY_RWX;
|
||||
else if (md->attribute & EFI_MEMORY_WT)
|
||||
desc.type = MT_MEMORY_RWX_NONCACHED;
|
||||
else if (md->attribute & EFI_MEMORY_WC)
|
||||
desc.type = MT_DEVICE_WC;
|
||||
else
|
||||
desc.type = MT_DEVICE;
|
||||
|
||||
create_mapping_late(mm, &desc, true);
|
||||
return 0;
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/efi.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stddef.h>
|
||||
@ -37,7 +38,9 @@
|
||||
#include <asm/cp15.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/efi.h>
|
||||
#include <asm/elf.h>
|
||||
#include <asm/early_ioremap.h>
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/procinfo.h>
|
||||
#include <asm/psci.h>
|
||||
@ -1023,8 +1026,8 @@ void __init setup_arch(char **cmdline_p)
|
||||
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
|
||||
*cmdline_p = cmd_line;
|
||||
|
||||
if (IS_ENABLED(CONFIG_FIX_EARLYCON_MEM))
|
||||
early_fixmap_init();
|
||||
early_fixmap_init();
|
||||
early_ioremap_init();
|
||||
|
||||
parse_early_param();
|
||||
|
||||
@ -1032,9 +1035,12 @@ void __init setup_arch(char **cmdline_p)
|
||||
early_paging_init(mdesc);
|
||||
#endif
|
||||
setup_dma_zone(mdesc);
|
||||
efi_init();
|
||||
sanity_check_meminfo();
|
||||
arm_memblock_init(mdesc);
|
||||
|
||||
early_ioremap_reset();
|
||||
|
||||
paging_init(mdesc);
|
||||
request_standard_resources(mdesc);
|
||||
|
||||
|
@ -192,7 +192,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max_low,
|
||||
#ifdef CONFIG_HAVE_ARCH_PFN_VALID
|
||||
int pfn_valid(unsigned long pfn)
|
||||
{
|
||||
return memblock_is_memory(__pfn_to_phys(pfn));
|
||||
return memblock_is_map_memory(__pfn_to_phys(pfn));
|
||||
}
|
||||
EXPORT_SYMBOL(pfn_valid);
|
||||
#endif
|
||||
@ -433,6 +433,9 @@ static void __init free_highpages(void)
|
||||
if (end <= max_low)
|
||||
continue;
|
||||
|
||||
if (memblock_is_nomap(mem))
|
||||
continue;
|
||||
|
||||
/* Truncate partial highmem entries */
|
||||
if (start < max_low)
|
||||
start = max_low;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <asm/cp15.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/early_ioremap.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/tlbflush.h>
|
||||
@ -469,3 +470,11 @@ int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_ioremap_io);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Must be called after early_fixmap_init
|
||||
*/
|
||||
void __init early_ioremap_init(void)
|
||||
{
|
||||
early_ioremap_setup();
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ void __init early_fixmap_init(void)
|
||||
* The early fixmap range spans multiple pmds, for which
|
||||
* we are not prepared:
|
||||
*/
|
||||
BUILD_BUG_ON((__fix_to_virt(__end_of_permanent_fixed_addresses) >> PMD_SHIFT)
|
||||
BUILD_BUG_ON((__fix_to_virt(__end_of_early_ioremap_region) >> PMD_SHIFT)
|
||||
!= FIXADDR_TOP >> PMD_SHIFT);
|
||||
|
||||
pmd = fixmap_pmd(FIXADDR_TOP);
|
||||
@ -724,30 +724,49 @@ static void __init *early_alloc(unsigned long sz)
|
||||
return early_alloc_aligned(sz, sz);
|
||||
}
|
||||
|
||||
static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot)
|
||||
static void *__init late_alloc(unsigned long sz)
|
||||
{
|
||||
void *ptr = (void *)__get_free_pages(PGALLOC_GFP, get_order(sz));
|
||||
|
||||
BUG_ON(!ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static pte_t * __init pte_alloc(pmd_t *pmd, unsigned long addr,
|
||||
unsigned long prot,
|
||||
void *(*alloc)(unsigned long sz))
|
||||
{
|
||||
if (pmd_none(*pmd)) {
|
||||
pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
|
||||
pte_t *pte = alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
|
||||
__pmd_populate(pmd, __pa(pte), prot);
|
||||
}
|
||||
BUG_ON(pmd_bad(*pmd));
|
||||
return pte_offset_kernel(pmd, addr);
|
||||
}
|
||||
|
||||
static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr,
|
||||
unsigned long prot)
|
||||
{
|
||||
return pte_alloc(pmd, addr, prot, early_alloc);
|
||||
}
|
||||
|
||||
static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
|
||||
unsigned long end, unsigned long pfn,
|
||||
const struct mem_type *type)
|
||||
const struct mem_type *type,
|
||||
void *(*alloc)(unsigned long sz),
|
||||
bool ng)
|
||||
{
|
||||
pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1);
|
||||
pte_t *pte = pte_alloc(pmd, addr, type->prot_l1, alloc);
|
||||
do {
|
||||
set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);
|
||||
set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)),
|
||||
ng ? PTE_EXT_NG : 0);
|
||||
pfn++;
|
||||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
}
|
||||
|
||||
static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
|
||||
unsigned long end, phys_addr_t phys,
|
||||
const struct mem_type *type)
|
||||
const struct mem_type *type, bool ng)
|
||||
{
|
||||
pmd_t *p = pmd;
|
||||
|
||||
@ -765,7 +784,7 @@ static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
|
||||
pmd++;
|
||||
#endif
|
||||
do {
|
||||
*pmd = __pmd(phys | type->prot_sect);
|
||||
*pmd = __pmd(phys | type->prot_sect | (ng ? PMD_SECT_nG : 0));
|
||||
phys += SECTION_SIZE;
|
||||
} while (pmd++, addr += SECTION_SIZE, addr != end);
|
||||
|
||||
@ -774,7 +793,8 @@ static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
|
||||
|
||||
static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
|
||||
unsigned long end, phys_addr_t phys,
|
||||
const struct mem_type *type)
|
||||
const struct mem_type *type,
|
||||
void *(*alloc)(unsigned long sz), bool ng)
|
||||
{
|
||||
pmd_t *pmd = pmd_offset(pud, addr);
|
||||
unsigned long next;
|
||||
@ -792,10 +812,10 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
|
||||
*/
|
||||
if (type->prot_sect &&
|
||||
((addr | next | phys) & ~SECTION_MASK) == 0) {
|
||||
__map_init_section(pmd, addr, next, phys, type);
|
||||
__map_init_section(pmd, addr, next, phys, type, ng);
|
||||
} else {
|
||||
alloc_init_pte(pmd, addr, next,
|
||||
__phys_to_pfn(phys), type);
|
||||
__phys_to_pfn(phys), type, alloc, ng);
|
||||
}
|
||||
|
||||
phys += next - addr;
|
||||
@ -805,21 +825,24 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
|
||||
|
||||
static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
|
||||
unsigned long end, phys_addr_t phys,
|
||||
const struct mem_type *type)
|
||||
const struct mem_type *type,
|
||||
void *(*alloc)(unsigned long sz), bool ng)
|
||||
{
|
||||
pud_t *pud = pud_offset(pgd, addr);
|
||||
unsigned long next;
|
||||
|
||||
do {
|
||||
next = pud_addr_end(addr, end);
|
||||
alloc_init_pmd(pud, addr, next, phys, type);
|
||||
alloc_init_pmd(pud, addr, next, phys, type, alloc, ng);
|
||||
phys += next - addr;
|
||||
} while (pud++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ARM_LPAE
|
||||
static void __init create_36bit_mapping(struct map_desc *md,
|
||||
const struct mem_type *type)
|
||||
static void __init create_36bit_mapping(struct mm_struct *mm,
|
||||
struct map_desc *md,
|
||||
const struct mem_type *type,
|
||||
bool ng)
|
||||
{
|
||||
unsigned long addr, length, end;
|
||||
phys_addr_t phys;
|
||||
@ -859,7 +882,7 @@ static void __init create_36bit_mapping(struct map_desc *md,
|
||||
*/
|
||||
phys |= (((md->pfn >> (32 - PAGE_SHIFT)) & 0xF) << 20);
|
||||
|
||||
pgd = pgd_offset_k(addr);
|
||||
pgd = pgd_offset(mm, addr);
|
||||
end = addr + length;
|
||||
do {
|
||||
pud_t *pud = pud_offset(pgd, addr);
|
||||
@ -867,7 +890,8 @@ static void __init create_36bit_mapping(struct map_desc *md,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
*pmd++ = __pmd(phys | type->prot_sect | PMD_SECT_SUPER);
|
||||
*pmd++ = __pmd(phys | type->prot_sect | PMD_SECT_SUPER |
|
||||
(ng ? PMD_SECT_nG : 0));
|
||||
|
||||
addr += SUPERSECTION_SIZE;
|
||||
phys += SUPERSECTION_SIZE;
|
||||
@ -876,33 +900,15 @@ static void __init create_36bit_mapping(struct map_desc *md,
|
||||
}
|
||||
#endif /* !CONFIG_ARM_LPAE */
|
||||
|
||||
/*
|
||||
* Create the page directory entries and any necessary
|
||||
* page tables for the mapping specified by `md'. We
|
||||
* are able to cope here with varying sizes and address
|
||||
* offsets, and we take full advantage of sections and
|
||||
* supersections.
|
||||
*/
|
||||
static void __init create_mapping(struct map_desc *md)
|
||||
static void __init __create_mapping(struct mm_struct *mm, struct map_desc *md,
|
||||
void *(*alloc)(unsigned long sz),
|
||||
bool ng)
|
||||
{
|
||||
unsigned long addr, length, end;
|
||||
phys_addr_t phys;
|
||||
const struct mem_type *type;
|
||||
pgd_t *pgd;
|
||||
|
||||
if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
|
||||
pr_warn("BUG: not creating mapping for 0x%08llx at 0x%08lx in user region\n",
|
||||
(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
|
||||
md->virtual >= PAGE_OFFSET && md->virtual < FIXADDR_START &&
|
||||
(md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) {
|
||||
pr_warn("BUG: mapping for 0x%08llx at 0x%08lx out of vmalloc space\n",
|
||||
(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
|
||||
}
|
||||
|
||||
type = &mem_types[md->type];
|
||||
|
||||
#ifndef CONFIG_ARM_LPAE
|
||||
@ -910,7 +916,7 @@ static void __init create_mapping(struct map_desc *md)
|
||||
* Catch 36-bit addresses
|
||||
*/
|
||||
if (md->pfn >= 0x100000) {
|
||||
create_36bit_mapping(md, type);
|
||||
create_36bit_mapping(mm, md, type, ng);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@ -925,18 +931,55 @@ static void __init create_mapping(struct map_desc *md)
|
||||
return;
|
||||
}
|
||||
|
||||
pgd = pgd_offset_k(addr);
|
||||
pgd = pgd_offset(mm, addr);
|
||||
end = addr + length;
|
||||
do {
|
||||
unsigned long next = pgd_addr_end(addr, end);
|
||||
|
||||
alloc_init_pud(pgd, addr, next, phys, type);
|
||||
alloc_init_pud(pgd, addr, next, phys, type, alloc, ng);
|
||||
|
||||
phys += next - addr;
|
||||
addr = next;
|
||||
} while (pgd++, addr != end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the page directory entries and any necessary
|
||||
* page tables for the mapping specified by `md'. We
|
||||
* are able to cope here with varying sizes and address
|
||||
* offsets, and we take full advantage of sections and
|
||||
* supersections.
|
||||
*/
|
||||
static void __init create_mapping(struct map_desc *md)
|
||||
{
|
||||
if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
|
||||
pr_warn("BUG: not creating mapping for 0x%08llx at 0x%08lx in user region\n",
|
||||
(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
|
||||
md->virtual >= PAGE_OFFSET && md->virtual < FIXADDR_START &&
|
||||
(md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) {
|
||||
pr_warn("BUG: mapping for 0x%08llx at 0x%08lx out of vmalloc space\n",
|
||||
(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
|
||||
}
|
||||
|
||||
__create_mapping(&init_mm, md, early_alloc, false);
|
||||
}
|
||||
|
||||
void __init create_mapping_late(struct mm_struct *mm, struct map_desc *md,
|
||||
bool ng)
|
||||
{
|
||||
#ifdef CONFIG_ARM_LPAE
|
||||
pud_t *pud = pud_alloc(mm, pgd_offset(mm, md->virtual), md->virtual);
|
||||
if (WARN_ON(!pud))
|
||||
return;
|
||||
pmd_alloc(mm, pud, 0);
|
||||
#endif
|
||||
__create_mapping(mm, md, late_alloc, ng);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the architecture specific mappings
|
||||
*/
|
||||
@ -1392,6 +1435,9 @@ static void __init map_lowmem(void)
|
||||
phys_addr_t end = start + reg->size;
|
||||
struct map_desc map;
|
||||
|
||||
if (memblock_is_nomap(reg))
|
||||
continue;
|
||||
|
||||
if (end > arm_lowmem_limit)
|
||||
end = arm_lowmem_limit;
|
||||
if (start >= end)
|
||||
|
@ -2,7 +2,9 @@
|
||||
#define _ASM_EFI_H
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/neon.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
#ifdef CONFIG_EFI
|
||||
extern void efi_init(void);
|
||||
@ -10,6 +12,8 @@ extern void efi_init(void);
|
||||
#define efi_init()
|
||||
#endif
|
||||
|
||||
int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
|
||||
|
||||
#define efi_call_virt(f, ...) \
|
||||
({ \
|
||||
efi_##f##_t *__f; \
|
||||
@ -63,6 +67,11 @@ extern void efi_init(void);
|
||||
* Services are enabled and the EFI_RUNTIME_SERVICES bit set.
|
||||
*/
|
||||
|
||||
static inline void efi_set_pgd(struct mm_struct *mm)
|
||||
{
|
||||
switch_mm(NULL, mm, NULL);
|
||||
}
|
||||
|
||||
void efi_virtmap_load(void);
|
||||
void efi_virtmap_unload(void);
|
||||
|
||||
|
@ -11,317 +11,34 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/efi.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
struct efi_memory_map memmap;
|
||||
|
||||
static u64 efi_system_table;
|
||||
|
||||
static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss;
|
||||
|
||||
static struct mm_struct efi_mm = {
|
||||
.mm_rb = RB_ROOT,
|
||||
.pgd = efi_pgd,
|
||||
.mm_users = ATOMIC_INIT(2),
|
||||
.mm_count = ATOMIC_INIT(1),
|
||||
.mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
|
||||
.page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
|
||||
.mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
|
||||
};
|
||||
|
||||
static int __init is_normal_ram(efi_memory_desc_t *md)
|
||||
int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
|
||||
{
|
||||
if (md->attribute & EFI_MEMORY_WB)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate a EFI virtual address into a physical address: this is necessary,
|
||||
* as some data members of the EFI system table are virtually remapped after
|
||||
* SetVirtualAddressMap() has been called.
|
||||
*/
|
||||
static phys_addr_t efi_to_phys(unsigned long addr)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME))
|
||||
continue;
|
||||
if (md->virt_addr == 0)
|
||||
/* no virtual mapping has been installed by the stub */
|
||||
break;
|
||||
if (md->virt_addr <= addr &&
|
||||
(addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT))
|
||||
return md->phys_addr + addr - md->virt_addr;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int __init uefi_init(void)
|
||||
{
|
||||
efi_char16_t *c16;
|
||||
void *config_tables;
|
||||
u64 table_size;
|
||||
char vendor[100] = "unknown";
|
||||
int i, retval;
|
||||
|
||||
efi.systab = early_memremap(efi_system_table,
|
||||
sizeof(efi_system_table_t));
|
||||
if (efi.systab == NULL) {
|
||||
pr_warn("Unable to map EFI system table.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
set_bit(EFI_BOOT, &efi.flags);
|
||||
set_bit(EFI_64BIT, &efi.flags);
|
||||
pteval_t prot_val;
|
||||
|
||||
/*
|
||||
* Verify the EFI Table
|
||||
* Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
|
||||
* executable, everything else can be mapped with the XN bits
|
||||
* set.
|
||||
*/
|
||||
if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
|
||||
pr_err("System table signature incorrect\n");
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if ((efi.systab->hdr.revision >> 16) < 2)
|
||||
pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
|
||||
efi.systab->hdr.revision >> 16,
|
||||
efi.systab->hdr.revision & 0xffff);
|
||||
|
||||
/* Show what we know for posterity */
|
||||
c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor),
|
||||
sizeof(vendor) * sizeof(efi_char16_t));
|
||||
if (c16) {
|
||||
for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
|
||||
vendor[i] = c16[i];
|
||||
vendor[i] = '\0';
|
||||
early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t));
|
||||
}
|
||||
|
||||
pr_info("EFI v%u.%.02u by %s\n",
|
||||
efi.systab->hdr.revision >> 16,
|
||||
efi.systab->hdr.revision & 0xffff, vendor);
|
||||
|
||||
table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables;
|
||||
config_tables = early_memremap(efi_to_phys(efi.systab->tables),
|
||||
table_size);
|
||||
if (config_tables == NULL) {
|
||||
pr_warn("Unable to map EFI config table array.\n");
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables,
|
||||
sizeof(efi_config_table_64_t), NULL);
|
||||
|
||||
early_memunmap(config_tables, table_size);
|
||||
out:
|
||||
early_memunmap(efi.systab, sizeof(efi_system_table_t));
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true for RAM regions we want to permanently reserve.
|
||||
*/
|
||||
static __init int is_reserve_region(efi_memory_desc_t *md)
|
||||
{
|
||||
switch (md->type) {
|
||||
case EFI_LOADER_CODE:
|
||||
case EFI_LOADER_DATA:
|
||||
case EFI_BOOT_SERVICES_CODE:
|
||||
case EFI_BOOT_SERVICES_DATA:
|
||||
case EFI_CONVENTIONAL_MEMORY:
|
||||
case EFI_PERSISTENT_MEMORY:
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return is_normal_ram(md);
|
||||
}
|
||||
|
||||
static __init void reserve_regions(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
u64 paddr, npages, size;
|
||||
|
||||
if (efi_enabled(EFI_DBG))
|
||||
pr_info("Processing EFI memory map:\n");
|
||||
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
paddr = md->phys_addr;
|
||||
npages = md->num_pages;
|
||||
|
||||
if (efi_enabled(EFI_DBG)) {
|
||||
char buf[64];
|
||||
|
||||
pr_info(" 0x%012llx-0x%012llx %s",
|
||||
paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
|
||||
efi_md_typeattr_format(buf, sizeof(buf), md));
|
||||
}
|
||||
|
||||
memrange_efi_to_native(&paddr, &npages);
|
||||
size = npages << PAGE_SHIFT;
|
||||
|
||||
if (is_normal_ram(md))
|
||||
early_init_dt_add_memory_arch(paddr, size);
|
||||
|
||||
if (is_reserve_region(md)) {
|
||||
memblock_reserve(paddr, size);
|
||||
if (efi_enabled(EFI_DBG))
|
||||
pr_cont("*");
|
||||
}
|
||||
|
||||
if (efi_enabled(EFI_DBG))
|
||||
pr_cont("\n");
|
||||
}
|
||||
|
||||
set_bit(EFI_MEMMAP, &efi.flags);
|
||||
}
|
||||
|
||||
void __init efi_init(void)
|
||||
{
|
||||
struct efi_fdt_params params;
|
||||
|
||||
/* Grab UEFI information placed in FDT by stub */
|
||||
if (!efi_get_fdt_params(¶ms))
|
||||
return;
|
||||
|
||||
efi_system_table = params.system_table;
|
||||
|
||||
memblock_reserve(params.mmap & PAGE_MASK,
|
||||
PAGE_ALIGN(params.mmap_size + (params.mmap & ~PAGE_MASK)));
|
||||
memmap.phys_map = params.mmap;
|
||||
memmap.map = early_memremap(params.mmap, params.mmap_size);
|
||||
if (memmap.map == NULL) {
|
||||
/*
|
||||
* If we are booting via UEFI, the UEFI memory map is the only
|
||||
* description of memory we have, so there is little point in
|
||||
* proceeding if we cannot access it.
|
||||
*/
|
||||
panic("Unable to map EFI memory map.\n");
|
||||
}
|
||||
memmap.map_end = memmap.map + params.mmap_size;
|
||||
memmap.desc_size = params.desc_size;
|
||||
memmap.desc_version = params.desc_ver;
|
||||
|
||||
if (uefi_init() < 0)
|
||||
return;
|
||||
|
||||
reserve_regions();
|
||||
early_memunmap(memmap.map, params.mmap_size);
|
||||
}
|
||||
|
||||
static bool __init efi_virtmap_init(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
init_new_context(NULL, &efi_mm);
|
||||
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
pgprot_t prot;
|
||||
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME))
|
||||
continue;
|
||||
if (md->virt_addr == 0)
|
||||
return false;
|
||||
|
||||
pr_info(" EFI remap 0x%016llx => %p\n",
|
||||
md->phys_addr, (void *)md->virt_addr);
|
||||
|
||||
/*
|
||||
* Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
|
||||
* executable, everything else can be mapped with the XN bits
|
||||
* set.
|
||||
*/
|
||||
if (!is_normal_ram(md))
|
||||
prot = __pgprot(PROT_DEVICE_nGnRE);
|
||||
else if (md->type == EFI_RUNTIME_SERVICES_CODE ||
|
||||
!PAGE_ALIGNED(md->phys_addr))
|
||||
prot = PAGE_KERNEL_EXEC;
|
||||
else
|
||||
prot = PAGE_KERNEL;
|
||||
|
||||
create_pgd_mapping(&efi_mm, md->phys_addr, md->virt_addr,
|
||||
md->num_pages << EFI_PAGE_SHIFT,
|
||||
__pgprot(pgprot_val(prot) | PTE_NG));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the UEFI Runtime Services if all prerequisites are in place, i.e.,
|
||||
* non-early mapping of the UEFI system table and virtual mappings for all
|
||||
* EFI_MEMORY_RUNTIME regions.
|
||||
*/
|
||||
static int __init arm64_enable_runtime_services(void)
|
||||
{
|
||||
u64 mapsize;
|
||||
|
||||
if (!efi_enabled(EFI_BOOT)) {
|
||||
pr_info("EFI services will not be available.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (efi_runtime_disabled()) {
|
||||
pr_info("EFI runtime services will be disabled.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_info("Remapping and enabling EFI services.\n");
|
||||
|
||||
mapsize = memmap.map_end - memmap.map;
|
||||
memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
|
||||
mapsize);
|
||||
if (!memmap.map) {
|
||||
pr_err("Failed to remap EFI memory map\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memmap.map_end = memmap.map + mapsize;
|
||||
efi.memmap = &memmap;
|
||||
|
||||
efi.systab = (__force void *)ioremap_cache(efi_system_table,
|
||||
sizeof(efi_system_table_t));
|
||||
if (!efi.systab) {
|
||||
pr_err("Failed to remap EFI System Table\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
set_bit(EFI_SYSTEM_TABLES, &efi.flags);
|
||||
|
||||
if (!efi_virtmap_init()) {
|
||||
pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Set up runtime services function pointers */
|
||||
efi_native_runtime_setup();
|
||||
set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
|
||||
efi.runtime_version = efi.systab->hdr.revision;
|
||||
if ((md->attribute & EFI_MEMORY_WB) == 0)
|
||||
prot_val = PROT_DEVICE_nGnRE;
|
||||
else if (md->type == EFI_RUNTIME_SERVICES_CODE ||
|
||||
!PAGE_ALIGNED(md->phys_addr))
|
||||
prot_val = pgprot_val(PAGE_KERNEL_EXEC);
|
||||
else
|
||||
prot_val = pgprot_val(PAGE_KERNEL);
|
||||
|
||||
create_pgd_mapping(mm, md->phys_addr, md->virt_addr,
|
||||
md->num_pages << EFI_PAGE_SHIFT,
|
||||
__pgprot(prot_val | PTE_NG));
|
||||
return 0;
|
||||
}
|
||||
early_initcall(arm64_enable_runtime_services);
|
||||
|
||||
static int __init arm64_dmi_init(void)
|
||||
{
|
||||
@ -337,23 +54,6 @@ static int __init arm64_dmi_init(void)
|
||||
}
|
||||
core_initcall(arm64_dmi_init);
|
||||
|
||||
static void efi_set_pgd(struct mm_struct *mm)
|
||||
{
|
||||
switch_mm(NULL, mm, NULL);
|
||||
}
|
||||
|
||||
void efi_virtmap_load(void)
|
||||
{
|
||||
preempt_disable();
|
||||
efi_set_pgd(&efi_mm);
|
||||
}
|
||||
|
||||
void efi_virtmap_unload(void)
|
||||
{
|
||||
efi_set_pgd(current->active_mm);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
/*
|
||||
* UpdateCapsule() depends on the system being shutdown via
|
||||
* ResetSystem().
|
||||
|
@ -120,7 +120,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
|
||||
#ifdef CONFIG_HAVE_ARCH_PFN_VALID
|
||||
int pfn_valid(unsigned long pfn)
|
||||
{
|
||||
return memblock_is_memory(pfn << PAGE_SHIFT);
|
||||
return memblock_is_map_memory(pfn << PAGE_SHIFT);
|
||||
}
|
||||
EXPORT_SYMBOL(pfn_valid);
|
||||
#endif
|
||||
|
@ -372,6 +372,8 @@ static void __init map_mem(void)
|
||||
|
||||
if (start >= end)
|
||||
break;
|
||||
if (memblock_is_nomap(reg))
|
||||
continue;
|
||||
|
||||
if (ARM64_SWAPPER_USES_SECTION_MAPS) {
|
||||
/*
|
||||
|
@ -18,3 +18,7 @@ obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
|
||||
obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
|
||||
obj-$(CONFIG_EFI_STUB) += libstub/
|
||||
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
|
||||
|
||||
arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
|
||||
obj-$(CONFIG_ARM) += $(arm-obj-y)
|
||||
obj-$(CONFIG_ARM64) += $(arm-obj-y)
|
||||
|
209
drivers/firmware/efi/arm-init.c
Normal file
209
drivers/firmware/efi/arm-init.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Extensible Firmware Interface
|
||||
*
|
||||
* Based on Extensible Firmware Interface Specification version 2.4
|
||||
*
|
||||
* Copyright (C) 2013 - 2015 Linaro Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
|
||||
#include <asm/efi.h>
|
||||
|
||||
struct efi_memory_map memmap;
|
||||
|
||||
u64 efi_system_table;
|
||||
|
||||
static int __init is_normal_ram(efi_memory_desc_t *md)
|
||||
{
|
||||
if (md->attribute & EFI_MEMORY_WB)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate a EFI virtual address into a physical address: this is necessary,
|
||||
* as some data members of the EFI system table are virtually remapped after
|
||||
* SetVirtualAddressMap() has been called.
|
||||
*/
|
||||
static phys_addr_t efi_to_phys(unsigned long addr)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME))
|
||||
continue;
|
||||
if (md->virt_addr == 0)
|
||||
/* no virtual mapping has been installed by the stub */
|
||||
break;
|
||||
if (md->virt_addr <= addr &&
|
||||
(addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT))
|
||||
return md->phys_addr + addr - md->virt_addr;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int __init uefi_init(void)
|
||||
{
|
||||
efi_char16_t *c16;
|
||||
void *config_tables;
|
||||
size_t table_size;
|
||||
char vendor[100] = "unknown";
|
||||
int i, retval;
|
||||
|
||||
efi.systab = early_memremap(efi_system_table,
|
||||
sizeof(efi_system_table_t));
|
||||
if (efi.systab == NULL) {
|
||||
pr_warn("Unable to map EFI system table.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
set_bit(EFI_BOOT, &efi.flags);
|
||||
if (IS_ENABLED(CONFIG_64BIT))
|
||||
set_bit(EFI_64BIT, &efi.flags);
|
||||
|
||||
/*
|
||||
* Verify the EFI Table
|
||||
*/
|
||||
if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
|
||||
pr_err("System table signature incorrect\n");
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if ((efi.systab->hdr.revision >> 16) < 2)
|
||||
pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
|
||||
efi.systab->hdr.revision >> 16,
|
||||
efi.systab->hdr.revision & 0xffff);
|
||||
|
||||
/* Show what we know for posterity */
|
||||
c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor),
|
||||
sizeof(vendor) * sizeof(efi_char16_t));
|
||||
if (c16) {
|
||||
for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
|
||||
vendor[i] = c16[i];
|
||||
vendor[i] = '\0';
|
||||
early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t));
|
||||
}
|
||||
|
||||
pr_info("EFI v%u.%.02u by %s\n",
|
||||
efi.systab->hdr.revision >> 16,
|
||||
efi.systab->hdr.revision & 0xffff, vendor);
|
||||
|
||||
table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables;
|
||||
config_tables = early_memremap(efi_to_phys(efi.systab->tables),
|
||||
table_size);
|
||||
if (config_tables == NULL) {
|
||||
pr_warn("Unable to map EFI config table array.\n");
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables,
|
||||
sizeof(efi_config_table_t), NULL);
|
||||
|
||||
early_memunmap(config_tables, table_size);
|
||||
out:
|
||||
early_memunmap(efi.systab, sizeof(efi_system_table_t));
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true for RAM regions we want to permanently reserve.
|
||||
*/
|
||||
static __init int is_reserve_region(efi_memory_desc_t *md)
|
||||
{
|
||||
switch (md->type) {
|
||||
case EFI_LOADER_CODE:
|
||||
case EFI_LOADER_DATA:
|
||||
case EFI_BOOT_SERVICES_CODE:
|
||||
case EFI_BOOT_SERVICES_DATA:
|
||||
case EFI_CONVENTIONAL_MEMORY:
|
||||
case EFI_PERSISTENT_MEMORY:
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return is_normal_ram(md);
|
||||
}
|
||||
|
||||
static __init void reserve_regions(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
u64 paddr, npages, size;
|
||||
|
||||
if (efi_enabled(EFI_DBG))
|
||||
pr_info("Processing EFI memory map:\n");
|
||||
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
paddr = md->phys_addr;
|
||||
npages = md->num_pages;
|
||||
|
||||
if (efi_enabled(EFI_DBG)) {
|
||||
char buf[64];
|
||||
|
||||
pr_info(" 0x%012llx-0x%012llx %s",
|
||||
paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
|
||||
efi_md_typeattr_format(buf, sizeof(buf), md));
|
||||
}
|
||||
|
||||
memrange_efi_to_native(&paddr, &npages);
|
||||
size = npages << PAGE_SHIFT;
|
||||
|
||||
if (is_normal_ram(md))
|
||||
early_init_dt_add_memory_arch(paddr, size);
|
||||
|
||||
if (is_reserve_region(md)) {
|
||||
memblock_mark_nomap(paddr, size);
|
||||
if (efi_enabled(EFI_DBG))
|
||||
pr_cont("*");
|
||||
}
|
||||
|
||||
if (efi_enabled(EFI_DBG))
|
||||
pr_cont("\n");
|
||||
}
|
||||
|
||||
set_bit(EFI_MEMMAP, &efi.flags);
|
||||
}
|
||||
|
||||
void __init efi_init(void)
|
||||
{
|
||||
struct efi_fdt_params params;
|
||||
|
||||
/* Grab UEFI information placed in FDT by stub */
|
||||
if (!efi_get_fdt_params(¶ms))
|
||||
return;
|
||||
|
||||
efi_system_table = params.system_table;
|
||||
|
||||
memmap.phys_map = params.mmap;
|
||||
memmap.map = early_memremap(params.mmap, params.mmap_size);
|
||||
if (memmap.map == NULL) {
|
||||
/*
|
||||
* If we are booting via UEFI, the UEFI memory map is the only
|
||||
* description of memory we have, so there is little point in
|
||||
* proceeding if we cannot access it.
|
||||
*/
|
||||
panic("Unable to map EFI memory map.\n");
|
||||
}
|
||||
memmap.map_end = memmap.map + params.mmap_size;
|
||||
memmap.desc_size = params.desc_size;
|
||||
memmap.desc_version = params.desc_ver;
|
||||
|
||||
if (uefi_init() < 0)
|
||||
return;
|
||||
|
||||
reserve_regions();
|
||||
early_memunmap(memmap.map, params.mmap_size);
|
||||
memblock_mark_nomap(params.mmap & PAGE_MASK,
|
||||
PAGE_ALIGN(params.mmap_size +
|
||||
(params.mmap & ~PAGE_MASK)));
|
||||
}
|
135
drivers/firmware/efi/arm-runtime.c
Normal file
135
drivers/firmware/efi/arm-runtime.c
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Extensible Firmware Interface
|
||||
*
|
||||
* Based on Extensible Firmware Interface Specification version 2.4
|
||||
*
|
||||
* Copyright (C) 2013, 2014 Linaro Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/efi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/efi.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
extern u64 efi_system_table;
|
||||
|
||||
static struct mm_struct efi_mm = {
|
||||
.mm_rb = RB_ROOT,
|
||||
.mm_users = ATOMIC_INIT(2),
|
||||
.mm_count = ATOMIC_INIT(1),
|
||||
.mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
|
||||
.page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
|
||||
.mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
|
||||
};
|
||||
|
||||
static bool __init efi_virtmap_init(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
efi_mm.pgd = pgd_alloc(&efi_mm);
|
||||
init_new_context(NULL, &efi_mm);
|
||||
|
||||
for_each_efi_memory_desc(&memmap, md) {
|
||||
phys_addr_t phys = md->phys_addr;
|
||||
int ret;
|
||||
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME))
|
||||
continue;
|
||||
if (md->virt_addr == 0)
|
||||
return false;
|
||||
|
||||
ret = efi_create_mapping(&efi_mm, md);
|
||||
if (!ret) {
|
||||
pr_info(" EFI remap %pa => %p\n",
|
||||
&phys, (void *)(unsigned long)md->virt_addr);
|
||||
} else {
|
||||
pr_warn(" EFI remap %pa: failed to create mapping (%d)\n",
|
||||
&phys, ret);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the UEFI Runtime Services if all prerequisites are in place, i.e.,
|
||||
* non-early mapping of the UEFI system table and virtual mappings for all
|
||||
* EFI_MEMORY_RUNTIME regions.
|
||||
*/
|
||||
static int __init arm_enable_runtime_services(void)
|
||||
{
|
||||
u64 mapsize;
|
||||
|
||||
if (!efi_enabled(EFI_BOOT)) {
|
||||
pr_info("EFI services will not be available.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (efi_runtime_disabled()) {
|
||||
pr_info("EFI runtime services will be disabled.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_info("Remapping and enabling EFI services.\n");
|
||||
|
||||
mapsize = memmap.map_end - memmap.map;
|
||||
memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
|
||||
mapsize);
|
||||
if (!memmap.map) {
|
||||
pr_err("Failed to remap EFI memory map\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memmap.map_end = memmap.map + mapsize;
|
||||
efi.memmap = &memmap;
|
||||
|
||||
efi.systab = (__force void *)ioremap_cache(efi_system_table,
|
||||
sizeof(efi_system_table_t));
|
||||
if (!efi.systab) {
|
||||
pr_err("Failed to remap EFI System Table\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
set_bit(EFI_SYSTEM_TABLES, &efi.flags);
|
||||
|
||||
if (!efi_virtmap_init()) {
|
||||
pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Set up runtime services function pointers */
|
||||
efi_native_runtime_setup();
|
||||
set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
|
||||
efi.runtime_version = efi.systab->hdr.revision;
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_initcall(arm_enable_runtime_services);
|
||||
|
||||
void efi_virtmap_load(void)
|
||||
{
|
||||
preempt_disable();
|
||||
efi_set_pgd(&efi_mm);
|
||||
}
|
||||
|
||||
void efi_virtmap_unload(void)
|
||||
{
|
||||
efi_set_pgd(current->active_mm);
|
||||
preempt_enable();
|
||||
}
|
@ -25,6 +25,8 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/efi.h>
|
||||
|
||||
struct efi __read_mostly efi = {
|
||||
.mps = EFI_INVALID_TABLE_ADDR,
|
||||
.acpi = EFI_INVALID_TABLE_ADDR,
|
||||
|
@ -34,6 +34,7 @@ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
|
||||
lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \
|
||||
$(patsubst %.c,lib-%.o,$(arm-deps))
|
||||
|
||||
lib-$(CONFIG_ARM) += arm32-stub.o
|
||||
lib-$(CONFIG_ARM64) += arm64-stub.o
|
||||
CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
|
||||
|
||||
@ -67,3 +68,11 @@ quiet_cmd_stubcopy = STUBCPY $@
|
||||
$(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y) \
|
||||
&& (echo >&2 "$@: absolute symbol references not allowed in the EFI stub"; \
|
||||
rm -f $@; /bin/false); else /bin/false; fi
|
||||
|
||||
#
|
||||
# ARM discards the .data section because it disallows r/w data in the
|
||||
# decompressor. So move our .data to .data.efistub, which is preserved
|
||||
# explicitly by the decompressor linker script.
|
||||
#
|
||||
STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub
|
||||
STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS
|
||||
|
@ -303,8 +303,10 @@ fail:
|
||||
* The value chosen is the largest non-zero power of 2 suitable for this purpose
|
||||
* both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can
|
||||
* be mapped efficiently.
|
||||
* Since 32-bit ARM could potentially execute with a 1G/3G user/kernel split,
|
||||
* map everything below 1 GB.
|
||||
*/
|
||||
#define EFI_RT_VIRTUAL_BASE 0x40000000
|
||||
#define EFI_RT_VIRTUAL_BASE SZ_512M
|
||||
|
||||
static int cmp_mem_desc(const void *l, const void *r)
|
||||
{
|
||||
|
85
drivers/firmware/efi/libstub/arm32-stub.c
Normal file
85
drivers/firmware/efi/libstub/arm32-stub.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Linaro Ltd; <roy.franz@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#include <linux/efi.h>
|
||||
#include <asm/efi.h>
|
||||
|
||||
efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
|
||||
unsigned long *image_addr,
|
||||
unsigned long *image_size,
|
||||
unsigned long *reserve_addr,
|
||||
unsigned long *reserve_size,
|
||||
unsigned long dram_base,
|
||||
efi_loaded_image_t *image)
|
||||
{
|
||||
unsigned long nr_pages;
|
||||
efi_status_t status;
|
||||
/* Use alloc_addr to tranlsate between types */
|
||||
efi_physical_addr_t alloc_addr;
|
||||
|
||||
/*
|
||||
* Verify that the DRAM base address is compatible with the ARM
|
||||
* boot protocol, which determines the base of DRAM by masking
|
||||
* off the low 27 bits of the address at which the zImage is
|
||||
* loaded. These assumptions are made by the decompressor,
|
||||
* before any memory map is available.
|
||||
*/
|
||||
dram_base = round_up(dram_base, SZ_128M);
|
||||
|
||||
/*
|
||||
* Reserve memory for the uncompressed kernel image. This is
|
||||
* all that prevents any future allocations from conflicting
|
||||
* with the kernel. Since we can't tell from the compressed
|
||||
* image how much DRAM the kernel actually uses (due to BSS
|
||||
* size uncertainty) we allocate the maximum possible size.
|
||||
* Do this very early, as prints can cause memory allocations
|
||||
* that may conflict with this.
|
||||
*/
|
||||
alloc_addr = dram_base;
|
||||
*reserve_size = MAX_UNCOMP_KERNEL_SIZE;
|
||||
nr_pages = round_up(*reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
|
||||
status = sys_table->boottime->allocate_pages(EFI_ALLOCATE_ADDRESS,
|
||||
EFI_LOADER_DATA,
|
||||
nr_pages, &alloc_addr);
|
||||
if (status != EFI_SUCCESS) {
|
||||
*reserve_size = 0;
|
||||
pr_efi_err(sys_table, "Unable to allocate memory for uncompressed kernel.\n");
|
||||
return status;
|
||||
}
|
||||
*reserve_addr = alloc_addr;
|
||||
|
||||
/*
|
||||
* Relocate the zImage, so that it appears in the lowest 128 MB
|
||||
* memory window.
|
||||
*/
|
||||
*image_size = image->image_size;
|
||||
status = efi_relocate_kernel(sys_table, image_addr, *image_size,
|
||||
*image_size,
|
||||
dram_base + MAX_UNCOMP_KERNEL_SIZE, 0);
|
||||
if (status != EFI_SUCCESS) {
|
||||
pr_efi_err(sys_table, "Failed to relocate kernel.\n");
|
||||
efi_free(sys_table, *reserve_size, *reserve_addr);
|
||||
*reserve_size = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if we were able to allocate memory low enough
|
||||
* in memory. The kernel determines the base of DRAM from the
|
||||
* address at which the zImage is loaded.
|
||||
*/
|
||||
if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) {
|
||||
pr_efi_err(sys_table, "Failed to relocate kernel, no low memory available.\n");
|
||||
efi_free(sys_table, *reserve_size, *reserve_addr);
|
||||
*reserve_size = 0;
|
||||
efi_free(sys_table, *image_size, *image_addr);
|
||||
*image_size = 0;
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
@ -25,6 +25,7 @@ enum {
|
||||
MEMBLOCK_NONE = 0x0, /* No special request */
|
||||
MEMBLOCK_HOTPLUG = 0x1, /* hotpluggable region */
|
||||
MEMBLOCK_MIRROR = 0x2, /* mirrored region */
|
||||
MEMBLOCK_NOMAP = 0x4, /* don't add to kernel direct mapping */
|
||||
};
|
||||
|
||||
struct memblock_region {
|
||||
@ -82,6 +83,7 @@ bool memblock_overlaps_region(struct memblock_type *type,
|
||||
int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size);
|
||||
int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size);
|
||||
int memblock_mark_mirror(phys_addr_t base, phys_addr_t size);
|
||||
int memblock_mark_nomap(phys_addr_t base, phys_addr_t size);
|
||||
ulong choose_memblock_flags(void);
|
||||
|
||||
/* Low level functions */
|
||||
@ -184,6 +186,11 @@ static inline bool memblock_is_mirror(struct memblock_region *m)
|
||||
return m->flags & MEMBLOCK_MIRROR;
|
||||
}
|
||||
|
||||
static inline bool memblock_is_nomap(struct memblock_region *m)
|
||||
{
|
||||
return m->flags & MEMBLOCK_NOMAP;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
|
||||
int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn,
|
||||
unsigned long *end_pfn);
|
||||
@ -319,6 +326,7 @@ phys_addr_t memblock_start_of_DRAM(void);
|
||||
phys_addr_t memblock_end_of_DRAM(void);
|
||||
void memblock_enforce_memory_limit(phys_addr_t memory_limit);
|
||||
int memblock_is_memory(phys_addr_t addr);
|
||||
int memblock_is_map_memory(phys_addr_t addr);
|
||||
int memblock_is_region_memory(phys_addr_t base, phys_addr_t size);
|
||||
int memblock_is_reserved(phys_addr_t addr);
|
||||
bool memblock_is_region_reserved(phys_addr_t base, phys_addr_t size);
|
||||
|
@ -822,6 +822,17 @@ int __init_memblock memblock_mark_mirror(phys_addr_t base, phys_addr_t size)
|
||||
return memblock_setclr_flag(base, size, 1, MEMBLOCK_MIRROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* memblock_mark_nomap - Mark a memory region with flag MEMBLOCK_NOMAP.
|
||||
* @base: the base phys addr of the region
|
||||
* @size: the size of the region
|
||||
*
|
||||
* Return 0 on success, -errno on failure.
|
||||
*/
|
||||
int __init_memblock memblock_mark_nomap(phys_addr_t base, phys_addr_t size)
|
||||
{
|
||||
return memblock_setclr_flag(base, size, 1, MEMBLOCK_NOMAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* __next_reserved_mem_region - next function for for_each_reserved_region()
|
||||
@ -913,6 +924,10 @@ void __init_memblock __next_mem_range(u64 *idx, int nid, ulong flags,
|
||||
if ((flags & MEMBLOCK_MIRROR) && !memblock_is_mirror(m))
|
||||
continue;
|
||||
|
||||
/* skip nomap memory unless we were asked for it explicitly */
|
||||
if (!(flags & MEMBLOCK_NOMAP) && memblock_is_nomap(m))
|
||||
continue;
|
||||
|
||||
if (!type_b) {
|
||||
if (out_start)
|
||||
*out_start = m_start;
|
||||
@ -1022,6 +1037,10 @@ void __init_memblock __next_mem_range_rev(u64 *idx, int nid, ulong flags,
|
||||
if ((flags & MEMBLOCK_MIRROR) && !memblock_is_mirror(m))
|
||||
continue;
|
||||
|
||||
/* skip nomap memory unless we were asked for it explicitly */
|
||||
if (!(flags & MEMBLOCK_NOMAP) && memblock_is_nomap(m))
|
||||
continue;
|
||||
|
||||
if (!type_b) {
|
||||
if (out_start)
|
||||
*out_start = m_start;
|
||||
@ -1519,6 +1538,15 @@ int __init_memblock memblock_is_memory(phys_addr_t addr)
|
||||
return memblock_search(&memblock.memory, addr) != -1;
|
||||
}
|
||||
|
||||
int __init_memblock memblock_is_map_memory(phys_addr_t addr)
|
||||
{
|
||||
int i = memblock_search(&memblock.memory, addr);
|
||||
|
||||
if (i == -1)
|
||||
return false;
|
||||
return !memblock_is_nomap(&memblock.memory.regions[i]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
|
||||
int __init_memblock memblock_search_pfn_nid(unsigned long pfn,
|
||||
unsigned long *start_pfn, unsigned long *end_pfn)
|
||||
|
Loading…
Reference in New Issue
Block a user