aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorSuzuki Poulose <suzuki@in.ibm.com>2011-12-14 17:58:12 -0500
committerJosh Boyer <jwboyer@gmail.com>2011-12-20 10:21:08 -0500
commit9c5f7d39a86316cd13baf973c90ed27f9f1cc979 (patch)
tree615117060a4b8b87cf1496abca0fe0365e845388 /arch/powerpc/kernel
parent239132454583d474932d8835f87a244f6f1bff9e (diff)
powerpc: Process dynamic relocations for kernel
The following patch implements the dynamic relocation processing for PPC32 kernel. relocate() accepts the target virtual address and relocates the kernel image to the same. Currently the following relocation types are handled : R_PPC_RELATIVE R_PPC_ADDR16_LO R_PPC_ADDR16_HI R_PPC_ADDR16_HA The last 3 relocations in the above list depends on value of Symbol indexed whose index is encoded in the Relocation entry. Hence we need the Symbol Table for processing such relocations. Note: The GNU ld for ppc32 produces buggy relocations for relocation types that depend on symbols. The value of the symbols with STB_LOCAL scope should be assumed to be zero. - Alan Modra Signed-off-by: Suzuki K. Poulose <suzuki@in.ibm.com> Signed-off-by: Josh Poimboeuf <jpoimboe@linux.vnet.ibm.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Alan Modra <amodra@au1.ibm.com> Cc: Kumar Gala <galak@kernel.crashing.org> Cc: linuxppc-dev <linuxppc-dev@lists.ozlabs.org> Signed-off-by: Josh Boyer <jwboyer@gmail.com>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/Makefile2
-rw-r--r--arch/powerpc/kernel/reloc_32.S208
-rw-r--r--arch/powerpc/kernel/vmlinux.lds.S8
3 files changed, 217 insertions, 1 deletions
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index ce4f7f179117..ee728e433aa2 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -85,6 +85,8 @@ extra-$(CONFIG_FSL_BOOKE) := head_fsl_booke.o
85extra-$(CONFIG_8xx) := head_8xx.o 85extra-$(CONFIG_8xx) := head_8xx.o
86extra-y += vmlinux.lds 86extra-y += vmlinux.lds
87 87
88obj-$(CONFIG_RELOCATABLE_PPC32) += reloc_32.o
89
88obj-$(CONFIG_PPC32) += entry_32.o setup_32.o 90obj-$(CONFIG_PPC32) += entry_32.o setup_32.o
89obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o 91obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o
90obj-$(CONFIG_KGDB) += kgdb.o 92obj-$(CONFIG_KGDB) += kgdb.o
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 */
18DT_RELA = 7 /* Tag for Elf32_Rela section */
19DT_RELASZ = 8 /* Size of the Rela relocs */
20DT_RELAENT = 9 /* Size of one Rela reloc entry */
21
22STN_UNDEF = 0 /* Undefined symbol index */
23STB_LOCAL = 0 /* Local binding for the symbol */
24
25R_PPC_ADDR16_LO = 4 /* Lower half of (S+A) */
26R_PPC_ADDR16_HI = 5 /* Upper half of (S+A) */
27R_PPC_ADDR16_HA = 6 /* High Adjusted (S+A) */
28R_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 */
380: 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
561: 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
63relasz:
64 cmpwi r5, DT_RELASZ
65 bne relaent
66 lwz r8, 4(r11) /* r8 = Total Rela relocs size */
67 b skip
68relaent:
69 cmpwi r5, DT_RELAENT
70 bne skip
71 lwz r6, 4(r11) /* r6 = Size of one Rela reloc */
72skip:
73 addi r11, r11, 8
74 b 1b
75eodyn: /* 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 */
108applyrela:
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
129get_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 */
143hi16:
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 */
154ha16:
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 */
167lo16:
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 */
178store_half:
179 sthx r0, r4, r7 /* memory[r4+r7] = (u16)r0 */
180
181nxtrela:
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
200done:
201 sync /* Wait for the flush to finish */
202 isync /* Discard prefetched instructions */
203 blr
204
205p_dyn: .long __dynamic_start - 0b
206p_rela: .long __rela_dyn_start - 0b
207p_sym: .long __dynamic_symtab - 0b
208p_st: .long _stext - 0b
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index 920276c0f6a1..710a54005dfb 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -170,7 +170,13 @@ SECTIONS
170 } 170 }
171#ifdef CONFIG_RELOCATABLE 171#ifdef CONFIG_RELOCATABLE
172 . = ALIGN(8); 172 . = ALIGN(8);
173 .dynsym : AT(ADDR(.dynsym) - LOAD_OFFSET) { *(.dynsym) } 173 .dynsym : AT(ADDR(.dynsym) - LOAD_OFFSET)
174 {
175#ifdef CONFIG_RELOCATABLE_PPC32
176 __dynamic_symtab = .;
177#endif
178 *(.dynsym)
179 }
174 .dynstr : AT(ADDR(.dynstr) - LOAD_OFFSET) { *(.dynstr) } 180 .dynstr : AT(ADDR(.dynstr) - LOAD_OFFSET) { *(.dynstr) }
175 .dynamic : AT(ADDR(.dynamic) - LOAD_OFFSET) 181 .dynamic : AT(ADDR(.dynamic) - LOAD_OFFSET)
176 { 182 {