/* * Copyright (C) 2018 Intel Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #ifdef CONFIG_RELOC #define DT_NULL 0U /* end of .dynamic section */ #define DT_RELA 7U /* relocation table */ #define DT_RELASZ 8U /* size of reloc table */ #define DT_RELAENT 9U /* size of one entry */ #define R_X86_64_RELATIVE 8UL struct Elf64_Dyn { uint64_t d_tag; uint64_t d_ptr; }; struct Elf64_Rel { uint64_t r_offset; uint64_t r_info; uint64_t reserved; }; #endif static inline uint64_t elf64_r_type(uint64_t i) { return (i & 0xffffffffUL); } /* get the delta between CONFIG_HV_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 (HVA) */ uint64_t get_hv_image_base(void) { return (get_hv_image_delta() + CONFIG_HV_RAM_START); } void relocate(void) { #ifdef CONFIG_RELOC 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) { /* 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; default: /* if no RELA/RELASZ found, both start and end will be initialized to NULL, and later while loop won't be executed */ break; } } /* * 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); } } #endif }