book3s_64_mmu_host.c 7.7 KB
Newer Older
A
Alexander Graf 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * 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>
23
#include <linux/hash.h>
A
Alexander Graf 已提交
24 25 26 27 28 29 30

#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>
31
#include "trace.h"
A
Alexander Graf 已提交
32 33 34 35

#define PTE_SIZE 12
#define VSID_ALL 0

36
void kvmppc_mmu_invalidate_pte(struct kvm_vcpu *vcpu, struct hpte_cache *pte)
A
Alexander Graf 已提交
37 38 39 40 41 42 43 44 45 46
{
	ppc_md.hpte_invalidate(pte->slot, pte->host_va,
			       MMU_PAGE_4K, MMU_SEGSIZE_256M,
			       false);
}

/* 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)
{
47
	return hash_64(gvsid, SID_MAP_BITS);
A
Alexander Graf 已提交
48 49 50 51 52 53 54
}

static struct kvmppc_sid_map *find_sid_vsid(struct kvm_vcpu *vcpu, u64 gvsid)
{
	struct kvmppc_sid_map *map;
	u16 sid_map_mask;

55
	if (vcpu->arch.shared->msr & MSR_PR)
A
Alexander Graf 已提交
56 57 58 59
		gvsid |= VSID_PR;

	sid_map_mask = kvmppc_sid_hash(vcpu, gvsid);
	map = &to_book3s(vcpu)->sid_map[sid_map_mask];
60
	if (map->valid && (map->guest_vsid == gvsid)) {
61
		trace_kvm_book3s_slb_found(gvsid, map->host_vsid);
A
Alexander Graf 已提交
62 63 64 65
		return map;
	}

	map = &to_book3s(vcpu)->sid_map[SID_MAP_MASK - sid_map_mask];
66
	if (map->valid && (map->guest_vsid == gvsid)) {
67
		trace_kvm_book3s_slb_found(gvsid, map->host_vsid);
A
Alexander Graf 已提交
68 69 70
		return map;
	}

71
	trace_kvm_book3s_slb_fail(sid_map_mask, gvsid);
A
Alexander Graf 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
	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 */
87
	hpaddr = kvmppc_gfn_to_pfn(vcpu, orig_pte->raddr >> PAGE_SHIFT);
88
	if (is_error_pfn(hpaddr)) {
A
Alexander Graf 已提交
89
		printk(KERN_INFO "Couldn't get guest page for gfn %lx!\n", orig_pte->eaddr);
A
Alexander Graf 已提交
90 91 92
		return -EINVAL;
	}
	hpaddr <<= PAGE_SHIFT;
93
	hpaddr |= orig_pte->raddr & (~0xfffULL & ~PAGE_MASK);
A
Alexander Graf 已提交
94 95 96 97 98

	/* 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) {
99 100
		ret = kvmppc_mmu_map_segment(vcpu, orig_pte->eaddr);
		WARN_ON(ret < 0);
A
Alexander Graf 已提交
101 102
		map = find_sid_vsid(vcpu, vsid);
	}
103 104 105 106 107 108
	if (!map) {
		printk(KERN_ERR "KVM: Segment map for 0x%llx (0x%lx) failed\n",
				vsid, orig_pte->eaddr);
		WARN_ON(true);
		return -EINVAL;
	}
A
Alexander Graf 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135

	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 */
		hash = ~hash;
136
		vflags ^= HPTE_V_SECONDARY;
A
Alexander Graf 已提交
137 138 139
		attempt++;
		goto map_again;
	} else {
140
		struct hpte_cache *pte = kvmppc_mmu_hpte_cache_next(vcpu);
A
Alexander Graf 已提交
141

142
		trace_kvm_book3s_64_mmu_map(rflags, hpteg, va, hpaddr, orig_pte);
A
Alexander Graf 已提交
143

144 145 146 147 148 149 150
		/* The ppc_md code may give us a secondary entry even though we
		   asked for a primary. Fix up. */
		if ((ret & _PTEIDX_SECONDARY) && !(vflags & HPTE_V_SECONDARY)) {
			hash = ~hash;
			hpteg = ((hash & htab_hash_mask) * HPTES_PER_GROUP);
		}

A
Alexander Graf 已提交
151 152 153 154
		pte->slot = hpteg + (ret & 7);
		pte->host_va = va;
		pte->pte = *orig_pte;
		pte->pfn = hpaddr >> PAGE_SHIFT;
155 156

		kvmppc_mmu_hpte_cache_map(vcpu, pte);
A
Alexander Graf 已提交
157 158 159 160 161 162 163 164 165 166 167 168
	}

	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;

169
	if (vcpu->arch.shared->msr & MSR_PR)
A
Alexander Graf 已提交
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
		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;

197
	trace_kvm_book3s_slb_map(sid_map_mask, gvsid, map->host_vsid);
198

A
Alexander Graf 已提交
199 200 201 202 203 204 205 206 207 208
	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;

209 210
	if (!to_svcpu(vcpu)->slb_max)
		to_svcpu(vcpu)->slb_max = 1;
A
Alexander Graf 已提交
211 212

	/* Are we overwriting? */
213 214
	for (i = 1; i < to_svcpu(vcpu)->slb_max; i++) {
		if (!(to_svcpu(vcpu)->slb[i].esid & SLB_ESID_V))
A
Alexander Graf 已提交
215
			found_inval = i;
216
		else if ((to_svcpu(vcpu)->slb[i].esid & ESID_MASK) == esid)
A
Alexander Graf 已提交
217 218 219 220 221 222 223 224 225 226 227 228 229
			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 */
230
	if ((to_svcpu(vcpu)->slb_max) == max_slb_size)
A
Alexander Graf 已提交
231 232
		kvmppc_mmu_flush_segments(vcpu);

233 234
	r = to_svcpu(vcpu)->slb_max;
	to_svcpu(vcpu)->slb_max++;
A
Alexander Graf 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

	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 */
252
		to_svcpu(vcpu)->slb[slb_index].esid = 0;
A
Alexander Graf 已提交
253 254 255 256 257 258 259 260 261 262 263 264 265
		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;

266 267
	to_svcpu(vcpu)->slb[slb_index].esid = slb_esid;
	to_svcpu(vcpu)->slb[slb_index].vsid = slb_vsid;
A
Alexander Graf 已提交
268

269
	trace_kvm_book3s_slbmte(slb_vsid, slb_esid);
A
Alexander Graf 已提交
270 271 272 273 274 275

	return 0;
}

void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu)
{
276 277
	to_svcpu(vcpu)->slb_max = 1;
	to_svcpu(vcpu)->slb[0].esid = 0;
A
Alexander Graf 已提交
278 279 280 281
}

void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
{
282
	kvmppc_mmu_hpte_destroy(vcpu);
A
Alexander Graf 已提交
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
	__destroy_context(to_book3s(vcpu)->context_id);
}

int kvmppc_mmu_init(struct kvm_vcpu *vcpu)
{
	struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
	int err;

	err = __init_new_context();
	if (err < 0)
		return -1;
	vcpu3s->context_id = err;

	vcpu3s->vsid_max = ((vcpu3s->context_id + 1) << USER_ESID_BITS) - 1;
	vcpu3s->vsid_first = vcpu3s->context_id << USER_ESID_BITS;
	vcpu3s->vsid_next = vcpu3s->vsid_first;

300 301
	kvmppc_mmu_hpte_init(vcpu);

A
Alexander Graf 已提交
302
	return 0;
A
Alexander Graf 已提交
303
}