diff options
Diffstat (limited to 'arch/x86/mm/fault_32.c')
-rw-r--r-- | arch/x86/mm/fault_32.c | 59 |
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" | |||
217 | KERN_ERR "******* Working around it, but it may cause SEGVs or burn power.\n" | 217 | KERN_ERR "******* Working around it, but it may cause SEGVs or burn power.\n" |
218 | KERN_ERR "******* Please consider a BIOS update.\n" | 218 | KERN_ERR "******* Please consider a BIOS update.\n" |
219 | KERN_ERR "******* Disabling USB legacy in the BIOS may also help.\n"; | 219 | KERN_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 | */ | ||
229 | static int is_errata93(struct pt_regs *regs, unsigned long address) | 231 | static 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 | */ |
255 | static inline int vmalloc_fault(unsigned long address) | 259 | static 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 | ||
277 | int show_unhandled_signals = 1; | 327 | int 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. |