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 "entry.h"
  33
  34static void __init setup_boot_command_line(void);
  35
  36/*
  37 * Initialize storage key for kernel pages
  38 */
  39static 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
  52static __initdata char sysinfo_page[PAGE_SIZE] __aligned(PAGE_SIZE);
  53
  54static 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. */
  75static 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
  92static 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
 121static __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
 135static 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
 153static 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
 166static 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
 177static __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
 194static __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
 209static __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
 243static 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
 251static 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}
 257early_param("novx", disable_vector_extension);
 258
 259static 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}
 272early_param("noexec", noexec_setup);
 273
 274static 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}
 285early_param("cad", cad_setup);
 286
 287/* Set up boot command line */
 288static 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
 307static 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
 317static 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
 334static 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
 345void __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}
 365