Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 80290
b: refs/heads/master
c: fdfe8aa
h: refs/heads/master
v: v3
  • Loading branch information
Harvey Harrison authored and Ingo Molnar committed Jan 30, 2008
1 parent b957586 commit b6a8696
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 7 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 6f4d368ef9e9f91aa0019c11e90773ea32d94625
refs/heads/master: fdfe8aa84dd78cfdff427d0520f5974fb5e9c220
59 changes: 56 additions & 3 deletions trunk/arch/x86/mm/fault_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,17 +217,20 @@ KERN_ERR "******* Your BIOS seems to not contain a fix for K8 errata #93\n"
KERN_ERR "******* Working around it, but it may cause SEGVs or burn power.\n"
KERN_ERR "******* Please consider a BIOS update.\n"
KERN_ERR "******* Disabling USB legacy in the BIOS may also help.\n";
#endif

/* Workaround for K8 erratum #93 & buggy BIOS.
BIOS SMM functions are required to use a specific workaround
to avoid corruption of the 64bit RIP register on C stepping K8.
A lot of BIOS that didn't get tested properly miss this.
The OS sees this as a page fault with the upper 32bits of RIP cleared.
Try to work around it here.
Note we only handle faults in kernel here. */

Note we only handle faults in kernel here.
Does nothing for X86_32
*/
static int is_errata93(struct pt_regs *regs, unsigned long address)
{
#ifdef CONFIG_X86_64
static int warned;
if (address != regs->ip)
return 0;
Expand All @@ -243,9 +246,10 @@ static int is_errata93(struct pt_regs *regs, unsigned long address)
regs->ip = address;
return 1;
}
#endif
return 0;
}
#endif


/*
* Handle a fault on the vmalloc or module mapping area
Expand All @@ -254,6 +258,7 @@ static int is_errata93(struct pt_regs *regs, unsigned long address)
*/
static inline int vmalloc_fault(unsigned long address)
{
#ifdef CONFIG_X86_32
unsigned long pgd_paddr;
pmd_t *pmd_k;
pte_t *pte_k;
Expand All @@ -272,6 +277,51 @@ static inline int vmalloc_fault(unsigned long address)
if (!pte_present(*pte_k))
return -1;
return 0;
#else
pgd_t *pgd, *pgd_ref;
pud_t *pud, *pud_ref;
pmd_t *pmd, *pmd_ref;
pte_t *pte, *pte_ref;

/* Copy kernel mappings over when needed. This can also
happen within a race in page table update. In the later
case just flush. */

pgd = pgd_offset(current->mm ?: &init_mm, address);
pgd_ref = pgd_offset_k(address);
if (pgd_none(*pgd_ref))
return -1;
if (pgd_none(*pgd))
set_pgd(pgd, *pgd_ref);
else
BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref));

/* Below here mismatches are bugs because these lower tables
are shared */

pud = pud_offset(pgd, address);
pud_ref = pud_offset(pgd_ref, address);
if (pud_none(*pud_ref))
return -1;
if (pud_none(*pud) || pud_page_vaddr(*pud) != pud_page_vaddr(*pud_ref))
BUG();
pmd = pmd_offset(pud, address);
pmd_ref = pmd_offset(pud_ref, address);
if (pmd_none(*pmd_ref))
return -1;
if (pmd_none(*pmd) || pmd_page(*pmd) != pmd_page(*pmd_ref))
BUG();
pte_ref = pte_offset_kernel(pmd_ref, address);
if (!pte_present(*pte_ref))
return -1;
pte = pte_offset_kernel(pmd, address);
/* Don't use pte_page here, because the mappings can point
outside mem_map, and the NUMA hash lookup cannot handle
that. */
if (!pte_present(*pte) || pte_pfn(*pte) != pte_pfn(*pte_ref))
BUG();
return 0;
#endif
}

int show_unhandled_signals = 1;
Expand Down Expand Up @@ -507,6 +557,9 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
if (is_prefetch(regs, address, error_code))
return;

if (is_errata93(regs, address))
return;

/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
Expand Down
30 changes: 27 additions & 3 deletions trunk/arch/x86/mm/fault_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,17 +223,20 @@ KERN_ERR "******* Your BIOS seems to not contain a fix for K8 errata #93\n"
KERN_ERR "******* Working around it, but it may cause SEGVs or burn power.\n"
KERN_ERR "******* Please consider a BIOS update.\n"
KERN_ERR "******* Disabling USB legacy in the BIOS may also help.\n";
#endif

/* Workaround for K8 erratum #93 & buggy BIOS.
BIOS SMM functions are required to use a specific workaround
to avoid corruption of the 64bit RIP register on C stepping K8.
A lot of BIOS that didn't get tested properly miss this.
The OS sees this as a page fault with the upper 32bits of RIP cleared.
Try to work around it here.
Note we only handle faults in kernel here. */

Note we only handle faults in kernel here.
Does nothing for X86_32
*/
static int is_errata93(struct pt_regs *regs, unsigned long address)
{
#ifdef CONFIG_X86_64
static int warned;
if (address != regs->ip)
return 0;
Expand All @@ -249,9 +252,9 @@ static int is_errata93(struct pt_regs *regs, unsigned long address)
regs->ip = address;
return 1;
}
#endif
return 0;
}
#endif

static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs,
unsigned long error_code)
Expand All @@ -278,6 +281,26 @@ static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs,
*/
static int vmalloc_fault(unsigned long address)
{
#ifdef CONFIG_X86_32
unsigned long pgd_paddr;
pmd_t *pmd_k;
pte_t *pte_k;
/*
* Synchronize this task's top level page-table
* with the 'reference' page table.
*
* Do _not_ use "current" here. We might be inside
* an interrupt in the middle of a task switch..
*/
pgd_paddr = read_cr3();
pmd_k = vmalloc_sync_one(__va(pgd_paddr), address);
if (!pmd_k)
return -1;
pte_k = pte_offset_kernel(pmd_k, address);
if (!pte_present(*pte_k))
return -1;
return 0;
#else
pgd_t *pgd, *pgd_ref;
pud_t *pud, *pud_ref;
pmd_t *pmd, *pmd_ref;
Expand Down Expand Up @@ -321,6 +344,7 @@ static int vmalloc_fault(unsigned long address)
if (!pte_present(*pte) || pte_pfn(*pte) != pte_pfn(*pte_ref))
BUG();
return 0;
#endif
}

int show_unhandled_signals = 1;
Expand Down

0 comments on commit b6a8696

Please sign in to comment.