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