/* Current implementations only support 52-bit space */ if (offset != 52) return -EINVAL;
/* Walk each level of the radix tree */ for (level = 3; level >= 0; --level) {
u64 addr; /* Check a valid size */ if (level && bits != p9_supported_radix_bits[level]) return -EINVAL; if (level == 0 && !(bits == 5 || bits == 9)) return -EINVAL;
offset -= bits;
index = (eaddr >> offset) & ((1UL << bits) - 1); /* Check that low bits of page table base are zero */ if (base & ((1UL << (bits + 3)) - 1)) return -EINVAL; /* Read the entry from guest memory */
addr = base + (index * sizeof(rpte));
kvm_vcpu_srcu_read_lock(vcpu);
ret = kvm_read_guest(kvm, addr, &rpte, sizeof(rpte));
kvm_vcpu_srcu_read_unlock(vcpu); if (ret) { if (pte_ret_p)
*pte_ret_p = addr; return ret;
}
pte = __be64_to_cpu(rpte); if (!(pte & _PAGE_PRESENT)) return -ENOENT; /* Check if a leaf entry */ if (pte & _PAGE_PTE) break; /* Get ready to walk the next level */
base = pte & RPDB_MASK;
bits = pte & RPDS_MASK;
}
/* Need a leaf at lowest level; 512GB pages not supported */ if (level < 0 || level == 3) return -EINVAL;
/* We found a valid leaf PTE */ /* Offset is now log base 2 of the page size */
gpa = pte & 0x01fffffffffff000ul; if (gpa & ((1ul << offset) - 1)) return -EINVAL;
gpa |= eaddr & ((1ul << offset) - 1); for (ps = MMU_PAGE_4K; ps < MMU_PAGE_COUNT; ++ps) if (offset == mmu_psize_defs[ps].shift) break;
gpte->page_size = ps;
gpte->page_shift = offset;
gpte->eaddr = eaddr;
gpte->raddr = gpa;
/* Work out permissions */
gpte->may_read = !!(pte & _PAGE_READ);
gpte->may_write = !!(pte & _PAGE_WRITE);
gpte->may_execute = !!(pte & _PAGE_EXEC);
/* Is the table big enough to contain this entry? */ if ((table_index * sizeof(entry)) >= size) return -EINVAL;
/* Read the table to find the root of the radix tree */
ptbl = (table & PRTB_MASK) + (table_index * sizeof(entry));
kvm_vcpu_srcu_read_lock(vcpu);
ret = kvm_read_guest(kvm, ptbl, &entry, sizeof(entry));
kvm_vcpu_srcu_read_unlock(vcpu); if (ret) return ret;
/* Root is stored in the first double word */
root = be64_to_cpu(entry.prtb0);
/* Check if we might have been invalidated; let the guest retry if so */
spin_lock(&kvm->mmu_lock);
ret = -EAGAIN; if (mmu_invalidate_retry(kvm, mmu_seq)) goto out_unlock;
/* Now traverse again under the lock and change the tree */
ret = -ENOMEM; if (p4d_none(*p4d)) { if (!new_pud) goto out_unlock;
p4d_populate(kvm->mm, p4d, new_pud);
new_pud = NULL;
}
pud = pud_offset(p4d, gpa); if (pud_leaf(*pud)) { unsignedlong hgpa = gpa & PUD_MASK;
/* Check if we raced and someone else has set the same thing */ if (level == 2) { if (pud_raw(*pud) == pte_raw(pte)) {
ret = 0; goto out_unlock;
} /* Valid 1GB page here already, add our extra bits */
WARN_ON_ONCE((pud_val(*pud) ^ pte_val(pte)) &
PTE_BITS_MUST_MATCH);
kvmppc_radix_update_pte(kvm, (pte_t *)pud, 0, pte_val(pte), hgpa, PUD_SHIFT);
ret = 0; goto out_unlock;
} /* *IfweracedwithanotherCPUwhichhasjustput *a1GBpteinafterwesawapmdpage,tryagain.
*/ if (!new_pmd) {
ret = -EAGAIN; goto out_unlock;
} /* Valid 1GB page here already, remove it */
kvmppc_unmap_pte(kvm, (pte_t *)pud, hgpa, PUD_SHIFT, NULL,
lpid);
} if (level == 2) { if (!pud_none(*pud)) { /* *There'sapagetablepagehere,butwewantedto *installalargepage,soremoveandfreethepage *tablepage.
*/
kvmppc_unmap_free_pud_entry_table(kvm, pud, gpa, lpid);
}
kvmppc_radix_set_pte_at(kvm, gpa, (pte_t *)pud, pte); if (rmapp && n_rmap)
kvmhv_insert_nest_rmap(kvm, rmapp, n_rmap);
ret = 0; goto out_unlock;
} if (pud_none(*pud)) { if (!new_pmd) goto out_unlock;
pud_populate(kvm->mm, pud, new_pmd);
new_pmd = NULL;
}
pmd = pmd_offset(pud, gpa); if (pmd_leaf(*pmd)) { unsignedlong lgpa = gpa & PMD_MASK;
/* Check if we raced and someone else has set the same thing */ if (level == 1) { if (pmd_raw(*pmd) == pte_raw(pte)) {
ret = 0; goto out_unlock;
} /* Valid 2MB page here already, add our extra bits */
WARN_ON_ONCE((pmd_val(*pmd) ^ pte_val(pte)) &
PTE_BITS_MUST_MATCH);
kvmppc_radix_update_pte(kvm, pmdp_ptep(pmd), 0, pte_val(pte), lgpa, PMD_SHIFT);
ret = 0; goto out_unlock;
}
/* *IfweracedwithanotherCPUwhichhasjustput *a2MBpteinafterwesawaptepage,tryagain.
*/ if (!new_ptep) {
ret = -EAGAIN; goto out_unlock;
} /* Valid 2MB page here already, remove it */
kvmppc_unmap_pte(kvm, pmdp_ptep(pmd), lgpa, PMD_SHIFT, NULL,
lpid);
} if (level == 1) { if (!pmd_none(*pmd)) { /* *There'sapagetablepagehere,butwewantedto *installalargepage,soremoveandfreethepage *tablepage.
*/
kvmppc_unmap_free_pmd_entry_table(kvm, pmd, gpa, lpid);
}
kvmppc_radix_set_pte_at(kvm, gpa, pmdp_ptep(pmd), pte); if (rmapp && n_rmap)
kvmhv_insert_nest_rmap(kvm, rmapp, n_rmap);
ret = 0; goto out_unlock;
} if (pmd_none(*pmd)) { if (!new_ptep) goto out_unlock;
pmd_populate(kvm->mm, pmd, new_ptep);
new_ptep = NULL;
}
ptep = pte_offset_kernel(pmd, gpa); if (pte_present(*ptep)) { /* Check if someone else set the same thing */ if (pte_raw(*ptep) == pte_raw(pte)) {
ret = 0; goto out_unlock;
} /* Valid page here already, add our extra bits */
WARN_ON_ONCE((pte_val(*ptep) ^ pte_val(pte)) &
PTE_BITS_MUST_MATCH);
kvmppc_radix_update_pte(kvm, ptep, 0, pte_val(pte), gpa, 0);
ret = 0; goto out_unlock;
}
kvmppc_radix_set_pte_at(kvm, gpa, ptep, pte); if (rmapp && n_rmap)
kvmhv_insert_nest_rmap(kvm, rmapp, n_rmap);
ret = 0;
out_unlock:
spin_unlock(&kvm->mmu_lock); if (new_pud)
pud_free(kvm->mm, new_pud); if (new_pmd)
kvmppc_pmd_free(new_pmd); if (new_ptep)
kvmppc_pte_free(new_ptep); return ret;
}
/* Allocate space in the tree and write the PTE */
ret = kvmppc_create_pte(kvm, kvm->arch.pgtable, pte, gpa, level,
mmu_seq, kvm->arch.lpid, NULL, NULL); if (inserted_pte)
*inserted_pte = pte; if (levelp)
*levelp = level;
if (page) { if (!ret && (pte_val(pte) & _PAGE_WRITE))
set_page_dirty_lock(page);
put_page(page);
}
/* Increment number of large pages if we (successfully) inserted one */ if (!ret) { if (level == 1)
kvm->stat.num_2M_pages++; elseif (level == 2)
kvm->stat.num_1G_pages++;
}
/* Check for unusual errors */ if (dsisr & DSISR_UNSUPP_MMU) {
pr_err("KVM: Got unsupported MMU fault\n"); return -EFAULT;
} if (dsisr & DSISR_BADACCESS) { /* Reflect to the guest as DSI */
pr_err("KVM: Got radix HV page fault with DSISR=%lx\n", dsisr);
kvmppc_core_queue_data_storage(vcpu,
kvmppc_get_msr(vcpu) & SRR1_PREFIXED,
ea, dsisr); return RESUME_GUEST;
}
/* Translate the logical address */
gpa = vcpu->arch.fault_gpa & ~0xfffUL;
gpa &= ~0xF000000000000000ul;
gfn = gpa >> PAGE_SHIFT; if (!(dsisr & DSISR_PRTABLE_FAULT))
gpa |= ea & 0xfff;
if (kvm->arch.secure_guest & KVMPPC_SECURE_INIT_DONE) return kvmppc_send_page_to_uv(kvm, gfn);
/* Get the corresponding memslot */
memslot = gfn_to_memslot(kvm, gfn);
/* No memslot means it's an emulated MMIO region */ if (!memslot || (memslot->flags & KVM_MEMSLOT_INVALID)) { if (dsisr & (DSISR_PRTABLE_FAULT | DSISR_BADACCESS |
DSISR_SET_RC)) { /* *Badaddressinguestpagetabletree,orother *unusualerror-reflectittotheguestasDSI.
*/
kvmppc_core_queue_data_storage(vcpu,
kvmppc_get_msr(vcpu) & SRR1_PREFIXED,
ea, dsisr); return RESUME_GUEST;
} return kvmppc_hv_emulate_mmio(vcpu, gpa, ea, writing);
}
if (memslot->flags & KVM_MEM_READONLY) { if (writing) { /* give the guest a DSI */
kvmppc_core_queue_data_storage(vcpu,
kvmppc_get_msr(vcpu) & SRR1_PREFIXED,
ea, DSISR_ISSTORE | DSISR_PROTFAULT); return RESUME_GUEST;
}
}
/* Failed to set the reference/change bits */ if (dsisr & DSISR_SET_RC) {
spin_lock(&kvm->mmu_lock); if (kvmppc_hv_handle_set_rc(kvm, false, writing,
gpa, kvm->arch.lpid))
dsisr &= ~DSISR_SET_RC;
spin_unlock(&kvm->mmu_lock);
ret = mutex_lock_interruptible(&p->mutex); if (ret) return ret;
if (p->chars_left) {
n = p->chars_left; if (n > len)
n = len;
r = copy_to_user(buf, p->buf + p->buf_index, n);
n -= r;
p->chars_left -= n;
p->buf_index += n;
buf += n;
len -= n;
ret = n; if (r) { if (!n)
ret = -EFAULT; goto out;
}
}
gpa = p->gpa;
nested = NULL;
pgt = NULL; while (len != 0 && p->lpid >= 0) { if (gpa >= RADIX_PGTABLE_RANGE) {
gpa = 0;
pgt = NULL; if (nested) {
kvmhv_put_nested(nested);
nested = NULL;
}
p->lpid = kvmhv_nested_next_lpid(kvm, p->lpid);
p->hdr = 0; if (p->lpid < 0) break;
} if (!pgt) { if (p->lpid == 0) {
pgt = kvm->arch.pgtable;
} else {
nested = kvmhv_get_nested(kvm, p->lpid, false); if (!nested) {
gpa = RADIX_PGTABLE_RANGE; continue;
}
pgt = nested->shadow_pgtable;
}
}
n = 0; if (!p->hdr) { if (p->lpid > 0)
n = scnprintf(p->buf, sizeof(p->buf), "\nNested LPID %d: ", p->lpid);
n += scnprintf(p->buf + n, sizeof(p->buf) - n, "pgdir: %lx\n", (unsignedlong)pgt);
p->hdr = 1; goto copy;
}
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.