linux/arch/s390/boot/ipl_parm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/kernel.h>
   3#include <linux/init.h>
   4#include <linux/ctype.h>
   5#include <linux/pgtable.h>
   6#include <asm/ebcdic.h>
   7#include <asm/sclp.h>
   8#include <asm/sections.h>
   9#include <asm/boot_data.h>
  10#include <asm/facility.h>
  11#include <asm/uv.h>
  12#include "boot.h"
  13
  14char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
  15int __bootdata(noexec_disabled);
  16
  17unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL;
  18struct ipl_parameter_block __bootdata_preserved(ipl_block);
  19int __bootdata_preserved(ipl_block_valid);
  20
  21unsigned long vmalloc_size = VMALLOC_DEFAULT_SIZE;
  22unsigned long memory_limit;
  23int vmalloc_size_set;
  24int kaslr_enabled;
  25
  26static inline int __diag308(unsigned long subcode, void *addr)
  27{
  28        unsigned long reg1, reg2;
  29        union register_pair r1;
  30        psw_t old;
  31
  32        r1.even = (unsigned long) addr;
  33        r1.odd  = 0;
  34        asm volatile(
  35                "       mvc     0(16,%[psw_old]),0(%[psw_pgm])\n"
  36                "       epsw    %[reg1],%[reg2]\n"
  37                "       st      %[reg1],0(%[psw_pgm])\n"
  38                "       st      %[reg2],4(%[psw_pgm])\n"
  39                "       larl    %[reg1],1f\n"
  40                "       stg     %[reg1],8(%[psw_pgm])\n"
  41                "       diag    %[r1],%[subcode],0x308\n"
  42                "1:     mvc     0(16,%[psw_pgm]),0(%[psw_old])\n"
  43                : [r1] "+&d" (r1.pair),
  44                  [reg1] "=&d" (reg1),
  45                  [reg2] "=&a" (reg2),
  46                  "+Q" (S390_lowcore.program_new_psw),
  47                  "=Q" (old)
  48                : [subcode] "d" (subcode),
  49                  [psw_old] "a" (&old),
  50                  [psw_pgm] "a" (&S390_lowcore.program_new_psw)
  51                : "cc", "memory");
  52        return r1.odd;
  53}
  54
  55void store_ipl_parmblock(void)
  56{
  57        int rc;
  58
  59        rc = __diag308(DIAG308_STORE, &ipl_block);
  60        if (rc == DIAG308_RC_OK &&
  61            ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
  62                ipl_block_valid = 1;
  63}
  64
  65bool is_ipl_block_dump(void)
  66{
  67        if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP &&
  68            ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP)
  69                return true;
  70        if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME &&
  71            ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP)
  72                return true;
  73        return false;
  74}
  75
  76static size_t scpdata_length(const u8 *buf, size_t count)
  77{
  78        while (count) {
  79                if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
  80                        break;
  81                count--;
  82        }
  83        return count;
  84}
  85
  86static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
  87                                          const struct ipl_parameter_block *ipb)
  88{
  89        const __u8 *scp_data;
  90        __u32 scp_data_len;
  91        int has_lowercase;
  92        size_t count = 0;
  93        size_t i;
  94
  95        switch (ipb->pb0_hdr.pbt) {
  96        case IPL_PBT_FCP:
  97                scp_data_len = ipb->fcp.scp_data_len;
  98                scp_data = ipb->fcp.scp_data;
  99                break;
 100        case IPL_PBT_NVME:
 101                scp_data_len = ipb->nvme.scp_data_len;
 102                scp_data = ipb->nvme.scp_data;
 103                break;
 104        default:
 105                goto out;
 106        }
 107
 108        count = min(size - 1, scpdata_length(scp_data, scp_data_len));
 109        if (!count)
 110                goto out;
 111
 112        has_lowercase = 0;
 113        for (i = 0; i < count; i++) {
 114                if (!isascii(scp_data[i])) {
 115                        count = 0;
 116                        goto out;
 117                }
 118                if (!has_lowercase && islower(scp_data[i]))
 119                        has_lowercase = 1;
 120        }
 121
 122        if (has_lowercase)
 123                memcpy(dest, scp_data, count);
 124        else
 125                for (i = 0; i < count; i++)
 126                        dest[i] = tolower(scp_data[i]);
 127out:
 128        dest[count] = '\0';
 129        return count;
 130}
 131
 132static void append_ipl_block_parm(void)
 133{
 134        char *parm, *delim;
 135        size_t len, rc = 0;
 136
 137        len = strlen(early_command_line);
 138
 139        delim = early_command_line + len;    /* '\0' character position */
 140        parm = early_command_line + len + 1; /* append right after '\0' */
 141
 142        switch (ipl_block.pb0_hdr.pbt) {
 143        case IPL_PBT_CCW:
 144                rc = ipl_block_get_ascii_vmparm(
 145                        parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
 146                break;
 147        case IPL_PBT_FCP:
 148        case IPL_PBT_NVME:
 149                rc = ipl_block_get_ascii_scpdata(
 150                        parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
 151                break;
 152        }
 153        if (rc) {
 154                if (*parm == '=')
 155                        memmove(early_command_line, parm + 1, rc);
 156                else
 157                        *delim = ' '; /* replace '\0' with space */
 158        }
 159}
 160
 161static inline int has_ebcdic_char(const char *str)
 162{
 163        int i;
 164
 165        for (i = 0; str[i]; i++)
 166                if (str[i] & 0x80)
 167                        return 1;
 168        return 0;
 169}
 170
 171void setup_boot_command_line(void)
 172{
 173        parmarea.command_line[ARCH_COMMAND_LINE_SIZE - 1] = 0;
 174        /* convert arch command line to ascii if necessary */
 175        if (has_ebcdic_char(parmarea.command_line))
 176                EBCASC(parmarea.command_line, ARCH_COMMAND_LINE_SIZE);
 177        /* copy arch command line */
 178        strcpy(early_command_line, strim(parmarea.command_line));
 179
 180        /* append IPL PARM data to the boot command line */
 181        if (!is_prot_virt_guest() && ipl_block_valid)
 182                append_ipl_block_parm();
 183}
 184
 185static void modify_facility(unsigned long nr, bool clear)
 186{
 187        if (clear)
 188                __clear_facility(nr, stfle_fac_list);
 189        else
 190                __set_facility(nr, stfle_fac_list);
 191}
 192
 193static void check_cleared_facilities(void)
 194{
 195        unsigned long als[] = { FACILITIES_ALS };
 196        int i;
 197
 198        for (i = 0; i < ARRAY_SIZE(als); i++) {
 199                if ((stfle_fac_list[i] & als[i]) != als[i]) {
 200                        sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n");
 201                        print_missing_facilities();
 202                        break;
 203                }
 204        }
 205}
 206
 207static void modify_fac_list(char *str)
 208{
 209        unsigned long val, endval;
 210        char *endp;
 211        bool clear;
 212
 213        while (*str) {
 214                clear = false;
 215                if (*str == '!') {
 216                        clear = true;
 217                        str++;
 218                }
 219                val = simple_strtoull(str, &endp, 0);
 220                if (str == endp)
 221                        break;
 222                str = endp;
 223                if (*str == '-') {
 224                        str++;
 225                        endval = simple_strtoull(str, &endp, 0);
 226                        if (str == endp)
 227                                break;
 228                        str = endp;
 229                        while (val <= endval) {
 230                                modify_facility(val, clear);
 231                                val++;
 232                        }
 233                } else {
 234                        modify_facility(val, clear);
 235                }
 236                if (*str != ',')
 237                        break;
 238                str++;
 239        }
 240        check_cleared_facilities();
 241}
 242
 243static char command_line_buf[COMMAND_LINE_SIZE];
 244void parse_boot_command_line(void)
 245{
 246        char *param, *val;
 247        bool enabled;
 248        char *args;
 249        int rc;
 250
 251        kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE);
 252        args = strcpy(command_line_buf, early_command_line);
 253        while (*args) {
 254                args = next_arg(args, &param, &val);
 255
 256                if (!strcmp(param, "mem") && val)
 257                        memory_limit = round_down(memparse(val, NULL), PAGE_SIZE);
 258
 259                if (!strcmp(param, "vmalloc") && val) {
 260                        vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE);
 261                        vmalloc_size_set = 1;
 262                }
 263
 264                if (!strcmp(param, "dfltcc") && val) {
 265                        if (!strcmp(val, "off"))
 266                                zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED;
 267                        else if (!strcmp(val, "on"))
 268                                zlib_dfltcc_support = ZLIB_DFLTCC_FULL;
 269                        else if (!strcmp(val, "def_only"))
 270                                zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY;
 271                        else if (!strcmp(val, "inf_only"))
 272                                zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY;
 273                        else if (!strcmp(val, "always"))
 274                                zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG;
 275                }
 276
 277                if (!strcmp(param, "noexec")) {
 278                        rc = kstrtobool(val, &enabled);
 279                        if (!rc && !enabled)
 280                                noexec_disabled = 1;
 281                }
 282
 283                if (!strcmp(param, "facilities") && val)
 284                        modify_fac_list(val);
 285
 286                if (!strcmp(param, "nokaslr"))
 287                        kaslr_enabled = 0;
 288
 289#if IS_ENABLED(CONFIG_KVM)
 290                if (!strcmp(param, "prot_virt")) {
 291                        rc = kstrtobool(val, &enabled);
 292                        if (!rc && enabled)
 293                                prot_virt_host = 1;
 294                }
 295#endif
 296        }
 297}
 298