mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-22 11:13:07 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
163 lines
4.7 KiB
C
163 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* MIPS Relocation
|
|
*
|
|
* Copyright (c) 2017 Imagination Technologies Ltd.
|
|
*
|
|
* Relocation data, found in the .rel section, is generated by the mips-relocs
|
|
* tool & contains a record of all locations in the U-Boot binary that need to
|
|
* be fixed up during relocation.
|
|
*
|
|
* The data is a sequence of unsigned integers, which are of somewhat arbitrary
|
|
* size. This is achieved by encoding integers as a sequence of bytes, each of
|
|
* which contains 7 bits of data with the most significant bit indicating
|
|
* whether any further bytes need to be read. The least significant bits of the
|
|
* integer are found in the first byte - ie. it somewhat resembles little
|
|
* endian.
|
|
*
|
|
* Each pair of two integers represents a relocation that must be applied. The
|
|
* first integer represents the type of relocation as a standard ELF relocation
|
|
* type (ie. R_MIPS_*). The second integer represents the offset at which to
|
|
* apply the relocation, relative to the previous relocation or for the first
|
|
* relocation the start of the relocated .text section.
|
|
*
|
|
* The end of the relocation data is indicated when type R_MIPS_NONE (0) is
|
|
* read, at which point no further integers should be read. That is, the
|
|
* terminating R_MIPS_NONE reloc includes no offset.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/relocs.h>
|
|
#include <asm/sections.h>
|
|
|
|
/**
|
|
* read_uint() - Read an unsigned integer from the buffer
|
|
* @buf: pointer to a pointer to the reloc buffer
|
|
*
|
|
* Read one whole unsigned integer from the relocation data pointed to by @buf,
|
|
* advancing @buf past the bytes encoding the integer.
|
|
*
|
|
* Returns: the integer read from @buf
|
|
*/
|
|
static unsigned long read_uint(uint8_t **buf)
|
|
{
|
|
unsigned long val = 0;
|
|
unsigned int shift = 0;
|
|
uint8_t new;
|
|
|
|
do {
|
|
new = *(*buf)++;
|
|
val |= (new & 0x7f) << shift;
|
|
shift += 7;
|
|
} while (new & 0x80);
|
|
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* apply_reloc() - Apply a single relocation
|
|
* @type: the type of reloc (R_MIPS_*)
|
|
* @addr: the address that the reloc should be applied to
|
|
* @off: the relocation offset, ie. number of bytes we're moving U-Boot by
|
|
*
|
|
* Apply a single relocation of type @type at @addr. This function is
|
|
* intentionally simple, and does the bare minimum needed to fixup the
|
|
* relocated U-Boot - in particular, it does not check for overflows.
|
|
*/
|
|
static void apply_reloc(unsigned int type, void *addr, long off)
|
|
{
|
|
uint32_t u32;
|
|
|
|
switch (type) {
|
|
case R_MIPS_26:
|
|
u32 = *(uint32_t *)addr;
|
|
u32 = (u32 & GENMASK(31, 26)) |
|
|
((u32 + (off >> 2)) & GENMASK(25, 0));
|
|
*(uint32_t *)addr = u32;
|
|
break;
|
|
|
|
case R_MIPS_32:
|
|
*(uint32_t *)addr += off;
|
|
break;
|
|
|
|
case R_MIPS_64:
|
|
*(uint64_t *)addr += off;
|
|
break;
|
|
|
|
case R_MIPS_HI16:
|
|
*(uint32_t *)addr += off >> 16;
|
|
break;
|
|
|
|
default:
|
|
panic("Unhandled reloc type %u\n", type);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* relocate_code() - Relocate U-Boot, generally from flash to DDR
|
|
* @start_addr_sp: new stack pointer
|
|
* @new_gd: pointer to relocated global data
|
|
* @relocaddr: the address to relocate to
|
|
*
|
|
* Relocate U-Boot from its current location (generally in flash) to a new one
|
|
* (generally in DDR). This function will copy the U-Boot binary & apply
|
|
* relocations as necessary, then jump to board_init_r in the new build of
|
|
* U-Boot. As such, this function does not return.
|
|
*/
|
|
void relocate_code(ulong start_addr_sp, gd_t *new_gd, ulong relocaddr)
|
|
{
|
|
unsigned long addr, length, bss_len;
|
|
uint8_t *buf, *bss_start;
|
|
unsigned int type;
|
|
long off;
|
|
|
|
/*
|
|
* Ensure that we're relocating by an offset which is a multiple of
|
|
* 64KiB, ie. doesn't change the least significant 16 bits of any
|
|
* addresses. This allows us to discard R_MIPS_LO16 relocs, saving
|
|
* space in the U-Boot binary & complexity in handling them.
|
|
*/
|
|
off = relocaddr - (unsigned long)__text_start;
|
|
if (off & 0xffff)
|
|
panic("Mis-aligned relocation\n");
|
|
|
|
/* Copy U-Boot to RAM */
|
|
length = __image_copy_end - __text_start;
|
|
memcpy((void *)relocaddr, __text_start, length);
|
|
|
|
/* Now apply relocations to the copy in RAM */
|
|
buf = __rel_start;
|
|
addr = relocaddr;
|
|
while (true) {
|
|
type = read_uint(&buf);
|
|
if (type == R_MIPS_NONE)
|
|
break;
|
|
|
|
addr += read_uint(&buf) << 2;
|
|
apply_reloc(type, (void *)addr, off);
|
|
}
|
|
|
|
/* Ensure the icache is coherent */
|
|
flush_cache(relocaddr, length);
|
|
|
|
/* Clear the .bss section */
|
|
bss_start = (uint8_t *)((unsigned long)__bss_start + off);
|
|
bss_len = (unsigned long)&__bss_end - (unsigned long)__bss_start;
|
|
memset(bss_start, 0, bss_len);
|
|
|
|
/* Jump to the relocated U-Boot */
|
|
asm volatile(
|
|
"move $29, %0\n"
|
|
" move $4, %1\n"
|
|
" move $5, %2\n"
|
|
" move $31, $0\n"
|
|
" jr %3"
|
|
: /* no outputs */
|
|
: "r"(start_addr_sp),
|
|
"r"(new_gd),
|
|
"r"(relocaddr),
|
|
"r"((unsigned long)board_init_r + off));
|
|
|
|
/* Since we jumped to the new U-Boot above, we won't get here */
|
|
unreachable();
|
|
}
|