diff options
Diffstat (limited to 'arch/powerpc/kernel/reloc_32.S')
-rw-r--r-- | arch/powerpc/kernel/reloc_32.S | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/reloc_32.S b/arch/powerpc/kernel/reloc_32.S new file mode 100644 index 000000000000..ef46ba6e094f --- /dev/null +++ b/arch/powerpc/kernel/reloc_32.S | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * Code to process dynamic relocations for PPC32. | ||
3 | * | ||
4 | * Copyrights (C) IBM Corporation, 2011. | ||
5 | * Author: Suzuki Poulose <suzuki@in.ibm.com> | ||
6 | * | ||
7 | * - Based on ppc64 code - reloc_64.S | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version | ||
12 | * 2 of the License, or (at your option) any later version. | ||
13 | */ | ||
14 | |||
15 | #include <asm/ppc_asm.h> | ||
16 | |||
17 | /* Dynamic section table entry tags */ | ||
18 | DT_RELA = 7 /* Tag for Elf32_Rela section */ | ||
19 | DT_RELASZ = 8 /* Size of the Rela relocs */ | ||
20 | DT_RELAENT = 9 /* Size of one Rela reloc entry */ | ||
21 | |||
22 | STN_UNDEF = 0 /* Undefined symbol index */ | ||
23 | STB_LOCAL = 0 /* Local binding for the symbol */ | ||
24 | |||
25 | R_PPC_ADDR16_LO = 4 /* Lower half of (S+A) */ | ||
26 | R_PPC_ADDR16_HI = 5 /* Upper half of (S+A) */ | ||
27 | R_PPC_ADDR16_HA = 6 /* High Adjusted (S+A) */ | ||
28 | R_PPC_RELATIVE = 22 | ||
29 | |||
30 | /* | ||
31 | * r3 = desired final address | ||
32 | */ | ||
33 | |||
34 | _GLOBAL(relocate) | ||
35 | |||
36 | mflr r0 /* Save our LR */ | ||
37 | bl 0f /* Find our current runtime address */ | ||
38 | 0: mflr r12 /* Make it accessible */ | ||
39 | mtlr r0 | ||
40 | |||
41 | lwz r11, (p_dyn - 0b)(r12) | ||
42 | add r11, r11, r12 /* runtime address of .dynamic section */ | ||
43 | lwz r9, (p_rela - 0b)(r12) | ||
44 | add r9, r9, r12 /* runtime address of .rela.dyn section */ | ||
45 | lwz r10, (p_st - 0b)(r12) | ||
46 | add r10, r10, r12 /* runtime address of _stext section */ | ||
47 | lwz r13, (p_sym - 0b)(r12) | ||
48 | add r13, r13, r12 /* runtime address of .dynsym section */ | ||
49 | |||
50 | /* | ||
51 | * Scan the dynamic section for RELA, RELASZ entries | ||
52 | */ | ||
53 | li r6, 0 | ||
54 | li r7, 0 | ||
55 | li r8, 0 | ||
56 | 1: lwz r5, 0(r11) /* ELF_Dyn.d_tag */ | ||
57 | cmpwi r5, 0 /* End of ELF_Dyn[] */ | ||
58 | beq eodyn | ||
59 | cmpwi r5, DT_RELA | ||
60 | bne relasz | ||
61 | lwz r7, 4(r11) /* r7 = rela.link */ | ||
62 | b skip | ||
63 | relasz: | ||
64 | cmpwi r5, DT_RELASZ | ||
65 | bne relaent | ||
66 | lwz r8, 4(r11) /* r8 = Total Rela relocs size */ | ||
67 | b skip | ||
68 | relaent: | ||
69 | cmpwi r5, DT_RELAENT | ||
70 | bne skip | ||
71 | lwz r6, 4(r11) /* r6 = Size of one Rela reloc */ | ||
72 | skip: | ||
73 | addi r11, r11, 8 | ||
74 | b 1b | ||
75 | eodyn: /* End of Dyn Table scan */ | ||
76 | |||
77 | /* Check if we have found all the entries */ | ||
78 | cmpwi r7, 0 | ||
79 | beq done | ||
80 | cmpwi r8, 0 | ||
81 | beq done | ||
82 | cmpwi r6, 0 | ||
83 | beq done | ||
84 | |||
85 | |||
86 | /* | ||
87 | * Work out the current offset from the link time address of .rela | ||
88 | * section. | ||
89 | * cur_offset[r7] = rela.run[r9] - rela.link [r7] | ||
90 | * _stext.link[r12] = _stext.run[r10] - cur_offset[r7] | ||
91 | * final_offset[r3] = _stext.final[r3] - _stext.link[r12] | ||
92 | */ | ||
93 | subf r7, r7, r9 /* cur_offset */ | ||
94 | subf r12, r7, r10 | ||
95 | subf r3, r12, r3 /* final_offset */ | ||
96 | |||
97 | subf r8, r6, r8 /* relaz -= relaent */ | ||
98 | /* | ||
99 | * Scan through the .rela table and process each entry | ||
100 | * r9 - points to the current .rela table entry | ||
101 | * r13 - points to the symbol table | ||
102 | */ | ||
103 | |||
104 | /* | ||
105 | * Check if we have a relocation based on symbol | ||
106 | * r5 will hold the value of the symbol. | ||
107 | */ | ||
108 | applyrela: | ||
109 | lwz r4, 4(r9) /* r4 = rela.r_info */ | ||
110 | srwi r5, r4, 8 /* ELF32_R_SYM(r_info) */ | ||
111 | cmpwi r5, STN_UNDEF /* sym == STN_UNDEF ? */ | ||
112 | beq get_type /* value = 0 */ | ||
113 | /* Find the value of the symbol at index(r5) */ | ||
114 | slwi r5, r5, 4 /* r5 = r5 * sizeof(Elf32_Sym) */ | ||
115 | add r12, r13, r5 /* r12 = &__dyn_sym[Index] */ | ||
116 | |||
117 | /* | ||
118 | * GNU ld has a bug, where dynamic relocs based on | ||
119 | * STB_LOCAL symbols, the value should be assumed | ||
120 | * to be zero. - Alan Modra | ||
121 | */ | ||
122 | /* XXX: Do we need to check if we are using GNU ld ? */ | ||
123 | lbz r5, 12(r12) /* r5 = dyn_sym[Index].st_info */ | ||
124 | extrwi r5, r5, 4, 24 /* r5 = ELF32_ST_BIND(r5) */ | ||
125 | cmpwi r5, STB_LOCAL /* st_value = 0, ld bug */ | ||
126 | beq get_type /* We have r5 = 0 */ | ||
127 | lwz r5, 4(r12) /* r5 = __dyn_sym[Index].st_value */ | ||
128 | |||
129 | get_type: | ||
130 | /* Load the relocation type to r4 */ | ||
131 | extrwi r4, r4, 8, 24 /* r4 = ELF32_R_TYPE(r_info) = ((char*)r4)[3] */ | ||
132 | |||
133 | /* R_PPC_RELATIVE */ | ||
134 | cmpwi r4, R_PPC_RELATIVE | ||
135 | bne hi16 | ||
136 | lwz r4, 0(r9) /* r_offset */ | ||
137 | lwz r0, 8(r9) /* r_addend */ | ||
138 | add r0, r0, r3 /* final addend */ | ||
139 | stwx r0, r4, r7 /* memory[r4+r7]) = (u32)r0 */ | ||
140 | b nxtrela /* continue */ | ||
141 | |||
142 | /* R_PPC_ADDR16_HI */ | ||
143 | hi16: | ||
144 | cmpwi r4, R_PPC_ADDR16_HI | ||
145 | bne ha16 | ||
146 | lwz r4, 0(r9) /* r_offset */ | ||
147 | lwz r0, 8(r9) /* r_addend */ | ||
148 | add r0, r0, r3 | ||
149 | add r0, r0, r5 /* r0 = (S+A+Offset) */ | ||
150 | extrwi r0, r0, 16, 0 /* r0 = (r0 >> 16) */ | ||
151 | b store_half | ||
152 | |||
153 | /* R_PPC_ADDR16_HA */ | ||
154 | ha16: | ||
155 | cmpwi r4, R_PPC_ADDR16_HA | ||
156 | bne lo16 | ||
157 | lwz r4, 0(r9) /* r_offset */ | ||
158 | lwz r0, 8(r9) /* r_addend */ | ||
159 | add r0, r0, r3 | ||
160 | add r0, r0, r5 /* r0 = (S+A+Offset) */ | ||
161 | extrwi r5, r0, 1, 16 /* Extract bit 16 */ | ||
162 | extrwi r0, r0, 16, 0 /* r0 = (r0 >> 16) */ | ||
163 | add r0, r0, r5 /* Add it to r0 */ | ||
164 | b store_half | ||
165 | |||
166 | /* R_PPC_ADDR16_LO */ | ||
167 | lo16: | ||
168 | cmpwi r4, R_PPC_ADDR16_LO | ||
169 | bne nxtrela | ||
170 | lwz r4, 0(r9) /* r_offset */ | ||
171 | lwz r0, 8(r9) /* r_addend */ | ||
172 | add r0, r0, r3 | ||
173 | add r0, r0, r5 /* r0 = (S+A+Offset) */ | ||
174 | extrwi r0, r0, 16, 16 /* r0 &= 0xffff */ | ||
175 | /* Fall through to */ | ||
176 | |||
177 | /* Store half word */ | ||
178 | store_half: | ||
179 | sthx r0, r4, r7 /* memory[r4+r7] = (u16)r0 */ | ||
180 | |||
181 | nxtrela: | ||
182 | /* | ||
183 | * We have to flush the modified instructions to the | ||
184 | * main storage from the d-cache. And also, invalidate the | ||
185 | * cached instructions in i-cache which has been modified. | ||
186 | * | ||
187 | * We delay the sync / isync operation till the end, since | ||
188 | * we won't be executing the modified instructions until | ||
189 | * we return from here. | ||
190 | */ | ||
191 | dcbst r4,r7 | ||
192 | sync /* Ensure the data is flushed before icbi */ | ||
193 | icbi r4,r7 | ||
194 | cmpwi r8, 0 /* relasz = 0 ? */ | ||
195 | ble done | ||
196 | add r9, r9, r6 /* move to next entry in the .rela table */ | ||
197 | subf r8, r6, r8 /* relasz -= relaent */ | ||
198 | b applyrela | ||
199 | |||
200 | done: | ||
201 | sync /* Wait for the flush to finish */ | ||
202 | isync /* Discard prefetched instructions */ | ||
203 | blr | ||
204 | |||
205 | p_dyn: .long __dynamic_start - 0b | ||
206 | p_rela: .long __rela_dyn_start - 0b | ||
207 | p_sym: .long __dynamic_symtab - 0b | ||
208 | p_st: .long _stext - 0b | ||