pseries_lpar.c

来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 855 行 · 第 1/2 页

C
855
字号
/* * 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/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>#include <asm/tlbflush.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_ANDCOND		(1UL<<(63-33))#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);}static void tce_build_pSeriesLP(struct TceTable *tbl, long tcenum, 				unsigned long uaddr, int direction ){	u64 setTceRc;	union Tce tce;		PPCDBG(PPCDBG_TCE, "build_tce: uaddr = 0x%lx\n", uaddr);	PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx, tbl = 0x%lx, index=%lx\n", 	       tcenum, tbl, tbl->index);	tce.wholeTce = 0;	tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT;	tce.tceBits.readWrite = 1;	if ( direction != PCI_DMA_TODEVICE ) tce.tceBits.pciWrite = 1;	setTceRc = plpar_tce_put((u64)tbl->index, 				 (u64)tcenum << 12, 				 tce.wholeTce );	/* Make sure the update is visible to hardware.	 * ToDo: sync after setting *all* the tce's.	 */	__asm__ __volatile__ ("sync" : : : "memory");	if(setTceRc) {		PPCDBG(PPCDBG_TCE, "setTce failed. rc=%ld\n", setTceRc);		PPCDBG(PPCDBG_TCE, "\tindex   = 0x%lx\n", (u64)tbl->index);		PPCDBG(PPCDBG_TCE, "\ttcenum  = 0x%lx\n", (u64)tcenum);		PPCDBG(PPCDBG_TCE, "\ttce val = 0x%lx\n", tce.wholeTce );	}}static inline void free_tce_range(struct TceTable *tbl, 				  long tcenum, unsigned order ){	unsigned long flags;	/* Lock the tce allocation bitmap */	spin_lock_irqsave( &(tbl->lock), flags );	/* Do the actual work */	free_tce_range_nolock( tbl, tcenum, order );		/* Unlock the tce allocation bitmap */	spin_unlock_irqrestore( &(tbl->lock), flags );}static void tce_free_pSeriesLP(struct TceTable *tbl, dma_addr_t dma_addr, 			       unsigned order, unsigned numPages){	u64 setTceRc;	long tcenum, freeTce, maxTcenum;	unsigned i;	union Tce tce;	maxTcenum = (tbl->size * (PAGE_SIZE / sizeof(union Tce))) - 1;		tcenum = dma_addr >> PAGE_SHIFT;	freeTce = tcenum - tbl->startOffset;	if ( freeTce > maxTcenum ) {		printk("free_tces: tcenum > maxTcenum\n");		printk("\ttcenum    = 0x%lx\n", tcenum); 		printk("\tfreeTce   = 0x%lx\n", freeTce); 		printk("\tmaxTcenum = 0x%lx\n", maxTcenum); 		printk("\tTCE Table = 0x%lx\n", (u64)tbl);		printk("\tbus#      = 0x%lx\n", 		       (u64)tbl->busNumber );		printk("\tsize      = 0x%lx\n", (u64)tbl->size);		printk("\tstartOff  = 0x%lx\n", 		       (u64)tbl->startOffset );		printk("\tindex     = 0x%lx\n", (u64)tbl->index);		return;	}		for (i=0; i<numPages; ++i) {		tce.wholeTce = 0;		setTceRc = plpar_tce_put((u64)tbl->index, 					 (u64)tcenum << 12, /* note: not freeTce */					 tce.wholeTce );		if ( setTceRc ) {			printk("tce_free: setTce failed\n");			printk("\trc      = %ld\n", setTceRc);			printk("\tindex   = 0x%lx\n", 			       (u64)tbl->index);			printk("\ttcenum  = 0x%lx\n", (u64)tcenum);			printk("\tfreeTce = 0x%lx\n", (u64)freeTce);			printk("\ttce val = 0x%lx\n", 			       tce.wholeTce );		}		++tcenum;	}	/* Make sure the update is visible to hardware. */	__asm__ __volatile__ ("sync" : : : "memory");	free_tce_range( tbl, freeTce, order );}/* PowerPC Interrupts for lpar. *//* NOTE: this typedef is duplicated (for now) from xics.c! */typedef struct {	int (*xirr_info_get)(int cpu);	void (*xirr_info_set)(int cpu, int val);	void (*cppr_info)(int cpu, u8 val);	void (*qirr_info)(int cpu, u8 val);} xics_ops;static int pSeriesLP_xirr_info_get(int n_cpu){	unsigned long lpar_rc;	unsigned long return_value; 	lpar_rc = plpar_xirr(&return_value);	if (lpar_rc != H_Success) {		panic(" bad return code xirr - rc = %lx \n", lpar_rc); 	}	return ((int)(return_value));}static void pSeriesLP_xirr_info_set(int n_cpu, int value){	unsigned long lpar_rc;	unsigned long val64 = value & 0xffffffff;	lpar_rc = plpar_eoi(val64);	if (lpar_rc != H_Success) {		panic(" bad return code EOI - rc = %ld, value=%lx \n", lpar_rc, val64); 	}}static void pSeriesLP_cppr_info(int n_cpu, u8 value){	unsigned long lpar_rc;	lpar_rc = plpar_cppr(value);	if (lpar_rc != H_Success) {		panic(" bad return code cppr - rc = %lx \n", lpar_rc); 	}}static void pSeriesLP_qirr_info(int n_cpu , u8 value){	unsigned long lpar_rc;	lpar_rc = plpar_ipi(get_hard_smp_processor_id(n_cpu),value);	if (lpar_rc != H_Success) {		panic(" bad return code qirr -ipi  - rc = %lx \n", lpar_rc); 	}}xics_ops pSeriesLP_ops = {	pSeriesLP_xirr_info_get,	pSeriesLP_xirr_info_set,	pSeriesLP_cppr_info,	pSeriesLP_qirr_info};/* end TAI-LPAR */int vtermno;	/* virtual terminal# for udbg  */static void udbg_putcLP(unsigned char c){	char buf[16];	unsigned long rc;	if (c == '\n')		udbg_putcLP('\r');	buf[0] = c;	do {		rc = plpar_put_term_char(vtermno, 1, buf);	} while(rc == H_Busy);}/* Buffered chars getc */static long inbuflen;static long inbuf[2];	/* must be 2 longs */static int udbg_getc_pollLP(void){	/* The interface is tricky because it may return up to 16 chars.	 * We save them statically for future calls to udbg_getc().	 */	char ch, *buf = (char *)inbuf;	int i;	long rc;	if (inbuflen == 0) {		/* get some more chars. */		inbuflen = 0;		rc = plpar_get_term_char(vtermno, &inbuflen, buf);		if (inbuflen == 0 && rc == H_Success)			return -1;	}	ch = buf[0];	for (i = 1; i < inbuflen; i++)	/* shuffle them down. */		buf[i-1] = buf[i];	inbuflen--;	return ch;}static unsigned char udbg_getcLP(void){	int ch;	for (;;) {		ch = udbg_getc_pollLP();		if (ch == -1) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?