aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2011-12-12 07:32:27 -0500
committerAvi Kivity <avi@redhat.com>2012-03-05 07:52:37 -0500
commit9d0ef5ea043d1242897d15c71bd1a15da79b4a5d (patch)
tree2847b3bd444b999f3c2a32d4cbb220d0e788e93a /arch
parentda9d1d7f2875cc8c1ffbce8f3501d0b33f4e7a4d (diff)
KVM: PPC: Allow I/O mappings in memory slots
This provides for the case where userspace maps an I/O device into the address range of a memory slot using a VM_PFNMAP mapping. In that case, we work out the pfn from vma->vm_pgoff, and record the cache enable bits from vma->vm_page_prot in two low-order bits in the slot_phys array entries. Then, in kvmppc_h_enter() we check that the cache bits in the HPTE that the guest wants to insert match the cache bits in the slot_phys array entry. However, we do allow the guest to create what it thinks is a non-cacheable or write-through mapping to memory that is actually cacheable, so that we can use normal system memory as part of an emulated device later on. In that case the actual HPTE we insert is a cacheable HPTE. Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/include/asm/kvm_book3s_64.h26
-rw-r--r--arch/powerpc/include/asm/kvm_host.h2
-rw-r--r--arch/powerpc/kvm/book3s_64_mmu_hv.c65
-rw-r--r--arch/powerpc/kvm/book3s_hv_rm_mmu.c15
4 files changed, 84 insertions, 24 deletions
diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h
index 10920f7a2b8d..18b590d261ff 100644
--- a/arch/powerpc/include/asm/kvm_book3s_64.h
+++ b/arch/powerpc/include/asm/kvm_book3s_64.h
@@ -113,6 +113,32 @@ static inline unsigned long hpte_page_size(unsigned long h, unsigned long l)
113 return 0; /* error */ 113 return 0; /* error */
114} 114}
115 115
116static inline int hpte_cache_flags_ok(unsigned long ptel, unsigned long io_type)
117{
118 unsigned int wimg = ptel & HPTE_R_WIMG;
119
120 /* Handle SAO */
121 if (wimg == (HPTE_R_W | HPTE_R_I | HPTE_R_M) &&
122 cpu_has_feature(CPU_FTR_ARCH_206))
123 wimg = HPTE_R_M;
124
125 if (!io_type)
126 return wimg == HPTE_R_M;
127
128 return (wimg & (HPTE_R_W | HPTE_R_I)) == io_type;
129}
130
131/* Return HPTE cache control bits corresponding to Linux pte bits */
132static inline unsigned long hpte_cache_bits(unsigned long pte_val)
133{
134#if _PAGE_NO_CACHE == HPTE_R_I && _PAGE_WRITETHRU == HPTE_R_W
135 return pte_val & (HPTE_R_W | HPTE_R_I);
136#else
137 return ((pte_val & _PAGE_NO_CACHE) ? HPTE_R_I : 0) +
138 ((pte_val & _PAGE_WRITETHRU) ? HPTE_R_W : 0);
139#endif
140}
141
116static inline bool slot_is_aligned(struct kvm_memory_slot *memslot, 142static inline bool slot_is_aligned(struct kvm_memory_slot *memslot,
117 unsigned long pagesize) 143 unsigned long pagesize)
118{ 144{
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index 9252d5e3758d..243bc8038572 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -178,6 +178,8 @@ struct revmap_entry {
178 178
179/* Low-order bits in kvm->arch.slot_phys[][] */ 179/* Low-order bits in kvm->arch.slot_phys[][] */
180#define KVMPPC_PAGE_ORDER_MASK 0x1f 180#define KVMPPC_PAGE_ORDER_MASK 0x1f
181#define KVMPPC_PAGE_NO_CACHE HPTE_R_I /* 0x20 */
182#define KVMPPC_PAGE_WRITETHRU HPTE_R_W /* 0x40 */
181#define KVMPPC_GOT_PAGE 0x80 183#define KVMPPC_GOT_PAGE 0x80
182 184
183struct kvm_arch { 185struct kvm_arch {
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index cc18f3d67a57..b904c40a17bc 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -199,7 +199,8 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
199 struct page *page, *hpage, *pages[1]; 199 struct page *page, *hpage, *pages[1];
200 unsigned long s, pgsize; 200 unsigned long s, pgsize;
201 unsigned long *physp; 201 unsigned long *physp;
202 unsigned int got, pgorder; 202 unsigned int is_io, got, pgorder;
203 struct vm_area_struct *vma;
203 unsigned long pfn, i, npages; 204 unsigned long pfn, i, npages;
204 205
205 physp = kvm->arch.slot_phys[memslot->id]; 206 physp = kvm->arch.slot_phys[memslot->id];
@@ -208,34 +209,51 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
208 if (physp[gfn - memslot->base_gfn]) 209 if (physp[gfn - memslot->base_gfn])
209 return 0; 210 return 0;
210 211
212 is_io = 0;
213 got = 0;
211 page = NULL; 214 page = NULL;
212 pgsize = psize; 215 pgsize = psize;
216 err = -EINVAL;
213 start = gfn_to_hva_memslot(memslot, gfn); 217 start = gfn_to_hva_memslot(memslot, gfn);
214 218
215 /* Instantiate and get the page we want access to */ 219 /* Instantiate and get the page we want access to */
216 np = get_user_pages_fast(start, 1, 1, pages); 220 np = get_user_pages_fast(start, 1, 1, pages);
217 if (np != 1) 221 if (np != 1) {
218 return -EINVAL; 222 /* Look up the vma for the page */
219 page = pages[0]; 223 down_read(&current->mm->mmap_sem);
220 got = KVMPPC_GOT_PAGE; 224 vma = find_vma(current->mm, start);
225 if (!vma || vma->vm_start > start ||
226 start + psize > vma->vm_end ||
227 !(vma->vm_flags & VM_PFNMAP))
228 goto up_err;
229 is_io = hpte_cache_bits(pgprot_val(vma->vm_page_prot));
230 pfn = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
231 /* check alignment of pfn vs. requested page size */
232 if (psize > PAGE_SIZE && (pfn & ((psize >> PAGE_SHIFT) - 1)))
233 goto up_err;
234 up_read(&current->mm->mmap_sem);
221 235
222 /* See if this is a large page */ 236 } else {
223 s = PAGE_SIZE; 237 page = pages[0];
224 if (PageHuge(page)) { 238 got = KVMPPC_GOT_PAGE;
225 hpage = compound_head(page); 239
226 s <<= compound_order(hpage); 240 /* See if this is a large page */
227 /* Get the whole large page if slot alignment is ok */ 241 s = PAGE_SIZE;
228 if (s > psize && slot_is_aligned(memslot, s) && 242 if (PageHuge(page)) {
229 !(memslot->userspace_addr & (s - 1))) { 243 hpage = compound_head(page);
230 start &= ~(s - 1); 244 s <<= compound_order(hpage);
231 pgsize = s; 245 /* Get the whole large page if slot alignment is ok */
232 page = hpage; 246 if (s > psize && slot_is_aligned(memslot, s) &&
247 !(memslot->userspace_addr & (s - 1))) {
248 start &= ~(s - 1);
249 pgsize = s;
250 page = hpage;
251 }
233 } 252 }
253 if (s < psize)
254 goto out;
255 pfn = page_to_pfn(page);
234 } 256 }
235 err = -EINVAL;
236 if (s < psize)
237 goto out;
238 pfn = page_to_pfn(page);
239 257
240 npages = pgsize >> PAGE_SHIFT; 258 npages = pgsize >> PAGE_SHIFT;
241 pgorder = __ilog2(npages); 259 pgorder = __ilog2(npages);
@@ -243,7 +261,8 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
243 spin_lock(&kvm->arch.slot_phys_lock); 261 spin_lock(&kvm->arch.slot_phys_lock);
244 for (i = 0; i < npages; ++i) { 262 for (i = 0; i < npages; ++i) {
245 if (!physp[i]) { 263 if (!physp[i]) {
246 physp[i] = ((pfn + i) << PAGE_SHIFT) + got + pgorder; 264 physp[i] = ((pfn + i) << PAGE_SHIFT) +
265 got + is_io + pgorder;
247 got = 0; 266 got = 0;
248 } 267 }
249 } 268 }
@@ -257,6 +276,10 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
257 put_page(page); 276 put_page(page);
258 } 277 }
259 return err; 278 return err;
279
280 up_err:
281 up_read(&current->mm->mmap_sem);
282 return err;
260} 283}
261 284
262/* 285/*
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index c086eb0fa992..3f5b016490d0 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -65,6 +65,7 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
65 unsigned long g_ptel = ptel; 65 unsigned long g_ptel = ptel;
66 struct kvm_memory_slot *memslot; 66 struct kvm_memory_slot *memslot;
67 unsigned long *physp, pte_size; 67 unsigned long *physp, pte_size;
68 unsigned long is_io;
68 bool realmode = vcpu->arch.vcore->vcore_state == VCORE_RUNNING; 69 bool realmode = vcpu->arch.vcore->vcore_state == VCORE_RUNNING;
69 70
70 psize = hpte_page_size(pteh, ptel); 71 psize = hpte_page_size(pteh, ptel);
@@ -92,6 +93,7 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
92 pa = *physp; 93 pa = *physp;
93 if (!pa) 94 if (!pa)
94 return H_TOO_HARD; 95 return H_TOO_HARD;
96 is_io = pa & (HPTE_R_I | HPTE_R_W);
95 pte_size = PAGE_SIZE << (pa & KVMPPC_PAGE_ORDER_MASK); 97 pte_size = PAGE_SIZE << (pa & KVMPPC_PAGE_ORDER_MASK);
96 pa &= PAGE_MASK; 98 pa &= PAGE_MASK;
97 99
@@ -104,9 +106,16 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
104 ptel |= pa; 106 ptel |= pa;
105 107
106 /* Check WIMG */ 108 /* Check WIMG */
107 if ((ptel & HPTE_R_WIMG) != HPTE_R_M && 109 if (!hpte_cache_flags_ok(ptel, is_io)) {
108 (ptel & HPTE_R_WIMG) != (HPTE_R_W | HPTE_R_I | HPTE_R_M)) 110 if (is_io)
109 return H_PARAMETER; 111 return H_PARAMETER;
112 /*
113 * Allow guest to map emulated device memory as
114 * uncacheable, but actually make it cacheable.
115 */
116 ptel &= ~(HPTE_R_W|HPTE_R_I|HPTE_R_G);
117 ptel |= HPTE_R_M;
118 }
110 pteh &= ~0x60UL; 119 pteh &= ~0x60UL;
111 pteh |= HPTE_V_VALID; 120 pteh |= HPTE_V_VALID;
112 121