aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/mm/fault_32.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/mm/fault_32.c')
-rw-r--r--arch/x86/mm/fault_32.c59
1 files changed, 56 insertions, 3 deletions
diff --git a/arch/x86/mm/fault_32.c b/arch/x86/mm/fault_32.c
index 31113deeb7c0..eba90198b15a 100644
--- a/arch/x86/mm/fault_32.c
+++ b/arch/x86/mm/fault_32.c
@@ -217,6 +217,7 @@ KERN_ERR "******* Your BIOS seems to not contain a fix for K8 errata #93\n"
217KERN_ERR "******* Working around it, but it may cause SEGVs or burn power.\n" 217KERN_ERR "******* Working around it, but it may cause SEGVs or burn power.\n"
218KERN_ERR "******* Please consider a BIOS update.\n" 218KERN_ERR "******* Please consider a BIOS update.\n"
219KERN_ERR "******* Disabling USB legacy in the BIOS may also help.\n"; 219KERN_ERR "******* Disabling USB legacy in the BIOS may also help.\n";
220#endif
220 221
221/* Workaround for K8 erratum #93 & buggy BIOS. 222/* Workaround for K8 erratum #93 & buggy BIOS.
222 BIOS SMM functions are required to use a specific workaround 223 BIOS SMM functions are required to use a specific workaround
@@ -224,10 +225,12 @@ KERN_ERR "******* Disabling USB legacy in the BIOS may also help.\n";
224 A lot of BIOS that didn't get tested properly miss this. 225 A lot of BIOS that didn't get tested properly miss this.
225 The OS sees this as a page fault with the upper 32bits of RIP cleared. 226 The OS sees this as a page fault with the upper 32bits of RIP cleared.
226 Try to work around it here. 227 Try to work around it here.
227 Note we only handle faults in kernel here. */ 228 Note we only handle faults in kernel here.
228 229 Does nothing for X86_32
230 */
229static int is_errata93(struct pt_regs *regs, unsigned long address) 231static int is_errata93(struct pt_regs *regs, unsigned long address)
230{ 232{
233#ifdef CONFIG_X86_64
231 static int warned; 234 static int warned;
232 if (address != regs->ip) 235 if (address != regs->ip)
233 return 0; 236 return 0;
@@ -243,9 +246,10 @@ static int is_errata93(struct pt_regs *regs, unsigned long address)
243 regs->ip = address; 246 regs->ip = address;
244 return 1; 247 return 1;
245 } 248 }
249#endif
246 return 0; 250 return 0;
247} 251}
248#endif 252
249 253
250/* 254/*
251 * Handle a fault on the vmalloc or module mapping area 255 * Handle a fault on the vmalloc or module mapping area
@@ -254,6 +258,7 @@ static int is_errata93(struct pt_regs *regs, unsigned long address)
254 */ 258 */
255static inline int vmalloc_fault(unsigned long address) 259static inline int vmalloc_fault(unsigned long address)
256{ 260{
261#ifdef CONFIG_X86_32
257 unsigned long pgd_paddr; 262 unsigned long pgd_paddr;
258 pmd_t *pmd_k; 263 pmd_t *pmd_k;
259 pte_t *pte_k; 264 pte_t *pte_k;
@@ -272,6 +277,51 @@ static inline int vmalloc_fault(unsigned long address)
272 if (!pte_present(*pte_k)) 277 if (!pte_present(*pte_k))
273 return -1; 278 return -1;
274 return 0; 279 return 0;
280#else
281 pgd_t *pgd, *pgd_ref;
282 pud_t *pud, *pud_ref;
283 pmd_t *pmd, *pmd_ref;
284 pte_t *pte, *pte_ref;
285
286 /* Copy kernel mappings over when needed. This can also
287 happen within a race in page table update. In the later
288 case just flush. */
289
290 pgd = pgd_offset(current->mm ?: &init_mm, address);
291 pgd_ref = pgd_offset_k(address);
292 if (pgd_none(*pgd_ref))
293 return -1;
294 if (pgd_none(*pgd))
295 set_pgd(pgd, *pgd_ref);
296 else
297 BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref));
298
299 /* Below here mismatches are bugs because these lower tables
300 are shared */
301
302 pud = pud_offset(pgd, address);
303 pud_ref = pud_offset(pgd_ref, address);
304 if (pud_none(*pud_ref))
305 return -1;
306 if (pud_none(*pud) || pud_page_vaddr(*pud) != pud_page_vaddr(*pud_ref))
307 BUG();
308 pmd = pmd_offset(pud, address);
309 pmd_ref = pmd_offset(pud_ref, address);
310 if (pmd_none(*pmd_ref))
311 return -1;
312 if (pmd_none(*pmd) || pmd_page(*pmd) != pmd_page(*pmd_ref))
313 BUG();
314 pte_ref = pte_offset_kernel(pmd_ref, address);
315 if (!pte_present(*pte_ref))
316 return -1;
317 pte = pte_offset_kernel(pmd, address);
318 /* Don't use pte_page here, because the mappings can point
319 outside mem_map, and the NUMA hash lookup cannot handle
320 that. */
321 if (!pte_present(*pte) || pte_pfn(*pte) != pte_pfn(*pte_ref))
322 BUG();
323 return 0;
324#endif
275} 325}
276 326
277int show_unhandled_signals = 1; 327int show_unhandled_signals = 1;
@@ -507,6 +557,9 @@ no_context:
507 if (is_prefetch(regs, address, error_code)) 557 if (is_prefetch(regs, address, error_code))
508 return; 558 return;
509 559
560 if (is_errata93(regs, address))
561 return;
562
510/* 563/*
511 * Oops. The kernel tried to access some bad page. We'll have to 564 * Oops. The kernel tried to access some bad page. We'll have to
512 * terminate things with extreme prejudice. 565 * terminate things with extreme prejudice.