book3s_32_mmu_host.c 10.0 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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
/*
 * Copyright (C) 2010 SUSE Linux Products GmbH. All rights reserved.
 *
 * Authors:
 *     Alexander Graf <agraf@suse.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-hash32.h>
#include <asm/machdep.h>
#include <asm/mmu_context.h>
#include <asm/hw_irq.h>

/* #define DEBUG_MMU */
/* #define DEBUG_SR */

#ifdef DEBUG_MMU
#define dprintk_mmu(a, ...) printk(KERN_INFO a, __VA_ARGS__)
#else
#define dprintk_mmu(a, ...) do { } while(0)
#endif

#ifdef DEBUG_SR
#define dprintk_sr(a, ...) printk(KERN_INFO a, __VA_ARGS__)
#else
#define dprintk_sr(a, ...) do { } while(0)
#endif

#if PAGE_SHIFT != 12
#error Unknown page size
#endif

#ifdef CONFIG_SMP
#error XXX need to grab mmu_hash_lock
#endif

#ifdef CONFIG_PTE_64BIT
#error Only 32 bit pages are supported for now
#endif

A
Alexander Graf 已提交
57 58 59
static ulong htab;
static u32 htabmask;

60
void kvmppc_mmu_invalidate_pte(struct kvm_vcpu *vcpu, struct hpte_cache *pte)
A
Alexander Graf 已提交
61 62 63
{
	volatile u32 *pteg;

64
	/* Remove from host HTAB */
A
Alexander Graf 已提交
65 66
	pteg = (u32*)pte->slot;
	pteg[0] = 0;
67 68

	/* And make sure it's gone from the TLB too */
A
Alexander Graf 已提交
69 70 71 72 73 74 75 76 77 78
	asm volatile ("sync");
	asm volatile ("tlbie %0" : : "r" (pte->pte.eaddr) : "memory");
	asm volatile ("sync");
	asm volatile ("tlbsync");
}

/* 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)
{
79 80 81 82 83 84 85 86
	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));
A
Alexander Graf 已提交
87 88 89 90 91 92 93 94
}


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

95
	if (vcpu->arch.shared->msr & MSR_PR)
A
Alexander Graf 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
		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_sr("SR: 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_sr("SR: Searching 0x%llx -> 0x%llx\n",
			    gvsid, map->host_vsid);
		return map;
	}

	dprintk_sr("SR: Searching 0x%llx -> not found\n", gvsid);
	return NULL;
}

static u32 *kvmppc_mmu_get_pteg(struct kvm_vcpu *vcpu, u32 vsid, u32 eaddr,
				bool primary)
{
A
Alexander Graf 已提交
120 121
	u32 page, hash;
	ulong pteg = htab;
A
Alexander Graf 已提交
122 123 124 125 126 127 128 129 130 131 132

	page = (eaddr & ~ESID_MASK) >> 12;

	hash = ((vsid ^ page) << 6);
	if (!primary)
		hash = ~hash;

	hash &= htabmask;

	pteg |= hash;

A
Alexander Graf 已提交
133 134
	dprintk_mmu("htab: %lx | hash: %x | htabmask: %x | pteg: %lx\n",
		htab, hash, htabmask, pteg);
A
Alexander Graf 已提交
135 136 137 138 139 140

	return (u32*)pteg;
}

extern char etext[];

141 142
int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte,
			bool iswrite)
A
Alexander Graf 已提交
143 144
{
	pfn_t hpaddr;
145
	u64 vpn;
A
Alexander Graf 已提交
146 147 148 149 150 151 152 153 154
	u64 vsid;
	struct kvmppc_sid_map *map;
	volatile u32 *pteg;
	u32 eaddr = orig_pte->eaddr;
	u32 pteg0, pteg1;
	register int rr = 0;
	bool primary = false;
	bool evict = false;
	struct hpte_cache *pte;
155
	int r = 0;
156
	bool writable;
A
Alexander Graf 已提交
157 158

	/* Get host physical address for gpa */
159 160
	hpaddr = kvmppc_gfn_to_pfn(vcpu, orig_pte->raddr >> PAGE_SHIFT,
				   iswrite, &writable);
161
	if (is_error_noslot_pfn(hpaddr)) {
A
Alexander Graf 已提交
162
		printk(KERN_INFO "Couldn't get guest page for gfn %lx!\n",
A
Alexander Graf 已提交
163
				 orig_pte->eaddr);
164 165
		r = -EINVAL;
		goto out;
A
Alexander Graf 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178
	}
	hpaddr <<= PAGE_SHIFT;

	/* 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, eaddr);
		map = find_sid_vsid(vcpu, vsid);
	}
	BUG_ON(!map);

	vsid = map->host_vsid;
179 180
	vpn = (vsid << (SID_SHIFT - VPN_SHIFT)) |
		((eaddr & ~ESID_MASK) >> VPN_SHIFT);
A
Alexander Graf 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
next_pteg:
	if (rr == 16) {
		primary = !primary;
		evict = true;
		rr = 0;
	}

	pteg = kvmppc_mmu_get_pteg(vcpu, vsid, eaddr, primary);

	/* not evicting yet */
	if (!evict && (pteg[rr] & PTE_V)) {
		rr += 2;
		goto next_pteg;
	}

	dprintk_mmu("KVM: old PTEG: %p (%d)\n", pteg, rr);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[0], pteg[1]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[2], pteg[3]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[4], pteg[5]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[6], pteg[7]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[8], pteg[9]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[10], pteg[11]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[12], pteg[13]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[14], pteg[15]);

	pteg0 = ((eaddr & 0x0fffffff) >> 22) | (vsid << 7) | PTE_V |
		(primary ? 0 : PTE_SEC);
	pteg1 = hpaddr | PTE_M | PTE_R | PTE_C;

210
	if (orig_pte->may_write && writable) {
A
Alexander Graf 已提交
211 212 213 214 215 216
		pteg1 |= PP_RWRW;
		mark_page_dirty(vcpu->kvm, orig_pte->raddr >> PAGE_SHIFT);
	} else {
		pteg1 |= PP_RWRX;
	}

217 218 219
	if (orig_pte->may_execute)
		kvmppc_mmu_flush_icache(hpaddr >> PAGE_SHIFT);

A
Alexander Graf 已提交
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
	local_irq_disable();

	if (pteg[rr]) {
		pteg[rr] = 0;
		asm volatile ("sync");
	}
	pteg[rr + 1] = pteg1;
	pteg[rr] = pteg0;
	asm volatile ("sync");

	local_irq_enable();

	dprintk_mmu("KVM: new PTEG: %p\n", pteg);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[0], pteg[1]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[2], pteg[3]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[4], pteg[5]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[6], pteg[7]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[8], pteg[9]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[10], pteg[11]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[12], pteg[13]);
	dprintk_mmu("KVM:   %08x - %08x\n", pteg[14], pteg[15]);


	/* Now tell our Shadow PTE code about the new page */

245
	pte = kvmppc_mmu_hpte_cache_next(vcpu);
A
Alexander Graf 已提交
246 247 248 249

	dprintk_mmu("KVM: %c%c Map 0x%llx: [%lx] 0x%llx (0x%llx) -> %lx\n",
		    orig_pte->may_write ? 'w' : '-',
		    orig_pte->may_execute ? 'x' : '-',
250
		    orig_pte->eaddr, (ulong)pteg, vpn,
A
Alexander Graf 已提交
251 252 253
		    orig_pte->vpage, hpaddr);

	pte->slot = (ulong)&pteg[rr];
254
	pte->host_vpn = vpn;
A
Alexander Graf 已提交
255 256 257
	pte->pte = *orig_pte;
	pte->pfn = hpaddr >> PAGE_SHIFT;

258 259
	kvmppc_mmu_hpte_cache_map(vcpu, pte);

260
	kvm_release_pfn_clean(hpaddr >> PAGE_SHIFT);
261 262
out:
	return r;
A
Alexander Graf 已提交
263 264
}

265 266 267 268 269
void kvmppc_mmu_unmap_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte)
{
	kvmppc_mmu_pte_vflush(vcpu, pte->vpage, 0xfffffffffULL);
}

A
Alexander Graf 已提交
270 271 272 273 274 275 276
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;

277
	if (vcpu->arch.shared->msr & MSR_PR)
A
Alexander Graf 已提交
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
		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! */
293 294
	if (vcpu_book3s->vsid_next >= VSID_POOL_SIZE) {
		vcpu_book3s->vsid_next = 0;
A
Alexander Graf 已提交
295 296 297 298 299
		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);
	}
300 301
	map->host_vsid = vcpu_book3s->vsid_pool[vcpu_book3s->vsid_next];
	vcpu_book3s->vsid_next++;
A
Alexander Graf 已提交
302 303 304 305 306 307 308 309 310 311 312 313 314

	map->guest_vsid = gvsid;
	map->valid = true;

	return map;
}

int kvmppc_mmu_map_segment(struct kvm_vcpu *vcpu, ulong eaddr)
{
	u32 esid = eaddr >> SID_SHIFT;
	u64 gvsid;
	u32 sr;
	struct kvmppc_sid_map *map;
315 316
	struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
	int r = 0;
A
Alexander Graf 已提交
317 318 319 320

	if (vcpu->arch.mmu.esid_to_vsid(vcpu, esid, &gvsid)) {
		/* Invalidate an entry */
		svcpu->sr[esid] = SR_INVALID;
321 322
		r = -ENOENT;
		goto out;
A
Alexander Graf 已提交
323 324 325 326 327 328 329 330 331 332 333 334
	}

	map = find_sid_vsid(vcpu, gvsid);
	if (!map)
		map = create_sid_map(vcpu, gvsid);

	map->guest_esid = esid;
	sr = map->host_vsid | SR_KP;
	svcpu->sr[esid] = sr;

	dprintk_sr("MMU: mtsr %d, 0x%x\n", esid, sr);

335 336 337
out:
	svcpu_put(svcpu);
	return r;
A
Alexander Graf 已提交
338 339 340 341 342
}

void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu)
{
	int i;
343
	struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu);
A
Alexander Graf 已提交
344 345 346 347

	dprintk_sr("MMU: flushing all segments (%d)\n", ARRAY_SIZE(svcpu->sr));
	for (i = 0; i < ARRAY_SIZE(svcpu->sr); i++)
		svcpu->sr[i] = SR_INVALID;
348 349

	svcpu_put(svcpu);
A
Alexander Graf 已提交
350 351
}

352
void kvmppc_mmu_destroy_pr(struct kvm_vcpu *vcpu)
A
Alexander Graf 已提交
353
{
354 355
	int i;

356
	kvmppc_mmu_hpte_destroy(vcpu);
A
Alexander Graf 已提交
357
	preempt_disable();
358 359
	for (i = 0; i < SID_CONTEXTS; i++)
		__destroy_context(to_book3s(vcpu)->context_id[i]);
A
Alexander Graf 已提交
360 361 362 363
	preempt_enable();
}

/* From mm/mmu_context_hash32.c */
364
#define CTX_TO_VSID(c, id)	((((c) * (897 * 16)) + (id * 0x111)) & 0xffffff)
A
Alexander Graf 已提交
365 366 367 368 369

int kvmppc_mmu_init(struct kvm_vcpu *vcpu)
{
	struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
	int err;
A
Alexander Graf 已提交
370
	ulong sdr1;
371 372
	int i;
	int j;
A
Alexander Graf 已提交
373

374 375 376 377 378
	for (i = 0; i < SID_CONTEXTS; i++) {
		err = __init_new_context();
		if (err < 0)
			goto init_fail;
		vcpu3s->context_id[i] = err;
A
Alexander Graf 已提交
379

380 381 382 383
		/* Remember context id for this combination */
		for (j = 0; j < 16; j++)
			vcpu3s->vsid_pool[(i * 16) + j] = CTX_TO_VSID(err, j);
	}
A
Alexander Graf 已提交
384

385
	vcpu3s->vsid_next = 0;
A
Alexander Graf 已提交
386

A
Alexander Graf 已提交
387 388 389 390 391
	/* Remember where the HTAB is */
	asm ( "mfsdr1 %0" : "=r"(sdr1) );
	htabmask = ((sdr1 & 0x1FF) << 16) | 0xFFC0;
	htab = (ulong)__va(sdr1 & 0xffff0000);

392 393
	kvmppc_mmu_hpte_init(vcpu);

A
Alexander Graf 已提交
394
	return 0;
395 396 397 398 399 400 401 402 403 404

init_fail:
	for (j = 0; j < i; j++) {
		if (!vcpu3s->context_id[j])
			continue;

		__destroy_context(to_book3s(vcpu)->context_id[j]);
	}

	return -1;
A
Alexander Graf 已提交
405
}