linux/arch/powerpc/kvm/book3s_mmu_hpte.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2010 SUSE Linux Products GmbH. All rights reserved.
   3 *
   4 * Authors:
   5 *     Alexander Graf <agraf@suse.de>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License, version 2, as
   9 * published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  19 */
  20
  21#include <linux/kvm_host.h>
  22#include <linux/hash.h>
  23#include <linux/slab.h>
  24
  25#include <asm/kvm_ppc.h>
  26#include <asm/kvm_book3s.h>
  27#include <asm/machdep.h>
  28#include <asm/mmu_context.h>
  29#include <asm/hw_irq.h>
  30
  31#include "trace.h"
  32
  33#define PTE_SIZE        12
  34
  35static struct kmem_cache *hpte_cache;
  36
  37static inline u64 kvmppc_mmu_hash_pte(u64 eaddr)
  38{
  39        return hash_64(eaddr >> PTE_SIZE, HPTEG_HASH_BITS_PTE);
  40}
  41
  42static inline u64 kvmppc_mmu_hash_pte_long(u64 eaddr)
  43{
  44        return hash_64((eaddr & 0x0ffff000) >> PTE_SIZE,
  45                       HPTEG_HASH_BITS_PTE_LONG);
  46}
  47
  48static inline u64 kvmppc_mmu_hash_vpte(u64 vpage)
  49{
  50        return hash_64(vpage & 0xfffffffffULL, HPTEG_HASH_BITS_VPTE);
  51}
  52
  53static inline u64 kvmppc_mmu_hash_vpte_long(u64 vpage)
  54{
  55        return hash_64((vpage & 0xffffff000ULL) >> 12,
  56                       HPTEG_HASH_BITS_VPTE_LONG);
  57}
  58
  59void kvmppc_mmu_hpte_cache_map(struct kvm_vcpu *vcpu, struct hpte_cache *pte)
  60{
  61        u64 index;
  62        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
  63
  64        trace_kvm_book3s_mmu_map(pte);
  65
  66        spin_lock(&vcpu3s->mmu_lock);
  67
  68        /* Add to ePTE list */
  69        index = kvmppc_mmu_hash_pte(pte->pte.eaddr);
  70        hlist_add_head_rcu(&pte->list_pte, &vcpu3s->hpte_hash_pte[index]);
  71
  72        /* Add to ePTE_long list */
  73        index = kvmppc_mmu_hash_pte_long(pte->pte.eaddr);
  74        hlist_add_head_rcu(&pte->list_pte_long,
  75                           &vcpu3s->hpte_hash_pte_long[index]);
  76
  77        /* Add to vPTE list */
  78        index = kvmppc_mmu_hash_vpte(pte->pte.vpage);
  79        hlist_add_head_rcu(&pte->list_vpte, &vcpu3s->hpte_hash_vpte[index]);
  80
  81        /* Add to vPTE_long list */
  82        index = kvmppc_mmu_hash_vpte_long(pte->pte.vpage);
  83        hlist_add_head_rcu(&pte->list_vpte_long,
  84                           &vcpu3s->hpte_hash_vpte_long[index]);
  85
  86        spin_unlock(&vcpu3s->mmu_lock);
  87}
  88
  89static void free_pte_rcu(struct rcu_head *head)
  90{
  91        struct hpte_cache *pte = container_of(head, struct hpte_cache, rcu_head);
  92        kmem_cache_free(hpte_cache, pte);
  93}
  94
  95static void invalidate_pte(struct kvm_vcpu *vcpu, struct hpte_cache *pte)
  96{
  97        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
  98
  99        trace_kvm_book3s_mmu_invalidate(pte);
 100
 101        /* Different for 32 and 64 bit */
 102        kvmppc_mmu_invalidate_pte(vcpu, pte);
 103
 104        spin_lock(&vcpu3s->mmu_lock);
 105
 106        /* pte already invalidated in between? */
 107        if (hlist_unhashed(&pte->list_pte)) {
 108                spin_unlock(&vcpu3s->mmu_lock);
 109                return;
 110        }
 111
 112        hlist_del_init_rcu(&pte->list_pte);
 113        hlist_del_init_rcu(&pte->list_pte_long);
 114        hlist_del_init_rcu(&pte->list_vpte);
 115        hlist_del_init_rcu(&pte->list_vpte_long);
 116
 117        if (pte->pte.may_write)
 118                kvm_release_pfn_dirty(pte->pfn);
 119        else
 120                kvm_release_pfn_clean(pte->pfn);
 121
 122        spin_unlock(&vcpu3s->mmu_lock);
 123
 124        vcpu3s->hpte_cache_count--;
 125        call_rcu(&pte->rcu_head, free_pte_rcu);
 126}
 127
 128static void kvmppc_mmu_pte_flush_all(struct kvm_vcpu *vcpu)
 129{
 130        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
 131        struct hpte_cache *pte;
 132        struct hlist_node *node;
 133        int i;
 134
 135        rcu_read_lock();
 136
 137        for (i = 0; i < HPTEG_HASH_NUM_VPTE_LONG; i++) {
 138                struct hlist_head *list = &vcpu3s->hpte_hash_vpte_long[i];
 139
 140                hlist_for_each_entry_rcu(pte, node, list, list_vpte_long)
 141                        invalidate_pte(vcpu, pte);
 142        }
 143
 144        rcu_read_unlock();
 145}
 146
 147static void kvmppc_mmu_pte_flush_page(struct kvm_vcpu *vcpu, ulong guest_ea)
 148{
 149        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
 150        struct hlist_head *list;
 151        struct hlist_node *node;
 152        struct hpte_cache *pte;
 153
 154        /* Find the list of entries in the map */
 155        list = &vcpu3s->hpte_hash_pte[kvmppc_mmu_hash_pte(guest_ea)];
 156
 157        rcu_read_lock();
 158
 159        /* Check the list for matching entries and invalidate */
 160        hlist_for_each_entry_rcu(pte, node, list, list_pte)
 161                if ((pte->pte.eaddr & ~0xfffUL) == guest_ea)
 162                        invalidate_pte(vcpu, pte);
 163
 164        rcu_read_unlock();
 165}
 166
 167static void kvmppc_mmu_pte_flush_long(struct kvm_vcpu *vcpu, ulong guest_ea)
 168{
 169        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
 170        struct hlist_head *list;
 171        struct hlist_node *node;
 172        struct hpte_cache *pte;
 173
 174        /* Find the list of entries in the map */
 175        list = &vcpu3s->hpte_hash_pte_long[
 176                        kvmppc_mmu_hash_pte_long(guest_ea)];
 177
 178        rcu_read_lock();
 179
 180        /* Check the list for matching entries and invalidate */
 181        hlist_for_each_entry_rcu(pte, node, list, list_pte_long)
 182                if ((pte->pte.eaddr & 0x0ffff000UL) == guest_ea)
 183                        invalidate_pte(vcpu, pte);
 184
 185        rcu_read_unlock();
 186}
 187
 188void kvmppc_mmu_pte_flush(struct kvm_vcpu *vcpu, ulong guest_ea, ulong ea_mask)
 189{
 190        trace_kvm_book3s_mmu_flush("", vcpu, guest_ea, ea_mask);
 191        guest_ea &= ea_mask;
 192
 193        switch (ea_mask) {
 194        case ~0xfffUL:
 195                kvmppc_mmu_pte_flush_page(vcpu, guest_ea);
 196                break;
 197        case 0x0ffff000:
 198                kvmppc_mmu_pte_flush_long(vcpu, guest_ea);
 199                break;
 200        case 0:
 201                /* Doing a complete flush -> start from scratch */
 202                kvmppc_mmu_pte_flush_all(vcpu);
 203                break;
 204        default:
 205                WARN_ON(1);
 206                break;
 207        }
 208}
 209
 210/* Flush with mask 0xfffffffff */
 211static void kvmppc_mmu_pte_vflush_short(struct kvm_vcpu *vcpu, u64 guest_vp)
 212{
 213        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
 214        struct hlist_head *list;
 215        struct hlist_node *node;
 216        struct hpte_cache *pte;
 217        u64 vp_mask = 0xfffffffffULL;
 218
 219        list = &vcpu3s->hpte_hash_vpte[kvmppc_mmu_hash_vpte(guest_vp)];
 220
 221        rcu_read_lock();
 222
 223        /* Check the list for matching entries and invalidate */
 224        hlist_for_each_entry_rcu(pte, node, list, list_vpte)
 225                if ((pte->pte.vpage & vp_mask) == guest_vp)
 226                        invalidate_pte(vcpu, pte);
 227
 228        rcu_read_unlock();
 229}
 230
 231/* Flush with mask 0xffffff000 */
 232static void kvmppc_mmu_pte_vflush_long(struct kvm_vcpu *vcpu, u64 guest_vp)
 233{
 234        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
 235        struct hlist_head *list;
 236        struct hlist_node *node;
 237        struct hpte_cache *pte;
 238        u64 vp_mask = 0xffffff000ULL;
 239
 240        list = &vcpu3s->hpte_hash_vpte_long[
 241                kvmppc_mmu_hash_vpte_long(guest_vp)];
 242
 243        rcu_read_lock();
 244
 245        /* Check the list for matching entries and invalidate */
 246        hlist_for_each_entry_rcu(pte, node, list, list_vpte_long)
 247                if ((pte->pte.vpage & vp_mask) == guest_vp)
 248                        invalidate_pte(vcpu, pte);
 249
 250        rcu_read_unlock();
 251}
 252
 253void kvmppc_mmu_pte_vflush(struct kvm_vcpu *vcpu, u64 guest_vp, u64 vp_mask)
 254{
 255        trace_kvm_book3s_mmu_flush("v", vcpu, guest_vp, vp_mask);
 256        guest_vp &= vp_mask;
 257
 258        switch(vp_mask) {
 259        case 0xfffffffffULL:
 260                kvmppc_mmu_pte_vflush_short(vcpu, guest_vp);
 261                break;
 262        case 0xffffff000ULL:
 263                kvmppc_mmu_pte_vflush_long(vcpu, guest_vp);
 264                break;
 265        default:
 266                WARN_ON(1);
 267                return;
 268        }
 269}
 270
 271void kvmppc_mmu_pte_pflush(struct kvm_vcpu *vcpu, ulong pa_start, ulong pa_end)
 272{
 273        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
 274        struct hlist_node *node;
 275        struct hpte_cache *pte;
 276        int i;
 277
 278        trace_kvm_book3s_mmu_flush("p", vcpu, pa_start, pa_end);
 279
 280        rcu_read_lock();
 281
 282        for (i = 0; i < HPTEG_HASH_NUM_VPTE_LONG; i++) {
 283                struct hlist_head *list = &vcpu3s->hpte_hash_vpte_long[i];
 284
 285                hlist_for_each_entry_rcu(pte, node, list, list_vpte_long)
 286                        if ((pte->pte.raddr >= pa_start) &&
 287                            (pte->pte.raddr < pa_end))
 288                                invalidate_pte(vcpu, pte);
 289        }
 290
 291        rcu_read_unlock();
 292}
 293
 294struct hpte_cache *kvmppc_mmu_hpte_cache_next(struct kvm_vcpu *vcpu)
 295{
 296        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
 297        struct hpte_cache *pte;
 298
 299        pte = kmem_cache_zalloc(hpte_cache, GFP_KERNEL);
 300        vcpu3s->hpte_cache_count++;
 301
 302        if (vcpu3s->hpte_cache_count == HPTEG_CACHE_NUM)
 303                kvmppc_mmu_pte_flush_all(vcpu);
 304
 305        return pte;
 306}
 307
 308void kvmppc_mmu_hpte_destroy(struct kvm_vcpu *vcpu)
 309{
 310        kvmppc_mmu_pte_flush(vcpu, 0, 0);
 311}
 312
 313static void kvmppc_mmu_hpte_init_hash(struct hlist_head *hash_list, int len)
 314{
 315        int i;
 316
 317        for (i = 0; i < len; i++)
 318                INIT_HLIST_HEAD(&hash_list[i]);
 319}
 320
 321int kvmppc_mmu_hpte_init(struct kvm_vcpu *vcpu)
 322{
 323        struct kvmppc_vcpu_book3s *vcpu3s = to_book3s(vcpu);
 324
 325        /* init hpte lookup hashes */
 326        kvmppc_mmu_hpte_init_hash(vcpu3s->hpte_hash_pte,
 327                                  ARRAY_SIZE(vcpu3s->hpte_hash_pte));
 328        kvmppc_mmu_hpte_init_hash(vcpu3s->hpte_hash_pte_long,
 329                                  ARRAY_SIZE(vcpu3s->hpte_hash_pte_long));
 330        kvmppc_mmu_hpte_init_hash(vcpu3s->hpte_hash_vpte,
 331                                  ARRAY_SIZE(vcpu3s->hpte_hash_vpte));
 332        kvmppc_mmu_hpte_init_hash(vcpu3s->hpte_hash_vpte_long,
 333                                  ARRAY_SIZE(vcpu3s->hpte_hash_vpte_long));
 334
 335        spin_lock_init(&vcpu3s->mmu_lock);
 336
 337        return 0;
 338}
 339
 340int kvmppc_mmu_hpte_sysinit(void)
 341{
 342        /* init hpte slab cache */
 343        hpte_cache = kmem_cache_create("kvm-spt", sizeof(struct hpte_cache),
 344                                       sizeof(struct hpte_cache), 0, NULL);
 345
 346        return 0;
 347}
 348
 349void kvmppc_mmu_hpte_sysexit(void)
 350{
 351        kmem_cache_destroy(hpte_cache);
 352}
 353