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 17*0c4f2623SVasily Gorbik unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL; 18*0c4f2623SVasily Gorbik struct ipl_parameter_block __bootdata_preserved(ipl_block); 19*0c4f2623SVasily Gorbik int __bootdata_preserved(ipl_block_valid); 20*0c4f2623SVasily Gorbik 21*0c4f2623SVasily 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; 3149698745SVasily Gorbik psw_t old = S390_lowcore.program_new_psw; 3249698745SVasily Gorbik 3349698745SVasily Gorbik asm volatile( 3449698745SVasily Gorbik " epsw %0,%1\n" 3549698745SVasily Gorbik " st %0,%[psw_pgm]\n" 3649698745SVasily Gorbik " st %1,%[psw_pgm]+4\n" 3749698745SVasily Gorbik " larl %0,1f\n" 3849698745SVasily Gorbik " stg %0,%[psw_pgm]+8\n" 3949698745SVasily Gorbik " diag %[addr],%[subcode],0x308\n" 4049698745SVasily Gorbik "1: nopr %%r7\n" 4149698745SVasily Gorbik : "=&d" (reg1), "=&a" (reg2), 4249698745SVasily Gorbik [psw_pgm] "=Q" (S390_lowcore.program_new_psw), 4349698745SVasily Gorbik [addr] "+d" (_addr), "+d" (_rc) 4449698745SVasily Gorbik : [subcode] "d" (subcode) 4549698745SVasily Gorbik : "cc", "memory"); 4649698745SVasily Gorbik S390_lowcore.program_new_psw = old; 4749698745SVasily Gorbik return _rc; 4849698745SVasily Gorbik } 4949698745SVasily Gorbik 5049698745SVasily Gorbik void store_ipl_parmblock(void) 5149698745SVasily Gorbik { 5249698745SVasily Gorbik int rc; 5349698745SVasily Gorbik 541e941d39SVasily Gorbik rc = __diag308(DIAG308_STORE, &ipl_block); 5549698745SVasily Gorbik if (rc == DIAG308_RC_OK && 561e941d39SVasily Gorbik ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION) 571e941d39SVasily Gorbik ipl_block_valid = 1; 5849698745SVasily Gorbik } 5949698745SVasily Gorbik 6073045a08SVasily Gorbik bool is_ipl_block_dump(void) 6173045a08SVasily Gorbik { 6273045a08SVasily Gorbik if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP && 6373045a08SVasily Gorbik ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) 6473045a08SVasily Gorbik return true; 6573045a08SVasily Gorbik if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME && 6673045a08SVasily Gorbik ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP) 6773045a08SVasily Gorbik return true; 6873045a08SVasily Gorbik return false; 6973045a08SVasily Gorbik } 7073045a08SVasily Gorbik 714ae98789SArnd Bergmann static size_t scpdata_length(const u8 *buf, size_t count) 7249698745SVasily Gorbik { 7349698745SVasily Gorbik while (count) { 7449698745SVasily Gorbik if (buf[count - 1] != '\0' && buf[count - 1] != ' ') 7549698745SVasily Gorbik break; 7649698745SVasily Gorbik count--; 7749698745SVasily Gorbik } 7849698745SVasily Gorbik return count; 7949698745SVasily Gorbik } 8049698745SVasily Gorbik 8149698745SVasily Gorbik static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size, 8249698745SVasily Gorbik const struct ipl_parameter_block *ipb) 8349698745SVasily Gorbik { 84d9f12e48SAlexander Egorenkov const __u8 *scp_data; 85d9f12e48SAlexander Egorenkov __u32 scp_data_len; 8649698745SVasily Gorbik int has_lowercase; 87d9f12e48SAlexander Egorenkov size_t count = 0; 88d9f12e48SAlexander Egorenkov size_t i; 8949698745SVasily Gorbik 90d9f12e48SAlexander Egorenkov switch (ipb->pb0_hdr.pbt) { 91d9f12e48SAlexander Egorenkov case IPL_PBT_FCP: 92d9f12e48SAlexander Egorenkov scp_data_len = ipb->fcp.scp_data_len; 93d9f12e48SAlexander Egorenkov scp_data = ipb->fcp.scp_data; 94d9f12e48SAlexander Egorenkov break; 95d9f12e48SAlexander Egorenkov case IPL_PBT_NVME: 96d9f12e48SAlexander Egorenkov scp_data_len = ipb->nvme.scp_data_len; 97d9f12e48SAlexander Egorenkov scp_data = ipb->nvme.scp_data; 98d9f12e48SAlexander Egorenkov break; 99d9f12e48SAlexander Egorenkov default: 100d9f12e48SAlexander Egorenkov goto out; 101d9f12e48SAlexander Egorenkov } 102d9f12e48SAlexander Egorenkov 103d9f12e48SAlexander Egorenkov count = min(size - 1, scpdata_length(scp_data, scp_data_len)); 10449698745SVasily Gorbik if (!count) 10549698745SVasily Gorbik goto out; 10649698745SVasily Gorbik 10749698745SVasily Gorbik has_lowercase = 0; 10849698745SVasily Gorbik for (i = 0; i < count; i++) { 109d9f12e48SAlexander Egorenkov if (!isascii(scp_data[i])) { 11049698745SVasily Gorbik count = 0; 11149698745SVasily Gorbik goto out; 11249698745SVasily Gorbik } 113d9f12e48SAlexander Egorenkov if (!has_lowercase && islower(scp_data[i])) 11449698745SVasily Gorbik has_lowercase = 1; 11549698745SVasily Gorbik } 11649698745SVasily Gorbik 11749698745SVasily Gorbik if (has_lowercase) 118d9f12e48SAlexander Egorenkov memcpy(dest, scp_data, count); 11949698745SVasily Gorbik else 12049698745SVasily Gorbik for (i = 0; i < count; i++) 121d9f12e48SAlexander Egorenkov dest[i] = tolower(scp_data[i]); 12249698745SVasily Gorbik out: 12349698745SVasily Gorbik dest[count] = '\0'; 12449698745SVasily Gorbik return count; 12549698745SVasily Gorbik } 12649698745SVasily Gorbik 12749698745SVasily Gorbik static void append_ipl_block_parm(void) 12849698745SVasily Gorbik { 12949698745SVasily Gorbik char *parm, *delim; 13049698745SVasily Gorbik size_t len, rc = 0; 13149698745SVasily Gorbik 13249698745SVasily Gorbik len = strlen(early_command_line); 13349698745SVasily Gorbik 13449698745SVasily Gorbik delim = early_command_line + len; /* '\0' character position */ 13549698745SVasily Gorbik parm = early_command_line + len + 1; /* append right after '\0' */ 13649698745SVasily Gorbik 1375f1207fbSMartin Schwidefsky switch (ipl_block.pb0_hdr.pbt) { 1385f1207fbSMartin Schwidefsky case IPL_PBT_CCW: 13949698745SVasily Gorbik rc = ipl_block_get_ascii_vmparm( 1401e941d39SVasily Gorbik parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 14149698745SVasily Gorbik break; 1425f1207fbSMartin Schwidefsky case IPL_PBT_FCP: 143d9f12e48SAlexander Egorenkov case IPL_PBT_NVME: 14449698745SVasily Gorbik rc = ipl_block_get_ascii_scpdata( 1451e941d39SVasily Gorbik parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 14649698745SVasily Gorbik break; 14749698745SVasily Gorbik } 14849698745SVasily Gorbik if (rc) { 14949698745SVasily Gorbik if (*parm == '=') 15049698745SVasily Gorbik memmove(early_command_line, parm + 1, rc); 15149698745SVasily Gorbik else 15249698745SVasily Gorbik *delim = ' '; /* replace '\0' with space */ 15349698745SVasily Gorbik } 15449698745SVasily Gorbik } 15549698745SVasily Gorbik 15649698745SVasily Gorbik static inline int has_ebcdic_char(const char *str) 15749698745SVasily Gorbik { 15849698745SVasily Gorbik int i; 15949698745SVasily Gorbik 16049698745SVasily Gorbik for (i = 0; str[i]; i++) 16149698745SVasily Gorbik if (str[i] & 0x80) 16249698745SVasily Gorbik return 1; 16349698745SVasily Gorbik return 0; 16449698745SVasily Gorbik } 16549698745SVasily Gorbik 16649698745SVasily Gorbik void setup_boot_command_line(void) 16749698745SVasily Gorbik { 16827c1dac0SHeiko Carstens parmarea.command_line[ARCH_COMMAND_LINE_SIZE - 1] = 0; 16949698745SVasily Gorbik /* convert arch command line to ascii if necessary */ 17027c1dac0SHeiko Carstens if (has_ebcdic_char(parmarea.command_line)) 17127c1dac0SHeiko Carstens EBCASC(parmarea.command_line, ARCH_COMMAND_LINE_SIZE); 17249698745SVasily Gorbik /* copy arch command line */ 17327c1dac0SHeiko Carstens strcpy(early_command_line, strim(parmarea.command_line)); 17449698745SVasily Gorbik 17549698745SVasily Gorbik /* append IPL PARM data to the boot command line */ 176093ddccbSVasily Gorbik if (!is_prot_virt_guest() && ipl_block_valid) 17749698745SVasily Gorbik append_ipl_block_parm(); 17849698745SVasily Gorbik } 17949698745SVasily Gorbik 180b5e80459SVasily Gorbik static void modify_facility(unsigned long nr, bool clear) 181b5e80459SVasily Gorbik { 182b5e80459SVasily Gorbik if (clear) 18317e89e13SSven Schnelle __clear_facility(nr, stfle_fac_list); 184b5e80459SVasily Gorbik else 18517e89e13SSven Schnelle __set_facility(nr, stfle_fac_list); 186b5e80459SVasily Gorbik } 187b5e80459SVasily Gorbik 1886d85dac2SVasily Gorbik static void check_cleared_facilities(void) 1896d85dac2SVasily Gorbik { 1906d85dac2SVasily Gorbik unsigned long als[] = { FACILITIES_ALS }; 1916d85dac2SVasily Gorbik int i; 1926d85dac2SVasily Gorbik 1936d85dac2SVasily Gorbik for (i = 0; i < ARRAY_SIZE(als); i++) { 19417e89e13SSven Schnelle if ((stfle_fac_list[i] & als[i]) != als[i]) { 1956d85dac2SVasily Gorbik sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n"); 1966d85dac2SVasily Gorbik print_missing_facilities(); 1976d85dac2SVasily Gorbik break; 1986d85dac2SVasily Gorbik } 1996d85dac2SVasily Gorbik } 2006d85dac2SVasily Gorbik } 2016d85dac2SVasily Gorbik 202b5e80459SVasily Gorbik static void modify_fac_list(char *str) 203b5e80459SVasily Gorbik { 204b5e80459SVasily Gorbik unsigned long val, endval; 205b5e80459SVasily Gorbik char *endp; 206b5e80459SVasily Gorbik bool clear; 207b5e80459SVasily Gorbik 208b5e80459SVasily Gorbik while (*str) { 209b5e80459SVasily Gorbik clear = false; 210b5e80459SVasily Gorbik if (*str == '!') { 211b5e80459SVasily Gorbik clear = true; 212b5e80459SVasily Gorbik str++; 213b5e80459SVasily Gorbik } 214b5e80459SVasily Gorbik val = simple_strtoull(str, &endp, 0); 215b5e80459SVasily Gorbik if (str == endp) 216b5e80459SVasily Gorbik break; 217b5e80459SVasily Gorbik str = endp; 218b5e80459SVasily Gorbik if (*str == '-') { 219b5e80459SVasily Gorbik str++; 220b5e80459SVasily Gorbik endval = simple_strtoull(str, &endp, 0); 221b5e80459SVasily Gorbik if (str == endp) 222b5e80459SVasily Gorbik break; 223b5e80459SVasily Gorbik str = endp; 224b5e80459SVasily Gorbik while (val <= endval) { 225b5e80459SVasily Gorbik modify_facility(val, clear); 226b5e80459SVasily Gorbik val++; 227b5e80459SVasily Gorbik } 228b5e80459SVasily Gorbik } else { 229b5e80459SVasily Gorbik modify_facility(val, clear); 230b5e80459SVasily Gorbik } 231b5e80459SVasily Gorbik if (*str != ',') 232b5e80459SVasily Gorbik break; 233b5e80459SVasily Gorbik str++; 234b5e80459SVasily Gorbik } 2356d85dac2SVasily Gorbik check_cleared_facilities(); 236b5e80459SVasily Gorbik } 237b5e80459SVasily Gorbik 238980d5f9aSAlexander Egorenkov static char command_line_buf[COMMAND_LINE_SIZE]; 239b5e80459SVasily Gorbik void parse_boot_command_line(void) 24049698745SVasily Gorbik { 24149698745SVasily Gorbik char *param, *val; 242d58106c3SVasily Gorbik bool enabled; 243d58106c3SVasily Gorbik char *args; 244d58106c3SVasily Gorbik int rc; 24549698745SVasily Gorbik 246b2d24b97SGerald Schaefer kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE); 24749698745SVasily Gorbik args = strcpy(command_line_buf, early_command_line); 24849698745SVasily Gorbik while (*args) { 24949698745SVasily Gorbik args = next_arg(args, ¶m, &val); 25049698745SVasily Gorbik 25173045a08SVasily Gorbik if (!strcmp(param, "mem") && val) 25273045a08SVasily Gorbik memory_limit = round_down(memparse(val, NULL), PAGE_SIZE); 253d58106c3SVasily Gorbik 25490178c19SHeiko Carstens if (!strcmp(param, "vmalloc") && val) { 25559793c5aSVasily Gorbik vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE); 25690178c19SHeiko Carstens vmalloc_size_set = 1; 25790178c19SHeiko Carstens } 25859793c5aSVasily Gorbik 25986cde618SVasily Gorbik if (!strcmp(param, "dfltcc") && val) { 260c65e6815SMikhail Zaslonko if (!strcmp(val, "off")) 261c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED; 262c65e6815SMikhail Zaslonko else if (!strcmp(val, "on")) 263c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_FULL; 264c65e6815SMikhail Zaslonko else if (!strcmp(val, "def_only")) 265c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY; 266c65e6815SMikhail Zaslonko else if (!strcmp(val, "inf_only")) 267c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY; 268c65e6815SMikhail Zaslonko else if (!strcmp(val, "always")) 269c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG; 270c65e6815SMikhail Zaslonko } 271c65e6815SMikhail Zaslonko 272d58106c3SVasily Gorbik if (!strcmp(param, "noexec")) { 273d58106c3SVasily Gorbik rc = kstrtobool(val, &enabled); 274d58106c3SVasily Gorbik if (!rc && !enabled) 275d58106c3SVasily Gorbik noexec_disabled = 1; 276d58106c3SVasily Gorbik } 277b5e80459SVasily Gorbik 278227f52a4SVasily Gorbik if (!strcmp(param, "facilities") && val) 279b5e80459SVasily Gorbik modify_fac_list(val); 280b2d24b97SGerald Schaefer 281b2d24b97SGerald Schaefer if (!strcmp(param, "nokaslr")) 282b2d24b97SGerald Schaefer kaslr_enabled = 0; 2831d6671aeSVasily Gorbik 2841d6671aeSVasily Gorbik #if IS_ENABLED(CONFIG_KVM) 2851d6671aeSVasily Gorbik if (!strcmp(param, "prot_virt")) { 2861d6671aeSVasily Gorbik rc = kstrtobool(val, &enabled); 2871d6671aeSVasily Gorbik if (!rc && enabled) 2881d6671aeSVasily Gorbik prot_virt_host = 1; 2891d6671aeSVasily Gorbik } 2901d6671aeSVasily Gorbik #endif 29149698745SVasily Gorbik } 29249698745SVasily Gorbik } 293