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