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 <gen.zheng@intel.com>
Signed-off-by: Zide Chen <zide.chen@intel.com>
Reviewed-by: Yin Fengwei <fengwei.yin@intel.com>
This commit is contained in:
Zide Chen 2018-06-22 08:15:12 -07:00 committed by lijinxia
parent b52be90a2f
commit ca728fb3c5
3 changed files with 156 additions and 0 deletions

View File

@ -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

22
hypervisor/boot/include/reloc.h Executable file
View File

@ -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 */

133
hypervisor/boot/reloc.c Normal file
View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <hypervisor.h>
#include <reloc.h>
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);
}
}