1267 lines · 29.8 KB · C
RawDownload
/* *  linux/arch/arm/kernel/setup.c * *  Copyright (C) 1995-2001 Russell King * * 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/export.h>#include <linux/kernel.h>#include <linux/stddef.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/utsname.h>#include <linux/initrd.h>#include <linux/console.h>#include <linux/bootmem.h>#include <linux/seq_file.h>#include <linux/screen_info.h>#include <linux/of_iommu.h>#include <linux/of_platform.h>#include <linux/init.h>#include <linux/kexec.h>#include <linux/of_fdt.h>#include <linux/cpu.h>#include <linux/interrupt.h>#include <linux/smp.h>#include <linux/proc_fs.h>#include <linux/memblock.h>#include <linux/bug.h>#include <linux/compiler.h>#include <linux/sort.h>#include <linux/psci.h> #include <asm/unified.h>#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>#include <asm/sections.h>#include <asm/setup.h>#include <asm/smp_plat.h>#include <asm/mach-types.h>#include <asm/cacheflush.h>#include <asm/cachetype.h>#include <asm/tlbflush.h>#include <asm/xen/hypervisor.h> #include <asm/prom.h>#include <asm/mach/arch.h>#include <asm/mach/irq.h>#include <asm/mach/time.h>#include <asm/system_info.h>#include <asm/system_misc.h>#include <asm/traps.h>#include <asm/unwind.h>#include <asm/memblock.h>#include <asm/virt.h> #include "atags.h"  #if defined(CONFIG_FPE_NWFPE) || defined(CONFIG_FPE_FASTFPE)char fpe_type[8]; static int __init fpe_setup(char *line){	memcpy(fpe_type, line, 8);	return 1;} __setup("fpe=", fpe_setup);#endif extern void init_default_cache_policy(unsigned long);extern void paging_init(const struct machine_desc *desc);extern void early_paging_init(const struct machine_desc *);extern void sanity_check_meminfo(void);extern enum reboot_mode reboot_mode;extern void setup_dma_zone(const struct machine_desc *desc); unsigned int processor_id;EXPORT_SYMBOL(processor_id);unsigned int __machine_arch_type __read_mostly;EXPORT_SYMBOL(__machine_arch_type);unsigned int cacheid __read_mostly;EXPORT_SYMBOL(cacheid); unsigned int __atags_pointer __initdata; unsigned int system_rev;EXPORT_SYMBOL(system_rev); const char *system_serial;EXPORT_SYMBOL(system_serial); unsigned int system_serial_low;EXPORT_SYMBOL(system_serial_low); unsigned int system_serial_high;EXPORT_SYMBOL(system_serial_high); unsigned int elf_hwcap __read_mostly;EXPORT_SYMBOL(elf_hwcap); unsigned int elf_hwcap2 __read_mostly;EXPORT_SYMBOL(elf_hwcap2);  #ifdef MULTI_CPUstruct processor processor __read_mostly;#endif#ifdef MULTI_TLBstruct cpu_tlb_fns cpu_tlb __read_mostly;#endif#ifdef MULTI_USERstruct cpu_user_fns cpu_user __read_mostly;#endif#ifdef MULTI_CACHEstruct cpu_cache_fns cpu_cache __read_mostly;#endif#ifdef CONFIG_OUTER_CACHEstruct outer_cache_fns outer_cache __read_mostly;EXPORT_SYMBOL(outer_cache);#endif /* * Cached cpu_architecture() result for use by assembler code. * C code should use the cpu_architecture() function instead of accessing this * variable directly. */int __cpu_architecture __read_mostly = CPU_ARCH_UNKNOWN; struct stack {	u32 irq[3];	u32 abt[3];	u32 und[3];	u32 fiq[3];} ____cacheline_aligned; #ifndef CONFIG_CPU_V7Mstatic struct stack stacks[NR_CPUS];#endif char elf_platform[ELF_PLATFORM_SIZE];EXPORT_SYMBOL(elf_platform); static const char *cpu_name;static const char *machine_name;static char __initdata cmd_line[COMMAND_LINE_SIZE];const struct machine_desc *machine_desc __initdata; static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };#define ENDIANNESS ((char)endian_test.l) DEFINE_PER_CPU(struct cpuinfo_arm, cpu_data); /* * Standard memory resources */static struct resource mem_res[] = {	{		.name = "Video RAM",		.start = 0,		.end = 0,		.flags = IORESOURCE_MEM	},	{		.name = "Kernel code",		.start = 0,		.end = 0,		.flags = IORESOURCE_SYSTEM_RAM	},	{		.name = "Kernel data",		.start = 0,		.end = 0,		.flags = IORESOURCE_SYSTEM_RAM	}}; #define video_ram   mem_res[0]#define kernel_code mem_res[1]#define kernel_data mem_res[2] static struct resource io_res[] = {	{		.name = "reserved",		.start = 0x3bc,		.end = 0x3be,		.flags = IORESOURCE_IO | IORESOURCE_BUSY	},	{		.name = "reserved",		.start = 0x378,		.end = 0x37f,		.flags = IORESOURCE_IO | IORESOURCE_BUSY	},	{		.name = "reserved",		.start = 0x278,		.end = 0x27f,		.flags = IORESOURCE_IO | IORESOURCE_BUSY	}}; #define lp0 io_res[0]#define lp1 io_res[1]#define lp2 io_res[2] static const char *proc_arch[] = {	"undefined/unknown",	"3",	"4",	"4T",	"5",	"5T",	"5TE",	"5TEJ",	"6TEJ",	"7",	"7M",	"?(12)",	"?(13)",	"?(14)",	"?(15)",	"?(16)",	"?(17)",}; #ifdef CONFIG_CPU_V7Mstatic int __get_cpu_architecture(void){	return CPU_ARCH_ARMv7M;}#elsestatic int __get_cpu_architecture(void){	int cpu_arch; 	if ((read_cpuid_id() & 0x0008f000) == 0) {		cpu_arch = CPU_ARCH_UNKNOWN;	} else if ((read_cpuid_id() & 0x0008f000) == 0x00007000) {		cpu_arch = (read_cpuid_id() & (1 << 23)) ? CPU_ARCH_ARMv4T : CPU_ARCH_ARMv3;	} else if ((read_cpuid_id() & 0x00080000) == 0x00000000) {		cpu_arch = (read_cpuid_id() >> 16) & 7;		if (cpu_arch)			cpu_arch += CPU_ARCH_ARMv3;	} else if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) {		/* Revised CPUID format. Read the Memory Model Feature		 * Register 0 and check for VMSAv7 or PMSAv7 */		unsigned int mmfr0 = read_cpuid_ext(CPUID_EXT_MMFR0);		if ((mmfr0 & 0x0000000f) >= 0x00000003 ||		    (mmfr0 & 0x000000f0) >= 0x00000030)			cpu_arch = CPU_ARCH_ARMv7;		else if ((mmfr0 & 0x0000000f) == 0x00000002 ||			 (mmfr0 & 0x000000f0) == 0x00000020)			cpu_arch = CPU_ARCH_ARMv6;		else			cpu_arch = CPU_ARCH_UNKNOWN;	} else		cpu_arch = CPU_ARCH_UNKNOWN; 	return cpu_arch;}#endif int __pure cpu_architecture(void){	BUG_ON(__cpu_architecture == CPU_ARCH_UNKNOWN); 	return __cpu_architecture;} static int cpu_has_aliasing_icache(unsigned int arch){	int aliasing_icache;	unsigned int id_reg, num_sets, line_size; 	/* PIPT caches never alias. */	if (icache_is_pipt())		return 0; 	/* arch specifies the register format */	switch (arch) {	case CPU_ARCH_ARMv7:		asm("mcr	p15, 2, %0, c0, c0, 0 @ set CSSELR"		    : /* No output operands */		    : "r" (1));		isb();		asm("mrc	p15, 1, %0, c0, c0, 0 @ read CCSIDR"		    : "=r" (id_reg));		line_size = 4 << ((id_reg & 0x7) + 2);		num_sets = ((id_reg >> 13) & 0x7fff) + 1;		aliasing_icache = (line_size * num_sets) > PAGE_SIZE;		break;	case CPU_ARCH_ARMv6:		aliasing_icache = read_cpuid_cachetype() & (1 << 11);		break;	default:		/* I-cache aliases will be handled by D-cache aliasing code */		aliasing_icache = 0;	} 	return aliasing_icache;} static void __init cacheid_init(void){	unsigned int arch = cpu_architecture(); 	if (arch == CPU_ARCH_ARMv7M) {		cacheid = 0;	} else if (arch >= CPU_ARCH_ARMv6) {		unsigned int cachetype = read_cpuid_cachetype();		if ((cachetype & (7 << 29)) == 4 << 29) {			/* ARMv7 register format */			arch = CPU_ARCH_ARMv7;			cacheid = CACHEID_VIPT_NONALIASING;			switch (cachetype & (3 << 14)) {			case (1 << 14):				cacheid |= CACHEID_ASID_TAGGED;				break;			case (3 << 14):				cacheid |= CACHEID_PIPT;				break;			}		} else {			arch = CPU_ARCH_ARMv6;			if (cachetype & (1 << 23))				cacheid = CACHEID_VIPT_ALIASING;			else				cacheid = CACHEID_VIPT_NONALIASING;		}		if (cpu_has_aliasing_icache(arch))			cacheid |= CACHEID_VIPT_I_ALIASING;	} else {		cacheid = CACHEID_VIVT;	} 	pr_info("CPU: %s data cache, %s instruction cache\n",		cache_is_vivt() ? "VIVT" :		cache_is_vipt_aliasing() ? "VIPT aliasing" :		cache_is_vipt_nonaliasing() ? "PIPT / VIPT nonaliasing" : "unknown",		cache_is_vivt() ? "VIVT" :		icache_is_vivt_asid_tagged() ? "VIVT ASID tagged" :		icache_is_vipt_aliasing() ? "VIPT aliasing" :		icache_is_pipt() ? "PIPT" :		cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown");} /* * These functions re-use the assembly code in head.S, which * already provide the required functionality. */extern struct proc_info_list *lookup_processor_type(unsigned int); void __init early_print(const char *str, ...){	extern void printascii(const char *);	char buf[256];	va_list ap; 	va_start(ap, str);	vsnprintf(buf, sizeof(buf), str, ap);	va_end(ap); #ifdef CONFIG_DEBUG_LL	printascii(buf);#endif	printk("%s", buf);} #ifdef CONFIG_ARM_PATCH_IDIV static inline u32 __attribute_const__ sdiv_instruction(void){	if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {		/* "sdiv r0, r0, r1" */		u32 insn = __opcode_thumb32_compose(0xfb90, 0xf0f1);		return __opcode_to_mem_thumb32(insn);	} 	/* "sdiv r0, r0, r1" */	return __opcode_to_mem_arm(0xe710f110);} static inline u32 __attribute_const__ udiv_instruction(void){	if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {		/* "udiv r0, r0, r1" */		u32 insn = __opcode_thumb32_compose(0xfbb0, 0xf0f1);		return __opcode_to_mem_thumb32(insn);	} 	/* "udiv r0, r0, r1" */	return __opcode_to_mem_arm(0xe730f110);} static inline u32 __attribute_const__ bx_lr_instruction(void){	if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {		/* "bx lr; nop" */		u32 insn = __opcode_thumb32_compose(0x4770, 0x46c0);		return __opcode_to_mem_thumb32(insn);	} 	/* "bx lr" */	return __opcode_to_mem_arm(0xe12fff1e);} static void __init patch_aeabi_idiv(void){	extern void __aeabi_uidiv(void);	extern void __aeabi_idiv(void);	uintptr_t fn_addr;	unsigned int mask; 	mask = IS_ENABLED(CONFIG_THUMB2_KERNEL) ? HWCAP_IDIVT : HWCAP_IDIVA;	if (!(elf_hwcap & mask))		return; 	pr_info("CPU: div instructions available: patching division code\n"); 	fn_addr = ((uintptr_t)&__aeabi_uidiv) & ~1;	asm ("" : "+g" (fn_addr));	((u32 *)fn_addr)[0] = udiv_instruction();	((u32 *)fn_addr)[1] = bx_lr_instruction();	flush_icache_range(fn_addr, fn_addr + 8); 	fn_addr = ((uintptr_t)&__aeabi_idiv) & ~1;	asm ("" : "+g" (fn_addr));	((u32 *)fn_addr)[0] = sdiv_instruction();	((u32 *)fn_addr)[1] = bx_lr_instruction();	flush_icache_range(fn_addr, fn_addr + 8);} #elsestatic inline void patch_aeabi_idiv(void) { }#endif static void __init cpuid_init_hwcaps(void){	int block;	u32 isar5; 	if (cpu_architecture() < CPU_ARCH_ARMv7)		return; 	block = cpuid_feature_extract(CPUID_EXT_ISAR0, 24);	if (block >= 2)		elf_hwcap |= HWCAP_IDIVA;	if (block >= 1)		elf_hwcap |= HWCAP_IDIVT; 	/* LPAE implies atomic ldrd/strd instructions */	block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);	if (block >= 5)		elf_hwcap |= HWCAP_LPAE; 	/* check for supported v8 Crypto instructions */	isar5 = read_cpuid_ext(CPUID_EXT_ISAR5); 	block = cpuid_feature_extract_field(isar5, 4);	if (block >= 2)		elf_hwcap2 |= HWCAP2_PMULL;	if (block >= 1)		elf_hwcap2 |= HWCAP2_AES; 	block = cpuid_feature_extract_field(isar5, 8);	if (block >= 1)		elf_hwcap2 |= HWCAP2_SHA1; 	block = cpuid_feature_extract_field(isar5, 12);	if (block >= 1)		elf_hwcap2 |= HWCAP2_SHA2; 	block = cpuid_feature_extract_field(isar5, 16);	if (block >= 1)		elf_hwcap2 |= HWCAP2_CRC32;} static void __init elf_hwcap_fixup(void){	unsigned id = read_cpuid_id(); 	/*	 * HWCAP_TLS is available only on 1136 r1p0 and later,	 * see also kuser_get_tls_init.	 */	if (read_cpuid_part() == ARM_CPU_PART_ARM1136 &&	    ((id >> 20) & 3) == 0) {		elf_hwcap &= ~HWCAP_TLS;

Lines 1–500 of 1267.