linux/arch/powerpc/platforms/ps3/htab.c
<<
>>
Prefs
   1/*
   2 *  PS3 pagetable management routines.
   3 *
   4 *  Copyright (C) 2006 Sony Computer Entertainment Inc.
   5 *  Copyright 2006, 2007 Sony Corporation
   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 as published by
   9 *  the Free Software Foundation; version 2 of the License.
  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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19 */
  20
  21#include <linux/kernel.h>
  22#include <linux/memblock.h>
  23
  24#include <asm/machdep.h>
  25#include <asm/prom.h>
  26#include <asm/udbg.h>
  27#include <asm/lv1call.h>
  28#include <asm/ps3fb.h>
  29
  30#define PS3_VERBOSE_RESULT
  31#include "platform.h"
  32
  33/**
  34 * enum lpar_vas_id - id of LPAR virtual address space.
  35 * @lpar_vas_id_current: Current selected virtual address space
  36 *
  37 * Identify the target LPAR address space.
  38 */
  39
  40enum ps3_lpar_vas_id {
  41        PS3_LPAR_VAS_ID_CURRENT = 0,
  42};
  43
  44
  45static DEFINE_SPINLOCK(ps3_htab_lock);
  46
  47static long ps3_hpte_insert(unsigned long hpte_group, unsigned long vpn,
  48        unsigned long pa, unsigned long rflags, unsigned long vflags,
  49        int psize, int apsize, int ssize)
  50{
  51        int result;
  52        u64 hpte_v, hpte_r;
  53        u64 inserted_index;
  54        u64 evicted_v, evicted_r;
  55        u64 hpte_v_array[4], hpte_rs;
  56        unsigned long flags;
  57        long ret = -1;
  58
  59        /*
  60         * lv1_insert_htab_entry() will search for victim
  61         * entry in both primary and secondary pte group
  62         */
  63        vflags &= ~HPTE_V_SECONDARY;
  64
  65        hpte_v = hpte_encode_v(vpn, psize, apsize, ssize) | vflags | HPTE_V_VALID;
  66        hpte_r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize, apsize) | rflags;
  67
  68        spin_lock_irqsave(&ps3_htab_lock, flags);
  69
  70        /* talk hvc to replace entries BOLTED == 0 */
  71        result = lv1_insert_htab_entry(PS3_LPAR_VAS_ID_CURRENT, hpte_group,
  72                                       hpte_v, hpte_r,
  73                                       HPTE_V_BOLTED, 0,
  74                                       &inserted_index,
  75                                       &evicted_v, &evicted_r);
  76
  77        if (result) {
  78                /* all entries bolted !*/
  79                pr_info("%s:result=%s vpn=%lx pa=%lx ix=%lx v=%llx r=%llx\n",
  80                        __func__, ps3_result(result), vpn, pa, hpte_group,
  81                        hpte_v, hpte_r);
  82                BUG();
  83        }
  84
  85        /*
  86         * see if the entry is inserted into secondary pteg
  87         */
  88        result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT,
  89                                       inserted_index & ~0x3UL,
  90                                       &hpte_v_array[0], &hpte_v_array[1],
  91                                       &hpte_v_array[2], &hpte_v_array[3],
  92                                       &hpte_rs);
  93        BUG_ON(result);
  94
  95        if (hpte_v_array[inserted_index % 4] & HPTE_V_SECONDARY)
  96                ret = (inserted_index & 7) | (1 << 3);
  97        else
  98                ret = inserted_index & 7;
  99
 100        spin_unlock_irqrestore(&ps3_htab_lock, flags);
 101
 102        return ret;
 103}
 104
 105static long ps3_hpte_remove(unsigned long hpte_group)
 106{
 107        panic("ps3_hpte_remove() not implemented");
 108        return 0;
 109}
 110
 111static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp,
 112                              unsigned long vpn, int psize, int apsize,
 113                              int ssize, unsigned long inv_flags)
 114{
 115        int result;
 116        u64 hpte_v, want_v, hpte_rs;
 117        u64 hpte_v_array[4];
 118        unsigned long flags;
 119        long ret;
 120
 121        want_v = hpte_encode_avpn(vpn, psize, ssize);
 122
 123        spin_lock_irqsave(&ps3_htab_lock, flags);
 124
 125        result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, slot & ~0x3UL,
 126                                       &hpte_v_array[0], &hpte_v_array[1],
 127                                       &hpte_v_array[2], &hpte_v_array[3],
 128                                       &hpte_rs);
 129
 130        if (result) {
 131                pr_info("%s: result=%s read vpn=%lx slot=%lx psize=%d\n",
 132                        __func__, ps3_result(result), vpn, slot, psize);
 133                BUG();
 134        }
 135
 136        hpte_v = hpte_v_array[slot % 4];
 137
 138        /*
 139         * As lv1_read_htab_entries() does not give us the RPN, we can
 140         * not synthesize the new hpte_r value here, and therefore can
 141         * not update the hpte with lv1_insert_htab_entry(), so we
 142         * instead invalidate it and ask the caller to update it via
 143         * ps3_hpte_insert() by returning a -1 value.
 144         */
 145        if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) {
 146                /* not found */
 147                ret = -1;
 148        } else {
 149                /* entry found, just invalidate it */
 150                result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT,
 151                                              slot, 0, 0);
 152                ret = -1;
 153        }
 154
 155        spin_unlock_irqrestore(&ps3_htab_lock, flags);
 156        return ret;
 157}
 158
 159static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
 160        int psize, int ssize)
 161{
 162        panic("ps3_hpte_updateboltedpp() not implemented");
 163}
 164
 165static void ps3_hpte_invalidate(unsigned long slot, unsigned long vpn,
 166                                int psize, int apsize, int ssize, int local)
 167{
 168        unsigned long flags;
 169        int result;
 170
 171        spin_lock_irqsave(&ps3_htab_lock, flags);
 172
 173        result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, slot, 0, 0);
 174
 175        if (result) {
 176                pr_info("%s: result=%s vpn=%lx slot=%lx psize=%d\n",
 177                        __func__, ps3_result(result), vpn, slot, psize);
 178                BUG();
 179        }
 180
 181        spin_unlock_irqrestore(&ps3_htab_lock, flags);
 182}
 183
 184/* Called during kexec sequence with MMU off */
 185static notrace void ps3_hpte_clear(void)
 186{
 187        unsigned long hpte_count = (1UL << ppc64_pft_size) >> 4;
 188        u64 i;
 189
 190        for (i = 0; i < hpte_count; i++)
 191                lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, i, 0, 0);
 192
 193        ps3_mm_shutdown();
 194        ps3_mm_vas_destroy();
 195}
 196
 197void __init ps3_hpte_init(unsigned long htab_size)
 198{
 199        mmu_hash_ops.hpte_invalidate = ps3_hpte_invalidate;
 200        mmu_hash_ops.hpte_updatepp = ps3_hpte_updatepp;
 201        mmu_hash_ops.hpte_updateboltedpp = ps3_hpte_updateboltedpp;
 202        mmu_hash_ops.hpte_insert = ps3_hpte_insert;
 203        mmu_hash_ops.hpte_remove = ps3_hpte_remove;
 204        mmu_hash_ops.hpte_clear_all = ps3_hpte_clear;
 205
 206        ppc64_pft_size = __ilog2(htab_size);
 207}
 208
 209