linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
From: Alexander Graf <agraf@suse.de>
To: kvm@vger.kernel.org
Cc: Kevin Wolf <kwolf@redhat.com>, Arnd Bergmann <arnd@arndb.de>,
	Hollis Blanchard <hollisb@us.ibm.com>,
	Marcelo Tosatti <mtosatti@redhat.com>,
	kvm-ppc <kvm-ppc@vger.kernel.org>,
	linuxppc-dev@ozlabs.org, Avi Kivity <avi@redhat.com>,
	bphilips@suse.de, Olof Johansson <olof@lixom.net>
Subject: [PATCH 11/27] Add book3s_64 Host MMU handling
Date: Fri, 30 Oct 2009 16:47:11 +0100	[thread overview]
Message-ID: <1256917647-6200-12-git-send-email-agraf@suse.de> (raw)
In-Reply-To: <1256917647-6200-1-git-send-email-agraf@suse.de>

We designed the Book3S port of KVM as modular as possible. Most
of the code could be easily used on a Book3S_32 host as well.

The main difference between 32 and 64 bit cores is the MMU. To keep
things well separated, we treat the book3s_64 MMU as one possible compile
option.

This patch adds all the MMU helpers the rest of the code needs in
order to modify the host's MMU, like setting PTEs and segments.

Signed-off-by: Alexander Graf <agraf@suse.de>

---

v5 -> v6

  - don't take mmap_sem
  - dprintk instead if scattered #ifdef's
  - minor cleanups
  - // -> /* */ (book3s_64_mmu_host.c)
---
 arch/powerpc/kvm/book3s_64_mmu_host.c |  408 +++++++++++++++++++++++++++++++++
 1 files changed, 408 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/kvm/book3s_64_mmu_host.c

diff --git a/arch/powerpc/kvm/book3s_64_mmu_host.c b/arch/powerpc/kvm/book3s_64_mmu_host.c
new file mode 100644
index 0000000..f2899b2
--- /dev/null
+++ b/arch/powerpc/kvm/book3s_64_mmu_host.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2009 SUSE Linux Products GmbH. All rights reserved.
+ *
+ * Authors:
+ *     Alexander Graf <agraf@suse.de>
+ *     Kevin Wolf <mail@kevin-wolf.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_ppc.h>
+#include <asm/kvm_book3s.h>
+#include <asm/mmu-hash64.h>
+#include <asm/machdep.h>
+#include <asm/mmu_context.h>
+#include <asm/hw_irq.h>
+
+#define PTE_SIZE 12
+#define VSID_ALL 0
+
+/* #define DEBUG_MMU */
+/* #define DEBUG_SLB */
+
+#ifdef DEBUG_MMU
+#define dprintk_mmu(a, ...) printk(KERN_INFO a, __VA_ARGS__)
+#else
+#define dprintk_mmu(a, ...) do { } while(0)
+#endif
+
+#ifdef DEBUG_SLB
+#define dprintk_slb(a, ...) printk(KERN_INFO a, __VA_ARGS__)
+#else
+#define dprintk_slb(a, ...) do { } while(0)
+#endif
+
+static void invalidate_pte(struct hpte_cache *pte)
+{
+	dprintk_mmu("KVM: Flushing SPT %d: 0x%llx (0x%llx) -> 0x%llx\n",
+		    i, pte->pte.eaddr, pte->pte.vpage, pte->host_va);
+
+	ppc_md.hpte_invalidate(pte->slot, pte->host_va,
+			       MMU_PAGE_4K, MMU_SEGSIZE_256M,
+			       false);
+	pte->host_va = 0;
+	kvm_release_pfn_dirty(pte->pfn);
+}
+
+void kvmppc_mmu_pte_flush(struct kvm_vcpu *vcpu, u64 guest_ea, u64 ea_mask)
+{
+	int i;
+
+	dprintk_mmu("KVM: Flushing %d Shadow PTEs: 0x%llx & 0x%llx\n",
+		    vcpu->arch.hpte_cache_offset, guest_ea, ea_mask);
+	BUG_ON(vcpu->arch.hpte_cache_offset > HPTEG_CACHE_NUM);
+
+	guest_ea &= ea_mask;
+	for (i = 0; i < vcpu->arch.hpte_cache_offset; i++) {
+		struct hpte_cache *pte;
+
+		pte = &vcpu->arch.hpte_cache[i];
+		if (!pte->host_va)
+			continue;
+
+		if ((pte->pte.eaddr & ea_mask) == guest_ea) {
+			invalidate_pte(pte);
+		}
+	}
+
+	/* Doing a complete flush -> start from scratch */
+	if (!ea_mask)
+		vcpu->arch.hpte_cache_offset = 0;
+}
+
+void kvmppc_mmu_pte_vflush(struct kvm_vcpu *vcpu, u64 guest_vp, u64 vp_mask)
+{
+	int i;
+
+	dprintk_mmu("KVM: Flushing %d Shadow vPTEs: 0x%llx & 0x%llx\n",
+		    vcpu->arch.hpte_cache_offset, guest_vp, vp_mask);
+	BUG_ON(vcpu->arch.hpte_cache_offset > HPTEG_CACHE_NUM);
+
+	guest_vp &= vp_mask;
+	for (i = 0; i < vcpu->arch.hpte_cache_offset; i++) {
+		struct hpte_cache *pte;
+
+		pte = &vcpu->arch.hpte_cache[i];
+		if (!pte->host_va)
+			continue;
+
+		if ((pte->pte.vpage & vp_mask) == guest_vp) {
+			invalidate_pte(pte);
+		}
+	}
+}
+
+void kvmppc_mmu_pte_pflush(struct kvm_vcpu *vcpu, u64 pa_start, u64 pa_end)
+{
+	int i;
+
+	dprintk_mmu("KVM: Flushing %d Shadow pPTEs: 0x%llx & 0x%llx\n",
+		    vcpu->arch.hpte_cache_offset, guest_pa, pa_mask);
+	BUG_ON(vcpu->arch.hpte_cache_offset > HPTEG_CACHE_NUM);
+
+	for (i = 0; i < vcpu->arch.hpte_cache_offset; i++) {
+		struct hpte_cache *pte;
+
+		pte = &vcpu->arch.hpte_cache[i];
+		if (!pte->host_va)
+			continue;
+
+		if ((pte->pte.raddr >= pa_start) &&
+		    (pte->pte.raddr < pa_end)) {
+			invalidate_pte(pte);
+		}
+	}
+}
+
+struct kvmppc_pte *kvmppc_mmu_find_pte(struct kvm_vcpu *vcpu, u64 ea, bool data)
+{
+	int i;
+	u64 guest_vp;
+
+	guest_vp = vcpu->arch.mmu.ea_to_vp(vcpu, ea, false);
+	for (i=0; i<vcpu->arch.hpte_cache_offset; i++) {
+		struct hpte_cache *pte;
+
+		pte = &vcpu->arch.hpte_cache[i];
+		if (!pte->host_va)
+			continue;
+
+		if (pte->pte.vpage == guest_vp)
+			return &pte->pte;
+	}
+
+	return NULL;
+}
+
+static int kvmppc_mmu_hpte_cache_next(struct kvm_vcpu *vcpu)
+{
+	if (vcpu->arch.hpte_cache_offset == HPTEG_CACHE_NUM)
+		kvmppc_mmu_pte_flush(vcpu, 0, 0);
+
+	return vcpu->arch.hpte_cache_offset++;
+}
+
+/* We keep 512 gvsid->hvsid entries, mapping the guest ones to the array using
+ * a hash, so we don't waste cycles on looping */
+static u16 kvmppc_sid_hash(struct kvm_vcpu *vcpu, u64 gvsid)
+{
+	return (u16)(((gvsid >> (SID_MAP_BITS * 7)) & SID_MAP_MASK) ^
+		     ((gvsid >> (SID_MAP_BITS * 6)) & SID_MAP_MASK) ^
+		     ((gvsid >> (SID_MAP_BITS * 5)) & SID_MAP_MASK) ^
+		     ((gvsid >> (SID_MAP_BITS * 4)) & SID_MAP_MASK) ^
+		     ((gvsid >> (SID_MAP_BITS * 3)) & SID_MAP_MASK) ^
+		     ((gvsid >> (SID_MAP_BITS * 2)) & SID_MAP_MASK) ^
+		     ((gvsid >> (SID_MAP_BITS * 1)) & SID_MAP_MASK) ^
+		     ((gvsid >> (SID_MAP_BITS * 0)) & SID_MAP_MASK));
+}
+
+
+static struct kvmppc_sid_map *find_sid_vsid(struct kvm_vcpu *vcpu, u64 gvsid)
+{
+	struct kvmppc_sid_map *map;
+	u16 sid_map_mask;
+
+	if (vcpu->arch.msr & MSR_PR)
+		gvsid |= VSID_PR;
+
+	sid_map_mask = kvmppc_sid_hash(vcpu, gvsid);
+	map = &to_book3s(vcpu)->sid_map[sid_map_mask];
+	if (map->guest_vsid == gvsid) {
+		dprintk_slb("SLB: Searching 0x%llx -> 0x%llx\n",
+			    gvsid, map->host_vsid);
+		return map;
+	}
+
+	map = &to_book3s(vcpu)->sid_map[SID_MAP_MASK - sid_map_mask];
+	if (map->guest_vsid == gvsid) {
+		dprintk_slb("SLB: Searching 0x%llx -> 0x%llx\n",
+			    gvsid, map->host_vsid);
+		return map;
+	}
+
+	dprintk_slb("SLB: Searching 0x%llx -> not found\n", gvsid);
+	return NULL;
+}
+
+int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte)
+{
+	pfn_t hpaddr;
+	ulong hash, hpteg, va;
+	u64 vsid;
+	int ret;
+	int rflags = 0x192;
+	int vflags = 0;
+	int attempt = 0;
+	struct kvmppc_sid_map *map;
+
+	/* Get host physical address for gpa */
+	hpaddr = gfn_to_pfn(vcpu->kvm, orig_pte->raddr >> PAGE_SHIFT);
+	if (kvm_is_error_hva(hpaddr)) {
+		printk(KERN_INFO "Couldn't get guest page for gfn %llx!\n", orig_pte->eaddr);
+		return -EINVAL;
+	}
+	hpaddr <<= PAGE_SHIFT;
+#if PAGE_SHIFT == 12
+#elif PAGE_SHIFT == 16
+	hpaddr |= orig_pte->raddr & 0xf000;
+#else
+#error Unknown page size
+#endif
+
+	/* and write the mapping ea -> hpa into the pt */
+	vcpu->arch.mmu.esid_to_vsid(vcpu, orig_pte->eaddr >> SID_SHIFT, &vsid);
+	map = find_sid_vsid(vcpu, vsid);
+	if (!map) {
+		kvmppc_mmu_map_segment(vcpu, orig_pte->eaddr);
+		map = find_sid_vsid(vcpu, vsid);
+	}
+	BUG_ON(!map);
+
+	vsid = map->host_vsid;
+	va = hpt_va(orig_pte->eaddr, vsid, MMU_SEGSIZE_256M);
+
+	if (!orig_pte->may_write)
+		rflags |= HPTE_R_PP;
+	else
+		mark_page_dirty(vcpu->kvm, orig_pte->raddr >> PAGE_SHIFT);
+
+	if (!orig_pte->may_execute)
+		rflags |= HPTE_R_N;
+
+	hash = hpt_hash(va, PTE_SIZE, MMU_SEGSIZE_256M);
+
+map_again:
+	hpteg = ((hash & htab_hash_mask) * HPTES_PER_GROUP);
+
+	/* In case we tried normal mapping already, let's nuke old entries */
+	if (attempt > 1)
+		if (ppc_md.hpte_remove(hpteg) < 0)
+			return -1;
+
+	ret = ppc_md.hpte_insert(hpteg, va, hpaddr, rflags, vflags, MMU_PAGE_4K, MMU_SEGSIZE_256M);
+
+	if (ret < 0) {
+		/* If we couldn't map a primary PTE, try a secondary */
+#ifdef USE_SECONDARY
+		hash = ~hash;
+		attempt++;
+		if (attempt % 2)
+			vflags = HPTE_V_SECONDARY;
+		else
+			vflags = 0;
+#else
+		attempt = 2;
+#endif
+		goto map_again;
+	} else {
+		int hpte_id = kvmppc_mmu_hpte_cache_next(vcpu);
+		struct hpte_cache *pte = &vcpu->arch.hpte_cache[hpte_id];
+
+		dprintk_mmu("KVM: %c%c Map 0x%llx: [%lx] 0x%lx (0x%llx) -> %lx\n",
+			    ((rflags & HPTE_R_PP) == 3) ? '-' : 'w',
+			    (rflags & HPTE_R_N) ? '-' : 'x',
+			    orig_pte->eaddr, hpteg, va, orig_pte->vpage, hpaddr);
+
+		pte->slot = hpteg + (ret & 7);
+		pte->host_va = va;
+		pte->pte = *orig_pte;
+		pte->pfn = hpaddr >> PAGE_SHIFT;
+	}
+
+	return 0;
+}
+
+static struct kvmppc_sid_map *create_sid_map(struct kvm_vcpu *vcpu, u64 gvsid)
+{
+	struct kvmppc_sid_map *map;
+	struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
+	u16 sid_map_mask;
+	static int backwards_map = 0;
+
+	if (vcpu->arch.msr & MSR_PR)
+		gvsid |= VSID_PR;
+
+	/* We might get collisions that trap in preceding order, so let's
+	   map them differently */
+
+	sid_map_mask = kvmppc_sid_hash(vcpu, gvsid);
+	if (backwards_map)
+		sid_map_mask = SID_MAP_MASK - sid_map_mask;
+
+	map = &to_book3s(vcpu)->sid_map[sid_map_mask];
+
+	/* Make sure we're taking the other map next time */
+	backwards_map = !backwards_map;
+
+	/* Uh-oh ... out of mappings. Let's flush! */
+	if (vcpu_book3s->vsid_next == vcpu_book3s->vsid_max) {
+		vcpu_book3s->vsid_next = vcpu_book3s->vsid_first;
+		memset(vcpu_book3s->sid_map, 0,
+		       sizeof(struct kvmppc_sid_map) * SID_MAP_NUM);
+		kvmppc_mmu_pte_flush(vcpu, 0, 0);
+		kvmppc_mmu_flush_segments(vcpu);
+	}
+	map->host_vsid = vcpu_book3s->vsid_next++;
+
+	map->guest_vsid = gvsid;
+	map->valid = true;
+
+	return map;
+}
+
+static int kvmppc_mmu_next_segment(struct kvm_vcpu *vcpu, ulong esid)
+{
+	int i;
+	int max_slb_size = 64;
+	int found_inval = -1;
+	int r;
+
+	if (!get_paca()->kvm_slb_max)
+		get_paca()->kvm_slb_max = 1;
+
+	/* Are we overwriting? */
+	for (i = 1; i < get_paca()->kvm_slb_max; i++) {
+		if (!(get_paca()->kvm_slb[i].esid & SLB_ESID_V))
+			found_inval = i;
+		else if ((get_paca()->kvm_slb[i].esid & ESID_MASK) == esid)
+			return i;
+	}
+
+	/* Found a spare entry that was invalidated before */
+	if (found_inval > 0)
+		return found_inval;
+
+	/* No spare invalid entry, so create one */
+
+	if (mmu_slb_size < 64)
+		max_slb_size = mmu_slb_size;
+
+	/* Overflowing -> purge */
+	if ((get_paca()->kvm_slb_max) == max_slb_size)
+		kvmppc_mmu_flush_segments(vcpu);
+
+	r = get_paca()->kvm_slb_max;
+	get_paca()->kvm_slb_max++;
+
+	return r;
+}
+
+int kvmppc_mmu_map_segment(struct kvm_vcpu *vcpu, ulong eaddr)
+{
+	u64 esid = eaddr >> SID_SHIFT;
+	u64 slb_esid = (eaddr & ESID_MASK) | SLB_ESID_V;
+	u64 slb_vsid = SLB_VSID_USER;
+	u64 gvsid;
+	int slb_index;
+	struct kvmppc_sid_map *map;
+
+	slb_index = kvmppc_mmu_next_segment(vcpu, eaddr & ESID_MASK);
+
+	if (vcpu->arch.mmu.esid_to_vsid(vcpu, esid, &gvsid)) {
+		/* Invalidate an entry */
+		get_paca()->kvm_slb[slb_index].esid = 0;
+		return -ENOENT;
+	}
+
+	map = find_sid_vsid(vcpu, gvsid);
+	if (!map)
+		map = create_sid_map(vcpu, gvsid);
+
+	map->guest_esid = esid;
+
+	slb_vsid |= (map->host_vsid << 12);
+	slb_vsid &= ~SLB_VSID_KP;
+	slb_esid |= slb_index;
+
+	get_paca()->kvm_slb[slb_index].esid = slb_esid;
+	get_paca()->kvm_slb[slb_index].vsid = slb_vsid;
+
+	dprintk_slb("slbmte %#llx, %#llx\n", slb_vsid, slb_esid);
+
+	return 0;
+}
+
+void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu)
+{
+	get_paca()->kvm_slb_max = 1;
+	get_paca()->kvm_slb[0].esid = 0;
+}
+
+void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
+{
+	kvmppc_mmu_pte_flush(vcpu, 0, 0);
+}
-- 
1.6.0.2

  parent reply	other threads:[~2009-10-30 15:47 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-10-30 15:47 [PATCH 00/27] Add KVM support for Book3s_64 (PPC64) hosts v6 Alexander Graf
2009-10-30 15:47 ` [PATCH 01/27] Move dirty logging code to sub-arch Alexander Graf
2009-10-30 15:47 ` [PATCH 02/27] Pass PVR in sregs Alexander Graf
2009-10-30 15:47 ` [PATCH 03/27] Add Book3s definitions Alexander Graf
2009-10-30 15:47 ` [PATCH 04/27] Add Book3s fields to vcpu structs Alexander Graf
2009-10-30 15:47 ` [PATCH 05/27] Add asm/kvm_book3s.h Alexander Graf
2009-10-30 15:47 ` [PATCH 06/27] Add Book3s_64 intercept helpers Alexander Graf
2009-10-30 15:47 ` [PATCH 07/27] Add book3s_64 highmem asm code Alexander Graf
2009-10-30 15:47 ` [PATCH 08/27] Add SLB switching code for entry/exit Alexander Graf
2009-11-01 23:23   ` Michael Neuling
2009-11-02  9:23     ` Alexander Graf
2009-11-02  9:39       ` Michael Neuling
2009-11-02  9:59         ` Alexander Graf
2009-10-30 15:47 ` [PATCH 09/27] Add interrupt handling code Alexander Graf
2009-10-30 15:47 ` [PATCH 10/27] Add book3s.c Alexander Graf
2009-10-30 15:47 ` Alexander Graf [this message]
2009-11-01 23:39   ` [PATCH 11/27] Add book3s_64 Host MMU handling Michael Neuling
2009-11-02  9:26     ` Alexander Graf
2009-10-30 15:47 ` [PATCH 12/27] Add book3s_64 guest MMU Alexander Graf
2009-10-30 15:47 ` [PATCH 13/27] Add book3s_32 " Alexander Graf
2009-10-30 15:47 ` [PATCH 14/27] Add book3s_64 specific opcode emulation Alexander Graf
2009-11-03  8:47   ` Segher Boessenkool
2009-11-03  9:06     ` Alexander Graf
2009-11-03 21:38       ` Benjamin Herrenschmidt
2009-11-04  8:43         ` Arnd Bergmann
2009-11-04  8:47           ` Benjamin Herrenschmidt
2009-11-04 11:35             ` Alexander Graf
2009-11-05  0:53       ` Segher Boessenkool
2009-11-05 10:09         ` Alexander Graf
2009-10-30 15:47 ` [PATCH 15/27] Add mfdec emulation Alexander Graf
2009-10-30 15:47 ` [PATCH 16/27] Add desktop PowerPC specific emulation Alexander Graf
2009-10-30 15:47 ` [PATCH 17/27] Make head_64.S aware of KVM real mode code Alexander Graf
2009-10-30 15:47 ` [PATCH 18/27] Add Book3s_64 offsets to asm-offsets.c Alexander Graf
2009-10-30 15:47 ` [PATCH 19/27] Export symbols for KVM module Alexander Graf
2009-10-31  4:37   ` Stephen Rothwell
2009-10-31 12:02     ` Alexander Graf
2009-10-30 15:47 ` [PATCH 20/27] Split init_new_context and destroy_context Alexander Graf
2009-10-31  4:40   ` Stephen Rothwell
2009-10-31 21:20     ` Alexander Graf
2009-10-31 21:37       ` Benjamin Herrenschmidt
2009-10-30 15:47 ` [PATCH 21/27] Export KVM symbols for module Alexander Graf
2009-10-30 15:47 ` [PATCH 22/27] Add fields to PACA Alexander Graf
2009-10-30 15:47 ` [PATCH 23/27] Export new PACA constants in asm-offsets Alexander Graf
2009-10-30 15:47 ` [PATCH 24/27] Include Book3s_64 target in buildsystem Alexander Graf
2009-10-30 15:47 ` [PATCH 25/27] Fix trace.h Alexander Graf
2009-10-30 15:47 ` [PATCH 26/27] Use Little Endian for Dirty Bitmap Alexander Graf
2009-10-30 15:47 ` [PATCH 27/27] Use hrtimers for the decrementer Alexander Graf
2009-11-05  6:03 ` [PATCH 00/27] Add KVM support for Book3s_64 (PPC64) hosts v6 Benjamin Herrenschmidt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1256917647-6200-12-git-send-email-agraf@suse.de \
    --to=agraf@suse.de \
    --cc=arnd@arndb.de \
    --cc=avi@redhat.com \
    --cc=bphilips@suse.de \
    --cc=hollisb@us.ibm.com \
    --cc=kvm-ppc@vger.kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=kwolf@redhat.com \
    --cc=linuxppc-dev@ozlabs.org \
    --cc=mtosatti@redhat.com \
    --cc=olof@lixom.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).