xref: /openbmc/linux/arch/s390/boot/ipl_parm.c (revision 49698745)
149698745SVasily Gorbik // SPDX-License-Identifier: GPL-2.0
249698745SVasily Gorbik #include <linux/init.h>
349698745SVasily Gorbik #include <linux/ctype.h>
449698745SVasily Gorbik #include <asm/ebcdic.h>
549698745SVasily Gorbik #include <asm/sclp.h>
649698745SVasily Gorbik #include <asm/sections.h>
749698745SVasily Gorbik #include <asm/boot_data.h>
849698745SVasily Gorbik #include "boot.h"
949698745SVasily Gorbik 
1049698745SVasily Gorbik char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
1149698745SVasily Gorbik struct ipl_parameter_block __bootdata(early_ipl_block);
1249698745SVasily Gorbik int __bootdata(early_ipl_block_valid);
1349698745SVasily Gorbik 
1449698745SVasily Gorbik unsigned long __bootdata(memory_end);
1549698745SVasily Gorbik int __bootdata(memory_end_set);
1649698745SVasily Gorbik 
1749698745SVasily Gorbik static inline int __diag308(unsigned long subcode, void *addr)
1849698745SVasily Gorbik {
1949698745SVasily Gorbik 	register unsigned long _addr asm("0") = (unsigned long)addr;
2049698745SVasily Gorbik 	register unsigned long _rc asm("1") = 0;
2149698745SVasily Gorbik 	unsigned long reg1, reg2;
2249698745SVasily Gorbik 	psw_t old = S390_lowcore.program_new_psw;
2349698745SVasily Gorbik 
2449698745SVasily Gorbik 	asm volatile(
2549698745SVasily Gorbik 		"	epsw	%0,%1\n"
2649698745SVasily Gorbik 		"	st	%0,%[psw_pgm]\n"
2749698745SVasily Gorbik 		"	st	%1,%[psw_pgm]+4\n"
2849698745SVasily Gorbik 		"	larl	%0,1f\n"
2949698745SVasily Gorbik 		"	stg	%0,%[psw_pgm]+8\n"
3049698745SVasily Gorbik 		"	diag	%[addr],%[subcode],0x308\n"
3149698745SVasily Gorbik 		"1:	nopr	%%r7\n"
3249698745SVasily Gorbik 		: "=&d" (reg1), "=&a" (reg2),
3349698745SVasily Gorbik 		  [psw_pgm] "=Q" (S390_lowcore.program_new_psw),
3449698745SVasily Gorbik 		  [addr] "+d" (_addr), "+d" (_rc)
3549698745SVasily Gorbik 		: [subcode] "d" (subcode)
3649698745SVasily Gorbik 		: "cc", "memory");
3749698745SVasily Gorbik 	S390_lowcore.program_new_psw = old;
3849698745SVasily Gorbik 	return _rc;
3949698745SVasily Gorbik }
4049698745SVasily Gorbik 
4149698745SVasily Gorbik void store_ipl_parmblock(void)
4249698745SVasily Gorbik {
4349698745SVasily Gorbik 	int rc;
4449698745SVasily Gorbik 
4549698745SVasily Gorbik 	rc = __diag308(DIAG308_STORE, &early_ipl_block);
4649698745SVasily Gorbik 	if (rc == DIAG308_RC_OK &&
4749698745SVasily Gorbik 	    early_ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
4849698745SVasily Gorbik 		early_ipl_block_valid = 1;
4949698745SVasily Gorbik }
5049698745SVasily Gorbik 
5149698745SVasily Gorbik static size_t scpdata_length(const char *buf, size_t count)
5249698745SVasily Gorbik {
5349698745SVasily Gorbik 	while (count) {
5449698745SVasily Gorbik 		if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
5549698745SVasily Gorbik 			break;
5649698745SVasily Gorbik 		count--;
5749698745SVasily Gorbik 	}
5849698745SVasily Gorbik 	return count;
5949698745SVasily Gorbik }
6049698745SVasily Gorbik 
6149698745SVasily Gorbik static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
6249698745SVasily Gorbik 					  const struct ipl_parameter_block *ipb)
6349698745SVasily Gorbik {
6449698745SVasily Gorbik 	size_t count;
6549698745SVasily Gorbik 	size_t i;
6649698745SVasily Gorbik 	int has_lowercase;
6749698745SVasily Gorbik 
6849698745SVasily Gorbik 	count = min(size - 1, scpdata_length(ipb->ipl_info.fcp.scp_data,
6949698745SVasily Gorbik 					     ipb->ipl_info.fcp.scp_data_len));
7049698745SVasily Gorbik 	if (!count)
7149698745SVasily Gorbik 		goto out;
7249698745SVasily Gorbik 
7349698745SVasily Gorbik 	has_lowercase = 0;
7449698745SVasily Gorbik 	for (i = 0; i < count; i++) {
7549698745SVasily Gorbik 		if (!isascii(ipb->ipl_info.fcp.scp_data[i])) {
7649698745SVasily Gorbik 			count = 0;
7749698745SVasily Gorbik 			goto out;
7849698745SVasily Gorbik 		}
7949698745SVasily Gorbik 		if (!has_lowercase && islower(ipb->ipl_info.fcp.scp_data[i]))
8049698745SVasily Gorbik 			has_lowercase = 1;
8149698745SVasily Gorbik 	}
8249698745SVasily Gorbik 
8349698745SVasily Gorbik 	if (has_lowercase)
8449698745SVasily Gorbik 		memcpy(dest, ipb->ipl_info.fcp.scp_data, count);
8549698745SVasily Gorbik 	else
8649698745SVasily Gorbik 		for (i = 0; i < count; i++)
8749698745SVasily Gorbik 			dest[i] = tolower(ipb->ipl_info.fcp.scp_data[i]);
8849698745SVasily Gorbik out:
8949698745SVasily Gorbik 	dest[count] = '\0';
9049698745SVasily Gorbik 	return count;
9149698745SVasily Gorbik }
9249698745SVasily Gorbik 
9349698745SVasily Gorbik static void append_ipl_block_parm(void)
9449698745SVasily Gorbik {
9549698745SVasily Gorbik 	char *parm, *delim;
9649698745SVasily Gorbik 	size_t len, rc = 0;
9749698745SVasily Gorbik 
9849698745SVasily Gorbik 	len = strlen(early_command_line);
9949698745SVasily Gorbik 
10049698745SVasily Gorbik 	delim = early_command_line + len;    /* '\0' character position */
10149698745SVasily Gorbik 	parm = early_command_line + len + 1; /* append right after '\0' */
10249698745SVasily Gorbik 
10349698745SVasily Gorbik 	switch (early_ipl_block.hdr.pbt) {
10449698745SVasily Gorbik 	case DIAG308_IPL_TYPE_CCW:
10549698745SVasily Gorbik 		rc = ipl_block_get_ascii_vmparm(
10649698745SVasily Gorbik 			parm, COMMAND_LINE_SIZE - len - 1, &early_ipl_block);
10749698745SVasily Gorbik 		break;
10849698745SVasily Gorbik 	case DIAG308_IPL_TYPE_FCP:
10949698745SVasily Gorbik 		rc = ipl_block_get_ascii_scpdata(
11049698745SVasily Gorbik 			parm, COMMAND_LINE_SIZE - len - 1, &early_ipl_block);
11149698745SVasily Gorbik 		break;
11249698745SVasily Gorbik 	}
11349698745SVasily Gorbik 	if (rc) {
11449698745SVasily Gorbik 		if (*parm == '=')
11549698745SVasily Gorbik 			memmove(early_command_line, parm + 1, rc);
11649698745SVasily Gorbik 		else
11749698745SVasily Gorbik 			*delim = ' '; /* replace '\0' with space */
11849698745SVasily Gorbik 	}
11949698745SVasily Gorbik }
12049698745SVasily Gorbik 
12149698745SVasily Gorbik static inline int has_ebcdic_char(const char *str)
12249698745SVasily Gorbik {
12349698745SVasily Gorbik 	int i;
12449698745SVasily Gorbik 
12549698745SVasily Gorbik 	for (i = 0; str[i]; i++)
12649698745SVasily Gorbik 		if (str[i] & 0x80)
12749698745SVasily Gorbik 			return 1;
12849698745SVasily Gorbik 	return 0;
12949698745SVasily Gorbik }
13049698745SVasily Gorbik 
13149698745SVasily Gorbik void setup_boot_command_line(void)
13249698745SVasily Gorbik {
13349698745SVasily Gorbik 	COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0;
13449698745SVasily Gorbik 	/* convert arch command line to ascii if necessary */
13549698745SVasily Gorbik 	if (has_ebcdic_char(COMMAND_LINE))
13649698745SVasily Gorbik 		EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
13749698745SVasily Gorbik 	/* copy arch command line */
13849698745SVasily Gorbik 	strcpy(early_command_line, strim(COMMAND_LINE));
13949698745SVasily Gorbik 
14049698745SVasily Gorbik 	/* append IPL PARM data to the boot command line */
14149698745SVasily Gorbik 	if (early_ipl_block_valid)
14249698745SVasily Gorbik 		append_ipl_block_parm();
14349698745SVasily Gorbik }
14449698745SVasily Gorbik 
14549698745SVasily Gorbik static char command_line_buf[COMMAND_LINE_SIZE] __section(.data);
14649698745SVasily Gorbik static void parse_mem_opt(void)
14749698745SVasily Gorbik {
14849698745SVasily Gorbik 	char *args;
14949698745SVasily Gorbik 	char *param, *val;
15049698745SVasily Gorbik 
15149698745SVasily Gorbik 	args = strcpy(command_line_buf, early_command_line);
15249698745SVasily Gorbik 	while (*args) {
15349698745SVasily Gorbik 		args = next_arg(args, &param, &val);
15449698745SVasily Gorbik 
15549698745SVasily Gorbik 		if (!strcmp(param, "mem")) {
15649698745SVasily Gorbik 			memory_end = memparse(val, NULL);
15749698745SVasily Gorbik 			memory_end_set = 1;
15849698745SVasily Gorbik 		}
15949698745SVasily Gorbik 	}
16049698745SVasily Gorbik }
16149698745SVasily Gorbik 
16249698745SVasily Gorbik void setup_memory_end(void)
16349698745SVasily Gorbik {
16449698745SVasily Gorbik 	parse_mem_opt();
16549698745SVasily Gorbik #ifdef CONFIG_CRASH_DUMP
16649698745SVasily Gorbik 	if (!OLDMEM_BASE && early_ipl_block_valid &&
16749698745SVasily Gorbik 	    early_ipl_block.hdr.pbt == DIAG308_IPL_TYPE_FCP &&
16849698745SVasily Gorbik 	    early_ipl_block.ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP) {
16949698745SVasily Gorbik 		if (!sclp_early_get_hsa_size(&memory_end) && memory_end)
17049698745SVasily Gorbik 			memory_end_set = 1;
17149698745SVasily Gorbik 	}
17249698745SVasily Gorbik #endif
17349698745SVasily Gorbik }
174