1023 lines · 25.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Common boot and setup code for both 32-bit and 64-bit. * Extracted from arch/powerpc/kernel/setup_64.c. * * Copyright (C) 2001 PPC64 Team, IBM Corp */ #undef DEBUG #include <linux/export.h>#include <linux/panic_notifier.h>#include <linux/string.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/reboot.h>#include <linux/delay.h>#include <linux/initrd.h>#include <linux/platform_device.h>#include <linux/printk.h>#include <linux/seq_file.h>#include <linux/ioport.h>#include <linux/console.h>#include <linux/root_dev.h>#include <linux/cpu.h>#include <linux/unistd.h>#include <linux/seq_buf.h>#include <linux/serial.h>#include <linux/serial_8250.h>#include <linux/percpu.h>#include <linux/memblock.h>#include <linux/of.h>#include <linux/of_fdt.h>#include <linux/of_irq.h>#include <linux/hugetlb.h>#include <linux/pgtable.h>#include <asm/io.h>#include <asm/paca.h>#include <asm/processor.h>#include <asm/vdso_datapage.h>#include <asm/smp.h>#include <asm/elf.h>#include <asm/machdep.h>#include <asm/time.h>#include <asm/cputable.h>#include <asm/sections.h>#include <asm/firmware.h>#include <asm/btext.h>#include <asm/nvram.h>#include <asm/setup.h>#include <asm/rtas.h>#include <asm/iommu.h>#include <asm/serial.h>#include <asm/cache.h>#include <asm/page.h>#include <asm/mmu.h>#include <asm/xmon.h>#include <asm/cputhreads.h>#include <mm/mmu_decl.h>#include <asm/archrandom.h>#include <asm/fadump.h>#include <asm/udbg.h>#include <asm/hugetlb.h>#include <asm/livepatch.h>#include <asm/mmu_context.h>#include <asm/cpu_has_feature.h>#include <asm/kasan.h>#include <asm/mce.h>#include <asm/systemcfg.h>#include <linux/kmsg_dump.h> #include "setup.h" #ifdef DEBUG#define DBG(fmt...) udbg_printf(fmt)#else#define DBG(fmt...)#endif /* The main machine-dep calls structure */struct machdep_calls ppc_md;EXPORT_SYMBOL(ppc_md);struct machdep_calls *machine_id;EXPORT_SYMBOL(machine_id); int boot_cpuid = -1;EXPORT_SYMBOL_GPL(boot_cpuid);int __initdata boot_core_hwid = -1; #ifdef CONFIG_PPC64int boot_cpu_hwid = -1;#endif /* * These are used in binfmt_elf.c to put aux entries on the stack * for each elf executable being started. */int dcache_bsize;int icache_bsize; /* Variables required to store legacy IO irq routing */int of_i8042_kbd_irq;EXPORT_SYMBOL_GPL(of_i8042_kbd_irq);int of_i8042_aux_irq;EXPORT_SYMBOL_GPL(of_i8042_aux_irq); #ifdef __DO_IRQ_CANON/* XXX should go elsewhere eventually */int ppc_do_canonicalize_irqs;EXPORT_SYMBOL(ppc_do_canonicalize_irqs);#endif #ifdef CONFIG_CRASH_DUMP/* This keeps a track of which one is the crashing cpu. */int crashing_cpu = -1;#endif /* also used by kexec */void machine_shutdown(void){	/*	 * if fadump is active, cleanup the fadump registration before we	 * shutdown.	 */	fadump_cleanup(); 	if (ppc_md.machine_shutdown)		ppc_md.machine_shutdown();} static void machine_hang(void){	pr_emerg("System Halted, OK to turn off power\n");	local_irq_disable();	while (1)		;} void machine_restart(char *cmd){	machine_shutdown();	if (ppc_md.restart)		ppc_md.restart(cmd); 	smp_send_stop(); 	do_kernel_restart(cmd);	mdelay(1000); 	machine_hang();} void machine_power_off(void){	machine_shutdown();	do_kernel_power_off();	smp_send_stop();	machine_hang();}/* Used by the G5 thermal driver */EXPORT_SYMBOL_GPL(machine_power_off); void (*pm_power_off)(void);EXPORT_SYMBOL_GPL(pm_power_off); size_t __must_check arch_get_random_seed_longs(unsigned long *v, size_t max_longs){	if (max_longs && ppc_md.get_random_seed && ppc_md.get_random_seed(v))		return 1;	return 0;}EXPORT_SYMBOL(arch_get_random_seed_longs); void machine_halt(void){	machine_shutdown();	if (ppc_md.halt)		ppc_md.halt(); 	smp_send_stop();	machine_hang();} #ifdef CONFIG_SMPDEFINE_PER_CPU(unsigned int, cpu_pvr);#endif static void show_cpuinfo_summary(struct seq_file *m){	struct device_node *root;	const char *model = NULL;	unsigned long bogosum = 0;	int i; 	if (IS_ENABLED(CONFIG_SMP) && IS_ENABLED(CONFIG_PPC32)) {		for_each_online_cpu(i)			bogosum += loops_per_jiffy;		seq_printf(m, "total bogomips\t: %lu.%02lu\n",			   bogosum / (500000 / HZ), bogosum / (5000 / HZ) % 100);	}	seq_printf(m, "timebase\t: %lu\n", ppc_tb_freq);	if (ppc_md.name)		seq_printf(m, "platform\t: %s\n", ppc_md.name);	root = of_find_node_by_path("/");	if (root)		model = of_get_property(root, "model", NULL);	if (model)		seq_printf(m, "model\t\t: %s\n", model);	of_node_put(root); 	if (ppc_md.show_cpuinfo != NULL)		ppc_md.show_cpuinfo(m); 	/* Display the amount of memory */	if (IS_ENABLED(CONFIG_PPC32))		seq_printf(m, "Memory\t\t: %d MB\n",			   (unsigned int)(total_memory / (1024 * 1024)));} static int show_cpuinfo(struct seq_file *m, void *v){	unsigned long cpu_id = (unsigned long)v - 1;	unsigned int pvr;	unsigned long proc_freq;	unsigned short maj;	unsigned short min; #ifdef CONFIG_SMP	pvr = per_cpu(cpu_pvr, cpu_id);#else	pvr = mfspr(SPRN_PVR);#endif	maj = (pvr >> 8) & 0xFF;	min = pvr & 0xFF; 	seq_printf(m, "processor\t: %lu\ncpu\t\t: ", cpu_id); 	if (cur_cpu_spec->pvr_mask && cur_cpu_spec->cpu_name)		seq_puts(m, cur_cpu_spec->cpu_name);	else		seq_printf(m, "unknown (%08x)", pvr); 	if (cpu_has_feature(CPU_FTR_ALTIVEC))		seq_puts(m, ", altivec supported"); 	seq_putc(m, '\n'); #ifdef CONFIG_TAU	if (cpu_has_feature(CPU_FTR_TAU)) {		if (IS_ENABLED(CONFIG_TAU_AVERAGE)) {			/* more straightforward, but potentially misleading */			seq_printf(m,  "temperature \t: %u C (uncalibrated)\n",				   cpu_temp(cpu_id));		} else {			/* show the actual temp sensor range */			u32 temp;			temp = cpu_temp_both(cpu_id);			seq_printf(m, "temperature \t: %u-%u C (uncalibrated)\n",				   temp & 0xff, temp >> 16);		}	}#endif /* CONFIG_TAU */ 	/*	 * Platforms that have variable clock rates, should implement	 * the method ppc_md.get_proc_freq() that reports the clock	 * rate of a given cpu. The rest can use ppc_proc_freq to	 * report the clock rate that is same across all cpus.	 */	if (ppc_md.get_proc_freq)		proc_freq = ppc_md.get_proc_freq(cpu_id);	else		proc_freq = ppc_proc_freq; 	if (proc_freq)		seq_printf(m, "clock\t\t: %lu.%06luMHz\n",			   proc_freq / 1000000, proc_freq % 1000000); 	/* If we are a Freescale core do a simple check so	 * we don't have to keep adding cases in the future */	if (PVR_VER(pvr) & 0x8000) {		switch (PVR_VER(pvr)) {		case 0x8000:	/* 7441/7450/7451, Voyager */		case 0x8001:	/* 7445/7455, Apollo 6 */		case 0x8002:	/* 7447/7457, Apollo 7 */		case 0x8003:	/* 7447A, Apollo 7 PM */		case 0x8004:	/* 7448, Apollo 8 */		case 0x800c:	/* 7410, Nitro */			maj = ((pvr >> 8) & 0xF);			min = PVR_MIN(pvr);			break;		default:	/* e500/book-e */			maj = PVR_MAJ(pvr);			min = PVR_MIN(pvr);			break;		}	} else {		switch (PVR_VER(pvr)) {			case 0x1008:	/* 740P/750P ?? */				maj = ((pvr >> 8) & 0xFF) - 1;				min = pvr & 0xFF;				break;			case 0x004e: /* POWER9 bits 12-15 give chip type */			case 0x0080: /* POWER10 bit 12 gives SMT8/4 */				maj = (pvr >> 8) & 0x0F;				min = pvr & 0xFF;				break;			default:				maj = (pvr >> 8) & 0xFF;				min = pvr & 0xFF;				break;		}	} 	seq_printf(m, "revision\t: %hd.%hd (pvr %04x %04x)\n",		   maj, min, PVR_VER(pvr), PVR_REV(pvr)); 	if (IS_ENABLED(CONFIG_PPC32))		seq_printf(m, "bogomips\t: %lu.%02lu\n", loops_per_jiffy / (500000 / HZ),			   (loops_per_jiffy / (5000 / HZ)) % 100); 	seq_putc(m, '\n'); 	/* If this is the last cpu, print the summary */	if (cpu_id == cpumask_last(cpu_online_mask))		show_cpuinfo_summary(m); 	return 0;} static void *c_start(struct seq_file *m, loff_t *pos){	*pos = cpumask_next(*pos - 1, cpu_online_mask);	if ((*pos) < nr_cpu_ids)		return (void *)(unsigned long)(*pos + 1);	return NULL;} static void *c_next(struct seq_file *m, void *v, loff_t *pos){	(*pos)++;	return c_start(m, pos);} 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	= show_cpuinfo,}; void __init check_for_initrd(void){#ifdef CONFIG_BLK_DEV_INITRD	DBG(" -> check_for_initrd()  initrd_start=0x%lx  initrd_end=0x%lx\n",	    initrd_start, initrd_end); 	/* If we were passed an initrd, set the ROOT_DEV properly if the values	 * look sensible. If not, clear initrd reference.	 */	if (is_kernel_addr(initrd_start) && is_kernel_addr(initrd_end) &&	    initrd_end > initrd_start)		ROOT_DEV = Root_RAM0;	else		initrd_start = initrd_end = 0; 	if (initrd_start)		pr_info("Found initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end); 	DBG(" <- check_for_initrd()\n");#endif /* CONFIG_BLK_DEV_INITRD */} #ifdef CONFIG_SMP int threads_per_core, threads_per_subcore, threads_shift __read_mostly;cpumask_t threads_core_mask __read_mostly;EXPORT_SYMBOL_GPL(threads_per_core);EXPORT_SYMBOL_GPL(threads_per_subcore);EXPORT_SYMBOL_GPL(threads_shift);EXPORT_SYMBOL_GPL(threads_core_mask); static void __init cpu_init_thread_core_maps(int tpc){	int i; 	threads_per_core = tpc;	threads_per_subcore = tpc;	cpumask_clear(&threads_core_mask); 	/* This implementation only supports power of 2 number of threads	 * for simplicity and performance	 */	threads_shift = ilog2(tpc);	BUG_ON(tpc != (1 << threads_shift)); 	for (i = 0; i < tpc; i++)		cpumask_set_cpu(i, &threads_core_mask); 	printk(KERN_INFO "CPU maps initialized for %d thread%s per core\n",	       tpc, str_plural(tpc));	printk(KERN_DEBUG " (thread shift is %d)\n", threads_shift);}  u32 *cpu_to_phys_id = NULL; static int assign_threads(unsigned int cpu, unsigned int nthreads, bool present,			  const __be32 *hw_ids){	for (int i = 0; i < nthreads && cpu < nr_cpu_ids; i++) {		__be32 hwid; 		hwid = be32_to_cpu(hw_ids[i]); 		DBG("    thread %d -> cpu %d (hard id %d)\n", i, cpu, hwid); 		set_cpu_present(cpu, present);		set_cpu_possible(cpu, true);		cpu_to_phys_id[cpu] = hwid;		cpu++;	} 	return cpu;} /** * setup_cpu_maps - initialize the following cpu maps: *                  cpu_possible_mask *                  cpu_present_mask * * Having the possible map set up early allows us to restrict allocations * of things like irqstacks to nr_cpu_ids rather than NR_CPUS. * * We do not initialize the online map here; cpus set their own bits in * cpu_online_mask as they come up. * * This function is valid only for Open Firmware systems.  finish_device_tree * must be called before using this. * * While we're here, we may as well set the "physical" cpu ids in the paca. * * NOTE: This must match the parsing done in early_init_dt_scan_cpus. */void __init smp_setup_cpu_maps(void){	struct device_node *dn;	int cpu = 0;	int nthreads = 1; 	DBG("smp_setup_cpu_maps()\n"); 	cpu_to_phys_id = memblock_alloc_or_panic(nr_cpu_ids * sizeof(u32),					__alignof__(u32)); 	for_each_node_by_type(dn, "cpu") {		const __be32 *intserv;		__be32 cpu_be;		int len; 		DBG("  * %pOF...\n", dn); 		intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s",				&len);		if (intserv) {			DBG("    ibm,ppc-interrupt-server#s -> %lu threads\n",			    (len / sizeof(int)));		} else {			DBG("    no ibm,ppc-interrupt-server#s -> 1 thread\n");			intserv = of_get_property(dn, "reg", &len);			if (!intserv) {				cpu_be = cpu_to_be32(cpu);				/* XXX: what is this? uninitialized?? */				intserv = &cpu_be;	/* assume logical == phys */				len = 4;			}		} 		nthreads = len / sizeof(int); 		bool avail = of_device_is_available(dn);		if (!avail)			avail = !of_property_match_string(dn,					"enable-method", "spin-table"); 		if (boot_core_hwid >= 0) {			if (cpu == 0) {				pr_info("Skipping CPU node %pOF to allow for boot core.\n", dn);				cpu = nthreads;				continue;			} 			if (be32_to_cpu(intserv[0]) == boot_core_hwid) {				pr_info("Renumbered boot core %pOF to logical 0\n", dn);

Lines 1–500 of 1023.