18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/kernel.h> 38c2ecf20Sopenharmony_ci#include <linux/init.h> 48c2ecf20Sopenharmony_ci#include <linux/ctype.h> 58c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 68c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 78c2ecf20Sopenharmony_ci#include <asm/sclp.h> 88c2ecf20Sopenharmony_ci#include <asm/sections.h> 98c2ecf20Sopenharmony_ci#include <asm/boot_data.h> 108c2ecf20Sopenharmony_ci#include <asm/facility.h> 118c2ecf20Sopenharmony_ci#include <asm/uv.h> 128c2ecf20Sopenharmony_ci#include "boot.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cichar __bootdata(early_command_line)[COMMAND_LINE_SIZE]; 158c2ecf20Sopenharmony_cistruct ipl_parameter_block __bootdata_preserved(ipl_block); 168c2ecf20Sopenharmony_ciint __bootdata_preserved(ipl_block_valid); 178c2ecf20Sopenharmony_ciunsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ciunsigned long __bootdata(vmalloc_size) = VMALLOC_DEFAULT_SIZE; 208c2ecf20Sopenharmony_ciunsigned long __bootdata(memory_end); 218c2ecf20Sopenharmony_ciint __bootdata(memory_end_set); 228c2ecf20Sopenharmony_ciint __bootdata(noexec_disabled); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ciint kaslr_enabled; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic inline int __diag308(unsigned long subcode, void *addr) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci register unsigned long _addr asm("0") = (unsigned long)addr; 298c2ecf20Sopenharmony_ci register unsigned long _rc asm("1") = 0; 308c2ecf20Sopenharmony_ci unsigned long reg1, reg2; 318c2ecf20Sopenharmony_ci psw_t old; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci asm volatile( 348c2ecf20Sopenharmony_ci " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n" 358c2ecf20Sopenharmony_ci " epsw %0,%1\n" 368c2ecf20Sopenharmony_ci " st %0,0(%[psw_pgm])\n" 378c2ecf20Sopenharmony_ci " st %1,4(%[psw_pgm])\n" 388c2ecf20Sopenharmony_ci " larl %0,1f\n" 398c2ecf20Sopenharmony_ci " stg %0,8(%[psw_pgm])\n" 408c2ecf20Sopenharmony_ci " diag %[addr],%[subcode],0x308\n" 418c2ecf20Sopenharmony_ci "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n" 428c2ecf20Sopenharmony_ci : "=&d" (reg1), "=&a" (reg2), 438c2ecf20Sopenharmony_ci "+Q" (S390_lowcore.program_new_psw), 448c2ecf20Sopenharmony_ci "=Q" (old), 458c2ecf20Sopenharmony_ci [addr] "+d" (_addr), "+d" (_rc) 468c2ecf20Sopenharmony_ci : [subcode] "d" (subcode), 478c2ecf20Sopenharmony_ci [psw_old] "a" (&old), 488c2ecf20Sopenharmony_ci [psw_pgm] "a" (&S390_lowcore.program_new_psw) 498c2ecf20Sopenharmony_ci : "cc", "memory"); 508c2ecf20Sopenharmony_ci return _rc; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_civoid store_ipl_parmblock(void) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int rc; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci rc = __diag308(DIAG308_STORE, &ipl_block); 588c2ecf20Sopenharmony_ci if (rc == DIAG308_RC_OK && 598c2ecf20Sopenharmony_ci ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION) 608c2ecf20Sopenharmony_ci ipl_block_valid = 1; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic size_t scpdata_length(const u8 *buf, size_t count) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci while (count) { 668c2ecf20Sopenharmony_ci if (buf[count - 1] != '\0' && buf[count - 1] != ' ') 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci count--; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci return count; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic size_t ipl_block_get_ascii_scpdata(char *dest, size_t size, 748c2ecf20Sopenharmony_ci const struct ipl_parameter_block *ipb) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci const __u8 *scp_data; 778c2ecf20Sopenharmony_ci __u32 scp_data_len; 788c2ecf20Sopenharmony_ci int has_lowercase; 798c2ecf20Sopenharmony_ci size_t count = 0; 808c2ecf20Sopenharmony_ci size_t i; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci switch (ipb->pb0_hdr.pbt) { 838c2ecf20Sopenharmony_ci case IPL_PBT_FCP: 848c2ecf20Sopenharmony_ci scp_data_len = ipb->fcp.scp_data_len; 858c2ecf20Sopenharmony_ci scp_data = ipb->fcp.scp_data; 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci case IPL_PBT_NVME: 888c2ecf20Sopenharmony_ci scp_data_len = ipb->nvme.scp_data_len; 898c2ecf20Sopenharmony_ci scp_data = ipb->nvme.scp_data; 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci default: 928c2ecf20Sopenharmony_ci goto out; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci count = min(size - 1, scpdata_length(scp_data, scp_data_len)); 968c2ecf20Sopenharmony_ci if (!count) 978c2ecf20Sopenharmony_ci goto out; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci has_lowercase = 0; 1008c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1018c2ecf20Sopenharmony_ci if (!isascii(scp_data[i])) { 1028c2ecf20Sopenharmony_ci count = 0; 1038c2ecf20Sopenharmony_ci goto out; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci if (!has_lowercase && islower(scp_data[i])) 1068c2ecf20Sopenharmony_ci has_lowercase = 1; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (has_lowercase) 1108c2ecf20Sopenharmony_ci memcpy(dest, scp_data, count); 1118c2ecf20Sopenharmony_ci else 1128c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 1138c2ecf20Sopenharmony_ci dest[i] = tolower(scp_data[i]); 1148c2ecf20Sopenharmony_ciout: 1158c2ecf20Sopenharmony_ci dest[count] = '\0'; 1168c2ecf20Sopenharmony_ci return count; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void append_ipl_block_parm(void) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci char *parm, *delim; 1228c2ecf20Sopenharmony_ci size_t len, rc = 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci len = strlen(early_command_line); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci delim = early_command_line + len; /* '\0' character position */ 1278c2ecf20Sopenharmony_ci parm = early_command_line + len + 1; /* append right after '\0' */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci switch (ipl_block.pb0_hdr.pbt) { 1308c2ecf20Sopenharmony_ci case IPL_PBT_CCW: 1318c2ecf20Sopenharmony_ci rc = ipl_block_get_ascii_vmparm( 1328c2ecf20Sopenharmony_ci parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci case IPL_PBT_FCP: 1358c2ecf20Sopenharmony_ci case IPL_PBT_NVME: 1368c2ecf20Sopenharmony_ci rc = ipl_block_get_ascii_scpdata( 1378c2ecf20Sopenharmony_ci parm, COMMAND_LINE_SIZE - len - 1, &ipl_block); 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci if (rc) { 1418c2ecf20Sopenharmony_ci if (*parm == '=') 1428c2ecf20Sopenharmony_ci memmove(early_command_line, parm + 1, rc); 1438c2ecf20Sopenharmony_ci else 1448c2ecf20Sopenharmony_ci *delim = ' '; /* replace '\0' with space */ 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic inline int has_ebcdic_char(const char *str) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int i; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci for (i = 0; str[i]; i++) 1538c2ecf20Sopenharmony_ci if (str[i] & 0x80) 1548c2ecf20Sopenharmony_ci return 1; 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_civoid setup_boot_command_line(void) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0; 1618c2ecf20Sopenharmony_ci /* convert arch command line to ascii if necessary */ 1628c2ecf20Sopenharmony_ci if (has_ebcdic_char(COMMAND_LINE)) 1638c2ecf20Sopenharmony_ci EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE); 1648c2ecf20Sopenharmony_ci /* copy arch command line */ 1658c2ecf20Sopenharmony_ci strcpy(early_command_line, strim(COMMAND_LINE)); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* append IPL PARM data to the boot command line */ 1688c2ecf20Sopenharmony_ci if (!is_prot_virt_guest() && ipl_block_valid) 1698c2ecf20Sopenharmony_ci append_ipl_block_parm(); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void modify_facility(unsigned long nr, bool clear) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci if (clear) 1758c2ecf20Sopenharmony_ci __clear_facility(nr, S390_lowcore.stfle_fac_list); 1768c2ecf20Sopenharmony_ci else 1778c2ecf20Sopenharmony_ci __set_facility(nr, S390_lowcore.stfle_fac_list); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void check_cleared_facilities(void) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci unsigned long als[] = { FACILITIES_ALS }; 1838c2ecf20Sopenharmony_ci int i; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(als); i++) { 1868c2ecf20Sopenharmony_ci if ((S390_lowcore.stfle_fac_list[i] & als[i]) != als[i]) { 1878c2ecf20Sopenharmony_ci sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n"); 1888c2ecf20Sopenharmony_ci print_missing_facilities(); 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void modify_fac_list(char *str) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci unsigned long val, endval; 1978c2ecf20Sopenharmony_ci char *endp; 1988c2ecf20Sopenharmony_ci bool clear; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci while (*str) { 2018c2ecf20Sopenharmony_ci clear = false; 2028c2ecf20Sopenharmony_ci if (*str == '!') { 2038c2ecf20Sopenharmony_ci clear = true; 2048c2ecf20Sopenharmony_ci str++; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci val = simple_strtoull(str, &endp, 0); 2078c2ecf20Sopenharmony_ci if (str == endp) 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci str = endp; 2108c2ecf20Sopenharmony_ci if (*str == '-') { 2118c2ecf20Sopenharmony_ci str++; 2128c2ecf20Sopenharmony_ci endval = simple_strtoull(str, &endp, 0); 2138c2ecf20Sopenharmony_ci if (str == endp) 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci str = endp; 2168c2ecf20Sopenharmony_ci while (val <= endval) { 2178c2ecf20Sopenharmony_ci modify_facility(val, clear); 2188c2ecf20Sopenharmony_ci val++; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci } else { 2218c2ecf20Sopenharmony_ci modify_facility(val, clear); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci if (*str != ',') 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci str++; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci check_cleared_facilities(); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic char command_line_buf[COMMAND_LINE_SIZE]; 2318c2ecf20Sopenharmony_civoid parse_boot_command_line(void) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci char *param, *val; 2348c2ecf20Sopenharmony_ci bool enabled; 2358c2ecf20Sopenharmony_ci char *args; 2368c2ecf20Sopenharmony_ci int rc; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE); 2398c2ecf20Sopenharmony_ci args = strcpy(command_line_buf, early_command_line); 2408c2ecf20Sopenharmony_ci while (*args) { 2418c2ecf20Sopenharmony_ci args = next_arg(args, ¶m, &val); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!strcmp(param, "mem") && val) { 2448c2ecf20Sopenharmony_ci memory_end = round_down(memparse(val, NULL), PAGE_SIZE); 2458c2ecf20Sopenharmony_ci memory_end_set = 1; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!strcmp(param, "vmalloc") && val) 2498c2ecf20Sopenharmony_ci vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!strcmp(param, "dfltcc") && val) { 2528c2ecf20Sopenharmony_ci if (!strcmp(val, "off")) 2538c2ecf20Sopenharmony_ci zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED; 2548c2ecf20Sopenharmony_ci else if (!strcmp(val, "on")) 2558c2ecf20Sopenharmony_ci zlib_dfltcc_support = ZLIB_DFLTCC_FULL; 2568c2ecf20Sopenharmony_ci else if (!strcmp(val, "def_only")) 2578c2ecf20Sopenharmony_ci zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY; 2588c2ecf20Sopenharmony_ci else if (!strcmp(val, "inf_only")) 2598c2ecf20Sopenharmony_ci zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY; 2608c2ecf20Sopenharmony_ci else if (!strcmp(val, "always")) 2618c2ecf20Sopenharmony_ci zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (!strcmp(param, "noexec")) { 2658c2ecf20Sopenharmony_ci rc = kstrtobool(val, &enabled); 2668c2ecf20Sopenharmony_ci if (!rc && !enabled) 2678c2ecf20Sopenharmony_ci noexec_disabled = 1; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (!strcmp(param, "facilities") && val) 2718c2ecf20Sopenharmony_ci modify_fac_list(val); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (!strcmp(param, "nokaslr")) 2748c2ecf20Sopenharmony_ci kaslr_enabled = 0; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_KVM) 2778c2ecf20Sopenharmony_ci if (!strcmp(param, "prot_virt")) { 2788c2ecf20Sopenharmony_ci rc = kstrtobool(val, &enabled); 2798c2ecf20Sopenharmony_ci if (!rc && enabled) 2808c2ecf20Sopenharmony_ci prot_virt_host = 1; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci#endif 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic inline bool is_ipl_block_dump(void) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP && 2898c2ecf20Sopenharmony_ci ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP) 2908c2ecf20Sopenharmony_ci return true; 2918c2ecf20Sopenharmony_ci if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME && 2928c2ecf20Sopenharmony_ci ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP) 2938c2ecf20Sopenharmony_ci return true; 2948c2ecf20Sopenharmony_ci return false; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_civoid setup_memory_end(void) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci#ifdef CONFIG_CRASH_DUMP 3008c2ecf20Sopenharmony_ci if (OLDMEM_BASE) { 3018c2ecf20Sopenharmony_ci kaslr_enabled = 0; 3028c2ecf20Sopenharmony_ci } else if (ipl_block_valid && is_ipl_block_dump()) { 3038c2ecf20Sopenharmony_ci kaslr_enabled = 0; 3048c2ecf20Sopenharmony_ci if (!sclp_early_get_hsa_size(&memory_end) && memory_end) 3058c2ecf20Sopenharmony_ci memory_end_set = 1; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci#endif 3088c2ecf20Sopenharmony_ci} 309