📄 htab.c
字号:
/* * * * PowerPC64 port by Mike Corrigan and Dave Engebretsen * {mikejc|engebret}@us.ibm.com * * Copyright (c) 2000 Mike Corrigan <mikejc@us.ibm.com> * * Module name: htab.c * * Description: * PowerPC Hashed Page Table functions * * 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/processor.h>#include <asm/pgtable.h>#include <asm/mmu.h>#include <asm/mmu_context.h>#include <asm/page.h>#include <asm/types.h>#include <asm/init.h>#include <asm/system.h>#include <asm/iSeries/LparData.h>#include <linux/spinlock.h>#include <asm/ppcdebug.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/sysctl.h>#include <linux/ctype.h>#include <linux/cache.h>#include <asm/uaccess.h>#include <asm/naca.h>#include <asm/system.h>#include <asm/pmc.h>#include <asm/machdep.h>#include <asm/lmb.h>#include <asm/eeh.h>/* For iSeries */#include <asm/iSeries/HvCallHpt.h>/* Note: pte --> Linux PTE * HPTE --> PowerPC Hashed Page Table Entry */HTAB htab_data = {NULL, 0, 0, 0, 0};int proc_dol2crvec(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp);void htab_initialize(void);void make_pte_LPAR(HPTE *htab, unsigned long va, unsigned long pa, int mode, unsigned long hash_mask, int large);extern unsigned long reloc_offset(void);extern unsigned long get_kernel_vsid( unsigned long ea );extern void cacheable_memzero( void *, unsigned int );extern unsigned long _SDR1;extern unsigned long klimit;extern unsigned long _ASR;extern inline void make_ste(unsigned long stab, unsigned long esid, unsigned long vsid);extern char _stext[], _etext[], __start_naca[], __end_stab[];static spinlock_t hash_table_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) - offset))#define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) + offset))#define RELOC(x) (*PTRRELOC(&(x)))extern unsigned long htab_size( unsigned long );unsigned long hpte_getword0_iSeries( unsigned long slot );#define KB (1024)#define MB (1024*KB)static inline voidcreate_pte_mapping(unsigned long start, unsigned long end, unsigned long mode, unsigned long mask, int large){ unsigned long addr, offset = reloc_offset(); HTAB *_htab_data = PTRRELOC(&htab_data); HPTE *htab = (HPTE *)__v2a(_htab_data->htab); unsigned int step; if (large) step = 16*MB; else step = 4*KB; for (addr = start; addr < end; addr += step) { unsigned long vsid = get_kernel_vsid(addr); unsigned long va = (vsid << 28) | (addr & 0xfffffff); make_pte(htab, va, (unsigned long)__v2a(addr), mode, mask, large); }}voidhtab_initialize(void){ unsigned long table, htab_size_bytes; unsigned long pteg_count; unsigned long mode_ro, mode_rw, mask; unsigned long offset = reloc_offset(); struct naca_struct *_naca = RELOC(naca); HTAB *_htab_data = PTRRELOC(&htab_data); /* * Calculate the required size of the htab. We want the number of * PTEGs to equal one half the number of real pages. */ htab_size_bytes = 1UL << _naca->pftSize; pteg_count = htab_size_bytes >> 7; /* For debug, make the HTAB 1/8 as big as it normally would be. */ ifppcdebug(PPCDBG_HTABSIZE) { pteg_count >>= 3; htab_size_bytes = pteg_count << 7; } _htab_data->htab_num_ptegs = pteg_count; _htab_data->htab_hash_mask = pteg_count - 1; if(_naca->platform == PLATFORM_PSERIES) { /* Find storage for the HPT. Must be contiguous in * the absolute address space. */ table = lmb_alloc(htab_size_bytes, htab_size_bytes); if ( !table ) panic("ERROR, cannot find space for HPTE\n"); _htab_data->htab = (HPTE *)__a2v(table); /* htab absolute addr + encoded htabsize */ RELOC(_SDR1) = table + __ilog2(pteg_count) - 11; /* Initialize the HPT with no entries */ cacheable_memzero((void *)table, htab_size_bytes); } else { _htab_data->htab = NULL; RELOC(_SDR1) = 0; } mode_ro = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RXRX; mode_rw = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX; mask = pteg_count-1; /* Create PTE's for the kernel text and data sections plus * the HPT and HPTX arrays. Make the assumption that * (addr & KERNELBASE) == 0 (ie they are disjoint). * We also assume that the va is <= 64 bits. */#if 0 create_pte_mapping((unsigned long)_stext, (unsigned long)__start_naca, mode_ro, mask); create_pte_mapping((unsigned long)__start_naca, (unsigned long)__end_stab, mode_rw, mask); create_pte_mapping((unsigned long)__end_stab, (unsigned long)_etext, mode_ro, mask); create_pte_mapping((unsigned long)_etext, RELOC(klimit), mode_rw, mask); create_pte_mapping((unsigned long)__a2v(table), (unsigned long)__a2v(table+htab_size_bytes), mode_rw, mask);#else#ifndef CONFIG_PPC_ISERIES if (__is_processor(PV_POWER4) && _naca->physicalMemorySize > 256*MB) { create_pte_mapping((unsigned long)KERNELBASE, KERNELBASE + 256*MB, mode_rw, mask, 0); create_pte_mapping((unsigned long)KERNELBASE + 256*MB, KERNELBASE + (_naca->physicalMemorySize), mode_rw, mask, 1); return; }#endif create_pte_mapping((unsigned long)KERNELBASE, KERNELBASE+(_naca->physicalMemorySize), mode_rw, mask, 0);#endif}#undef KB#undef MB/* * Create a pte. Used during initialization only. * We assume the PTE will fit in the primary PTEG. */void make_pte(HPTE *htab, unsigned long va, unsigned long pa, int mode, unsigned long hash_mask, int large){ HPTE *hptep; unsigned long hash, i; volatile unsigned long x = 1; unsigned long vpn;#ifdef CONFIG_PPC_PSERIES if(naca->platform == PLATFORM_PSERIES_LPAR) { make_pte_LPAR(htab, va, pa, mode, hash_mask, large); return; }#endif if (large) vpn = va >> 24; else vpn = va >> 12; hash = hpt_hash(vpn, large); hptep = htab + ((hash & hash_mask)*HPTES_PER_GROUP); for (i = 0; i < 8; ++i, ++hptep) { if ( hptep->dw0.dw0.v == 0 ) { /* !valid */ hptep->dw1.dword1 = pa | mode; hptep->dw0.dword0 = 0; hptep->dw0.dw0.avpn = va >> 23; hptep->dw0.dw0.bolted = 1; /* bolted */ hptep->dw0.dw0.v = 1; /* make valid */ return; } } /* We should _never_ get here and too early to call xmon. */ for(;x;x|=1);}/* Functions to invalidate a HPTE */static void hpte_invalidate_iSeries( unsigned long slot ){ HvCallHpt_invalidateSetSwBitsGet( slot, 0, 0 );}static void hpte_invalidate_pSeries( unsigned long slot ){ /* Local copy of the first doubleword of the HPTE */ union { unsigned long d; Hpte_dword0 h; } hpte_dw0; /* Locate the HPTE */ HPTE * hptep = htab_data.htab + slot; /* Get the first doubleword of the HPTE */ hpte_dw0.d = hptep->dw0.dword0; /* Invalidate the hpte */ hptep->dw0.dword0 = 0; /* Invalidate the tlb */ { unsigned long vsid, group, pi, pi_high; vsid = hpte_dw0.h.avpn >> 5; group = slot >> 3; if(hpte_dw0.h.h) { group = ~group; } pi = (vsid ^ group) & 0x7ff; pi_high = (hpte_dw0.h.avpn & 0x1f) << 11; pi |= pi_high; _tlbie(pi << 12); }}/* Select an available HPT slot for a new HPTE * return slot index (if in primary group) * return -slot index (if in secondary group) */static long hpte_selectslot_iSeries( unsigned long vpn ){ HPTE hpte; long ret_slot, orig_slot; unsigned long primary_hash; unsigned long hpteg_slot; unsigned long slot; unsigned i, k; union { unsigned long d; Hpte_dword0 h; } hpte_dw0; ret_slot = orig_slot = HvCallHpt_findValid( &hpte, vpn ); if ( hpte.dw0.dw0.v ) { /* If valid ...what do we do now? */ udbg_printf( "hpte_selectslot_iSeries: vpn 0x%016lx already valid at slot 0x%016lx\n", vpn, ret_slot ); udbg_printf( "hpte_selectslot_iSeries: returned hpte 0x%016lx 0x%016lx\n", hpte.dw0.dword0, hpte.dw1.dword1 ); return (0x8000000000000000); /* panic("select_hpte_slot found entry already valid\n"); */ } if ( ret_slot == -1 ) { /* -1 indicates no available slots */ /* No available entry found in secondary group */ PMC_SW_SYSTEM(htab_capacity_castouts); primary_hash = hpt_hash(vpn, 0); hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; k = htab_data.next_round_robin++ & 0x7; for ( i=0; i<HPTES_PER_GROUP; ++i ) { if ( k == HPTES_PER_GROUP ) k = 0; slot = hpteg_slot + k; hpte_dw0.d = hpte_getword0_iSeries( slot ); if ( !hpte_dw0.h.bolted ) { hpte_invalidate_iSeries( slot ); ret_slot = slot; } ++k; } } else { if ( ret_slot < 0 ) { PMC_SW_SYSTEM(htab_primary_overflows); ret_slot &= 0x7fffffffffffffff; ret_slot = -ret_slot; } } if ( ret_slot == -1 ) { /* No non-bolted entry found in primary group - time to panic */ udbg_printf("hpte_selectslot_pSeries - No non-bolted HPTE in group 0x%lx! \n", hpteg_slot/HPTES_PER_GROUP); panic("No non-bolted HPTE in group %lx", (unsigned long)hpteg_slot/HPTES_PER_GROUP); } PPCDBG(PPCDBG_MM, "hpte_selectslot_iSeries: vpn=0x%016lx, orig_slot=0x%016lx, ret_slot=0x%016lx \n", vpn, orig_slot, ret_slot ); return ret_slot;}static long hpte_selectslot_pSeries(unsigned long vpn){ HPTE * hptep; unsigned long primary_hash; unsigned long hpteg_slot; unsigned i, k; /* Search the primary group for an available slot */ primary_hash = hpt_hash(vpn, 0); hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; hptep = htab_data.htab + hpteg_slot; for (i=0; i<HPTES_PER_GROUP; ++i) { if ( hptep->dw0.dw0.v == 0 ) { /* If an available slot found, return it */ return hpteg_slot + i; } hptep++; } /* No available entry found in primary group */ PMC_SW_SYSTEM(htab_primary_overflows); /* Search the secondary group */ hpteg_slot = ( ~primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; hptep = htab_data.htab + hpteg_slot; for (i=0; i<HPTES_PER_GROUP; ++i) { if ( hptep->dw0.dw0.v == 0 ) { /* If an available slot found, return it */ return -(hpteg_slot + i); } hptep++; } /* No available entry found in secondary group */ PMC_SW_SYSTEM(htab_capacity_castouts); /* Select an entry in the primary group to replace */ hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; hptep = htab_data.htab + hpteg_slot; k = htab_data.next_round_robin++ & 0x7; for (i=0; i<HPTES_PER_GROUP; ++i) { if (k == HPTES_PER_GROUP) k = 0; if (!hptep[k].dw0.dw0.bolted) { hpteg_slot += k; /* Invalidate the current entry */ ppc_md.hpte_invalidate(hpteg_slot); return hpteg_slot; } ++k; } /* No non-bolted entry found in primary group - time to panic */ udbg_printf("hpte_selectslot_pSeries - No non-bolted HPTE in group 0x%lx! \n", hpteg_slot/HPTES_PER_GROUP); /* xmon(0); */ panic("No non-bolted HPTE in group %lx", (unsigned long)hpteg_slot/HPTES_PER_GROUP); /* keep the compiler happy */ return 0;}unsigned long hpte_getword0_iSeries( unsigned long slot ){ unsigned long dword0; HPTE hpte; HvCallHpt_get( &hpte, slot );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -