stab.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:10k
- /*
- * PowerPC64 Segment Translation Support.
- *
- * Dave Engebretsen and Mike Corrigan {engebret|mikejc}@us.ibm.com
- * Copyright (c) 2001 Dave Engebretsen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
- #include <linux/config.h>
- #include <asm/pgtable.h>
- #include <asm/mmu.h>
- #include <asm/mmu_context.h>
- #include <asm/paca.h>
- #include <asm/naca.h>
- #include <asm/pmc.h>
- inline int make_ste(unsigned long stab,
- unsigned long esid, unsigned long vsid);
- inline void make_slbe(unsigned long esid, unsigned long vsid,
- int large);
- /*
- * Build an entry for the base kernel segment and put it into
- * the segment table or SLB. All other segment table or SLB
- * entries are faulted in.
- */
- void stab_initialize(unsigned long stab)
- {
- unsigned long esid, vsid;
- esid = GET_ESID(KERNELBASE);
- vsid = get_kernel_vsid(esid << SID_SHIFT);
- if (!__is_processor(PV_POWER4) && !__is_processor(PV_POWER4p)) {
- __asm__ __volatile__("isync; slbia; isync":::"memory");
- make_ste(stab, esid, vsid);
- } else {
- /* Invalidate the entire SLB & all the ERATS */
- __asm__ __volatile__("isync" : : : "memory");
- #ifndef CONFIG_PPC_ISERIES
- __asm__ __volatile__("slbmte %0,%0"
- : : "r" (0) : "memory");
- __asm__ __volatile__("isync; slbia; isync":::"memory");
- make_slbe(esid, vsid, 0);
- #else
- __asm__ __volatile__("isync; slbia; isync":::"memory");
- #endif
- }
- }
- /*
- * Create a segment table entry for the given esid/vsid pair.
- */
- inline int
- make_ste(unsigned long stab, unsigned long esid, unsigned long vsid)
- {
- unsigned long entry, group, old_esid, castout_entry, i;
- unsigned int global_entry;
- STE *ste, *castout_ste;
- /* Search the primary group first. */
- global_entry = (esid & 0x1f) << 3;
- ste = (STE *)(stab | ((esid & 0x1f) << 7));
- /*
- * Find an empty entry, if one exists.
- */
- for(group = 0; group < 2; group++) {
- for(entry = 0; entry < 8; entry++, ste++) {
- if(!(ste->dw0.dw0.v)) {
- ste->dw1.dw1.vsid = vsid;
- /* Order VSID updte */
- __asm__ __volatile__ ("eieio" : : : "memory");
- ste->dw0.dw0.esid = esid;
- ste->dw0.dw0.v = 1;
- ste->dw0.dw0.kp = 1;
- /* Order update */
- __asm__ __volatile__ ("sync" : : : "memory");
- return(global_entry | entry);
- }
- }
- /* Now search the secondary group. */
- global_entry = ((~esid) & 0x1f) << 3;
- ste = (STE *)(stab | (((~esid) & 0x1f) << 7));
- }
- /*
- * Could not find empty entry, pick one with a round robin selection.
- * Search all entries in the two groups. Note that the first time
- * we get here, we start with entry 1 so the initializer
- * can be common with the SLB castout code.
- */
- /* This assumes we never castout when initializing the stab. */
- PMC_SW_PROCESSOR(stab_capacity_castouts);
- castout_entry = get_paca()->xStab_data.next_round_robin;
- for(i = 0; i < 16; i++) {
- if(castout_entry < 8) {
- global_entry = (esid & 0x1f) << 3;
- ste = (STE *)(stab | ((esid & 0x1f) << 7));
- castout_ste = ste + castout_entry;
- } else {
- global_entry = ((~esid) & 0x1f) << 3;
- ste = (STE *)(stab | (((~esid) & 0x1f) << 7));
- castout_ste = ste + (castout_entry - 8);
- }
- if((((castout_ste->dw0.dw0.esid) >> 32) == 0) ||
- (((castout_ste->dw0.dw0.esid) & 0xffffffff) > 0)) {
- /* Found an entry to castout. It is either a user */
- /* region, or a secondary kernel segment. */
- break;
- }
- castout_entry = (castout_entry + 1) & 0xf;
- }
- get_paca()->xStab_data.next_round_robin = (castout_entry + 1) & 0xf;
- /* Modify the old entry to the new value. */
- /* Force previous translations to complete. DRENG */
- __asm__ __volatile__ ("isync" : : : "memory" );
- castout_ste->dw0.dw0.v = 0;
- __asm__ __volatile__ ("sync" : : : "memory" ); /* Order update */
- castout_ste->dw1.dw1.vsid = vsid;
- __asm__ __volatile__ ("eieio" : : : "memory" ); /* Order update */
- old_esid = castout_ste->dw0.dw0.esid;
- castout_ste->dw0.dw0.esid = esid;
- castout_ste->dw0.dw0.v = 1;
- castout_ste->dw0.dw0.kp = 1;
- __asm__ __volatile__ ("slbie %0" : : "r" (old_esid << SID_SHIFT));
- /* Ensure completion of slbie */
- __asm__ __volatile__ ("sync" : : : "memory" );
- return(global_entry | (castout_entry & 0x7));
- }
- /*
- * Create a segment buffer entry for the given esid/vsid pair.
- */
- inline void make_slbe(unsigned long esid, unsigned long vsid, int large)
- {
- unsigned long entry, castout_entry;
- slb_dword0 castout_esid_data;
- union {
- unsigned long word0;
- slb_dword0 data;
- } esid_data;
- union {
- unsigned long word0;
- slb_dword1 data;
- } vsid_data;
-
- /*
- * Find an empty entry, if one exists.
- */
- for(entry = 0; entry < naca->slb_size; entry++) {
- __asm__ __volatile__("slbmfee %0,%1"
- : "=r" (esid_data) : "r" (entry));
- if(!esid_data.data.v) {
- /*
- * Write the new SLB entry.
- */
- vsid_data.word0 = 0;
- vsid_data.data.vsid = vsid;
- vsid_data.data.kp = 1;
- if (large)
- vsid_data.data.l = 1;
- esid_data.word0 = 0;
- esid_data.data.esid = esid;
- esid_data.data.v = 1;
- esid_data.data.index = entry;
- /* slbie not needed as no previous mapping existed. */
- /* Order update */
- __asm__ __volatile__ ("isync" : : : "memory");
- __asm__ __volatile__ ("slbmte %0,%1"
- : : "r" (vsid_data),
- "r" (esid_data));
- /* Order update */
- __asm__ __volatile__ ("isync" : : : "memory");
- return;
- }
- }
-
- /*
- * Could not find empty entry, pick one with a round robin selection.
- */
- PMC_SW_PROCESSOR(stab_capacity_castouts);
- castout_entry = get_paca()->xStab_data.next_round_robin;
- __asm__ __volatile__("slbmfee %0,%1"
- : "=r" (castout_esid_data)
- : "r" (castout_entry));
- entry = castout_entry;
- castout_entry++;
- if(castout_entry >= naca->slb_size) {
- castout_entry = 1;
- }
- get_paca()->xStab_data.next_round_robin = castout_entry;
- /* Invalidate the old entry. */
- castout_esid_data.v = 0; /* Set the class to 0 */
- /* slbie not needed as the previous mapping is still valid. */
- __asm__ __volatile__("slbie %0" : : "r" (castout_esid_data));
-
- /*
- * Write the new SLB entry.
- */
- vsid_data.word0 = 0;
- vsid_data.data.vsid = vsid;
- vsid_data.data.kp = 1;
- if (large)
- vsid_data.data.l = 1;
-
- esid_data.word0 = 0;
- esid_data.data.esid = esid;
- esid_data.data.v = 1;
- esid_data.data.index = entry;
-
- __asm__ __volatile__ ("isync" : : : "memory"); /* Order update */
- __asm__ __volatile__ ("slbmte %0,%1"
- : : "r" (vsid_data), "r" (esid_data));
- __asm__ __volatile__ ("isync" : : : "memory" ); /* Order update */
- }
- /*
- * Allocate a segment table entry for the given ea.
- */
- int ste_allocate ( unsigned long ea,
- unsigned long trap)
- {
- unsigned long vsid, esid;
- int kernel_segment = 0;
- PMC_SW_PROCESSOR(stab_faults);
- /* Check for invalid effective addresses. */
- if (!IS_VALID_EA(ea)) {
- return 1;
- }
-
- /* Kernel or user address? */
- if (REGION_ID(ea)) {
- kernel_segment = 1;
- vsid = get_kernel_vsid( ea );
- } else {
- struct mm_struct *mm = current->mm;
- if ( mm ) {
- vsid = get_vsid(mm->context, ea );
- } else {
- return 1;
- }
- }
- esid = GET_ESID(ea);
- if (trap == 0x380 || trap == 0x480) {
- #ifndef CONFIG_PPC_ISERIES
- if (REGION_ID(ea) == KERNEL_REGION_ID)
- make_slbe(esid, vsid, 1);
- else
- #endif
- make_slbe(esid, vsid, 0);
- } else {
- unsigned char top_entry, stab_entry, *segments;
- stab_entry = make_ste(get_paca()->xStab_data.virt, esid, vsid);
- PMC_SW_PROCESSOR_A(stab_entry_use, stab_entry & 0xf);
- segments = get_paca()->xSegments;
- top_entry = segments[0];
- if(!kernel_segment && top_entry < (STAB_CACHE_SIZE - 1)) {
- top_entry++;
- segments[top_entry] = stab_entry;
- if(top_entry == STAB_CACHE_SIZE - 1) top_entry = 0xff;
- segments[0] = top_entry;
- }
- }
-
- return(0);
- }
-
- /*
- * Flush all entries from the segment table of the current processor.
- * Kernel and Bolted entries are not removed as we cannot tolerate
- * faults on those addresses.
- */
- #define STAB_PRESSURE 0
- void flush_stab(void)
- {
- STE *stab = (STE *) get_paca()->xStab_data.virt;
- unsigned char *segments = get_paca()->xSegments;
- unsigned long flags, i;
- if (!__is_processor(PV_POWER4) && !__is_processor(PV_POWER4p)) {
- unsigned long entry;
- STE *ste;
- /* Force previous translations to complete. DRENG */
- __asm__ __volatile__ ("isync" : : : "memory");
- __save_and_cli(flags);
- if(segments[0] != 0xff && !STAB_PRESSURE) {
- for(i = 1; i <= segments[0]; i++) {
- ste = stab + segments[i];
- ste->dw0.dw0.v = 0;
- PMC_SW_PROCESSOR(stab_invalidations);
- }
- } else {
- /* Invalidate all entries. */
- ste = stab;
- /* Never flush the first entry. */
- ste += 1;
- for(entry = 1;
- entry < (PAGE_SIZE / sizeof(STE));
- entry++, ste++) {
- unsigned long ea;
- ea = ste->dw0.dw0.esid << SID_SHIFT;
- if (STAB_PRESSURE || (!REGION_ID(ea))) {
- ste->dw0.dw0.v = 0;
- PMC_SW_PROCESSOR(stab_invalidations);
- }
- }
- }
- *((unsigned long *)segments) = 0;
- __restore_flags(flags);
- /* Invalidate the SLB. */
- /* Force invals to complete. */
- __asm__ __volatile__ ("sync" : : : "memory");
- /* Flush the SLB. */
- __asm__ __volatile__ ("slbia" : : : "memory");
- /* Force flush to complete. */
- __asm__ __volatile__ ("sync" : : : "memory");
- } else {
- unsigned long flags;
- PMC_SW_PROCESSOR(stab_invalidations);
- __save_and_cli(flags);
- __asm__ __volatile__("isync; slbia; isync":::"memory");
- __restore_flags(flags);
- }
- }