386 lines · 8.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  Copyright IBM Corp. 2008 *  Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) */ #define pr_fmt(fmt) "cpu: " fmt #include <linux/stop_machine.h>#include <linux/cpufeature.h>#include <linux/bitops.h>#include <linux/kernel.h>#include <linux/random.h>#include <linux/sched/mm.h>#include <linux/init.h>#include <linux/seq_file.h>#include <linux/mm_types.h>#include <linux/delay.h>#include <linux/cpu.h>#include <linux/smp.h>#include <asm/text-patching.h>#include <asm/machine.h>#include <asm/diag.h>#include <asm/facility.h>#include <asm/elf.h>#include <asm/lowcore.h>#include <asm/param.h>#include <asm/sclp.h>#include <asm/smp.h> unsigned long __read_mostly elf_hwcap;char elf_platform[ELF_PLATFORM_SIZE]; struct cpu_info {	unsigned int cpu_mhz_dynamic;	unsigned int cpu_mhz_static;	struct cpuid cpu_id;}; static DEFINE_PER_CPU(struct cpu_info, cpu_info);static DEFINE_PER_CPU(int, cpu_relax_retry); static bool machine_has_cpu_mhz; void __init cpu_detect_mhz_feature(void){	if (test_facility(34) && __ecag(ECAG_CPU_ATTRIBUTE, 0) != -1UL)		machine_has_cpu_mhz = true;} static void update_cpu_mhz(void *arg){	unsigned long mhz;	struct cpu_info *c; 	mhz = __ecag(ECAG_CPU_ATTRIBUTE, 0);	c = this_cpu_ptr(&cpu_info);	c->cpu_mhz_dynamic = mhz >> 32;	c->cpu_mhz_static = mhz & 0xffffffff;} void s390_update_cpu_mhz(void){	s390_adjust_jiffies();	if (machine_has_cpu_mhz)		on_each_cpu(update_cpu_mhz, NULL, 0);} void notrace stop_machine_yield(const struct cpumask *cpumask){	int cpu, this_cpu; 	this_cpu = smp_processor_id();	if (__this_cpu_inc_return(cpu_relax_retry) >= spin_retry) {		__this_cpu_write(cpu_relax_retry, 0);		cpu = cpumask_next_wrap(this_cpu, cpumask);		if (cpu >= nr_cpu_ids)			return;		if (arch_vcpu_is_preempted(cpu))			smp_yield_cpu(cpu);	}} static void do_sync_core(void *info){	sync_core();} void text_poke_sync(void){	on_each_cpu(do_sync_core, NULL, 1);} void text_poke_sync_lock(void){	cpus_read_lock();	text_poke_sync();	cpus_read_unlock();} /* * cpu_init - initializes state that is per-CPU. */void cpu_init(void){	struct cpuid *id = this_cpu_ptr(&cpu_info.cpu_id); 	get_cpu_id(id);	if (machine_has_cpu_mhz)		update_cpu_mhz(NULL);	mmgrab(&init_mm);	current->active_mm = &init_mm;	BUG_ON(current->mm);	enter_lazy_tlb(&init_mm, current);} static void show_facilities(struct seq_file *m){	unsigned int bit; 	seq_puts(m, "facilities      :");	for_each_set_bit_inv(bit, (long *)&stfle_fac_list, MAX_FACILITY_BIT)		seq_printf(m, " %d", bit);	seq_putc(m, '\n');} static void show_cpu_summary(struct seq_file *m, void *v){	static const char *hwcap_str[] = {		[HWCAP_NR_ESAN3]	= "esan3",		[HWCAP_NR_ZARCH]	= "zarch",		[HWCAP_NR_STFLE]	= "stfle",		[HWCAP_NR_MSA]		= "msa",		[HWCAP_NR_LDISP]	= "ldisp",		[HWCAP_NR_EIMM]		= "eimm",		[HWCAP_NR_DFP]		= "dfp",		[HWCAP_NR_HPAGE]	= "edat",		[HWCAP_NR_ETF3EH]	= "etf3eh",		[HWCAP_NR_HIGH_GPRS]	= "highgprs",		[HWCAP_NR_TE]		= "te",		[HWCAP_NR_VXRS]		= "vx",		[HWCAP_NR_VXRS_BCD]	= "vxd",		[HWCAP_NR_VXRS_EXT]	= "vxe",		[HWCAP_NR_GS]		= "gs",		[HWCAP_NR_VXRS_EXT2]	= "vxe2",		[HWCAP_NR_VXRS_PDE]	= "vxp",		[HWCAP_NR_SORT]		= "sort",		[HWCAP_NR_DFLT]		= "dflt",		[HWCAP_NR_VXRS_PDE2]	= "vxp2",		[HWCAP_NR_NNPA]		= "nnpa",		[HWCAP_NR_PCI_MIO]	= "pcimio",		[HWCAP_NR_SIE]		= "sie",	};	int i, cpu; 	BUILD_BUG_ON(ARRAY_SIZE(hwcap_str) != HWCAP_NR_MAX);	seq_printf(m, "vendor_id       : IBM/S390\n"		   "# processors    : %i\n"		   "bogomips per cpu: %lu.%02lu\n",		   num_online_cpus(), loops_per_jiffy/(500000/HZ),		   (loops_per_jiffy/(5000/HZ))%100);	seq_printf(m, "max thread id   : %d\n", smp_cpu_mtid);	seq_puts(m, "features\t: ");	for (i = 0; i < ARRAY_SIZE(hwcap_str); i++)		if (hwcap_str[i] && (elf_hwcap & (1UL << i)))			seq_printf(m, "%s ", hwcap_str[i]);	seq_puts(m, "\n");	show_facilities(m);	show_cacheinfo(m);	for_each_online_cpu(cpu) {		struct cpuid *id = &per_cpu(cpu_info.cpu_id, cpu); 		seq_printf(m, "processor %d: "			   "version = %02X,  "			   "identification = %06X,  "			   "machine = %04X\n",			   cpu, id->version, id->ident, id->machine);	}} static int __init setup_hwcaps(void){	/* instructions named N3, "backported" to esa-mode */	elf_hwcap |= HWCAP_ESAN3; 	/* z/Architecture mode active */	elf_hwcap |= HWCAP_ZARCH; 	/* store-facility-list-extended */	if (test_facility(7))		elf_hwcap |= HWCAP_STFLE; 	/* message-security assist */	if (test_facility(17))		elf_hwcap |= HWCAP_MSA; 	/* long-displacement */	if (test_facility(19))		elf_hwcap |= HWCAP_LDISP; 	/* extended-immediate */	elf_hwcap |= HWCAP_EIMM; 	/* extended-translation facility 3 enhancement */	if (test_facility(22) && test_facility(30))		elf_hwcap |= HWCAP_ETF3EH; 	/* decimal floating point & perform floating point operation */	if (test_facility(42) && test_facility(44))		elf_hwcap |= HWCAP_DFP; 	/* huge page support */	if (cpu_has_edat1())		elf_hwcap |= HWCAP_HPAGE; 	/* 64-bit register support for 31-bit processes */	elf_hwcap |= HWCAP_HIGH_GPRS; 	/* transactional execution */	if (machine_has_tx())		elf_hwcap |= HWCAP_TE; 	/* vector */	if (test_facility(129)) {		elf_hwcap |= HWCAP_VXRS;		if (test_facility(134))			elf_hwcap |= HWCAP_VXRS_BCD;		if (test_facility(135))			elf_hwcap |= HWCAP_VXRS_EXT;		if (test_facility(148))			elf_hwcap |= HWCAP_VXRS_EXT2;		if (test_facility(152))			elf_hwcap |= HWCAP_VXRS_PDE;		if (test_facility(192))			elf_hwcap |= HWCAP_VXRS_PDE2;	} 	if (test_facility(150))		elf_hwcap |= HWCAP_SORT; 	if (test_facility(151))		elf_hwcap |= HWCAP_DFLT; 	if (test_facility(165))		elf_hwcap |= HWCAP_NNPA; 	/* guarded storage */	if (cpu_has_gs())		elf_hwcap |= HWCAP_GS; 	if (test_machine_feature(MFEATURE_PCI_MIO))		elf_hwcap |= HWCAP_PCI_MIO; 	/* virtualization support */	if (sclp.has_sief2)		elf_hwcap |= HWCAP_SIE; 	return 0;}arch_initcall(setup_hwcaps); static int __init setup_elf_platform(void){	struct cpuid cpu_id; 	get_cpu_id(&cpu_id);	add_device_randomness(&cpu_id, sizeof(cpu_id));	switch (cpu_id.machine) {	default:	/* Use "z10" as default. */		strscpy(elf_platform, "z10");		break;	case 0x2817:	case 0x2818:		strscpy(elf_platform, "z196");		break;	case 0x2827:	case 0x2828:		strscpy(elf_platform, "zEC12");		break;	case 0x2964:	case 0x2965:		strscpy(elf_platform, "z13");		break;	case 0x3906:	case 0x3907:		strscpy(elf_platform, "z14");		break;	case 0x8561:	case 0x8562:		strscpy(elf_platform, "z15");		break;	case 0x3931:	case 0x3932:		strscpy(elf_platform, "z16");		break;	case 0x9175:	case 0x9176:		strscpy(elf_platform, "z17");		break;	}	return 0;}arch_initcall(setup_elf_platform); static void show_cpu_topology(struct seq_file *m, unsigned long n){#ifdef CONFIG_SCHED_TOPOLOGY	seq_printf(m, "physical id     : %d\n", topology_physical_package_id(n));	seq_printf(m, "core id         : %d\n", topology_core_id(n));	seq_printf(m, "book id         : %d\n", topology_book_id(n));	seq_printf(m, "drawer id       : %d\n", topology_drawer_id(n));	seq_printf(m, "dedicated       : %d\n", topology_cpu_dedicated(n));	seq_printf(m, "address         : %d\n", smp_cpu_get_cpu_address(n));	seq_printf(m, "siblings        : %d\n", cpumask_weight(topology_core_cpumask(n)));	seq_printf(m, "cpu cores       : %d\n", topology_booted_cores(n));#endif /* CONFIG_SCHED_TOPOLOGY */} static void show_cpu_ids(struct seq_file *m, unsigned long n){	struct cpuid *id = &per_cpu(cpu_info.cpu_id, n); 	seq_printf(m, "version         : %02X\n", id->version);	seq_printf(m, "identification  : %06X\n", id->ident);	seq_printf(m, "machine         : %04X\n", id->machine);} static void show_cpu_mhz(struct seq_file *m, unsigned long n){	struct cpu_info *c = per_cpu_ptr(&cpu_info, n); 	if (!machine_has_cpu_mhz)		return;	seq_printf(m, "cpu MHz dynamic : %d\n", c->cpu_mhz_dynamic);	seq_printf(m, "cpu MHz static  : %d\n", c->cpu_mhz_static);} /* * show_cpuinfo - Get information on one CPU for use by procfs. */static int show_cpuinfo(struct seq_file *m, void *v){	unsigned long n = (unsigned long) v - 1;	unsigned long first = cpumask_first(cpu_online_mask); 	if (n == first)		show_cpu_summary(m, v);	seq_printf(m, "\ncpu number      : %ld\n", n);	show_cpu_topology(m, n);	show_cpu_ids(m, n);	show_cpu_mhz(m, n);	return 0;} static inline void *c_update(loff_t *pos){	if (*pos)		*pos = cpumask_next(*pos - 1, cpu_online_mask);	else		*pos = cpumask_first(cpu_online_mask);	return *pos < nr_cpu_ids ? (void *)*pos + 1 : NULL;} static void *c_start(struct seq_file *m, loff_t *pos){	cpus_read_lock();	return c_update(pos);} static void *c_next(struct seq_file *m, void *v, loff_t *pos){	++*pos;	return c_update(pos);} static void c_stop(struct seq_file *m, void *v){	cpus_read_unlock();} const struct seq_operations cpuinfo_op = {	.start	= c_start,	.next	= c_next,	.stop	= c_stop,	.show	= show_cpuinfo,};