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]; 151e941d39SVasily Gorbik struct ipl_parameter_block __bootdata_preserved(ipl_block); 161e941d39SVasily Gorbik int __bootdata_preserved(ipl_block_valid); 17c65e6815SMikhail Zaslonko unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL; 1849698745SVasily Gorbik 1959793c5aSVasily Gorbik unsigned long __bootdata(vmalloc_size) = VMALLOC_DEFAULT_SIZE; 2049698745SVasily Gorbik unsigned long __bootdata(memory_end); 2149698745SVasily Gorbik int __bootdata(memory_end_set); 22d58106c3SVasily Gorbik int __bootdata(noexec_disabled); 2349698745SVasily Gorbik 24b2d24b97SGerald Schaefer int kaslr_enabled __section(.data); 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 604ae98789SArnd Bergmann static size_t scpdata_length(const u8 *buf, size_t count) 6149698745SVasily Gorbik { 6249698745SVasily Gorbik while (count) { 6349698745SVasily Gorbik if (buf[count - 1] != '\0' && buf[count - 1] != ' ') 6449698745SVasily Gorbik break; 6549698745SVasily Gorbik count--; 6649698745SVasily Gorbik } 6749698745SVasily Gorbik return count; 6849698745SVasily Gorbik } 6949698745SVasily Gorbik 7049698745SVasily Gorbik static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size, 7149698745SVasily Gorbik const struct ipl_parameter_block *ipb) 7249698745SVasily Gorbik { 7349698745SVasily Gorbik size_t count; 7449698745SVasily Gorbik size_t i; 7549698745SVasily Gorbik int has_lowercase; 7649698745SVasily Gorbik 7786c74d86SMartin Schwidefsky count = min(size - 1, scpdata_length(ipb->fcp.scp_data, 7886c74d86SMartin Schwidefsky ipb->fcp.scp_data_len)); 7949698745SVasily Gorbik if (!count) 8049698745SVasily Gorbik goto out; 8149698745SVasily Gorbik 8249698745SVasily Gorbik has_lowercase = 0; 8349698745SVasily Gorbik for (i = 0; i < count; i++) { 8486c74d86SMartin Schwidefsky if (!isascii(ipb->fcp.scp_data[i])) { 8549698745SVasily Gorbik count = 0; 8649698745SVasily Gorbik goto out; 8749698745SVasily Gorbik } 8886c74d86SMartin Schwidefsky if (!has_lowercase && islower(ipb->fcp.scp_data[i])) 8949698745SVasily Gorbik has_lowercase = 1; 9049698745SVasily Gorbik } 9149698745SVasily Gorbik 9249698745SVasily Gorbik if (has_lowercase) 9386c74d86SMartin Schwidefsky memcpy(dest, ipb->fcp.scp_data, count); 9449698745SVasily Gorbik else 9549698745SVasily Gorbik for (i = 0; i < count; i++) 9686c74d86SMartin Schwidefsky dest[i] = tolower(ipb->fcp.scp_data[i]); 9749698745SVasily Gorbik out: 9849698745SVasily Gorbik dest[count] = '\0'; 9949698745SVasily Gorbik return count; 10049698745SVasily Gorbik } 10149698745SVasily Gorbik 10249698745SVasily Gorbik static void append_ipl_block_parm(void) 10349698745SVasily Gorbik { 10449698745SVasily Gorbik char *parm, *delim; 10549698745SVasily Gorbik size_t len, rc = 0; 10649698745SVasily Gorbik 10749698745SVasily Gorbik len = strlen(early_command_line); 10849698745SVasily Gorbik 10949698745SVasily Gorbik delim = early_command_line + len; /* '\0' character position */ 11049698745SVasily Gorbik parm = early_command_line + len + 1; /* append right after '\0' */ 11149698745SVasily Gorbik 1125f1207fbSMartin Schwidefsky switch (ipl_block.pb0_hdr.pbt) { 1135f1207fbSMartin Schwidefsky case IPL_PBT_CCW: 11449698745SVasily Gorbik rc = ipl_block_get_ascii_vmparm( 1151e941d39SVasily Gorbik parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 11649698745SVasily Gorbik break; 1175f1207fbSMartin Schwidefsky case IPL_PBT_FCP: 11849698745SVasily Gorbik rc = ipl_block_get_ascii_scpdata( 1191e941d39SVasily Gorbik parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 12049698745SVasily Gorbik break; 12149698745SVasily Gorbik } 12249698745SVasily Gorbik if (rc) { 12349698745SVasily Gorbik if (*parm == '=') 12449698745SVasily Gorbik memmove(early_command_line, parm + 1, rc); 12549698745SVasily Gorbik else 12649698745SVasily Gorbik *delim = ' '; /* replace '\0' with space */ 12749698745SVasily Gorbik } 12849698745SVasily Gorbik } 12949698745SVasily Gorbik 13049698745SVasily Gorbik static inline int has_ebcdic_char(const char *str) 13149698745SVasily Gorbik { 13249698745SVasily Gorbik int i; 13349698745SVasily Gorbik 13449698745SVasily Gorbik for (i = 0; str[i]; i++) 13549698745SVasily Gorbik if (str[i] & 0x80) 13649698745SVasily Gorbik return 1; 13749698745SVasily Gorbik return 0; 13849698745SVasily Gorbik } 13949698745SVasily Gorbik 14049698745SVasily Gorbik void setup_boot_command_line(void) 14149698745SVasily Gorbik { 14249698745SVasily Gorbik COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0; 14349698745SVasily Gorbik /* convert arch command line to ascii if necessary */ 14449698745SVasily Gorbik if (has_ebcdic_char(COMMAND_LINE)) 14549698745SVasily Gorbik EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE); 14649698745SVasily Gorbik /* copy arch command line */ 14749698745SVasily Gorbik strcpy(early_command_line, strim(COMMAND_LINE)); 14849698745SVasily Gorbik 14949698745SVasily Gorbik /* append IPL PARM data to the boot command line */ 150093ddccbSVasily Gorbik if (!is_prot_virt_guest() && ipl_block_valid) 15149698745SVasily Gorbik append_ipl_block_parm(); 15249698745SVasily Gorbik } 15349698745SVasily Gorbik 154b5e80459SVasily Gorbik static void modify_facility(unsigned long nr, bool clear) 155b5e80459SVasily Gorbik { 156b5e80459SVasily Gorbik if (clear) 157b5e80459SVasily Gorbik __clear_facility(nr, S390_lowcore.stfle_fac_list); 158b5e80459SVasily Gorbik else 159b5e80459SVasily Gorbik __set_facility(nr, S390_lowcore.stfle_fac_list); 160b5e80459SVasily Gorbik } 161b5e80459SVasily Gorbik 1626d85dac2SVasily Gorbik static void check_cleared_facilities(void) 1636d85dac2SVasily Gorbik { 1646d85dac2SVasily Gorbik unsigned long als[] = { FACILITIES_ALS }; 1656d85dac2SVasily Gorbik int i; 1666d85dac2SVasily Gorbik 1676d85dac2SVasily Gorbik for (i = 0; i < ARRAY_SIZE(als); i++) { 1686d85dac2SVasily Gorbik if ((S390_lowcore.stfle_fac_list[i] & als[i]) != als[i]) { 1696d85dac2SVasily Gorbik sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n"); 1706d85dac2SVasily Gorbik print_missing_facilities(); 1716d85dac2SVasily Gorbik break; 1726d85dac2SVasily Gorbik } 1736d85dac2SVasily Gorbik } 1746d85dac2SVasily Gorbik } 1756d85dac2SVasily Gorbik 176b5e80459SVasily Gorbik static void modify_fac_list(char *str) 177b5e80459SVasily Gorbik { 178b5e80459SVasily Gorbik unsigned long val, endval; 179b5e80459SVasily Gorbik char *endp; 180b5e80459SVasily Gorbik bool clear; 181b5e80459SVasily Gorbik 182b5e80459SVasily Gorbik while (*str) { 183b5e80459SVasily Gorbik clear = false; 184b5e80459SVasily Gorbik if (*str == '!') { 185b5e80459SVasily Gorbik clear = true; 186b5e80459SVasily Gorbik str++; 187b5e80459SVasily Gorbik } 188b5e80459SVasily Gorbik val = simple_strtoull(str, &endp, 0); 189b5e80459SVasily Gorbik if (str == endp) 190b5e80459SVasily Gorbik break; 191b5e80459SVasily Gorbik str = endp; 192b5e80459SVasily Gorbik if (*str == '-') { 193b5e80459SVasily Gorbik str++; 194b5e80459SVasily Gorbik endval = simple_strtoull(str, &endp, 0); 195b5e80459SVasily Gorbik if (str == endp) 196b5e80459SVasily Gorbik break; 197b5e80459SVasily Gorbik str = endp; 198b5e80459SVasily Gorbik while (val <= endval) { 199b5e80459SVasily Gorbik modify_facility(val, clear); 200b5e80459SVasily Gorbik val++; 201b5e80459SVasily Gorbik } 202b5e80459SVasily Gorbik } else { 203b5e80459SVasily Gorbik modify_facility(val, clear); 204b5e80459SVasily Gorbik } 205b5e80459SVasily Gorbik if (*str != ',') 206b5e80459SVasily Gorbik break; 207b5e80459SVasily Gorbik str++; 208b5e80459SVasily Gorbik } 2096d85dac2SVasily Gorbik check_cleared_facilities(); 210b5e80459SVasily Gorbik } 211b5e80459SVasily Gorbik 21249698745SVasily Gorbik static char command_line_buf[COMMAND_LINE_SIZE] __section(.data); 213b5e80459SVasily Gorbik void parse_boot_command_line(void) 21449698745SVasily Gorbik { 21549698745SVasily Gorbik char *param, *val; 216d58106c3SVasily Gorbik bool enabled; 217d58106c3SVasily Gorbik char *args; 218d58106c3SVasily Gorbik int rc; 21949698745SVasily Gorbik 220b2d24b97SGerald Schaefer kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE); 22149698745SVasily Gorbik args = strcpy(command_line_buf, early_command_line); 22249698745SVasily Gorbik while (*args) { 22349698745SVasily Gorbik args = next_arg(args, ¶m, &val); 22449698745SVasily Gorbik 225227f52a4SVasily Gorbik if (!strcmp(param, "mem") && val) { 22622a33c7eSVasily Gorbik memory_end = round_down(memparse(val, NULL), PAGE_SIZE); 22749698745SVasily Gorbik memory_end_set = 1; 22849698745SVasily Gorbik } 229d58106c3SVasily Gorbik 230227f52a4SVasily Gorbik if (!strcmp(param, "vmalloc") && val) 23159793c5aSVasily Gorbik vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE); 23259793c5aSVasily Gorbik 233c65e6815SMikhail Zaslonko if (!strcmp(param, "dfltcc")) { 234c65e6815SMikhail Zaslonko if (!strcmp(val, "off")) 235c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED; 236c65e6815SMikhail Zaslonko else if (!strcmp(val, "on")) 237c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_FULL; 238c65e6815SMikhail Zaslonko else if (!strcmp(val, "def_only")) 239c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY; 240c65e6815SMikhail Zaslonko else if (!strcmp(val, "inf_only")) 241c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY; 242c65e6815SMikhail Zaslonko else if (!strcmp(val, "always")) 243c65e6815SMikhail Zaslonko zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG; 244c65e6815SMikhail Zaslonko } 245c65e6815SMikhail Zaslonko 246d58106c3SVasily Gorbik if (!strcmp(param, "noexec")) { 247d58106c3SVasily Gorbik rc = kstrtobool(val, &enabled); 248d58106c3SVasily Gorbik if (!rc && !enabled) 249d58106c3SVasily Gorbik noexec_disabled = 1; 250d58106c3SVasily Gorbik } 251b5e80459SVasily Gorbik 252227f52a4SVasily Gorbik if (!strcmp(param, "facilities") && val) 253b5e80459SVasily Gorbik modify_fac_list(val); 254b2d24b97SGerald Schaefer 255b2d24b97SGerald Schaefer if (!strcmp(param, "nokaslr")) 256b2d24b97SGerald Schaefer kaslr_enabled = 0; 25749698745SVasily Gorbik } 25849698745SVasily Gorbik } 25949698745SVasily Gorbik 26049698745SVasily Gorbik void setup_memory_end(void) 26149698745SVasily Gorbik { 26249698745SVasily Gorbik #ifdef CONFIG_CRASH_DUMP 263b2d24b97SGerald Schaefer if (OLDMEM_BASE) { 264b2d24b97SGerald Schaefer kaslr_enabled = 0; 265b2d24b97SGerald Schaefer } else if (ipl_block_valid && 2665f1207fbSMartin Schwidefsky ipl_block.pb0_hdr.pbt == IPL_PBT_FCP && 2675f1207fbSMartin Schwidefsky ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) { 268b2d24b97SGerald Schaefer kaslr_enabled = 0; 26949698745SVasily Gorbik if (!sclp_early_get_hsa_size(&memory_end) && memory_end) 27049698745SVasily Gorbik memory_end_set = 1; 27149698745SVasily Gorbik } 27249698745SVasily Gorbik #endif 27349698745SVasily Gorbik } 274