summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2013-07-23 22:17:00 +0000
committerneel <neel@FreeBSD.org>2013-07-23 22:17:00 +0000
commit50750f842b8d55e14418a44c689459edb947dc39 (patch)
treeedd03ebee378453d12a6d33cb17cdd30e03c229d /sys/amd64
parentbc9fec6137dec23cc035c0794e79ca2d9a6d7ef5 (diff)
downloadFreeBSD-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.c21
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);
OpenPOWER on IntegriCloud