149698745SVasily Gorbik // SPDX-License-Identifier: GPL-2.0 26d85dac2SVasily Gorbik #include <linux/kernel.h> 349698745SVasily Gorbik #include <linux/init.h> 449698745SVasily Gorbik #include <linux/ctype.h> 565fddcfcSMike Rapoport #include <linux/pgtable.h> 649698745SVasily Gorbik #include <asm/ebcdic.h> 749698745SVasily Gorbik #include <asm/sclp.h> 849698745SVasily Gorbik #include <asm/sections.h> 949698745SVasily Gorbik #include <asm/boot_data.h> 10b5e80459SVasily Gorbik #include <asm/facility.h> 11db9492ceSVasily Gorbik #include <asm/uv.h> 1249698745SVasily Gorbik #include "boot.h" 1349698745SVasily Gorbik 1449698745SVasily Gorbik char __bootdata(early_command_line)[COMMAND_LINE_SIZE]; 15d58106c3SVasily Gorbik int __bootdata(noexec_disabled); 1649698745SVasily Gorbik 170c4f2623SVasily Gorbik unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL; 180c4f2623SVasily Gorbik struct ipl_parameter_block __bootdata_preserved(ipl_block); 190c4f2623SVasily Gorbik int __bootdata_preserved(ipl_block_valid); 200c4f2623SVasily Gorbik 210c4f2623SVasily Gorbik unsigned long vmalloc_size = VMALLOC_DEFAULT_SIZE; 2273045a08SVasily Gorbik unsigned long memory_limit; 2390178c19SHeiko Carstens int vmalloc_size_set; 24980d5f9aSAlexander Egorenkov int kaslr_enabled; 25b2d24b97SGerald Schaefer 2649698745SVasily Gorbik static inline int __diag308(unsigned long subcode, void *addr) 2749698745SVasily Gorbik { 2849698745SVasily Gorbik register unsigned long _addr asm("0") = (unsigned long)addr; 2949698745SVasily Gorbik register unsigned long _rc asm("1") = 0; 3049698745SVasily Gorbik unsigned long reg1, reg2; 31*88c2510cSHeiko Carstens psw_t old; 3249698745SVasily Gorbik 3349698745SVasily Gorbik asm volatile( 34*88c2510cSHeiko Carstens " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n" 3549698745SVasily Gorbik " epsw %0,%1\n" 36*88c2510cSHeiko Carstens " st %0,0(%[psw_pgm])\n" 37*88c2510cSHeiko Carstens " st %1,4(%[psw_pgm])\n" 3849698745SVasily Gorbik " larl %0,1f\n" 39*88c2510cSHeiko Carstens " stg %0,8(%[psw_pgm])\n" 4049698745SVasily Gorbik " diag %[addr],%[subcode],0x308\n" 41*88c2510cSHeiko Carstens "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n" 4249698745SVasily Gorbik : "=&d" (reg1), "=&a" (reg2), 43*88c2510cSHeiko Carstens "+Q" (S390_lowcore.program_new_psw), 44*88c2510cSHeiko Carstens "=Q" (old), 4549698745SVasily Gorbik [addr] "+d" (_addr), "+d" (_rc) 46*88c2510cSHeiko Carstens : [subcode] "d" (subcode), 47*88c2510cSHeiko Carstens [psw_old] "a" (&old), 48*88c2510cSHeiko Carstens [psw_pgm] "a" (&S390_lowcore.program_new_psw) 4949698745SVasily Gorbik : "cc", "memory"); 5049698745SVasily Gorbik return _rc; 5149698745SVasily Gorbik } 5249698745SVasily Gorbik 5349698745SVasily Gorbik void store_ipl_parmblock(void) 5449698745SVasily Gorbik { 5549698745SVasily Gorbik int rc; 5649698745SVasily Gorbik 571e941d39SVasily Gorbik rc = __diag308(DIAG308_STORE, &ipl_block); 5849698745SVasily Gorbik if (rc == DIAG308_RC_OK && 591e941d39SVasily Gorbik ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION) 601e941d39SVasily Gorbik ipl_block_valid = 1; 6149698745SVasily Gorbik } 6249698745SVasily Gorbik 6373045a08SVasily Gorbik bool is_ipl_block_dump(void) 6473045a08SVasily Gorbik { 6573045a08SVasily Gorbik if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP && 6673045a08SVasily Gorbik ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) 6773045a08SVasily Gorbik return true; 6873045a08SVasily Gorbik if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME && 6973045a08SVasily Gorbik ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP) 7073045a08SVasily Gorbik return true; 7173045a08SVasily Gorbik return false; 7273045a08SVasily Gorbik } 7373045a08SVasily Gorbik 744ae98789SArnd Bergmann static size_t scpdata_length(const u8 *buf, size_t count) 7549698745SVasily Gorbik { 7649698745SVasily Gorbik while (count) { 7749698745SVasily Gorbik if (buf[count - 1] != '\0' && buf[count - 1] != ' ') 7849698745SVasily Gorbik break; 7949698745SVasily Gorbik count--; 8049698745SVasily Gorbik } 8149698745SVasily Gorbik return count; 8249698745SVasily Gorbik } 8349698745SVasily Gorbik 8449698745SVasily Gorbik static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size, 8549698745SVasily Gorbik const struct ipl_parameter_block *ipb) 8649698745SVasily Gorbik { 87d9f12e48SAlexander Egorenkov const __u8 *scp_data; 88d9f12e48SAlexander Egorenkov __u32 scp_data_len; 8949698745SVasily Gorbik int has_lowercase; 90d9f12e48SAlexander Egorenkov size_t count = 0; 91d9f12e48SAlexander Egorenkov size_t i; 9249698745SVasily Gorbik 93d9f12e48SAlexander Egorenkov switch (ipb->pb0_hdr.pbt) { 94d9f12e48SAlexander Egorenkov case IPL_PBT_FCP: 95d9f12e48SAlexander Egorenkov scp_data_len = ipb->fcp.scp_data_len; 96d9f12e48SAlexander Egorenkov scp_data = ipb->fcp.scp_data; 97d9f12e48SAlexander Egorenkov break; 98d9f12e48SAlexander Egorenkov case IPL_PBT_NVME: 99d9f12e48SAlexander Egorenkov scp_data_len = ipb->nvme.scp_data_len; 100d9f12e48SAlexander Egorenkov scp_data = ipb->nvme.scp_data; 101d9f12e48SAlexander Egorenkov break; 102d9f12e48SAlexander Egorenkov default: 103d9f12e48SAlexander Egorenkov goto out; 104d9f12e48SAlexander Egorenkov } 105d9f12e48SAlexander Egorenkov 106d9f12e48SAlexander Egorenkov count = min(size - 1, scpdata_length(scp_data, scp_data_len)); 10749698745SVasily Gorbik if (!count) 10849698745SVasily Gorbik goto out; 10949698745SVasily Gorbik 11049698745SVasily Gorbik has_lowercase = 0; 11149698745SVasily Gorbik for (i = 0; i < count; i++) { 112d9f12e48SAlexander Egorenkov if (!isascii(scp_data[i])) { 11349698745SVasily Gorbik count = 0; 11449698745SVasily Gorbik goto out; 11549698745SVasily Gorbik } 116d9f12e48SAlexander Egorenkov if (!has_lowercase && islower(scp_data[i])) 11749698745SVasily Gorbik has_lowercase = 1; 11849698745SVasily Gorbik } 11949698745SVasily Gorbik 12049698745SVasily Gorbik if (has_lowercase) 121d9f12e48SAlexander Egorenkov memcpy(dest, scp_data, count); 12249698745SVasily Gorbik else 12349698745SVasily Gorbik for (i = 0; i < count; i++) 124d9f12e48SAlexander Egorenkov dest[i] = tolower(scp_data[i]); 12549698745SVasily Gorbik out: 12649698745SVasily Gorbik dest[count] = '\0'; 12749698745SVasily Gorbik return count; 12849698745SVasily Gorbik } 12949698745SVasily Gorbik 13049698745SVasily Gorbik static void append_ipl_block_parm(void) 13149698745SVasily Gorbik { 13249698745SVasily Gorbik char *parm, *delim; 13349698745SVasily Gorbik size_t len, rc = 0; 13449698745SVasily Gorbik 13549698745SVasily Gorbik len = strlen(early_command_line); 13649698745SVasily Gorbik 13749698745SVasily Gorbik delim = early_command_line + len; /* '\0' character position */ 13849698745SVasily Gorbik parm = early_command_line + len + 1; /* append right after '\0' */ 13949698745SVasily Gorbik 1405f1207fbSMartin Schwidefsky switch (ipl_block.pb0_hdr.pbt) { 1415f1207fbSMartin Schwidefsky case IPL_PBT_CCW: 14249698745SVasily Gorbik rc = ipl_block_get_ascii_vmparm( 1431e941d39SVasily Gorbik parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 14449698745SVasily Gorbik break; 1455f1207fbSMartin Schwidefsky case IPL_PBT_FCP: 146d9f12e48SAlexander Egorenkov case IPL_PBT_NVME: 14749698745SVasily Gorbik rc = ipl_block_get_ascii_scpdata( 1481e941d39SVasily Gorbik parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 14949698745SVasily Gorbik break; 15049698745SVasily Gorbik } 15149698745SVasily Gorbik if (rc) { 15249698745SVasily Gorbik if (*parm == '=') 15349698745SVasily Gorbik memmove(early_command_line, parm + 1, rc); 15449698745SVasily Gorbik else 15549698745SVasily Gorbik *delim = ' '; /* replace '\0' with space */ 15649698745SVasily Gorbik } 15749698745SVasily Gorbik } 15849698745SVasily Gorbik 15949698745SVasily Gorbik static inline int has_ebcdic_char(const char *str) 16049698745SVasily Gorbik { 16149698745SVasily Gorbik int i; 16249698745SVasily Gorbik 16349698745SVasily Gorbik for (i = 0; str[i]; i++) 16449698745SVasily Gorbik if (str[i] & 0x80) 16549698745SVasily Gorbik return 1; 16649698745SVasily Gorbik return 0; 16749698745SVasily Gorbik } 16849698745SVasily Gorbik 16949698745SVasily Gorbik void setup_boot_command_line(void) 17049698745SVasily Gorbik { 17127c1dac0SHeiko Carstens parmarea.command_line[ARCH_COMMAND_LINE_SIZE - 1] = 0; 17249698745SVasily Gorbik /* convert arch command line to ascii if necessary */ 17327c1dac0SHeiko Carstens if (has_ebcdic_char(parmarea.command_line)) 17427c1dac0SHeiko Carstens EBCASC(parmarea.command_line, ARCH_COMMAND_LINE_SIZE); 17549698745SVasily Gorbik /* copy arch command line */ 17627c1dac0SHeiko Carstens strcpy(early_command_line, strim(parmarea.command_line)); 17749698745SVasily Gorbik 17849698745SVasily Gorbik /* append IPL PARM data to the boot command line */ 179093ddccbSVasily Gorbik if (!is_prot_virt_guest() && ipl_block_valid) 18049698745SVasily Gorbik append_ipl_block_parm(); 18149698745SVasily Gorbik } 18249698745SVasily Gorbik 183b5e80459SVasily Gorbik static void modify_facility(unsigned long nr, bool clear) 184b5e80459SVasily Gorbik { 185b5e80459SVasily Gorbik if (clear) 18617e89e13SSven Schnelle __clear_facility(nr, stfle_fac_list); 187b5e80459SVasily Gorbik else 18817e89e13SSven Schnelle __set_facility(nr, stfle_fac_list); 189b5e80459SVasily Gorbik } 190b5e80459SVasily Gorbik 1916d85dac2SVasily Gorbik static void check_cleared_facilities(void) 1926d85dac2SVasily Gorbik { 1936d85dac2SVasily Gorbik unsigned long als[] = { FACILITIES_ALS }; 1946d85dac2SVasily Gorbik int i; 1956d85dac2SVasily Gorbik 1966d85dac2SVasily Gorbik for (i = 0; i < ARRAY_SIZE(als); i++) { 19717e89e13SSven Schnelle if ((stfle_fac_list[i] & als[i]) != als[i]) { 1986d85dac2SVasily Gorbik sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n"); 1996d85dac2SVasily Gorbik print_missing_facilities(); 2006d85dac2SVasily Gorbik break; 2016d85dac2SVasily Gorbik } 2026d85dac2SVasily Gorbik } 2036d85dac2SVasily Gorbik } 2046d85dac2SVasily Gorbik 205b5e80459SVasily Gorbik static void modify_fac_list(char *str) 206b5e80459SVasily Gorbik { 207b5e80459SVasily Gorbik unsigned long val, endval; 208b5e80459SVasily Gorbik char *endp; 209b5e80459SVasily Gorbik bool clear; 210b5e80459SVasily Gorbik 211b5e80459SVasily Gorbik while (*str) { 212b5e80459SVasily Gorbik clear = false; 213b5e80459SVasily Gorbik if (*str == '!') { 214b5e80459SVasily Gorbik clear = true; 215b5e80459SVasily Gorbik str++; 216b5e80459SVasily Gorbik } 217b5e80459SVasily Gorbik val = simple_strtoull(str, &endp, 0); 218b5e80459SVasily Gorbik if (str == endp) 219b5e80459SVasily Gorbik break; 220b5e80459SVasily Gorbik str = endp; 221b5e80459SVasily Gorbik if (*str == '-') { 222b5e80459SVasily Gorbik str++; 223b5e80459SVasily Gorbik endval = simple_strtoull(str, &endp, 0); 224b5e80459SVasily Gorbik if (str == endp) 225b5e80459SVasily Gorbik break; 226b5e80459SVasily Gorbik str = endp; 227b5e80459SVasily Gorbik while (val <= endval) { 228b5e80459SVasily Gorbik modify_facility(val, clear); 229b5e80459SVasily Gorbik val++; 230b5e80459SVasily Gorbik } 231b5e80459SVasily Gorbik } else { 232b5e80459SVasily Gorbik modify_facility(val, clear); 233b5e80459SVasily Gorbik } 234b5e80459SVasily Gorbik if (*str != ',') 235b5e80459SVasily Gorbik break; 236b5e80459SVasily Gorbik str++; 237b5e80459SVasily Gorbik } 2386d85dac2SVasily Gorbik check_cleared_facilities(); 239b5e80459SVasily Gorbik } 240b5e80459SVasily Gorbik 241980d5f9aSAlexander Egorenkov static char command_line_buf[COMMAND_LINE_SIZE]; 242b5e80459SVasily Gorbik void parse_boot_command_line(void) 24349698745SVasily Gorbik { 24449698745SVasily Gorbik char *param, *val; 245d58106c3SVasily Gorbik bool enabled; 246d58106c3SVasily Gorbik char *args; 247d58106c3SVasily Gorbik int rc; 24849698745SVasily Gorbik 249b2d24b97SGerald Schaefer kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE); 25049698745SVasily Gorbik args = strcpy(command_line_buf, early_command_line); 25149698745SVasily Gorbik while (*args) { 25249698745SVasily Gorbik args = next_arg(args, ¶m, &val); 25349698745SVasily Gorbik 25473045a08SVasily Gorbik if (!strcmp(param, "mem") && val) 25573045a08SVasily Gorbik memory_limit = round_down(memparse(val, NULL), PAGE_SIZE); 256d58106c3SVasily Gorbik 25790178c19SHeiko Carstens if (!strcmp(param, "vmalloc") && val) { 25859793c5aSVasily Gorbik vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE); 25990178c19SHeiko Carstens vmalloc_size_set = 1; 26090178c19SHeiko Carstens } 26159793c5aSVasily Gorbik 26286cde618SVasily Gorbik if (!strcmp(param, "dfltcc") && val) { 263c65e6815SMikhail Zaslonko if (!strcmp(val, "off")) 264c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED; 265c65e6815SMikhail Zaslonko else if (!strcmp(val, "on")) 266c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_FULL; 267c65e6815SMikhail Zaslonko else if (!strcmp(val, "def_only")) 268c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY; 269c65e6815SMikhail Zaslonko else if (!strcmp(val, "inf_only")) 270c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY; 271c65e6815SMikhail Zaslonko else if (!strcmp(val, "always")) 272c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG; 273c65e6815SMikhail Zaslonko } 274c65e6815SMikhail Zaslonko 275d58106c3SVasily Gorbik if (!strcmp(param, "noexec")) { 276d58106c3SVasily Gorbik rc = kstrtobool(val, &enabled); 277d58106c3SVasily Gorbik if (!rc && !enabled) 278d58106c3SVasily Gorbik noexec_disabled = 1; 279d58106c3SVasily Gorbik } 280b5e80459SVasily Gorbik 281227f52a4SVasily Gorbik if (!strcmp(param, "facilities") && val) 282b5e80459SVasily Gorbik modify_fac_list(val); 283b2d24b97SGerald Schaefer 284b2d24b97SGerald Schaefer if (!strcmp(param, "nokaslr")) 285b2d24b97SGerald Schaefer kaslr_enabled = 0; 2861d6671aeSVasily Gorbik 2871d6671aeSVasily Gorbik #if IS_ENABLED(CONFIG_KVM) 2881d6671aeSVasily Gorbik if (!strcmp(param, "prot_virt")) { 2891d6671aeSVasily Gorbik rc = kstrtobool(val, &enabled); 2901d6671aeSVasily Gorbik if (!rc && enabled) 2911d6671aeSVasily Gorbik prot_virt_host = 1; 2921d6671aeSVasily Gorbik } 2931d6671aeSVasily Gorbik #endif 29449698745SVasily Gorbik } 29549698745SVasily Gorbik } 296