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