btfixup.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:10k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

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