xref: /OK3568_Linux_fs/u-boot/arch/mips/lib/reloc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * MIPS Relocation
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (c) 2017 Imagination Technologies Ltd.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0+
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Relocation data, found in the .rel section, is generated by the mips-relocs
9*4882a593Smuzhiyun  * tool & contains a record of all locations in the U-Boot binary that need to
10*4882a593Smuzhiyun  * be fixed up during relocation.
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * The data is a sequence of unsigned integers, which are of somewhat arbitrary
13*4882a593Smuzhiyun  * size. This is achieved by encoding integers as a sequence of bytes, each of
14*4882a593Smuzhiyun  * which contains 7 bits of data with the most significant bit indicating
15*4882a593Smuzhiyun  * whether any further bytes need to be read. The least significant bits of the
16*4882a593Smuzhiyun  * integer are found in the first byte - ie. it somewhat resembles little
17*4882a593Smuzhiyun  * endian.
18*4882a593Smuzhiyun  *
19*4882a593Smuzhiyun  * Each pair of two integers represents a relocation that must be applied. The
20*4882a593Smuzhiyun  * first integer represents the type of relocation as a standard ELF relocation
21*4882a593Smuzhiyun  * type (ie. R_MIPS_*). The second integer represents the offset at which to
22*4882a593Smuzhiyun  * apply the relocation, relative to the previous relocation or for the first
23*4882a593Smuzhiyun  * relocation the start of the relocated .text section.
24*4882a593Smuzhiyun  *
25*4882a593Smuzhiyun  * The end of the relocation data is indicated when type R_MIPS_NONE (0) is
26*4882a593Smuzhiyun  * read, at which point no further integers should be read. That is, the
27*4882a593Smuzhiyun  * terminating R_MIPS_NONE reloc includes no offset.
28*4882a593Smuzhiyun  */
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #include <common.h>
31*4882a593Smuzhiyun #include <asm/relocs.h>
32*4882a593Smuzhiyun #include <asm/sections.h>
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun /**
35*4882a593Smuzhiyun  * read_uint() - Read an unsigned integer from the buffer
36*4882a593Smuzhiyun  * @buf: pointer to a pointer to the reloc buffer
37*4882a593Smuzhiyun  *
38*4882a593Smuzhiyun  * Read one whole unsigned integer from the relocation data pointed to by @buf,
39*4882a593Smuzhiyun  * advancing @buf past the bytes encoding the integer.
40*4882a593Smuzhiyun  *
41*4882a593Smuzhiyun  * Returns: the integer read from @buf
42*4882a593Smuzhiyun  */
read_uint(uint8_t ** buf)43*4882a593Smuzhiyun static unsigned long read_uint(uint8_t **buf)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun 	unsigned long val = 0;
46*4882a593Smuzhiyun 	unsigned int shift = 0;
47*4882a593Smuzhiyun 	uint8_t new;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	do {
50*4882a593Smuzhiyun 		new = *(*buf)++;
51*4882a593Smuzhiyun 		val |= (new & 0x7f) << shift;
52*4882a593Smuzhiyun 		shift += 7;
53*4882a593Smuzhiyun 	} while (new & 0x80);
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	return val;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun /**
59*4882a593Smuzhiyun  * apply_reloc() - Apply a single relocation
60*4882a593Smuzhiyun  * @type: the type of reloc (R_MIPS_*)
61*4882a593Smuzhiyun  * @addr: the address that the reloc should be applied to
62*4882a593Smuzhiyun  * @off: the relocation offset, ie. number of bytes we're moving U-Boot by
63*4882a593Smuzhiyun  *
64*4882a593Smuzhiyun  * Apply a single relocation of type @type at @addr. This function is
65*4882a593Smuzhiyun  * intentionally simple, and does the bare minimum needed to fixup the
66*4882a593Smuzhiyun  * relocated U-Boot - in particular, it does not check for overflows.
67*4882a593Smuzhiyun  */
apply_reloc(unsigned int type,void * addr,long off)68*4882a593Smuzhiyun static void apply_reloc(unsigned int type, void *addr, long off)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	uint32_t u32;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	switch (type) {
73*4882a593Smuzhiyun 	case R_MIPS_26:
74*4882a593Smuzhiyun 		u32 = *(uint32_t *)addr;
75*4882a593Smuzhiyun 		u32 = (u32 & GENMASK(31, 26)) |
76*4882a593Smuzhiyun 		      ((u32 + (off >> 2)) & GENMASK(25, 0));
77*4882a593Smuzhiyun 		*(uint32_t *)addr = u32;
78*4882a593Smuzhiyun 		break;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	case R_MIPS_32:
81*4882a593Smuzhiyun 		*(uint32_t *)addr += off;
82*4882a593Smuzhiyun 		break;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	case R_MIPS_64:
85*4882a593Smuzhiyun 		*(uint64_t *)addr += off;
86*4882a593Smuzhiyun 		break;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	case R_MIPS_HI16:
89*4882a593Smuzhiyun 		*(uint32_t *)addr += off >> 16;
90*4882a593Smuzhiyun 		break;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	default:
93*4882a593Smuzhiyun 		panic("Unhandled reloc type %u\n", type);
94*4882a593Smuzhiyun 	}
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun /**
98*4882a593Smuzhiyun  * relocate_code() - Relocate U-Boot, generally from flash to DDR
99*4882a593Smuzhiyun  * @start_addr_sp: new stack pointer
100*4882a593Smuzhiyun  * @new_gd: pointer to relocated global data
101*4882a593Smuzhiyun  * @relocaddr: the address to relocate to
102*4882a593Smuzhiyun  *
103*4882a593Smuzhiyun  * Relocate U-Boot from its current location (generally in flash) to a new one
104*4882a593Smuzhiyun  * (generally in DDR). This function will copy the U-Boot binary & apply
105*4882a593Smuzhiyun  * relocations as necessary, then jump to board_init_r in the new build of
106*4882a593Smuzhiyun  * U-Boot. As such, this function does not return.
107*4882a593Smuzhiyun  */
relocate_code(ulong start_addr_sp,gd_t * new_gd,ulong relocaddr)108*4882a593Smuzhiyun void relocate_code(ulong start_addr_sp, gd_t *new_gd, ulong relocaddr)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun 	unsigned long addr, length, bss_len;
111*4882a593Smuzhiyun 	uint8_t *buf, *bss_start;
112*4882a593Smuzhiyun 	unsigned int type;
113*4882a593Smuzhiyun 	long off;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	/*
116*4882a593Smuzhiyun 	 * Ensure that we're relocating by an offset which is a multiple of
117*4882a593Smuzhiyun 	 * 64KiB, ie. doesn't change the least significant 16 bits of any
118*4882a593Smuzhiyun 	 * addresses. This allows us to discard R_MIPS_LO16 relocs, saving
119*4882a593Smuzhiyun 	 * space in the U-Boot binary & complexity in handling them.
120*4882a593Smuzhiyun 	 */
121*4882a593Smuzhiyun 	off = relocaddr - (unsigned long)__text_start;
122*4882a593Smuzhiyun 	if (off & 0xffff)
123*4882a593Smuzhiyun 		panic("Mis-aligned relocation\n");
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	/* Copy U-Boot to RAM */
126*4882a593Smuzhiyun 	length = __image_copy_end - __text_start;
127*4882a593Smuzhiyun 	memcpy((void *)relocaddr, __text_start, length);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	/* Now apply relocations to the copy in RAM */
130*4882a593Smuzhiyun 	buf = __rel_start;
131*4882a593Smuzhiyun 	addr = relocaddr;
132*4882a593Smuzhiyun 	while (true) {
133*4882a593Smuzhiyun 		type = read_uint(&buf);
134*4882a593Smuzhiyun 		if (type == R_MIPS_NONE)
135*4882a593Smuzhiyun 			break;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 		addr += read_uint(&buf) << 2;
138*4882a593Smuzhiyun 		apply_reloc(type, (void *)addr, off);
139*4882a593Smuzhiyun 	}
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	/* Ensure the icache is coherent */
142*4882a593Smuzhiyun 	flush_cache(relocaddr, length);
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	/* Clear the .bss section */
145*4882a593Smuzhiyun 	bss_start = (uint8_t *)((unsigned long)__bss_start + off);
146*4882a593Smuzhiyun 	bss_len = (unsigned long)&__bss_end - (unsigned long)__bss_start;
147*4882a593Smuzhiyun 	memset(bss_start, 0, bss_len);
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	/* Jump to the relocated U-Boot */
150*4882a593Smuzhiyun 	asm volatile(
151*4882a593Smuzhiyun 		       "move	$29, %0\n"
152*4882a593Smuzhiyun 		"	move	$4, %1\n"
153*4882a593Smuzhiyun 		"	move	$5, %2\n"
154*4882a593Smuzhiyun 		"	move	$31, $0\n"
155*4882a593Smuzhiyun 		"	jr	%3"
156*4882a593Smuzhiyun 		: /* no outputs */
157*4882a593Smuzhiyun 		: "r"(start_addr_sp),
158*4882a593Smuzhiyun 		  "r"(new_gd),
159*4882a593Smuzhiyun 		  "r"(relocaddr),
160*4882a593Smuzhiyun 		  "r"((unsigned long)board_init_r + off));
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	/* Since we jumped to the new U-Boot above, we won't get here */
163*4882a593Smuzhiyun 	unreachable();
164*4882a593Smuzhiyun }
165