sparc32: Move cache and TLB flushes over to method ops.
[platform/adaptation/renesas_rcar/renesas_kernel.git] / arch / sparc / mm / btfixup.c
1 /* btfixup.c: Boot time code fixup and relocator, so that
2  * we can get rid of most indirect calls to achieve single
3  * image sun4c and srmmu kernel.
4  *
5  * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
6  */
7
8 #include <linux/kernel.h>
9 #include <linux/init.h>
10 #include <asm/btfixup.h>
11 #include <asm/page.h>
12 #include <asm/pgalloc.h>
13 #include <asm/pgtable.h>
14 #include <asm/oplib.h>
15 #include <asm/cacheflush.h>
16
17 #define BTFIXUP_OPTIMIZE_NOP
18 #define BTFIXUP_OPTIMIZE_OTHER
19
20 extern char *srmmu_name;
21 static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for ";
22 static char str_srmmu[] __initdata = "srmmu[%s]/";
23 static char str_iommu[] __initdata = "iommu\n";
24 static char str_iounit[] __initdata = "io-unit\n";
25
26 static int visited __initdata = 0;
27 extern unsigned int ___btfixup_start[], ___btfixup_end[], __init_begin[], __init_end[], __init_text_end[];
28 extern unsigned int _stext[], _end[], __start___ksymtab[], __stop___ksymtab[];
29 static char wrong_f[] __initdata = "Trying to set f fixup %p to invalid function %08x\n";
30 static char wrong_b[] __initdata = "Trying to set b fixup %p to invalid function %08x\n";
31 static char wrong_s[] __initdata = "Trying to set s fixup %p to invalid value %08x\n";
32 static char wrong_h[] __initdata = "Trying to set h fixup %p to invalid value %08x\n";
33 static char wrong_a[] __initdata = "Trying to set a fixup %p to invalid value %08x\n";
34 static char wrong[] __initdata = "Wrong address for %c fixup %p\n";
35 static char insn_f[] __initdata = "Fixup f %p refers to weird instructions at %p[%08x,%08x]\n";
36 static char insn_b[] __initdata = "Fixup b %p doesn't refer to a SETHI at %p[%08x]\n";
37 static char insn_s[] __initdata = "Fixup s %p doesn't refer to an OR at %p[%08x]\n";
38 static char insn_h[] __initdata = "Fixup h %p doesn't refer to a SETHI at %p[%08x]\n";
39 static char insn_a[] __initdata = "Fixup a %p doesn't refer to a SETHI nor OR at %p[%08x]\n";
40 static char insn_i[] __initdata = "Fixup i %p doesn't refer to a valid instruction at %p[%08x]\n";
41 static char wrong_setaddr[] __initdata = "Garbled CALL/INT patch at %p[%08x,%08x,%08x]=%08x\n";
42
43 #ifdef BTFIXUP_OPTIMIZE_OTHER
44 static void __init set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value)
45 {
46         if (!fmangled)
47                 *addr = value;
48         else {
49                 unsigned int *q = (unsigned int *)q1;
50                 if (*addr == 0x01000000) {
51                         /* Noped */
52                         *q = value;
53                 } else if (addr[-1] == *q) {
54                         /* Moved */
55                         addr[-1] = value;
56                         *q = value;
57                 } else {
58                         prom_printf(wrong_setaddr, addr-1, addr[-1], *addr, *q, value);
59                         prom_halt();
60                 }
61         }
62 }
63 #else
64 static inline void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value)
65 {
66         *addr = value;
67 }
68 #endif
69
70 void __init btfixup(void)
71 {
72         unsigned int *p, *q;
73         int type, count;
74         unsigned insn;
75         unsigned *addr;
76         int fmangled = 0;
77         
78         if (!visited) {
79                 visited++;
80                 printk(version);
81                 printk(str_srmmu, srmmu_name);
82                 if (sparc_cpu_model == sun4d)
83                         printk(str_iounit);
84                 else
85                         printk(str_iommu);
86         }
87         for (p = ___btfixup_start; p < ___btfixup_end; ) {
88                 count = p[2];
89                 q = p + 3;
90                 switch (type = *(unsigned char *)p) {
91                 case 'f': 
92                         count = p[3];
93                         q = p + 4;
94                         if (((p[0] & 1) || p[1]) 
95                             && ((p[1] & 3) || (unsigned *)(p[1]) < _stext || (unsigned *)(p[1]) >= _end)) {
96                                 prom_printf(wrong_f, p, p[1]);
97                                 prom_halt();
98                         }
99                         break;
100                 case 'b':
101                         if (p[1] < (unsigned long)__init_begin || p[1] >= (unsigned long)__init_text_end || (p[1] & 3)) {
102                                 prom_printf(wrong_b, p, p[1]);
103                                 prom_halt();
104                         }
105                         break;
106                 case 's':
107                         if (p[1] + 0x1000 >= 0x2000) {
108                                 prom_printf(wrong_s, p, p[1]);
109                                 prom_halt();
110                         }
111                         break;
112                 case 'h':
113                         if (p[1] & 0x3ff) {
114                                 prom_printf(wrong_h, p, p[1]);
115                                 prom_halt();
116                         }
117                         break;
118                 case 'a':
119                         if (p[1] + 0x1000 >= 0x2000 && (p[1] & 0x3ff)) {
120                                 prom_printf(wrong_a, p, p[1]);
121                                 prom_halt();
122                         }
123                         break;
124                 }
125                 if (p[0] & 1) {
126                         p[0] &= ~1;
127                         while (count) {
128                                 fmangled = 0;
129                                 addr = (unsigned *)*q;
130                                 if (addr < _stext || addr >= _end) {
131                                         prom_printf(wrong, type, p);
132                                         prom_halt();
133                                 }
134                                 insn = *addr;
135 #ifdef BTFIXUP_OPTIMIZE_OTHER                           
136                                 if (type != 'f' && q[1]) {
137                                         insn = *(unsigned int *)q[1];
138                                         if (!insn || insn == 1)
139                                                 insn = *addr;
140                                         else
141                                                 fmangled = 1;
142                                 }
143 #endif
144                                 switch (type) {
145                                 case 'f':       /* CALL */
146                                         if (addr >= __start___ksymtab && addr < __stop___ksymtab) {
147                                                 *addr = p[1];
148                                                 break;
149                                         } else if (!q[1]) {
150                                                 if ((insn & 0xc1c00000) == 0x01000000) { /* SETHI */
151                                                         *addr = (insn & 0xffc00000) | (p[1] >> 10); break;
152                                                 } else if ((insn & 0xc1f82000) == 0x80102000) { /* OR X, %LO(i), Y */
153                                                         *addr = (insn & 0xffffe000) | (p[1] & 0x3ff); break;
154                                                 } else if ((insn & 0xc0000000) != 0x40000000) { /* !CALL */
155                                 bad_f:
156                                                         prom_printf(insn_f, p, addr, insn, addr[1]);
157                                                         prom_halt();
158                                                 }
159                                         } else if (q[1] != 1)
160                                                 addr[1] = q[1];
161                                         if (p[2] == BTFIXUPCALL_NORM) {
162                                 norm_f: 
163                                                 *addr = 0x40000000 | ((p[1] - (unsigned)addr) >> 2);
164                                                 q[1] = 0;
165                                                 break;
166                                         }
167 #ifndef BTFIXUP_OPTIMIZE_NOP
168                                         goto norm_f;
169 #else
170                                         if (!(addr[1] & 0x80000000)) {
171                                                 if ((addr[1] & 0xc1c00000) != 0x01000000)       /* !SETHI */
172                                                         goto bad_f; /* CALL, Bicc, FBfcc, CBccc are weird in delay slot, aren't they? */
173                                         } else {
174                                                 if ((addr[1] & 0x01800000) == 0x01800000) {
175                                                         if ((addr[1] & 0x01f80000) == 0x01e80000) {
176                                                                 /* RESTORE */
177                                                                 goto norm_f; /* It is dangerous to patch that */
178                                                         }
179                                                         goto bad_f;
180                                                 }
181                                                 if ((addr[1] & 0xffffe003) == 0x9e03e000) {
182                                                         /* ADD %O7, XX, %o7 */
183                                                         int displac = (addr[1] << 19);
184                                                         
185                                                         displac = (displac >> 21) + 2;
186                                                         *addr = (0x10800000) + (displac & 0x3fffff);
187                                                         q[1] = addr[1];
188                                                         addr[1] = p[2];
189                                                         break;
190                                                 }
191                                                 if ((addr[1] & 0x201f) == 0x200f || (addr[1] & 0x7c000) == 0x3c000)
192                                                         goto norm_f; /* Someone is playing bad tricks with us: rs1 or rs2 is o7 */
193                                                 if ((addr[1] & 0x3e000000) == 0x1e000000)
194                                                         goto norm_f; /* rd is %o7. We'd better take care. */
195                                         }
196                                         if (p[2] == BTFIXUPCALL_NOP) {
197                                                 *addr = 0x01000000;
198                                                 q[1] = 1;
199                                                 break;
200                                         }
201 #ifndef BTFIXUP_OPTIMIZE_OTHER
202                                         goto norm_f;
203 #else
204                                         if (addr[1] == 0x01000000) {    /* NOP in the delay slot */
205                                                 q[1] = addr[1];
206                                                 *addr = p[2];
207                                                 break;
208                                         }
209                                         if ((addr[1] & 0xc0000000) != 0xc0000000) {
210                                                 /* Not a memory operation */
211                                                 if ((addr[1] & 0x30000000) == 0x10000000) {
212                                                         /* Ok, non-memory op with rd %oX */
213                                                         if ((addr[1] & 0x3e000000) == 0x1c000000)
214                                                                 goto bad_f; /* Aiee. Someone is playing strange %sp tricks */
215                                                         if ((addr[1] & 0x3e000000) > 0x12000000 ||
216                                                             ((addr[1] & 0x3e000000) == 0x12000000 &&
217                                                              p[2] != BTFIXUPCALL_STO1O0 && p[2] != BTFIXUPCALL_SWAPO0O1) ||
218                                                             ((p[2] & 0xffffe000) == BTFIXUPCALL_RETINT(0))) {
219                                                                 /* Nobody uses the result. We can nop it out. */
220                                                                 *addr = p[2];
221                                                                 q[1] = addr[1];
222                                                                 addr[1] = 0x01000000;
223                                                                 break;
224                                                         }
225                                                         if ((addr[1] & 0xf1ffffe0) == 0x90100000) {
226                                                                 /* MOV %reg, %Ox */
227                                                                 if ((addr[1] & 0x3e000000) == 0x10000000 &&
228                                                                     (p[2] & 0x7c000) == 0x20000) {
229                                                                         /* Ok, it is call xx; mov reg, %o0 and call optimizes
230                                                                            to doing something on %o0. Patch the patch. */
231                                                                         *addr = (p[2] & ~0x7c000) | ((addr[1] & 0x1f) << 14);
232                                                                         q[1] = addr[1];
233                                                                         addr[1] = 0x01000000;
234                                                                         break;
235                                                                 }
236                                                                 if ((addr[1] & 0x3e000000) == 0x12000000 &&
237                                                                     p[2] == BTFIXUPCALL_STO1O0) {
238                                                                         *addr = (p[2] & ~0x3e000000) | ((addr[1] & 0x1f) << 25);
239                                                                         q[1] = addr[1];
240                                                                         addr[1] = 0x01000000;
241                                                                         break;
242                                                                 }
243                                                         }
244                                                 }
245                                         }
246                                         *addr = addr[1];
247                                         q[1] = addr[1];
248                                         addr[1] = p[2];
249                                         break;
250 #endif /* BTFIXUP_OPTIMIZE_OTHER */
251 #endif /* BTFIXUP_OPTIMIZE_NOP */
252                                 case 'b':       /* BLACKBOX */
253                                         /* Has to be sethi i, xx */
254                                         if ((insn & 0xc1c00000) != 0x01000000) {
255                                                 prom_printf(insn_b, p, addr, insn);
256                                                 prom_halt();
257                                         } else {
258                                                 void (*do_fixup)(unsigned *);
259                                                 
260                                                 do_fixup = (void (*)(unsigned *))p[1];
261                                                 do_fixup(addr);
262                                         }
263                                         break;
264                                 case 's':       /* SIMM13 */
265                                         /* Has to be or %g0, i, xx */
266                                         if ((insn & 0xc1ffe000) != 0x80102000) {
267                                                 prom_printf(insn_s, p, addr, insn);
268                                                 prom_halt();
269                                         }
270                                         set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x1fff));
271                                         break;
272                                 case 'h':       /* SETHI */
273                                         /* Has to be sethi i, xx */
274                                         if ((insn & 0xc1c00000) != 0x01000000) {
275                                                 prom_printf(insn_h, p, addr, insn);
276                                                 prom_halt();
277                                         }
278                                         set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
279                                         break;
280                                 case 'a':       /* HALF */
281                                         /* Has to be sethi i, xx or or %g0, i, xx */
282                                         if ((insn & 0xc1c00000) != 0x01000000 &&
283                                             (insn & 0xc1ffe000) != 0x80102000) {
284                                                 prom_printf(insn_a, p, addr, insn);
285                                                 prom_halt();
286                                         }
287                                         if (p[1] & 0x3ff)
288                                                 set_addr(addr, q[1], fmangled, 
289                                                         (insn & 0x3e000000) | 0x80102000 | (p[1] & 0x1fff));
290                                         else
291                                                 set_addr(addr, q[1], fmangled, 
292                                                         (insn & 0x3e000000) | 0x01000000 | (p[1] >> 10));
293                                         break;
294                                 case 'i':       /* INT */
295                                         if ((insn & 0xc1c00000) == 0x01000000) /* %HI */
296                                                 set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
297                                         else if ((insn & 0x80002000) == 0x80002000) /* %LO */
298                                                 set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x3ff));
299                                         else {
300                                                 prom_printf(insn_i, p, addr, insn);
301                                                 prom_halt();
302                                         }
303                                         break;
304                                 }
305                                 count -= 2;
306                                 q += 2;
307                         }
308                 } else
309                         p = q + count;
310         }
311 #ifdef CONFIG_SMP
312         local_ops->cache_all();
313 #else
314         sparc32_cachetlb_ops->cache_all();
315 #endif
316 }