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 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 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 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 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 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 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 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 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 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 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]; 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, ¶m, &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