1/*
2 * SPDX-License-Identifier: BSD-3-Clause
3 * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
4 */
5
6#include <arch.h>
7#include <asm_macros.S>
8#include <xlat_defs.h>
9
10	.globl	fixup_gdt_reloc
11
12/* ---------------------------------------------------------------------------
13 * Helper to fixup Global Descriptor table (GDT) and dynamic relocations
14 * (.rela.dyn) at runtime.
15 *
16 * This function is meant to be used when the firmware is compiled with -fpie
17 * and linked with -pie options. We rely on the linker script exporting
18 * appropriate markers for start and end of the section. For Global Offset
19 * Table (GOT), we expect 'rmm_got_start' and 'rmm_got_end' symbols to be
20 * defined. Similarly for *.rela.dyn, we expect rmm_rela_start and rmm_rela_end
21 * to be defined. We also expect `rmm_base` and `rmm_end` symbols to be
22 * defined by the linker script and are 4KB aligned. The RMM should be
23 * statically linked to start at 0x0.
24 *
25 * Clobber list: x0 to x7.
26 * ---------------------------------------------------------------------------
27 */
28
29/* Relocation codes */
30#define R_AARCH64_NONE		0
31#define R_AARCH64_RELATIVE	1027
32
33func fixup_gdt_reloc
34	/* Lower Limit for fixup */
35	mov	x0, xzr
36	/* rmm_base and rmm_end are 4KB aligned hence adrp is enough */
37	adrp	x2, rmm_base
38	adrp	x1, rmm_end
39	/* Upper Limit for fixup (rmm_end - rmm_base) */
40	sub	x1, x1, x2
41
42	/*
43	 * Since RMM will be compiled to start at 0x0, the current
44         * PC relative `rmm_base` loaded in x2 will be the Diff(S)
45	 * to be applied to the fixups.
46	 */
47	cbz	x2, 4f	/* Diff(S) = 0. No relocation needed */
48
49	adrp	x6, rmm_got_start
50	add	x6, x6, :lo12:rmm_got_start
51	adrp	x7, rmm_got_end
52	add	x7, x7, :lo12:rmm_got_end
53
54	/*
55	 * GOT is an array of 64_bit addresses which must be fixed up as
56	 * new_addr = old_addr + Diff(S).
57	 * The new_addr is the address currently the binary is executing from
58	 * and old_addr is the address at compile time.
59	 */
601:	ldr	x3, [x6]
61	/* Skip adding offset if address is < lower limit */
62	cmp	x3, x0
63	b.lo	2f
64
65	/* Skip adding offset if address is > upper limit */
66	cmp	x3, x1
67	b.hi	2f
68	add	x3, x3, x2
69	str	x3, [x6]
70
712:	add	x6, x6, #8
72	cmp	x6, x7
73	b.lo	1b
74
75	/* Starting dynamic relocations */
763:	adrp	x6, rmm_rela_start
77	add	x6, x6, :lo12:rmm_rela_start
78	adrp	x7, rmm_rela_end
79	add	x7, x7, :lo12:rmm_rela_end
80
81	/*
82	 * According to ELF-64 specification, the RELA data structure is as
83	 * follows:
84	 *	typedef struct {
85	 *		Elf64_Addr r_offset;
86	 *		Elf64_Xword r_info;
87	 *		Elf64_Sxword r_addend;
88	 *	} Elf64_Rela;
89	 *
90	 * r_offset is address of reference
91	 * r_info is symbol index and type of relocation (in this case
92	 * code 1027 which corresponds to R_AARCH64_RELATIVE).
93	 * r_addend is constant part of expression.
94	 *
95	 * Size of Elf64_Rela structure is 24 bytes.
96	 */
97
981:	ldr	x3, [x6, #8]	/* r_info */
99	/* Skip R_AARCH64_NONE entry with code 0 */
100	cbz	x3, 2f
101
102#ifndef NDEBUG
103	/* Assert that the relocation type is R_AARCH64_RELATIVE */
104	cmp	x3, #R_AARCH64_RELATIVE
105	ASM_ASSERT eq
106#endif
107	ldr	x4, [x6, #16]	/* r_addend */
108
109	/* Skip adding offset if r_addend is < lower limit */
110	cmp	x4, x0
111	b.lo	2f
112
113	/* Skip adding offset if r_addend entry is > upper limit */
114	cmp	x4, x1
115	b.hi	2f
116
117	ldr	x3, [x6]	/* r_offset */
118	add	x4, x4, x2	/* Diff(S) + r_addend */
119	str	x4, [x3, x2]
120
1212:	add	x6, x6, #24
122	cmp	x6, x7
123	b.lo	1b
124
1254:
126	ret
127endfunc fixup_gdt_reloc
128