xref: /linux/arch/s390/kernel/early.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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>
2230410373SHeiko Carstens #include <asm/access-regs.h>
231ec2772eSMartin Schwidefsky #include <asm/diag.h>
24a0443fbbSHendrik Brueckner #include <asm/ebcdic.h>
25fd2527f2SHeiko Carstens #include <asm/fpu.h>
2646b05d26SMichael Holzheu #include <asm/ipl.h>
27ab14de6cSHeiko Carstens #include <asm/lowcore.h>
28ab14de6cSHeiko Carstens #include <asm/processor.h>
29ab14de6cSHeiko Carstens #include <asm/sections.h>
30ab14de6cSHeiko Carstens #include <asm/setup.h>
3192e6ecf3SChristian Borntraeger #include <asm/sysinfo.h>
32ab14de6cSHeiko Carstens #include <asm/cpcmd.h>
33ab14de6cSHeiko Carstens #include <asm/sclp.h>
34a0616cdeSDavid Howells #include <asm/facility.h>
3549698745SVasily Gorbik #include <asm/boot_data.h>
36a806170eSHeiko Carstens #include "entry.h"
37ab14de6cSHeiko Carstens 
388ee0d2fbSVasily Gorbik #define decompressor_handled_param(param)			\
398ee0d2fbSVasily Gorbik static int __init ignore_decompressor_param_##param(char *s)	\
408ee0d2fbSVasily Gorbik {								\
418ee0d2fbSVasily Gorbik 	return 0;						\
428ee0d2fbSVasily Gorbik }								\
438ee0d2fbSVasily Gorbik early_param(#param, ignore_decompressor_param_##param)
448ee0d2fbSVasily Gorbik 
458ee0d2fbSVasily Gorbik decompressor_handled_param(mem);
468ee0d2fbSVasily Gorbik decompressor_handled_param(vmalloc);
478ee0d2fbSVasily Gorbik decompressor_handled_param(dfltcc);
488ee0d2fbSVasily Gorbik decompressor_handled_param(facilities);
498ee0d2fbSVasily Gorbik decompressor_handled_param(nokaslr);
50468a3bc2SHeiko Carstens decompressor_handled_param(cmma);
51*5ade5be4SSven Schnelle decompressor_handled_param(relocate_lowcore);
528ee0d2fbSVasily Gorbik #if IS_ENABLED(CONFIG_KVM)
538ee0d2fbSVasily Gorbik decompressor_handled_param(prot_virt);
548ee0d2fbSVasily Gorbik #endif
558ee0d2fbSVasily Gorbik 
kasan_early_init(void)56557b1970SVasily Gorbik static void __init kasan_early_init(void)
57557b1970SVasily Gorbik {
58557b1970SVasily Gorbik #ifdef CONFIG_KASAN
59557b1970SVasily Gorbik 	init_task.kasan_depth = 0;
60557b1970SVasily Gorbik 	sclp_early_printk("KernelAddressSanitizer initialized\n");
61557b1970SVasily Gorbik #endif
62557b1970SVasily Gorbik }
63557b1970SVasily Gorbik 
reset_tod_clock(void)642e83e0ebSVasily Gorbik static void __init reset_tod_clock(void)
652e83e0ebSVasily Gorbik {
66530f639fSHeiko Carstens 	union tod_clock clk;
672e83e0ebSVasily Gorbik 
68530f639fSHeiko Carstens 	if (store_tod_clock_ext_cc(&clk) == 0)
692e83e0ebSVasily Gorbik 		return;
702e83e0ebSVasily Gorbik 	/* TOD clock not running. Set the clock to Unix Epoch. */
71530f639fSHeiko Carstens 	if (set_tod_clock(TOD_UNIX_EPOCH) || store_tod_clock_ext_cc(&clk))
722e83e0ebSVasily Gorbik 		disabled_wait();
732e83e0ebSVasily Gorbik 
74f8d8977aSHeiko Carstens 	memset(&tod_clock_base, 0, sizeof(tod_clock_base));
75f8d8977aSHeiko Carstens 	tod_clock_base.tod = TOD_UNIX_EPOCH;
76208da1d5SSven Schnelle 	get_lowcore()->last_update_clock = TOD_UNIX_EPOCH;
772e83e0ebSVasily Gorbik }
782e83e0ebSVasily Gorbik 
79b6112ccbSMartin Schwidefsky /*
80ab14de6cSHeiko Carstens  * Initialize storage key for kernel pages
81ab14de6cSHeiko Carstens  */
init_kernel_storage_key(void)82ab14de6cSHeiko Carstens static noinline __init void init_kernel_storage_key(void)
83ab14de6cSHeiko Carstens {
84127c1fefSMartin Schwidefsky #if PAGE_DEFAULT_KEY
85ab14de6cSHeiko Carstens 	unsigned long end_pfn, init_pfn;
86ab14de6cSHeiko Carstens 
87320d9555SVasily Gorbik 	end_pfn = PFN_UP(__pa(_end));
88ab14de6cSHeiko Carstens 
89ab14de6cSHeiko Carstens 	for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++)
90e2b8d7afSMartin Schwidefsky 		page_set_storage_key(init_pfn << PAGE_SHIFT,
91e2b8d7afSMartin Schwidefsky 				     PAGE_DEFAULT_KEY, 0);
92127c1fefSMartin Schwidefsky #endif
93ab14de6cSHeiko Carstens }
94ab14de6cSHeiko Carstens 
95fade4dc4SHeiko Carstens static __initdata char sysinfo_page[PAGE_SIZE] __aligned(PAGE_SIZE);
9692e6ecf3SChristian Borntraeger 
detect_machine_type(void)97ab14de6cSHeiko Carstens static noinline __init void detect_machine_type(void)
98ab14de6cSHeiko Carstens {
99fade4dc4SHeiko Carstens 	struct sysinfo_3_2_2 *vmms = (struct sysinfo_3_2_2 *)&sysinfo_page;
100fade4dc4SHeiko Carstens 
10127d71602SMartin Schwidefsky 	/* Check current-configuration-level */
102caf757c6SHeiko Carstens 	if (stsi(NULL, 0, 0, 0) <= 2) {
103208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_LPAR;
10492e6ecf3SChristian Borntraeger 		return;
10527d71602SMartin Schwidefsky 	}
10627d71602SMartin Schwidefsky 	/* Get virtual-machine cpu information. */
107caf757c6SHeiko Carstens 	if (stsi(vmms, 3, 2, 2) || !vmms->count)
10892e6ecf3SChristian Borntraeger 		return;
109ab14de6cSHeiko Carstens 
11003aa047eSChristian Borntraeger 	/* Detect known hypervisors */
111fade4dc4SHeiko Carstens 	if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3))
112208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_KVM;
11303aa047eSChristian Borntraeger 	else if (!memcmp(vmms->vm[0].cpi, "\xa9\x61\xe5\xd4", 4))
114208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_VM;
115ab14de6cSHeiko Carstens }
116ab14de6cSHeiko Carstens 
117d2f03974SHeiko Carstens /* Remove leading, trailing and double whitespace. */
strim_all(char * str)118d2f03974SHeiko Carstens static inline void strim_all(char *str)
119d2f03974SHeiko Carstens {
120d2f03974SHeiko Carstens 	char *s;
121d2f03974SHeiko Carstens 
122d2f03974SHeiko Carstens 	s = strim(str);
123d2f03974SHeiko Carstens 	if (s != str)
124d2f03974SHeiko Carstens 		memmove(str, s, strlen(s));
125d2f03974SHeiko Carstens 	while (*str) {
126d2f03974SHeiko Carstens 		if (!isspace(*str++))
127d2f03974SHeiko Carstens 			continue;
128d2f03974SHeiko Carstens 		if (isspace(*str)) {
129d2f03974SHeiko Carstens 			s = skip_spaces(str);
130d2f03974SHeiko Carstens 			memmove(str, s, strlen(s) + 1);
131d2f03974SHeiko Carstens 		}
132d2f03974SHeiko Carstens 	}
133d2f03974SHeiko Carstens }
134d2f03974SHeiko Carstens 
setup_arch_string(void)1354b8fe77aSChristian Borntraeger static noinline __init void setup_arch_string(void)
1364b8fe77aSChristian Borntraeger {
1374b8fe77aSChristian Borntraeger 	struct sysinfo_1_1_1 *mach = (struct sysinfo_1_1_1 *)&sysinfo_page;
1382f8876f9SHeiko Carstens 	struct sysinfo_3_2_2 *vm = (struct sysinfo_3_2_2 *)&sysinfo_page;
1392f8876f9SHeiko Carstens 	char mstr[80], hvstr[17];
1404b8fe77aSChristian Borntraeger 
1414b8fe77aSChristian Borntraeger 	if (stsi(mach, 1, 1, 1))
1424b8fe77aSChristian Borntraeger 		return;
1434b8fe77aSChristian Borntraeger 	EBCASC(mach->manufacturer, sizeof(mach->manufacturer));
1444b8fe77aSChristian Borntraeger 	EBCASC(mach->type, sizeof(mach->type));
1454b8fe77aSChristian Borntraeger 	EBCASC(mach->model, sizeof(mach->model));
1464b8fe77aSChristian Borntraeger 	EBCASC(mach->model_capacity, sizeof(mach->model_capacity));
147d2f03974SHeiko Carstens 	sprintf(mstr, "%-16.16s %-4.4s %-16.16s %-16.16s",
148d2f03974SHeiko Carstens 		mach->manufacturer, mach->type,
149d2f03974SHeiko Carstens 		mach->model, mach->model_capacity);
150d2f03974SHeiko Carstens 	strim_all(mstr);
1512f8876f9SHeiko Carstens 	if (stsi(vm, 3, 2, 2) == 0 && vm->count) {
1522f8876f9SHeiko Carstens 		EBCASC(vm->vm[0].cpi, sizeof(vm->vm[0].cpi));
1532f8876f9SHeiko Carstens 		sprintf(hvstr, "%-16.16s", vm->vm[0].cpi);
1542f8876f9SHeiko Carstens 		strim_all(hvstr);
1552f8876f9SHeiko Carstens 	} else {
1562f8876f9SHeiko Carstens 		sprintf(hvstr, "%s",
1574b8fe77aSChristian Borntraeger 			MACHINE_IS_LPAR ? "LPAR" :
1584b8fe77aSChristian Borntraeger 			MACHINE_IS_VM ? "z/VM" :
1594b8fe77aSChristian Borntraeger 			MACHINE_IS_KVM ? "KVM" : "unknown");
1604b8fe77aSChristian Borntraeger 	}
1612f8876f9SHeiko Carstens 	dump_stack_set_arch_desc("%s (%s)", mstr, hvstr);
1622f8876f9SHeiko Carstens }
1634b8fe77aSChristian Borntraeger 
setup_topology(void)164fade4dc4SHeiko Carstens static __init void setup_topology(void)
165fade4dc4SHeiko Carstens {
166fade4dc4SHeiko Carstens 	int max_mnest;
167fade4dc4SHeiko Carstens 
168fade4dc4SHeiko Carstens 	if (!test_facility(11))
169fade4dc4SHeiko Carstens 		return;
170208da1d5SSven Schnelle 	get_lowcore()->machine_flags |= MACHINE_FLAG_TOPOLOGY;
171fade4dc4SHeiko Carstens 	for (max_mnest = 6; max_mnest > 1; max_mnest--) {
172caf757c6SHeiko Carstens 		if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0)
173fade4dc4SHeiko Carstens 			break;
174fade4dc4SHeiko Carstens 	}
175fade4dc4SHeiko Carstens 	topology_max_mnest = max_mnest;
176fade4dc4SHeiko Carstens }
177fade4dc4SHeiko Carstens 
__do_early_pgm_check(struct pt_regs * regs)17885806016SHeiko Carstens void __do_early_pgm_check(struct pt_regs *regs)
179ab14de6cSHeiko Carstens {
18046fee16fSHeiko Carstens 	if (!fixup_exception(regs))
18198587c2dSMartin Schwidefsky 		disabled_wait();
182ab14de6cSHeiko Carstens }
183ab14de6cSHeiko Carstens 
setup_lowcore_early(void)1845f954c34SHeiko Carstens static noinline __init void setup_lowcore_early(void)
185ab14de6cSHeiko Carstens {
186ab14de6cSHeiko Carstens 	psw_t psw;
187ab14de6cSHeiko Carstens 
18885806016SHeiko Carstens 	psw.addr = (unsigned long)early_pgm_check_handler;
189bb1520d5SAlexander Gordeev 	psw.mask = PSW_KERNEL_BITS;
190208da1d5SSven Schnelle 	get_lowcore()->program_new_psw = psw;
191208da1d5SSven Schnelle 	get_lowcore()->preempt_count = INIT_PREEMPT_COUNT;
192ab14de6cSHeiko Carstens }
193ab14de6cSHeiko Carstens 
detect_diag9c(void)1942e5061e4SHeiko Carstens static __init void detect_diag9c(void)
1952e5061e4SHeiko Carstens {
1962e5061e4SHeiko Carstens 	unsigned int cpu_address;
1972e5061e4SHeiko Carstens 	int rc;
1982e5061e4SHeiko Carstens 
1992e5061e4SHeiko Carstens 	cpu_address = stap();
2001ec2772eSMartin Schwidefsky 	diag_stat_inc(DIAG_STAT_X09C);
2012e5061e4SHeiko Carstens 	asm volatile(
2022e5061e4SHeiko Carstens 		"	diag	%2,0,0x9c\n"
2032e5061e4SHeiko Carstens 		"0:	la	%0,0\n"
2042e5061e4SHeiko Carstens 		"1:\n"
2052e5061e4SHeiko Carstens 		EX_TABLE(0b,1b)
2062e5061e4SHeiko Carstens 		: "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc");
2072e5061e4SHeiko Carstens 	if (!rc)
208208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_DIAG9C;
2092e5061e4SHeiko Carstens }
2102e5061e4SHeiko Carstens 
detect_machine_facilities(void)2112e5061e4SHeiko Carstens static __init void detect_machine_facilities(void)
2122e5061e4SHeiko Carstens {
2133c7ef08bSHeiko Carstens 	if (test_facility(8)) {
214208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_EDAT1;
21599441a38SHeiko Carstens 		system_ctl_set_bit(0, CR0_EDAT_BIT);
2163c7ef08bSHeiko Carstens 	}
21785e9d0e5SHeiko Carstens 	if (test_facility(78))
218208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_EDAT2;
21914375bc4SMartin Schwidefsky 	if (test_facility(3))
220208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_IDTE;
221a1c5befcSHeiko Carstens 	if (test_facility(50) && test_facility(73)) {
222208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_TE;
22399441a38SHeiko Carstens 		system_ctl_set_bit(0, CR0_TRANSACTIONAL_EXECUTION_BIT);
224a1c5befcSHeiko Carstens 	}
2251b948d6cSMartin Schwidefsky 	if (test_facility(51))
226208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_TLB_LC;
22718564756SHeiko Carstens 	if (test_facility(129))
22899441a38SHeiko Carstens 		system_ctl_set_bit(0, CR0_VECTOR_BIT);
229c0f1d478SHeiko Carstens 	if (test_facility(130))
230208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_NX;
231916cda1aSMartin Schwidefsky 	if (test_facility(133))
232208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_GS;
233f8d8977aSHeiko Carstens 	if (test_facility(139) && (tod_clock_base.tod >> 63)) {
2346e2ef5e4SMartin Schwidefsky 		/* Enabled signed clock comparator comparisons */
235208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_SCC;
2366e2ef5e4SMartin Schwidefsky 		clock_comparator_max = -1ULL >> 1;
23799441a38SHeiko Carstens 		system_ctl_set_bit(0, CR0_CLOCK_COMPARATOR_SIGN_BIT);
2386e2ef5e4SMartin Schwidefsky 	}
2393322ba0dSNiklas Schnelle 	if (IS_ENABLED(CONFIG_PCI) && test_facility(153)) {
240208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_PCI_MIO;
2413322ba0dSNiklas Schnelle 		/* the control bit is set during PCI initialization */
2423322ba0dSNiklas Schnelle 	}
2430807b856SGerald Schaefer 	if (test_facility(194))
244208da1d5SSven Schnelle 		get_lowcore()->machine_flags |= MACHINE_FLAG_RDP;
245b5510d9bSHendrik Brueckner }
246b5510d9bSHendrik Brueckner 
save_vector_registers(void)2471a36a39eSMartin Schwidefsky static inline void save_vector_registers(void)
2481a36a39eSMartin Schwidefsky {
2491a36a39eSMartin Schwidefsky #ifdef CONFIG_CRASH_DUMP
2501a36a39eSMartin Schwidefsky 	if (test_facility(129))
2511a36a39eSMartin Schwidefsky 		save_vx_regs(boot_cpu_vector_save_area);
2521a36a39eSMartin Schwidefsky #endif
2531a36a39eSMartin Schwidefsky }
2541a36a39eSMartin Schwidefsky 
setup_low_address_protection(void)2554a172528SHeiko Carstens static inline void setup_low_address_protection(void)
256c02ee6a1SVasily Gorbik {
25799441a38SHeiko Carstens 	system_ctl_set_bit(0, CR0_LOW_ADDRESS_PROTECTION_BIT);
258c02ee6a1SVasily Gorbik }
259c02ee6a1SVasily Gorbik 
setup_access_registers(void)260c2313594SVasily Gorbik static inline void setup_access_registers(void)
261c2313594SVasily Gorbik {
262c2313594SVasily Gorbik 	unsigned int acrs[NUM_ACRS] = { 0 };
263c2313594SVasily Gorbik 
264c2313594SVasily Gorbik 	restore_access_regs(acrs);
265c2313594SVasily Gorbik }
266c2313594SVasily Gorbik 
26749698745SVasily Gorbik char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
setup_boot_command_line(void)2681fb81057SMichael Holzheu static void __init setup_boot_command_line(void)
2691fb81057SMichael Holzheu {
270a0443fbbSHendrik Brueckner 	/* copy arch command line */
271820109fbSWolfram Sang 	strscpy(boot_command_line, early_command_line, COMMAND_LINE_SIZE);
272a0443fbbSHendrik Brueckner }
273a0443fbbSHendrik Brueckner 
sort_amode31_extable(void)274a156f09cSHeiko Carstens static void __init sort_amode31_extable(void)
275a156f09cSHeiko Carstens {
276a156f09cSHeiko Carstens 	sort_extable(__start_amode31_ex_table, __stop_amode31_ex_table);
277a156f09cSHeiko Carstens }
278a156f09cSHeiko Carstens 
startup_init(void)279ab14de6cSHeiko Carstens void __init startup_init(void)
280ab14de6cSHeiko Carstens {
281557b1970SVasily Gorbik 	kasan_early_init();
2822e83e0ebSVasily Gorbik 	reset_tod_clock();
283b1c0854dSMartin Schwidefsky 	time_early_init();
284ab14de6cSHeiko Carstens 	init_kernel_storage_key();
285ab14de6cSHeiko Carstens 	lockdep_off();
286a156f09cSHeiko Carstens 	sort_amode31_extable();
287ab14de6cSHeiko Carstens 	setup_lowcore_early();
288a0443fbbSHendrik Brueckner 	detect_machine_type();
2894b8fe77aSChristian Borntraeger 	setup_arch_string();
290a0443fbbSHendrik Brueckner 	setup_boot_command_line();
2912e5061e4SHeiko Carstens 	detect_diag9c();
2922e5061e4SHeiko Carstens 	detect_machine_facilities();
2931a36a39eSMartin Schwidefsky 	save_vector_registers();
294fade4dc4SHeiko Carstens 	setup_topology();
2957b50da53SMichael Holzheu 	sclp_early_detect();
2964a172528SHeiko Carstens 	setup_low_address_protection();
297c2313594SVasily Gorbik 	setup_access_registers();
298ab14de6cSHeiko Carstens 	lockdep_on();
299ab14de6cSHeiko Carstens }
300