1734958efSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0-only
2734958efSArd Biesheuvel // Copyright 2023 Google LLC
3734958efSArd Biesheuvel // Authors: Ard Biesheuvel <ardb@google.com>
4734958efSArd Biesheuvel // Peter Collingbourne <pcc@google.com>
5734958efSArd Biesheuvel
6734958efSArd Biesheuvel #include <linux/elf.h>
7734958efSArd Biesheuvel #include <linux/init.h>
8734958efSArd Biesheuvel #include <linux/types.h>
9734958efSArd Biesheuvel
10*97a6f43bSArd Biesheuvel #include "pi.h"
11*97a6f43bSArd Biesheuvel
12734958efSArd Biesheuvel extern const Elf64_Rela rela_start[], rela_end[];
13734958efSArd Biesheuvel extern const u64 relr_start[], relr_end[];
14734958efSArd Biesheuvel
relocate_kernel(u64 offset)15734958efSArd Biesheuvel void __init relocate_kernel(u64 offset)
16734958efSArd Biesheuvel {
17734958efSArd Biesheuvel u64 *place = NULL;
18734958efSArd Biesheuvel
19734958efSArd Biesheuvel for (const Elf64_Rela *rela = rela_start; rela < rela_end; rela++) {
20734958efSArd Biesheuvel if (ELF64_R_TYPE(rela->r_info) != R_AARCH64_RELATIVE)
21734958efSArd Biesheuvel continue;
22734958efSArd Biesheuvel *(u64 *)(rela->r_offset + offset) = rela->r_addend + offset;
23734958efSArd Biesheuvel }
24734958efSArd Biesheuvel
25734958efSArd Biesheuvel if (!IS_ENABLED(CONFIG_RELR) || !offset)
26734958efSArd Biesheuvel return;
27734958efSArd Biesheuvel
28734958efSArd Biesheuvel /*
29734958efSArd Biesheuvel * Apply RELR relocations.
30734958efSArd Biesheuvel *
31734958efSArd Biesheuvel * RELR is a compressed format for storing relative relocations. The
32734958efSArd Biesheuvel * encoded sequence of entries looks like:
33734958efSArd Biesheuvel * [ AAAAAAAA BBBBBBB1 BBBBBBB1 ... AAAAAAAA BBBBBB1 ... ]
34734958efSArd Biesheuvel *
35734958efSArd Biesheuvel * i.e. start with an address, followed by any number of bitmaps. The
36734958efSArd Biesheuvel * address entry encodes 1 relocation. The subsequent bitmap entries
37734958efSArd Biesheuvel * encode up to 63 relocations each, at subsequent offsets following
38734958efSArd Biesheuvel * the last address entry.
39734958efSArd Biesheuvel *
40734958efSArd Biesheuvel * The bitmap entries must have 1 in the least significant bit. The
41734958efSArd Biesheuvel * assumption here is that an address cannot have 1 in lsb. Odd
42734958efSArd Biesheuvel * addresses are not supported. Any odd addresses are stored in the
43734958efSArd Biesheuvel * RELA section, which is handled above.
44734958efSArd Biesheuvel *
45734958efSArd Biesheuvel * With the exception of the least significant bit, each bit in the
46734958efSArd Biesheuvel * bitmap corresponds with a machine word that follows the base address
47734958efSArd Biesheuvel * word, and the bit value indicates whether or not a relocation needs
48734958efSArd Biesheuvel * to be applied to it. The second least significant bit represents the
49734958efSArd Biesheuvel * machine word immediately following the initial address, and each bit
50734958efSArd Biesheuvel * that follows represents the next word, in linear order. As such, a
51734958efSArd Biesheuvel * single bitmap can encode up to 63 relocations in a 64-bit object.
52734958efSArd Biesheuvel */
53734958efSArd Biesheuvel for (const u64 *relr = relr_start; relr < relr_end; relr++) {
54734958efSArd Biesheuvel if ((*relr & 1) == 0) {
55734958efSArd Biesheuvel place = (u64 *)(*relr + offset);
56734958efSArd Biesheuvel *place++ += offset;
57734958efSArd Biesheuvel } else {
58734958efSArd Biesheuvel for (u64 *p = place, r = *relr >> 1; r; p++, r >>= 1)
59734958efSArd Biesheuvel if (r & 1)
60734958efSArd Biesheuvel *p += offset;
61734958efSArd Biesheuvel place += 63;
62734958efSArd Biesheuvel }
63734958efSArd Biesheuvel }
64734958efSArd Biesheuvel }
65