extable.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #include <linux/module.h>
  2. #include <linux/spinlock.h>
  3. #include <linux/sort.h>
  4. #include <asm/uaccess.h>
  5. static inline unsigned long
  6. ex_insn_addr(const struct exception_table_entry *x)
  7. {
  8. return (unsigned long)&x->insn + x->insn;
  9. }
  10. static inline unsigned long
  11. ex_fixup_addr(const struct exception_table_entry *x)
  12. {
  13. return (unsigned long)&x->fixup + x->fixup;
  14. }
  15. int fixup_exception(struct pt_regs *regs)
  16. {
  17. const struct exception_table_entry *fixup;
  18. unsigned long new_ip;
  19. #ifdef CONFIG_PNPBIOS
  20. if (unlikely(SEGMENT_IS_PNP_CODE(regs->cs))) {
  21. extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp;
  22. extern u32 pnp_bios_is_utter_crap;
  23. pnp_bios_is_utter_crap = 1;
  24. printk(KERN_CRIT "PNPBIOS fault.. attempting recovery.\n");
  25. __asm__ volatile(
  26. "movl %0, %%esp\n\t"
  27. "jmp *%1\n\t"
  28. : : "g" (pnp_bios_fault_esp), "g" (pnp_bios_fault_eip));
  29. panic("do_trap: can't hit this");
  30. }
  31. #endif
  32. fixup = search_exception_tables(regs->ip);
  33. if (fixup) {
  34. new_ip = ex_fixup_addr(fixup);
  35. if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
  36. /* Special hack for uaccess_err */
  37. current_thread_info()->uaccess_err = 1;
  38. new_ip -= 0x7ffffff0;
  39. }
  40. regs->ip = new_ip;
  41. return 1;
  42. }
  43. return 0;
  44. }
  45. /* Restricted version used during very early boot */
  46. int __init early_fixup_exception(unsigned long *ip)
  47. {
  48. const struct exception_table_entry *fixup;
  49. unsigned long new_ip;
  50. fixup = search_exception_tables(*ip);
  51. if (fixup) {
  52. new_ip = ex_fixup_addr(fixup);
  53. if (fixup->fixup - fixup->insn >= 0x7ffffff0 - 4) {
  54. /* uaccess handling not supported during early boot */
  55. return 0;
  56. }
  57. *ip = new_ip;
  58. return 1;
  59. }
  60. return 0;
  61. }
  62. /*
  63. * Search one exception table for an entry corresponding to the
  64. * given instruction address, and return the address of the entry,
  65. * or NULL if none is found.
  66. * We use a binary search, and thus we assume that the table is
  67. * already sorted.
  68. */
  69. const struct exception_table_entry *
  70. search_extable(const struct exception_table_entry *first,
  71. const struct exception_table_entry *last,
  72. unsigned long value)
  73. {
  74. while (first <= last) {
  75. const struct exception_table_entry *mid;
  76. unsigned long addr;
  77. mid = ((last - first) >> 1) + first;
  78. addr = ex_insn_addr(mid);
  79. if (addr < value)
  80. first = mid + 1;
  81. else if (addr > value)
  82. last = mid - 1;
  83. else
  84. return mid;
  85. }
  86. return NULL;
  87. }
  88. /*
  89. * The exception table needs to be sorted so that the binary
  90. * search that we use to find entries in it works properly.
  91. * This is used both for the kernel exception table and for
  92. * the exception tables of modules that get loaded.
  93. *
  94. */
  95. static int cmp_ex(const void *a, const void *b)
  96. {
  97. const struct exception_table_entry *x = a, *y = b;
  98. /*
  99. * This value will always end up fittin in an int, because on
  100. * both i386 and x86-64 the kernel symbol-reachable address
  101. * space is < 2 GiB.
  102. *
  103. * This compare is only valid after normalization.
  104. */
  105. return x->insn - y->insn;
  106. }
  107. void sort_extable(struct exception_table_entry *start,
  108. struct exception_table_entry *finish)
  109. {
  110. struct exception_table_entry *p;
  111. int i;
  112. /* Convert all entries to being relative to the start of the section */
  113. i = 0;
  114. for (p = start; p < finish; p++) {
  115. p->insn += i;
  116. i += 4;
  117. p->fixup += i;
  118. i += 4;
  119. }
  120. sort(start, finish - start, sizeof(struct exception_table_entry),
  121. cmp_ex, NULL);
  122. /* Denormalize all entries */
  123. i = 0;
  124. for (p = start; p < finish; p++) {
  125. p->insn -= i;
  126. i += 4;
  127. p->fixup -= i;
  128. i += 4;
  129. }
  130. }
  131. #ifdef CONFIG_MODULES
  132. /*
  133. * If the exception table is sorted, any referring to the module init
  134. * will be at the beginning or the end.
  135. */
  136. void trim_init_extable(struct module *m)
  137. {
  138. /*trim the beginning*/
  139. while (m->num_exentries &&
  140. within_module_init(ex_insn_addr(&m->extable[0]), m)) {
  141. m->extable++;
  142. m->num_exentries--;
  143. }
  144. /*trim the end*/
  145. while (m->num_exentries &&
  146. within_module_init(ex_insn_addr(&m->extable[m->num_exentries-1]), m))
  147. m->num_exentries--;
  148. }
  149. #endif /* CONFIG_MODULES */