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