/*
* Copyright (c) 2019 Petr Pavlu
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @addtogroup boot_arm64
* @{
*/
/** @file
* @brief Image self-relocation support.
*/
#include <arch/relocate.h>
#include <printf.h>
/** Self-relocate the bootloader.
*
* Note: This code is responsible for self-relocating the bootloader when it is
* started. As such, it is required that it is written in a way that it itself
* does not need any dynamic relocation.
*/
efi_status_t self_relocate(uintptr_t base, const elf_dyn_t *dyn)
{
const elf_rela_t *rela = NULL;
uint64_t relasz = 0;
uint64_t relaent = 0;
/* Parse the dynamic array. */
while (dyn->d_tag != DT_NULL) {
switch (dyn->d_tag) {
case DT_RELA:
rela = (const elf_rela_t *) (base + dyn->d_un.d_ptr);
break;
case DT_RELASZ:
relasz = dyn->d_un.d_val;
break;
case DT_RELAENT:
relaent = dyn->d_un.d_val;
break;
}
dyn++;
}
/* Validate obtained information. */
if (rela == NULL)
return EFI_SUCCESS;
if (relaent == 0 || relasz % relaent != 0)
return EFI_UNSUPPORTED;
/* Process relocations in the image. */
while (relasz > 0) {
switch (ELF_R_TYPE(rela->r_info)) {
case R_AARCH64_RELATIVE:
*(uint64_t *) (base + rela->r_offset) =
base + rela->r_addend;
break;
default:
return EFI_UNSUPPORTED;
}
rela = (const elf_rela_t *) ((const char *) rela + relaent);
relasz -= relaent;
}
return EFI_SUCCESS;
}
/** @}
*/