xref: /openbmc/linux/arch/s390/boot/ipl_parm.c (revision 88c2510c)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/kernel.h>
3 #include <linux/init.h>
4 #include <linux/ctype.h>
5 #include <linux/pgtable.h>
6 #include <asm/ebcdic.h>
7 #include <asm/sclp.h>
8 #include <asm/sections.h>
9 #include <asm/boot_data.h>
10 #include <asm/facility.h>
11 #include <asm/uv.h>
12 #include "boot.h"
13 
14 char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
15 int __bootdata(noexec_disabled);
16 
17 unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL;
18 struct ipl_parameter_block __bootdata_preserved(ipl_block);
19 int __bootdata_preserved(ipl_block_valid);
20 
21 unsigned long vmalloc_size = VMALLOC_DEFAULT_SIZE;
22 unsigned long memory_limit;
23 int vmalloc_size_set;
24 int kaslr_enabled;
25 
26 static inline int __diag308(unsigned long subcode, void *addr)
27 {
28 	register unsigned long _addr asm("0") = (unsigned long)addr;
29 	register unsigned long _rc asm("1") = 0;
30 	unsigned long reg1, reg2;
31 	psw_t old;
32 
33 	asm volatile(
34 		"	mvc	0(16,%[psw_old]),0(%[psw_pgm])\n"
35 		"	epsw	%0,%1\n"
36 		"	st	%0,0(%[psw_pgm])\n"
37 		"	st	%1,4(%[psw_pgm])\n"
38 		"	larl	%0,1f\n"
39 		"	stg	%0,8(%[psw_pgm])\n"
40 		"	diag	%[addr],%[subcode],0x308\n"
41 		"1:	mvc	0(16,%[psw_pgm]),0(%[psw_old])\n"
42 		: "=&d" (reg1), "=&a" (reg2),
43 		  "+Q" (S390_lowcore.program_new_psw),
44 		  "=Q" (old),
45 		  [addr] "+d" (_addr), "+d" (_rc)
46 		: [subcode] "d" (subcode),
47 		  [psw_old] "a" (&old),
48 		  [psw_pgm] "a" (&S390_lowcore.program_new_psw)
49 		: "cc", "memory");
50 	return _rc;
51 }
52 
53 void store_ipl_parmblock(void)
54 {
55 	int rc;
56 
57 	rc = __diag308(DIAG308_STORE, &ipl_block);
58 	if (rc == DIAG308_RC_OK &&
59 	    ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
60 		ipl_block_valid = 1;
61 }
62 
63 bool is_ipl_block_dump(void)
64 {
65 	if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP &&
66 	    ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP)
67 		return true;
68 	if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME &&
69 	    ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP)
70 		return true;
71 	return false;
72 }
73 
74 static size_t scpdata_length(const u8 *buf, size_t count)
75 {
76 	while (count) {
77 		if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
78 			break;
79 		count--;
80 	}
81 	return count;
82 }
83 
84 static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
85 					  const struct ipl_parameter_block *ipb)
86 {
87 	const __u8 *scp_data;
88 	__u32 scp_data_len;
89 	int has_lowercase;
90 	size_t count = 0;
91 	size_t i;
92 
93 	switch (ipb->pb0_hdr.pbt) {
94 	case IPL_PBT_FCP:
95 		scp_data_len = ipb->fcp.scp_data_len;
96 		scp_data = ipb->fcp.scp_data;
97 		break;
98 	case IPL_PBT_NVME:
99 		scp_data_len = ipb->nvme.scp_data_len;
100 		scp_data = ipb->nvme.scp_data;
101 		break;
102 	default:
103 		goto out;
104 	}
105 
106 	count = min(size - 1, scpdata_length(scp_data, scp_data_len));
107 	if (!count)
108 		goto out;
109 
110 	has_lowercase = 0;
111 	for (i = 0; i < count; i++) {
112 		if (!isascii(scp_data[i])) {
113 			count = 0;
114 			goto out;
115 		}
116 		if (!has_lowercase && islower(scp_data[i]))
117 			has_lowercase = 1;
118 	}
119 
120 	if (has_lowercase)
121 		memcpy(dest, scp_data, count);
122 	else
123 		for (i = 0; i < count; i++)
124 			dest[i] = tolower(scp_data[i]);
125 out:
126 	dest[count] = '\0';
127 	return count;
128 }
129 
130 static void append_ipl_block_parm(void)
131 {
132 	char *parm, *delim;
133 	size_t len, rc = 0;
134 
135 	len = strlen(early_command_line);
136 
137 	delim = early_command_line + len;    /* '\0' character position */
138 	parm = early_command_line + len + 1; /* append right after '\0' */
139 
140 	switch (ipl_block.pb0_hdr.pbt) {
141 	case IPL_PBT_CCW:
142 		rc = ipl_block_get_ascii_vmparm(
143 			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
144 		break;
145 	case IPL_PBT_FCP:
146 	case IPL_PBT_NVME:
147 		rc = ipl_block_get_ascii_scpdata(
148 			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
149 		break;
150 	}
151 	if (rc) {
152 		if (*parm == '=')
153 			memmove(early_command_line, parm + 1, rc);
154 		else
155 			*delim = ' '; /* replace '\0' with space */
156 	}
157 }
158 
159 static inline int has_ebcdic_char(const char *str)
160 {
161 	int i;
162 
163 	for (i = 0; str[i]; i++)
164 		if (str[i] & 0x80)
165 			return 1;
166 	return 0;
167 }
168 
169 void setup_boot_command_line(void)
170 {
171 	parmarea.command_line[ARCH_COMMAND_LINE_SIZE - 1] = 0;
172 	/* convert arch command line to ascii if necessary */
173 	if (has_ebcdic_char(parmarea.command_line))
174 		EBCASC(parmarea.command_line, ARCH_COMMAND_LINE_SIZE);
175 	/* copy arch command line */
176 	strcpy(early_command_line, strim(parmarea.command_line));
177 
178 	/* append IPL PARM data to the boot command line */
179 	if (!is_prot_virt_guest() && ipl_block_valid)
180 		append_ipl_block_parm();
181 }
182 
183 static void modify_facility(unsigned long nr, bool clear)
184 {
185 	if (clear)
186 		__clear_facility(nr, stfle_fac_list);
187 	else
188 		__set_facility(nr, stfle_fac_list);
189 }
190 
191 static void check_cleared_facilities(void)
192 {
193 	unsigned long als[] = { FACILITIES_ALS };
194 	int i;
195 
196 	for (i = 0; i < ARRAY_SIZE(als); i++) {
197 		if ((stfle_fac_list[i] & als[i]) != als[i]) {
198 			sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n");
199 			print_missing_facilities();
200 			break;
201 		}
202 	}
203 }
204 
205 static void modify_fac_list(char *str)
206 {
207 	unsigned long val, endval;
208 	char *endp;
209 	bool clear;
210 
211 	while (*str) {
212 		clear = false;
213 		if (*str == '!') {
214 			clear = true;
215 			str++;
216 		}
217 		val = simple_strtoull(str, &endp, 0);
218 		if (str == endp)
219 			break;
220 		str = endp;
221 		if (*str == '-') {
222 			str++;
223 			endval = simple_strtoull(str, &endp, 0);
224 			if (str == endp)
225 				break;
226 			str = endp;
227 			while (val <= endval) {
228 				modify_facility(val, clear);
229 				val++;
230 			}
231 		} else {
232 			modify_facility(val, clear);
233 		}
234 		if (*str != ',')
235 			break;
236 		str++;
237 	}
238 	check_cleared_facilities();
239 }
240 
241 static char command_line_buf[COMMAND_LINE_SIZE];
242 void parse_boot_command_line(void)
243 {
244 	char *param, *val;
245 	bool enabled;
246 	char *args;
247 	int rc;
248 
249 	kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE);
250 	args = strcpy(command_line_buf, early_command_line);
251 	while (*args) {
252 		args = next_arg(args, &param, &val);
253 
254 		if (!strcmp(param, "mem") && val)
255 			memory_limit = round_down(memparse(val, NULL), PAGE_SIZE);
256 
257 		if (!strcmp(param, "vmalloc") && val) {
258 			vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE);
259 			vmalloc_size_set = 1;
260 		}
261 
262 		if (!strcmp(param, "dfltcc") && val) {
263 			if (!strcmp(val, "off"))
264 				zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED;
265 			else if (!strcmp(val, "on"))
266 				zlib_dfltcc_support = ZLIB_DFLTCC_FULL;
267 			else if (!strcmp(val, "def_only"))
268 				zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY;
269 			else if (!strcmp(val, "inf_only"))
270 				zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY;
271 			else if (!strcmp(val, "always"))
272 				zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG;
273 		}
274 
275 		if (!strcmp(param, "noexec")) {
276 			rc = kstrtobool(val, &enabled);
277 			if (!rc && !enabled)
278 				noexec_disabled = 1;
279 		}
280 
281 		if (!strcmp(param, "facilities") && val)
282 			modify_fac_list(val);
283 
284 		if (!strcmp(param, "nokaslr"))
285 			kaslr_enabled = 0;
286 
287 #if IS_ENABLED(CONFIG_KVM)
288 		if (!strcmp(param, "prot_virt")) {
289 			rc = kstrtobool(val, &enabled);
290 			if (!rc && enabled)
291 				prot_virt_host = 1;
292 		}
293 #endif
294 	}
295 }
296