linux/arch/s390/kernel/early.c
<<
>>
Prefs
   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 <asm/boot_data.h>
  33#include "entry.h"
  34
  35/*
  36 * Initialize storage key for kernel pages
  37 */
  38static noinline __init void init_kernel_storage_key(void)
  39{
  40#if PAGE_DEFAULT_KEY
  41        unsigned long end_pfn, init_pfn;
  42
  43        end_pfn = PFN_UP(__pa(_end));
  44
  45        for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++)
  46                page_set_storage_key(init_pfn << PAGE_SHIFT,
  47                                     PAGE_DEFAULT_KEY, 0);
  48#endif
  49}
  50
  51static __initdata char sysinfo_page[PAGE_SIZE] __aligned(PAGE_SIZE);
  52
  53static noinline __init void detect_machine_type(void)
  54{
  55        struct sysinfo_3_2_2 *vmms = (struct sysinfo_3_2_2 *)&sysinfo_page;
  56
  57        /* Check current-configuration-level */
  58        if (stsi(NULL, 0, 0, 0) <= 2) {
  59                S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR;
  60                return;
  61        }
  62        /* Get virtual-machine cpu information. */
  63        if (stsi(vmms, 3, 2, 2) || !vmms->count)
  64                return;
  65
  66        /* Detect known hypervisors */
  67        if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3))
  68                S390_lowcore.machine_flags |= MACHINE_FLAG_KVM;
  69        else if (!memcmp(vmms->vm[0].cpi, "\xa9\x61\xe5\xd4", 4))
  70                S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
  71}
  72
  73/* Remove leading, trailing and double whitespace. */
  74static inline void strim_all(char *str)
  75{
  76        char *s;
  77
  78        s = strim(str);
  79        if (s != str)
  80                memmove(str, s, strlen(s));
  81        while (*str) {
  82                if (!isspace(*str++))
  83                        continue;
  84                if (isspace(*str)) {
  85                        s = skip_spaces(str);
  86                        memmove(str, s, strlen(s) + 1);
  87                }
  88        }
  89}
  90
  91static noinline __init void setup_arch_string(void)
  92{
  93        struct sysinfo_1_1_1 *mach = (struct sysinfo_1_1_1 *)&sysinfo_page;
  94        struct sysinfo_3_2_2 *vm = (struct sysinfo_3_2_2 *)&sysinfo_page;
  95        char mstr[80], hvstr[17];
  96
  97        if (stsi(mach, 1, 1, 1))
  98                return;
  99        EBCASC(mach->manufacturer, sizeof(mach->manufacturer));
 100        EBCASC(mach->type, sizeof(mach->type));
 101        EBCASC(mach->model, sizeof(mach->model));
 102        EBCASC(mach->model_capacity, sizeof(mach->model_capacity));
 103        sprintf(mstr, "%-16.16s %-4.4s %-16.16s %-16.16s",
 104                mach->manufacturer, mach->type,
 105                mach->model, mach->model_capacity);
 106        strim_all(mstr);
 107        if (stsi(vm, 3, 2, 2) == 0 && vm->count) {
 108                EBCASC(vm->vm[0].cpi, sizeof(vm->vm[0].cpi));
 109                sprintf(hvstr, "%-16.16s", vm->vm[0].cpi);
 110                strim_all(hvstr);
 111        } else {
 112                sprintf(hvstr, "%s",
 113                        MACHINE_IS_LPAR ? "LPAR" :
 114                        MACHINE_IS_VM ? "z/VM" :
 115                        MACHINE_IS_KVM ? "KVM" : "unknown");
 116        }
 117        dump_stack_set_arch_desc("%s (%s)", mstr, hvstr);
 118}
 119
 120static __init void setup_topology(void)
 121{
 122        int max_mnest;
 123
 124        if (!test_facility(11))
 125                return;
 126        S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;
 127        for (max_mnest = 6; max_mnest > 1; max_mnest--) {
 128                if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0)
 129                        break;
 130        }
 131        topology_max_mnest = max_mnest;
 132}
 133
 134static void early_pgm_check_handler(void)
 135{
 136        const struct exception_table_entry *fixup;
 137        unsigned long cr0, cr0_new;
 138        unsigned long addr;
 139
 140        addr = S390_lowcore.program_old_psw.addr;
 141        fixup = s390_search_extables(addr);
 142        if (!fixup)
 143                disabled_wait(0);
 144        /* Disable low address protection before storing into lowcore. */
 145        __ctl_store(cr0, 0, 0);
 146        cr0_new = cr0 & ~(1UL << 28);
 147        __ctl_load(cr0_new, 0, 0);
 148        S390_lowcore.program_old_psw.addr = extable_fixup(fixup);
 149        __ctl_load(cr0, 0, 0);
 150}
 151
 152static noinline __init void setup_lowcore_early(void)
 153{
 154        psw_t psw;
 155
 156        psw.mask = PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA;
 157        psw.addr = (unsigned long) s390_base_ext_handler;
 158        S390_lowcore.external_new_psw = psw;
 159        psw.addr = (unsigned long) s390_base_pgm_handler;
 160        S390_lowcore.program_new_psw = psw;
 161        s390_base_pgm_handler_fn = early_pgm_check_handler;
 162        S390_lowcore.preempt_count = INIT_PREEMPT_COUNT;
 163}
 164
 165static noinline __init void setup_facility_list(void)
 166{
 167        memcpy(S390_lowcore.alt_stfle_fac_list,
 168               S390_lowcore.stfle_fac_list,
 169               sizeof(S390_lowcore.alt_stfle_fac_list));
 170        if (!IS_ENABLED(CONFIG_KERNEL_NOBP))
 171                __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
 172}
 173
 174static __init void detect_diag9c(void)
 175{
 176        unsigned int cpu_address;
 177        int rc;
 178
 179        cpu_address = stap();
 180        diag_stat_inc(DIAG_STAT_X09C);
 181        asm volatile(
 182                "       diag    %2,0,0x9c\n"
 183                "0:     la      %0,0\n"
 184                "1:\n"
 185                EX_TABLE(0b,1b)
 186                : "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc");
 187        if (!rc)
 188                S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG9C;
 189}
 190
 191static __init void detect_diag44(void)
 192{
 193        int rc;
 194
 195        diag_stat_inc(DIAG_STAT_X044);
 196        asm volatile(
 197                "       diag    0,0,0x44\n"
 198                "0:     la      %0,0\n"
 199                "1:\n"
 200                EX_TABLE(0b,1b)
 201                : "=d" (rc) : "0" (-EOPNOTSUPP) : "cc");
 202        if (!rc)
 203                S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG44;
 204}
 205
 206static __init void detect_machine_facilities(void)
 207{
 208        if (test_facility(8)) {
 209                S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT1;
 210                __ctl_set_bit(0, 23);
 211        }
 212        if (test_facility(78))
 213                S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT2;
 214        if (test_facility(3))
 215                S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
 216        if (test_facility(50) && test_facility(73)) {
 217                S390_lowcore.machine_flags |= MACHINE_FLAG_TE;
 218                __ctl_set_bit(0, 55);
 219        }
 220        if (test_facility(51))
 221                S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC;
 222        if (test_facility(129)) {
 223                S390_lowcore.machine_flags |= MACHINE_FLAG_VX;
 224                __ctl_set_bit(0, 17);
 225        }
 226        if (test_facility(130)) {
 227                S390_lowcore.machine_flags |= MACHINE_FLAG_NX;
 228                __ctl_set_bit(0, 20);
 229        }
 230        if (test_facility(133))
 231                S390_lowcore.machine_flags |= MACHINE_FLAG_GS;
 232        if (test_facility(139) && (tod_clock_base[1] & 0x80)) {
 233                /* Enabled signed clock comparator comparisons */
 234                S390_lowcore.machine_flags |= MACHINE_FLAG_SCC;
 235                clock_comparator_max = -1ULL >> 1;
 236                __ctl_set_bit(0, 53);
 237        }
 238}
 239
 240static inline void save_vector_registers(void)
 241{
 242#ifdef CONFIG_CRASH_DUMP
 243        if (test_facility(129))
 244                save_vx_regs(boot_cpu_vector_save_area);
 245#endif
 246}
 247
 248static int __init disable_vector_extension(char *str)
 249{
 250        S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
 251        __ctl_clear_bit(0, 17);
 252        return 0;
 253}
 254early_param("novx", disable_vector_extension);
 255
 256static int __init noexec_setup(char *str)
 257{
 258        bool enabled;
 259        int rc;
 260
 261        rc = kstrtobool(str, &enabled);
 262        if (!rc && !enabled) {
 263                /* Disable no-execute support */
 264                S390_lowcore.machine_flags &= ~MACHINE_FLAG_NX;
 265                __ctl_clear_bit(0, 20);
 266        }
 267        return rc;
 268}
 269early_param("noexec", noexec_setup);
 270
 271static int __init cad_setup(char *str)
 272{
 273        bool enabled;
 274        int rc;
 275
 276        rc = kstrtobool(str, &enabled);
 277        if (!rc && enabled && test_facility(128))
 278                /* Enable problem state CAD. */
 279                __ctl_set_bit(2, 3);
 280        return rc;
 281}
 282early_param("cad", cad_setup);
 283
 284char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
 285static void __init setup_boot_command_line(void)
 286{
 287        /* copy arch command line */
 288        strlcpy(boot_command_line, early_command_line, ARCH_COMMAND_LINE_SIZE);
 289}
 290
 291static void __init check_image_bootable(void)
 292{
 293        if (!memcmp(EP_STRING, (void *)EP_OFFSET, strlen(EP_STRING)))
 294                return;
 295
 296        sclp_early_printk("Linux kernel boot failure: An attempt to boot a vmlinux ELF image failed.\n");
 297        sclp_early_printk("This image does not contain all parts necessary for starting up. Use\n");
 298        sclp_early_printk("bzImage or arch/s390/boot/compressed/vmlinux instead.\n");
 299        disabled_wait(0xbadb007);
 300}
 301
 302void __init startup_init(void)
 303{
 304        check_image_bootable();
 305        time_early_init();
 306        init_kernel_storage_key();
 307        lockdep_off();
 308        setup_lowcore_early();
 309        setup_facility_list();
 310        detect_machine_type();
 311        setup_arch_string();
 312        setup_boot_command_line();
 313        detect_diag9c();
 314        detect_diag44();
 315        detect_machine_facilities();
 316        save_vector_registers();
 317        setup_topology();
 318        sclp_early_detect();
 319        lockdep_on();
 320}
 321