mirror of
https://github.com/torvalds/linux.git
synced 2026-06-08 22:52:35 +02:00
Merge branch 'linux-linaro-lsk' into linux-linaro-lsk-android
Conflicts: drivers/of/fdt.c
This commit is contained in:
commit
c98536343e
|
|
@ -111,8 +111,14 @@ Before jumping into the kernel, the following conditions must be met:
|
|||
- Caches, MMUs
|
||||
The MMU must be off.
|
||||
Instruction cache may be on or off.
|
||||
Data cache must be off and invalidated.
|
||||
External caches (if present) must be configured and disabled.
|
||||
The address range corresponding to the loaded kernel image must be
|
||||
cleaned to the PoC. In the presence of a system cache or other
|
||||
coherent masters with caches enabled, this will typically require
|
||||
cache maintenance by VA rather than set/way operations.
|
||||
System caches which respect the architected cache maintenance by VA
|
||||
operations must be configured and may be enabled.
|
||||
System caches which do not respect architected cache maintenance by VA
|
||||
operations (not recommended) must be configured and disabled.
|
||||
|
||||
- Architected timers
|
||||
CNTFRQ must be programmed with the timer frequency.
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ struct machine_desc * __init setup_machine_fdt(void *dt)
|
|||
const char *model, *compat;
|
||||
void *clk;
|
||||
char manufacturer[16];
|
||||
unsigned long len;
|
||||
int len;
|
||||
|
||||
/* check device tree validity */
|
||||
if (be32_to_cpu(devtree->magic) != OF_DT_HEADER)
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
|
|||
}
|
||||
if (!mdesc_best) {
|
||||
const char *prop;
|
||||
long size;
|
||||
int size;
|
||||
|
||||
early_print("\nError: unrecognized/unsupported "
|
||||
"device tree compatible list:\n[ ");
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ static int __init vexpress_dt_find_scu(unsigned long node,
|
|||
{
|
||||
if (of_flat_dt_match(node, vexpress_dt_cortex_a9_match)) {
|
||||
phys_addr_t phys_addr;
|
||||
__be32 *reg = of_get_flat_dt_prop(node, "reg", NULL);
|
||||
const __be32 *reg = of_get_flat_dt_prop(node, "reg", NULL);
|
||||
|
||||
if (WARN_ON(!reg))
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -116,8 +116,8 @@ device_initcall(s5p_mfc_memory_init);
|
|||
int __init s5p_fdt_find_mfc_mem(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
__be32 *prop;
|
||||
unsigned long len;
|
||||
const __be32 *prop;
|
||||
int len;
|
||||
struct s5p_mfc_dt_meminfo *mfc_mem = data;
|
||||
|
||||
if (!data)
|
||||
|
|
|
|||
|
|
@ -24,3 +24,7 @@ memory {
|
|||
reg = < 0x1 0x00000000 0x0 0x80000000 >; /* Updated by bootloader */
|
||||
};
|
||||
};
|
||||
|
||||
&serial0 {
|
||||
status = "ok";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -273,8 +273,9 @@ rtcclk: rtcclk@17000000 {
|
|||
};
|
||||
|
||||
serial0: serial@1c020000 {
|
||||
status = "disabled";
|
||||
device_type = "serial";
|
||||
compatible = "ns16550";
|
||||
compatible = "ns16550a";
|
||||
reg = <0 0x1c020000 0x0 0x1000>;
|
||||
reg-shift = <2>;
|
||||
clock-frequency = <10000000>; /* Updated by bootloader */
|
||||
|
|
@ -282,6 +283,39 @@ serial0: serial@1c020000 {
|
|||
interrupts = <0x0 0x4c 0x4>;
|
||||
};
|
||||
|
||||
serial1: serial@1c021000 {
|
||||
status = "disabled";
|
||||
device_type = "serial";
|
||||
compatible = "ns16550a";
|
||||
reg = <0 0x1c021000 0x0 0x1000>;
|
||||
reg-shift = <2>;
|
||||
clock-frequency = <10000000>; /* Updated by bootloader */
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0x0 0x4d 0x4>;
|
||||
};
|
||||
|
||||
serial2: serial@1c022000 {
|
||||
status = "disabled";
|
||||
device_type = "serial";
|
||||
compatible = "ns16550a";
|
||||
reg = <0 0x1c022000 0x0 0x1000>;
|
||||
reg-shift = <2>;
|
||||
clock-frequency = <10000000>; /* Updated by bootloader */
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0x0 0x4e 0x4>;
|
||||
};
|
||||
|
||||
serial3: serial@1c023000 {
|
||||
status = "disabled";
|
||||
device_type = "serial";
|
||||
compatible = "ns16550a";
|
||||
reg = <0 0x1c023000 0x0 0x1000>;
|
||||
reg-shift = <2>;
|
||||
clock-frequency = <10000000>; /* Updated by bootloader */
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0x0 0x4f 0x4>;
|
||||
};
|
||||
|
||||
phy1: phy@1f21a000 {
|
||||
compatible = "apm,xgene-phy";
|
||||
reg = <0x0 0x1f21a000 0x0 0x100>;
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ extern void __memset_io(volatile void __iomem *, int, size_t);
|
|||
*/
|
||||
extern void __iomem *__ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot);
|
||||
extern void __iounmap(volatile void __iomem *addr);
|
||||
extern void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size);
|
||||
|
||||
#define ioremap(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
|
||||
#define ioremap_nocache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
|
||||
|
|
|
|||
|
|
@ -97,6 +97,12 @@
|
|||
#define MT_NORMAL_NC 3
|
||||
#define MT_NORMAL 4
|
||||
|
||||
/*
|
||||
* Memory types for Stage-2 translation
|
||||
*/
|
||||
#define MT_S2_NORMAL 0xf
|
||||
#define MT_S2_DEVICE_nGnRE 0x1
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
extern phys_addr_t memstart_addr;
|
||||
|
|
|
|||
|
|
@ -31,5 +31,7 @@ extern void paging_init(void);
|
|||
extern void setup_mm_for_reboot(void);
|
||||
extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt);
|
||||
extern void init_mem_pgprot(void);
|
||||
/* create an identity mapping for memory (or io if map_io is true) */
|
||||
extern void create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@
|
|||
/* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */
|
||||
#define __HAVE_ARCH_GATE_AREA 1
|
||||
|
||||
/*
|
||||
* The idmap and swapper page tables need some space reserved in the kernel
|
||||
* image. The idmap only requires a pgd and a next level table to (section) map
|
||||
* the kernel, while the swapper also maps the FDT and requires an additional
|
||||
* table to map an early UART. See __create_page_tables for more information.
|
||||
*/
|
||||
#define SWAPPER_DIR_SIZE (3 * PAGE_SIZE)
|
||||
#define IDMAP_DIR_SIZE (2 * PAGE_SIZE)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#ifdef CONFIG_ARM64_64K_PAGES
|
||||
|
|
|
|||
|
|
@ -79,6 +79,24 @@
|
|||
#define PTE_ATTRINDX(t) (_AT(pteval_t, (t)) << 2)
|
||||
#define PTE_ATTRINDX_MASK (_AT(pteval_t, 7) << 2)
|
||||
|
||||
/*
|
||||
* 2nd stage PTE definitions
|
||||
*/
|
||||
#define PTE_S2_RDONLY (_AT(pteval_t, 1) << 6) /* HAP[2:1] */
|
||||
#define PTE_S2_RDWR (_AT(pteval_t, 3) << 6) /* HAP[2:1] */
|
||||
|
||||
/*
|
||||
* Memory Attribute override for Stage-2 (MemAttr[3:0])
|
||||
*/
|
||||
#define PTE_S2_MEMATTR(t) (_AT(pteval_t, (t)) << 2)
|
||||
#define PTE_S2_MEMATTR_MASK (_AT(pteval_t, 0xf) << 2)
|
||||
|
||||
/*
|
||||
* EL2/HYP PTE/PMD definitions
|
||||
*/
|
||||
#define PMD_HYP PMD_SECT_USER
|
||||
#define PTE_HYP PTE_USER
|
||||
|
||||
/*
|
||||
* 40-bit physical address supported.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -288,6 +288,12 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
|
|||
|
||||
#define pmd_bad(pmd) (!(pmd_val(pmd) & 2))
|
||||
|
||||
#define pmd_table(pmd) ((pmd_val(pmd) & PMD_TYPE_MASK) == \
|
||||
PMD_TYPE_TABLE)
|
||||
#define pmd_sect(pmd) ((pmd_val(pmd) & PMD_TYPE_MASK) == \
|
||||
PMD_TYPE_SECT)
|
||||
|
||||
|
||||
static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)
|
||||
{
|
||||
*pmdp = pmd;
|
||||
|
|
@ -372,9 +378,6 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
|
|||
extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
|
||||
extern pgd_t idmap_pg_dir[PTRS_PER_PGD];
|
||||
|
||||
#define SWAPPER_DIR_SIZE (3 * PAGE_SIZE)
|
||||
#define IDMAP_DIR_SIZE (2 * PAGE_SIZE)
|
||||
|
||||
/*
|
||||
* Encode and decode a swap entry:
|
||||
* bits 0-1: present (must be zero)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <asm/assembler.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/memory.h>
|
||||
#include <asm/thread_info.h>
|
||||
|
|
@ -34,29 +35,17 @@
|
|||
#include <asm/page.h>
|
||||
#include <asm/virt.h>
|
||||
|
||||
/*
|
||||
* swapper_pg_dir is the virtual address of the initial page table. We place
|
||||
* the page tables 3 * PAGE_SIZE below KERNEL_RAM_VADDR. The idmap_pg_dir has
|
||||
* 2 pages and is placed below swapper_pg_dir.
|
||||
*/
|
||||
#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
|
||||
|
||||
#if (KERNEL_RAM_VADDR & 0xfffff) != 0x80000
|
||||
#error KERNEL_RAM_VADDR must start at 0xXXX80000
|
||||
#endif
|
||||
|
||||
#define SWAPPER_DIR_SIZE (3 * PAGE_SIZE)
|
||||
#define IDMAP_DIR_SIZE (2 * PAGE_SIZE)
|
||||
|
||||
.globl swapper_pg_dir
|
||||
.equ swapper_pg_dir, KERNEL_RAM_VADDR - SWAPPER_DIR_SIZE
|
||||
|
||||
.globl idmap_pg_dir
|
||||
.equ idmap_pg_dir, swapper_pg_dir - IDMAP_DIR_SIZE
|
||||
|
||||
.macro pgtbl, ttb0, ttb1, phys
|
||||
add \ttb1, \phys, #TEXT_OFFSET - SWAPPER_DIR_SIZE
|
||||
sub \ttb0, \ttb1, #IDMAP_DIR_SIZE
|
||||
.macro pgtbl, ttb0, ttb1, virt_to_phys
|
||||
ldr \ttb1, =swapper_pg_dir
|
||||
ldr \ttb0, =idmap_pg_dir
|
||||
add \ttb1, \ttb1, \virt_to_phys
|
||||
add \ttb0, \ttb0, \virt_to_phys
|
||||
.endm
|
||||
|
||||
#ifdef CONFIG_ARM64_64K_PAGES
|
||||
|
|
@ -240,8 +229,9 @@ ENDPROC(set_cpu_boot_mode_flag)
|
|||
* This is not in .bss, because we set it sufficiently early that the boot-time
|
||||
* zeroing of .bss would clobber it.
|
||||
*/
|
||||
.pushsection .data
|
||||
.pushsection .data..cacheline_aligned
|
||||
ENTRY(__boot_cpu_mode)
|
||||
.align L1_CACHE_SHIFT
|
||||
.long BOOT_CPU_MODE_EL2
|
||||
.long 0
|
||||
.popsection
|
||||
|
|
@ -297,7 +287,7 @@ ENTRY(secondary_startup)
|
|||
mov x23, x0 // x23=current cpu_table
|
||||
cbz x23, __error_p // invalid processor (x23=0)?
|
||||
|
||||
pgtbl x25, x26, x24 // x25=TTBR0, x26=TTBR1
|
||||
pgtbl x25, x26, x28 // x25=TTBR0, x26=TTBR1
|
||||
ldr x12, [x23, #CPU_INFO_SETUP]
|
||||
add x12, x12, x28 // __virt_to_phys
|
||||
blr x12 // initialise processor
|
||||
|
|
@ -339,8 +329,13 @@ ENDPROC(__enable_mmu)
|
|||
* x27 = *virtual* address to jump to upon completion
|
||||
*
|
||||
* other registers depend on the function called upon completion
|
||||
*
|
||||
* We align the entire function to the smallest power of two larger than it to
|
||||
* ensure it fits within a single block map entry. Otherwise were PHYS_OFFSET
|
||||
* close to the end of a 512MB or 1GB block we might require an additional
|
||||
* table to map the entire function.
|
||||
*/
|
||||
.align 6
|
||||
.align 4
|
||||
__turn_mmu_on:
|
||||
msr sctlr_el1, x0
|
||||
isb
|
||||
|
|
@ -406,7 +401,16 @@ ENDPROC(__calc_phys_offset)
|
|||
* - pgd entry for fixed mappings (TTBR1)
|
||||
*/
|
||||
__create_page_tables:
|
||||
pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses
|
||||
pgtbl x25, x26, x28 // idmap_pg_dir and swapper_pg_dir addresses
|
||||
mov x27, lr
|
||||
|
||||
/*
|
||||
* Invalidate the idmap and swapper page tables to avoid potential
|
||||
* dirty cache lines being evicted.
|
||||
*/
|
||||
mov x0, x25
|
||||
add x1, x26, #SWAPPER_DIR_SIZE
|
||||
bl __inval_cache_range
|
||||
|
||||
/*
|
||||
* Clear the idmap and swapper page tables.
|
||||
|
|
@ -466,6 +470,17 @@ __create_page_tables:
|
|||
ldr x5, =FIXADDR_TOP // Fixed mapping virtual address
|
||||
add x0, x26, #2 * PAGE_SIZE // section table address
|
||||
create_pgd_entry x26, x0, x5, x6, x7
|
||||
|
||||
/*
|
||||
* Since the page tables have been populated with non-cacheable
|
||||
* accesses (MMU disabled), invalidate the idmap and swapper page
|
||||
* tables again to remove any speculatively loaded cache lines.
|
||||
*/
|
||||
mov x0, x25
|
||||
add x1, x26, #SWAPPER_DIR_SIZE
|
||||
bl __inval_cache_range
|
||||
|
||||
mov lr, x27
|
||||
ret
|
||||
ENDPROC(__create_page_tables)
|
||||
.ltorg
|
||||
|
|
@ -475,7 +490,7 @@ ENDPROC(__create_page_tables)
|
|||
__switch_data:
|
||||
.quad __mmap_switched
|
||||
.quad __bss_start // x6
|
||||
.quad _end // x7
|
||||
.quad __bss_stop // x7
|
||||
.quad processor_id // x4
|
||||
.quad __fdt_pointer // x5
|
||||
.quad memstart_addr // x6
|
||||
|
|
|
|||
|
|
@ -138,11 +138,12 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
|
|||
int uses_interp)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
unsigned long vdso_base, vdso_mapping_len;
|
||||
unsigned long vdso_base, vdso_text_len, vdso_mapping_len;
|
||||
int ret;
|
||||
|
||||
vdso_text_len = vdso_pages << PAGE_SHIFT;
|
||||
/* Be sure to map the data page */
|
||||
vdso_mapping_len = (vdso_pages + 1) << PAGE_SHIFT;
|
||||
vdso_mapping_len = vdso_text_len + PAGE_SIZE;
|
||||
|
||||
down_write(&mm->mmap_sem);
|
||||
vdso_base = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0);
|
||||
|
|
@ -152,35 +153,52 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
|
|||
}
|
||||
mm->context.vdso = (void *)vdso_base;
|
||||
|
||||
ret = install_special_mapping(mm, vdso_base, vdso_mapping_len,
|
||||
ret = install_special_mapping(mm, vdso_base, vdso_text_len,
|
||||
VM_READ|VM_EXEC|
|
||||
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
|
||||
vdso_pagelist);
|
||||
if (ret) {
|
||||
mm->context.vdso = NULL;
|
||||
if (ret)
|
||||
goto up_fail;
|
||||
}
|
||||
|
||||
vdso_base += vdso_text_len;
|
||||
ret = install_special_mapping(mm, vdso_base, PAGE_SIZE,
|
||||
VM_READ|VM_MAYREAD,
|
||||
vdso_pagelist + vdso_pages);
|
||||
if (ret)
|
||||
goto up_fail;
|
||||
|
||||
up_write(&mm->mmap_sem);
|
||||
return 0;
|
||||
|
||||
up_fail:
|
||||
mm->context.vdso = NULL;
|
||||
up_write(&mm->mmap_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *arch_vma_name(struct vm_area_struct *vma)
|
||||
{
|
||||
unsigned long vdso_text;
|
||||
|
||||
if (!vma->vm_mm)
|
||||
return NULL;
|
||||
|
||||
vdso_text = (unsigned long)vma->vm_mm->context.vdso;
|
||||
|
||||
/*
|
||||
* We can re-use the vdso pointer in mm_context_t for identifying
|
||||
* the vectors page for compat applications. The vDSO will always
|
||||
* sit above TASK_UNMAPPED_BASE and so we don't need to worry about
|
||||
* it conflicting with the vectors base.
|
||||
*/
|
||||
if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso) {
|
||||
if (vma->vm_start == vdso_text) {
|
||||
#ifdef CONFIG_COMPAT
|
||||
if (vma->vm_start == AARCH32_VECTORS_BASE)
|
||||
return "[vectors]";
|
||||
#endif
|
||||
return "[vdso]";
|
||||
} else if (vma->vm_start == (vdso_text + (vdso_pages << PAGE_SHIFT))) {
|
||||
return "[vvar]";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ $(obj-vdso): %.o: %.S
|
|||
$(call if_changed_dep,vdsoas)
|
||||
|
||||
# Actual build commands
|
||||
quiet_cmd_vdsold = VDSOL $@
|
||||
quiet_cmd_vdsold = VDSOL $@
|
||||
cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@
|
||||
quiet_cmd_vdsoas = VDSOA $@
|
||||
quiet_cmd_vdsoas = VDSOA $@
|
||||
cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $<
|
||||
|
||||
# Install commands for the unstripped file
|
||||
|
|
|
|||
|
|
@ -90,6 +90,13 @@ SECTIONS
|
|||
_edata = .;
|
||||
|
||||
BSS_SECTION(0, 0, 0)
|
||||
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
idmap_pg_dir = .;
|
||||
. += IDMAP_DIR_SIZE;
|
||||
swapper_pg_dir = .;
|
||||
. += SWAPPER_DIR_SIZE;
|
||||
|
||||
_end = .;
|
||||
|
||||
STABS_DEBUG
|
||||
|
|
|
|||
|
|
@ -167,6 +167,14 @@ ENTRY(__flush_dcache_area)
|
|||
ret
|
||||
ENDPROC(__flush_dcache_area)
|
||||
|
||||
/*
|
||||
* __inval_cache_range(start, end)
|
||||
* - start - start address of region
|
||||
* - end - end address of region
|
||||
*/
|
||||
ENTRY(__inval_cache_range)
|
||||
/* FALLTHROUGH */
|
||||
|
||||
/*
|
||||
* __dma_inv_range(start, end)
|
||||
* - start - virtual start address of region
|
||||
|
|
@ -190,6 +198,7 @@ __dma_inv_range:
|
|||
b.lo 2b
|
||||
dsb sy
|
||||
ret
|
||||
ENDPROC(__inval_cache_range)
|
||||
ENDPROC(__dma_inv_range)
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@ void __cpu_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)
|
|||
copy_page(kto, kfrom);
|
||||
__flush_dcache_area(kto, PAGE_SIZE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__cpu_copy_user_page);
|
||||
|
||||
void __cpu_clear_user_page(void *kaddr, unsigned long vaddr)
|
||||
{
|
||||
clear_page(kaddr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__cpu_clear_user_page);
|
||||
|
|
|
|||
|
|
@ -136,7 +136,10 @@ void __init arm64_memblock_init(void)
|
|||
{
|
||||
u64 *reserve_map, base, size;
|
||||
|
||||
/* Register the kernel text, kernel data and initrd with memblock */
|
||||
/*
|
||||
* Register the kernel text, kernel data, initrd, and initial
|
||||
* pagetables with memblock.
|
||||
*/
|
||||
memblock_reserve(__pa(_text), _end - _text);
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
if (phys_initrd_size) {
|
||||
|
|
@ -148,13 +151,6 @@ void __init arm64_memblock_init(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Reserve the page tables. These are already in use,
|
||||
* and can only be in node 0.
|
||||
*/
|
||||
memblock_reserve(__pa(swapper_pg_dir), SWAPPER_DIR_SIZE);
|
||||
memblock_reserve(__pa(idmap_pg_dir), IDMAP_DIR_SIZE);
|
||||
|
||||
/* Reserve the dtb region */
|
||||
memblock_reserve(virt_to_phys(initial_boot_params),
|
||||
be32_to_cpu(initial_boot_params->totalsize));
|
||||
|
|
|
|||
|
|
@ -136,7 +136,8 @@ static void __init *early_alloc(unsigned long sz)
|
|||
}
|
||||
|
||||
static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
|
||||
unsigned long end, unsigned long pfn)
|
||||
unsigned long end, unsigned long pfn,
|
||||
pgprot_t prot)
|
||||
{
|
||||
pte_t *pte;
|
||||
|
||||
|
|
@ -148,16 +149,28 @@ static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
|
|||
|
||||
pte = pte_offset_kernel(pmd, addr);
|
||||
do {
|
||||
set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
|
||||
set_pte(pte, pfn_pte(pfn, prot));
|
||||
pfn++;
|
||||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
}
|
||||
|
||||
static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
|
||||
unsigned long end, phys_addr_t phys)
|
||||
unsigned long end, phys_addr_t phys,
|
||||
int map_io)
|
||||
{
|
||||
pmd_t *pmd;
|
||||
unsigned long next;
|
||||
pmdval_t prot_sect;
|
||||
pgprot_t prot_pte;
|
||||
|
||||
if (map_io) {
|
||||
prot_sect = PMD_TYPE_SECT | PMD_SECT_AF |
|
||||
PMD_ATTRINDX(MT_DEVICE_nGnRE);
|
||||
prot_pte = __pgprot(PROT_DEVICE_nGnRE);
|
||||
} else {
|
||||
prot_sect = PROT_SECT_NORMAL_EXEC;
|
||||
prot_pte = PAGE_KERNEL_EXEC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for initial section mappings in the pgd/pud and remove them.
|
||||
|
|
@ -173,7 +186,7 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
|
|||
/* try section mapping first */
|
||||
if (((addr | next | phys) & ~SECTION_MASK) == 0) {
|
||||
pmd_t old_pmd =*pmd;
|
||||
set_pmd(pmd, __pmd(phys | PROT_SECT_NORMAL_EXEC));
|
||||
set_pmd(pmd, __pmd(phys | prot_sect));
|
||||
/*
|
||||
* Check for previous table entries created during
|
||||
* boot (__create_page_tables) and flush them.
|
||||
|
|
@ -181,21 +194,23 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
|
|||
if (!pmd_none(old_pmd))
|
||||
flush_tlb_all();
|
||||
} else {
|
||||
alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys));
|
||||
alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys),
|
||||
prot_pte);
|
||||
}
|
||||
phys += next - addr;
|
||||
} while (pmd++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
|
||||
unsigned long end, unsigned long phys)
|
||||
unsigned long end, unsigned long phys,
|
||||
int map_io)
|
||||
{
|
||||
pud_t *pud = pud_offset(pgd, addr);
|
||||
unsigned long next;
|
||||
|
||||
do {
|
||||
next = pud_addr_end(addr, end);
|
||||
alloc_init_pmd(pud, addr, next, phys);
|
||||
alloc_init_pmd(pud, addr, next, phys, map_io);
|
||||
phys += next - addr;
|
||||
} while (pud++, addr = next, addr != end);
|
||||
}
|
||||
|
|
@ -204,30 +219,44 @@ static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
|
|||
* Create the page directory entries and any necessary page tables for the
|
||||
* mapping specified by 'md'.
|
||||
*/
|
||||
static void __init create_mapping(phys_addr_t phys, unsigned long virt,
|
||||
phys_addr_t size)
|
||||
static void __init __create_mapping(pgd_t *pgd, phys_addr_t phys,
|
||||
unsigned long virt, phys_addr_t size,
|
||||
int map_io)
|
||||
{
|
||||
unsigned long addr, length, end, next;
|
||||
pgd_t *pgd;
|
||||
|
||||
if (virt < VMALLOC_START) {
|
||||
pr_warning("BUG: not creating mapping for 0x%016llx at 0x%016lx - outside kernel range\n",
|
||||
phys, virt);
|
||||
return;
|
||||
}
|
||||
|
||||
addr = virt & PAGE_MASK;
|
||||
length = PAGE_ALIGN(size + (virt & ~PAGE_MASK));
|
||||
|
||||
pgd = pgd_offset_k(addr);
|
||||
end = addr + length;
|
||||
do {
|
||||
next = pgd_addr_end(addr, end);
|
||||
alloc_init_pud(pgd, addr, next, phys);
|
||||
alloc_init_pud(pgd, addr, next, phys, map_io);
|
||||
phys += next - addr;
|
||||
} while (pgd++, addr = next, addr != end);
|
||||
}
|
||||
|
||||
static void __init create_mapping(phys_addr_t phys, unsigned long virt,
|
||||
phys_addr_t size)
|
||||
{
|
||||
if (virt < VMALLOC_START) {
|
||||
pr_warn("BUG: not creating mapping for %pa at 0x%016lx - outside kernel range\n",
|
||||
&phys, virt);
|
||||
return;
|
||||
}
|
||||
__create_mapping(pgd_offset_k(virt & PAGE_MASK), phys, virt, size, 0);
|
||||
}
|
||||
|
||||
void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io)
|
||||
{
|
||||
if ((addr >> PGDIR_SHIFT) >= ARRAY_SIZE(idmap_pg_dir)) {
|
||||
pr_warn("BUG: not creating id mapping for %pa\n", &addr);
|
||||
return;
|
||||
}
|
||||
__create_mapping(&idmap_pg_dir[pgd_index(addr)],
|
||||
addr, addr, size, map_io);
|
||||
}
|
||||
|
||||
static void __init map_mem(void)
|
||||
{
|
||||
struct memblock_region *reg;
|
||||
|
|
@ -342,6 +371,9 @@ int kern_addr_valid(unsigned long addr)
|
|||
if (pmd_none(*pmd))
|
||||
return 0;
|
||||
|
||||
if (pmd_sect(*pmd))
|
||||
return pfn_valid(pmd_pfn(*pmd));
|
||||
|
||||
pte = pte_offset_kernel(pmd, addr);
|
||||
if (pte_none(*pte))
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -52,13 +52,13 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_EARLY_PRINTK
|
||||
static char *stdout;
|
||||
static const char *stdout;
|
||||
|
||||
static int __init early_init_dt_scan_chosen_serial(unsigned long node,
|
||||
const char *uname, int depth, void *data)
|
||||
{
|
||||
unsigned long l;
|
||||
char *p;
|
||||
int l;
|
||||
const char *p;
|
||||
|
||||
pr_debug("%s: depth: %d, uname: %s\n", __func__, depth, uname);
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ static int __init early_init_dt_scan_chosen_serial(unsigned long node,
|
|||
(strncmp(p, "xlnx,opb-uartlite", 17) == 0) ||
|
||||
(strncmp(p, "xlnx,axi-uartlite", 17) == 0) ||
|
||||
(strncmp(p, "xlnx,mdm", 8) == 0)) {
|
||||
unsigned int *addrp;
|
||||
const unsigned int *addrp;
|
||||
|
||||
*(u32 *)data = UARTLITE;
|
||||
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ int crash_mem_ranges;
|
|||
int __init early_init_dt_scan_fw_dump(unsigned long node,
|
||||
const char *uname, int depth, void *data)
|
||||
{
|
||||
__be32 *sections;
|
||||
const __be32 *sections;
|
||||
int i, num_sections;
|
||||
unsigned long size;
|
||||
int size;
|
||||
const int *token;
|
||||
|
||||
if (depth != 1 || strcmp(uname, "rtas") != 0)
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ static struct ibm_pa_feature {
|
|||
{CPU_FTR_REAL_LE, PPC_FEATURE_TRUE_LE, 5, 0, 0},
|
||||
};
|
||||
|
||||
static void __init scan_features(unsigned long node, unsigned char *ftrs,
|
||||
static void __init scan_features(unsigned long node, const unsigned char *ftrs,
|
||||
unsigned long tablelen,
|
||||
struct ibm_pa_feature *fp,
|
||||
unsigned long ft_size)
|
||||
|
|
@ -201,8 +201,8 @@ static void __init scan_features(unsigned long node, unsigned char *ftrs,
|
|||
|
||||
static void __init check_cpu_pa_features(unsigned long node)
|
||||
{
|
||||
unsigned char *pa_ftrs;
|
||||
unsigned long tablelen;
|
||||
const unsigned char *pa_ftrs;
|
||||
int tablelen;
|
||||
|
||||
pa_ftrs = of_get_flat_dt_prop(node, "ibm,pa-features", &tablelen);
|
||||
if (pa_ftrs == NULL)
|
||||
|
|
@ -215,7 +215,7 @@ static void __init check_cpu_pa_features(unsigned long node)
|
|||
#ifdef CONFIG_PPC_STD_MMU_64
|
||||
static void __init check_cpu_slb_size(unsigned long node)
|
||||
{
|
||||
u32 *slb_size_ptr;
|
||||
const __be32 *slb_size_ptr;
|
||||
|
||||
slb_size_ptr = of_get_flat_dt_prop(node, "slb-size", NULL);
|
||||
if (slb_size_ptr != NULL) {
|
||||
|
|
@ -256,7 +256,7 @@ static struct feature_property {
|
|||
static inline void identical_pvr_fixup(unsigned long node)
|
||||
{
|
||||
unsigned int pvr;
|
||||
char *model = of_get_flat_dt_prop(node, "model", NULL);
|
||||
const char *model = of_get_flat_dt_prop(node, "model", NULL);
|
||||
|
||||
/*
|
||||
* Since 440GR(x)/440EP(x) processors have the same pvr,
|
||||
|
|
@ -294,11 +294,11 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
|
|||
const char *uname, int depth,
|
||||
void *data)
|
||||
{
|
||||
char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
const u32 *prop;
|
||||
const u32 *intserv;
|
||||
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
const __be32 *prop;
|
||||
const __be32 *intserv;
|
||||
int i, nthreads;
|
||||
unsigned long len;
|
||||
int len;
|
||||
int found = -1;
|
||||
int found_thread = 0;
|
||||
|
||||
|
|
@ -389,7 +389,7 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
|
|||
int __init early_init_dt_scan_chosen_ppc(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
unsigned long *lprop;
|
||||
const unsigned long *lprop; /* All these set by kernel, so no need to convert endian */
|
||||
|
||||
/* Use common scan routine to determine if this is the chosen node */
|
||||
if (early_init_dt_scan_chosen(node, uname, depth, data) == 0)
|
||||
|
|
@ -440,8 +440,9 @@ int __init early_init_dt_scan_chosen_ppc(unsigned long node, const char *uname,
|
|||
*/
|
||||
static int __init early_init_dt_scan_drconf_memory(unsigned long node)
|
||||
{
|
||||
__be32 *dm, *ls, *usm;
|
||||
unsigned long l, n, flags;
|
||||
const __be32 *dm, *ls, *usm;
|
||||
int l;
|
||||
unsigned long n, flags;
|
||||
u64 base, size, memblock_size;
|
||||
unsigned int is_kexec_kdump = 0, rngs;
|
||||
|
||||
|
|
|
|||
|
|
@ -1135,7 +1135,7 @@ void __init rtas_initialize(void)
|
|||
int __init early_init_dt_scan_rtas(unsigned long node,
|
||||
const char *uname, int depth, void *data)
|
||||
{
|
||||
u32 *basep, *entryp, *sizep;
|
||||
const u32 *basep, *entryp, *sizep;
|
||||
|
||||
if (depth != 1 || strcmp(uname, "rtas") != 0)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -250,9 +250,9 @@ static int __init htab_dt_scan_seg_sizes(unsigned long node,
|
|||
const char *uname, int depth,
|
||||
void *data)
|
||||
{
|
||||
char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
u32 *prop;
|
||||
unsigned long size = 0;
|
||||
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
const __be32 *prop;
|
||||
int size = 0;
|
||||
|
||||
/* We are scanning "cpu" nodes only */
|
||||
if (type == NULL || strcmp(type, "cpu") != 0)
|
||||
|
|
@ -306,9 +306,9 @@ static int __init htab_dt_scan_page_sizes(unsigned long node,
|
|||
const char *uname, int depth,
|
||||
void *data)
|
||||
{
|
||||
char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
u32 *prop;
|
||||
unsigned long size = 0;
|
||||
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
const __be32 *prop;
|
||||
int size = 0;
|
||||
|
||||
/* We are scanning "cpu" nodes only */
|
||||
if (type == NULL || strcmp(type, "cpu") != 0)
|
||||
|
|
@ -389,9 +389,9 @@ static int __init htab_dt_scan_page_sizes(unsigned long node,
|
|||
static int __init htab_dt_scan_hugepage_blocks(unsigned long node,
|
||||
const char *uname, int depth,
|
||||
void *data) {
|
||||
char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
unsigned long *addr_prop;
|
||||
u32 *page_count_prop;
|
||||
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
const __be64 *addr_prop;
|
||||
const __be32 *page_count_prop;
|
||||
unsigned int expected_pages;
|
||||
long unsigned int phys_addr;
|
||||
long unsigned int block_size;
|
||||
|
|
@ -533,8 +533,8 @@ static int __init htab_dt_scan_pftsize(unsigned long node,
|
|||
const char *uname, int depth,
|
||||
void *data)
|
||||
{
|
||||
char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
u32 *prop;
|
||||
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
const __be32 *prop;
|
||||
|
||||
/* We are scanning "cpu" nodes only */
|
||||
if (type == NULL || strcmp(type, "cpu") != 0)
|
||||
|
|
|
|||
|
|
@ -199,8 +199,8 @@ static void __init efika_setup_arch(void)
|
|||
|
||||
static int __init efika_probe(void)
|
||||
{
|
||||
char *model = of_get_flat_dt_prop(of_get_flat_dt_root(),
|
||||
"model", NULL);
|
||||
const char *model = of_get_flat_dt_prop(of_get_flat_dt_root(),
|
||||
"model", NULL);
|
||||
|
||||
if (model == NULL)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -574,8 +574,8 @@ chrp_init2(void)
|
|||
|
||||
static int __init chrp_probe(void)
|
||||
{
|
||||
char *dtype = of_get_flat_dt_prop(of_get_flat_dt_root(),
|
||||
"device_type", NULL);
|
||||
const char *dtype = of_get_flat_dt_prop(of_get_flat_dt_root(),
|
||||
"device_type", NULL);
|
||||
if (dtype == NULL)
|
||||
return 0;
|
||||
if (strcmp(dtype, "chrp"))
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ static unsigned int opal_irq_count;
|
|||
int __init early_init_dt_scan_opal(unsigned long node,
|
||||
const char *uname, int depth, void *data)
|
||||
{
|
||||
const void *basep, *entryp;
|
||||
unsigned long basesz, entrysz;
|
||||
const void *basep, *entryp, *sizep;
|
||||
int basesz, entrysz, runtimesz;
|
||||
|
||||
if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
|
||||
return 0;
|
||||
|
|
@ -50,10 +50,10 @@ int __init early_init_dt_scan_opal(unsigned long node,
|
|||
opal.base = of_read_number(basep, basesz/4);
|
||||
opal.entry = of_read_number(entryp, entrysz/4);
|
||||
|
||||
pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n",
|
||||
pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%d)\n",
|
||||
opal.base, basep, basesz);
|
||||
pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n",
|
||||
opal.entry, entryp, entrysz);
|
||||
pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%d)\n",
|
||||
opal.size, sizep, runtimesz);
|
||||
|
||||
powerpc_firmware_features |= FW_FEATURE_OPAL;
|
||||
if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) {
|
||||
|
|
@ -105,7 +105,7 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count)
|
|||
opal_poll_events(&evt);
|
||||
if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0)
|
||||
return 0;
|
||||
len = count;
|
||||
len = cpu_to_be64(count);
|
||||
rc = opal_console_read(vtermno, &len, buf);
|
||||
if (rc == OPAL_SUCCESS)
|
||||
return len;
|
||||
|
|
|
|||
|
|
@ -646,7 +646,7 @@ static int __init pseries_probe_fw_features(unsigned long node,
|
|||
void *data)
|
||||
{
|
||||
const char *prop;
|
||||
unsigned long len;
|
||||
int len;
|
||||
static int hypertas_found;
|
||||
static int vec5_found;
|
||||
|
||||
|
|
@ -679,7 +679,7 @@ static int __init pseries_probe_fw_features(unsigned long node,
|
|||
static int __init pSeries_probe(void)
|
||||
{
|
||||
unsigned long root = of_get_flat_dt_root();
|
||||
char *dtype = of_get_flat_dt_prop(root, "device_type", NULL);
|
||||
const char *dtype = of_get_flat_dt_prop(root, "device_type", NULL);
|
||||
|
||||
if (dtype == NULL)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -222,6 +222,43 @@ static int __init parse_bootparam(const bp_tag_t* tag)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
bool __initdata dt_memory_scan = false;
|
||||
|
||||
#if XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY
|
||||
unsigned long xtensa_kio_paddr = XCHAL_KIO_DEFAULT_PADDR;
|
||||
EXPORT_SYMBOL(xtensa_kio_paddr);
|
||||
|
||||
static int __init xtensa_dt_io_area(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
const __be32 *ranges;
|
||||
int len;
|
||||
|
||||
if (depth > 1)
|
||||
return 0;
|
||||
|
||||
if (!of_flat_dt_is_compatible(node, "simple-bus"))
|
||||
return 0;
|
||||
|
||||
ranges = of_get_flat_dt_prop(node, "ranges", &len);
|
||||
if (!ranges)
|
||||
return 1;
|
||||
if (len == 0)
|
||||
return 1;
|
||||
|
||||
xtensa_kio_paddr = of_read_ulong(ranges+1, 1);
|
||||
/* round down to nearest 256MB boundary */
|
||||
xtensa_kio_paddr &= 0xf0000000;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
static int __init xtensa_dt_io_area(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
void __init early_init_dt_add_memory_arch(u64 base, u64 size)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ config OF_SELFTEST
|
|||
config OF_FLATTREE
|
||||
bool
|
||||
select DTC
|
||||
select LIBFDT
|
||||
|
||||
config OF_EARLY_FLATTREE
|
||||
bool
|
||||
|
|
@ -83,4 +84,10 @@ config OF_MTD
|
|||
depends on MTD
|
||||
def_bool y
|
||||
|
||||
config OF_RESERVED_MEM
|
||||
depends on OF_EARLY_FLATTREE
|
||||
bool
|
||||
help
|
||||
Helpers to allow for reservation of memory regions
|
||||
|
||||
endmenu # OF
|
||||
|
|
|
|||
|
|
@ -11,3 +11,6 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o
|
|||
obj-$(CONFIG_OF_PCI) += of_pci.o
|
||||
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
|
||||
obj-$(CONFIG_OF_MTD) += of_mtd.o
|
||||
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
|
||||
|
||||
CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ struct device_node *of_allnodes;
|
|||
EXPORT_SYMBOL(of_allnodes);
|
||||
struct device_node *of_chosen;
|
||||
struct device_node *of_aliases;
|
||||
static struct device_node *of_stdout;
|
||||
|
||||
DEFINE_MUTEX(of_aliases_mutex);
|
||||
|
||||
|
|
@ -1690,6 +1691,15 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
|
|||
of_chosen = of_find_node_by_path("/chosen");
|
||||
if (of_chosen == NULL)
|
||||
of_chosen = of_find_node_by_path("/chosen@0");
|
||||
|
||||
if (of_chosen) {
|
||||
const char *name;
|
||||
|
||||
name = of_get_property(of_chosen, "linux,stdout-path", NULL);
|
||||
if (name)
|
||||
of_stdout = of_find_node_by_path(name);
|
||||
}
|
||||
|
||||
of_aliases = of_find_node_by_path("/aliases");
|
||||
if (!of_aliases)
|
||||
return;
|
||||
|
|
@ -1799,3 +1809,19 @@ const char *of_prop_next_string(struct property *prop, const char *cur)
|
|||
return curv;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_prop_next_string);
|
||||
|
||||
/**
|
||||
* of_device_is_stdout_path - check if a device node matches the
|
||||
* linux,stdout-path property
|
||||
*
|
||||
* Check if this device node matches the linux,stdout-path property
|
||||
* in the chosen node. return true if yes, false otherwise.
|
||||
*/
|
||||
int of_device_is_stdout_path(struct device_node *dn)
|
||||
{
|
||||
if (!of_stdout)
|
||||
return false;
|
||||
|
||||
return of_stdout == dn;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_device_is_stdout_path);
|
||||
|
|
|
|||
461
drivers/of/fdt.c
461
drivers/of/fdt.c
|
|
@ -14,9 +14,13 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/libfdt.h>
|
||||
|
||||
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
|
||||
#ifdef CONFIG_PPC
|
||||
|
|
@ -25,54 +29,6 @@
|
|||
|
||||
#include <asm/page.h>
|
||||
|
||||
char *of_fdt_get_string(struct boot_param_header *blob, u32 offset)
|
||||
{
|
||||
return ((char *)blob) +
|
||||
be32_to_cpu(blob->off_dt_strings) + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_fdt_get_property - Given a node in the given flat blob, return
|
||||
* the property ptr
|
||||
*/
|
||||
void *of_fdt_get_property(struct boot_param_header *blob,
|
||||
unsigned long node, const char *name,
|
||||
unsigned long *size)
|
||||
{
|
||||
unsigned long p = node;
|
||||
|
||||
do {
|
||||
u32 tag = be32_to_cpup((__be32 *)p);
|
||||
u32 sz, noff;
|
||||
const char *nstr;
|
||||
|
||||
p += 4;
|
||||
if (tag == OF_DT_NOP)
|
||||
continue;
|
||||
if (tag != OF_DT_PROP)
|
||||
return NULL;
|
||||
|
||||
sz = be32_to_cpup((__be32 *)p);
|
||||
noff = be32_to_cpup((__be32 *)(p + 4));
|
||||
p += 8;
|
||||
if (be32_to_cpu(blob->version) < 0x10)
|
||||
p = ALIGN(p, sz >= 8 ? 8 : 4);
|
||||
|
||||
nstr = of_fdt_get_string(blob, noff);
|
||||
if (nstr == NULL) {
|
||||
pr_warning("Can't find property index name !\n");
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp(name, nstr) == 0) {
|
||||
if (size)
|
||||
*size = sz;
|
||||
return (void *)p;
|
||||
}
|
||||
p += sz;
|
||||
p = ALIGN(p, 4);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_fdt_is_compatible - Return true if given node from the given blob has
|
||||
* compat in its compatible list
|
||||
|
|
@ -87,9 +43,10 @@ int of_fdt_is_compatible(struct boot_param_header *blob,
|
|||
unsigned long node, const char *compat)
|
||||
{
|
||||
const char *cp;
|
||||
unsigned long cplen, l, score = 0;
|
||||
int cplen;
|
||||
unsigned long l, score = 0;
|
||||
|
||||
cp = of_fdt_get_property(blob, node, "compatible", &cplen);
|
||||
cp = fdt_getprop(blob, node, "compatible", &cplen);
|
||||
if (cp == NULL)
|
||||
return 0;
|
||||
while (cplen > 0) {
|
||||
|
|
@ -125,12 +82,12 @@ int of_fdt_match(struct boot_param_header *blob, unsigned long node,
|
|||
return score;
|
||||
}
|
||||
|
||||
static void *unflatten_dt_alloc(unsigned long *mem, unsigned long size,
|
||||
static void *unflatten_dt_alloc(void **mem, unsigned long size,
|
||||
unsigned long align)
|
||||
{
|
||||
void *res;
|
||||
|
||||
*mem = ALIGN(*mem, align);
|
||||
*mem = PTR_ALIGN(*mem, align);
|
||||
res = (void *)*mem;
|
||||
*mem += size;
|
||||
|
||||
|
|
@ -146,30 +103,29 @@ static void *unflatten_dt_alloc(unsigned long *mem, unsigned long size,
|
|||
* @allnextpp: pointer to ->allnext from last allocated device_node
|
||||
* @fpsize: Size of the node path up at the current depth.
|
||||
*/
|
||||
static unsigned long unflatten_dt_node(struct boot_param_header *blob,
|
||||
unsigned long mem,
|
||||
unsigned long *p,
|
||||
static void * unflatten_dt_node(struct boot_param_header *blob,
|
||||
void *mem,
|
||||
int *poffset,
|
||||
struct device_node *dad,
|
||||
struct device_node ***allnextpp,
|
||||
unsigned long fpsize)
|
||||
{
|
||||
const __be32 *p;
|
||||
struct device_node *np;
|
||||
struct property *pp, **prev_pp = NULL;
|
||||
char *pathp;
|
||||
u32 tag;
|
||||
const char *pathp;
|
||||
unsigned int l, allocl;
|
||||
static int depth = 0;
|
||||
int old_depth;
|
||||
int offset;
|
||||
int has_name = 0;
|
||||
int new_format = 0;
|
||||
|
||||
tag = be32_to_cpup((__be32 *)(*p));
|
||||
if (tag != OF_DT_BEGIN_NODE) {
|
||||
pr_err("Weird tag at start of node: %x\n", tag);
|
||||
pathp = fdt_get_name(blob, *poffset, &l);
|
||||
if (!pathp)
|
||||
return mem;
|
||||
}
|
||||
*p += 4;
|
||||
pathp = (char *)*p;
|
||||
l = allocl = strlen(pathp) + 1;
|
||||
*p = ALIGN(*p + l, 4);
|
||||
|
||||
allocl = l++;
|
||||
|
||||
/* version 0x10 has a more compact unit name here instead of the full
|
||||
* path. we accumulate the full path size using "fpsize", we'll rebuild
|
||||
|
|
@ -187,7 +143,7 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
|
|||
fpsize = 1;
|
||||
allocl = 2;
|
||||
l = 1;
|
||||
*pathp = '\0';
|
||||
pathp = "";
|
||||
} else {
|
||||
/* account for '/' and path size minus terminal 0
|
||||
* already in 'l'
|
||||
|
|
@ -235,32 +191,23 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
|
|||
kref_init(&np->kref);
|
||||
}
|
||||
/* process properties */
|
||||
while (1) {
|
||||
u32 sz, noff;
|
||||
char *pname;
|
||||
for (offset = fdt_first_property_offset(blob, *poffset);
|
||||
(offset >= 0);
|
||||
(offset = fdt_next_property_offset(blob, offset))) {
|
||||
const char *pname;
|
||||
u32 sz;
|
||||
|
||||
tag = be32_to_cpup((__be32 *)(*p));
|
||||
if (tag == OF_DT_NOP) {
|
||||
*p += 4;
|
||||
continue;
|
||||
}
|
||||
if (tag != OF_DT_PROP)
|
||||
if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
|
||||
offset = -FDT_ERR_INTERNAL;
|
||||
break;
|
||||
*p += 4;
|
||||
sz = be32_to_cpup((__be32 *)(*p));
|
||||
noff = be32_to_cpup((__be32 *)((*p) + 4));
|
||||
*p += 8;
|
||||
if (be32_to_cpu(blob->version) < 0x10)
|
||||
*p = ALIGN(*p, sz >= 8 ? 8 : 4);
|
||||
}
|
||||
|
||||
pname = of_fdt_get_string(blob, noff);
|
||||
if (pname == NULL) {
|
||||
pr_info("Can't find property name in list !\n");
|
||||
break;
|
||||
}
|
||||
if (strcmp(pname, "name") == 0)
|
||||
has_name = 1;
|
||||
l = strlen(pname) + 1;
|
||||
pp = unflatten_dt_alloc(&mem, sizeof(struct property),
|
||||
__alignof__(struct property));
|
||||
if (allnextpp) {
|
||||
|
|
@ -272,26 +219,25 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
|
|||
if ((strcmp(pname, "phandle") == 0) ||
|
||||
(strcmp(pname, "linux,phandle") == 0)) {
|
||||
if (np->phandle == 0)
|
||||
np->phandle = be32_to_cpup((__be32*)*p);
|
||||
np->phandle = be32_to_cpup(p);
|
||||
}
|
||||
/* And we process the "ibm,phandle" property
|
||||
* used in pSeries dynamic device tree
|
||||
* stuff */
|
||||
if (strcmp(pname, "ibm,phandle") == 0)
|
||||
np->phandle = be32_to_cpup((__be32 *)*p);
|
||||
pp->name = pname;
|
||||
np->phandle = be32_to_cpup(p);
|
||||
pp->name = (char *)pname;
|
||||
pp->length = sz;
|
||||
pp->value = (void *)*p;
|
||||
pp->value = (__be32 *)p;
|
||||
*prev_pp = pp;
|
||||
prev_pp = &pp->next;
|
||||
}
|
||||
*p = ALIGN((*p) + sz, 4);
|
||||
}
|
||||
/* with version 0x10 we may not have the name property, recreate
|
||||
* it here from the unit name if absent
|
||||
*/
|
||||
if (!has_name) {
|
||||
char *p1 = pathp, *ps = pathp, *pa = NULL;
|
||||
const char *p1 = pathp, *ps = pathp, *pa = NULL;
|
||||
int sz;
|
||||
|
||||
while (*p1) {
|
||||
|
|
@ -328,19 +274,18 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
|
|||
if (!np->type)
|
||||
np->type = "<NULL>";
|
||||
}
|
||||
while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {
|
||||
if (tag == OF_DT_NOP)
|
||||
*p += 4;
|
||||
else
|
||||
mem = unflatten_dt_node(blob, mem, p, np, allnextpp,
|
||||
fpsize);
|
||||
tag = be32_to_cpup((__be32 *)(*p));
|
||||
}
|
||||
if (tag != OF_DT_END_NODE) {
|
||||
pr_err("Weird tag at end of node: %x\n", tag);
|
||||
return mem;
|
||||
}
|
||||
*p += 4;
|
||||
|
||||
old_depth = depth;
|
||||
*poffset = fdt_next_node(blob, *poffset, &depth);
|
||||
if (depth < 0)
|
||||
depth = 0;
|
||||
while (*poffset > 0 && depth > old_depth)
|
||||
mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
|
||||
fpsize);
|
||||
|
||||
if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
|
||||
pr_err("unflatten: error %d processing FDT\n", *poffset);
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
|
|
@ -360,7 +305,9 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
|
|||
struct device_node **mynodes,
|
||||
void * (*dt_alloc)(u64 size, u64 align))
|
||||
{
|
||||
unsigned long start, mem, size;
|
||||
unsigned long size;
|
||||
int start;
|
||||
void *mem;
|
||||
struct device_node **allnextp = mynodes;
|
||||
|
||||
pr_debug(" -> unflatten_device_tree()\n");
|
||||
|
|
@ -381,30 +328,25 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
|
|||
}
|
||||
|
||||
/* First pass, scan for size */
|
||||
start = ((unsigned long)blob) +
|
||||
be32_to_cpu(blob->off_dt_struct);
|
||||
size = unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
|
||||
size = (size | 3) + 1;
|
||||
start = 0;
|
||||
size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
|
||||
size = ALIGN(size, 4);
|
||||
|
||||
pr_debug(" size is %lx, allocating...\n", size);
|
||||
|
||||
/* Allocate memory for the expanded device tree */
|
||||
mem = (unsigned long)
|
||||
dt_alloc(size + 4, __alignof__(struct device_node));
|
||||
mem = dt_alloc(size + 4, __alignof__(struct device_node));
|
||||
|
||||
memset((void *)mem, 0, size);
|
||||
|
||||
((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
|
||||
|
||||
pr_debug(" unflattening %lx...\n", mem);
|
||||
pr_debug(" unflattening %p...\n", mem);
|
||||
|
||||
/* Second pass, do actual unflattening */
|
||||
start = ((unsigned long)blob) +
|
||||
be32_to_cpu(blob->off_dt_struct);
|
||||
start = 0;
|
||||
unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
|
||||
if (be32_to_cpup((__be32 *)start) != OF_DT_END)
|
||||
pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
|
||||
if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef)
|
||||
if (be32_to_cpup(mem + size) != 0xdeadbeef)
|
||||
pr_warning("End of tree marker overwritten: %08x\n",
|
||||
be32_to_cpu(((__be32 *)mem)[size / 4]));
|
||||
*allnextp = NULL;
|
||||
|
|
@ -442,6 +384,129 @@ struct boot_param_header *initial_boot_params;
|
|||
|
||||
#ifdef CONFIG_OF_EARLY_FLATTREE
|
||||
|
||||
/**
|
||||
* res_mem_reserve_reg() - reserve all memory described in 'reg' property
|
||||
*/
|
||||
static int __init __reserved_mem_reserve_reg(unsigned long node,
|
||||
const char *uname)
|
||||
{
|
||||
int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
|
||||
phys_addr_t base, size;
|
||||
int len;
|
||||
const __be32 *prop;
|
||||
int nomap, first = 1;
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "reg", &len);
|
||||
if (!prop)
|
||||
return -ENOENT;
|
||||
|
||||
if (len && len % t_len != 0) {
|
||||
pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
|
||||
uname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
|
||||
|
||||
while (len >= t_len) {
|
||||
base = dt_mem_next_cell(dt_root_addr_cells, &prop);
|
||||
size = dt_mem_next_cell(dt_root_size_cells, &prop);
|
||||
|
||||
if (base && size &&
|
||||
early_init_dt_reserve_memory_arch(base, size, nomap) == 0)
|
||||
pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n",
|
||||
uname, &base, (unsigned long)size / SZ_1M);
|
||||
else
|
||||
pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n",
|
||||
uname, &base, (unsigned long)size / SZ_1M);
|
||||
|
||||
len -= t_len;
|
||||
if (first) {
|
||||
fdt_reserved_mem_save_node(node, uname, base, size);
|
||||
first = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __reserved_mem_check_root() - check if #size-cells, #address-cells provided
|
||||
* in /reserved-memory matches the values supported by the current implementation,
|
||||
* also check if ranges property has been provided
|
||||
*/
|
||||
static int __init __reserved_mem_check_root(unsigned long node)
|
||||
{
|
||||
const __be32 *prop;
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
|
||||
if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
|
||||
return -EINVAL;
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
|
||||
if (!prop || be32_to_cpup(prop) != dt_root_addr_cells)
|
||||
return -EINVAL;
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "ranges", NULL);
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
|
||||
*/
|
||||
static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
static int found;
|
||||
const char *status;
|
||||
int err;
|
||||
|
||||
if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {
|
||||
if (__reserved_mem_check_root(node) != 0) {
|
||||
pr_err("Reserved memory: unsupported node format, ignoring\n");
|
||||
/* break scan */
|
||||
return 1;
|
||||
}
|
||||
found = 1;
|
||||
/* scan next node */
|
||||
return 0;
|
||||
} else if (!found) {
|
||||
/* scan next node */
|
||||
return 0;
|
||||
} else if (found && depth < 2) {
|
||||
/* scanning of /reserved-memory has been finished */
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = of_get_flat_dt_prop(node, "status", NULL);
|
||||
if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0)
|
||||
return 0;
|
||||
|
||||
err = __reserved_mem_reserve_reg(node, uname);
|
||||
if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL))
|
||||
fdt_reserved_mem_save_node(node, uname, 0, 0);
|
||||
|
||||
/* scan next node */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* early_init_fdt_scan_reserved_mem() - create reserved memory regions
|
||||
*
|
||||
* This function grabs memory from early allocator for device exclusive use
|
||||
* defined in device tree structures. It should be called by arch specific code
|
||||
* once the early allocator (i.e. memblock) has been fully activated.
|
||||
*/
|
||||
void __init early_init_fdt_scan_reserved_mem(void)
|
||||
{
|
||||
if (!initial_boot_params)
|
||||
return;
|
||||
|
||||
of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
|
||||
fdt_init_reserved_mem();
|
||||
}
|
||||
|
||||
/**
|
||||
* of_scan_flat_dt - scan flattened tree blob and call callback on each.
|
||||
* @it: callback function
|
||||
|
|
@ -456,47 +521,19 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
|
|||
void *data),
|
||||
void *data)
|
||||
{
|
||||
unsigned long p = ((unsigned long)initial_boot_params) +
|
||||
be32_to_cpu(initial_boot_params->off_dt_struct);
|
||||
int rc = 0;
|
||||
int depth = -1;
|
||||
const void *blob = initial_boot_params;
|
||||
const char *pathp;
|
||||
int offset, rc = 0, depth = -1;
|
||||
|
||||
do {
|
||||
u32 tag = be32_to_cpup((__be32 *)p);
|
||||
const char *pathp;
|
||||
for (offset = fdt_next_node(blob, -1, &depth);
|
||||
offset >= 0 && depth >= 0 && !rc;
|
||||
offset = fdt_next_node(blob, offset, &depth)) {
|
||||
|
||||
p += 4;
|
||||
if (tag == OF_DT_END_NODE) {
|
||||
depth--;
|
||||
continue;
|
||||
}
|
||||
if (tag == OF_DT_NOP)
|
||||
continue;
|
||||
if (tag == OF_DT_END)
|
||||
break;
|
||||
if (tag == OF_DT_PROP) {
|
||||
u32 sz = be32_to_cpup((__be32 *)p);
|
||||
p += 8;
|
||||
if (be32_to_cpu(initial_boot_params->version) < 0x10)
|
||||
p = ALIGN(p, sz >= 8 ? 8 : 4);
|
||||
p += sz;
|
||||
p = ALIGN(p, 4);
|
||||
continue;
|
||||
}
|
||||
if (tag != OF_DT_BEGIN_NODE) {
|
||||
pr_err("Invalid tag %x in flat device tree!\n", tag);
|
||||
return -EINVAL;
|
||||
}
|
||||
depth++;
|
||||
pathp = (char *)p;
|
||||
p = ALIGN(p + strlen(pathp) + 1, 4);
|
||||
pathp = fdt_get_name(blob, offset, NULL);
|
||||
if (*pathp == '/')
|
||||
pathp = kbasename(pathp);
|
||||
rc = it(p, pathp, depth, data);
|
||||
if (rc != 0)
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
rc = it(offset, pathp, depth, data);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -505,14 +542,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
|
|||
*/
|
||||
unsigned long __init of_get_flat_dt_root(void)
|
||||
{
|
||||
unsigned long p = ((unsigned long)initial_boot_params) +
|
||||
be32_to_cpu(initial_boot_params->off_dt_struct);
|
||||
|
||||
while (be32_to_cpup((__be32 *)p) == OF_DT_NOP)
|
||||
p += 4;
|
||||
BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
|
||||
p += 4;
|
||||
return ALIGN(p + strlen((char *)p) + 1, 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -521,10 +551,10 @@ unsigned long __init of_get_flat_dt_root(void)
|
|||
* This function can be used within scan_flattened_dt callback to get
|
||||
* access to properties
|
||||
*/
|
||||
void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
|
||||
unsigned long *size)
|
||||
const void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
|
||||
int *size)
|
||||
{
|
||||
return of_fdt_get_property(initial_boot_params, node, name, size);
|
||||
return fdt_getprop(initial_boot_params, node, name, size);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -545,6 +575,15 @@ int __init of_flat_dt_match(unsigned long node, const char *const *compat)
|
|||
return of_fdt_match(initial_boot_params, node, compat);
|
||||
}
|
||||
|
||||
struct fdt_scan_status {
|
||||
const char *name;
|
||||
int namelen;
|
||||
int depth;
|
||||
int found;
|
||||
int (*iterator)(unsigned long node, const char *uname, int depth, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
/**
|
||||
* early_init_dt_check_for_initrd - Decode initrd location from flat tree
|
||||
|
|
@ -553,8 +592,8 @@ int __init of_flat_dt_match(unsigned long node, const char *const *compat)
|
|||
void __init early_init_dt_check_for_initrd(unsigned long node)
|
||||
{
|
||||
u64 start, end;
|
||||
unsigned long len;
|
||||
__be32 *prop;
|
||||
int len;
|
||||
const __be32 *prop;
|
||||
|
||||
pr_debug("Looking for initrd properties... ");
|
||||
|
||||
|
|
@ -584,7 +623,7 @@ inline void early_init_dt_check_for_initrd(unsigned long node)
|
|||
int __init early_init_dt_scan_root(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
__be32 *prop;
|
||||
const __be32 *prop;
|
||||
|
||||
if (depth != 0)
|
||||
return 0;
|
||||
|
|
@ -606,9 +645,9 @@ int __init early_init_dt_scan_root(unsigned long node, const char *uname,
|
|||
return 1;
|
||||
}
|
||||
|
||||
u64 __init dt_mem_next_cell(int s, __be32 **cellp)
|
||||
u64 __init dt_mem_next_cell(int s, const __be32 **cellp)
|
||||
{
|
||||
__be32 *p = *cellp;
|
||||
const __be32 *p = *cellp;
|
||||
|
||||
*cellp = p + s;
|
||||
return of_read_number(p, s);
|
||||
|
|
@ -620,9 +659,9 @@ u64 __init dt_mem_next_cell(int s, __be32 **cellp)
|
|||
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
__be32 *reg, *endp;
|
||||
unsigned long l;
|
||||
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
|
||||
const __be32 *reg, *endp;
|
||||
int l;
|
||||
|
||||
/* We are scanning "memory" nodes only */
|
||||
if (type == NULL) {
|
||||
|
|
@ -643,7 +682,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
|
|||
|
||||
endp = reg + (l / sizeof(__be32));
|
||||
|
||||
pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
|
||||
pr_debug("memory scan node %s, reg size %d, data: %x %x %x %x,\n",
|
||||
uname, l, reg[0], reg[1], reg[2], reg[3]);
|
||||
|
||||
while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
|
||||
|
|
@ -689,8 +728,8 @@ static const char *config_cmdline = "";
|
|||
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
unsigned long l = 0;
|
||||
char *p = NULL;
|
||||
int l;
|
||||
const char *p;
|
||||
char *cmdline = data;
|
||||
|
||||
pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
|
||||
|
|
@ -730,6 +769,80 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_MEMBLOCK
|
||||
void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
|
||||
{
|
||||
const u64 phys_offset = __pa(PAGE_OFFSET);
|
||||
base &= PAGE_MASK;
|
||||
size &= PAGE_MASK;
|
||||
if (base + size < phys_offset) {
|
||||
pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
|
||||
base, base + size);
|
||||
return;
|
||||
}
|
||||
if (base < phys_offset) {
|
||||
pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
|
||||
base, phys_offset);
|
||||
size -= phys_offset - base;
|
||||
base = phys_offset;
|
||||
}
|
||||
memblock_add(base, size);
|
||||
}
|
||||
|
||||
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
|
||||
phys_addr_t size, bool nomap)
|
||||
{
|
||||
if (memblock_is_region_reserved(base, size))
|
||||
return -EBUSY;
|
||||
if (nomap)
|
||||
return memblock_remove(base, size);
|
||||
return memblock_reserve(base, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* called from unflatten_device_tree() to bootstrap devicetree itself
|
||||
* Architectures can override this definition if memblock isn't used
|
||||
*/
|
||||
void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align)
|
||||
{
|
||||
return __va(memblock_alloc(size, align));
|
||||
}
|
||||
#else
|
||||
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
|
||||
phys_addr_t size, bool nomap)
|
||||
{
|
||||
pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n",
|
||||
base, size, nomap ? " (nomap)" : "");
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool __init early_init_dt_scan(void *params)
|
||||
{
|
||||
if (!params)
|
||||
return false;
|
||||
|
||||
/* Setup flat device-tree pointer */
|
||||
initial_boot_params = params;
|
||||
|
||||
/* check device tree validity */
|
||||
if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) {
|
||||
initial_boot_params = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Retrieve various information from the /chosen node */
|
||||
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
|
||||
|
||||
/* Initialize {size,address}-cells info */
|
||||
of_scan_flat_dt(early_init_dt_scan_root, NULL);
|
||||
|
||||
/* Setup memory, calling early_init_dt_add_memory_arch */
|
||||
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* unflatten_device_tree - create tree of device_nodes from flat blob
|
||||
*
|
||||
|
|
|
|||
217
drivers/of/of_reserved_mem.c
Normal file
217
drivers/of/of_reserved_mem.c
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Device tree based initialization code for reserved memory.
|
||||
*
|
||||
* Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
|
||||
* Copyright (c) 2013,2014 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
* Author: Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
* Author: Josh Cartwright <joshc@codeaurora.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License or (at your optional) any later version of the license.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
|
||||
#define MAX_RESERVED_REGIONS 16
|
||||
static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
|
||||
static int reserved_mem_count;
|
||||
|
||||
#if defined(CONFIG_HAVE_MEMBLOCK)
|
||||
#include <linux/memblock.h>
|
||||
int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
|
||||
phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
|
||||
phys_addr_t *res_base)
|
||||
{
|
||||
/*
|
||||
* We use __memblock_alloc_base() because memblock_alloc_base()
|
||||
* panic()s on allocation failure.
|
||||
*/
|
||||
phys_addr_t base = __memblock_alloc_base(size, align, end);
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Check if the allocated region fits in to start..end window
|
||||
*/
|
||||
if (base < start) {
|
||||
memblock_free(base, size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*res_base = base;
|
||||
if (nomap)
|
||||
return memblock_remove(base, size);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
|
||||
phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
|
||||
phys_addr_t *res_base)
|
||||
{
|
||||
pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n",
|
||||
size, nomap ? " (nomap)" : "");
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* res_mem_save_node() - save fdt node for second pass initialization
|
||||
*/
|
||||
void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
|
||||
phys_addr_t base, phys_addr_t size)
|
||||
{
|
||||
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
|
||||
|
||||
if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
|
||||
pr_err("Reserved memory: not enough space all defined regions.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rmem->fdt_node = node;
|
||||
rmem->name = uname;
|
||||
rmem->base = base;
|
||||
rmem->size = size;
|
||||
|
||||
reserved_mem_count++;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* res_mem_alloc_size() - allocate reserved memory described by 'size', 'align'
|
||||
* and 'alloc-ranges' properties
|
||||
*/
|
||||
static int __init __reserved_mem_alloc_size(unsigned long node,
|
||||
const char *uname, phys_addr_t *res_base, phys_addr_t *res_size)
|
||||
{
|
||||
int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
|
||||
phys_addr_t start = 0, end = 0;
|
||||
phys_addr_t base = 0, align = 0, size;
|
||||
int len;
|
||||
const __be32 *prop;
|
||||
int nomap;
|
||||
int ret;
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "size", &len);
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
|
||||
if (len != dt_root_size_cells * sizeof(__be32)) {
|
||||
pr_err("Reserved memory: invalid size property in '%s' node.\n",
|
||||
uname);
|
||||
return -EINVAL;
|
||||
}
|
||||
size = dt_mem_next_cell(dt_root_size_cells, &prop);
|
||||
|
||||
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "alignment", &len);
|
||||
if (prop) {
|
||||
if (len != dt_root_addr_cells * sizeof(__be32)) {
|
||||
pr_err("Reserved memory: invalid alignment property in '%s' node.\n",
|
||||
uname);
|
||||
return -EINVAL;
|
||||
}
|
||||
align = dt_mem_next_cell(dt_root_addr_cells, &prop);
|
||||
}
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "alloc-ranges", &len);
|
||||
if (prop) {
|
||||
|
||||
if (len % t_len != 0) {
|
||||
pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n",
|
||||
uname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
base = 0;
|
||||
|
||||
while (len > 0) {
|
||||
start = dt_mem_next_cell(dt_root_addr_cells, &prop);
|
||||
end = start + dt_mem_next_cell(dt_root_size_cells,
|
||||
&prop);
|
||||
|
||||
ret = early_init_dt_alloc_reserved_memory_arch(size,
|
||||
align, start, end, nomap, &base);
|
||||
if (ret == 0) {
|
||||
pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n",
|
||||
uname, &base,
|
||||
(unsigned long)size / SZ_1M);
|
||||
break;
|
||||
}
|
||||
len -= t_len;
|
||||
}
|
||||
|
||||
} else {
|
||||
ret = early_init_dt_alloc_reserved_memory_arch(size, align,
|
||||
0, 0, nomap, &base);
|
||||
if (ret == 0)
|
||||
pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n",
|
||||
uname, &base, (unsigned long)size / SZ_1M);
|
||||
}
|
||||
|
||||
if (base == 0) {
|
||||
pr_info("Reserved memory: failed to allocate memory for node '%s'\n",
|
||||
uname);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*res_base = base;
|
||||
*res_size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id __rmem_of_table_sentinel
|
||||
__used __section(__reservedmem_of_table_end);
|
||||
|
||||
/**
|
||||
* res_mem_init_node() - call region specific reserved memory init code
|
||||
*/
|
||||
static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
|
||||
{
|
||||
extern const struct of_device_id __reservedmem_of_table[];
|
||||
const struct of_device_id *i;
|
||||
|
||||
for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) {
|
||||
reservedmem_of_init_fn initfn = i->data;
|
||||
const char *compat = i->compatible;
|
||||
|
||||
if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
|
||||
continue;
|
||||
|
||||
if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) {
|
||||
pr_info("Reserved memory: initialized node %s, compatible id %s\n",
|
||||
rmem->name, compat);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* fdt_init_reserved_mem - allocate and init all saved reserved memory regions
|
||||
*/
|
||||
void __init fdt_init_reserved_mem(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < reserved_mem_count; i++) {
|
||||
struct reserved_mem *rmem = &reserved_mem[i];
|
||||
unsigned long node = rmem->fdt_node;
|
||||
int err = 0;
|
||||
|
||||
if (rmem->size == 0)
|
||||
err = __reserved_mem_alloc_size(node, rmem->name,
|
||||
&rmem->base, &rmem->size);
|
||||
if (err == 0)
|
||||
__reserved_mem_init_node(rmem);
|
||||
}
|
||||
}
|
||||
|
|
@ -171,6 +171,16 @@
|
|||
#define CLK_OF_TABLES()
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF_RESERVED_MEM
|
||||
#define RESERVEDMEM_OF_TABLES() \
|
||||
. = ALIGN(8); \
|
||||
VMLINUX_SYMBOL(__reservedmem_of_table) = .; \
|
||||
*(__reservedmem_of_table) \
|
||||
*(__reservedmem_of_table_end)
|
||||
#else
|
||||
#define RESERVEDMEM_OF_TABLES()
|
||||
#endif
|
||||
|
||||
#define KERNEL_DTB() \
|
||||
STRUCT_ALIGN(); \
|
||||
VMLINUX_SYMBOL(__dtb_start) = .; \
|
||||
|
|
@ -515,6 +525,7 @@
|
|||
CPU_DISCARD(init.rodata) \
|
||||
MEM_DISCARD(init.rodata) \
|
||||
CLK_OF_TABLES() \
|
||||
RESERVEDMEM_OF_TABLES() \
|
||||
CLKSRC_OF_TABLES() \
|
||||
KERNEL_DTB() \
|
||||
IRQCHIP_OF_MATCH_TABLE()
|
||||
|
|
|
|||
|
|
@ -344,6 +344,8 @@ const char *of_prop_next_string(struct property *prop, const char *cur);
|
|||
s; \
|
||||
s = of_prop_next_string(prop, s))
|
||||
|
||||
int of_device_is_stdout_path(struct device_node *dn);
|
||||
|
||||
#else /* CONFIG_OF */
|
||||
|
||||
static inline const char* of_node_full_name(struct device_node *np)
|
||||
|
|
@ -512,6 +514,11 @@ static inline int of_machine_is_compatible(const char *compat)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int of_device_is_stdout_path(struct device_node *dn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define of_match_ptr(_ptr) NULL
|
||||
#define of_match_node(_matches, _node) NULL
|
||||
#define of_property_for_each_u32(np, propname, prop, p, u) \
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ extern char *of_fdt_get_string(struct boot_param_header *blob, u32 offset);
|
|||
extern void *of_fdt_get_property(struct boot_param_header *blob,
|
||||
unsigned long node,
|
||||
const char *name,
|
||||
unsigned long *size);
|
||||
int *size);
|
||||
extern int of_fdt_is_compatible(struct boot_param_header *blob,
|
||||
unsigned long node,
|
||||
const char *compat);
|
||||
|
|
@ -81,12 +81,11 @@ extern int __initdata dt_root_size_cells;
|
|||
extern struct boot_param_header *initial_boot_params;
|
||||
|
||||
/* For scanning the flat device-tree at boot time */
|
||||
extern char *find_flat_dt_string(u32 offset);
|
||||
extern int of_scan_flat_dt(int (*it)(unsigned long node, const char *uname,
|
||||
int depth, void *data),
|
||||
void *data);
|
||||
extern void *of_get_flat_dt_prop(unsigned long node, const char *name,
|
||||
unsigned long *size);
|
||||
extern const void *of_get_flat_dt_prop(unsigned long node, const char *name,
|
||||
int *size);
|
||||
extern int of_flat_dt_is_compatible(unsigned long node, const char *name);
|
||||
extern int of_flat_dt_match(unsigned long node, const char *const *matches);
|
||||
extern unsigned long of_get_flat_dt_root(void);
|
||||
|
|
@ -117,9 +116,12 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname,
|
|||
extern void early_init_dt_check_for_initrd(unsigned long node);
|
||||
extern int early_init_dt_scan_memory(unsigned long node, const char *uname,
|
||||
int depth, void *data);
|
||||
extern void early_init_fdt_scan_reserved_mem(void);
|
||||
extern void early_init_dt_add_memory_arch(u64 base, u64 size);
|
||||
extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size,
|
||||
bool no_map);
|
||||
extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align);
|
||||
extern u64 dt_mem_next_cell(int s, __be32 **cellp);
|
||||
extern u64 dt_mem_next_cell(int s, const __be32 **cellp);
|
||||
|
||||
/*
|
||||
* If BLK_DEV_INITRD, the fdt early init code will call this function,
|
||||
|
|
@ -138,6 +140,8 @@ extern int early_init_dt_scan_root(unsigned long node, const char *uname,
|
|||
extern void unflatten_device_tree(void);
|
||||
extern void early_init_devtree(void *);
|
||||
#else /* CONFIG_OF_FLATTREE */
|
||||
static inline void early_init_fdt_scan_reserved_mem(void) {}
|
||||
static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }
|
||||
static inline void unflatten_device_tree(void) {}
|
||||
#endif /* CONFIG_OF_FLATTREE */
|
||||
|
||||
|
|
|
|||
53
include/linux/of_reserved_mem.h
Normal file
53
include/linux/of_reserved_mem.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef __OF_RESERVED_MEM_H
|
||||
#define __OF_RESERVED_MEM_H
|
||||
|
||||
struct device;
|
||||
struct of_phandle_args;
|
||||
struct reserved_mem_ops;
|
||||
|
||||
struct reserved_mem {
|
||||
const char *name;
|
||||
unsigned long fdt_node;
|
||||
const struct reserved_mem_ops *ops;
|
||||
phys_addr_t base;
|
||||
phys_addr_t size;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct reserved_mem_ops {
|
||||
void (*device_init)(struct reserved_mem *rmem,
|
||||
struct device *dev);
|
||||
void (*device_release)(struct reserved_mem *rmem,
|
||||
struct device *dev);
|
||||
};
|
||||
|
||||
typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem,
|
||||
unsigned long node, const char *uname);
|
||||
|
||||
#ifdef CONFIG_OF_RESERVED_MEM
|
||||
void fdt_init_reserved_mem(void);
|
||||
void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
|
||||
phys_addr_t base, phys_addr_t size);
|
||||
|
||||
#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
|
||||
static const struct of_device_id __reservedmem_of_table_##name \
|
||||
__used __section(__reservedmem_of_table) \
|
||||
= { .compatible = compat, \
|
||||
.data = (init == (reservedmem_of_init_fn)NULL) ? \
|
||||
init : init }
|
||||
|
||||
#else
|
||||
static inline void fdt_init_reserved_mem(void) { }
|
||||
static inline void fdt_reserved_mem_save_node(unsigned long node,
|
||||
const char *uname, phys_addr_t base, phys_addr_t size) { }
|
||||
|
||||
#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
|
||||
static const struct of_device_id __reservedmem_of_table_##name \
|
||||
__attribute__((unused)) \
|
||||
= { .compatible = compat, \
|
||||
.data = (init == (reservedmem_of_init_fn)NULL) ? \
|
||||
init : init }
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __OF_RESERVED_MEM_H */
|
||||
|
|
@ -140,7 +140,8 @@ obj-$(CONFIG_GENERIC_STRNLEN_USER) += strnlen_user.o
|
|||
|
||||
obj-$(CONFIG_STMP_DEVICE) += stmp_device.o
|
||||
|
||||
libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o
|
||||
libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \
|
||||
fdt_empty_tree.o
|
||||
$(foreach file, $(libfdt_files), \
|
||||
$(eval CFLAGS_$(file) = -I$(src)/../scripts/dtc/libfdt))
|
||||
lib-$(CONFIG_LIBFDT) += $(libfdt_files)
|
||||
|
|
|
|||
2
lib/fdt_empty_tree.c
Normal file
2
lib/fdt_empty_tree.c
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#include <linux/libfdt_env.h>
|
||||
#include "../scripts/dtc/libfdt/fdt_empty_tree.c"
|
||||
Loading…
Reference in New Issue
Block a user