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/uv.h> 11 #include "boot.h" 12 13 char __bootdata(early_command_line)[COMMAND_LINE_SIZE]; 14 struct ipl_parameter_block __bootdata_preserved(ipl_block); 15 int __bootdata_preserved(ipl_block_valid); 16 17 unsigned long __bootdata(memory_end); 18 int __bootdata(memory_end_set); 19 int __bootdata(noexec_disabled); 20 21 int kaslr_enabled __section(.data); 22 23 static inline int __diag308(unsigned long subcode, void *addr) 24 { 25 register unsigned long _addr asm("0") = (unsigned long)addr; 26 register unsigned long _rc asm("1") = 0; 27 unsigned long reg1, reg2; 28 psw_t old = S390_lowcore.program_new_psw; 29 30 asm volatile( 31 " epsw %0,%1\n" 32 " st %0,%[psw_pgm]\n" 33 " st %1,%[psw_pgm]+4\n" 34 " larl %0,1f\n" 35 " stg %0,%[psw_pgm]+8\n" 36 " diag %[addr],%[subcode],0x308\n" 37 "1: nopr %%r7\n" 38 : "=&d" (reg1), "=&a" (reg2), 39 [psw_pgm] "=Q" (S390_lowcore.program_new_psw), 40 [addr] "+d" (_addr), "+d" (_rc) 41 : [subcode] "d" (subcode) 42 : "cc", "memory"); 43 S390_lowcore.program_new_psw = old; 44 return _rc; 45 } 46 47 void store_ipl_parmblock(void) 48 { 49 int rc; 50 51 uv_set_shared(__pa(&ipl_block)); 52 rc = __diag308(DIAG308_STORE, &ipl_block); 53 uv_remove_shared(__pa(&ipl_block)); 54 if (rc == DIAG308_RC_OK && 55 ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION) 56 ipl_block_valid = 1; 57 } 58 59 static size_t scpdata_length(const u8 *buf, size_t count) 60 { 61 while (count) { 62 if (buf[count - 1] != '\0' && buf[count - 1] != ' ') 63 break; 64 count--; 65 } 66 return count; 67 } 68 69 static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size, 70 const struct ipl_parameter_block *ipb) 71 { 72 size_t count; 73 size_t i; 74 int has_lowercase; 75 76 count = min(size - 1, scpdata_length(ipb->fcp.scp_data, 77 ipb->fcp.scp_data_len)); 78 if (!count) 79 goto out; 80 81 has_lowercase = 0; 82 for (i = 0; i < count; i++) { 83 if (!isascii(ipb->fcp.scp_data[i])) { 84 count = 0; 85 goto out; 86 } 87 if (!has_lowercase && islower(ipb->fcp.scp_data[i])) 88 has_lowercase = 1; 89 } 90 91 if (has_lowercase) 92 memcpy(dest, ipb->fcp.scp_data, count); 93 else 94 for (i = 0; i < count; i++) 95 dest[i] = tolower(ipb->fcp.scp_data[i]); 96 out: 97 dest[count] = '\0'; 98 return count; 99 } 100 101 static void append_ipl_block_parm(void) 102 { 103 char *parm, *delim; 104 size_t len, rc = 0; 105 106 len = strlen(early_command_line); 107 108 delim = early_command_line + len; /* '\0' character position */ 109 parm = early_command_line + len + 1; /* append right after '\0' */ 110 111 switch (ipl_block.pb0_hdr.pbt) { 112 case IPL_PBT_CCW: 113 rc = ipl_block_get_ascii_vmparm( 114 parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 115 break; 116 case IPL_PBT_FCP: 117 rc = ipl_block_get_ascii_scpdata( 118 parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 119 break; 120 } 121 if (rc) { 122 if (*parm == '=') 123 memmove(early_command_line, parm + 1, rc); 124 else 125 *delim = ' '; /* replace '\0' with space */ 126 } 127 } 128 129 static inline int has_ebcdic_char(const char *str) 130 { 131 int i; 132 133 for (i = 0; str[i]; i++) 134 if (str[i] & 0x80) 135 return 1; 136 return 0; 137 } 138 139 void setup_boot_command_line(void) 140 { 141 COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0; 142 /* convert arch command line to ascii if necessary */ 143 if (has_ebcdic_char(COMMAND_LINE)) 144 EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE); 145 /* copy arch command line */ 146 strcpy(early_command_line, strim(COMMAND_LINE)); 147 148 /* append IPL PARM data to the boot command line */ 149 if (!is_prot_virt_guest() && ipl_block_valid) 150 append_ipl_block_parm(); 151 } 152 153 static void modify_facility(unsigned long nr, bool clear) 154 { 155 if (clear) 156 __clear_facility(nr, S390_lowcore.stfle_fac_list); 157 else 158 __set_facility(nr, S390_lowcore.stfle_fac_list); 159 } 160 161 static void check_cleared_facilities(void) 162 { 163 unsigned long als[] = { FACILITIES_ALS }; 164 int i; 165 166 for (i = 0; i < ARRAY_SIZE(als); i++) { 167 if ((S390_lowcore.stfle_fac_list[i] & als[i]) != als[i]) { 168 sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n"); 169 print_missing_facilities(); 170 break; 171 } 172 } 173 } 174 175 static void modify_fac_list(char *str) 176 { 177 unsigned long val, endval; 178 char *endp; 179 bool clear; 180 181 while (*str) { 182 clear = false; 183 if (*str == '!') { 184 clear = true; 185 str++; 186 } 187 val = simple_strtoull(str, &endp, 0); 188 if (str == endp) 189 break; 190 str = endp; 191 if (*str == '-') { 192 str++; 193 endval = simple_strtoull(str, &endp, 0); 194 if (str == endp) 195 break; 196 str = endp; 197 while (val <= endval) { 198 modify_facility(val, clear); 199 val++; 200 } 201 } else { 202 modify_facility(val, clear); 203 } 204 if (*str != ',') 205 break; 206 str++; 207 } 208 check_cleared_facilities(); 209 } 210 211 static char command_line_buf[COMMAND_LINE_SIZE] __section(.data); 212 void parse_boot_command_line(void) 213 { 214 char *param, *val; 215 bool enabled; 216 char *args; 217 int rc; 218 219 kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE); 220 args = strcpy(command_line_buf, early_command_line); 221 while (*args) { 222 args = next_arg(args, ¶m, &val); 223 224 if (!strcmp(param, "mem")) { 225 memory_end = memparse(val, NULL); 226 memory_end_set = 1; 227 } 228 229 if (!strcmp(param, "noexec")) { 230 rc = kstrtobool(val, &enabled); 231 if (!rc && !enabled) 232 noexec_disabled = 1; 233 } 234 235 if (!strcmp(param, "facilities")) 236 modify_fac_list(val); 237 238 if (!strcmp(param, "nokaslr")) 239 kaslr_enabled = 0; 240 } 241 } 242 243 void setup_memory_end(void) 244 { 245 #ifdef CONFIG_CRASH_DUMP 246 if (OLDMEM_BASE) { 247 kaslr_enabled = 0; 248 } else if (ipl_block_valid && 249 ipl_block.pb0_hdr.pbt == IPL_PBT_FCP && 250 ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) { 251 kaslr_enabled = 0; 252 if (!sclp_early_get_hsa_size(&memory_end) && memory_end) 253 memory_end_set = 1; 254 } 255 #endif 256 } 257