|
@@ -29,6 +29,7 @@
|
|
|
#ifndef __ASSEMBLY__
|
|
|
#include <linux/sched.h>
|
|
|
#include <linux/mm_types.h>
|
|
|
+#include <linux/page-flags.h>
|
|
|
#include <asm/bug.h>
|
|
|
#include <asm/page.h>
|
|
|
|
|
@@ -221,13 +222,15 @@ extern unsigned long MODULES_END;
|
|
|
/* Software bits in the page table entry */
|
|
|
#define _PAGE_SWT 0x001 /* SW pte type bit t */
|
|
|
#define _PAGE_SWX 0x002 /* SW pte type bit x */
|
|
|
-#define _PAGE_SWC 0x004 /* SW pte changed bit (for KVM) */
|
|
|
-#define _PAGE_SWR 0x008 /* SW pte referenced bit (for KVM) */
|
|
|
-#define _PAGE_SPECIAL 0x010 /* SW associated with special page */
|
|
|
+#define _PAGE_SWC 0x004 /* SW pte changed bit */
|
|
|
+#define _PAGE_SWR 0x008 /* SW pte referenced bit */
|
|
|
+#define _PAGE_SWW 0x010 /* SW pte write bit */
|
|
|
+#define _PAGE_SPECIAL 0x020 /* SW associated with special page */
|
|
|
#define __HAVE_ARCH_PTE_SPECIAL
|
|
|
|
|
|
/* Set of bits not changed in pte_modify */
|
|
|
-#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_SWC | _PAGE_SWR)
|
|
|
+#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_CO | \
|
|
|
+ _PAGE_SWC | _PAGE_SWR)
|
|
|
|
|
|
/* Six different types of pages. */
|
|
|
#define _PAGE_TYPE_EMPTY 0x400
|
|
@@ -321,6 +324,7 @@ extern unsigned long MODULES_END;
|
|
|
|
|
|
/* Bits in the region table entry */
|
|
|
#define _REGION_ENTRY_ORIGIN ~0xfffUL/* region/segment table origin */
|
|
|
+#define _REGION_ENTRY_RO 0x200 /* region protection bit */
|
|
|
#define _REGION_ENTRY_INV 0x20 /* invalid region table entry */
|
|
|
#define _REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */
|
|
|
#define _REGION_ENTRY_TYPE_R1 0x0c /* region first table type */
|
|
@@ -382,9 +386,10 @@ extern unsigned long MODULES_END;
|
|
|
*/
|
|
|
#define PAGE_NONE __pgprot(_PAGE_TYPE_NONE)
|
|
|
#define PAGE_RO __pgprot(_PAGE_TYPE_RO)
|
|
|
-#define PAGE_RW __pgprot(_PAGE_TYPE_RW)
|
|
|
+#define PAGE_RW __pgprot(_PAGE_TYPE_RO | _PAGE_SWW)
|
|
|
+#define PAGE_RWC __pgprot(_PAGE_TYPE_RW | _PAGE_SWW | _PAGE_SWC)
|
|
|
|
|
|
-#define PAGE_KERNEL PAGE_RW
|
|
|
+#define PAGE_KERNEL PAGE_RWC
|
|
|
#define PAGE_SHARED PAGE_KERNEL
|
|
|
#define PAGE_COPY PAGE_RO
|
|
|
|
|
@@ -632,23 +637,23 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste)
|
|
|
bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
|
|
|
/* Clear page changed & referenced bit in the storage key */
|
|
|
if (bits & _PAGE_CHANGED)
|
|
|
- page_set_storage_key(address, skey ^ bits, 1);
|
|
|
+ page_set_storage_key(address, skey ^ bits, 0);
|
|
|
else if (bits)
|
|
|
page_reset_referenced(address);
|
|
|
/* Transfer page changed & referenced bit to guest bits in pgste */
|
|
|
pgste_val(pgste) |= bits << 48; /* RCP_GR_BIT & RCP_GC_BIT */
|
|
|
/* Get host changed & referenced bits from pgste */
|
|
|
bits |= (pgste_val(pgste) & (RCP_HR_BIT | RCP_HC_BIT)) >> 52;
|
|
|
- /* Clear host bits in pgste. */
|
|
|
+ /* Transfer page changed & referenced bit to kvm user bits */
|
|
|
+ pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */
|
|
|
+ /* Clear relevant host bits in pgste. */
|
|
|
pgste_val(pgste) &= ~(RCP_HR_BIT | RCP_HC_BIT);
|
|
|
pgste_val(pgste) &= ~(RCP_ACC_BITS | RCP_FP_BIT);
|
|
|
/* Copy page access key and fetch protection bit to pgste */
|
|
|
pgste_val(pgste) |=
|
|
|
(unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
|
|
|
- /* Transfer changed and referenced to kvm user bits */
|
|
|
- pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */
|
|
|
- /* Transfer changed & referenced to pte sofware bits */
|
|
|
- pte_val(*ptep) |= bits << 1; /* _PAGE_SWR & _PAGE_SWC */
|
|
|
+ /* Transfer referenced bit to pte */
|
|
|
+ pte_val(*ptep) |= (bits & _PAGE_REFERENCED) << 1;
|
|
|
#endif
|
|
|
return pgste;
|
|
|
|
|
@@ -661,20 +666,25 @@ static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste)
|
|
|
|
|
|
if (!pte_present(*ptep))
|
|
|
return pgste;
|
|
|
+ /* Get referenced bit from storage key */
|
|
|
young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK);
|
|
|
- /* Transfer page referenced bit to pte software bit (host view) */
|
|
|
- if (young || (pgste_val(pgste) & RCP_HR_BIT))
|
|
|
+ if (young)
|
|
|
+ pgste_val(pgste) |= RCP_GR_BIT;
|
|
|
+ /* Get host referenced bit from pgste */
|
|
|
+ if (pgste_val(pgste) & RCP_HR_BIT) {
|
|
|
+ pgste_val(pgste) &= ~RCP_HR_BIT;
|
|
|
+ young = 1;
|
|
|
+ }
|
|
|
+ /* Transfer referenced bit to kvm user bits and pte */
|
|
|
+ if (young) {
|
|
|
+ pgste_val(pgste) |= KVM_UR_BIT;
|
|
|
pte_val(*ptep) |= _PAGE_SWR;
|
|
|
- /* Clear host referenced bit in pgste. */
|
|
|
- pgste_val(pgste) &= ~RCP_HR_BIT;
|
|
|
- /* Transfer page referenced bit to guest bit in pgste */
|
|
|
- pgste_val(pgste) |= (unsigned long) young << 50; /* set RCP_GR_BIT */
|
|
|
+ }
|
|
|
#endif
|
|
|
return pgste;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
-static inline void pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry)
|
|
|
+static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry)
|
|
|
{
|
|
|
#ifdef CONFIG_PGSTE
|
|
|
unsigned long address;
|
|
@@ -688,10 +698,23 @@ static inline void pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry)
|
|
|
/* Set page access key and fetch protection bit from pgste */
|
|
|
nkey |= (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56;
|
|
|
if (okey != nkey)
|
|
|
- page_set_storage_key(address, nkey, 1);
|
|
|
+ page_set_storage_key(address, nkey, 0);
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+static inline void pgste_set_pte(pte_t *ptep, pte_t entry)
|
|
|
+{
|
|
|
+ if (!MACHINE_HAS_ESOP && (pte_val(entry) & _PAGE_SWW)) {
|
|
|
+ /*
|
|
|
+ * Without enhanced suppression-on-protection force
|
|
|
+ * the dirty bit on for all writable ptes.
|
|
|
+ */
|
|
|
+ pte_val(entry) |= _PAGE_SWC;
|
|
|
+ pte_val(entry) &= ~_PAGE_RO;
|
|
|
+ }
|
|
|
+ *ptep = entry;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* struct gmap_struct - guest address space
|
|
|
* @mm: pointer to the parent mm_struct
|
|
@@ -750,11 +773,14 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
|
|
|
|
|
|
if (mm_has_pgste(mm)) {
|
|
|
pgste = pgste_get_lock(ptep);
|
|
|
- pgste_set_pte(ptep, pgste, entry);
|
|
|
- *ptep = entry;
|
|
|
+ pgste_set_key(ptep, pgste, entry);
|
|
|
+ pgste_set_pte(ptep, entry);
|
|
|
pgste_set_unlock(ptep, pgste);
|
|
|
- } else
|
|
|
+ } else {
|
|
|
+ if (!(pte_val(entry) & _PAGE_INVALID) && MACHINE_HAS_EDAT1)
|
|
|
+ pte_val(entry) |= _PAGE_CO;
|
|
|
*ptep = entry;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -763,16 +789,12 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
|
|
|
*/
|
|
|
static inline int pte_write(pte_t pte)
|
|
|
{
|
|
|
- return (pte_val(pte) & _PAGE_RO) == 0;
|
|
|
+ return (pte_val(pte) & _PAGE_SWW) != 0;
|
|
|
}
|
|
|
|
|
|
static inline int pte_dirty(pte_t pte)
|
|
|
{
|
|
|
-#ifdef CONFIG_PGSTE
|
|
|
- if (pte_val(pte) & _PAGE_SWC)
|
|
|
- return 1;
|
|
|
-#endif
|
|
|
- return 0;
|
|
|
+ return (pte_val(pte) & _PAGE_SWC) != 0;
|
|
|
}
|
|
|
|
|
|
static inline int pte_young(pte_t pte)
|
|
@@ -822,11 +844,14 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
|
|
|
{
|
|
|
pte_val(pte) &= _PAGE_CHG_MASK;
|
|
|
pte_val(pte) |= pgprot_val(newprot);
|
|
|
+ if ((pte_val(pte) & _PAGE_SWC) && (pte_val(pte) & _PAGE_SWW))
|
|
|
+ pte_val(pte) &= ~_PAGE_RO;
|
|
|
return pte;
|
|
|
}
|
|
|
|
|
|
static inline pte_t pte_wrprotect(pte_t pte)
|
|
|
{
|
|
|
+ pte_val(pte) &= ~_PAGE_SWW;
|
|
|
/* Do not clobber _PAGE_TYPE_NONE pages! */
|
|
|
if (!(pte_val(pte) & _PAGE_INVALID))
|
|
|
pte_val(pte) |= _PAGE_RO;
|
|
@@ -835,20 +860,26 @@ static inline pte_t pte_wrprotect(pte_t pte)
|
|
|
|
|
|
static inline pte_t pte_mkwrite(pte_t pte)
|
|
|
{
|
|
|
- pte_val(pte) &= ~_PAGE_RO;
|
|
|
+ pte_val(pte) |= _PAGE_SWW;
|
|
|
+ if (pte_val(pte) & _PAGE_SWC)
|
|
|
+ pte_val(pte) &= ~_PAGE_RO;
|
|
|
return pte;
|
|
|
}
|
|
|
|
|
|
static inline pte_t pte_mkclean(pte_t pte)
|
|
|
{
|
|
|
-#ifdef CONFIG_PGSTE
|
|
|
pte_val(pte) &= ~_PAGE_SWC;
|
|
|
-#endif
|
|
|
+ /* Do not clobber _PAGE_TYPE_NONE pages! */
|
|
|
+ if (!(pte_val(pte) & _PAGE_INVALID))
|
|
|
+ pte_val(pte) |= _PAGE_RO;
|
|
|
return pte;
|
|
|
}
|
|
|
|
|
|
static inline pte_t pte_mkdirty(pte_t pte)
|
|
|
{
|
|
|
+ pte_val(pte) |= _PAGE_SWC;
|
|
|
+ if (pte_val(pte) & _PAGE_SWW)
|
|
|
+ pte_val(pte) &= ~_PAGE_RO;
|
|
|
return pte;
|
|
|
}
|
|
|
|
|
@@ -886,10 +917,10 @@ static inline pte_t pte_mkhuge(pte_t pte)
|
|
|
pte_val(pte) |= _SEGMENT_ENTRY_INV;
|
|
|
}
|
|
|
/*
|
|
|
- * Clear SW pte bits SWT and SWX, there are no SW bits in a segment
|
|
|
- * table entry.
|
|
|
+ * Clear SW pte bits, there are no SW bits in a segment table entry.
|
|
|
*/
|
|
|
- pte_val(pte) &= ~(_PAGE_SWT | _PAGE_SWX);
|
|
|
+ pte_val(pte) &= ~(_PAGE_SWT | _PAGE_SWX | _PAGE_SWC |
|
|
|
+ _PAGE_SWR | _PAGE_SWW);
|
|
|
/*
|
|
|
* Also set the change-override bit because we don't need dirty bit
|
|
|
* tracking for hugetlbfs pages.
|
|
@@ -1041,9 +1072,11 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm,
|
|
|
unsigned long address,
|
|
|
pte_t *ptep, pte_t pte)
|
|
|
{
|
|
|
- *ptep = pte;
|
|
|
- if (mm_has_pgste(mm))
|
|
|
+ if (mm_has_pgste(mm)) {
|
|
|
+ pgste_set_pte(ptep, pte);
|
|
|
pgste_set_unlock(ptep, *(pgste_t *)(ptep + PTRS_PER_PTE));
|
|
|
+ } else
|
|
|
+ *ptep = pte;
|
|
|
}
|
|
|
|
|
|
#define __HAVE_ARCH_PTEP_CLEAR_FLUSH
|
|
@@ -1111,10 +1144,13 @@ static inline pte_t ptep_set_wrprotect(struct mm_struct *mm,
|
|
|
|
|
|
if (!mm_exclusive(mm))
|
|
|
__ptep_ipte(address, ptep);
|
|
|
- *ptep = pte_wrprotect(pte);
|
|
|
+ pte = pte_wrprotect(pte);
|
|
|
|
|
|
- if (mm_has_pgste(mm))
|
|
|
+ if (mm_has_pgste(mm)) {
|
|
|
+ pgste_set_pte(ptep, pte);
|
|
|
pgste_set_unlock(ptep, pgste);
|
|
|
+ } else
|
|
|
+ *ptep = pte;
|
|
|
}
|
|
|
return pte;
|
|
|
}
|
|
@@ -1132,10 +1168,12 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma,
|
|
|
pgste = pgste_get_lock(ptep);
|
|
|
|
|
|
__ptep_ipte(address, ptep);
|
|
|
- *ptep = entry;
|
|
|
|
|
|
- if (mm_has_pgste(vma->vm_mm))
|
|
|
+ if (mm_has_pgste(vma->vm_mm)) {
|
|
|
+ pgste_set_pte(ptep, entry);
|
|
|
pgste_set_unlock(ptep, pgste);
|
|
|
+ } else
|
|
|
+ *ptep = entry;
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
@@ -1153,8 +1191,13 @@ static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
|
|
|
static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
|
|
|
{
|
|
|
unsigned long physpage = page_to_phys(page);
|
|
|
+ pte_t __pte = mk_pte_phys(physpage, pgprot);
|
|
|
|
|
|
- return mk_pte_phys(physpage, pgprot);
|
|
|
+ if ((pte_val(__pte) & _PAGE_SWW) && PageDirty(page)) {
|
|
|
+ pte_val(__pte) |= _PAGE_SWC;
|
|
|
+ pte_val(__pte) &= ~_PAGE_RO;
|
|
|
+ }
|
|
|
+ return __pte;
|
|
|
}
|
|
|
|
|
|
#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
|
|
@@ -1246,6 +1289,8 @@ static inline int pmd_trans_splitting(pmd_t pmd)
|
|
|
static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr,
|
|
|
pmd_t *pmdp, pmd_t entry)
|
|
|
{
|
|
|
+ if (!(pmd_val(entry) & _SEGMENT_ENTRY_INV) && MACHINE_HAS_EDAT1)
|
|
|
+ pmd_val(entry) |= _SEGMENT_ENTRY_CO;
|
|
|
*pmdp = entry;
|
|
|
}
|
|
|
|