ARM: rockchip: rk3228: implement function rk3228_restart
[firefly-linux-kernel-4.4.55.git] / arch / arm / kernel / pie.c
1 /*
2  * Copyright 2013 Texas Instruments, Inc.
3  *      Russ Dill <russ.dill@ti.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/pie.h>
12 #include <linux/elf.h>
13
14 #include <asm/elf.h>
15 #include <asm/pie.h>
16
17 extern char __pie_rel_dyn_start[];
18 extern char __pie_rel_dyn_end[];
19 extern char __pie_tail_offset[];
20 extern char __pie_reloc_offset[];
21
22 struct arm_pie_tail {
23         int count;
24         uintptr_t offset[0];
25 };
26
27 int pie_arch_fill_tail(void *tail, void *common_start, void *common_end,
28                         void *overlay_start, void *code_start, void *code_end,
29                         void *rel_start, void *rel_end)
30 {
31         Elf32_Rel *rel;
32         int records;
33         int i;
34         struct arm_pie_tail *pie_tail = tail;
35         int count;
36         void *kern_off;
37
38         rel = (Elf32_Rel *) __pie_rel_dyn_start;
39         records = (__pie_rel_dyn_end - __pie_rel_dyn_start) / sizeof(*rel);
40
41         count = 0;
42         for (i = 0; i < records; i++, rel++) {
43                 if (ELF32_R_TYPE(rel->r_info) != R_ARM_RELATIVE)
44                         break;
45
46                 /* Adjust offset to match area in kernel */
47                 kern_off = common_start + rel->r_offset;
48
49                 if (kern_off >= common_start && kern_off < code_end) {
50                         if (tail)
51                                 pie_tail->offset[count] = rel->r_offset;
52                         count++;
53                 }
54         }
55
56         rel = (Elf32_Rel *) rel_start;
57         records = (rel_end - rel_start) / sizeof(*rel);
58
59         for (i = 0; i < records; i++, rel++) {
60                 if (ELF32_R_TYPE(rel->r_info) != R_ARM_RELATIVE)
61                         break;
62
63                 /* Adjust offset to match area in kernel */
64                 kern_off = common_start + rel->r_offset;
65
66                 if (kern_off >= common_start && kern_off < code_end) {
67                         if (tail)
68                                 pie_tail->offset[count] = rel->r_offset;
69                         count++;
70                 }
71         }
72
73         if (tail)
74                 pie_tail->count = count;
75
76         return count * sizeof(uintptr_t) + sizeof(*pie_tail);
77 }
78 EXPORT_SYMBOL_GPL(pie_arch_fill_tail);
79
80 /*
81  * R_ARM_RELATIVE: B(S) + A
82  * B(S) - Addressing origin of the output segment defining the symbol S.
83  * A - Addend for the relocation.
84  */
85 int pie_arch_fixup(struct pie_chunk *chunk, void *base, void *tail,
86                                                 unsigned long offset)
87 {
88         struct arm_pie_tail *pie_tail = tail;
89         void *reloc;
90         int i;
91
92         /* Perform relocation fixups for given offset */
93         for (i = 0; i < pie_tail->count; i++)
94                 *((uintptr_t *) (pie_tail->offset[i] + base)) += offset;
95
96         /* Store the PIE offset to tail and recol func */
97         *kern_to_pie(chunk, (uintptr_t *) __pie_tail_offset) = tail - base;
98         reloc = kern_to_pie(chunk,
99                                 (void *) fnptr_to_addr(&__pie___pie_relocate));
100         *kern_to_pie(chunk, (uintptr_t *) __pie_reloc_offset) = reloc - base;
101
102         return 0;
103 }
104 EXPORT_SYMBOL_GPL(pie_arch_fixup);