xref: /openbmc/linux/arch/s390/boot/ipl_parm.c (revision 59793c5a)
149698745SVasily Gorbik // SPDX-License-Identifier: GPL-2.0
26d85dac2SVasily Gorbik #include <linux/kernel.h>
349698745SVasily Gorbik #include <linux/init.h>
449698745SVasily Gorbik #include <linux/ctype.h>
549698745SVasily Gorbik #include <asm/ebcdic.h>
649698745SVasily Gorbik #include <asm/sclp.h>
749698745SVasily Gorbik #include <asm/sections.h>
849698745SVasily Gorbik #include <asm/boot_data.h>
9b5e80459SVasily Gorbik #include <asm/facility.h>
1059793c5aSVasily Gorbik #include <asm/pgtable.h>
11db9492ceSVasily Gorbik #include <asm/uv.h>
1249698745SVasily Gorbik #include "boot.h"
1349698745SVasily Gorbik 
1449698745SVasily Gorbik char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
151e941d39SVasily Gorbik struct ipl_parameter_block __bootdata_preserved(ipl_block);
161e941d39SVasily Gorbik int __bootdata_preserved(ipl_block_valid);
1749698745SVasily Gorbik 
1859793c5aSVasily Gorbik unsigned long __bootdata(vmalloc_size) = VMALLOC_DEFAULT_SIZE;
1949698745SVasily Gorbik unsigned long __bootdata(memory_end);
2049698745SVasily Gorbik int __bootdata(memory_end_set);
21d58106c3SVasily Gorbik int __bootdata(noexec_disabled);
2249698745SVasily Gorbik 
23b2d24b97SGerald Schaefer int kaslr_enabled __section(.data);
24b2d24b97SGerald Schaefer 
2549698745SVasily Gorbik static inline int __diag308(unsigned long subcode, void *addr)
2649698745SVasily Gorbik {
2749698745SVasily Gorbik 	register unsigned long _addr asm("0") = (unsigned long)addr;
2849698745SVasily Gorbik 	register unsigned long _rc asm("1") = 0;
2949698745SVasily Gorbik 	unsigned long reg1, reg2;
3049698745SVasily Gorbik 	psw_t old = S390_lowcore.program_new_psw;
3149698745SVasily Gorbik 
3249698745SVasily Gorbik 	asm volatile(
3349698745SVasily Gorbik 		"	epsw	%0,%1\n"
3449698745SVasily Gorbik 		"	st	%0,%[psw_pgm]\n"
3549698745SVasily Gorbik 		"	st	%1,%[psw_pgm]+4\n"
3649698745SVasily Gorbik 		"	larl	%0,1f\n"
3749698745SVasily Gorbik 		"	stg	%0,%[psw_pgm]+8\n"
3849698745SVasily Gorbik 		"	diag	%[addr],%[subcode],0x308\n"
3949698745SVasily Gorbik 		"1:	nopr	%%r7\n"
4049698745SVasily Gorbik 		: "=&d" (reg1), "=&a" (reg2),
4149698745SVasily Gorbik 		  [psw_pgm] "=Q" (S390_lowcore.program_new_psw),
4249698745SVasily Gorbik 		  [addr] "+d" (_addr), "+d" (_rc)
4349698745SVasily Gorbik 		: [subcode] "d" (subcode)
4449698745SVasily Gorbik 		: "cc", "memory");
4549698745SVasily Gorbik 	S390_lowcore.program_new_psw = old;
4649698745SVasily Gorbik 	return _rc;
4749698745SVasily Gorbik }
4849698745SVasily Gorbik 
4949698745SVasily Gorbik void store_ipl_parmblock(void)
5049698745SVasily Gorbik {
5149698745SVasily Gorbik 	int rc;
5249698745SVasily Gorbik 
53db9492ceSVasily Gorbik 	uv_set_shared(__pa(&ipl_block));
541e941d39SVasily Gorbik 	rc = __diag308(DIAG308_STORE, &ipl_block);
55db9492ceSVasily Gorbik 	uv_remove_shared(__pa(&ipl_block));
5649698745SVasily Gorbik 	if (rc == DIAG308_RC_OK &&
571e941d39SVasily Gorbik 	    ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
581e941d39SVasily Gorbik 		ipl_block_valid = 1;
5949698745SVasily Gorbik }
6049698745SVasily Gorbik 
614ae98789SArnd Bergmann static size_t scpdata_length(const u8 *buf, size_t count)
6249698745SVasily Gorbik {
6349698745SVasily Gorbik 	while (count) {
6449698745SVasily Gorbik 		if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
6549698745SVasily Gorbik 			break;
6649698745SVasily Gorbik 		count--;
6749698745SVasily Gorbik 	}
6849698745SVasily Gorbik 	return count;
6949698745SVasily Gorbik }
7049698745SVasily Gorbik 
7149698745SVasily Gorbik static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
7249698745SVasily Gorbik 					  const struct ipl_parameter_block *ipb)
7349698745SVasily Gorbik {
7449698745SVasily Gorbik 	size_t count;
7549698745SVasily Gorbik 	size_t i;
7649698745SVasily Gorbik 	int has_lowercase;
7749698745SVasily Gorbik 
7886c74d86SMartin Schwidefsky 	count = min(size - 1, scpdata_length(ipb->fcp.scp_data,
7986c74d86SMartin Schwidefsky 					     ipb->fcp.scp_data_len));
8049698745SVasily Gorbik 	if (!count)
8149698745SVasily Gorbik 		goto out;
8249698745SVasily Gorbik 
8349698745SVasily Gorbik 	has_lowercase = 0;
8449698745SVasily Gorbik 	for (i = 0; i < count; i++) {
8586c74d86SMartin Schwidefsky 		if (!isascii(ipb->fcp.scp_data[i])) {
8649698745SVasily Gorbik 			count = 0;
8749698745SVasily Gorbik 			goto out;
8849698745SVasily Gorbik 		}
8986c74d86SMartin Schwidefsky 		if (!has_lowercase && islower(ipb->fcp.scp_data[i]))
9049698745SVasily Gorbik 			has_lowercase = 1;
9149698745SVasily Gorbik 	}
9249698745SVasily Gorbik 
9349698745SVasily Gorbik 	if (has_lowercase)
9486c74d86SMartin Schwidefsky 		memcpy(dest, ipb->fcp.scp_data, count);
9549698745SVasily Gorbik 	else
9649698745SVasily Gorbik 		for (i = 0; i < count; i++)
9786c74d86SMartin Schwidefsky 			dest[i] = tolower(ipb->fcp.scp_data[i]);
9849698745SVasily Gorbik out:
9949698745SVasily Gorbik 	dest[count] = '\0';
10049698745SVasily Gorbik 	return count;
10149698745SVasily Gorbik }
10249698745SVasily Gorbik 
10349698745SVasily Gorbik static void append_ipl_block_parm(void)
10449698745SVasily Gorbik {
10549698745SVasily Gorbik 	char *parm, *delim;
10649698745SVasily Gorbik 	size_t len, rc = 0;
10749698745SVasily Gorbik 
10849698745SVasily Gorbik 	len = strlen(early_command_line);
10949698745SVasily Gorbik 
11049698745SVasily Gorbik 	delim = early_command_line + len;    /* '\0' character position */
11149698745SVasily Gorbik 	parm = early_command_line + len + 1; /* append right after '\0' */
11249698745SVasily Gorbik 
1135f1207fbSMartin Schwidefsky 	switch (ipl_block.pb0_hdr.pbt) {
1145f1207fbSMartin Schwidefsky 	case IPL_PBT_CCW:
11549698745SVasily Gorbik 		rc = ipl_block_get_ascii_vmparm(
1161e941d39SVasily Gorbik 			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
11749698745SVasily Gorbik 		break;
1185f1207fbSMartin Schwidefsky 	case IPL_PBT_FCP:
11949698745SVasily Gorbik 		rc = ipl_block_get_ascii_scpdata(
1201e941d39SVasily Gorbik 			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
12149698745SVasily Gorbik 		break;
12249698745SVasily Gorbik 	}
12349698745SVasily Gorbik 	if (rc) {
12449698745SVasily Gorbik 		if (*parm == '=')
12549698745SVasily Gorbik 			memmove(early_command_line, parm + 1, rc);
12649698745SVasily Gorbik 		else
12749698745SVasily Gorbik 			*delim = ' '; /* replace '\0' with space */
12849698745SVasily Gorbik 	}
12949698745SVasily Gorbik }
13049698745SVasily Gorbik 
13149698745SVasily Gorbik static inline int has_ebcdic_char(const char *str)
13249698745SVasily Gorbik {
13349698745SVasily Gorbik 	int i;
13449698745SVasily Gorbik 
13549698745SVasily Gorbik 	for (i = 0; str[i]; i++)
13649698745SVasily Gorbik 		if (str[i] & 0x80)
13749698745SVasily Gorbik 			return 1;
13849698745SVasily Gorbik 	return 0;
13949698745SVasily Gorbik }
14049698745SVasily Gorbik 
14149698745SVasily Gorbik void setup_boot_command_line(void)
14249698745SVasily Gorbik {
14349698745SVasily Gorbik 	COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0;
14449698745SVasily Gorbik 	/* convert arch command line to ascii if necessary */
14549698745SVasily Gorbik 	if (has_ebcdic_char(COMMAND_LINE))
14649698745SVasily Gorbik 		EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
14749698745SVasily Gorbik 	/* copy arch command line */
14849698745SVasily Gorbik 	strcpy(early_command_line, strim(COMMAND_LINE));
14949698745SVasily Gorbik 
15049698745SVasily Gorbik 	/* append IPL PARM data to the boot command line */
151093ddccbSVasily Gorbik 	if (!is_prot_virt_guest() && ipl_block_valid)
15249698745SVasily Gorbik 		append_ipl_block_parm();
15349698745SVasily Gorbik }
15449698745SVasily Gorbik 
155b5e80459SVasily Gorbik static void modify_facility(unsigned long nr, bool clear)
156b5e80459SVasily Gorbik {
157b5e80459SVasily Gorbik 	if (clear)
158b5e80459SVasily Gorbik 		__clear_facility(nr, S390_lowcore.stfle_fac_list);
159b5e80459SVasily Gorbik 	else
160b5e80459SVasily Gorbik 		__set_facility(nr, S390_lowcore.stfle_fac_list);
161b5e80459SVasily Gorbik }
162b5e80459SVasily Gorbik 
1636d85dac2SVasily Gorbik static void check_cleared_facilities(void)
1646d85dac2SVasily Gorbik {
1656d85dac2SVasily Gorbik 	unsigned long als[] = { FACILITIES_ALS };
1666d85dac2SVasily Gorbik 	int i;
1676d85dac2SVasily Gorbik 
1686d85dac2SVasily Gorbik 	for (i = 0; i < ARRAY_SIZE(als); i++) {
1696d85dac2SVasily Gorbik 		if ((S390_lowcore.stfle_fac_list[i] & als[i]) != als[i]) {
1706d85dac2SVasily Gorbik 			sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n");
1716d85dac2SVasily Gorbik 			print_missing_facilities();
1726d85dac2SVasily Gorbik 			break;
1736d85dac2SVasily Gorbik 		}
1746d85dac2SVasily Gorbik 	}
1756d85dac2SVasily Gorbik }
1766d85dac2SVasily Gorbik 
177b5e80459SVasily Gorbik static void modify_fac_list(char *str)
178b5e80459SVasily Gorbik {
179b5e80459SVasily Gorbik 	unsigned long val, endval;
180b5e80459SVasily Gorbik 	char *endp;
181b5e80459SVasily Gorbik 	bool clear;
182b5e80459SVasily Gorbik 
183b5e80459SVasily Gorbik 	while (*str) {
184b5e80459SVasily Gorbik 		clear = false;
185b5e80459SVasily Gorbik 		if (*str == '!') {
186b5e80459SVasily Gorbik 			clear = true;
187b5e80459SVasily Gorbik 			str++;
188b5e80459SVasily Gorbik 		}
189b5e80459SVasily Gorbik 		val = simple_strtoull(str, &endp, 0);
190b5e80459SVasily Gorbik 		if (str == endp)
191b5e80459SVasily Gorbik 			break;
192b5e80459SVasily Gorbik 		str = endp;
193b5e80459SVasily Gorbik 		if (*str == '-') {
194b5e80459SVasily Gorbik 			str++;
195b5e80459SVasily Gorbik 			endval = simple_strtoull(str, &endp, 0);
196b5e80459SVasily Gorbik 			if (str == endp)
197b5e80459SVasily Gorbik 				break;
198b5e80459SVasily Gorbik 			str = endp;
199b5e80459SVasily Gorbik 			while (val <= endval) {
200b5e80459SVasily Gorbik 				modify_facility(val, clear);
201b5e80459SVasily Gorbik 				val++;
202b5e80459SVasily Gorbik 			}
203b5e80459SVasily Gorbik 		} else {
204b5e80459SVasily Gorbik 			modify_facility(val, clear);
205b5e80459SVasily Gorbik 		}
206b5e80459SVasily Gorbik 		if (*str != ',')
207b5e80459SVasily Gorbik 			break;
208b5e80459SVasily Gorbik 		str++;
209b5e80459SVasily Gorbik 	}
2106d85dac2SVasily Gorbik 	check_cleared_facilities();
211b5e80459SVasily Gorbik }
212b5e80459SVasily Gorbik 
21349698745SVasily Gorbik static char command_line_buf[COMMAND_LINE_SIZE] __section(.data);
214b5e80459SVasily Gorbik void parse_boot_command_line(void)
21549698745SVasily Gorbik {
21649698745SVasily Gorbik 	char *param, *val;
217d58106c3SVasily Gorbik 	bool enabled;
218d58106c3SVasily Gorbik 	char *args;
219d58106c3SVasily Gorbik 	int rc;
22049698745SVasily Gorbik 
221b2d24b97SGerald Schaefer 	kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE);
22249698745SVasily Gorbik 	args = strcpy(command_line_buf, early_command_line);
22349698745SVasily Gorbik 	while (*args) {
22449698745SVasily Gorbik 		args = next_arg(args, &param, &val);
22549698745SVasily Gorbik 
22649698745SVasily Gorbik 		if (!strcmp(param, "mem")) {
22749698745SVasily Gorbik 			memory_end = memparse(val, NULL);
22849698745SVasily Gorbik 			memory_end_set = 1;
22949698745SVasily Gorbik 		}
230d58106c3SVasily Gorbik 
23159793c5aSVasily Gorbik 		if (!strcmp(param, "vmalloc"))
23259793c5aSVasily Gorbik 			vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE);
23359793c5aSVasily Gorbik 
234d58106c3SVasily Gorbik 		if (!strcmp(param, "noexec")) {
235d58106c3SVasily Gorbik 			rc = kstrtobool(val, &enabled);
236d58106c3SVasily Gorbik 			if (!rc && !enabled)
237d58106c3SVasily Gorbik 				noexec_disabled = 1;
238d58106c3SVasily Gorbik 		}
239b5e80459SVasily Gorbik 
240b5e80459SVasily Gorbik 		if (!strcmp(param, "facilities"))
241b5e80459SVasily Gorbik 			modify_fac_list(val);
242b2d24b97SGerald Schaefer 
243b2d24b97SGerald Schaefer 		if (!strcmp(param, "nokaslr"))
244b2d24b97SGerald Schaefer 			kaslr_enabled = 0;
24549698745SVasily Gorbik 	}
24649698745SVasily Gorbik }
24749698745SVasily Gorbik 
24849698745SVasily Gorbik void setup_memory_end(void)
24949698745SVasily Gorbik {
25049698745SVasily Gorbik #ifdef CONFIG_CRASH_DUMP
251b2d24b97SGerald Schaefer 	if (OLDMEM_BASE) {
252b2d24b97SGerald Schaefer 		kaslr_enabled = 0;
253b2d24b97SGerald Schaefer 	} else if (ipl_block_valid &&
2545f1207fbSMartin Schwidefsky 		   ipl_block.pb0_hdr.pbt == IPL_PBT_FCP &&
2555f1207fbSMartin Schwidefsky 		   ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) {
256b2d24b97SGerald Schaefer 		kaslr_enabled = 0;
25749698745SVasily Gorbik 		if (!sclp_early_get_hsa_size(&memory_end) && memory_end)
25849698745SVasily Gorbik 			memory_end_set = 1;
25949698745SVasily Gorbik 	}
26049698745SVasily Gorbik #endif
26149698745SVasily Gorbik }
262