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