diff options
author | neel <neel@FreeBSD.org> | 2013-07-23 22:17:00 +0000 |
---|---|---|
committer | neel <neel@FreeBSD.org> | 2013-07-23 22:17:00 +0000 |
commit | 50750f842b8d55e14418a44c689459edb947dc39 (patch) | |
tree | edd03ebee378453d12a6d33cb17cdd30e03c229d /sys/amd64 | |
parent | bc9fec6137dec23cc035c0794e79ca2d9a6d7ef5 (diff) | |
download | FreeBSD-src-50750f842b8d55e14418a44c689459edb947dc39.zip FreeBSD-src-50750f842b8d55e14418a44c689459edb947dc39.tar.gz |
Fix a bug introduced in r252646 that causes a page with the PG_PTE_PAT bit set
to be interpreted as a superpage. This is because PG_PTE_PAT is at the same
bit position in PTE as PG_PS is in a PDE.
This caused a number of regressions on amd64 systems: panic when starting
X applications, freeze during shutdown etc.
Pointy hat to: me
Tested by: gperez@entel.upc.edu, joel, dumbbell
Reviewed by: kib
Diffstat (limited to 'sys/amd64')
-rw-r--r-- | sys/amd64/amd64/pmap.c | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c index 19be4e0..d7e125b 100644 --- a/sys/amd64/amd64/pmap.c +++ b/sys/amd64/amd64/pmap.c @@ -4401,6 +4401,7 @@ pmap_remove_pages(pmap_t pmap) int64_t bit; uint64_t inuse, bitmask; int allfree, field, freed, idx; + boolean_t superpage; vm_paddr_t pa; if (pmap != PCPU_GET(curpmap)) { @@ -4427,12 +4428,26 @@ pmap_remove_pages(pmap_t pmap) pte = pmap_pdpe_to_pde(pte, pv->pv_va); tpte = *pte; if ((tpte & (PG_PS | PG_V)) == PG_V) { + superpage = FALSE; ptepde = tpte; pte = (pt_entry_t *)PHYS_TO_DMAP(tpte & PG_FRAME); pte = &pte[pmap_pte_index(pv->pv_va)]; tpte = *pte; + } else { + /* + * Keep track whether 'tpte' is a + * superpage explicitly instead of + * relying on PG_PS being set. + * + * This is because PG_PS is numerically + * identical to PG_PTE_PAT and thus a + * regular page could be mistaken for + * a superpage. + */ + superpage = TRUE; } + if ((tpte & PG_V) == 0) { panic("bad pte va %lx pte %lx", pv->pv_va, tpte); @@ -4446,7 +4461,7 @@ pmap_remove_pages(pmap_t pmap) continue; } - if (tpte & PG_PS) + if (superpage) pa = tpte & PG_PS_FRAME; else pa = tpte & PG_FRAME; @@ -4468,7 +4483,7 @@ pmap_remove_pages(pmap_t pmap) * Update the vm_page_t clean/reference bits. */ if ((tpte & (PG_M | PG_RW)) == (PG_M | PG_RW)) { - if ((tpte & PG_PS) != 0) { + if (superpage) { for (mt = m; mt < &m[NBPDR / PAGE_SIZE]; mt++) vm_page_dirty(mt); } else @@ -4479,7 +4494,7 @@ pmap_remove_pages(pmap_t pmap) /* Mark free */ pc->pc_map[field] |= bitmask; - if ((tpte & PG_PS) != 0) { + if (superpage) { pmap_resident_count_dec(pmap, NBPDR / PAGE_SIZE); pvh = pa_to_pvh(tpte & PG_PS_FRAME); TAILQ_REMOVE(&pvh->pv_list, pv, pv_next); |