From ca728fb3c58d66324f4a7bc3d253ced4ef241ce3 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Fri, 22 Jun 2018 08:15:12 -0700 Subject: [PATCH] hv: add code to fixup ELF relocation sections For UEFI boot, currently EFI application loads hypervisor to the hard coded COMNFIG_RAM_START, which may cause it fail to boot if this address is not available in the target. This patch series resolve this issue by allocating memory for hypervisor at run time, and do relocation fixup if the allocated address is different from the base address that the hypervisor is built. The summary of the first patch [1/6] in this series: In x86_64 ELF, .rela sections hold information of symbols which must be relocated before being referenced. This patch adds code to fixup .rela sections with the relocated offset, also, it provides utilities to assist address fixup Signed-off-by: Zheng Gen Signed-off-by: Zide Chen Reviewed-by: Yin Fengwei --- hypervisor/Makefile | 1 + hypervisor/boot/include/reloc.h | 22 ++++++ hypervisor/boot/reloc.c | 133 ++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100755 hypervisor/boot/include/reloc.h create mode 100644 hypervisor/boot/reloc.c diff --git a/hypervisor/Makefile b/hypervisor/Makefile index 9fe877761..49175822e 100644 --- a/hypervisor/Makefile +++ b/hypervisor/Makefile @@ -93,6 +93,7 @@ OBJCOPY ?= objcopy D_SRCS += $(wildcard debug/*.c) C_SRCS += boot/acpi.c C_SRCS += boot/dmar_parse.c +C_SRCS += boot/reloc.c C_SRCS += arch/x86/ioapic.c C_SRCS += arch/x86/lapic.c S_SRCS += arch/x86/trampoline.S diff --git a/hypervisor/boot/include/reloc.h b/hypervisor/boot/include/reloc.h new file mode 100755 index 000000000..0b420a868 --- /dev/null +++ b/hypervisor/boot/include/reloc.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) <2018> Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RELOCATE_H +#define RELOCATE_H + +extern void _relocate(void); +extern uint64_t get_hv_image_delta(void); +extern uint64_t get_hv_image_base(void); +extern uint64_t trampoline_relo_addr(void *addr); + +/* external symbols that are helpful for relocation */ +extern uint8_t _DYNAMIC[]; + +extern uint8_t cpu_primary_start_32[]; +extern uint8_t cpu_primary_start_64[]; + +extern uint8_t trampoline_spinlock_ptr[]; + +#endif /* RELOCATE_H */ diff --git a/hypervisor/boot/reloc.c b/hypervisor/boot/reloc.c new file mode 100644 index 000000000..c6ec0aea2 --- /dev/null +++ b/hypervisor/boot/reloc.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +struct Elf64_Dyn { + uint64_t d_tag; + uint64_t d_ptr; +}; + +#define DT_NULL 0 /* end of .dynamic section */ +#define DT_RELA 7 /* relocation table */ +#define DT_RELASZ 8 /* size of reloc table */ +#define DT_RELAENT 9 /* size of one entry */ + +struct Elf64_Rel { + uint64_t r_offset; + uint64_t r_info; + uint64_t reserved; +}; + +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define R_X86_64_RELATIVE 8 + +/* get the delta between CONFIG_RAM_START and the actual load address */ +uint64_t get_hv_image_delta(void) +{ + uint64_t addr; + + asm volatile (" call 0f\n" + "0: pop %%rax\n" + " sub $0b, %%rax\n" + " mov %%rax, %0\n" + : "=m" (addr) + : + : "%rax"); + + return addr; +} + +/* get the actual Hypervisor load address */ +uint64_t get_hv_image_base(void) +{ + return (get_hv_image_delta() + CONFIG_RAM_START); +} + +/* + * Because trampoline code is relocated in different way, if HV code + * accesses trampoline using relative addressing, it needs to take + * out the HV relocation delta + * + * This function is valid if: + * - The hpa of HV code is always higher than trampoline code + * - The HV code is always relocated to higher address, compared + * with CONFIG_RAM_START + */ +uint64_t trampoline_relo_addr(void *addr) +{ + return (uint64_t)addr - get_hv_image_delta(); +} + +void _relocate(void) +{ + struct Elf64_Dyn *dyn; + struct Elf64_Rel *start = NULL, *end = NULL; + uint64_t delta, size = 0; + uint64_t trampoline_end; + uint64_t primary_32_start, primary_32_end; + uint64_t *addr; + + /* get the delta that needs to be patched */ + delta = get_hv_image_delta(); + if (delta == 0U) + return; + + /* Look for the descriptoin of relocation sections */ + for (dyn = (struct Elf64_Dyn *)_DYNAMIC; dyn->d_tag != DT_NULL; dyn++) { + switch (dyn->d_tag) { + case DT_RELA: + start = (struct Elf64_Rel *)(dyn->d_ptr + delta); + break; + case DT_RELASZ: + end = (struct Elf64_Rel *)start + dyn->d_ptr; + break; + case DT_RELAENT: + size = dyn->d_ptr; + break; + } + } + + /* Sanity check */ + if ((start == NULL) || (size == 0U)) + return; + + /* + * Need to subtract the relocation delta to get the correct + * absolute addresses + */ + trampoline_end = (uint64_t)_ld_trampoline_end - delta; + primary_32_start = (uint64_t)cpu_primary_start_32 - delta; + primary_32_end = (uint64_t)cpu_primary_start_64 - delta; + + while (start < end) { + if ((ELF64_R_TYPE(start->r_info)) == R_X86_64_RELATIVE) { + addr = (uint64_t *)(delta + start->r_offset); + + /* + * we won't fixup any trampoline.S and cpu_primary.S here + * for a number of reasons: + * + * - trampoline code itself takes another relocation, + * so any entries for trampoline symbols can't be fixed up + * through .rela sections + * - In cpu_primary.S, the 32 bits code doesn't need relocation + * - Linker option "-z noreloc-overflow" could force R_X86_32 + * to R_X86_64 in the relocation sections, which could make + * the fixed up code dirty. Even if relocation for 32 bits + * is needed in the future, it's recommended to do it + * explicitly in the assembly code to avoid confusion. + */ + if ((start->r_offset > trampoline_end) && + ((start->r_offset < primary_32_start) || + (start->r_offset > primary_32_end))) { + *addr += delta; + } + } + start = (struct Elf64_Rel *)((char *)start + size); + } +}