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