xref: /linux/arch/s390/kernel/early.c (revision 468a3bc2b7b955a7cf97d47c6022bf1ae4a538a3)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2ab14de6cSHeiko Carstens /*
3155af2f9SHans-Joachim Picht  *    Copyright IBM Corp. 2007, 2009
4ab14de6cSHeiko Carstens  *    Author(s): Hongjie Yang <hongjie@us.ibm.com>,
5ab14de6cSHeiko Carstens  */
6ab14de6cSHeiko Carstens 
711af97e1SHendrik Brueckner #define KMSG_COMPONENT "setup"
811af97e1SHendrik Brueckner #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
911af97e1SHendrik Brueckner 
1092e6ecf3SChristian Borntraeger #include <linux/compiler.h>
11ab14de6cSHeiko Carstens #include <linux/init.h>
12ab14de6cSHeiko Carstens #include <linux/errno.h>
13ab14de6cSHeiko Carstens #include <linux/string.h>
14ab14de6cSHeiko Carstens #include <linux/ctype.h>
15ab14de6cSHeiko Carstens #include <linux/lockdep.h>
16dcc096c5SPaul Gortmaker #include <linux/extable.h>
17ab14de6cSHeiko Carstens #include <linux/pfn.h>
18ab14de6cSHeiko Carstens #include <linux/uaccess.h>
1911af97e1SHendrik Brueckner #include <linux/kernel.h>
20d09a307fSHeiko Carstens #include <asm/asm-extable.h>
21bb1520d5SAlexander Gordeev #include <linux/memblock.h>
221ec2772eSMartin Schwidefsky #include <asm/diag.h>
23a0443fbbSHendrik Brueckner #include <asm/ebcdic.h>
2446b05d26SMichael Holzheu #include <asm/ipl.h>
25ab14de6cSHeiko Carstens #include <asm/lowcore.h>
26ab14de6cSHeiko Carstens #include <asm/processor.h>
27ab14de6cSHeiko Carstens #include <asm/sections.h>
28ab14de6cSHeiko Carstens #include <asm/setup.h>
2992e6ecf3SChristian Borntraeger #include <asm/sysinfo.h>
30ab14de6cSHeiko Carstens #include <asm/cpcmd.h>
31ab14de6cSHeiko Carstens #include <asm/sclp.h>
32a0616cdeSDavid Howells #include <asm/facility.h>
3349698745SVasily Gorbik #include <asm/boot_data.h>
34c2313594SVasily Gorbik #include <asm/switch_to.h>
35a806170eSHeiko Carstens #include "entry.h"
36ab14de6cSHeiko Carstens 
378ee0d2fbSVasily Gorbik #define decompressor_handled_param(param)			\
388ee0d2fbSVasily Gorbik static int __init ignore_decompressor_param_##param(char *s)	\
398ee0d2fbSVasily Gorbik {								\
408ee0d2fbSVasily Gorbik 	return 0;						\
418ee0d2fbSVasily Gorbik }								\
428ee0d2fbSVasily Gorbik early_param(#param, ignore_decompressor_param_##param)
438ee0d2fbSVasily Gorbik 
448ee0d2fbSVasily Gorbik decompressor_handled_param(mem);
458ee0d2fbSVasily Gorbik decompressor_handled_param(vmalloc);
468ee0d2fbSVasily Gorbik decompressor_handled_param(dfltcc);
478ee0d2fbSVasily Gorbik decompressor_handled_param(facilities);
488ee0d2fbSVasily Gorbik decompressor_handled_param(nokaslr);
49*468a3bc2SHeiko Carstens decompressor_handled_param(cmma);
508ee0d2fbSVasily Gorbik #if IS_ENABLED(CONFIG_KVM)
518ee0d2fbSVasily Gorbik decompressor_handled_param(prot_virt);
528ee0d2fbSVasily Gorbik #endif
538ee0d2fbSVasily Gorbik 
54557b1970SVasily Gorbik static void __init kasan_early_init(void)
55557b1970SVasily Gorbik {
56557b1970SVasily Gorbik #ifdef CONFIG_KASAN
57557b1970SVasily Gorbik 	init_task.kasan_depth = 0;
58557b1970SVasily Gorbik 	sclp_early_printk("KernelAddressSanitizer initialized\n");
59557b1970SVasily Gorbik #endif
60557b1970SVasily Gorbik }
61557b1970SVasily Gorbik 
622e83e0ebSVasily Gorbik static void __init reset_tod_clock(void)
632e83e0ebSVasily Gorbik {
64530f639fSHeiko Carstens 	union tod_clock clk;
652e83e0ebSVasily Gorbik 
66530f639fSHeiko Carstens 	if (store_tod_clock_ext_cc(&clk) == 0)
672e83e0ebSVasily Gorbik 		return;
682e83e0ebSVasily Gorbik 	/* TOD clock not running. Set the clock to Unix Epoch. */
69530f639fSHeiko Carstens 	if (set_tod_clock(TOD_UNIX_EPOCH) || store_tod_clock_ext_cc(&clk))
702e83e0ebSVasily Gorbik 		disabled_wait();
712e83e0ebSVasily Gorbik 
72f8d8977aSHeiko Carstens 	memset(&tod_clock_base, 0, sizeof(tod_clock_base));
73f8d8977aSHeiko Carstens 	tod_clock_base.tod = TOD_UNIX_EPOCH;
742e83e0ebSVasily Gorbik 	S390_lowcore.last_update_clock = TOD_UNIX_EPOCH;
752e83e0ebSVasily Gorbik }
762e83e0ebSVasily Gorbik 
77b6112ccbSMartin Schwidefsky /*
78ab14de6cSHeiko Carstens  * Initialize storage key for kernel pages
79ab14de6cSHeiko Carstens  */
80ab14de6cSHeiko Carstens static noinline __init void init_kernel_storage_key(void)
81ab14de6cSHeiko Carstens {
82127c1fefSMartin Schwidefsky #if PAGE_DEFAULT_KEY
83ab14de6cSHeiko Carstens 	unsigned long end_pfn, init_pfn;
84ab14de6cSHeiko Carstens 
85320d9555SVasily Gorbik 	end_pfn = PFN_UP(__pa(_end));
86ab14de6cSHeiko Carstens 
87ab14de6cSHeiko Carstens 	for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++)
88e2b8d7afSMartin Schwidefsky 		page_set_storage_key(init_pfn << PAGE_SHIFT,
89e2b8d7afSMartin Schwidefsky 				     PAGE_DEFAULT_KEY, 0);
90127c1fefSMartin Schwidefsky #endif
91ab14de6cSHeiko Carstens }
92ab14de6cSHeiko Carstens 
93fade4dc4SHeiko Carstens static __initdata char sysinfo_page[PAGE_SIZE] __aligned(PAGE_SIZE);
9492e6ecf3SChristian Borntraeger 
95ab14de6cSHeiko Carstens static noinline __init void detect_machine_type(void)
96ab14de6cSHeiko Carstens {
97fade4dc4SHeiko Carstens 	struct sysinfo_3_2_2 *vmms = (struct sysinfo_3_2_2 *)&sysinfo_page;
98fade4dc4SHeiko Carstens 
9927d71602SMartin Schwidefsky 	/* Check current-configuration-level */
100caf757c6SHeiko Carstens 	if (stsi(NULL, 0, 0, 0) <= 2) {
10127d71602SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR;
10292e6ecf3SChristian Borntraeger 		return;
10327d71602SMartin Schwidefsky 	}
10427d71602SMartin Schwidefsky 	/* Get virtual-machine cpu information. */
105caf757c6SHeiko Carstens 	if (stsi(vmms, 3, 2, 2) || !vmms->count)
10692e6ecf3SChristian Borntraeger 		return;
107ab14de6cSHeiko Carstens 
10803aa047eSChristian Borntraeger 	/* Detect known hypervisors */
109fade4dc4SHeiko Carstens 	if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3))
110d3135e0cSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_KVM;
11103aa047eSChristian Borntraeger 	else if (!memcmp(vmms->vm[0].cpi, "\xa9\x61\xe5\xd4", 4))
112d3135e0cSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
113ab14de6cSHeiko Carstens }
114ab14de6cSHeiko Carstens 
115d2f03974SHeiko Carstens /* Remove leading, trailing and double whitespace. */
116d2f03974SHeiko Carstens static inline void strim_all(char *str)
117d2f03974SHeiko Carstens {
118d2f03974SHeiko Carstens 	char *s;
119d2f03974SHeiko Carstens 
120d2f03974SHeiko Carstens 	s = strim(str);
121d2f03974SHeiko Carstens 	if (s != str)
122d2f03974SHeiko Carstens 		memmove(str, s, strlen(s));
123d2f03974SHeiko Carstens 	while (*str) {
124d2f03974SHeiko Carstens 		if (!isspace(*str++))
125d2f03974SHeiko Carstens 			continue;
126d2f03974SHeiko Carstens 		if (isspace(*str)) {
127d2f03974SHeiko Carstens 			s = skip_spaces(str);
128d2f03974SHeiko Carstens 			memmove(str, s, strlen(s) + 1);
129d2f03974SHeiko Carstens 		}
130d2f03974SHeiko Carstens 	}
131d2f03974SHeiko Carstens }
132d2f03974SHeiko Carstens 
1334b8fe77aSChristian Borntraeger static noinline __init void setup_arch_string(void)
1344b8fe77aSChristian Borntraeger {
1354b8fe77aSChristian Borntraeger 	struct sysinfo_1_1_1 *mach = (struct sysinfo_1_1_1 *)&sysinfo_page;
1362f8876f9SHeiko Carstens 	struct sysinfo_3_2_2 *vm = (struct sysinfo_3_2_2 *)&sysinfo_page;
1372f8876f9SHeiko Carstens 	char mstr[80], hvstr[17];
1384b8fe77aSChristian Borntraeger 
1394b8fe77aSChristian Borntraeger 	if (stsi(mach, 1, 1, 1))
1404b8fe77aSChristian Borntraeger 		return;
1414b8fe77aSChristian Borntraeger 	EBCASC(mach->manufacturer, sizeof(mach->manufacturer));
1424b8fe77aSChristian Borntraeger 	EBCASC(mach->type, sizeof(mach->type));
1434b8fe77aSChristian Borntraeger 	EBCASC(mach->model, sizeof(mach->model));
1444b8fe77aSChristian Borntraeger 	EBCASC(mach->model_capacity, sizeof(mach->model_capacity));
145d2f03974SHeiko Carstens 	sprintf(mstr, "%-16.16s %-4.4s %-16.16s %-16.16s",
146d2f03974SHeiko Carstens 		mach->manufacturer, mach->type,
147d2f03974SHeiko Carstens 		mach->model, mach->model_capacity);
148d2f03974SHeiko Carstens 	strim_all(mstr);
1492f8876f9SHeiko Carstens 	if (stsi(vm, 3, 2, 2) == 0 && vm->count) {
1502f8876f9SHeiko Carstens 		EBCASC(vm->vm[0].cpi, sizeof(vm->vm[0].cpi));
1512f8876f9SHeiko Carstens 		sprintf(hvstr, "%-16.16s", vm->vm[0].cpi);
1522f8876f9SHeiko Carstens 		strim_all(hvstr);
1532f8876f9SHeiko Carstens 	} else {
1542f8876f9SHeiko Carstens 		sprintf(hvstr, "%s",
1554b8fe77aSChristian Borntraeger 			MACHINE_IS_LPAR ? "LPAR" :
1564b8fe77aSChristian Borntraeger 			MACHINE_IS_VM ? "z/VM" :
1574b8fe77aSChristian Borntraeger 			MACHINE_IS_KVM ? "KVM" : "unknown");
1584b8fe77aSChristian Borntraeger 	}
1592f8876f9SHeiko Carstens 	dump_stack_set_arch_desc("%s (%s)", mstr, hvstr);
1602f8876f9SHeiko Carstens }
1614b8fe77aSChristian Borntraeger 
162fade4dc4SHeiko Carstens static __init void setup_topology(void)
163fade4dc4SHeiko Carstens {
164fade4dc4SHeiko Carstens 	int max_mnest;
165fade4dc4SHeiko Carstens 
166fade4dc4SHeiko Carstens 	if (!test_facility(11))
167fade4dc4SHeiko Carstens 		return;
168fade4dc4SHeiko Carstens 	S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;
169fade4dc4SHeiko Carstens 	for (max_mnest = 6; max_mnest > 1; max_mnest--) {
170caf757c6SHeiko Carstens 		if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0)
171fade4dc4SHeiko Carstens 			break;
172fade4dc4SHeiko Carstens 	}
173fade4dc4SHeiko Carstens 	topology_max_mnest = max_mnest;
174fade4dc4SHeiko Carstens }
175fade4dc4SHeiko Carstens 
17685806016SHeiko Carstens void __do_early_pgm_check(struct pt_regs *regs)
177ab14de6cSHeiko Carstens {
17846fee16fSHeiko Carstens 	if (!fixup_exception(regs))
17998587c2dSMartin Schwidefsky 		disabled_wait();
180ab14de6cSHeiko Carstens }
181ab14de6cSHeiko Carstens 
1825f954c34SHeiko Carstens static noinline __init void setup_lowcore_early(void)
183ab14de6cSHeiko Carstens {
184ab14de6cSHeiko Carstens 	psw_t psw;
185ab14de6cSHeiko Carstens 
18685806016SHeiko Carstens 	psw.addr = (unsigned long)early_pgm_check_handler;
187bb1520d5SAlexander Gordeev 	psw.mask = PSW_KERNEL_BITS;
188ab14de6cSHeiko Carstens 	S390_lowcore.program_new_psw = psw;
189c360192bSMartin Schwidefsky 	S390_lowcore.preempt_count = INIT_PREEMPT_COUNT;
190ab14de6cSHeiko Carstens }
191ab14de6cSHeiko Carstens 
19214375bc4SMartin Schwidefsky static noinline __init void setup_facility_list(void)
19314375bc4SMartin Schwidefsky {
19417e89e13SSven Schnelle 	memcpy(alt_stfle_fac_list, stfle_fac_list, sizeof(alt_stfle_fac_list));
195d768bd89SMartin Schwidefsky 	if (!IS_ENABLED(CONFIG_KERNEL_NOBP))
19617e89e13SSven Schnelle 		__clear_facility(82, alt_stfle_fac_list);
19714375bc4SMartin Schwidefsky }
19814375bc4SMartin Schwidefsky 
1992e5061e4SHeiko Carstens static __init void detect_diag9c(void)
2002e5061e4SHeiko Carstens {
2012e5061e4SHeiko Carstens 	unsigned int cpu_address;
2022e5061e4SHeiko Carstens 	int rc;
2032e5061e4SHeiko Carstens 
2042e5061e4SHeiko Carstens 	cpu_address = stap();
2051ec2772eSMartin Schwidefsky 	diag_stat_inc(DIAG_STAT_X09C);
2062e5061e4SHeiko Carstens 	asm volatile(
2072e5061e4SHeiko Carstens 		"	diag	%2,0,0x9c\n"
2082e5061e4SHeiko Carstens 		"0:	la	%0,0\n"
2092e5061e4SHeiko Carstens 		"1:\n"
2102e5061e4SHeiko Carstens 		EX_TABLE(0b,1b)
2112e5061e4SHeiko Carstens 		: "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc");
2122e5061e4SHeiko Carstens 	if (!rc)
213d3135e0cSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG9C;
2142e5061e4SHeiko Carstens }
2152e5061e4SHeiko Carstens 
2162e5061e4SHeiko Carstens static __init void detect_machine_facilities(void)
2172e5061e4SHeiko Carstens {
2183c7ef08bSHeiko Carstens 	if (test_facility(8)) {
2193c7ef08bSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT1;
22099441a38SHeiko Carstens 		system_ctl_set_bit(0, CR0_EDAT_BIT);
2213c7ef08bSHeiko Carstens 	}
22285e9d0e5SHeiko Carstens 	if (test_facility(78))
22385e9d0e5SHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT2;
22414375bc4SMartin Schwidefsky 	if (test_facility(3))
225d3135e0cSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
226a1c5befcSHeiko Carstens 	if (test_facility(50) && test_facility(73)) {
227d35339a4SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_TE;
22899441a38SHeiko Carstens 		system_ctl_set_bit(0, CR0_TRANSACTIONAL_EXECUTION_BIT);
229a1c5befcSHeiko Carstens 	}
2301b948d6cSMartin Schwidefsky 	if (test_facility(51))
2311b948d6cSMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC;
232b5510d9bSHendrik Brueckner 	if (test_facility(129)) {
23380703617SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_VX;
23499441a38SHeiko Carstens 		system_ctl_set_bit(0, CR0_VECTOR_BIT);
2352e5061e4SHeiko Carstens 	}
236c0f1d478SHeiko Carstens 	if (test_facility(130))
23757d7f939SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_NX;
238916cda1aSMartin Schwidefsky 	if (test_facility(133))
239916cda1aSMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_GS;
240f8d8977aSHeiko Carstens 	if (test_facility(139) && (tod_clock_base.tod >> 63)) {
2416e2ef5e4SMartin Schwidefsky 		/* Enabled signed clock comparator comparisons */
2426e2ef5e4SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_SCC;
2436e2ef5e4SMartin Schwidefsky 		clock_comparator_max = -1ULL >> 1;
24499441a38SHeiko Carstens 		system_ctl_set_bit(0, CR0_CLOCK_COMPARATOR_SIGN_BIT);
2456e2ef5e4SMartin Schwidefsky 	}
2463322ba0dSNiklas Schnelle 	if (IS_ENABLED(CONFIG_PCI) && test_facility(153)) {
2473322ba0dSNiklas Schnelle 		S390_lowcore.machine_flags |= MACHINE_FLAG_PCI_MIO;
2483322ba0dSNiklas Schnelle 		/* the control bit is set during PCI initialization */
2493322ba0dSNiklas Schnelle 	}
2500807b856SGerald Schaefer 	if (test_facility(194))
2510807b856SGerald Schaefer 		S390_lowcore.machine_flags |= MACHINE_FLAG_RDP;
252b5510d9bSHendrik Brueckner }
253b5510d9bSHendrik Brueckner 
2541a36a39eSMartin Schwidefsky static inline void save_vector_registers(void)
2551a36a39eSMartin Schwidefsky {
2561a36a39eSMartin Schwidefsky #ifdef CONFIG_CRASH_DUMP
2571a36a39eSMartin Schwidefsky 	if (test_facility(129))
2581a36a39eSMartin Schwidefsky 		save_vx_regs(boot_cpu_vector_save_area);
2591a36a39eSMartin Schwidefsky #endif
2601a36a39eSMartin Schwidefsky }
2611a36a39eSMartin Schwidefsky 
2624a172528SHeiko Carstens static inline void setup_low_address_protection(void)
263c02ee6a1SVasily Gorbik {
26499441a38SHeiko Carstens 	system_ctl_set_bit(0, CR0_LOW_ADDRESS_PROTECTION_BIT);
265c02ee6a1SVasily Gorbik }
266c02ee6a1SVasily Gorbik 
267c2313594SVasily Gorbik static inline void setup_access_registers(void)
268c2313594SVasily Gorbik {
269c2313594SVasily Gorbik 	unsigned int acrs[NUM_ACRS] = { 0 };
270c2313594SVasily Gorbik 
271c2313594SVasily Gorbik 	restore_access_regs(acrs);
272c2313594SVasily Gorbik }
273c2313594SVasily Gorbik 
274b5510d9bSHendrik Brueckner static int __init disable_vector_extension(char *str)
275b5510d9bSHendrik Brueckner {
276b5510d9bSHendrik Brueckner 	S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
27799441a38SHeiko Carstens 	system_ctl_clear_bit(0, CR0_VECTOR_BIT);
278673cfddfSHeiko Carstens 	return 0;
279b5510d9bSHendrik Brueckner }
280b5510d9bSHendrik Brueckner early_param("novx", disable_vector_extension);
2812e5061e4SHeiko Carstens 
28249698745SVasily Gorbik char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
2831fb81057SMichael Holzheu static void __init setup_boot_command_line(void)
2841fb81057SMichael Holzheu {
285a0443fbbSHendrik Brueckner 	/* copy arch command line */
286820109fbSWolfram Sang 	strscpy(boot_command_line, early_command_line, COMMAND_LINE_SIZE);
287a0443fbbSHendrik Brueckner }
288a0443fbbSHendrik Brueckner 
289a156f09cSHeiko Carstens static void __init sort_amode31_extable(void)
290a156f09cSHeiko Carstens {
291a156f09cSHeiko Carstens 	sort_extable(__start_amode31_ex_table, __stop_amode31_ex_table);
292a156f09cSHeiko Carstens }
293a156f09cSHeiko Carstens 
294ab14de6cSHeiko Carstens void __init startup_init(void)
295ab14de6cSHeiko Carstens {
296557b1970SVasily Gorbik 	kasan_early_init();
2972e83e0ebSVasily Gorbik 	reset_tod_clock();
298b1c0854dSMartin Schwidefsky 	time_early_init();
299ab14de6cSHeiko Carstens 	init_kernel_storage_key();
300ab14de6cSHeiko Carstens 	lockdep_off();
301a156f09cSHeiko Carstens 	sort_amode31_extable();
302ab14de6cSHeiko Carstens 	setup_lowcore_early();
30314375bc4SMartin Schwidefsky 	setup_facility_list();
304a0443fbbSHendrik Brueckner 	detect_machine_type();
3054b8fe77aSChristian Borntraeger 	setup_arch_string();
306a0443fbbSHendrik Brueckner 	setup_boot_command_line();
3072e5061e4SHeiko Carstens 	detect_diag9c();
3082e5061e4SHeiko Carstens 	detect_machine_facilities();
3091a36a39eSMartin Schwidefsky 	save_vector_registers();
310fade4dc4SHeiko Carstens 	setup_topology();
3117b50da53SMichael Holzheu 	sclp_early_detect();
3124a172528SHeiko Carstens 	setup_low_address_protection();
313c2313594SVasily Gorbik 	setup_access_registers();
314ab14de6cSHeiko Carstens 	lockdep_on();
315ab14de6cSHeiko Carstens }
316