KVM: PPC: Book3S HV: Make kvmppc_mmu_radix_xlate process/partition table agnostic
authorSuraj Jitindar Singh <sjitindarsingh@gmail.com>
Mon, 8 Oct 2018 05:31:00 +0000 (16:31 +1100)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 9 Oct 2018 05:04:27 +0000 (16:04 +1100)
kvmppc_mmu_radix_xlate() is used to translate an effective address
through the process tables. The process table and partition tables have
identical layout. Exploit this fact to make the kvmppc_mmu_radix_xlate()
function able to translate either an effective address through the
process tables or a guest real address through the partition tables.

[paulus@ozlabs.org - reduced diffs from previous code]

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/kvm_book3s.h
arch/powerpc/kvm/book3s_64_mmu_radix.c

index dd18d8174504f5550538a6536aff04f7f6cd38ea..91c977948828155c09e598d61e5df35b28ac5a27 100644 (file)
@@ -188,6 +188,9 @@ extern int kvmppc_book3s_hcall_implemented(struct kvm *kvm, unsigned long hc);
 extern int kvmppc_book3s_radix_page_fault(struct kvm_run *run,
                        struct kvm_vcpu *vcpu,
                        unsigned long ea, unsigned long dsisr);
+extern int kvmppc_mmu_radix_translate_table(struct kvm_vcpu *vcpu, gva_t eaddr,
+                       struct kvmppc_pte *gpte, u64 table,
+                       int table_index, u64 *pte_ret_p);
 extern int kvmppc_mmu_radix_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
                        struct kvmppc_pte *gpte, bool data, bool iswrite);
 extern int kvmppc_init_vm_radix(struct kvm *kvm);
index 71951b5a2c68f7c70db0e2395442f97d674d2901..f2976f456fe0c64904e8ee36e3247a83aff619f6 100644 (file)
  */
 static int p9_supported_radix_bits[4] = { 5, 9, 9, 13 };
 
-int kvmppc_mmu_radix_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
-                          struct kvmppc_pte *gpte, bool data, bool iswrite)
+/*
+ * Used to walk a partition or process table radix tree in guest memory
+ * Note: We exploit the fact that a partition table and a process
+ * table have the same layout, a partition-scoped page table and a
+ * process-scoped page table have the same layout, and the 2nd
+ * doubleword of a partition table entry has the same layout as
+ * the PTCR register.
+ */
+int kvmppc_mmu_radix_translate_table(struct kvm_vcpu *vcpu, gva_t eaddr,
+                                    struct kvmppc_pte *gpte, u64 table,
+                                    int table_index, u64 *pte_ret_p)
 {
        struct kvm *kvm = vcpu->kvm;
-       u32 pid;
        int ret, level, ps;
-       __be64 prte, rpte;
-       unsigned long ptbl;
-       unsigned long root, pte, index;
+       unsigned long ptbl, root;
        unsigned long rts, bits, offset;
-       unsigned long gpa;
-       unsigned long proc_tbl_size;
+       unsigned long size, index;
+       struct prtb_entry entry;
+       u64 pte, base, gpa;
+       __be64 rpte;
 
-       /* Work out effective PID */
-       switch (eaddr >> 62) {
-       case 0:
-               pid = vcpu->arch.pid;
-               break;
-       case 3:
-               pid = 0;
-               break;
-       default:
+       if ((table & PRTS_MASK) > 24)
                return -EINVAL;
-       }
-       proc_tbl_size = 1 << ((kvm->arch.process_table & PRTS_MASK) + 12);
-       if (pid * 16 >= proc_tbl_size)
+       size = 1ul << ((table & PRTS_MASK) + 12);
+
+       /* Is the table big enough to contain this entry? */
+       if ((table_index * sizeof(entry)) >= size)
                return -EINVAL;
 
-       /* Read partition table to find root of tree for effective PID */
-       ptbl = (kvm->arch.process_table & PRTB_MASK) + (pid * 16);
-       ret = kvm_read_guest(kvm, ptbl, &prte, sizeof(prte));
+       /* Read the table to find the root of the radix tree */
+       ptbl = (table & PRTB_MASK) + (table_index * sizeof(entry));
+       ret = kvm_read_guest(kvm, ptbl, &entry, sizeof(entry));
        if (ret)
                return ret;
 
-       root = be64_to_cpu(prte);
+       /* Root is stored in the first double word */
+       root = be64_to_cpu(entry.prtb0);
        rts = ((root & RTS1_MASK) >> (RTS1_SHIFT - 3)) |
                ((root & RTS2_MASK) >> RTS2_SHIFT);
        bits = root & RPDS_MASK;
-       root = root & RPDB_MASK;
+       base = root & RPDB_MASK;
 
        offset = rts + 31;
 
-       /* current implementations only support 52-bit space */
+       /* 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) {
+               /* 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 (root & ((1UL << (bits + 3)) - 1))
+               /* Check that low bits of page table base are zero */
+               if (base & ((1UL << (bits + 3)) - 1))
                        return -EINVAL;
-               ret = kvm_read_guest(kvm, root + index * 8,
+               /* Read the entry from guest memory */
+               ret = kvm_read_guest(kvm, base + (index * sizeof(rpte)),
                                     &rpte, sizeof(rpte));
                if (ret)
                        return ret;
                pte = __be64_to_cpu(rpte);
                if (!(pte & _PAGE_PRESENT))
                        return -ENOENT;
+               /* Check if a leaf entry */
                if (pte & _PAGE_PTE)
                        break;
-               bits = pte & 0x1f;
-               root = pte & 0x0fffffffffffff00ul;
+               /* 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 */
+
+       /* Need a leaf at lowest level; 512GB pages not supported */
        if (level < 0 || level == 3)
                return -EINVAL;
 
-       /* offset is now log base 2 of the page size */
+       /* 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);
+       gpa |= eaddr & ((1ul << offset) - 1);
        for (ps = MMU_PAGE_4K; ps < MMU_PAGE_COUNT; ++ps)
                if (offset == mmu_psize_defs[ps].shift)
                        break;
@@ -118,6 +127,38 @@ int kvmppc_mmu_radix_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
        gpte->may_read = !!(pte & _PAGE_READ);
        gpte->may_write = !!(pte & _PAGE_WRITE);
        gpte->may_execute = !!(pte & _PAGE_EXEC);
+
+       if (pte_ret_p)
+               *pte_ret_p = pte;
+
+       return 0;
+}
+
+int kvmppc_mmu_radix_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
+                          struct kvmppc_pte *gpte, bool data, bool iswrite)
+{
+       u32 pid;
+       u64 pte;
+       int ret;
+
+       /* Work out effective PID */
+       switch (eaddr >> 62) {
+       case 0:
+               pid = vcpu->arch.pid;
+               break;
+       case 3:
+               pid = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = kvmppc_mmu_radix_translate_table(vcpu, eaddr, gpte,
+                               vcpu->kvm->arch.process_table, pid, &pte);
+       if (ret)
+               return ret;
+
+       /* Check privilege (applies only to process scoped translations) */
        if (kvmppc_get_msr(vcpu) & MSR_PR) {
                if (pte & _PAGE_PRIVILEGED) {
                        gpte->may_read = 0;