pgprot_t pgprot_writecombine(pgprot_t prot)
{ /* * mio_wb_bit_mask may be set on a different CPU, but it is only set * once at init and only read afterwards.
*/ return __pgprot(pgprot_val(prot) | mio_wb_bit_mask);
}
EXPORT_SYMBOL_GPL(pgprot_writecombine);
if (!mm_uses_skeys(mm) || pte_val(entry) & _PAGE_INVALID) return;
VM_BUG_ON(!(pte_val(*ptep) & _PAGE_INVALID));
address = pte_val(entry) & PAGE_MASK; /* * Set page access key and fetch protection bit from pgste. * The guest C/R information is still in the PGSTE, set real * key C/R to 0.
*/
nkey = (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56;
nkey |= (pgste_val(pgste) & (PGSTE_GR_BIT | PGSTE_GC_BIT)) >> 48;
page_set_storage_key(address, nkey, 0); #endif
}
staticinline pgste_t pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry)
{ #ifdef CONFIG_PGSTE if ((pte_val(entry) & _PAGE_PRESENT) &&
(pte_val(entry) & _PAGE_WRITE) &&
!(pte_val(entry) & _PAGE_INVALID)) { if (!machine_has_esop()) { /* * Without enhanced suppression-on-protection force * the dirty bit on for all writable ptes.
*/
entry = set_pte_bit(entry, __pgprot(_PAGE_DIRTY));
entry = clear_pte_bit(entry, __pgprot(_PAGE_PROTECT));
} if (!(pte_val(entry) & _PAGE_PROTECT)) /* This pte allows write access, set user-dirty */
pgste = set_pgste_bit(pgste, PGSTE_UC_BIT);
} #endif
set_pte(ptep, entry); return pgste;
}
/* * Caller must check that new PTE only differs in _PAGE_PROTECT HW bit, so that * RDP can be used instead of IPTE. See also comments at pte_allow_rdp().
*/ void ptep_reset_dat_prot(struct mm_struct *mm, unsignedlong addr, pte_t *ptep,
pte_t new)
{
preempt_disable();
atomic_inc(&mm->context.flush_count); if (cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
__ptep_rdp(addr, ptep, 0, 0, 1); else
__ptep_rdp(addr, ptep, 0, 0, 0); /* * PTE is not invalidated by RDP, only _PAGE_PROTECT is cleared. That * means it is still valid and active, and must not be changed according * to the architecture. But writing a new value that only differs in SW * bits is allowed.
*/
set_pte(ptep, new);
atomic_dec(&mm->context.flush_count);
preempt_enable();
}
EXPORT_SYMBOL(ptep_reset_dat_prot);
staticinlinevoid pudp_idte_global(struct mm_struct *mm, unsignedlong addr, pud_t *pudp)
{ if (machine_has_tlb_guest())
__pudp_idte(addr, pudp, IDTE_NODAT | IDTE_GUEST_ASCE,
mm->context.asce, IDTE_GLOBAL); elseif (cpu_has_idte())
__pudp_idte(addr, pudp, 0, 0, IDTE_GLOBAL); else /* * Invalid bit position is the same for pmd and pud, so we can * reuse _pmd_csp() here
*/
__pmdp_csp((pmd_t *) pudp);
}
/** * ptep_force_prot - change access rights of a locked pte * @mm: pointer to the process mm_struct * @addr: virtual address in the guest address space * @ptep: pointer to the page table entry * @prot: indicates guest access rights: PROT_NONE, PROT_READ or PROT_WRITE * @bit: pgste bit to set (e.g. for notification) * * Returns 0 if the access rights were changed and -EAGAIN if the current * and requested access rights are incompatible.
*/ int ptep_force_prot(struct mm_struct *mm, unsignedlong addr,
pte_t *ptep, int prot, unsignedlong bit)
{
pte_t entry;
pgste_t pgste; int pte_i, pte_p, nodat;
/* * Test and reset if a guest page is dirty
*/ bool ptep_test_and_clear_uc(struct mm_struct *mm, unsignedlong addr,
pte_t *ptep)
{
pgste_t pgste;
pte_t pte; bool dirty; int nodat;
/* * If we don't have a PTE table and if there is no huge page mapped, * we can ignore attempts to set the key to 0, because it already is 0.
*/ switch (pmd_lookup(mm, addr, &pmdp)) { case -ENOENT: return key ? -EFAULT : 0; case 0: break; default: return -EFAULT;
}
again:
ptl = pmd_lock(mm, pmdp); if (!pmd_present(*pmdp)) {
spin_unlock(ptl); return key ? -EFAULT : 0;
}
if (pmd_leaf(*pmdp)) {
paddr = pmd_val(*pmdp) & HPAGE_MASK;
paddr |= addr & ~HPAGE_MASK; /* * Huge pmds need quiescing operations, they are * always mapped.
*/
page_set_storage_key(paddr, key, 1);
spin_unlock(ptl); return 0;
}
spin_unlock(ptl);
ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl); if (!ptep) goto again; new = old = pgste_get_lock(ptep); new = clear_pgste_bit(new, PGSTE_GR_BIT | PGSTE_GC_BIT |
PGSTE_ACC_BITS | PGSTE_FP_BIT);
keyul = (unsignedlong) key; new = set_pgste_bit(new, (keyul & (_PAGE_CHANGED | _PAGE_REFERENCED)) << 48); new = set_pgste_bit(new, (keyul & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56); if (!(pte_val(*ptep) & _PAGE_INVALID)) { unsignedlong bits, skey;
paddr = pte_val(*ptep) & PAGE_MASK;
skey = (unsignedlong) page_get_storage_key(paddr);
bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
skey = key & (_PAGE_ACC_BITS | _PAGE_FP_BIT); /* Set storage key ACC and FP */
page_set_storage_key(paddr, skey, !nq); /* Merge host changed & referenced into pgste */ new = set_pgste_bit(new, bits << 52);
} /* changing the guest storage key is considered a change of the page */ if ((pgste_val(new) ^ pgste_val(old)) &
(PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT)) new = set_pgste_bit(new, PGSTE_UC_BIT);
/* * Conditionally set a guest storage key (handling csske). * oldkey will be updated when either mr or mc is set and a pointer is given. * * Returns 0 if a guests storage key update wasn't necessary, 1 if the guest * storage key was updated and -EFAULT on access errors.
*/ int cond_set_guest_storage_key(struct mm_struct *mm, unsignedlong addr, unsignedchar key, unsignedchar *oldkey, bool nq, bool mr, bool mc)
{ unsignedchar tmp, mask = _PAGE_ACC_BITS | _PAGE_FP_BIT; int rc;
/* we can drop the pgste lock between getting and setting the key */ if (mr | mc) {
rc = get_guest_storage_key(current->mm, addr, &tmp); if (rc) return rc; if (oldkey)
*oldkey = tmp; if (!mr)
mask |= _PAGE_REFERENCED; if (!mc)
mask |= _PAGE_CHANGED; if (!((tmp ^ key) & mask)) return 0;
}
rc = set_guest_storage_key(current->mm, addr, key, nq); return rc < 0 ? rc : 1;
}
EXPORT_SYMBOL(cond_set_guest_storage_key);
/* * Reset a guest reference bit (rrbe), returning the reference and changed bit. * * Returns < 0 in case of error, otherwise the cc to be reported to the guest.
*/ int reset_guest_reference_bit(struct mm_struct *mm, unsignedlong addr)
{
spinlock_t *ptl; unsignedlong paddr;
pgste_t old, new;
pmd_t *pmdp;
pte_t *ptep; int cc = 0;
/* * If we don't have a PTE table and if there is no huge page mapped, * the storage key is 0 and there is nothing for us to do.
*/ switch (pmd_lookup(mm, addr, &pmdp)) { case -ENOENT: return 0; case 0: break; default: return -EFAULT;
}
again:
ptl = pmd_lock(mm, pmdp); if (!pmd_present(*pmdp)) {
spin_unlock(ptl); return 0;
}
ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl); if (!ptep) goto again; new = old = pgste_get_lock(ptep); /* Reset guest reference bit only */ new = clear_pgste_bit(new, PGSTE_GR_BIT);
if (!(pte_val(*ptep) & _PAGE_INVALID)) {
paddr = pte_val(*ptep) & PAGE_MASK;
cc = page_reset_referenced(paddr); /* Merge real referenced bit into host-set */ new = set_pgste_bit(new, ((unsignedlong)cc << 53) & PGSTE_HR_BIT);
} /* Reflect guest's logical view, not physical */
cc |= (pgste_val(old) & (PGSTE_GR_BIT | PGSTE_GC_BIT)) >> 49; /* Changing the guest storage key is considered a change of the page */ if ((pgste_val(new) ^ pgste_val(old)) & PGSTE_GR_BIT) new = set_pgste_bit(new, PGSTE_UC_BIT);
/** * pgste_perform_essa - perform ESSA actions on the PGSTE. * @mm: the memory context. It must have PGSTEs, no check is performed here! * @hva: the host virtual address of the page whose PGSTE is to be processed * @orc: the specific action to perform, see the ESSA_SET_* macros. * @oldpte: the PTE will be saved there if the pointer is not NULL. * @oldpgste: the old PGSTE will be saved there if the pointer is not NULL. * * Return: 1 if the page is to be added to the CBRL, otherwise 0, * or < 0 in case of error. -EINVAL is returned for invalid values * of orc, -EFAULT for invalid addresses.
*/ int pgste_perform_essa(struct mm_struct *mm, unsignedlong hva, int orc, unsignedlong *oldpte, unsignedlong *oldpgste)
{ struct vm_area_struct *vma; unsignedlong pgstev;
spinlock_t *ptl;
pgste_t pgste;
pte_t *ptep; int res = 0;
WARN_ON_ONCE(orc > ESSA_MAX); if (unlikely(orc > ESSA_MAX)) return -EINVAL;
vma = vma_lookup(mm, hva); if (!vma || is_vm_hugetlb_page(vma)) return -EFAULT;
ptep = get_locked_pte(mm, hva, &ptl); if (unlikely(!ptep)) return -EFAULT;
pgste = pgste_get_lock(ptep);
pgstev = pgste_val(pgste); if (oldpte)
*oldpte = pte_val(*ptep); if (oldpgste)
*oldpgste = pgstev;
switch (orc) { case ESSA_GET_STATE: break; case ESSA_SET_STABLE:
pgstev &= ~(_PGSTE_GPS_USAGE_MASK | _PGSTE_GPS_NODAT);
pgstev |= _PGSTE_GPS_USAGE_STABLE; break; case ESSA_SET_UNUSED:
pgstev &= ~_PGSTE_GPS_USAGE_MASK;
pgstev |= _PGSTE_GPS_USAGE_UNUSED; if (pte_val(*ptep) & _PAGE_INVALID)
res = 1; break; case ESSA_SET_VOLATILE:
pgstev &= ~_PGSTE_GPS_USAGE_MASK;
pgstev |= _PGSTE_GPS_USAGE_VOLATILE; if (pte_val(*ptep) & _PAGE_INVALID)
res = 1; break; case ESSA_SET_POT_VOLATILE:
pgstev &= ~_PGSTE_GPS_USAGE_MASK; if (!(pte_val(*ptep) & _PAGE_INVALID)) {
pgstev |= _PGSTE_GPS_USAGE_POT_VOLATILE; break;
} if (pgstev & _PGSTE_GPS_ZERO) {
pgstev |= _PGSTE_GPS_USAGE_VOLATILE; break;
} if (!(pgstev & PGSTE_GC_BIT)) {
pgstev |= _PGSTE_GPS_USAGE_VOLATILE;
res = 1; break;
} break; case ESSA_SET_STABLE_RESIDENT:
pgstev &= ~_PGSTE_GPS_USAGE_MASK;
pgstev |= _PGSTE_GPS_USAGE_STABLE; /* * Since the resident state can go away any time after this * call, we will not make this page resident. We can revisit * this decision if a guest will ever start using this.
*/ break; case ESSA_SET_STABLE_IF_RESIDENT: if (!(pte_val(*ptep) & _PAGE_INVALID)) {
pgstev &= ~_PGSTE_GPS_USAGE_MASK;
pgstev |= _PGSTE_GPS_USAGE_STABLE;
} break; case ESSA_SET_STABLE_NODAT:
pgstev &= ~_PGSTE_GPS_USAGE_MASK;
pgstev |= _PGSTE_GPS_USAGE_STABLE | _PGSTE_GPS_NODAT; break; default: /* we should never get here! */ break;
} /* If we are discarding a page, set it to logical zero */ if (res)
pgstev |= _PGSTE_GPS_ZERO;
/** * set_pgste_bits - set specific PGSTE bits. * @mm: the memory context. It must have PGSTEs, no check is performed here! * @hva: the host virtual address of the page whose PGSTE is to be processed * @bits: a bitmask representing the bits that will be touched * @value: the values of the bits to be written. Only the bits in the mask * will be written. * * Return: 0 on success, < 0 in case of error.
*/ int set_pgste_bits(struct mm_struct *mm, unsignedlong hva, unsignedlong bits, unsignedlong value)
{ struct vm_area_struct *vma;
spinlock_t *ptl;
pgste_t new;
pte_t *ptep;
vma = vma_lookup(mm, hva); if (!vma || is_vm_hugetlb_page(vma)) return -EFAULT;
ptep = get_locked_pte(mm, hva, &ptl); if (unlikely(!ptep)) return -EFAULT; new = pgste_get_lock(ptep);
new = clear_pgste_bit(new, bits); new = set_pgste_bit(new, value & bits);
/** * get_pgste - get the current PGSTE for the given address. * @mm: the memory context. It must have PGSTEs, no check is performed here! * @hva: the host virtual address of the page whose PGSTE is to be processed * @pgstep: will be written with the current PGSTE for the given address. * * Return: 0 on success, < 0 in case of error.
*/ int get_pgste(struct mm_struct *mm, unsignedlong hva, unsignedlong *pgstep)
{ struct vm_area_struct *vma;
spinlock_t *ptl;
pte_t *ptep;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.