Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Copyright IBM Corp. 2007, 2009 |
| 4 | * Author(s): Hongjie Yang <hongjie@us.ibm.com>, |
| 5 | * Heiko Carstens <heiko.carstens@de.ibm.com> |
| 6 | */ |
| 7 | |
| 8 | #define KMSG_COMPONENT "setup" |
| 9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
| 10 | |
| 11 | #include <linux/compiler.h> |
| 12 | #include <linux/init.h> |
| 13 | #include <linux/errno.h> |
| 14 | #include <linux/string.h> |
| 15 | #include <linux/ctype.h> |
| 16 | #include <linux/lockdep.h> |
| 17 | #include <linux/extable.h> |
| 18 | #include <linux/pfn.h> |
| 19 | #include <linux/uaccess.h> |
| 20 | #include <linux/kernel.h> |
| 21 | #include <asm/diag.h> |
| 22 | #include <asm/ebcdic.h> |
| 23 | #include <asm/ipl.h> |
| 24 | #include <asm/lowcore.h> |
| 25 | #include <asm/processor.h> |
| 26 | #include <asm/sections.h> |
| 27 | #include <asm/setup.h> |
| 28 | #include <asm/sysinfo.h> |
| 29 | #include <asm/cpcmd.h> |
| 30 | #include <asm/sclp.h> |
| 31 | #include <asm/facility.h> |
| 32 | #include "entry.h" |
| 33 | |
| 34 | static void __init setup_boot_command_line(void); |
| 35 | |
| 36 | /* |
| 37 | * Initialize storage key for kernel pages |
| 38 | */ |
| 39 | static noinline __init void init_kernel_storage_key(void) |
| 40 | { |
| 41 | #if PAGE_DEFAULT_KEY |
| 42 | unsigned long end_pfn, init_pfn; |
| 43 | |
| 44 | end_pfn = PFN_UP(__pa(_end)); |
| 45 | |
| 46 | for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++) |
| 47 | page_set_storage_key(init_pfn << PAGE_SHIFT, |
| 48 | PAGE_DEFAULT_KEY, 0); |
| 49 | #endif |
| 50 | } |
| 51 | |
| 52 | static __initdata char sysinfo_page[PAGE_SIZE] __aligned(PAGE_SIZE); |
| 53 | |
| 54 | static noinline __init void detect_machine_type(void) |
| 55 | { |
| 56 | struct sysinfo_3_2_2 *vmms = (struct sysinfo_3_2_2 *)&sysinfo_page; |
| 57 | |
| 58 | /* Check current-configuration-level */ |
| 59 | if (stsi(NULL, 0, 0, 0) <= 2) { |
| 60 | S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR; |
| 61 | return; |
| 62 | } |
| 63 | /* Get virtual-machine cpu information. */ |
| 64 | if (stsi(vmms, 3, 2, 2) || !vmms->count) |
| 65 | return; |
| 66 | |
| 67 | /* Running under KVM? If not we assume z/VM */ |
| 68 | if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3)) |
| 69 | S390_lowcore.machine_flags |= MACHINE_FLAG_KVM; |
| 70 | else |
| 71 | S390_lowcore.machine_flags |= MACHINE_FLAG_VM; |
| 72 | } |
| 73 | |
| 74 | /* Remove leading, trailing and double whitespace. */ |
| 75 | static inline void strim_all(char *str) |
| 76 | { |
| 77 | char *s; |
| 78 | |
| 79 | s = strim(str); |
| 80 | if (s != str) |
| 81 | memmove(str, s, strlen(s)); |
| 82 | while (*str) { |
| 83 | if (!isspace(*str++)) |
| 84 | continue; |
| 85 | if (isspace(*str)) { |
| 86 | s = skip_spaces(str); |
| 87 | memmove(str, s, strlen(s) + 1); |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | static noinline __init void setup_arch_string(void) |
| 93 | { |
| 94 | struct sysinfo_1_1_1 *mach = (struct sysinfo_1_1_1 *)&sysinfo_page; |
| 95 | struct sysinfo_3_2_2 *vm = (struct sysinfo_3_2_2 *)&sysinfo_page; |
| 96 | char mstr[80], hvstr[17]; |
| 97 | |
| 98 | if (stsi(mach, 1, 1, 1)) |
| 99 | return; |
| 100 | EBCASC(mach->manufacturer, sizeof(mach->manufacturer)); |
| 101 | EBCASC(mach->type, sizeof(mach->type)); |
| 102 | EBCASC(mach->model, sizeof(mach->model)); |
| 103 | EBCASC(mach->model_capacity, sizeof(mach->model_capacity)); |
| 104 | sprintf(mstr, "%-16.16s %-4.4s %-16.16s %-16.16s", |
| 105 | mach->manufacturer, mach->type, |
| 106 | mach->model, mach->model_capacity); |
| 107 | strim_all(mstr); |
| 108 | if (stsi(vm, 3, 2, 2) == 0 && vm->count) { |
| 109 | EBCASC(vm->vm[0].cpi, sizeof(vm->vm[0].cpi)); |
| 110 | sprintf(hvstr, "%-16.16s", vm->vm[0].cpi); |
| 111 | strim_all(hvstr); |
| 112 | } else { |
| 113 | sprintf(hvstr, "%s", |
| 114 | MACHINE_IS_LPAR ? "LPAR" : |
| 115 | MACHINE_IS_VM ? "z/VM" : |
| 116 | MACHINE_IS_KVM ? "KVM" : "unknown"); |
| 117 | } |
| 118 | dump_stack_set_arch_desc("%s (%s)", mstr, hvstr); |
| 119 | } |
| 120 | |
| 121 | static __init void setup_topology(void) |
| 122 | { |
| 123 | int max_mnest; |
| 124 | |
| 125 | if (!test_facility(11)) |
| 126 | return; |
| 127 | S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY; |
| 128 | for (max_mnest = 6; max_mnest > 1; max_mnest--) { |
| 129 | if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0) |
| 130 | break; |
| 131 | } |
| 132 | topology_max_mnest = max_mnest; |
| 133 | } |
| 134 | |
| 135 | static void early_pgm_check_handler(void) |
| 136 | { |
| 137 | const struct exception_table_entry *fixup; |
| 138 | unsigned long cr0, cr0_new; |
| 139 | unsigned long addr; |
| 140 | |
| 141 | addr = S390_lowcore.program_old_psw.addr; |
| 142 | fixup = search_exception_tables(addr); |
| 143 | if (!fixup) |
| 144 | disabled_wait(0); |
| 145 | /* Disable low address protection before storing into lowcore. */ |
| 146 | __ctl_store(cr0, 0, 0); |
| 147 | cr0_new = cr0 & ~(1UL << 28); |
| 148 | __ctl_load(cr0_new, 0, 0); |
| 149 | S390_lowcore.program_old_psw.addr = extable_fixup(fixup); |
| 150 | __ctl_load(cr0, 0, 0); |
| 151 | } |
| 152 | |
| 153 | static noinline __init void setup_lowcore_early(void) |
| 154 | { |
| 155 | psw_t psw; |
| 156 | |
| 157 | psw.mask = PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA; |
| 158 | psw.addr = (unsigned long) s390_base_ext_handler; |
| 159 | S390_lowcore.external_new_psw = psw; |
| 160 | psw.addr = (unsigned long) s390_base_pgm_handler; |
| 161 | S390_lowcore.program_new_psw = psw; |
| 162 | s390_base_pgm_handler_fn = early_pgm_check_handler; |
| 163 | S390_lowcore.preempt_count = INIT_PREEMPT_COUNT; |
| 164 | } |
| 165 | |
| 166 | static noinline __init void setup_facility_list(void) |
| 167 | { |
| 168 | stfle(S390_lowcore.stfle_fac_list, |
| 169 | ARRAY_SIZE(S390_lowcore.stfle_fac_list)); |
| 170 | memcpy(S390_lowcore.alt_stfle_fac_list, |
| 171 | S390_lowcore.stfle_fac_list, |
| 172 | sizeof(S390_lowcore.alt_stfle_fac_list)); |
| 173 | if (!IS_ENABLED(CONFIG_KERNEL_NOBP)) |
| 174 | __clear_facility(82, S390_lowcore.alt_stfle_fac_list); |
| 175 | } |
| 176 | |
| 177 | static __init void detect_diag9c(void) |
| 178 | { |
| 179 | unsigned int cpu_address; |
| 180 | int rc; |
| 181 | |
| 182 | cpu_address = stap(); |
| 183 | diag_stat_inc(DIAG_STAT_X09C); |
| 184 | asm volatile( |
| 185 | " diag %2,0,0x9c\n" |
| 186 | "0: la %0,0\n" |
| 187 | "1:\n" |
| 188 | EX_TABLE(0b,1b) |
| 189 | : "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc"); |
| 190 | if (!rc) |
| 191 | S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG9C; |
| 192 | } |
| 193 | |
| 194 | static __init void detect_diag44(void) |
| 195 | { |
| 196 | int rc; |
| 197 | |
| 198 | diag_stat_inc(DIAG_STAT_X044); |
| 199 | asm volatile( |
| 200 | " diag 0,0,0x44\n" |
| 201 | "0: la %0,0\n" |
| 202 | "1:\n" |
| 203 | EX_TABLE(0b,1b) |
| 204 | : "=d" (rc) : "0" (-EOPNOTSUPP) : "cc"); |
| 205 | if (!rc) |
| 206 | S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG44; |
| 207 | } |
| 208 | |
| 209 | static __init void detect_machine_facilities(void) |
| 210 | { |
| 211 | if (test_facility(8)) { |
| 212 | S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT1; |
| 213 | __ctl_set_bit(0, 23); |
| 214 | } |
| 215 | if (test_facility(78)) |
| 216 | S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT2; |
| 217 | if (test_facility(3)) |
| 218 | S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE; |
| 219 | if (test_facility(50) && test_facility(73)) { |
| 220 | S390_lowcore.machine_flags |= MACHINE_FLAG_TE; |
| 221 | __ctl_set_bit(0, 55); |
| 222 | } |
| 223 | if (test_facility(51)) |
| 224 | S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC; |
| 225 | if (test_facility(129)) { |
| 226 | S390_lowcore.machine_flags |= MACHINE_FLAG_VX; |
| 227 | __ctl_set_bit(0, 17); |
| 228 | } |
| 229 | if (test_facility(130)) { |
| 230 | S390_lowcore.machine_flags |= MACHINE_FLAG_NX; |
| 231 | __ctl_set_bit(0, 20); |
| 232 | } |
| 233 | if (test_facility(133)) |
| 234 | S390_lowcore.machine_flags |= MACHINE_FLAG_GS; |
| 235 | if (test_facility(139) && (tod_clock_base[1] & 0x80)) { |
| 236 | /* Enabled signed clock comparator comparisons */ |
| 237 | S390_lowcore.machine_flags |= MACHINE_FLAG_SCC; |
| 238 | clock_comparator_max = -1ULL >> 1; |
| 239 | __ctl_set_bit(0, 53); |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | static inline void save_vector_registers(void) |
| 244 | { |
| 245 | #ifdef CONFIG_CRASH_DUMP |
| 246 | if (test_facility(129)) |
| 247 | save_vx_regs(boot_cpu_vector_save_area); |
| 248 | #endif |
| 249 | } |
| 250 | |
| 251 | static int __init disable_vector_extension(char *str) |
| 252 | { |
| 253 | S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX; |
| 254 | __ctl_clear_bit(0, 17); |
| 255 | return 0; |
| 256 | } |
| 257 | early_param("novx", disable_vector_extension); |
| 258 | |
| 259 | static int __init noexec_setup(char *str) |
| 260 | { |
| 261 | bool enabled; |
| 262 | int rc; |
| 263 | |
| 264 | rc = kstrtobool(str, &enabled); |
| 265 | if (!rc && !enabled) { |
| 266 | /* Disable no-execute support */ |
| 267 | S390_lowcore.machine_flags &= ~MACHINE_FLAG_NX; |
| 268 | __ctl_clear_bit(0, 20); |
| 269 | } |
| 270 | return rc; |
| 271 | } |
| 272 | early_param("noexec", noexec_setup); |
| 273 | |
| 274 | static int __init cad_setup(char *str) |
| 275 | { |
| 276 | bool enabled; |
| 277 | int rc; |
| 278 | |
| 279 | rc = kstrtobool(str, &enabled); |
| 280 | if (!rc && enabled && test_facility(128)) |
| 281 | /* Enable problem state CAD. */ |
| 282 | __ctl_set_bit(2, 3); |
| 283 | return rc; |
| 284 | } |
| 285 | early_param("cad", cad_setup); |
| 286 | |
| 287 | /* Set up boot command line */ |
| 288 | static void __init append_to_cmdline(size_t (*ipl_data)(char *, size_t)) |
| 289 | { |
| 290 | char *parm, *delim; |
| 291 | size_t rc, len; |
| 292 | |
| 293 | len = strlen(boot_command_line); |
| 294 | |
| 295 | delim = boot_command_line + len; /* '\0' character position */ |
| 296 | parm = boot_command_line + len + 1; /* append right after '\0' */ |
| 297 | |
| 298 | rc = ipl_data(parm, COMMAND_LINE_SIZE - len - 1); |
| 299 | if (rc) { |
| 300 | if (*parm == '=') |
| 301 | memmove(boot_command_line, parm + 1, rc); |
| 302 | else |
| 303 | *delim = ' '; /* replace '\0' with space */ |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | static inline int has_ebcdic_char(const char *str) |
| 308 | { |
| 309 | int i; |
| 310 | |
| 311 | for (i = 0; str[i]; i++) |
| 312 | if (str[i] & 0x80) |
| 313 | return 1; |
| 314 | return 0; |
| 315 | } |
| 316 | |
| 317 | static void __init setup_boot_command_line(void) |
| 318 | { |
| 319 | COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0; |
| 320 | /* convert arch command line to ascii if necessary */ |
| 321 | if (has_ebcdic_char(COMMAND_LINE)) |
| 322 | EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE); |
| 323 | /* copy arch command line */ |
| 324 | strlcpy(boot_command_line, strstrip(COMMAND_LINE), |
| 325 | ARCH_COMMAND_LINE_SIZE); |
| 326 | |
| 327 | /* append IPL PARM data to the boot command line */ |
| 328 | if (MACHINE_IS_VM) |
| 329 | append_to_cmdline(append_ipl_vmparm); |
| 330 | |
| 331 | append_to_cmdline(append_ipl_scpdata); |
| 332 | } |
| 333 | |
| 334 | static void __init check_image_bootable(void) |
| 335 | { |
| 336 | if (!memcmp(EP_STRING, (void *)EP_OFFSET, strlen(EP_STRING))) |
| 337 | return; |
| 338 | |
| 339 | sclp_early_printk("Linux kernel boot failure: An attempt to boot a vmlinux ELF image failed.\n"); |
| 340 | sclp_early_printk("This image does not contain all parts necessary for starting up. Use\n"); |
| 341 | sclp_early_printk("bzImage or arch/s390/boot/compressed/vmlinux instead.\n"); |
| 342 | disabled_wait(0xbadb007); |
| 343 | } |
| 344 | |
| 345 | void __init startup_init(void) |
| 346 | { |
| 347 | check_image_bootable(); |
| 348 | time_early_init(); |
| 349 | init_kernel_storage_key(); |
| 350 | lockdep_off(); |
| 351 | setup_lowcore_early(); |
| 352 | setup_facility_list(); |
| 353 | detect_machine_type(); |
| 354 | setup_arch_string(); |
| 355 | ipl_store_parameters(); |
| 356 | setup_boot_command_line(); |
| 357 | detect_diag9c(); |
| 358 | detect_diag44(); |
| 359 | detect_machine_facilities(); |
| 360 | save_vector_registers(); |
| 361 | setup_topology(); |
| 362 | sclp_early_detect(); |
| 363 | lockdep_on(); |
| 364 | } |