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