xref: /openbmc/linux/arch/s390/boot/ipl_parm.c (revision 88c2510c)
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>
11db9492ceSVasily Gorbik #include <asm/uv.h>
1249698745SVasily Gorbik #include "boot.h"
1349698745SVasily Gorbik 
1449698745SVasily Gorbik char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
15d58106c3SVasily Gorbik int __bootdata(noexec_disabled);
1649698745SVasily Gorbik 
170c4f2623SVasily Gorbik unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL;
180c4f2623SVasily Gorbik struct ipl_parameter_block __bootdata_preserved(ipl_block);
190c4f2623SVasily Gorbik int __bootdata_preserved(ipl_block_valid);
200c4f2623SVasily Gorbik 
210c4f2623SVasily Gorbik unsigned long vmalloc_size = VMALLOC_DEFAULT_SIZE;
2273045a08SVasily Gorbik unsigned long memory_limit;
2390178c19SHeiko Carstens int vmalloc_size_set;
24980d5f9aSAlexander Egorenkov int kaslr_enabled;
25b2d24b97SGerald Schaefer 
2649698745SVasily Gorbik static inline int __diag308(unsigned long subcode, void *addr)
2749698745SVasily Gorbik {
2849698745SVasily Gorbik 	register unsigned long _addr asm("0") = (unsigned long)addr;
2949698745SVasily Gorbik 	register unsigned long _rc asm("1") = 0;
3049698745SVasily Gorbik 	unsigned long reg1, reg2;
31*88c2510cSHeiko Carstens 	psw_t old;
3249698745SVasily Gorbik 
3349698745SVasily Gorbik 	asm volatile(
34*88c2510cSHeiko Carstens 		"	mvc	0(16,%[psw_old]),0(%[psw_pgm])\n"
3549698745SVasily Gorbik 		"	epsw	%0,%1\n"
36*88c2510cSHeiko Carstens 		"	st	%0,0(%[psw_pgm])\n"
37*88c2510cSHeiko Carstens 		"	st	%1,4(%[psw_pgm])\n"
3849698745SVasily Gorbik 		"	larl	%0,1f\n"
39*88c2510cSHeiko Carstens 		"	stg	%0,8(%[psw_pgm])\n"
4049698745SVasily Gorbik 		"	diag	%[addr],%[subcode],0x308\n"
41*88c2510cSHeiko Carstens 		"1:	mvc	0(16,%[psw_pgm]),0(%[psw_old])\n"
4249698745SVasily Gorbik 		: "=&d" (reg1), "=&a" (reg2),
43*88c2510cSHeiko Carstens 		  "+Q" (S390_lowcore.program_new_psw),
44*88c2510cSHeiko Carstens 		  "=Q" (old),
4549698745SVasily Gorbik 		  [addr] "+d" (_addr), "+d" (_rc)
46*88c2510cSHeiko Carstens 		: [subcode] "d" (subcode),
47*88c2510cSHeiko Carstens 		  [psw_old] "a" (&old),
48*88c2510cSHeiko Carstens 		  [psw_pgm] "a" (&S390_lowcore.program_new_psw)
4949698745SVasily Gorbik 		: "cc", "memory");
5049698745SVasily Gorbik 	return _rc;
5149698745SVasily Gorbik }
5249698745SVasily Gorbik 
5349698745SVasily Gorbik void store_ipl_parmblock(void)
5449698745SVasily Gorbik {
5549698745SVasily Gorbik 	int rc;
5649698745SVasily Gorbik 
571e941d39SVasily Gorbik 	rc = __diag308(DIAG308_STORE, &ipl_block);
5849698745SVasily Gorbik 	if (rc == DIAG308_RC_OK &&
591e941d39SVasily Gorbik 	    ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
601e941d39SVasily Gorbik 		ipl_block_valid = 1;
6149698745SVasily Gorbik }
6249698745SVasily Gorbik 
6373045a08SVasily Gorbik bool is_ipl_block_dump(void)
6473045a08SVasily Gorbik {
6573045a08SVasily Gorbik 	if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP &&
6673045a08SVasily Gorbik 	    ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP)
6773045a08SVasily Gorbik 		return true;
6873045a08SVasily Gorbik 	if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME &&
6973045a08SVasily Gorbik 	    ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP)
7073045a08SVasily Gorbik 		return true;
7173045a08SVasily Gorbik 	return false;
7273045a08SVasily Gorbik }
7373045a08SVasily Gorbik 
744ae98789SArnd Bergmann static size_t scpdata_length(const u8 *buf, size_t count)
7549698745SVasily Gorbik {
7649698745SVasily Gorbik 	while (count) {
7749698745SVasily Gorbik 		if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
7849698745SVasily Gorbik 			break;
7949698745SVasily Gorbik 		count--;
8049698745SVasily Gorbik 	}
8149698745SVasily Gorbik 	return count;
8249698745SVasily Gorbik }
8349698745SVasily Gorbik 
8449698745SVasily Gorbik static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
8549698745SVasily Gorbik 					  const struct ipl_parameter_block *ipb)
8649698745SVasily Gorbik {
87d9f12e48SAlexander Egorenkov 	const __u8 *scp_data;
88d9f12e48SAlexander Egorenkov 	__u32 scp_data_len;
8949698745SVasily Gorbik 	int has_lowercase;
90d9f12e48SAlexander Egorenkov 	size_t count = 0;
91d9f12e48SAlexander Egorenkov 	size_t i;
9249698745SVasily Gorbik 
93d9f12e48SAlexander Egorenkov 	switch (ipb->pb0_hdr.pbt) {
94d9f12e48SAlexander Egorenkov 	case IPL_PBT_FCP:
95d9f12e48SAlexander Egorenkov 		scp_data_len = ipb->fcp.scp_data_len;
96d9f12e48SAlexander Egorenkov 		scp_data = ipb->fcp.scp_data;
97d9f12e48SAlexander Egorenkov 		break;
98d9f12e48SAlexander Egorenkov 	case IPL_PBT_NVME:
99d9f12e48SAlexander Egorenkov 		scp_data_len = ipb->nvme.scp_data_len;
100d9f12e48SAlexander Egorenkov 		scp_data = ipb->nvme.scp_data;
101d9f12e48SAlexander Egorenkov 		break;
102d9f12e48SAlexander Egorenkov 	default:
103d9f12e48SAlexander Egorenkov 		goto out;
104d9f12e48SAlexander Egorenkov 	}
105d9f12e48SAlexander Egorenkov 
106d9f12e48SAlexander Egorenkov 	count = min(size - 1, scpdata_length(scp_data, scp_data_len));
10749698745SVasily Gorbik 	if (!count)
10849698745SVasily Gorbik 		goto out;
10949698745SVasily Gorbik 
11049698745SVasily Gorbik 	has_lowercase = 0;
11149698745SVasily Gorbik 	for (i = 0; i < count; i++) {
112d9f12e48SAlexander Egorenkov 		if (!isascii(scp_data[i])) {
11349698745SVasily Gorbik 			count = 0;
11449698745SVasily Gorbik 			goto out;
11549698745SVasily Gorbik 		}
116d9f12e48SAlexander Egorenkov 		if (!has_lowercase && islower(scp_data[i]))
11749698745SVasily Gorbik 			has_lowercase = 1;
11849698745SVasily Gorbik 	}
11949698745SVasily Gorbik 
12049698745SVasily Gorbik 	if (has_lowercase)
121d9f12e48SAlexander Egorenkov 		memcpy(dest, scp_data, count);
12249698745SVasily Gorbik 	else
12349698745SVasily Gorbik 		for (i = 0; i < count; i++)
124d9f12e48SAlexander Egorenkov 			dest[i] = tolower(scp_data[i]);
12549698745SVasily Gorbik out:
12649698745SVasily Gorbik 	dest[count] = '\0';
12749698745SVasily Gorbik 	return count;
12849698745SVasily Gorbik }
12949698745SVasily Gorbik 
13049698745SVasily Gorbik static void append_ipl_block_parm(void)
13149698745SVasily Gorbik {
13249698745SVasily Gorbik 	char *parm, *delim;
13349698745SVasily Gorbik 	size_t len, rc = 0;
13449698745SVasily Gorbik 
13549698745SVasily Gorbik 	len = strlen(early_command_line);
13649698745SVasily Gorbik 
13749698745SVasily Gorbik 	delim = early_command_line + len;    /* '\0' character position */
13849698745SVasily Gorbik 	parm = early_command_line + len + 1; /* append right after '\0' */
13949698745SVasily Gorbik 
1405f1207fbSMartin Schwidefsky 	switch (ipl_block.pb0_hdr.pbt) {
1415f1207fbSMartin Schwidefsky 	case IPL_PBT_CCW:
14249698745SVasily Gorbik 		rc = ipl_block_get_ascii_vmparm(
1431e941d39SVasily Gorbik 			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
14449698745SVasily Gorbik 		break;
1455f1207fbSMartin Schwidefsky 	case IPL_PBT_FCP:
146d9f12e48SAlexander Egorenkov 	case IPL_PBT_NVME:
14749698745SVasily Gorbik 		rc = ipl_block_get_ascii_scpdata(
1481e941d39SVasily Gorbik 			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
14949698745SVasily Gorbik 		break;
15049698745SVasily Gorbik 	}
15149698745SVasily Gorbik 	if (rc) {
15249698745SVasily Gorbik 		if (*parm == '=')
15349698745SVasily Gorbik 			memmove(early_command_line, parm + 1, rc);
15449698745SVasily Gorbik 		else
15549698745SVasily Gorbik 			*delim = ' '; /* replace '\0' with space */
15649698745SVasily Gorbik 	}
15749698745SVasily Gorbik }
15849698745SVasily Gorbik 
15949698745SVasily Gorbik static inline int has_ebcdic_char(const char *str)
16049698745SVasily Gorbik {
16149698745SVasily Gorbik 	int i;
16249698745SVasily Gorbik 
16349698745SVasily Gorbik 	for (i = 0; str[i]; i++)
16449698745SVasily Gorbik 		if (str[i] & 0x80)
16549698745SVasily Gorbik 			return 1;
16649698745SVasily Gorbik 	return 0;
16749698745SVasily Gorbik }
16849698745SVasily Gorbik 
16949698745SVasily Gorbik void setup_boot_command_line(void)
17049698745SVasily Gorbik {
17127c1dac0SHeiko Carstens 	parmarea.command_line[ARCH_COMMAND_LINE_SIZE - 1] = 0;
17249698745SVasily Gorbik 	/* convert arch command line to ascii if necessary */
17327c1dac0SHeiko Carstens 	if (has_ebcdic_char(parmarea.command_line))
17427c1dac0SHeiko Carstens 		EBCASC(parmarea.command_line, ARCH_COMMAND_LINE_SIZE);
17549698745SVasily Gorbik 	/* copy arch command line */
17627c1dac0SHeiko Carstens 	strcpy(early_command_line, strim(parmarea.command_line));
17749698745SVasily Gorbik 
17849698745SVasily Gorbik 	/* append IPL PARM data to the boot command line */
179093ddccbSVasily Gorbik 	if (!is_prot_virt_guest() && ipl_block_valid)
18049698745SVasily Gorbik 		append_ipl_block_parm();
18149698745SVasily Gorbik }
18249698745SVasily Gorbik 
183b5e80459SVasily Gorbik static void modify_facility(unsigned long nr, bool clear)
184b5e80459SVasily Gorbik {
185b5e80459SVasily Gorbik 	if (clear)
18617e89e13SSven Schnelle 		__clear_facility(nr, stfle_fac_list);
187b5e80459SVasily Gorbik 	else
18817e89e13SSven Schnelle 		__set_facility(nr, stfle_fac_list);
189b5e80459SVasily Gorbik }
190b5e80459SVasily Gorbik 
1916d85dac2SVasily Gorbik static void check_cleared_facilities(void)
1926d85dac2SVasily Gorbik {
1936d85dac2SVasily Gorbik 	unsigned long als[] = { FACILITIES_ALS };
1946d85dac2SVasily Gorbik 	int i;
1956d85dac2SVasily Gorbik 
1966d85dac2SVasily Gorbik 	for (i = 0; i < ARRAY_SIZE(als); i++) {
19717e89e13SSven Schnelle 		if ((stfle_fac_list[i] & als[i]) != als[i]) {
1986d85dac2SVasily Gorbik 			sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n");
1996d85dac2SVasily Gorbik 			print_missing_facilities();
2006d85dac2SVasily Gorbik 			break;
2016d85dac2SVasily Gorbik 		}
2026d85dac2SVasily Gorbik 	}
2036d85dac2SVasily Gorbik }
2046d85dac2SVasily Gorbik 
205b5e80459SVasily Gorbik static void modify_fac_list(char *str)
206b5e80459SVasily Gorbik {
207b5e80459SVasily Gorbik 	unsigned long val, endval;
208b5e80459SVasily Gorbik 	char *endp;
209b5e80459SVasily Gorbik 	bool clear;
210b5e80459SVasily Gorbik 
211b5e80459SVasily Gorbik 	while (*str) {
212b5e80459SVasily Gorbik 		clear = false;
213b5e80459SVasily Gorbik 		if (*str == '!') {
214b5e80459SVasily Gorbik 			clear = true;
215b5e80459SVasily Gorbik 			str++;
216b5e80459SVasily Gorbik 		}
217b5e80459SVasily Gorbik 		val = simple_strtoull(str, &endp, 0);
218b5e80459SVasily Gorbik 		if (str == endp)
219b5e80459SVasily Gorbik 			break;
220b5e80459SVasily Gorbik 		str = endp;
221b5e80459SVasily Gorbik 		if (*str == '-') {
222b5e80459SVasily Gorbik 			str++;
223b5e80459SVasily Gorbik 			endval = simple_strtoull(str, &endp, 0);
224b5e80459SVasily Gorbik 			if (str == endp)
225b5e80459SVasily Gorbik 				break;
226b5e80459SVasily Gorbik 			str = endp;
227b5e80459SVasily Gorbik 			while (val <= endval) {
228b5e80459SVasily Gorbik 				modify_facility(val, clear);
229b5e80459SVasily Gorbik 				val++;
230b5e80459SVasily Gorbik 			}
231b5e80459SVasily Gorbik 		} else {
232b5e80459SVasily Gorbik 			modify_facility(val, clear);
233b5e80459SVasily Gorbik 		}
234b5e80459SVasily Gorbik 		if (*str != ',')
235b5e80459SVasily Gorbik 			break;
236b5e80459SVasily Gorbik 		str++;
237b5e80459SVasily Gorbik 	}
2386d85dac2SVasily Gorbik 	check_cleared_facilities();
239b5e80459SVasily Gorbik }
240b5e80459SVasily Gorbik 
241980d5f9aSAlexander Egorenkov static char command_line_buf[COMMAND_LINE_SIZE];
242b5e80459SVasily Gorbik void parse_boot_command_line(void)
24349698745SVasily Gorbik {
24449698745SVasily Gorbik 	char *param, *val;
245d58106c3SVasily Gorbik 	bool enabled;
246d58106c3SVasily Gorbik 	char *args;
247d58106c3SVasily Gorbik 	int rc;
24849698745SVasily Gorbik 
249b2d24b97SGerald Schaefer 	kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE);
25049698745SVasily Gorbik 	args = strcpy(command_line_buf, early_command_line);
25149698745SVasily Gorbik 	while (*args) {
25249698745SVasily Gorbik 		args = next_arg(args, &param, &val);
25349698745SVasily Gorbik 
25473045a08SVasily Gorbik 		if (!strcmp(param, "mem") && val)
25573045a08SVasily Gorbik 			memory_limit = round_down(memparse(val, NULL), PAGE_SIZE);
256d58106c3SVasily Gorbik 
25790178c19SHeiko Carstens 		if (!strcmp(param, "vmalloc") && val) {
25859793c5aSVasily Gorbik 			vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE);
25990178c19SHeiko Carstens 			vmalloc_size_set = 1;
26090178c19SHeiko Carstens 		}
26159793c5aSVasily Gorbik 
26286cde618SVasily Gorbik 		if (!strcmp(param, "dfltcc") && val) {
263c65e6815SMikhail Zaslonko 			if (!strcmp(val, "off"))
264c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED;
265c65e6815SMikhail Zaslonko 			else if (!strcmp(val, "on"))
266c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_FULL;
267c65e6815SMikhail Zaslonko 			else if (!strcmp(val, "def_only"))
268c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY;
269c65e6815SMikhail Zaslonko 			else if (!strcmp(val, "inf_only"))
270c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY;
271c65e6815SMikhail Zaslonko 			else if (!strcmp(val, "always"))
272c65e6815SMikhail Zaslonko 				zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG;
273c65e6815SMikhail Zaslonko 		}
274c65e6815SMikhail Zaslonko 
275d58106c3SVasily Gorbik 		if (!strcmp(param, "noexec")) {
276d58106c3SVasily Gorbik 			rc = kstrtobool(val, &enabled);
277d58106c3SVasily Gorbik 			if (!rc && !enabled)
278d58106c3SVasily Gorbik 				noexec_disabled = 1;
279d58106c3SVasily Gorbik 		}
280b5e80459SVasily Gorbik 
281227f52a4SVasily Gorbik 		if (!strcmp(param, "facilities") && val)
282b5e80459SVasily Gorbik 			modify_fac_list(val);
283b2d24b97SGerald Schaefer 
284b2d24b97SGerald Schaefer 		if (!strcmp(param, "nokaslr"))
285b2d24b97SGerald Schaefer 			kaslr_enabled = 0;
2861d6671aeSVasily Gorbik 
2871d6671aeSVasily Gorbik #if IS_ENABLED(CONFIG_KVM)
2881d6671aeSVasily Gorbik 		if (!strcmp(param, "prot_virt")) {
2891d6671aeSVasily Gorbik 			rc = kstrtobool(val, &enabled);
2901d6671aeSVasily Gorbik 			if (!rc && enabled)
2911d6671aeSVasily Gorbik 				prot_virt_host = 1;
2921d6671aeSVasily Gorbik 		}
2931d6671aeSVasily Gorbik #endif
29449698745SVasily Gorbik 	}
29549698745SVasily Gorbik }
296