Skip to content

Commit 9e04ba6

Browse files
paulusmackmpe
authored andcommitted
KVM: PPC: Book3S HV: Add basic infrastructure for radix guests
This adds a field in struct kvm_arch and an inline helper to indicate whether a guest is a radix guest or not, plus a new file to contain the radix MMU code, which currently contains just a translate function which knows how to traverse the guest page tables to translate an address. Signed-off-by: Paul Mackerras <paulus@ozlabs.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
1 parent ef8c640 commit 9e04ba6

File tree

6 files changed

+160
-3
lines changed

6 files changed

+160
-3
lines changed

arch/powerpc/include/asm/kvm_book3s.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ extern void kvmppc_mmu_hpte_sysexit(void);
182182
extern int kvmppc_mmu_hv_init(void);
183183
extern int kvmppc_book3s_hcall_implemented(struct kvm *kvm, unsigned long hc);
184184

185+
extern int kvmppc_mmu_radix_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
186+
struct kvmppc_pte *gpte, bool data, bool iswrite);
187+
185188
/* XXX remove this export when load_last_inst() is generic */
186189
extern int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr, bool data);
187190
extern void kvmppc_book3s_queue_irqprio(struct kvm_vcpu *vcpu, unsigned int vec);

arch/powerpc/include/asm/kvm_book3s_64.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ static inline void svcpu_put(struct kvmppc_book3s_shadow_vcpu *svcpu)
3636
#endif
3737

3838
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
39+
40+
static inline bool kvm_is_radix(struct kvm *kvm)
41+
{
42+
return kvm->arch.radix;
43+
}
44+
3945
#define KVM_DEFAULT_HPT_ORDER 24 /* 16MB HPT by default */
4046
#endif
4147

arch/powerpc/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ struct kvm_arch {
264264
atomic_t hpte_mod_interest;
265265
cpumask_t need_tlb_flush;
266266
int hpt_cma_alloc;
267+
u8 radix;
268+
pgd_t *pgtable;
267269
u64 process_table;
268270
struct dentry *debugfs_dir;
269271
struct dentry *htab_dentry;

arch/powerpc/kvm/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ endif
7070
kvm-hv-y += \
7171
book3s_hv.o \
7272
book3s_hv_interrupts.o \
73-
book3s_64_mmu_hv.o
73+
book3s_64_mmu_hv.o \
74+
book3s_64_mmu_radix.o
7475

7576
kvm-book3s_64-builtin-xics-objs-$(CONFIG_KVM_XICS) := \
7677
book3s_hv_rm_xics.o

arch/powerpc/kvm/book3s_64_mmu_hv.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
119119
long err = -EBUSY;
120120
long order;
121121

122+
if (kvm_is_radix(kvm))
123+
return -EINVAL;
124+
122125
mutex_lock(&kvm->lock);
123126
if (kvm->arch.hpte_setup_done) {
124127
kvm->arch.hpte_setup_done = 0;
@@ -157,7 +160,7 @@ void kvmppc_free_hpt(struct kvm *kvm)
157160
if (kvm->arch.hpt_cma_alloc)
158161
kvm_release_hpt(virt_to_page(kvm->arch.hpt_virt),
159162
1 << (kvm->arch.hpt_order - PAGE_SHIFT));
160-
else
163+
else if (kvm->arch.hpt_virt)
161164
free_pages(kvm->arch.hpt_virt,
162165
kvm->arch.hpt_order - PAGE_SHIFT);
163166
}
@@ -1675,7 +1678,10 @@ void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu)
16751678

16761679
vcpu->arch.slb_nr = 32; /* POWER7/POWER8 */
16771680

1678-
mmu->xlate = kvmppc_mmu_book3s_64_hv_xlate;
1681+
if (kvm_is_radix(vcpu->kvm))
1682+
mmu->xlate = kvmppc_mmu_radix_xlate;
1683+
else
1684+
mmu->xlate = kvmppc_mmu_book3s_64_hv_xlate;
16791685
mmu->reset_msr = kvmppc_mmu_book3s_64_hv_reset_msr;
16801686

16811687
vcpu->arch.hflags |= BOOK3S_HFLAG_SLB;
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* This program is free software; you can redistribute it and/or modify
3+
* it under the terms of the GNU General Public License, version 2, as
4+
* published by the Free Software Foundation.
5+
*
6+
* Copyright 2016 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
7+
*/
8+
9+
#include <linux/types.h>
10+
#include <linux/string.h>
11+
#include <linux/kvm.h>
12+
#include <linux/kvm_host.h>
13+
14+
#include <asm/kvm_ppc.h>
15+
#include <asm/kvm_book3s.h>
16+
#include <asm/page.h>
17+
#include <asm/mmu.h>
18+
#include <asm/pgtable.h>
19+
#include <asm/pgalloc.h>
20+
21+
/*
22+
* Supported radix tree geometry.
23+
* Like p9, we support either 5 or 9 bits at the first (lowest) level,
24+
* for a page size of 64k or 4k.
25+
*/
26+
static int p9_supported_radix_bits[4] = { 5, 9, 9, 13 };
27+
28+
int kvmppc_mmu_radix_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
29+
struct kvmppc_pte *gpte, bool data, bool iswrite)
30+
{
31+
struct kvm *kvm = vcpu->kvm;
32+
u32 pid;
33+
int ret, level, ps;
34+
__be64 prte, rpte;
35+
unsigned long root, pte, index;
36+
unsigned long rts, bits, offset;
37+
unsigned long gpa;
38+
unsigned long proc_tbl_size;
39+
40+
/* Work out effective PID */
41+
switch (eaddr >> 62) {
42+
case 0:
43+
pid = vcpu->arch.pid;
44+
break;
45+
case 3:
46+
pid = 0;
47+
break;
48+
default:
49+
return -EINVAL;
50+
}
51+
proc_tbl_size = 1 << ((kvm->arch.process_table & PRTS_MASK) + 12);
52+
if (pid * 16 >= proc_tbl_size)
53+
return -EINVAL;
54+
55+
/* Read partition table to find root of tree for effective PID */
56+
ret = kvm_read_guest(kvm, kvm->arch.process_table + pid * 16,
57+
&prte, sizeof(prte));
58+
if (ret)
59+
return ret;
60+
61+
root = be64_to_cpu(prte);
62+
rts = ((root & RTS1_MASK) >> (RTS1_SHIFT - 3)) |
63+
((root & RTS2_MASK) >> RTS2_SHIFT);
64+
bits = root & RPDS_MASK;
65+
root = root & RPDB_MASK;
66+
67+
/* P9 DD1 interprets RTS (radix tree size) differently */
68+
offset = rts + 31;
69+
if (cpu_has_feature(CPU_FTR_POWER9_DD1))
70+
offset -= 3;
71+
72+
/* current implementations only support 52-bit space */
73+
if (offset != 52)
74+
return -EINVAL;
75+
76+
for (level = 3; level >= 0; --level) {
77+
if (level && bits != p9_supported_radix_bits[level])
78+
return -EINVAL;
79+
if (level == 0 && !(bits == 5 || bits == 9))
80+
return -EINVAL;
81+
offset -= bits;
82+
index = (eaddr >> offset) & ((1UL << bits) - 1);
83+
/* check that low bits of page table base are zero */
84+
if (root & ((1UL << (bits + 3)) - 1))
85+
return -EINVAL;
86+
ret = kvm_read_guest(kvm, root + index * 8,
87+
&rpte, sizeof(rpte));
88+
if (ret)
89+
return ret;
90+
pte = __be64_to_cpu(rpte);
91+
if (!(pte & _PAGE_PRESENT))
92+
return -ENOENT;
93+
if (pte & _PAGE_PTE)
94+
break;
95+
bits = pte & 0x1f;
96+
root = pte & 0x0fffffffffffff00ul;
97+
}
98+
/* need a leaf at lowest level; 512GB pages not supported */
99+
if (level < 0 || level == 3)
100+
return -EINVAL;
101+
102+
/* offset is now log base 2 of the page size */
103+
gpa = pte & 0x01fffffffffff000ul;
104+
if (gpa & ((1ul << offset) - 1))
105+
return -EINVAL;
106+
gpa += eaddr & ((1ul << offset) - 1);
107+
for (ps = MMU_PAGE_4K; ps < MMU_PAGE_COUNT; ++ps)
108+
if (offset == mmu_psize_defs[ps].shift)
109+
break;
110+
gpte->page_size = ps;
111+
112+
gpte->eaddr = eaddr;
113+
gpte->raddr = gpa;
114+
115+
/* Work out permissions */
116+
gpte->may_read = !!(pte & _PAGE_READ);
117+
gpte->may_write = !!(pte & _PAGE_WRITE);
118+
gpte->may_execute = !!(pte & _PAGE_EXEC);
119+
if (kvmppc_get_msr(vcpu) & MSR_PR) {
120+
if (pte & _PAGE_PRIVILEGED) {
121+
gpte->may_read = 0;
122+
gpte->may_write = 0;
123+
gpte->may_execute = 0;
124+
}
125+
} else {
126+
if (!(pte & _PAGE_PRIVILEGED)) {
127+
/* Check AMR/IAMR to see if strict mode is in force */
128+
if (vcpu->arch.amr & (1ul << 62))
129+
gpte->may_read = 0;
130+
if (vcpu->arch.amr & (1ul << 63))
131+
gpte->may_write = 0;
132+
if (vcpu->arch.iamr & (1ul << 62))
133+
gpte->may_execute = 0;
134+
}
135+
}
136+
137+
return 0;
138+
}
139+

0 commit comments

Comments
 (0)