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