Blame view
arch/powerpc/kvm/book3s_64_mmu_host.c
8.05 KB
0d8dc681c
|
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 |
/* * 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> |
82fdee7bc
|
30 |
#include "trace.h" |
0d8dc681c
|
31 32 |
#define PTE_SIZE 12 |
0d8dc681c
|
33 |
|
fef093bec
|
34 |
void kvmppc_mmu_invalidate_pte(struct kvm_vcpu *vcpu, struct hpte_cache *pte) |
0d8dc681c
|
35 |
{ |
0d8dc681c
|
36 37 38 |
ppc_md.hpte_invalidate(pte->slot, pte->host_va, MMU_PAGE_4K, MMU_SEGSIZE_256M, false); |
0d8dc681c
|
39 40 41 42 43 44 |
} /* 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) { |
b9877ce29
|
45 46 47 48 49 50 51 52 |
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)); |
0d8dc681c
|
53 |
} |
b9877ce29
|
54 |
|
0d8dc681c
|
55 56 57 58 |
static struct kvmppc_sid_map *find_sid_vsid(struct kvm_vcpu *vcpu, u64 gvsid) { struct kvmppc_sid_map *map; u16 sid_map_mask; |
666e7252a
|
59 |
if (vcpu->arch.shared->msr & MSR_PR) |
0d8dc681c
|
60 61 62 63 |
gvsid |= VSID_PR; sid_map_mask = kvmppc_sid_hash(vcpu, gvsid); map = &to_book3s(vcpu)->sid_map[sid_map_mask]; |
c22c31963
|
64 |
if (map->valid && (map->guest_vsid == gvsid)) { |
928d78be5
|
65 |
trace_kvm_book3s_slb_found(gvsid, map->host_vsid); |
0d8dc681c
|
66 67 68 69 |
return map; } map = &to_book3s(vcpu)->sid_map[SID_MAP_MASK - sid_map_mask]; |
c22c31963
|
70 |
if (map->valid && (map->guest_vsid == gvsid)) { |
928d78be5
|
71 |
trace_kvm_book3s_slb_found(gvsid, map->host_vsid); |
0d8dc681c
|
72 73 |
return map; } |
928d78be5
|
74 |
trace_kvm_book3s_slb_fail(sid_map_mask, gvsid); |
0d8dc681c
|
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
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 */ |
e8508940a
|
90 |
hpaddr = kvmppc_gfn_to_pfn(vcpu, orig_pte->raddr >> PAGE_SHIFT); |
49451389e
|
91 |
if (is_error_pfn(hpaddr)) { |
af7b4d104
|
92 93 |
printk(KERN_INFO "Couldn't get guest page for gfn %lx! ", orig_pte->eaddr); |
0d8dc681c
|
94 95 96 |
return -EINVAL; } hpaddr <<= PAGE_SHIFT; |
e8508940a
|
97 |
hpaddr |= orig_pte->raddr & (~0xfffULL & ~PAGE_MASK); |
0d8dc681c
|
98 99 100 101 102 |
/* 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) { |
ac2146718
|
103 104 |
ret = kvmppc_mmu_map_segment(vcpu, orig_pte->eaddr); WARN_ON(ret < 0); |
0d8dc681c
|
105 106 |
map = find_sid_vsid(vcpu, vsid); } |
ac2146718
|
107 108 109 110 111 112 113 |
if (!map) { printk(KERN_ERR "KVM: Segment map for 0x%llx (0x%lx) failed ", vsid, orig_pte->eaddr); WARN_ON(true); return -EINVAL; } |
0d8dc681c
|
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
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 */ |
0d8dc681c
|
140 |
hash = ~hash; |
20a340abd
|
141 |
vflags ^= HPTE_V_SECONDARY; |
0d8dc681c
|
142 |
attempt++; |
0d8dc681c
|
143 144 |
goto map_again; } else { |
fef093bec
|
145 |
struct hpte_cache *pte = kvmppc_mmu_hpte_cache_next(vcpu); |
0d8dc681c
|
146 |
|
82fdee7bc
|
147 |
trace_kvm_book3s_64_mmu_map(rflags, hpteg, va, hpaddr, orig_pte); |
0d8dc681c
|
148 |
|
a1eda280c
|
149 150 151 152 153 154 |
/* 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); } |
0d8dc681c
|
155 156 157 158 |
pte->slot = hpteg + (ret & 7); pte->host_va = va; pte->pte = *orig_pte; pte->pfn = hpaddr >> PAGE_SHIFT; |
fef093bec
|
159 160 |
kvmppc_mmu_hpte_cache_map(vcpu, pte); |
0d8dc681c
|
161 162 163 164 165 166 167 168 169 170 171 |
} 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; |
666e7252a
|
172 |
if (vcpu->arch.shared->msr & MSR_PR) |
0d8dc681c
|
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
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; |
928d78be5
|
199 |
trace_kvm_book3s_slb_map(sid_map_mask, gvsid, map->host_vsid); |
5156f274b
|
200 |
|
0d8dc681c
|
201 202 203 204 205 206 207 208 209 |
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; |
c7f38f46f
|
210 211 |
if (!to_svcpu(vcpu)->slb_max) to_svcpu(vcpu)->slb_max = 1; |
0d8dc681c
|
212 213 |
/* Are we overwriting? */ |
c7f38f46f
|
214 215 |
for (i = 1; i < to_svcpu(vcpu)->slb_max; i++) { if (!(to_svcpu(vcpu)->slb[i].esid & SLB_ESID_V)) |
0d8dc681c
|
216 |
found_inval = i; |
c7f38f46f
|
217 |
else if ((to_svcpu(vcpu)->slb[i].esid & ESID_MASK) == esid) |
0d8dc681c
|
218 219 220 221 222 223 224 225 226 227 228 229 230 |
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 */ |
c7f38f46f
|
231 |
if ((to_svcpu(vcpu)->slb_max) == max_slb_size) |
0d8dc681c
|
232 |
kvmppc_mmu_flush_segments(vcpu); |
c7f38f46f
|
233 234 |
r = to_svcpu(vcpu)->slb_max; to_svcpu(vcpu)->slb_max++; |
0d8dc681c
|
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 */ |
c7f38f46f
|
252 |
to_svcpu(vcpu)->slb[slb_index].esid = 0; |
0d8dc681c
|
253 254 255 256 257 258 259 260 261 262 263 264 |
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; |
c7f38f46f
|
265 266 |
to_svcpu(vcpu)->slb[slb_index].esid = slb_esid; to_svcpu(vcpu)->slb[slb_index].vsid = slb_vsid; |
0d8dc681c
|
267 |
|
928d78be5
|
268 |
trace_kvm_book3s_slbmte(slb_vsid, slb_esid); |
0d8dc681c
|
269 270 271 272 273 274 |
return 0; } void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu) { |
c7f38f46f
|
275 276 |
to_svcpu(vcpu)->slb_max = 1; to_svcpu(vcpu)->slb[0].esid = 0; |
0d8dc681c
|
277 278 279 280 |
} void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu) { |
fef093bec
|
281 |
kvmppc_mmu_hpte_destroy(vcpu); |
8b6db3bc9
|
282 |
__destroy_context(to_book3s(vcpu)->context_id[0]); |
9cc5e9538
|
283 284 285 286 287 288 289 290 291 292 |
} 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; |
8b6db3bc9
|
293 |
vcpu3s->context_id[0] = err; |
9cc5e9538
|
294 |
|
8b6db3bc9
|
295 296 |
vcpu3s->vsid_max = ((vcpu3s->context_id[0] + 1) << USER_ESID_BITS) - 1; vcpu3s->vsid_first = vcpu3s->context_id[0] << USER_ESID_BITS; |
9cc5e9538
|
297 |
vcpu3s->vsid_next = vcpu3s->vsid_first; |
fef093bec
|
298 |
kvmppc_mmu_hpte_init(vcpu); |
9cc5e9538
|
299 |
return 0; |
0d8dc681c
|
300 |
} |