📄 pseries_lpar.c
字号:
/* * pSeries_lpar.c * Copyright (C) 2001 Todd Inglett, IBM Corporation * * pSeries LPAR support. * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/config.h>#include <linux/kernel.h>#include <asm/processor.h>#include <asm/semaphore.h>#include <asm/mmu.h>#include <asm/page.h>#include <asm/pgtable.h>#include <asm/machdep.h>#include <asm/abs_addr.h>#include <asm/mmu_context.h>#include <asm/ppcdebug.h>#include <asm/pci_dma.h>#include <linux/pci.h>#include <asm/naca.h>/* Status return values */#define H_Success 0#define H_Busy 1 /* Hardware busy -- retry later */#define H_Hardware -1 /* Hardware error */#define H_Function -2 /* Function not supported */#define H_Privilege -3 /* Caller not privileged */#define H_Parameter -4 /* Parameter invalid, out-of-range or conflicting */#define H_Bad_Mode -5 /* Illegal msr value */#define H_PTEG_Full -6 /* PTEG is full */#define H_Not_Found -7 /* PTE was not found" */#define H_Reserved_DABR -8 /* DABR address is reserved by the hypervisor on this processor" *//* Flags */#define H_LARGE_PAGE (1UL<<(63-16))#define H_EXACT (1UL<<(63-24)) /* Use exact PTE or return H_PTEG_FULL */#define H_R_XLATE (1UL<<(63-25)) /* include a valid logical page num in the pte if the valid bit is set */#define H_READ_4 (1UL<<(63-26)) /* Return 4 PTEs */#define H_AVPN (1UL<<(63-32)) /* An avpn is provided as a sanity test */#define H_ICACHE_INVALIDATE (1UL<<(63-40)) /* icbi, etc. (ignored for IO pages) */#define H_ICACHE_SYNCHRONIZE (1UL<<(63-41)) /* dcbst, icbi, etc (ignored for IO pages */#define H_ZERO_PAGE (1UL<<(63-48)) /* zero the page before mapping (ignored for IO pages) */#define H_COPY_PAGE (1UL<<(63-49))#define H_N (1UL<<(63-61))#define H_PP1 (1UL<<(63-62))#define H_PP2 (1UL<<(63-63))/* pSeries hypervisor opcodes */#define H_REMOVE 0x04#define H_ENTER 0x08#define H_READ 0x0c#define H_CLEAR_MOD 0x10#define H_CLEAR_REF 0x14#define H_PROTECT 0x18#define H_GET_TCE 0x1c#define H_PUT_TCE 0x20#define H_SET_SPRG0 0x24#define H_SET_DABR 0x28#define H_PAGE_INIT 0x2c#define H_SET_ASR 0x30#define H_ASR_ON 0x34#define H_ASR_OFF 0x38#define H_LOGICAL_CI_LOAD 0x3c#define H_LOGICAL_CI_STORE 0x40#define H_LOGICAL_CACHE_LOAD 0x44#define H_LOGICAL_CACHE_STORE 0x48#define H_LOGICAL_ICBI 0x4c#define H_LOGICAL_DCBF 0x50#define H_GET_TERM_CHAR 0x54#define H_PUT_TERM_CHAR 0x58#define H_REAL_TO_LOGICAL 0x5c#define H_HYPERVISOR_DATA 0x60#define H_EOI 0x64#define H_CPPR 0x68#define H_IPI 0x6c#define H_IPOLL 0x70#define H_XIRR 0x74#define HSC ".long 0x44000022\n"#define H_ENTER_r3 "li 3, 0x08\n"/* plpar_hcall() -- Generic call interface using above opcodes * * The actual call interface is a hypervisor call instruction with * the opcode in R3 and input args in R4-R7. * Status is returned in R3 with variable output values in R4-R11. * Only H_PTE_READ with H_READ_4 uses R6-R11 so we ignore it for now * and return only two out args which MUST ALWAYS BE PROVIDED. */long plpar_hcall(unsigned long opcode, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long *out1, unsigned long *out2, unsigned long *out3);/* Same as plpar_hcall but for those opcodes that return no values * other than status. Slightly more efficient. */long plpar_hcall_norets(unsigned long opcode, ...);long plpar_pte_enter(unsigned long flags, unsigned long ptex, unsigned long new_pteh, unsigned long new_ptel, unsigned long *old_pteh_ret, unsigned long *old_ptel_ret){ unsigned long dummy, ret; ret = plpar_hcall(H_ENTER, flags, ptex, new_pteh, new_ptel, old_pteh_ret, old_ptel_ret, &dummy); return(ret);}long plpar_pte_remove(unsigned long flags, unsigned long ptex, unsigned long avpn, unsigned long *old_pteh_ret, unsigned long *old_ptel_ret){ unsigned long dummy; return plpar_hcall(H_REMOVE, flags, ptex, avpn, 0, old_pteh_ret, old_ptel_ret, &dummy);}long plpar_pte_read(unsigned long flags, unsigned long ptex, unsigned long *old_pteh_ret, unsigned long *old_ptel_ret){ unsigned long dummy; return plpar_hcall(H_READ, flags, ptex, 0, 0, old_pteh_ret, old_ptel_ret, &dummy);}long plpar_pte_protect(unsigned long flags, unsigned long ptex, unsigned long avpn){ return plpar_hcall_norets(H_PROTECT, flags, ptex);}long plpar_tce_get(unsigned long liobn, unsigned long ioba, unsigned long *tce_ret){ unsigned long dummy; return plpar_hcall(H_GET_TCE, liobn, ioba, 0, 0, tce_ret, &dummy, &dummy);}long plpar_tce_put(unsigned long liobn, unsigned long ioba, unsigned long tceval){ return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval);}long plpar_get_term_char(unsigned long termno, unsigned long *len_ret, char *buf_ret){ unsigned long *lbuf = (unsigned long *)buf_ret; /* ToDo: alignment? */ return plpar_hcall(H_GET_TERM_CHAR, termno, 0, 0, 0, len_ret, lbuf+0, lbuf+1);}long plpar_put_term_char(unsigned long termno, unsigned long len, const char *buffer){ unsigned long dummy; unsigned long *lbuf = (unsigned long *)buffer; /* ToDo: alignment? */ return plpar_hcall(H_PUT_TERM_CHAR, termno, len, lbuf[0], lbuf[1], &dummy, &dummy, &dummy);}long plpar_eoi(unsigned long xirr){ return plpar_hcall_norets(H_EOI, xirr);}long plpar_cppr(unsigned long cppr){ return plpar_hcall_norets(H_CPPR, cppr);}long plpar_ipi(unsigned long servernum, unsigned long mfrr){ return plpar_hcall_norets(H_IPI, servernum, mfrr);}long plpar_xirr(unsigned long *xirr_ret){ unsigned long dummy; return plpar_hcall(H_XIRR, 0, 0, 0, 0, xirr_ret, &dummy, &dummy);}long plpar_ipoll(unsigned long servernum, unsigned long* xirr_ret, unsigned long* mfrr_ret){ unsigned long dummy; return plpar_hcall(H_IPOLL, servernum, 0, 0, 0, xirr_ret, mfrr_ret, &dummy);}/* * The following section contains code that ultimately should * be put in the relavent file (htab.c, xics.c, etc). It has * been put here for the time being in order to ease maintainence * of the pSeries LPAR code until it can all be put into CVS. */static void hpte_invalidate_pSeriesLP(unsigned long slot){ HPTE old_pte; unsigned long lpar_rc; unsigned long flags = 0; lpar_rc = plpar_pte_remove(flags, slot, 0, &old_pte.dw0.dword0, &old_pte.dw1.dword1); if (lpar_rc != H_Success) BUG();}/* NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and * the low 3 bits of flags happen to line up. So no transform is needed. * We can probably optimize here and assume the high bits of newpp are * already zero. For now I am paranoid. */static void hpte_updatepp_pSeriesLP(long slot, unsigned long newpp, unsigned long va){ unsigned long lpar_rc; unsigned long flags; flags = newpp & 3; lpar_rc = plpar_pte_protect( flags, slot, 0); if (lpar_rc != H_Success) { udbg_printf( " bad return code from pte protect rc = %lx \n", lpar_rc); for (;;); }}static void hpte_updateboltedpp_pSeriesLP(unsigned long newpp, unsigned long ea){ unsigned long lpar_rc; unsigned long vsid,va,vpn,flags; long slot; vsid = get_kernel_vsid( ea ); va = ( vsid << 28 ) | ( ea & 0x0fffffff ); vpn = va >> PAGE_SHIFT; slot = ppc_md.hpte_find( vpn ); flags = newpp & 3; lpar_rc = plpar_pte_protect( flags, slot, 0); if (lpar_rc != H_Success) { udbg_printf( " bad return code from pte bolted protect rc = %lx \n", lpar_rc); for (;;); }}static unsigned long hpte_getword0_pSeriesLP(unsigned long slot){ unsigned long dword0; unsigned long lpar_rc; unsigned long dummy_word1; unsigned long flags; /* Read 1 pte at a time */ /* Do not need RPN to logical page translation */ /* No cross CEC PFT access */ flags = 0; lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1); if (lpar_rc != H_Success) { udbg_printf(" error on pte read in get_hpte0 rc = %lx \n", lpar_rc); for (;;); } return(dword0);}static long hpte_selectslot_pSeriesLP(unsigned long vpn){ unsigned long primary_hash; unsigned long hpteg_slot; unsigned i, k; unsigned long flags; HPTE pte_read; unsigned long lpar_rc; /* 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; /* Read 1 pte at a time */ /* Do not need RPN to logical page translation */ /* No cross CEC PFT access */ flags = 0; for (i=0; i<HPTES_PER_GROUP; ++i) { /* read the hpte entry from the slot */ lpar_rc = plpar_pte_read(flags, hpteg_slot + i, &pte_read.dw0.dword0, &pte_read.dw1.dword1); if (lpar_rc != H_Success) { udbg_printf(" read of hardware page table failed rc = %lx \n", lpar_rc); for (;;); } if ( pte_read.dw0.dw0.v == 0 ) { /* If an available slot found, return it */ return hpteg_slot + i; } } /* Search the secondary group for an available slot */ hpteg_slot = ( ~primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; for (i=0; i<HPTES_PER_GROUP; ++i) { /* read the hpte entry from the slot */ lpar_rc = plpar_pte_read(flags, hpteg_slot + i, &pte_read.dw0.dword0, &pte_read.dw1.dword1); if (lpar_rc != H_Success) { udbg_printf(" read of hardware page table failed2 rc = %lx \n", lpar_rc); for (;;); } if ( pte_read.dw0.dw0.v == 0 ) { /* If an available slot found, return it */ return hpteg_slot + i; } } /* No available entry found in secondary group */ /* Select an entry in the primary group to replace */ 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; lpar_rc = plpar_pte_read(flags, hpteg_slot + k, &pte_read.dw0.dword0, &pte_read.dw1.dword1); if (lpar_rc != H_Success) { udbg_printf( " pte read failed - rc = %lx", lpar_rc); for (;;); } if ( ! pte_read.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("select_hpte_slot - No non-bolted HPTE in group 0x%lx! \n", hpteg_slot/HPTES_PER_GROUP); udbg_printf("No non-bolted HPTE in group %lx", (unsigned long)hpteg_slot/HPTES_PER_GROUP); for (;;); /* never executes - avoid compiler errors */ return 0;}static void hpte_create_valid_pSeriesLP(unsigned long slot, unsigned long vpn, unsigned long prpn, unsigned hash, void *ptep, unsigned hpteflags, unsigned bolted){ /* Local copy of HPTE */ struct { /* Local copy of first doubleword of HPTE */ union { unsigned long d; Hpte_dword0 h; } dw0; /* Local copy of second doubleword of HPTE */ union { unsigned long d; Hpte_dword1 h; Hpte_dword1_flags f; } dw1; } lhpte; unsigned long avpn = vpn >> 11; unsigned long arpn = physRpn_to_absRpn( prpn ); unsigned long lpar_rc; unsigned long flags; HPTE ret_hpte;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -