18f75582aSVasily Gorbik // SPDX-License-Identifier: GPL-2.0 28f75582aSVasily Gorbik #include <linux/string.h> 3805bc0bcSGerald Schaefer #include <linux/elf.h> 415426ca4SVasily Gorbik #include <asm/setup.h> 5805bc0bcSGerald Schaefer #include <asm/kexec.h> 67516fc11SVasily Gorbik #include <asm/sclp.h> 75abb9351SVasily Gorbik #include <asm/uv.h> 88f75582aSVasily Gorbik #include "compressed/decompressor.h" 98f75582aSVasily Gorbik #include "boot.h" 108f75582aSVasily Gorbik 11d1b52a43SVasily Gorbik extern char __boot_data_start[], __boot_data_end[]; 12bf9921a9SGerald Schaefer extern char __boot_data_preserved_start[], __boot_data_preserved_end[]; 13d1b52a43SVasily Gorbik 147516fc11SVasily Gorbik void error(char *x) 157516fc11SVasily Gorbik { 167516fc11SVasily Gorbik sclp_early_printk("\n\n"); 177516fc11SVasily Gorbik sclp_early_printk(x); 187516fc11SVasily Gorbik sclp_early_printk("\n\n -- System halted"); 197516fc11SVasily Gorbik 207516fc11SVasily Gorbik disabled_wait(0xdeadbeef); 217516fc11SVasily Gorbik } 227516fc11SVasily Gorbik 2315426ca4SVasily Gorbik #ifdef CONFIG_KERNEL_UNCOMPRESSED 2415426ca4SVasily Gorbik unsigned long mem_safe_offset(void) 2515426ca4SVasily Gorbik { 2615426ca4SVasily Gorbik return vmlinux.default_lma + vmlinux.image_size + vmlinux.bss_size; 2715426ca4SVasily Gorbik } 2815426ca4SVasily Gorbik #endif 2915426ca4SVasily Gorbik 309641b8ccSMartin Schwidefsky static void rescue_initrd(unsigned long addr) 3115426ca4SVasily Gorbik { 3215426ca4SVasily Gorbik if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD)) 3315426ca4SVasily Gorbik return; 3415426ca4SVasily Gorbik if (!INITRD_START || !INITRD_SIZE) 3515426ca4SVasily Gorbik return; 369641b8ccSMartin Schwidefsky if (addr <= INITRD_START) 3715426ca4SVasily Gorbik return; 389641b8ccSMartin Schwidefsky memmove((void *)addr, (void *)INITRD_START, INITRD_SIZE); 399641b8ccSMartin Schwidefsky INITRD_START = addr; 4015426ca4SVasily Gorbik } 4115426ca4SVasily Gorbik 42d1b52a43SVasily Gorbik static void copy_bootdata(void) 43d1b52a43SVasily Gorbik { 44d1b52a43SVasily Gorbik if (__boot_data_end - __boot_data_start != vmlinux.bootdata_size) 45d1b52a43SVasily Gorbik error(".boot.data section size mismatch"); 46d1b52a43SVasily Gorbik memcpy((void *)vmlinux.bootdata_off, __boot_data_start, vmlinux.bootdata_size); 47bf9921a9SGerald Schaefer if (__boot_data_preserved_end - __boot_data_preserved_start != vmlinux.bootdata_preserved_size) 48bf9921a9SGerald Schaefer error(".boot.preserved.data section size mismatch"); 49bf9921a9SGerald Schaefer memcpy((void *)vmlinux.bootdata_preserved_off, __boot_data_preserved_start, vmlinux.bootdata_preserved_size); 50d1b52a43SVasily Gorbik } 51d1b52a43SVasily Gorbik 52805bc0bcSGerald Schaefer static void handle_relocs(unsigned long offset) 53805bc0bcSGerald Schaefer { 54805bc0bcSGerald Schaefer Elf64_Rela *rela_start, *rela_end, *rela; 55805bc0bcSGerald Schaefer int r_type, r_sym, rc; 56805bc0bcSGerald Schaefer Elf64_Addr loc, val; 57805bc0bcSGerald Schaefer Elf64_Sym *dynsym; 58805bc0bcSGerald Schaefer 59805bc0bcSGerald Schaefer rela_start = (Elf64_Rela *) vmlinux.rela_dyn_start; 60805bc0bcSGerald Schaefer rela_end = (Elf64_Rela *) vmlinux.rela_dyn_end; 61805bc0bcSGerald Schaefer dynsym = (Elf64_Sym *) vmlinux.dynsym_start; 62805bc0bcSGerald Schaefer for (rela = rela_start; rela < rela_end; rela++) { 63805bc0bcSGerald Schaefer loc = rela->r_offset + offset; 64805bc0bcSGerald Schaefer val = rela->r_addend + offset; 65805bc0bcSGerald Schaefer r_sym = ELF64_R_SYM(rela->r_info); 66805bc0bcSGerald Schaefer if (r_sym) 67805bc0bcSGerald Schaefer val += dynsym[r_sym].st_value; 68805bc0bcSGerald Schaefer r_type = ELF64_R_TYPE(rela->r_info); 69805bc0bcSGerald Schaefer rc = arch_kexec_do_relocs(r_type, (void *) loc, val, 0); 70805bc0bcSGerald Schaefer if (rc) 71805bc0bcSGerald Schaefer error("Unknown relocation type"); 72805bc0bcSGerald Schaefer } 73805bc0bcSGerald Schaefer } 74805bc0bcSGerald Schaefer 758f75582aSVasily Gorbik void startup_kernel(void) 768f75582aSVasily Gorbik { 779641b8ccSMartin Schwidefsky unsigned long safe_addr; 78369f91c3SVasily Gorbik void *img; 798f75582aSVasily Gorbik 8049698745SVasily Gorbik store_ipl_parmblock(); 819641b8ccSMartin Schwidefsky safe_addr = mem_safe_offset(); 829641b8ccSMartin Schwidefsky safe_addr = read_ipl_report(safe_addr); 839641b8ccSMartin Schwidefsky uv_query_info(); 849641b8ccSMartin Schwidefsky rescue_initrd(safe_addr); 859641b8ccSMartin Schwidefsky sclp_early_read_info(); 8649698745SVasily Gorbik setup_boot_command_line(); 87b5e80459SVasily Gorbik parse_boot_command_line(); 8849698745SVasily Gorbik setup_memory_end(); 896966d604SVasily Gorbik detect_memory(); 908f75582aSVasily Gorbik if (!IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) { 91369f91c3SVasily Gorbik img = decompress_kernel(); 92369f91c3SVasily Gorbik memmove((void *)vmlinux.default_lma, img, vmlinux.image_size); 938f75582aSVasily Gorbik } 94d1b52a43SVasily Gorbik copy_bootdata(); 95805bc0bcSGerald Schaefer if (IS_ENABLED(CONFIG_RELOCATABLE)) 96805bc0bcSGerald Schaefer handle_relocs(0); 97369f91c3SVasily Gorbik vmlinux.entry(); 988f75582aSVasily Gorbik } 99