Skip to content

Commit 9d0ef5e

Browse files
paulusmackavikivity
authored andcommitted
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>
1 parent da9d1d7 commit 9d0ef5e

File tree

4 files changed

+84
-24
lines changed

4 files changed

+84
-24
lines changed

arch/powerpc/include/asm/kvm_book3s_64.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,32 @@ static inline unsigned long hpte_page_size(unsigned long h, unsigned long l)
113113
return 0; /* error */
114114
}
115115

116+
static 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 */
132+
static 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+
116142
static inline bool slot_is_aligned(struct kvm_memory_slot *memslot,
117143
unsigned long pagesize)
118144
{

arch/powerpc/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ struct revmap_entry {
178178

179179
/* Low-order bits in kvm->arch.slot_phys[][] */
180180
#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 */
181183
#define KVMPPC_GOT_PAGE 0x80
182184

183185
struct kvm_arch {

arch/powerpc/kvm/book3s_64_mmu_hv.c

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
199199
struct page *page, *hpage, *pages[1];
200200
unsigned long s, pgsize;
201201
unsigned long *physp;
202-
unsigned int got, pgorder;
202+
unsigned int is_io, got, pgorder;
203+
struct vm_area_struct *vma;
203204
unsigned long pfn, i, npages;
204205

205206
physp = kvm->arch.slot_phys[memslot->id];
@@ -208,42 +209,60 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
208209
if (physp[gfn - memslot->base_gfn])
209210
return 0;
210211

212+
is_io = 0;
213+
got = 0;
211214
page = NULL;
212215
pgsize = psize;
216+
err = -EINVAL;
213217
start = gfn_to_hva_memslot(memslot, gfn);
214218

215219
/* Instantiate and get the page we want access to */
216220
np = get_user_pages_fast(start, 1, 1, pages);
217-
if (np != 1)
218-
return -EINVAL;
219-
page = pages[0];
220-
got = KVMPPC_GOT_PAGE;
221+
if (np != 1) {
222+
/* Look up the vma for the page */
223+
down_read(&current->mm->mmap_sem);
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);
221235

222-
/* See if this is a large page */
223-
s = PAGE_SIZE;
224-
if (PageHuge(page)) {
225-
hpage = compound_head(page);
226-
s <<= compound_order(hpage);
227-
/* Get the whole large page if slot alignment is ok */
228-
if (s > psize && slot_is_aligned(memslot, s) &&
229-
!(memslot->userspace_addr & (s - 1))) {
230-
start &= ~(s - 1);
231-
pgsize = s;
232-
page = hpage;
236+
} else {
237+
page = pages[0];
238+
got = KVMPPC_GOT_PAGE;
239+
240+
/* See if this is a large page */
241+
s = PAGE_SIZE;
242+
if (PageHuge(page)) {
243+
hpage = compound_head(page);
244+
s <<= compound_order(hpage);
245+
/* Get the whole large page if slot alignment is ok */
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+
}
233252
}
253+
if (s < psize)
254+
goto out;
255+
pfn = page_to_pfn(page);
234256
}
235-
err = -EINVAL;
236-
if (s < psize)
237-
goto out;
238-
pfn = page_to_pfn(page);
239257

240258
npages = pgsize >> PAGE_SHIFT;
241259
pgorder = __ilog2(npages);
242260
physp += (gfn - memslot->base_gfn) & ~(npages - 1);
243261
spin_lock(&kvm->arch.slot_phys_lock);
244262
for (i = 0; i < npages; ++i) {
245263
if (!physp[i]) {
246-
physp[i] = ((pfn + i) << PAGE_SHIFT) + got + pgorder;
264+
physp[i] = ((pfn + i) << PAGE_SHIFT) +
265+
got + is_io + pgorder;
247266
got = 0;
248267
}
249268
}
@@ -257,6 +276,10 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
257276
put_page(page);
258277
}
259278
return err;
279+
280+
up_err:
281+
up_read(&current->mm->mmap_sem);
282+
return err;
260283
}
261284

262285
/*

arch/powerpc/kvm/book3s_hv_rm_mmu.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
6565
unsigned long g_ptel = ptel;
6666
struct kvm_memory_slot *memslot;
6767
unsigned long *physp, pte_size;
68+
unsigned long is_io;
6869
bool realmode = vcpu->arch.vcore->vcore_state == VCORE_RUNNING;
6970

7071
psize = hpte_page_size(pteh, ptel);
@@ -92,6 +93,7 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
9293
pa = *physp;
9394
if (!pa)
9495
return H_TOO_HARD;
96+
is_io = pa & (HPTE_R_I | HPTE_R_W);
9597
pte_size = PAGE_SIZE << (pa & KVMPPC_PAGE_ORDER_MASK);
9698
pa &= PAGE_MASK;
9799

@@ -104,9 +106,16 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
104106
ptel |= pa;
105107

106108
/* Check WIMG */
107-
if ((ptel & HPTE_R_WIMG) != HPTE_R_M &&
108-
(ptel & HPTE_R_WIMG) != (HPTE_R_W | HPTE_R_I | HPTE_R_M))
109-
return H_PARAMETER;
109+
if (!hpte_cache_flags_ok(ptel, is_io)) {
110+
if (is_io)
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+
}
110119
pteh &= ~0x60UL;
111120
pteh |= HPTE_V_VALID;
112121

0 commit comments

Comments
 (0)