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