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