Merge tag 'u-boot-atmel-fixes-2020.07-a' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / arch / arc / lib / relocate.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved.
4  */
5
6 #include <common.h>
7 #include <elf.h>
8 #include <log.h>
9 #include <asm-generic/sections.h>
10
11 extern ulong __image_copy_start;
12 extern ulong __ivt_start;
13 extern ulong __ivt_end;
14 extern ulong __text_end;
15
16 DECLARE_GLOBAL_DATA_PTR;
17
18 int copy_uboot_to_ram(void)
19 {
20         size_t len = (size_t)&__image_copy_end - (size_t)&__image_copy_start;
21
22         if (gd->flags & GD_FLG_SKIP_RELOC)
23                 return 0;
24
25         memcpy((void *)gd->relocaddr, (void *)&__image_copy_start, len);
26
27         return 0;
28 }
29
30 int clear_bss(void)
31 {
32         ulong dst_addr = (ulong)&__bss_start + gd->reloc_off;
33         size_t len = (size_t)&__bss_end - (size_t)&__bss_start;
34
35         memset((void *)dst_addr, 0x00, len);
36
37         return 0;
38 }
39
40 /*
41  * Base functionality is taken from x86 version with added ARC-specifics
42  */
43 int do_elf_reloc_fixups(void)
44 {
45         Elf32_Rela *re_src = (Elf32_Rela *)(&__rel_dyn_start);
46         Elf32_Rela *re_end = (Elf32_Rela *)(&__rel_dyn_end);
47
48         if (gd->flags & GD_FLG_SKIP_RELOC)
49                 return 0;
50
51         debug("Section .rela.dyn is located at %08x-%08x\n",
52               (unsigned int)re_src, (unsigned int)re_end);
53
54         Elf32_Addr *offset_ptr_rom;
55         Elf32_Addr *offset_ptr_ram;
56
57         do {
58                 /* Get the location from the relocation entry */
59                 offset_ptr_rom = (Elf32_Addr *)re_src->r_offset;
60
61                 /* Check that the location of the relocation is in .text */
62                 if (offset_ptr_rom >= (Elf32_Addr *)&__image_copy_start &&
63                     offset_ptr_rom < (Elf32_Addr *)&__image_copy_end) {
64                         unsigned int val, do_swap = 0;
65                         /* Switch to the in-RAM version */
66                         offset_ptr_ram = (Elf32_Addr *)((ulong)offset_ptr_rom +
67                                                         gd->reloc_off);
68
69 #ifdef __LITTLE_ENDIAN__
70                         /* If location in ".text" section swap value */
71                         if (((u32)offset_ptr_rom >= (u32)&__text_start &&
72                              (u32)offset_ptr_rom <= (u32)&__text_end)
73 #if defined(__ARC700__) || defined(__ARC600__)
74                             || ((u32)offset_ptr_rom >= (u32)&__ivt_start &&
75                                 (u32)offset_ptr_rom <= (u32)&__ivt_end)
76 #endif
77                            )
78                                 do_swap = 1;
79 #endif
80
81                         debug("Patching value @ %08x (relocated to %08x)%s\n",
82                               (unsigned int)offset_ptr_rom,
83                               (unsigned int)offset_ptr_ram,
84                               do_swap ? ", middle-endian encoded" : "");
85
86                         /*
87                          * Use "memcpy" because target location might be
88                          * 16-bit aligned on ARC so we may need to read
89                          * byte-by-byte. On attempt to read entire word by
90                          * CPU throws an exception
91                          */
92                         memcpy(&val, offset_ptr_ram, sizeof(int));
93
94                         if (do_swap)
95                                 val = (val << 16) | (val >> 16);
96
97                         /* Check that the target points into executable */
98                         if (val < (unsigned int)&__image_copy_start ||
99                             val > (unsigned int)&__image_copy_end) {
100                                 /* TODO: Use panic() instead of debug()
101                                  *
102                                  * For some reason GCC might generate
103                                  * fake relocation even for LD/SC of constant
104                                  * inderectly. See an example below:
105                                  * ----------------------->8--------------------
106                                  * static int setup_mon_len(void)
107                                  * {
108                                  *         gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE;
109                                  *         return 0;
110                                  * }
111                                  * ----------------------->8--------------------
112                                  *
113                                  * And that's what we get in the binary:
114                                  * ----------------------->8--------------------
115                                  * 10005cb4 <setup_mon_len>:
116                                  * 10005cb4:       193c 3f80 0003 2f80     st      0x32f80,[r25,60]
117                                  *                         10005cb8: R_ARC_RELATIVE        *ABS*-0x10000000
118                                  * 10005cbc:       7fe0                    j_s.d   [blink]
119                                  * 10005cbe:       700c                    mov_s   r0,0
120                                  * ----------------------->8--------------------
121                                  */
122                                 debug("Relocation target %08x points outside of image\n",
123                                       val);
124                         }
125
126                         val += gd->reloc_off;
127
128                         if (do_swap)
129                                 val = (val << 16) | (val >> 16);
130
131                         memcpy(offset_ptr_ram, &val, sizeof(int));
132                 }
133         } while (++re_src < re_end);
134
135         return 0;
136 }