xref: /linux/arch/s390/kernel/early.c (revision 17e89e1340a377b2f14a14d7050f609328592793)
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  *		 Heiko Carstens <heiko.carstens@de.ibm.com>
6ab14de6cSHeiko Carstens  */
7ab14de6cSHeiko Carstens 
811af97e1SHendrik Brueckner #define KMSG_COMPONENT "setup"
911af97e1SHendrik Brueckner #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1011af97e1SHendrik Brueckner 
1192e6ecf3SChristian Borntraeger #include <linux/compiler.h>
12ab14de6cSHeiko Carstens #include <linux/init.h>
13ab14de6cSHeiko Carstens #include <linux/errno.h>
14ab14de6cSHeiko Carstens #include <linux/string.h>
15ab14de6cSHeiko Carstens #include <linux/ctype.h>
16ab14de6cSHeiko Carstens #include <linux/lockdep.h>
17dcc096c5SPaul Gortmaker #include <linux/extable.h>
18ab14de6cSHeiko Carstens #include <linux/pfn.h>
19ab14de6cSHeiko Carstens #include <linux/uaccess.h>
2011af97e1SHendrik Brueckner #include <linux/kernel.h>
211ec2772eSMartin Schwidefsky #include <asm/diag.h>
22a0443fbbSHendrik Brueckner #include <asm/ebcdic.h>
2346b05d26SMichael Holzheu #include <asm/ipl.h>
24ab14de6cSHeiko Carstens #include <asm/lowcore.h>
25ab14de6cSHeiko Carstens #include <asm/processor.h>
26ab14de6cSHeiko Carstens #include <asm/sections.h>
27ab14de6cSHeiko Carstens #include <asm/setup.h>
2892e6ecf3SChristian Borntraeger #include <asm/sysinfo.h>
29ab14de6cSHeiko Carstens #include <asm/cpcmd.h>
30ab14de6cSHeiko Carstens #include <asm/sclp.h>
31a0616cdeSDavid Howells #include <asm/facility.h>
3249698745SVasily Gorbik #include <asm/boot_data.h>
33c2313594SVasily Gorbik #include <asm/switch_to.h>
34a806170eSHeiko Carstens #include "entry.h"
35ab14de6cSHeiko Carstens 
362e83e0ebSVasily Gorbik static void __init reset_tod_clock(void)
372e83e0ebSVasily Gorbik {
38530f639fSHeiko Carstens 	union tod_clock clk;
392e83e0ebSVasily Gorbik 
40530f639fSHeiko Carstens 	if (store_tod_clock_ext_cc(&clk) == 0)
412e83e0ebSVasily Gorbik 		return;
422e83e0ebSVasily Gorbik 	/* TOD clock not running. Set the clock to Unix Epoch. */
43530f639fSHeiko Carstens 	if (set_tod_clock(TOD_UNIX_EPOCH) || store_tod_clock_ext_cc(&clk))
442e83e0ebSVasily Gorbik 		disabled_wait();
452e83e0ebSVasily Gorbik 
46f8d8977aSHeiko Carstens 	memset(&tod_clock_base, 0, sizeof(tod_clock_base));
47f8d8977aSHeiko Carstens 	tod_clock_base.tod = TOD_UNIX_EPOCH;
482e83e0ebSVasily Gorbik 	S390_lowcore.last_update_clock = TOD_UNIX_EPOCH;
492e83e0ebSVasily Gorbik }
502e83e0ebSVasily Gorbik 
51b6112ccbSMartin Schwidefsky /*
52ab14de6cSHeiko Carstens  * Initialize storage key for kernel pages
53ab14de6cSHeiko Carstens  */
54ab14de6cSHeiko Carstens static noinline __init void init_kernel_storage_key(void)
55ab14de6cSHeiko Carstens {
56127c1fefSMartin Schwidefsky #if PAGE_DEFAULT_KEY
57ab14de6cSHeiko Carstens 	unsigned long end_pfn, init_pfn;
58ab14de6cSHeiko Carstens 
59320d9555SVasily Gorbik 	end_pfn = PFN_UP(__pa(_end));
60ab14de6cSHeiko Carstens 
61ab14de6cSHeiko Carstens 	for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++)
62e2b8d7afSMartin Schwidefsky 		page_set_storage_key(init_pfn << PAGE_SHIFT,
63e2b8d7afSMartin Schwidefsky 				     PAGE_DEFAULT_KEY, 0);
64127c1fefSMartin Schwidefsky #endif
65ab14de6cSHeiko Carstens }
66ab14de6cSHeiko Carstens 
67fade4dc4SHeiko Carstens static __initdata char sysinfo_page[PAGE_SIZE] __aligned(PAGE_SIZE);
6892e6ecf3SChristian Borntraeger 
69ab14de6cSHeiko Carstens static noinline __init void detect_machine_type(void)
70ab14de6cSHeiko Carstens {
71fade4dc4SHeiko Carstens 	struct sysinfo_3_2_2 *vmms = (struct sysinfo_3_2_2 *)&sysinfo_page;
72fade4dc4SHeiko Carstens 
7327d71602SMartin Schwidefsky 	/* Check current-configuration-level */
74caf757c6SHeiko Carstens 	if (stsi(NULL, 0, 0, 0) <= 2) {
7527d71602SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR;
7692e6ecf3SChristian Borntraeger 		return;
7727d71602SMartin Schwidefsky 	}
7827d71602SMartin Schwidefsky 	/* Get virtual-machine cpu information. */
79caf757c6SHeiko Carstens 	if (stsi(vmms, 3, 2, 2) || !vmms->count)
8092e6ecf3SChristian Borntraeger 		return;
81ab14de6cSHeiko Carstens 
8203aa047eSChristian Borntraeger 	/* Detect known hypervisors */
83fade4dc4SHeiko Carstens 	if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3))
84d3135e0cSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_KVM;
8503aa047eSChristian Borntraeger 	else if (!memcmp(vmms->vm[0].cpi, "\xa9\x61\xe5\xd4", 4))
86d3135e0cSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
87ab14de6cSHeiko Carstens }
88ab14de6cSHeiko Carstens 
89d2f03974SHeiko Carstens /* Remove leading, trailing and double whitespace. */
90d2f03974SHeiko Carstens static inline void strim_all(char *str)
91d2f03974SHeiko Carstens {
92d2f03974SHeiko Carstens 	char *s;
93d2f03974SHeiko Carstens 
94d2f03974SHeiko Carstens 	s = strim(str);
95d2f03974SHeiko Carstens 	if (s != str)
96d2f03974SHeiko Carstens 		memmove(str, s, strlen(s));
97d2f03974SHeiko Carstens 	while (*str) {
98d2f03974SHeiko Carstens 		if (!isspace(*str++))
99d2f03974SHeiko Carstens 			continue;
100d2f03974SHeiko Carstens 		if (isspace(*str)) {
101d2f03974SHeiko Carstens 			s = skip_spaces(str);
102d2f03974SHeiko Carstens 			memmove(str, s, strlen(s) + 1);
103d2f03974SHeiko Carstens 		}
104d2f03974SHeiko Carstens 	}
105d2f03974SHeiko Carstens }
106d2f03974SHeiko Carstens 
1074b8fe77aSChristian Borntraeger static noinline __init void setup_arch_string(void)
1084b8fe77aSChristian Borntraeger {
1094b8fe77aSChristian Borntraeger 	struct sysinfo_1_1_1 *mach = (struct sysinfo_1_1_1 *)&sysinfo_page;
1102f8876f9SHeiko Carstens 	struct sysinfo_3_2_2 *vm = (struct sysinfo_3_2_2 *)&sysinfo_page;
1112f8876f9SHeiko Carstens 	char mstr[80], hvstr[17];
1124b8fe77aSChristian Borntraeger 
1134b8fe77aSChristian Borntraeger 	if (stsi(mach, 1, 1, 1))
1144b8fe77aSChristian Borntraeger 		return;
1154b8fe77aSChristian Borntraeger 	EBCASC(mach->manufacturer, sizeof(mach->manufacturer));
1164b8fe77aSChristian Borntraeger 	EBCASC(mach->type, sizeof(mach->type));
1174b8fe77aSChristian Borntraeger 	EBCASC(mach->model, sizeof(mach->model));
1184b8fe77aSChristian Borntraeger 	EBCASC(mach->model_capacity, sizeof(mach->model_capacity));
119d2f03974SHeiko Carstens 	sprintf(mstr, "%-16.16s %-4.4s %-16.16s %-16.16s",
120d2f03974SHeiko Carstens 		mach->manufacturer, mach->type,
121d2f03974SHeiko Carstens 		mach->model, mach->model_capacity);
122d2f03974SHeiko Carstens 	strim_all(mstr);
1232f8876f9SHeiko Carstens 	if (stsi(vm, 3, 2, 2) == 0 && vm->count) {
1242f8876f9SHeiko Carstens 		EBCASC(vm->vm[0].cpi, sizeof(vm->vm[0].cpi));
1252f8876f9SHeiko Carstens 		sprintf(hvstr, "%-16.16s", vm->vm[0].cpi);
1262f8876f9SHeiko Carstens 		strim_all(hvstr);
1272f8876f9SHeiko Carstens 	} else {
1282f8876f9SHeiko Carstens 		sprintf(hvstr, "%s",
1294b8fe77aSChristian Borntraeger 			MACHINE_IS_LPAR ? "LPAR" :
1304b8fe77aSChristian Borntraeger 			MACHINE_IS_VM ? "z/VM" :
1314b8fe77aSChristian Borntraeger 			MACHINE_IS_KVM ? "KVM" : "unknown");
1324b8fe77aSChristian Borntraeger 	}
1332f8876f9SHeiko Carstens 	dump_stack_set_arch_desc("%s (%s)", mstr, hvstr);
1342f8876f9SHeiko Carstens }
1354b8fe77aSChristian Borntraeger 
136fade4dc4SHeiko Carstens static __init void setup_topology(void)
137fade4dc4SHeiko Carstens {
138fade4dc4SHeiko Carstens 	int max_mnest;
139fade4dc4SHeiko Carstens 
140fade4dc4SHeiko Carstens 	if (!test_facility(11))
141fade4dc4SHeiko Carstens 		return;
142fade4dc4SHeiko Carstens 	S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;
143fade4dc4SHeiko Carstens 	for (max_mnest = 6; max_mnest > 1; max_mnest--) {
144caf757c6SHeiko Carstens 		if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0)
145fade4dc4SHeiko Carstens 			break;
146fade4dc4SHeiko Carstens 	}
147fade4dc4SHeiko Carstens 	topology_max_mnest = max_mnest;
148fade4dc4SHeiko Carstens }
149fade4dc4SHeiko Carstens 
150a3415703SMichael Holzheu static void early_pgm_check_handler(void)
151ab14de6cSHeiko Carstens {
152ab14de6cSHeiko Carstens 	const struct exception_table_entry *fixup;
15350be6345SPhilipp Hachtmann 	unsigned long cr0, cr0_new;
154eb608fb3SHeiko Carstens 	unsigned long addr;
155ab14de6cSHeiko Carstens 
156ab14de6cSHeiko Carstens 	addr = S390_lowcore.program_old_psw.addr;
157a80313ffSGerald Schaefer 	fixup = s390_search_extables(addr);
158ab14de6cSHeiko Carstens 	if (!fixup)
15998587c2dSMartin Schwidefsky 		disabled_wait();
16050be6345SPhilipp Hachtmann 	/* Disable low address protection before storing into lowcore. */
16150be6345SPhilipp Hachtmann 	__ctl_store(cr0, 0, 0);
16250be6345SPhilipp Hachtmann 	cr0_new = cr0 & ~(1UL << 28);
16350be6345SPhilipp Hachtmann 	__ctl_load(cr0_new, 0, 0);
164fecc868aSHeiko Carstens 	S390_lowcore.program_old_psw.addr = extable_fixup(fixup);
16550be6345SPhilipp Hachtmann 	__ctl_load(cr0, 0, 0);
166ab14de6cSHeiko Carstens }
167ab14de6cSHeiko Carstens 
1685f954c34SHeiko Carstens static noinline __init void setup_lowcore_early(void)
169ab14de6cSHeiko Carstens {
170ab14de6cSHeiko Carstens 	psw_t psw;
171ab14de6cSHeiko Carstens 
172f38b0a74SVasily Gorbik 	psw.addr = (unsigned long)s390_base_pgm_handler;
173b50511e4SMartin Schwidefsky 	psw.mask = PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA;
174998f5bbeSVasily Gorbik 	if (IS_ENABLED(CONFIG_KASAN))
175998f5bbeSVasily Gorbik 		psw.mask |= PSW_MASK_DAT;
176ab14de6cSHeiko Carstens 	S390_lowcore.program_new_psw = psw;
177ab14de6cSHeiko Carstens 	s390_base_pgm_handler_fn = early_pgm_check_handler;
178c360192bSMartin Schwidefsky 	S390_lowcore.preempt_count = INIT_PREEMPT_COUNT;
179ab14de6cSHeiko Carstens }
180ab14de6cSHeiko Carstens 
18114375bc4SMartin Schwidefsky static noinline __init void setup_facility_list(void)
18214375bc4SMartin Schwidefsky {
183*17e89e13SSven Schnelle 	memcpy(alt_stfle_fac_list, stfle_fac_list, sizeof(alt_stfle_fac_list));
184d768bd89SMartin Schwidefsky 	if (!IS_ENABLED(CONFIG_KERNEL_NOBP))
185*17e89e13SSven Schnelle 		__clear_facility(82, alt_stfle_fac_list);
18614375bc4SMartin Schwidefsky }
18714375bc4SMartin Schwidefsky 
1882e5061e4SHeiko Carstens static __init void detect_diag9c(void)
1892e5061e4SHeiko Carstens {
1902e5061e4SHeiko Carstens 	unsigned int cpu_address;
1912e5061e4SHeiko Carstens 	int rc;
1922e5061e4SHeiko Carstens 
1932e5061e4SHeiko Carstens 	cpu_address = stap();
1941ec2772eSMartin Schwidefsky 	diag_stat_inc(DIAG_STAT_X09C);
1952e5061e4SHeiko Carstens 	asm volatile(
1962e5061e4SHeiko Carstens 		"	diag	%2,0,0x9c\n"
1972e5061e4SHeiko Carstens 		"0:	la	%0,0\n"
1982e5061e4SHeiko Carstens 		"1:\n"
1992e5061e4SHeiko Carstens 		EX_TABLE(0b,1b)
2002e5061e4SHeiko Carstens 		: "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc");
2012e5061e4SHeiko Carstens 	if (!rc)
202d3135e0cSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG9C;
2032e5061e4SHeiko Carstens }
2042e5061e4SHeiko Carstens 
2052e5061e4SHeiko Carstens static __init void detect_machine_facilities(void)
2062e5061e4SHeiko Carstens {
2073c7ef08bSHeiko Carstens 	if (test_facility(8)) {
2083c7ef08bSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT1;
2093c7ef08bSHeiko Carstens 		__ctl_set_bit(0, 23);
2103c7ef08bSHeiko Carstens 	}
21185e9d0e5SHeiko Carstens 	if (test_facility(78))
21285e9d0e5SHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT2;
21314375bc4SMartin Schwidefsky 	if (test_facility(3))
214d3135e0cSHeiko Carstens 		S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
215a1c5befcSHeiko Carstens 	if (test_facility(50) && test_facility(73)) {
216d35339a4SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_TE;
217a1c5befcSHeiko Carstens 		__ctl_set_bit(0, 55);
218a1c5befcSHeiko Carstens 	}
2191b948d6cSMartin Schwidefsky 	if (test_facility(51))
2201b948d6cSMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC;
221b5510d9bSHendrik Brueckner 	if (test_facility(129)) {
22280703617SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_VX;
223b5510d9bSHendrik Brueckner 		__ctl_set_bit(0, 17);
2242e5061e4SHeiko Carstens 	}
225d3baaeb5SVasily Gorbik 	if (test_facility(130) && !noexec_disabled) {
22657d7f939SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_NX;
22757d7f939SMartin Schwidefsky 		__ctl_set_bit(0, 20);
22857d7f939SMartin Schwidefsky 	}
229916cda1aSMartin Schwidefsky 	if (test_facility(133))
230916cda1aSMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_GS;
231f8d8977aSHeiko Carstens 	if (test_facility(139) && (tod_clock_base.tod >> 63)) {
2326e2ef5e4SMartin Schwidefsky 		/* Enabled signed clock comparator comparisons */
2336e2ef5e4SMartin Schwidefsky 		S390_lowcore.machine_flags |= MACHINE_FLAG_SCC;
2346e2ef5e4SMartin Schwidefsky 		clock_comparator_max = -1ULL >> 1;
2356e2ef5e4SMartin Schwidefsky 		__ctl_set_bit(0, 53);
2366e2ef5e4SMartin Schwidefsky 	}
237b5510d9bSHendrik Brueckner }
238b5510d9bSHendrik Brueckner 
2391a36a39eSMartin Schwidefsky static inline void save_vector_registers(void)
2401a36a39eSMartin Schwidefsky {
2411a36a39eSMartin Schwidefsky #ifdef CONFIG_CRASH_DUMP
2421a36a39eSMartin Schwidefsky 	if (test_facility(129))
2431a36a39eSMartin Schwidefsky 		save_vx_regs(boot_cpu_vector_save_area);
2441a36a39eSMartin Schwidefsky #endif
2451a36a39eSMartin Schwidefsky }
2461a36a39eSMartin Schwidefsky 
247c02ee6a1SVasily Gorbik static inline void setup_control_registers(void)
248c02ee6a1SVasily Gorbik {
249c02ee6a1SVasily Gorbik 	unsigned long reg;
250c02ee6a1SVasily Gorbik 
251c02ee6a1SVasily Gorbik 	__ctl_store(reg, 0, 0);
252c02ee6a1SVasily Gorbik 	reg |= CR0_LOW_ADDRESS_PROTECTION;
253c02ee6a1SVasily Gorbik 	reg |= CR0_EMERGENCY_SIGNAL_SUBMASK;
254c02ee6a1SVasily Gorbik 	reg |= CR0_EXTERNAL_CALL_SUBMASK;
255c02ee6a1SVasily Gorbik 	__ctl_load(reg, 0, 0);
256c02ee6a1SVasily Gorbik }
257c02ee6a1SVasily Gorbik 
258c2313594SVasily Gorbik static inline void setup_access_registers(void)
259c2313594SVasily Gorbik {
260c2313594SVasily Gorbik 	unsigned int acrs[NUM_ACRS] = { 0 };
261c2313594SVasily Gorbik 
262c2313594SVasily Gorbik 	restore_access_regs(acrs);
263c2313594SVasily Gorbik }
264c2313594SVasily Gorbik 
265b5510d9bSHendrik Brueckner static int __init disable_vector_extension(char *str)
266b5510d9bSHendrik Brueckner {
267b5510d9bSHendrik Brueckner 	S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
268b5510d9bSHendrik Brueckner 	__ctl_clear_bit(0, 17);
269673cfddfSHeiko Carstens 	return 0;
270b5510d9bSHendrik Brueckner }
271b5510d9bSHendrik Brueckner early_param("novx", disable_vector_extension);
2722e5061e4SHeiko Carstens 
27349698745SVasily Gorbik char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
2741fb81057SMichael Holzheu static void __init setup_boot_command_line(void)
2751fb81057SMichael Holzheu {
276a0443fbbSHendrik Brueckner 	/* copy arch command line */
27749698745SVasily Gorbik 	strlcpy(boot_command_line, early_command_line, ARCH_COMMAND_LINE_SIZE);
278a0443fbbSHendrik Brueckner }
279a0443fbbSHendrik Brueckner 
280627c9b62SVasily Gorbik static void __init check_image_bootable(void)
281627c9b62SVasily Gorbik {
282627c9b62SVasily Gorbik 	if (!memcmp(EP_STRING, (void *)EP_OFFSET, strlen(EP_STRING)))
283627c9b62SVasily Gorbik 		return;
284627c9b62SVasily Gorbik 
285627c9b62SVasily Gorbik 	sclp_early_printk("Linux kernel boot failure: An attempt to boot a vmlinux ELF image failed.\n");
286627c9b62SVasily Gorbik 	sclp_early_printk("This image does not contain all parts necessary for starting up. Use\n");
287627c9b62SVasily Gorbik 	sclp_early_printk("bzImage or arch/s390/boot/compressed/vmlinux instead.\n");
28898587c2dSMartin Schwidefsky 	disabled_wait();
289627c9b62SVasily Gorbik }
290627c9b62SVasily Gorbik 
291ab14de6cSHeiko Carstens void __init startup_init(void)
292ab14de6cSHeiko Carstens {
2932e83e0ebSVasily Gorbik 	reset_tod_clock();
294627c9b62SVasily Gorbik 	check_image_bootable();
295b1c0854dSMartin Schwidefsky 	time_early_init();
296ab14de6cSHeiko Carstens 	init_kernel_storage_key();
297ab14de6cSHeiko Carstens 	lockdep_off();
298ab14de6cSHeiko Carstens 	setup_lowcore_early();
29914375bc4SMartin Schwidefsky 	setup_facility_list();
300a0443fbbSHendrik Brueckner 	detect_machine_type();
3014b8fe77aSChristian Borntraeger 	setup_arch_string();
302a0443fbbSHendrik Brueckner 	setup_boot_command_line();
3032e5061e4SHeiko Carstens 	detect_diag9c();
3042e5061e4SHeiko Carstens 	detect_machine_facilities();
3051a36a39eSMartin Schwidefsky 	save_vector_registers();
306fade4dc4SHeiko Carstens 	setup_topology();
3077b50da53SMichael Holzheu 	sclp_early_detect();
308c02ee6a1SVasily Gorbik 	setup_control_registers();
309c2313594SVasily Gorbik 	setup_access_registers();
310ab14de6cSHeiko Carstens 	lockdep_on();
311ab14de6cSHeiko Carstens }
312