// SPDX-License-Identifier: GPL-2.0-only
/*
* linux / arch / arm / kernel / setup . c
*
* Copyright ( C ) 1995 - 2001 Russell King
*/
#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/seq_file.h>
#include <linux/screen_info.h>
#include <linux/init.h>
#include <linux/kexec.h>
#include <linux/libfdt.h>
#include <linux/of.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 <asm /kasan.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
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_CPU
struct processor processor __ro_after_init;
#if defined (CONFIG_BIG_LITTLE) && defined (CONFIG_HARDEN_BRANCH_PREDICTOR)
struct processor *cpu_vtable[NR_CPUS] = {
[0 ] = &processor,
};
#endif
#endif
#ifdef MULTI_TLB
struct cpu_tlb_fns cpu_tlb __ro_after_init;
#endif
#ifdef MULTI_USER
struct cpu_user_fns cpu_user __ro_after_init;
#endif
#ifdef MULTI_CACHE
struct cpu_cache_fns cpu_cache __ro_after_init;
#endif
#ifdef CONFIG_OUTER_CACHE
struct outer_cache_fns outer_cache __ro_after_init;
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[4 ];
u32 abt[4 ];
u32 und[4 ];
u32 fiq[4 ];
} ____cacheline_aligned;
#ifndef CONFIG_CPU_V7M
static 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 = 0 x3bc,
.end = 0 x3be,
.flags = IORESOURCE_IO | IORESOURCE_BUSY
},
{
.name = "reserved" ,
.start = 0 x378,
.end = 0 x37f,
.flags = IORESOURCE_IO | IORESOURCE_BUSY
},
{
.name = "reserved" ,
.start = 0 x278,
.end = 0 x27f,
.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_V7M
static int __get_cpu_architecture(void )
{
return CPU_ARCH_ARMv7M;
}
#else
static int __get_cpu_architecture(void )
{
int cpu_arch;
if ((read_cpuid_id() & 0 x0008f000) == 0 ) {
cpu_arch = CPU_ARCH_UNKNOWN;
} else if ((read_cpuid_id() & 0 x0008f000) == 0 x00007000) {
cpu_arch = (read_cpuid_id() & (1 << 23 )) ? CPU_ARCH_ARMv4T : CPU_ARCH_ARMv3;
} else if ((read_cpuid_id() & 0 x00080000) == 0 x00000000) {
cpu_arch = (read_cpuid_id() >> 16 ) & 7 ;
if (cpu_arch)
cpu_arch += CPU_ARCH_ARMv3;
} else if ((read_cpuid_id() & 0 x000f0000) == 0 x000f0000) {
/* 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 & 0 x0000000f) >= 0 x00000003 ||
(mmfr0 & 0 x000000f0) >= 0 x00000030)
cpu_arch = CPU_ARCH_ARMv7;
else if ((mmfr0 & 0 x0000000f) == 0 x00000002 ||
(mmfr0 & 0 x000000f0) == 0 x00000020)
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:
set_csselr(CSSELR_ICACHE | CSSELR_L1);
isb();
id_reg = read_ccsidr();
line_size = 4 << ((id_reg & 0 x7) + 2 );
num_sets = ((id_reg >> 13 ) & 0 x7fff) + 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_ARMv6) {
unsigned int cachetype = read_cpuid_cachetype();
if ((arch == CPU_ARCH_ARMv7M) && !(cachetype & 0 xf000f)) {
cacheid = 0 ;
} else 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(0 xfb90, 0 xf0f1);
return __opcode_to_mem_thumb32(insn);
}
/* "sdiv r0, r0, r1" */
return __opcode_to_mem_arm(0 xe710f110);
}
static inline u32 __attribute_const__ udiv_instruction(void )
{
if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
/* "udiv r0, r0, r1" */
u32 insn = __opcode_thumb32_compose(0 xfbb0, 0 xf0f1);
return __opcode_to_mem_thumb32(insn);
}
/* "udiv r0, r0, r1" */
return __opcode_to_mem_arm(0 xe730f110);
}
static inline u32 __attribute_const__ bx_lr_instruction(void )
{
if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
/* "bx lr; nop" */
u32 insn = __opcode_thumb32_compose(0 x4770, 0 x46c0);
return __opcode_to_mem_thumb32(insn);
}
/* "bx lr" */
return __opcode_to_mem_arm(0 xe12fff1e);
}
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 );
}
#else
static inline void patch_aeabi_idiv(void ) { }
#endif
static void __init cpuid_init_hwcaps(void )
{
int block;
u32 isar5;
u32 isar6;
u32 pfr2;
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;
/* Check for Speculation barrier instruction */
isar6 = read_cpuid_ext(CPUID_EXT_ISAR6);
block = cpuid_feature_extract_field(isar6, 12 );
if (block >= 1 )
elf_hwcap2 |= HWCAP2_SB;
/* Check for Speculative Store Bypassing control */
pfr2 = read_cpuid_ext(CPUID_EXT_PFR2);
block = cpuid_feature_extract_field(pfr2, 4 );
if (block >= 1 )
elf_hwcap2 |= HWCAP2_SSBS;
}
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;
return ;
}
/* Verify if CPUID scheme is implemented */
if ((id & 0 x000f0000) != 0 x000f0000)
return ;
/*
* If the CPU supports LDREX / STREX and LDREXB / STREXB ,
* avoid advertising SWP ; it may not be atomic with
* multiprocessing cores .
*/
if (cpuid_feature_extract(CPUID_EXT_ISAR3, 12 ) > 1 ||
(cpuid_feature_extract(CPUID_EXT_ISAR3, 12 ) == 1 &&
cpuid_feature_extract(CPUID_EXT_ISAR4, 20 ) >= 3 ))
elf_hwcap &= ~HWCAP_SWP;
}
/*
* cpu_init - initialise one CPU .
*
* cpu_init sets up the per - CPU stacks .
*/
void notrace cpu_init(void )
{
#ifndef CONFIG_CPU_V7M
unsigned int cpu = smp_processor_id();
struct stack *stk = &stacks[cpu];
if (cpu >= NR_CPUS) {
pr_crit("CPU%u: bad primary CPU number\n" , cpu);
BUG();
}
/*
* This only works on resume and secondary cores . For booting on the
* boot cpu , smp_prepare_boot_cpu is called after percpu area setup .
*/
set_my_cpu_offset(per_cpu_offset(cpu));
cpu_proc_init();
/*
* Define the placement constraint for the inline asm directive below .
* In Thumb - 2 , msr with an immediate value is not allowed .
*/
#ifdef CONFIG_THUMB2_KERNEL
#define PLC_l "l"
#define PLC_r "r"
#else
#define PLC_l "I"
#define PLC_r "I"
#endif
/*
* setup stacks for re - entrant exception handlers
*/
__asm__ (
"msr cpsr_c, %1\n\t"
"add r14, %0, %2\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %3\n\t"
"add r14, %0, %4\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %5\n\t"
"add r14, %0, %6\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %7\n\t"
"add r14, %0, %8\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %9"
:
: "r" (stk),
PLC_r (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
"I" (offsetof(struct stack, irq[0 ])),
PLC_r (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
"I" (offsetof(struct stack, abt[0 ])),
PLC_r (PSR_F_BIT | PSR_I_BIT | UND_MODE),
"I" (offsetof(struct stack, und[0 ])),
PLC_r (PSR_F_BIT | PSR_I_BIT | FIQ_MODE),
"I" (offsetof(struct stack, fiq[0 ])),
PLC_l (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
: "r14" );
#endif
}
u32 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1 ] = MPIDR_INVALID };
void __init smp_setup_processor_id(void )
{
int i;
u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0 ;
u32 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0 );
cpu_logical_map(0 ) = cpu;
for (i = 1 ; i < nr_cpu_ids; ++i)
cpu_logical_map(i) = i == cpu ? 0 : i;
/*
* clear _ _ my_cpu_offset on boot CPU to avoid hang caused by
* using percpu variable early , for example , lockdep will
* access percpu variable inside lock_release
*/
set_my_cpu_offset(0 );
pr_info("Booting Linux on physical CPU 0x%x\n" , mpidr);
}
struct mpidr_hash mpidr_hash;
#ifdef CONFIG_SMP
/**
* smp_build_mpidr_hash - Pre - compute shifts required at each affinity
* level in order to build a linear index from an
* MPIDR value . Resulting algorithm is a collision
* free hash carried out through shifting and ORing
*/
static void __init smp_build_mpidr_hash(void )
{
u32 i, affinity;
u32 fs[3 ], bits[3 ], ls, mask = 0 ;
/*
* Pre - scan the list of MPIDRS and filter out bits that do
* not contribute to affinity levels , ie they never toggle .
*/
for_each_possible_cpu(i)
mask |= (cpu_logical_map(i) ^ cpu_logical_map(0 ));
pr_debug("mask of set bits 0x%x\n" , mask);
/*
* Find and stash the last and first bit set at all affinity levels to
* check how many bits are required to represent them .
*/
for (i = 0 ; i < 3 ; i++) {
affinity = MPIDR_AFFINITY_LEVEL(mask, i);
/*
* Find the MSB bit and LSB bits position
* to determine how many bits are required
* to express the affinity level .
*/
ls = fls(affinity);
fs[i] = affinity ? ffs(affinity) - 1 : 0 ;
bits[i] = ls - fs[i];
}
/*
* An index can be created from the MPIDR by isolating the
* significant bits at each affinity level and by shifting
* them in order to compress the 24 bits values space to a
* compressed set of values . This is equivalent to hashing
* the MPIDR through shifting and ORing . It is a collision free
* hash though not minimal since some levels might contain a number
* of CPUs that is not an exact power of 2 and their bit
* representation might contain holes , eg MPIDR [ 7 : 0 ] = { 0 x2 , 0 x80 } .
*/
mpidr_hash.shift_aff[0 ] = fs[0 ];
mpidr_hash.shift_aff[1 ] = MPIDR_LEVEL_BITS + fs[1 ] - bits[0 ];
mpidr_hash.shift_aff[2 ] = 2 *MPIDR_LEVEL_BITS + fs[2 ] -
(bits[1 ] + bits[0 ]);
mpidr_hash.mask = mask;
mpidr_hash.bits = bits[2 ] + bits[1 ] + bits[0 ];
pr_debug("MPIDR hash: aff0[%u] aff1[%u] aff2[%u] mask[0x%x] bits[%u]\n" ,
mpidr_hash.shift_aff[0 ],
mpidr_hash.shift_aff[1 ],
mpidr_hash.shift_aff[2 ],
mpidr_hash.mask,
mpidr_hash.bits);
/*
* 4 x is an arbitrary value used to warn on a hash table much bigger
* than expected on most systems .
*/
if (mpidr_hash_size() > 4 * num_possible_cpus())
pr_warn("Large number of MPIDR hash buckets detected\n" );
sync_cache_w(&mpidr_hash);
}
#endif
/*
* locate processor in the list of supported processor types . The linker
* builds this table for us from the entries in arch / arm / mm / proc - * . S
*/
struct proc_info_list *lookup_processor(u32 midr)
{
struct proc_info_list *list = lookup_processor_type(midr);
if (!list) {
pr_err("CPU%u: configuration botched (ID %08x), CPU halted\n" ,
smp_processor_id(), midr);
while (1 )
/* can't use cpu_relax() here as it may require MMU setup */;
}
return list;
}
static void __init setup_processor(void )
{
unsigned int midr = read_cpuid_id();
struct proc_info_list *list = lookup_processor(midr);
cpu_name = list->cpu_name;
__cpu_architecture = __get_cpu_architecture();
init_proc_vtable(list->proc);
#ifdef MULTI_TLB
cpu_tlb = *list->tlb;
#endif
#ifdef MULTI_USER
cpu_user = *list->user;
#endif
#ifdef MULTI_CACHE
cpu_cache = *list->cache;
#endif
pr_info("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n" ,
list->cpu_name, midr, midr & 15 ,
proc_arch[cpu_architecture()], get_cr());
snprintf(init_utsname()->machine, __NEW_UTS_LEN + 1 , "%s%c" ,
list->arch_name, ENDIANNESS);
snprintf(elf_platform, ELF_PLATFORM_SIZE, "%s%c" ,
list->elf_name, ENDIANNESS);
elf_hwcap = list->elf_hwcap;
cpuid_init_hwcaps();
patch_aeabi_idiv();
#ifndef CONFIG_ARM_THUMB
elf_hwcap &= ~(HWCAP_THUMB | HWCAP_IDIVT);
#endif
#ifdef CONFIG_MMU
init_default_cache_policy(list->__cpu_mm_mmu_flags);
#endif
erratum_a15_798181_init();
elf_hwcap_fixup();
cacheid_init();
cpu_init();
}
void __init dump_machine_table(void )
{
const struct machine_desc *p;
early_print("Available machine support:\n\nID (hex)\tNAME\n" );
for_each_machine_desc(p)
early_print("%08x\t%s\n" , p->nr, p->name);
early_print("\nPlease check your kernel config and/or bootloader.\n" );
while (true )
/* can't use cpu_relax() here as it may require MMU setup */;
}
int __init arm_add_memory(u64 start, u64 size)
{
u64 aligned_start;
/*
* Ensure that start / size are aligned to a page boundary .
* Size is rounded down , start is rounded up .
*/
aligned_start = PAGE_ALIGN(start);
if (aligned_start > start + size)
size = 0 ;
else
size -= aligned_start - start;
#ifndef CONFIG_PHYS_ADDR_T_64BIT
if (aligned_start > ULONG_MAX) {
pr_crit("Ignoring memory at 0x%08llx outside 32-bit physical address space\n" ,
start);
return -EINVAL;
}
if (aligned_start + size > ULONG_MAX) {
pr_crit("Truncating memory at 0x%08llx to fit in 32-bit physical address space\n" ,
(long long )start);
/*
* To ensure bank - > start + bank - > size is representable in
* 32 bits , we use ULONG_MAX as the upper limit rather than 4 GB .
* This means we lose a page after masking .
*/
size = ULONG_MAX - aligned_start;
}
#endif
if (aligned_start < PHYS_OFFSET) {
if (aligned_start + size <= PHYS_OFFSET) {
pr_info("Ignoring memory below PHYS_OFFSET: 0x%08llx-0x%08llx\n" ,
aligned_start, aligned_start + size);
return -EINVAL;
}
pr_info("Ignoring memory below PHYS_OFFSET: 0x%08llx-0x%08llx\n" ,
aligned_start, (u64)PHYS_OFFSET);
size -= PHYS_OFFSET - aligned_start;
aligned_start = PHYS_OFFSET;
}
start = aligned_start;
size = size & ~(phys_addr_t)(PAGE_SIZE - 1 );
/*
* Check whether this memory region has non - zero size or
* invalid node number .
*/
if (size == 0 )
return -EINVAL;
memblock_add(start, size);
return 0 ;
}
/*
* Pick out the memory size . We look for mem = size @ start ,
* where start and size are " size [ KkMm ] "
*/
static int __init early_mem(char *p)
{
static int usermem __initdata = 0 ;
u64 size;
u64 start;
char *endp;
/*
* If the user specifies memory size , we
* blow away any automatically generated
* size .
*/
if (usermem == 0 ) {
usermem = 1 ;
memblock_remove(memblock_start_of_DRAM(),
memblock_end_of_DRAM() - memblock_start_of_DRAM());
}
start = PHYS_OFFSET;
size = memparse(p, &endp);
if (*endp == '@' )
start = memparse(endp + 1 , NULL);
arm_add_memory(start, size);
return 0 ;
}
early_param("mem" , early_mem);
static void __init request_standard_resources(const struct machine_desc *mdesc)
{
phys_addr_t start, end, res_end;
struct resource *res;
u64 i;
kernel_code.start = virt_to_phys(_text);
kernel_code.end = virt_to_phys(__init_begin - 1 );
kernel_data.start = virt_to_phys(_sdata);
kernel_data.end = virt_to_phys(_end - 1 );
for_each_mem_range(i, &start, &end) {
unsigned long boot_alias_start;
/*
* In memblock , end points to the first byte after the
* range while in resourses , end points to the last byte in
* the range .
*/
res_end = end - 1 ;
/*
* Some systems have a special memory alias which is only
* used for booting . We need to advertise this region to
* kexec - tools so they know where bootable RAM is located .
*/
boot_alias_start = phys_to_idmap(start);
if (arm_has_idmap_alias() && boot_alias_start != IDMAP_INVALID_ADDR) {
res = memblock_alloc_or_panic(sizeof (*res), SMP_CACHE_BYTES);
res->name = "System RAM (boot alias)" ;
res->start = boot_alias_start;
res->end = phys_to_idmap(res_end);
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
request_resource(&iomem_resource, res);
}
res = memblock_alloc_or_panic(sizeof (*res), SMP_CACHE_BYTES);
res->name = "System RAM" ;
res->start = start;
res->end = res_end;
res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
request_resource(&iomem_resource, res);
if (kernel_code.start >= res->start &&
kernel_code.end <= res->end)
request_resource(res, &kernel_code);
if (kernel_data.start >= res->start &&
kernel_data.end <= res->end)
request_resource(res, &kernel_data);
}
if (mdesc->video_start) {
video_ram.start = mdesc->video_start;
video_ram.end = mdesc->video_end;
request_resource(&iomem_resource, &video_ram);
}
/*
* Some machines don ' t have the possibility of ever
* possessing lp0 , lp1 or lp2
*/
if (mdesc->reserve_lp0)
request_resource(&ioport_resource, &lp0);
if (mdesc->reserve_lp1)
request_resource(&ioport_resource, &lp1);
if (mdesc->reserve_lp2)
request_resource(&ioport_resource, &lp2);
}
#if defined (CONFIG_VGA_CONSOLE)
struct screen_info vgacon_screen_info = {
.orig_video_lines = 30 ,
.orig_video_cols = 80 ,
.orig_video_mode = 0 ,
.orig_video_ega_bx = 0 ,
.orig_video_isVGA = 1 ,
.orig_video_points = 8
};
#endif
static int __init customize_machine(void )
{
/*
* customizes platform devices , or adds new ones
* On DT based machines , we fall back to populating the
* machine from the device tree , if no callback is provided ,
* otherwise we would always need an init_machine callback .
*/
if (machine_desc->init_machine)
machine_desc->init_machine();
return 0 ;
}
arch_initcall(customize_machine);
static int __init init_machine_late(void )
{
struct device_node *root;
int ret;
if (machine_desc->init_late)
machine_desc->init_late();
root = of_find_node_by_path("/" );
if (root) {
ret = of_property_read_string(root, "serial-number" ,
&system_serial);
if (ret)
system_serial = NULL;
}
if (!system_serial)
system_serial = kasprintf(GFP_KERNEL, "%08x%08x" ,
system_serial_high,
system_serial_low);
return 0 ;
}
late_initcall(init_machine_late);
#ifdef CONFIG_CRASH_RESERVE
/*
* The crash region must be aligned to 128 MB to avoid
* zImage relocating below the reserved region .
*/
#define CRASH_ALIGN (128 << 20 )
static inline unsigned long long get_total_mem(void )
{
unsigned long total;
total = max_low_pfn - min_low_pfn;
return total << PAGE_SHIFT;
}
/**
* reserve_crashkernel ( ) - reserves memory are for crash kernel
*
* This function reserves memory area given in " crashkernel = " kernel command
* line parameter . The memory reserved is used by a dump capture kernel when
* primary kernel is crashing .
*/
static void __init reserve_crashkernel(void )
{
unsigned long long crash_size, crash_base;
unsigned long long total_mem;
int ret;
total_mem = get_total_mem();
ret = parse_crashkernel(boot_command_line, total_mem,
&crash_size, &crash_base,
NULL, NULL, NULL);
/* invalid value specified or crashkernel=0 */
if (ret || !crash_size)
return ;
if (crash_base <= 0 ) {
unsigned long long crash_max = idmap_to_phys((u32)~0 );
unsigned long long lowmem_max = __pa(high_memory - 1 ) + 1 ;
if (crash_max > lowmem_max)
crash_max = lowmem_max;
crash_base = memblock_phys_alloc_range(crash_size, CRASH_ALIGN,
CRASH_ALIGN, crash_max);
if (!crash_base) {
pr_err("crashkernel reservation failed - No suitable area found.\n" );
return ;
}
} else {
unsigned long long crash_max = crash_base + crash_size;
unsigned long long start;
start = memblock_phys_alloc_range(crash_size, SECTION_SIZE,
crash_base, crash_max);
if (!start) {
pr_err("crashkernel reservation failed - memory is in use.\n" );
return ;
}
}
pr_info("Reserving %ldMB of memory at %ldMB for crashkernel (System RAM: %ldMB)\n" ,
(unsigned long )(crash_size >> 20 ),
(unsigned long )(crash_base >> 20 ),
(unsigned long )(total_mem >> 20 ));
/* The crashk resource must always be located in normal mem */
crashk_res.start = crash_base;
crashk_res.end = crash_base + crash_size - 1 ;
insert_resource(&iomem_resource, &crashk_res);
if (arm_has_idmap_alias()) {
/*
* If we have a special RAM alias for use at boot , we
* need to advertise to kexec tools where the alias is .
*/
static struct resource crashk_boot_res = {
.name = "Crash kernel (boot alias)" ,
.flags = IORESOURCE_BUSY | IORESOURCE_MEM,
};
crashk_boot_res.start = phys_to_idmap(crash_base);
crashk_boot_res.end = crashk_boot_res.start + crash_size - 1 ;
insert_resource(&iomem_resource, &crashk_boot_res);
}
}
#else
static inline void reserve_crashkernel(void ) {}
#endif /* CONFIG_CRASH_RESERVE*/
void __init hyp_mode_check(void )
{
#ifdef CONFIG_ARM_VIRT_EXT
sync_boot_mode();
if (is_hyp_mode_available()) {
pr_info("CPU: All CPU(s) started in HYP mode.\n" );
pr_info("CPU: Virtualization extensions available.\n" );
} else if (is_hyp_mode_mismatched()) {
pr_warn("CPU: WARNING: CPU(s) started in wrong/inconsistent modes (primary CPU mode 0x%x)\n" ,
__boot_cpu_mode & MODE_MASK);
pr_warn("CPU: This may indicate a broken bootloader or firmware.\n" );
} else
pr_info("CPU: All CPU(s) started in SVC mode.\n" );
#endif
}
static void (*__arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
static int arm_restart(struct notifier_block *nb, unsigned long action,
void *data)
{
__arm_pm_restart(action, data);
return NOTIFY_DONE;
}
static struct notifier_block arm_restart_nb = {
.notifier_call = arm_restart,
.priority = 128 ,
};
void __init setup_arch(char **cmdline_p)
{
const struct machine_desc *mdesc = NULL;
void *atags_vaddr = NULL;
if (__atags_pointer)
atags_vaddr = FDT_VIRT_BASE(__atags_pointer);
setup_processor();
if (atags_vaddr) {
mdesc = setup_machine_fdt(atags_vaddr);
if (mdesc)
memblock_reserve(__atags_pointer,
fdt_totalsize(atags_vaddr));
}
if (!mdesc)
mdesc = setup_machine_tags(atags_vaddr, __machine_arch_type);
if (!mdesc) {
early_print("\nError: invalid dtb and unrecognized/unsupported machine ID\n" );
early_print(" r1=0x%08x, r2=0x%08x\n" , __machine_arch_type,
__atags_pointer);
if (__atags_pointer)
early_print(" r2[]=%*ph\n" , 16 , atags_vaddr);
dump_machine_table();
}
machine_desc = mdesc;
machine_name = mdesc->name;
dump_stack_set_arch_desc("%s" , mdesc->name);
if (mdesc->reboot_mode != REBOOT_HARD)
reboot_mode = mdesc->reboot_mode;
setup_initial_init_mm(_text, _etext, _edata, _end);
/* populate cmd_line too for later use, preserving boot_command_line */
strscpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
early_fixmap_init();
early_ioremap_init();
parse_early_param();
#ifdef CONFIG_MMU
early_mm_init(mdesc);
#endif
setup_dma_zone(mdesc);
xen_early_init();
arm_efi_init();
/*
* Make sure the calculation for lowmem / highmem is set appropriately
* before reserving / allocating any memory
*/
adjust_lowmem_bounds();
arm_memblock_init(mdesc);
/* Memory may have been removed so recalculate the bounds. */
adjust_lowmem_bounds();
early_ioremap_reset();
paging_init(mdesc);
kasan_init();
request_standard_resources(mdesc);
if (mdesc->restart) {
__arm_pm_restart = mdesc->restart;
register_restart_handler(&arm_restart_nb);
}
unflatten_device_tree();
arm_dt_init_cpu_maps();
psci_dt_init();
#ifdef CONFIG_SMP
if (is_smp()) {
if (!mdesc->smp_init || !mdesc->smp_init()) {
if (psci_smp_available())
smp_set_ops(&psci_smp_ops);
else if (mdesc->smp)
smp_set_ops(mdesc->smp);
}
smp_init_cpus();
smp_build_mpidr_hash();
}
#endif
if (!is_smp())
hyp_mode_check();
reserve_crashkernel();
#ifdef CONFIG_VT
#if defined (CONFIG_VGA_CONSOLE)
vgacon_register_screen(&vgacon_screen_info);
#endif
#endif
if (mdesc->init_early)
mdesc->init_early();
}
bool arch_cpu_is_hotpluggable(int num)
{
return platform_can_hotplug_cpu(num);
}
#ifdef CONFIG_HAVE_PROC_CPU
static int __init proc_cpu_init(void )
{
struct proc_dir_entry *res;
res = proc_mkdir("cpu" , NULL);
if (!res)
return -ENOMEM;
return 0 ;
}
fs_initcall(proc_cpu_init);
#endif
static const char *hwcap_str[] = {
"swp" ,
"half" ,
"thumb" ,
"26bit" ,
"fastmult" ,
"fpa" ,
"vfp" ,
"edsp" ,
"java" ,
"iwmmxt" ,
"crunch" ,
"thumbee" ,
"neon" ,
"vfpv3" ,
"vfpv3d16" ,
"tls" ,
"vfpv4" ,
"idiva" ,
"idivt" ,
"vfpd32" ,
"lpae" ,
"evtstrm" ,
"fphp" ,
"asimdhp" ,
"asimddp" ,
"asimdfhm" ,
"asimdbf16" ,
"i8mm" ,
NULL
};
static const char *hwcap2_str[] = {
"aes" ,
"pmull" ,
"sha1" ,
"sha2" ,
"crc32" ,
"sb" ,
"ssbs" ,
NULL
};
static int c_show(struct seq_file *m, void *v)
{
int i, j;
u32 cpuid;
for_each_online_cpu(i) {
/*
* glibc reads / proc / cpuinfo to determine the number of
* online processors , looking for lines beginning with
* " processor " . Give glibc what it expects .
*/
seq_printf(m, "processor\t: %d\n" , i);
cpuid = is_smp() ? per_cpu(cpu_data, i).cpuid : read_cpuid_id();
seq_printf(m, "model name\t: %s rev %d (%s)\n" ,
cpu_name, cpuid & 15 , elf_platform);
#if defined (CONFIG_SMP)
seq_printf(m, "BogoMIPS\t: %lu.%02lu\n" ,
per_cpu(cpu_data, i).loops_per_jiffy / (500000 UL/HZ),
(per_cpu(cpu_data, i).loops_per_jiffy / (5000 UL/HZ)) % 100 );
#else
seq_printf(m, "BogoMIPS\t: %lu.%02lu\n" ,
loops_per_jiffy / (500000 /HZ),
(loops_per_jiffy / (5000 /HZ)) % 100 );
#endif
/* dump out the processor features */
seq_puts(m, "Features\t: " );
for (j = 0 ; hwcap_str[j]; j++)
if (elf_hwcap & (1 << j))
seq_printf(m, "%s " , hwcap_str[j]);
for (j = 0 ; hwcap2_str[j]; j++)
if (elf_hwcap2 & (1 << j))
seq_printf(m, "%s " , hwcap2_str[j]);
seq_printf(m, "\nCPU implementer\t: 0x%02x\n" , cpuid >> 24 );
seq_printf(m, "CPU architecture: %s\n" ,
proc_arch[cpu_architecture()]);
if ((cpuid & 0 x0008f000) == 0 x00000000) {
/* pre-ARM7 */
seq_printf(m, "CPU part\t: %07x\n" , cpuid >> 4 );
} else {
if ((cpuid & 0 x0008f000) == 0 x00007000) {
/* ARM7 */
seq_printf(m, "CPU variant\t: 0x%02x\n" ,
(cpuid >> 16 ) & 127 );
} else {
/* post-ARM7 */
seq_printf(m, "CPU variant\t: 0x%x\n" ,
(cpuid >> 20 ) & 15 );
}
seq_printf(m, "CPU part\t: 0x%03x\n" ,
(cpuid >> 4 ) & 0 xfff);
}
seq_printf(m, "CPU revision\t: %d\n\n" , cpuid & 15 );
}
seq_printf(m, "Hardware\t: %s\n" , machine_name);
seq_printf(m, "Revision\t: %04x\n" , system_rev);
seq_printf(m, "Serial\t\t: %s\n" , system_serial);
return 0 ;
}
static void *c_start(struct seq_file *m, loff_t *pos)
{
return *pos < 1 ? (void *)1 : NULL;
}
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
++*pos;
return NULL;
}
static void c_stop(struct seq_file *m, void *v)
{
}
const struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = c_show
};
Messung V0.5 in Prozent C=96 H=85 G=90
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland