linux-stable/arch/s390/boot/startup.c
Gerald Schaefer 805bc0bc23 s390/kernel: build a relocatable kernel
This patch adds support for building a relocatable kernel with -fPIE.
The kernel will be relocated to 0 early in the boot process.

Signed-off-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Reviewed-by: Philipp Rudo <prudo@linux.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2019-04-29 10:47:10 +02:00

98 lines
2.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/string.h>
#include <linux/elf.h>
#include <asm/setup.h>
#include <asm/kexec.h>
#include <asm/sclp.h>
#include <asm/uv.h>
#include "compressed/decompressor.h"
#include "boot.h"
extern char __boot_data_start[], __boot_data_end[];
extern char __boot_data_preserved_start[], __boot_data_preserved_end[];
void error(char *x)
{
sclp_early_printk("\n\n");
sclp_early_printk(x);
sclp_early_printk("\n\n -- System halted");
disabled_wait(0xdeadbeef);
}
#ifdef CONFIG_KERNEL_UNCOMPRESSED
unsigned long mem_safe_offset(void)
{
return vmlinux.default_lma + vmlinux.image_size + vmlinux.bss_size;
}
#endif
static void rescue_initrd(unsigned long addr)
{
if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD))
return;
if (!INITRD_START || !INITRD_SIZE)
return;
if (addr <= INITRD_START)
return;
memmove((void *)addr, (void *)INITRD_START, INITRD_SIZE);
INITRD_START = addr;
}
static void copy_bootdata(void)
{
if (__boot_data_end - __boot_data_start != vmlinux.bootdata_size)
error(".boot.data section size mismatch");
memcpy((void *)vmlinux.bootdata_off, __boot_data_start, vmlinux.bootdata_size);
if (__boot_data_preserved_end - __boot_data_preserved_start != vmlinux.bootdata_preserved_size)
error(".boot.preserved.data section size mismatch");
memcpy((void *)vmlinux.bootdata_preserved_off, __boot_data_preserved_start, vmlinux.bootdata_preserved_size);
}
static void handle_relocs(unsigned long offset)
{
Elf64_Rela *rela_start, *rela_end, *rela;
int r_type, r_sym, rc;
Elf64_Addr loc, val;
Elf64_Sym *dynsym;
rela_start = (Elf64_Rela *) vmlinux.rela_dyn_start;
rela_end = (Elf64_Rela *) vmlinux.rela_dyn_end;
dynsym = (Elf64_Sym *) vmlinux.dynsym_start;
for (rela = rela_start; rela < rela_end; rela++) {
loc = rela->r_offset + offset;
val = rela->r_addend + offset;
r_sym = ELF64_R_SYM(rela->r_info);
if (r_sym)
val += dynsym[r_sym].st_value;
r_type = ELF64_R_TYPE(rela->r_info);
rc = arch_kexec_do_relocs(r_type, (void *) loc, val, 0);
if (rc)
error("Unknown relocation type");
}
}
void startup_kernel(void)
{
unsigned long safe_addr;
void *img;
store_ipl_parmblock();
safe_addr = mem_safe_offset();
safe_addr = read_ipl_report(safe_addr);
uv_query_info();
rescue_initrd(safe_addr);
sclp_early_read_info();
setup_boot_command_line();
parse_boot_command_line();
setup_memory_end();
detect_memory();
if (!IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) {
img = decompress_kernel();
memmove((void *)vmlinux.default_lma, img, vmlinux.image_size);
}
copy_bootdata();
if (IS_ENABLED(CONFIG_RELOCATABLE))
handle_relocs(0);
vmlinux.entry();
}