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