18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/string.h> 38c2ecf20Sopenharmony_ci#include <linux/elf.h> 48c2ecf20Sopenharmony_ci#include <asm/sections.h> 58c2ecf20Sopenharmony_ci#include <asm/setup.h> 68c2ecf20Sopenharmony_ci#include <asm/kexec.h> 78c2ecf20Sopenharmony_ci#include <asm/sclp.h> 88c2ecf20Sopenharmony_ci#include <asm/diag.h> 98c2ecf20Sopenharmony_ci#include <asm/uv.h> 108c2ecf20Sopenharmony_ci#include "compressed/decompressor.h" 118c2ecf20Sopenharmony_ci#include "boot.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ciextern char __boot_data_start[], __boot_data_end[]; 148c2ecf20Sopenharmony_ciextern char __boot_data_preserved_start[], __boot_data_preserved_end[]; 158c2ecf20Sopenharmony_ciunsigned long __bootdata_preserved(__kaslr_offset); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * Some code and data needs to stay below 2 GB, even when the kernel would be 198c2ecf20Sopenharmony_ci * relocated above 2 GB, because it has to use 31 bit addresses. 208c2ecf20Sopenharmony_ci * Such code and data is part of the .dma section, and its location is passed 218c2ecf20Sopenharmony_ci * over to the decompressed / relocated kernel via the .boot.preserved.data 228c2ecf20Sopenharmony_ci * section. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ciextern char _sdma[], _edma[]; 258c2ecf20Sopenharmony_ciextern char _stext_dma[], _etext_dma[]; 268c2ecf20Sopenharmony_ciextern struct exception_table_entry _start_dma_ex_table[]; 278c2ecf20Sopenharmony_ciextern struct exception_table_entry _stop_dma_ex_table[]; 288c2ecf20Sopenharmony_ciunsigned long __bootdata_preserved(__sdma) = __pa(&_sdma); 298c2ecf20Sopenharmony_ciunsigned long __bootdata_preserved(__edma) = __pa(&_edma); 308c2ecf20Sopenharmony_ciunsigned long __bootdata_preserved(__stext_dma) = __pa(&_stext_dma); 318c2ecf20Sopenharmony_ciunsigned long __bootdata_preserved(__etext_dma) = __pa(&_etext_dma); 328c2ecf20Sopenharmony_cistruct exception_table_entry * 338c2ecf20Sopenharmony_ci __bootdata_preserved(__start_dma_ex_table) = _start_dma_ex_table; 348c2ecf20Sopenharmony_cistruct exception_table_entry * 358c2ecf20Sopenharmony_ci __bootdata_preserved(__stop_dma_ex_table) = _stop_dma_ex_table; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciint _diag210_dma(struct diag210 *addr); 388c2ecf20Sopenharmony_ciint _diag26c_dma(void *req, void *resp, enum diag26c_sc subcode); 398c2ecf20Sopenharmony_ciint _diag14_dma(unsigned long rx, unsigned long ry1, unsigned long subcode); 408c2ecf20Sopenharmony_civoid _diag0c_dma(struct hypfs_diag0c_entry *entry); 418c2ecf20Sopenharmony_civoid _diag308_reset_dma(void); 428c2ecf20Sopenharmony_cistruct diag_ops __bootdata_preserved(diag_dma_ops) = { 438c2ecf20Sopenharmony_ci .diag210 = _diag210_dma, 448c2ecf20Sopenharmony_ci .diag26c = _diag26c_dma, 458c2ecf20Sopenharmony_ci .diag14 = _diag14_dma, 468c2ecf20Sopenharmony_ci .diag0c = _diag0c_dma, 478c2ecf20Sopenharmony_ci .diag308_reset = _diag308_reset_dma 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_cistatic struct diag210 _diag210_tmp_dma __section(".dma.data"); 508c2ecf20Sopenharmony_cistruct diag210 *__bootdata_preserved(__diag210_tmp_dma) = &_diag210_tmp_dma; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_civoid error(char *x) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci sclp_early_printk("\n\n"); 558c2ecf20Sopenharmony_ci sclp_early_printk(x); 568c2ecf20Sopenharmony_ci sclp_early_printk("\n\n -- System halted"); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci disabled_wait(); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#ifdef CONFIG_KERNEL_UNCOMPRESSED 628c2ecf20Sopenharmony_ciunsigned long mem_safe_offset(void) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return vmlinux.default_lma + vmlinux.image_size + vmlinux.bss_size; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci#endif 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void rescue_initrd(unsigned long addr) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD)) 718c2ecf20Sopenharmony_ci return; 728c2ecf20Sopenharmony_ci if (!INITRD_START || !INITRD_SIZE) 738c2ecf20Sopenharmony_ci return; 748c2ecf20Sopenharmony_ci if (addr <= INITRD_START) 758c2ecf20Sopenharmony_ci return; 768c2ecf20Sopenharmony_ci memmove((void *)addr, (void *)INITRD_START, INITRD_SIZE); 778c2ecf20Sopenharmony_ci INITRD_START = addr; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void copy_bootdata(void) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci if (__boot_data_end - __boot_data_start != vmlinux.bootdata_size) 838c2ecf20Sopenharmony_ci error(".boot.data section size mismatch"); 848c2ecf20Sopenharmony_ci memcpy((void *)vmlinux.bootdata_off, __boot_data_start, vmlinux.bootdata_size); 858c2ecf20Sopenharmony_ci if (__boot_data_preserved_end - __boot_data_preserved_start != vmlinux.bootdata_preserved_size) 868c2ecf20Sopenharmony_ci error(".boot.preserved.data section size mismatch"); 878c2ecf20Sopenharmony_ci memcpy((void *)vmlinux.bootdata_preserved_off, __boot_data_preserved_start, vmlinux.bootdata_preserved_size); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void handle_relocs(unsigned long offset) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci Elf64_Rela *rela_start, *rela_end, *rela; 938c2ecf20Sopenharmony_ci int r_type, r_sym, rc; 948c2ecf20Sopenharmony_ci Elf64_Addr loc, val; 958c2ecf20Sopenharmony_ci Elf64_Sym *dynsym; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci rela_start = (Elf64_Rela *) vmlinux.rela_dyn_start; 988c2ecf20Sopenharmony_ci rela_end = (Elf64_Rela *) vmlinux.rela_dyn_end; 998c2ecf20Sopenharmony_ci dynsym = (Elf64_Sym *) vmlinux.dynsym_start; 1008c2ecf20Sopenharmony_ci for (rela = rela_start; rela < rela_end; rela++) { 1018c2ecf20Sopenharmony_ci loc = rela->r_offset + offset; 1028c2ecf20Sopenharmony_ci val = rela->r_addend; 1038c2ecf20Sopenharmony_ci r_sym = ELF64_R_SYM(rela->r_info); 1048c2ecf20Sopenharmony_ci if (r_sym) { 1058c2ecf20Sopenharmony_ci if (dynsym[r_sym].st_shndx != SHN_UNDEF) 1068c2ecf20Sopenharmony_ci val += dynsym[r_sym].st_value + offset; 1078c2ecf20Sopenharmony_ci } else { 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * 0 == undefined symbol table index (STN_UNDEF), 1108c2ecf20Sopenharmony_ci * used for R_390_RELATIVE, only add KASLR offset 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci val += offset; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci r_type = ELF64_R_TYPE(rela->r_info); 1158c2ecf20Sopenharmony_ci rc = arch_kexec_do_relocs(r_type, (void *) loc, val, 0); 1168c2ecf20Sopenharmony_ci if (rc) 1178c2ecf20Sopenharmony_ci error("Unknown relocation type"); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * This function clears the BSS section of the decompressed Linux kernel and NOT the decompressor's. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_cistatic void clear_bss_section(void) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci memset((void *)vmlinux.default_lma + vmlinux.image_size, 0, vmlinux.bss_size); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_civoid startup_kernel(void) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci unsigned long random_lma; 1328c2ecf20Sopenharmony_ci unsigned long safe_addr; 1338c2ecf20Sopenharmony_ci void *img; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci store_ipl_parmblock(); 1368c2ecf20Sopenharmony_ci safe_addr = mem_safe_offset(); 1378c2ecf20Sopenharmony_ci safe_addr = read_ipl_report(safe_addr); 1388c2ecf20Sopenharmony_ci uv_query_info(); 1398c2ecf20Sopenharmony_ci rescue_initrd(safe_addr); 1408c2ecf20Sopenharmony_ci sclp_early_read_info(); 1418c2ecf20Sopenharmony_ci setup_boot_command_line(); 1428c2ecf20Sopenharmony_ci parse_boot_command_line(); 1438c2ecf20Sopenharmony_ci setup_memory_end(); 1448c2ecf20Sopenharmony_ci detect_memory(); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci random_lma = __kaslr_offset = 0; 1478c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_enabled) { 1488c2ecf20Sopenharmony_ci random_lma = get_random_base(safe_addr); 1498c2ecf20Sopenharmony_ci if (random_lma) { 1508c2ecf20Sopenharmony_ci __kaslr_offset = random_lma - vmlinux.default_lma; 1518c2ecf20Sopenharmony_ci img = (void *)vmlinux.default_lma; 1528c2ecf20Sopenharmony_ci vmlinux.default_lma += __kaslr_offset; 1538c2ecf20Sopenharmony_ci vmlinux.entry += __kaslr_offset; 1548c2ecf20Sopenharmony_ci vmlinux.bootdata_off += __kaslr_offset; 1558c2ecf20Sopenharmony_ci vmlinux.bootdata_preserved_off += __kaslr_offset; 1568c2ecf20Sopenharmony_ci vmlinux.rela_dyn_start += __kaslr_offset; 1578c2ecf20Sopenharmony_ci vmlinux.rela_dyn_end += __kaslr_offset; 1588c2ecf20Sopenharmony_ci vmlinux.dynsym_start += __kaslr_offset; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) { 1638c2ecf20Sopenharmony_ci img = decompress_kernel(); 1648c2ecf20Sopenharmony_ci memmove((void *)vmlinux.default_lma, img, vmlinux.image_size); 1658c2ecf20Sopenharmony_ci } else if (__kaslr_offset) 1668c2ecf20Sopenharmony_ci memcpy((void *)vmlinux.default_lma, img, vmlinux.image_size); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci clear_bss_section(); 1698c2ecf20Sopenharmony_ci copy_bootdata(); 1708c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_RELOCATABLE)) 1718c2ecf20Sopenharmony_ci handle_relocs(__kaslr_offset); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (__kaslr_offset) { 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * Save KASLR offset for early dumps, before vmcore_info is set. 1768c2ecf20Sopenharmony_ci * Mark as uneven to distinguish from real vmcore_info pointer. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci S390_lowcore.vmcore_info = __kaslr_offset | 0x1UL; 1798c2ecf20Sopenharmony_ci /* Clear non-relocated kernel */ 1808c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) 1818c2ecf20Sopenharmony_ci memset(img, 0, vmlinux.image_size); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci vmlinux.entry(); 1848c2ecf20Sopenharmony_ci} 185