Path: blob/master/arch/powerpc/platforms/cell/beat_htab.c
10818 views
/*1* "Cell Reference Set" HTAB support.2*3* (C) Copyright 2006-2007 TOSHIBA CORPORATION4*5* This code is based on arch/powerpc/platforms/pseries/lpar.c:6* Copyright (C) 2001 Todd Inglett, IBM Corporation7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or11* (at your option) any later version.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License along19* with this program; if not, write to the Free Software Foundation, Inc.,20* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.21*/2223#undef DEBUG_LOW2425#include <linux/kernel.h>26#include <linux/spinlock.h>2728#include <asm/mmu.h>29#include <asm/page.h>30#include <asm/pgtable.h>31#include <asm/machdep.h>32#include <asm/udbg.h>3334#include "beat_wrapper.h"3536#ifdef DEBUG_LOW37#define DBG_LOW(fmt...) do { udbg_printf(fmt); } while (0)38#else39#define DBG_LOW(fmt...) do { } while (0)40#endif4142static DEFINE_RAW_SPINLOCK(beat_htab_lock);4344static inline unsigned int beat_read_mask(unsigned hpte_group)45{46unsigned long rmask = 0;47u64 hpte_v[5];4849beat_read_htab_entries(0, hpte_group + 0, hpte_v);50if (!(hpte_v[0] & HPTE_V_BOLTED))51rmask |= 0x8000;52if (!(hpte_v[1] & HPTE_V_BOLTED))53rmask |= 0x4000;54if (!(hpte_v[2] & HPTE_V_BOLTED))55rmask |= 0x2000;56if (!(hpte_v[3] & HPTE_V_BOLTED))57rmask |= 0x1000;58beat_read_htab_entries(0, hpte_group + 4, hpte_v);59if (!(hpte_v[0] & HPTE_V_BOLTED))60rmask |= 0x0800;61if (!(hpte_v[1] & HPTE_V_BOLTED))62rmask |= 0x0400;63if (!(hpte_v[2] & HPTE_V_BOLTED))64rmask |= 0x0200;65if (!(hpte_v[3] & HPTE_V_BOLTED))66rmask |= 0x0100;67hpte_group = ~hpte_group & (htab_hash_mask * HPTES_PER_GROUP);68beat_read_htab_entries(0, hpte_group + 0, hpte_v);69if (!(hpte_v[0] & HPTE_V_BOLTED))70rmask |= 0x80;71if (!(hpte_v[1] & HPTE_V_BOLTED))72rmask |= 0x40;73if (!(hpte_v[2] & HPTE_V_BOLTED))74rmask |= 0x20;75if (!(hpte_v[3] & HPTE_V_BOLTED))76rmask |= 0x10;77beat_read_htab_entries(0, hpte_group + 4, hpte_v);78if (!(hpte_v[0] & HPTE_V_BOLTED))79rmask |= 0x08;80if (!(hpte_v[1] & HPTE_V_BOLTED))81rmask |= 0x04;82if (!(hpte_v[2] & HPTE_V_BOLTED))83rmask |= 0x02;84if (!(hpte_v[3] & HPTE_V_BOLTED))85rmask |= 0x01;86return rmask;87}8889static long beat_lpar_hpte_insert(unsigned long hpte_group,90unsigned long va, unsigned long pa,91unsigned long rflags, unsigned long vflags,92int psize, int ssize)93{94unsigned long lpar_rc;95u64 hpte_v, hpte_r, slot;9697/* same as iseries */98if (vflags & HPTE_V_SECONDARY)99return -1;100101if (!(vflags & HPTE_V_BOLTED))102DBG_LOW("hpte_insert(group=%lx, va=%016lx, pa=%016lx, "103"rflags=%lx, vflags=%lx, psize=%d)\n",104hpte_group, va, pa, rflags, vflags, psize);105106hpte_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M) |107vflags | HPTE_V_VALID;108hpte_r = hpte_encode_r(pa, psize) | rflags;109110if (!(vflags & HPTE_V_BOLTED))111DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r);112113if (rflags & _PAGE_NO_CACHE)114hpte_r &= ~_PAGE_COHERENT;115116raw_spin_lock(&beat_htab_lock);117lpar_rc = beat_read_mask(hpte_group);118if (lpar_rc == 0) {119if (!(vflags & HPTE_V_BOLTED))120DBG_LOW(" full\n");121raw_spin_unlock(&beat_htab_lock);122return -1;123}124125lpar_rc = beat_insert_htab_entry(0, hpte_group, lpar_rc << 48,126hpte_v, hpte_r, &slot);127raw_spin_unlock(&beat_htab_lock);128129/*130* Since we try and ioremap PHBs we don't own, the pte insert131* will fail. However we must catch the failure in hash_page132* or we will loop forever, so return -2 in this case.133*/134if (unlikely(lpar_rc != 0)) {135if (!(vflags & HPTE_V_BOLTED))136DBG_LOW(" lpar err %lx\n", lpar_rc);137return -2;138}139if (!(vflags & HPTE_V_BOLTED))140DBG_LOW(" -> slot: %lx\n", slot);141142/* We have to pass down the secondary bucket bit here as well */143return (slot ^ hpte_group) & 15;144}145146static long beat_lpar_hpte_remove(unsigned long hpte_group)147{148DBG_LOW("hpte_remove(group=%lx)\n", hpte_group);149return -1;150}151152static unsigned long beat_lpar_hpte_getword0(unsigned long slot)153{154unsigned long dword0;155unsigned long lpar_rc;156u64 dword[5];157158lpar_rc = beat_read_htab_entries(0, slot & ~3UL, dword);159160dword0 = dword[slot&3];161162BUG_ON(lpar_rc != 0);163164return dword0;165}166167static void beat_lpar_hptab_clear(void)168{169unsigned long size_bytes = 1UL << ppc64_pft_size;170unsigned long hpte_count = size_bytes >> 4;171int i;172u64 dummy0, dummy1;173174/* TODO: Use bulk call */175for (i = 0; i < hpte_count; i++)176beat_write_htab_entry(0, i, 0, 0, -1UL, -1UL, &dummy0, &dummy1);177}178179/*180* NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and181* the low 3 bits of flags happen to line up. So no transform is needed.182* We can probably optimize here and assume the high bits of newpp are183* already zero. For now I am paranoid.184*/185static long beat_lpar_hpte_updatepp(unsigned long slot,186unsigned long newpp,187unsigned long va,188int psize, int ssize, int local)189{190unsigned long lpar_rc;191u64 dummy0, dummy1;192unsigned long want_v;193194want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);195196DBG_LOW(" update: "197"avpnv=%016lx, slot=%016lx, psize: %d, newpp %016lx ... ",198want_v & HPTE_V_AVPN, slot, psize, newpp);199200raw_spin_lock(&beat_htab_lock);201dummy0 = beat_lpar_hpte_getword0(slot);202if ((dummy0 & ~0x7FUL) != (want_v & ~0x7FUL)) {203DBG_LOW("not found !\n");204raw_spin_unlock(&beat_htab_lock);205return -1;206}207208lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7, &dummy0,209&dummy1);210raw_spin_unlock(&beat_htab_lock);211if (lpar_rc != 0 || dummy0 == 0) {212DBG_LOW("not found !\n");213return -1;214}215216DBG_LOW("ok %lx %lx\n", dummy0, dummy1);217218BUG_ON(lpar_rc != 0);219220return 0;221}222223static long beat_lpar_hpte_find(unsigned long va, int psize)224{225unsigned long hash;226unsigned long i, j;227long slot;228unsigned long want_v, hpte_v;229230hash = hpt_hash(va, mmu_psize_defs[psize].shift, MMU_SEGSIZE_256M);231want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);232233for (j = 0; j < 2; j++) {234slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;235for (i = 0; i < HPTES_PER_GROUP; i++) {236hpte_v = beat_lpar_hpte_getword0(slot);237238if (HPTE_V_COMPARE(hpte_v, want_v)239&& (hpte_v & HPTE_V_VALID)240&& (!!(hpte_v & HPTE_V_SECONDARY) == j)) {241/* HPTE matches */242if (j)243slot = -slot;244return slot;245}246++slot;247}248hash = ~hash;249}250251return -1;252}253254static void beat_lpar_hpte_updateboltedpp(unsigned long newpp,255unsigned long ea,256int psize, int ssize)257{258unsigned long lpar_rc, slot, vsid, va;259u64 dummy0, dummy1;260261vsid = get_kernel_vsid(ea, MMU_SEGSIZE_256M);262va = (vsid << 28) | (ea & 0x0fffffff);263264raw_spin_lock(&beat_htab_lock);265slot = beat_lpar_hpte_find(va, psize);266BUG_ON(slot == -1);267268lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7,269&dummy0, &dummy1);270raw_spin_unlock(&beat_htab_lock);271272BUG_ON(lpar_rc != 0);273}274275static void beat_lpar_hpte_invalidate(unsigned long slot, unsigned long va,276int psize, int ssize, int local)277{278unsigned long want_v;279unsigned long lpar_rc;280u64 dummy1, dummy2;281unsigned long flags;282283DBG_LOW(" inval : slot=%lx, va=%016lx, psize: %d, local: %d\n",284slot, va, psize, local);285want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);286287raw_spin_lock_irqsave(&beat_htab_lock, flags);288dummy1 = beat_lpar_hpte_getword0(slot);289290if ((dummy1 & ~0x7FUL) != (want_v & ~0x7FUL)) {291DBG_LOW("not found !\n");292raw_spin_unlock_irqrestore(&beat_htab_lock, flags);293return;294}295296lpar_rc = beat_write_htab_entry(0, slot, 0, 0, HPTE_V_VALID, 0,297&dummy1, &dummy2);298raw_spin_unlock_irqrestore(&beat_htab_lock, flags);299300BUG_ON(lpar_rc != 0);301}302303void __init hpte_init_beat(void)304{305ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate;306ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp;307ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp;308ppc_md.hpte_insert = beat_lpar_hpte_insert;309ppc_md.hpte_remove = beat_lpar_hpte_remove;310ppc_md.hpte_clear_all = beat_lpar_hptab_clear;311}312313static long beat_lpar_hpte_insert_v3(unsigned long hpte_group,314unsigned long va, unsigned long pa,315unsigned long rflags, unsigned long vflags,316int psize, int ssize)317{318unsigned long lpar_rc;319u64 hpte_v, hpte_r, slot;320321/* same as iseries */322if (vflags & HPTE_V_SECONDARY)323return -1;324325if (!(vflags & HPTE_V_BOLTED))326DBG_LOW("hpte_insert(group=%lx, va=%016lx, pa=%016lx, "327"rflags=%lx, vflags=%lx, psize=%d)\n",328hpte_group, va, pa, rflags, vflags, psize);329330hpte_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M) |331vflags | HPTE_V_VALID;332hpte_r = hpte_encode_r(pa, psize) | rflags;333334if (!(vflags & HPTE_V_BOLTED))335DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r);336337if (rflags & _PAGE_NO_CACHE)338hpte_r &= ~_PAGE_COHERENT;339340/* insert into not-volted entry */341lpar_rc = beat_insert_htab_entry3(0, hpte_group, hpte_v, hpte_r,342HPTE_V_BOLTED, 0, &slot);343/*344* Since we try and ioremap PHBs we don't own, the pte insert345* will fail. However we must catch the failure in hash_page346* or we will loop forever, so return -2 in this case.347*/348if (unlikely(lpar_rc != 0)) {349if (!(vflags & HPTE_V_BOLTED))350DBG_LOW(" lpar err %lx\n", lpar_rc);351return -2;352}353if (!(vflags & HPTE_V_BOLTED))354DBG_LOW(" -> slot: %lx\n", slot);355356/* We have to pass down the secondary bucket bit here as well */357return (slot ^ hpte_group) & 15;358}359360/*361* NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and362* the low 3 bits of flags happen to line up. So no transform is needed.363* We can probably optimize here and assume the high bits of newpp are364* already zero. For now I am paranoid.365*/366static long beat_lpar_hpte_updatepp_v3(unsigned long slot,367unsigned long newpp,368unsigned long va,369int psize, int ssize, int local)370{371unsigned long lpar_rc;372unsigned long want_v;373unsigned long pss;374375want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);376pss = (psize == MMU_PAGE_4K) ? -1UL : mmu_psize_defs[psize].penc;377378DBG_LOW(" update: "379"avpnv=%016lx, slot=%016lx, psize: %d, newpp %016lx ... ",380want_v & HPTE_V_AVPN, slot, psize, newpp);381382lpar_rc = beat_update_htab_permission3(0, slot, want_v, pss, 7, newpp);383384if (lpar_rc == 0xfffffff7) {385DBG_LOW("not found !\n");386return -1;387}388389DBG_LOW("ok\n");390391BUG_ON(lpar_rc != 0);392393return 0;394}395396static void beat_lpar_hpte_invalidate_v3(unsigned long slot, unsigned long va,397int psize, int ssize, int local)398{399unsigned long want_v;400unsigned long lpar_rc;401unsigned long pss;402403DBG_LOW(" inval : slot=%lx, va=%016lx, psize: %d, local: %d\n",404slot, va, psize, local);405want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);406pss = (psize == MMU_PAGE_4K) ? -1UL : mmu_psize_defs[psize].penc;407408lpar_rc = beat_invalidate_htab_entry3(0, slot, want_v, pss);409410/* E_busy can be valid output: page may be already replaced */411BUG_ON(lpar_rc != 0 && lpar_rc != 0xfffffff7);412}413414static int64_t _beat_lpar_hptab_clear_v3(void)415{416return beat_clear_htab3(0);417}418419static void beat_lpar_hptab_clear_v3(void)420{421_beat_lpar_hptab_clear_v3();422}423424void __init hpte_init_beat_v3(void)425{426if (_beat_lpar_hptab_clear_v3() == 0) {427ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate_v3;428ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp_v3;429ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp;430ppc_md.hpte_insert = beat_lpar_hpte_insert_v3;431ppc_md.hpte_remove = beat_lpar_hpte_remove;432ppc_md.hpte_clear_all = beat_lpar_hptab_clear_v3;433} else {434ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate;435ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp;436ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp;437ppc_md.hpte_insert = beat_lpar_hpte_insert;438ppc_md.hpte_remove = beat_lpar_hpte_remove;439ppc_md.hpte_clear_all = beat_lpar_hptab_clear;440}441}442443444