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/abs_lowcore.h> 7 #include <asm/page-states.h> 8 #include <asm/machine.h> 9 #include <asm/ebcdic.h> 10 #include <asm/sclp.h> 11 #include <asm/sections.h> 12 #include <asm/boot_data.h> 13 #include <asm/facility.h> 14 #include <asm/setup.h> 15 #include <asm/uv.h> 16 #include "boot.h" 17 18 struct parmarea parmarea __section(".parmarea") = { 19 .kernel_version = (unsigned long)kernel_version, 20 .max_command_line_size = COMMAND_LINE_SIZE, 21 .command_line = "root=/dev/ram0 ro", 22 }; 23 24 char __bootdata(early_command_line)[COMMAND_LINE_SIZE]; 25 26 unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL; 27 struct ipl_parameter_block __bootdata_preserved(ipl_block); 28 int __bootdata_preserved(ipl_block_valid); 29 int __bootdata_preserved(__kaslr_enabled); 30 int __bootdata_preserved(cmma_flag) = 1; 31 32 unsigned long vmalloc_size = VMALLOC_DEFAULT_SIZE; 33 unsigned long memory_limit; 34 int vmalloc_size_set; 35 36 static inline int __diag308(unsigned long subcode, void *addr) 37 { 38 union register_pair r1 = { .even = (unsigned long)addr, .odd = 0 }; 39 40 asm_inline volatile( 41 " diag %[r1],%[subcode],0x308\n" 42 "0:\n" 43 EX_TABLE(0b, 0b) 44 : [r1] "+d" (r1.pair) 45 : [subcode] "d" (subcode) 46 : "cc", "memory"); 47 return r1.odd; 48 } 49 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 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 if (ipl_block.pb0_hdr.pbt == IPL_PBT_ECKD && 69 ipl_block.eckd.opt == IPL_PB0_ECKD_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 case IPL_PBT_ECKD: 103 scp_data_len = ipb->eckd.scp_data_len; 104 scp_data = ipb->eckd.scp_data; 105 break; 106 107 default: 108 goto out; 109 } 110 111 count = min(size - 1, scpdata_length(scp_data, scp_data_len)); 112 if (!count) 113 goto out; 114 115 has_lowercase = 0; 116 for (i = 0; i < count; i++) { 117 if (!isascii(scp_data[i])) { 118 count = 0; 119 goto out; 120 } 121 if (!has_lowercase && islower(scp_data[i])) 122 has_lowercase = 1; 123 } 124 125 if (has_lowercase) 126 memcpy(dest, scp_data, count); 127 else 128 for (i = 0; i < count; i++) 129 dest[i] = tolower(scp_data[i]); 130 out: 131 dest[count] = '\0'; 132 return count; 133 } 134 135 static void append_ipl_block_parm(void) 136 { 137 char *parm, *delim; 138 size_t len, rc = 0; 139 140 len = strlen(early_command_line); 141 142 delim = early_command_line + len; /* '\0' character position */ 143 parm = early_command_line + len + 1; /* append right after '\0' */ 144 145 switch (ipl_block.pb0_hdr.pbt) { 146 case IPL_PBT_CCW: 147 rc = ipl_block_get_ascii_vmparm( 148 parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 149 break; 150 case IPL_PBT_FCP: 151 case IPL_PBT_NVME: 152 case IPL_PBT_ECKD: 153 rc = ipl_block_get_ascii_scpdata( 154 parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 155 break; 156 } 157 if (rc) { 158 if (*parm == '=') 159 memmove(early_command_line, parm + 1, rc); 160 else 161 *delim = ' '; /* replace '\0' with space */ 162 } 163 } 164 165 static inline int has_ebcdic_char(const char *str) 166 { 167 int i; 168 169 for (i = 0; str[i]; i++) 170 if (str[i] & 0x80) 171 return 1; 172 return 0; 173 } 174 175 void setup_boot_command_line(void) 176 { 177 parmarea.command_line[COMMAND_LINE_SIZE - 1] = 0; 178 /* convert arch command line to ascii if necessary */ 179 if (has_ebcdic_char(parmarea.command_line)) 180 EBCASC(parmarea.command_line, COMMAND_LINE_SIZE); 181 /* copy arch command line */ 182 strscpy(early_command_line, strim(parmarea.command_line)); 183 184 /* append IPL PARM data to the boot command line */ 185 if (!is_prot_virt_guest() && ipl_block_valid) 186 append_ipl_block_parm(); 187 } 188 189 static void modify_facility(unsigned long nr, bool clear) 190 { 191 if (clear) 192 __clear_facility(nr, stfle_fac_list); 193 else 194 __set_facility(nr, stfle_fac_list); 195 } 196 197 static void check_cleared_facilities(void) 198 { 199 unsigned long als[] = { FACILITIES_ALS }; 200 int i; 201 202 for (i = 0; i < ARRAY_SIZE(als); i++) { 203 if ((stfle_fac_list[i] & als[i]) != als[i]) { 204 boot_emerg("The Linux kernel requires facilities cleared via command line option\n"); 205 print_missing_facilities(); 206 break; 207 } 208 } 209 } 210 211 static void modify_fac_list(char *str) 212 { 213 unsigned long val, endval; 214 char *endp; 215 bool clear; 216 217 while (*str) { 218 clear = false; 219 if (*str == '!') { 220 clear = true; 221 str++; 222 } 223 val = simple_strtoull(str, &endp, 0); 224 if (str == endp) 225 break; 226 str = endp; 227 if (*str == '-') { 228 str++; 229 endval = simple_strtoull(str, &endp, 0); 230 if (str == endp) 231 break; 232 str = endp; 233 while (val <= endval) { 234 modify_facility(val, clear); 235 val++; 236 } 237 } else { 238 modify_facility(val, clear); 239 } 240 if (*str != ',') 241 break; 242 str++; 243 } 244 check_cleared_facilities(); 245 } 246 247 static char command_line_buf[COMMAND_LINE_SIZE]; 248 void parse_boot_command_line(void) 249 { 250 char *param, *val; 251 bool enabled; 252 char *args; 253 int rc; 254 255 __kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE); 256 strscpy(command_line_buf, early_command_line); 257 args = command_line_buf; 258 while (*args) { 259 args = next_arg(args, ¶m, &val); 260 261 if (!strcmp(param, "mem") && val) 262 memory_limit = round_down(memparse(val, NULL), PAGE_SIZE); 263 264 if (!strcmp(param, "vmalloc") && val) { 265 vmalloc_size = round_up(memparse(val, NULL), _SEGMENT_SIZE); 266 vmalloc_size_set = 1; 267 } 268 269 if (!strcmp(param, "dfltcc") && val) { 270 if (!strcmp(val, "off")) 271 zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED; 272 else if (!strcmp(val, "on")) 273 zlib_dfltcc_support = ZLIB_DFLTCC_FULL; 274 else if (!strcmp(val, "def_only")) 275 zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY; 276 else if (!strcmp(val, "inf_only")) 277 zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY; 278 else if (!strcmp(val, "always")) 279 zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG; 280 } 281 282 if (!strcmp(param, "facilities") && val) 283 modify_fac_list(val); 284 285 if (!strcmp(param, "debug-alternative")) 286 alt_debug_setup(val); 287 288 if (!strcmp(param, "nokaslr")) 289 __kaslr_enabled = 0; 290 291 if (!strcmp(param, "cmma")) { 292 rc = kstrtobool(val, &enabled); 293 if (!rc && !enabled) 294 cmma_flag = 0; 295 } 296 297 #if IS_ENABLED(CONFIG_KVM) 298 if (!strcmp(param, "prot_virt")) { 299 rc = kstrtobool(val, &enabled); 300 if (!rc && enabled) 301 prot_virt_host = 1; 302 } 303 #endif 304 if (!strcmp(param, "relocate_lowcore") && test_facility(193)) 305 set_machine_feature(MFEATURE_LOWCORE); 306 if (!strcmp(param, "earlyprintk")) 307 boot_earlyprintk = true; 308 if (!strcmp(param, "debug")) 309 boot_console_loglevel = CONSOLE_LOGLEVEL_DEBUG; 310 if (!strcmp(param, "bootdebug")) { 311 bootdebug = true; 312 if (val) 313 strscpy(bootdebug_filter, val); 314 } 315 if (!strcmp(param, "quiet")) 316 boot_console_loglevel = CONSOLE_LOGLEVEL_QUIET; 317 if (!strcmp(param, "ignore_loglevel")) 318 boot_ignore_loglevel = true; 319 if (!strcmp(param, "loglevel")) { 320 boot_console_loglevel = simple_strtoull(val, NULL, 10); 321 if (boot_console_loglevel < CONSOLE_LOGLEVEL_MIN) 322 boot_console_loglevel = CONSOLE_LOGLEVEL_MIN; 323 } 324 } 325 } 326