xref: /openbmc/linux/arch/s390/boot/ipl_parm.c (revision 05a8ba5c)
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>
565fddcfcSMike Rapoport #include <linux/pgtable.h>
649698745SVasily Gorbik #include <asm/ebcdic.h>
749698745SVasily Gorbik #include <asm/sclp.h>
849698745SVasily Gorbik #include <asm/sections.h>
949698745SVasily Gorbik #include <asm/boot_data.h>
10b5e80459SVasily Gorbik #include <asm/facility.h>
11f84d88edSHeiko Carstens #include <asm/setup.h>
12db9492ceSVasily Gorbik #include <asm/uv.h>
1349698745SVasily Gorbik #include "boot.h"
1449698745SVasily Gorbik 
15f84d88edSHeiko Carstens struct parmarea parmarea __section(".parmarea") = {
16f84d88edSHeiko Carstens 	.kernel_version		= (unsigned long)kernel_version,
17f84d88edSHeiko Carstens 	.max_command_line_size	= COMMAND_LINE_SIZE,
18f84d88edSHeiko Carstens 	.command_line		= "root=/dev/ram0 ro",
19f84d88edSHeiko Carstens };
20f84d88edSHeiko Carstens 
2149698745SVasily Gorbik char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
2249698745SVasily Gorbik 
230c4f2623SVasily Gorbik unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL;
240c4f2623SVasily Gorbik struct ipl_parameter_block __bootdata_preserved(ipl_block);
250c4f2623SVasily Gorbik int __bootdata_preserved(ipl_block_valid);
26bb87190cSHeiko Carstens int __bootdata_preserved(__kaslr_enabled);
270c4f2623SVasily Gorbik 
280c4f2623SVasily Gorbik unsigned long vmalloc_size = VMALLOC_DEFAULT_SIZE;
2973045a08SVasily Gorbik unsigned long memory_limit;
3090178c19SHeiko Carstens int vmalloc_size_set;
31b2d24b97SGerald Schaefer 
__diag308(unsigned long subcode,void * addr)3249698745SVasily Gorbik static inline int __diag308(unsigned long subcode, void *addr)
3349698745SVasily Gorbik {
3449698745SVasily Gorbik 	unsigned long reg1, reg2;
359e250926SHeiko Carstens 	union register_pair r1;
3688c2510cSHeiko Carstens 	psw_t old;
3749698745SVasily Gorbik 
389e250926SHeiko Carstens 	r1.even = (unsigned long) addr;
399e250926SHeiko Carstens 	r1.odd	= 0;
4049698745SVasily Gorbik 	asm volatile(
4188c2510cSHeiko Carstens 		"	mvc	0(16,%[psw_old]),0(%[psw_pgm])\n"
429e250926SHeiko Carstens 		"	epsw	%[reg1],%[reg2]\n"
439e250926SHeiko Carstens 		"	st	%[reg1],0(%[psw_pgm])\n"
449e250926SHeiko Carstens 		"	st	%[reg2],4(%[psw_pgm])\n"
459e250926SHeiko Carstens 		"	larl	%[reg1],1f\n"
469e250926SHeiko Carstens 		"	stg	%[reg1],8(%[psw_pgm])\n"
479e250926SHeiko Carstens 		"	diag	%[r1],%[subcode],0x308\n"
4888c2510cSHeiko Carstens 		"1:	mvc	0(16,%[psw_pgm]),0(%[psw_old])\n"
499e250926SHeiko Carstens 		: [r1] "+&d" (r1.pair),
509e250926SHeiko Carstens 		  [reg1] "=&d" (reg1),
519e250926SHeiko Carstens 		  [reg2] "=&a" (reg2),
5288c2510cSHeiko Carstens 		  "+Q" (S390_lowcore.program_new_psw),
539e250926SHeiko Carstens 		  "=Q" (old)
5488c2510cSHeiko Carstens 		: [subcode] "d" (subcode),
5588c2510cSHeiko Carstens 		  [psw_old] "a" (&old),
5688c2510cSHeiko Carstens 		  [psw_pgm] "a" (&S390_lowcore.program_new_psw)
5749698745SVasily Gorbik 		: "cc", "memory");
589e250926SHeiko Carstens 	return r1.odd;
5949698745SVasily Gorbik }
6049698745SVasily Gorbik 
store_ipl_parmblock(void)6149698745SVasily Gorbik void store_ipl_parmblock(void)
6249698745SVasily Gorbik {
6349698745SVasily Gorbik 	int rc;
6449698745SVasily Gorbik 
651e941d39SVasily Gorbik 	rc = __diag308(DIAG308_STORE, &ipl_block);
6649698745SVasily Gorbik 	if (rc == DIAG308_RC_OK &&
671e941d39SVasily Gorbik 	    ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
681e941d39SVasily Gorbik 		ipl_block_valid = 1;
6949698745SVasily Gorbik }
7049698745SVasily Gorbik 
is_ipl_block_dump(void)7173045a08SVasily Gorbik bool is_ipl_block_dump(void)
7273045a08SVasily Gorbik {
7373045a08SVasily Gorbik 	if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP &&
7473045a08SVasily Gorbik 	    ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP)
7573045a08SVasily Gorbik 		return true;
7673045a08SVasily Gorbik 	if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME &&
7773045a08SVasily Gorbik 	    ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP)
7873045a08SVasily Gorbik 		return true;
79e2d2a296SSven Schnelle 	if (ipl_block.pb0_hdr.pbt == IPL_PBT_ECKD &&
80e2d2a296SSven Schnelle 	    ipl_block.eckd.opt == IPL_PB0_ECKD_OPT_DUMP)
81e2d2a296SSven Schnelle 		return true;
8273045a08SVasily Gorbik 	return false;
8373045a08SVasily Gorbik }
8473045a08SVasily Gorbik 
scpdata_length(const u8 * buf,size_t count)854ae98789SArnd Bergmann static size_t scpdata_length(const u8 *buf, size_t count)
8649698745SVasily Gorbik {
8749698745SVasily Gorbik 	while (count) {
8849698745SVasily Gorbik 		if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
8949698745SVasily Gorbik 			break;
9049698745SVasily Gorbik 		count--;
9149698745SVasily Gorbik 	}
9249698745SVasily Gorbik 	return count;
9349698745SVasily Gorbik }
9449698745SVasily Gorbik 
ipl_block_get_ascii_scpdata(char * dest,size_t size,const struct ipl_parameter_block * ipb)9549698745SVasily Gorbik static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
9649698745SVasily Gorbik 					  const struct ipl_parameter_block *ipb)
9749698745SVasily Gorbik {
98d9f12e48SAlexander Egorenkov 	const __u8 *scp_data;
99d9f12e48SAlexander Egorenkov 	__u32 scp_data_len;
10049698745SVasily Gorbik 	int has_lowercase;
101d9f12e48SAlexander Egorenkov 	size_t count = 0;
102d9f12e48SAlexander Egorenkov 	size_t i;
10349698745SVasily Gorbik 
104d9f12e48SAlexander Egorenkov 	switch (ipb->pb0_hdr.pbt) {
105d9f12e48SAlexander Egorenkov 	case IPL_PBT_FCP:
106d9f12e48SAlexander Egorenkov 		scp_data_len = ipb->fcp.scp_data_len;
107d9f12e48SAlexander Egorenkov 		scp_data = ipb->fcp.scp_data;
108d9f12e48SAlexander Egorenkov 		break;
109d9f12e48SAlexander Egorenkov 	case IPL_PBT_NVME:
110d9f12e48SAlexander Egorenkov 		scp_data_len = ipb->nvme.scp_data_len;
111d9f12e48SAlexander Egorenkov 		scp_data = ipb->nvme.scp_data;
112d9f12e48SAlexander Egorenkov 		break;
11387fd22e0SSven Schnelle 	case IPL_PBT_ECKD:
11487fd22e0SSven Schnelle 		scp_data_len = ipb->eckd.scp_data_len;
11587fd22e0SSven Schnelle 		scp_data = ipb->eckd.scp_data;
11687fd22e0SSven Schnelle 		break;
11787fd22e0SSven Schnelle 
118d9f12e48SAlexander Egorenkov 	default:
119d9f12e48SAlexander Egorenkov 		goto out;
120d9f12e48SAlexander Egorenkov 	}
121d9f12e48SAlexander Egorenkov 
122d9f12e48SAlexander Egorenkov 	count = min(size - 1, scpdata_length(scp_data, scp_data_len));
12349698745SVasily Gorbik 	if (!count)
12449698745SVasily Gorbik 		goto out;
12549698745SVasily Gorbik 
12649698745SVasily Gorbik 	has_lowercase = 0;
12749698745SVasily Gorbik 	for (i = 0; i < count; i++) {
128d9f12e48SAlexander Egorenkov 		if (!isascii(scp_data[i])) {
12949698745SVasily Gorbik 			count = 0;
13049698745SVasily Gorbik 			goto out;
13149698745SVasily Gorbik 		}
132d9f12e48SAlexander Egorenkov 		if (!has_lowercase && islower(scp_data[i]))
13349698745SVasily Gorbik 			has_lowercase = 1;
13449698745SVasily Gorbik 	}
13549698745SVasily Gorbik 
13649698745SVasily Gorbik 	if (has_lowercase)
137d9f12e48SAlexander Egorenkov 		memcpy(dest, scp_data, count);
13849698745SVasily Gorbik 	else
13949698745SVasily Gorbik 		for (i = 0; i < count; i++)
140d9f12e48SAlexander Egorenkov 			dest[i] = tolower(scp_data[i]);
14149698745SVasily Gorbik out:
14249698745SVasily Gorbik 	dest[count] = '\0';
14349698745SVasily Gorbik 	return count;
14449698745SVasily Gorbik }
14549698745SVasily Gorbik 
append_ipl_block_parm(void)14649698745SVasily Gorbik static void append_ipl_block_parm(void)
14749698745SVasily Gorbik {
14849698745SVasily Gorbik 	char *parm, *delim;
14949698745SVasily Gorbik 	size_t len, rc = 0;
15049698745SVasily Gorbik 
15149698745SVasily Gorbik 	len = strlen(early_command_line);
15249698745SVasily Gorbik 
15349698745SVasily Gorbik 	delim = early_command_line + len;    /* '\0' character position */
15449698745SVasily Gorbik 	parm = early_command_line + len + 1; /* append right after '\0' */
15549698745SVasily Gorbik 
1565f1207fbSMartin Schwidefsky 	switch (ipl_block.pb0_hdr.pbt) {
1575f1207fbSMartin Schwidefsky 	case IPL_PBT_CCW:
15849698745SVasily Gorbik 		rc = ipl_block_get_ascii_vmparm(
1591e941d39SVasily Gorbik 			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
16049698745SVasily Gorbik 		break;
1615f1207fbSMartin Schwidefsky 	case IPL_PBT_FCP:
162d9f12e48SAlexander Egorenkov 	case IPL_PBT_NVME:
16387fd22e0SSven Schnelle 	case IPL_PBT_ECKD:
16449698745SVasily Gorbik 		rc = ipl_block_get_ascii_scpdata(
1651e941d39SVasily Gorbik 			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
16649698745SVasily Gorbik 		break;
16749698745SVasily Gorbik 	}
16849698745SVasily Gorbik 	if (rc) {
16949698745SVasily Gorbik 		if (*parm == '=')
17049698745SVasily Gorbik 			memmove(early_command_line, parm + 1, rc);
17149698745SVasily Gorbik 		else
17249698745SVasily Gorbik 			*delim = ' '; /* replace '\0' with space */
17349698745SVasily Gorbik 	}
17449698745SVasily Gorbik }
17549698745SVasily Gorbik 
has_ebcdic_char(const char * str)17649698745SVasily Gorbik static inline int has_ebcdic_char(const char *str)
17749698745SVasily Gorbik {
17849698745SVasily Gorbik 	int i;
17949698745SVasily Gorbik 
18049698745SVasily Gorbik 	for (i = 0; str[i]; i++)
18149698745SVasily Gorbik 		if (str[i] & 0x80)
18249698745SVasily Gorbik 			return 1;
18349698745SVasily Gorbik 	return 0;
18449698745SVasily Gorbik }
18549698745SVasily Gorbik 
setup_boot_command_line(void)18649698745SVasily Gorbik void setup_boot_command_line(void)
18749698745SVasily Gorbik {
1885ecb2da6SSven Schnelle 	parmarea.command_line[COMMAND_LINE_SIZE - 1] = 0;
18949698745SVasily Gorbik 	/* convert arch command line to ascii if necessary */
19027c1dac0SHeiko Carstens 	if (has_ebcdic_char(parmarea.command_line))
1915ecb2da6SSven Schnelle 		EBCASC(parmarea.command_line, COMMAND_LINE_SIZE);
19249698745SVasily Gorbik 	/* copy arch command line */
19327c1dac0SHeiko Carstens 	strcpy(early_command_line, strim(parmarea.command_line));
19449698745SVasily Gorbik 
19549698745SVasily Gorbik 	/* append IPL PARM data to the boot command line */
196093ddccbSVasily Gorbik 	if (!is_prot_virt_guest() && ipl_block_valid)
19749698745SVasily Gorbik 		append_ipl_block_parm();
19849698745SVasily Gorbik }
19949698745SVasily Gorbik 
modify_facility(unsigned long nr,bool clear)200b5e80459SVasily Gorbik static void modify_facility(unsigned long nr, bool clear)
201b5e80459SVasily Gorbik {
202b5e80459SVasily Gorbik 	if (clear)
20317e89e13SSven Schnelle 		__clear_facility(nr, stfle_fac_list);
204b5e80459SVasily Gorbik 	else
20517e89e13SSven Schnelle 		__set_facility(nr, stfle_fac_list);
206b5e80459SVasily Gorbik }
207b5e80459SVasily Gorbik 
check_cleared_facilities(void)2086d85dac2SVasily Gorbik static void check_cleared_facilities(void)
2096d85dac2SVasily Gorbik {
2106d85dac2SVasily Gorbik 	unsigned long als[] = { FACILITIES_ALS };
2116d85dac2SVasily Gorbik 	int i;
2126d85dac2SVasily Gorbik 
2136d85dac2SVasily Gorbik 	for (i = 0; i < ARRAY_SIZE(als); i++) {
21417e89e13SSven Schnelle 		if ((stfle_fac_list[i] & als[i]) != als[i]) {
2156d85dac2SVasily Gorbik 			sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n");
2166d85dac2SVasily Gorbik 			print_missing_facilities();
2176d85dac2SVasily Gorbik 			break;
2186d85dac2SVasily Gorbik 		}
2196d85dac2SVasily Gorbik 	}
2206d85dac2SVasily Gorbik }
2216d85dac2SVasily Gorbik 
modify_fac_list(char * str)222b5e80459SVasily Gorbik static void modify_fac_list(char *str)
223b5e80459SVasily Gorbik {
224b5e80459SVasily Gorbik 	unsigned long val, endval;
225b5e80459SVasily Gorbik 	char *endp;
226b5e80459SVasily Gorbik 	bool clear;
227b5e80459SVasily Gorbik 
228b5e80459SVasily Gorbik 	while (*str) {
229b5e80459SVasily Gorbik 		clear = false;
230b5e80459SVasily Gorbik 		if (*str == '!') {
231b5e80459SVasily Gorbik 			clear = true;
232b5e80459SVasily Gorbik 			str++;
233b5e80459SVasily Gorbik 		}
234b5e80459SVasily Gorbik 		val = simple_strtoull(str, &endp, 0);
235b5e80459SVasily Gorbik 		if (str == endp)
236b5e80459SVasily Gorbik 			break;
237b5e80459SVasily Gorbik 		str = endp;
238b5e80459SVasily Gorbik 		if (*str == '-') {
239b5e80459SVasily Gorbik 			str++;
240b5e80459SVasily Gorbik 			endval = simple_strtoull(str, &endp, 0);
241b5e80459SVasily Gorbik 			if (str == endp)
242b5e80459SVasily Gorbik 				break;
243b5e80459SVasily Gorbik 			str = endp;
244b5e80459SVasily Gorbik 			while (val <= endval) {
245b5e80459SVasily Gorbik 				modify_facility(val, clear);
246b5e80459SVasily Gorbik 				val++;
247b5e80459SVasily Gorbik 			}
248b5e80459SVasily Gorbik 		} else {
249b5e80459SVasily Gorbik 			modify_facility(val, clear);
250b5e80459SVasily Gorbik 		}
251b5e80459SVasily Gorbik 		if (*str != ',')
252b5e80459SVasily Gorbik 			break;
253b5e80459SVasily Gorbik 		str++;
254b5e80459SVasily Gorbik 	}
2556d85dac2SVasily Gorbik 	check_cleared_facilities();
256b5e80459SVasily Gorbik }
257b5e80459SVasily Gorbik 
258980d5f9aSAlexander Egorenkov static char command_line_buf[COMMAND_LINE_SIZE];
parse_boot_command_line(void)259b5e80459SVasily Gorbik void parse_boot_command_line(void)
26049698745SVasily Gorbik {
26149698745SVasily Gorbik 	char *param, *val;
262d58106c3SVasily Gorbik 	bool enabled;
263d58106c3SVasily Gorbik 	char *args;
264d58106c3SVasily Gorbik 	int rc;
26549698745SVasily Gorbik 
266bb87190cSHeiko Carstens 	__kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE);
26749698745SVasily Gorbik 	args = strcpy(command_line_buf, early_command_line);
26849698745SVasily Gorbik 	while (*args) {
26949698745SVasily Gorbik 		args = next_arg(args, &param, &val);
27049698745SVasily Gorbik 
27173045a08SVasily Gorbik 		if (!strcmp(param, "mem") && val)
27273045a08SVasily Gorbik 			memory_limit = round_down(memparse(val, NULL), PAGE_SIZE);
273d58106c3SVasily Gorbik 
27490178c19SHeiko Carstens 		if (!strcmp(param, "vmalloc") && val) {
275*05a8ba5cSAlexander Gordeev 			vmalloc_size = round_up(memparse(val, NULL), _SEGMENT_SIZE);
27690178c19SHeiko Carstens 			vmalloc_size_set = 1;
27790178c19SHeiko Carstens 		}
27859793c5aSVasily Gorbik 
27986cde618SVasily Gorbik 		if (!strcmp(param, "dfltcc") && val) {
280c65e6815SMikhail Zaslonko 			if (!strcmp(val, "off"))
281c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED;
282c65e6815SMikhail Zaslonko 			else if (!strcmp(val, "on"))
283c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_FULL;
284c65e6815SMikhail Zaslonko 			else if (!strcmp(val, "def_only"))
285c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY;
286c65e6815SMikhail Zaslonko 			else if (!strcmp(val, "inf_only"))
287c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY;
288c65e6815SMikhail Zaslonko 			else if (!strcmp(val, "always"))
289c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG;
290c65e6815SMikhail Zaslonko 		}
291c65e6815SMikhail Zaslonko 
292227f52a4SVasily Gorbik 		if (!strcmp(param, "facilities") && val)
293b5e80459SVasily Gorbik 			modify_fac_list(val);
294b2d24b97SGerald Schaefer 
295b2d24b97SGerald Schaefer 		if (!strcmp(param, "nokaslr"))
296bb87190cSHeiko Carstens 			__kaslr_enabled = 0;
2971d6671aeSVasily Gorbik 
2981d6671aeSVasily Gorbik #if IS_ENABLED(CONFIG_KVM)
2991d6671aeSVasily Gorbik 		if (!strcmp(param, "prot_virt")) {
3001d6671aeSVasily Gorbik 			rc = kstrtobool(val, &enabled);
3011d6671aeSVasily Gorbik 			if (!rc && enabled)
3021d6671aeSVasily Gorbik 				prot_virt_host = 1;
3031d6671aeSVasily Gorbik 		}
3041d6671aeSVasily Gorbik #endif
30549698745SVasily Gorbik 	}
30649698745SVasily Gorbik }
307