📄 mf.c
字号:
/* * mf.c * Copyright (C) 2001 Troy D. Armstrong IBM Corporation * * This modules exists as an interface between a Linux secondary partition * running on an iSeries and the primary partition's Virtual Service * Processor (VSP) object. The VSP has final authority over powering on/off * all partitions in the iSeries. It also provides miscellaneous low-level * machine facility type operations. * * * 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 <asm/iSeries/mf.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/mm.h>#include <asm/iSeries/HvLpConfig.h>#include <linux/slab.h>#include <linux/delay.h>#include <asm/nvram.h>#include <asm/time.h>#include <asm/iSeries/ItSpCommArea.h>#include <asm/iSeries/mf_proc.h>#include <asm/iSeries/iSeries_proc.h>#include <asm/uaccess.h>#include <linux/pci.h>extern struct pci_dev * iSeries_vio_dev;/* * This is the structure layout for the Machine Facilites LPAR event * flows. */struct VspCmdData;struct CeMsgData;union SafeCast{ u64 ptrAsU64; void *ptr;};typedef void (*CeMsgCompleteHandler)( void *token, struct CeMsgData *vspCmdRsp );struct CeMsgCompleteData{ CeMsgCompleteHandler xHdlr; void *xToken;};struct VspRspData{ struct semaphore *xSemaphore; struct VspCmdData *xResponse;};struct IoMFLpEvent{ struct HvLpEvent xHvLpEvent; u16 xSubtypeRc; u16 xRsvd1; u32 xRsvd2; union { struct AllocData { u16 xSize; u16 xType; u32 xCount; u16 xRsvd3; u8 xRsvd4; HvLpIndex xTargetLp; } xAllocData; struct CeMsgData { u8 xCEMsg[12]; char xReserved[4]; struct CeMsgCompleteData *xToken; } xCEMsgData; struct VspCmdData { union SafeCast xTokenUnion; u16 xCmd; HvLpIndex xLpIndex; u8 xRc; u32 xReserved1; union VspCmdSubData { struct { u64 xState; } xGetStateOut; struct { u64 xIplType; } xGetIplTypeOut, xFunction02SelectIplTypeIn; struct { u64 xIplMode; } xGetIplModeOut, xFunction02SelectIplModeIn; struct { u64 xPage[4]; } xGetSrcHistoryIn; struct { u64 xFlag; } xGetAutoIplWhenPrimaryIplsOut, xSetAutoIplWhenPrimaryIplsIn, xWhiteButtonPowerOffIn, xFunction08FastPowerOffIn, xIsSpcnRackPowerIncompleteOut; struct { u64 xToken; u64 xAddressType; u64 xSide; u32 xTransferLength; u32 xOffset; } xSetKernelImageIn, xGetKernelImageIn, xSetKernelCmdLineIn, xGetKernelCmdLineIn; struct { u32 xTransferLength; } xGetKernelImageOut,xGetKernelCmdLineOut; u8 xReserved2[80]; } xSubData; } xVspCmd; } xUnion;};/* * All outgoing event traffic is kept on a FIFO queue. The first * pointer points to the one that is outstanding, and all new * requests get stuck on the end. Also, we keep a certain number of * preallocated stack elements so that we can operate very early in * the boot up sequence (before kmalloc is ready). */struct StackElement{ struct StackElement * next; struct IoMFLpEvent event; MFCompleteHandler hdlr; char dmaData[72]; unsigned dmaDataLength; unsigned remoteAddress;};static spinlock_t spinlock;static struct StackElement * head = NULL;static struct StackElement * tail = NULL;static struct StackElement * avail = NULL;static struct StackElement prealloc[16];/* * Put a stack element onto the available queue, so it can get reused. * Attention! You must have the spinlock before calling! */void free( struct StackElement * element ){ if ( element != NULL ) { element->next = avail; avail = element; }}/* * Enqueue the outbound event onto the stack. If the queue was * empty to begin with, we must also issue it via the Hypervisor * interface. There is a section of code below that will touch * the first stack pointer without the protection of the spinlock. * This is OK, because we know that nobody else will be modifying * the first pointer when we do this. */static int signalEvent( struct StackElement * newElement ){ int rc = 0; unsigned long flags; int go = 1; struct StackElement * element; HvLpEvent_Rc hvRc; /* enqueue the event */ if ( newElement != NULL ) { spin_lock_irqsave( &spinlock, flags ); if ( head == NULL ) head = newElement; else { go = 0; tail->next = newElement; } newElement->next = NULL; tail = newElement; spin_unlock_irqrestore( &spinlock, flags ); } /* send the event */ while ( go ) { go = 0; /* any DMA data to send beforehand? */ if ( head->dmaDataLength > 0 ) HvCallEvent_dmaToSp( head->dmaData, head->remoteAddress, head->dmaDataLength, HvLpDma_Direction_LocalToRemote ); hvRc = HvCallEvent_signalLpEvent(&head->event.xHvLpEvent); if ( hvRc != HvLpEvent_Rc_Good ) { printk( KERN_ERR "mf.c: HvCallEvent_signalLpEvent() failed with %d\n", (int)hvRc ); spin_lock_irqsave( &spinlock, flags ); element = head; head = head->next; if ( head != NULL ) go = 1; spin_unlock_irqrestore( &spinlock, flags ); if ( element == newElement ) rc = -EIO; else { if ( element->hdlr != NULL ) { union SafeCast mySafeCast; mySafeCast.ptrAsU64 = element->event.xHvLpEvent.xCorrelationToken; (*element->hdlr)( mySafeCast.ptr, -EIO ); } } spin_lock_irqsave( &spinlock, flags ); free( element ); spin_unlock_irqrestore( &spinlock, flags ); } } return rc;}/* * Allocate a new StackElement structure, and initialize it. */static struct StackElement * newStackElement( void ){ struct StackElement * newElement = NULL; HvLpIndex primaryLp = HvLpConfig_getPrimaryLpIndex(); unsigned long flags; if ( newElement == NULL ) { spin_lock_irqsave( &spinlock, flags ); if ( avail != NULL ) { newElement = avail; avail = avail->next; } spin_unlock_irqrestore( &spinlock, flags ); } if ( newElement == NULL ) newElement = kmalloc(sizeof(struct StackElement),GFP_ATOMIC); if ( newElement == NULL ) { printk( KERN_ERR "mf.c: unable to kmalloc %ld bytes\n", sizeof(struct StackElement) ); return NULL; } memset( newElement, 0, sizeof(struct StackElement) ); newElement->event.xHvLpEvent.xFlags.xValid = 1; newElement->event.xHvLpEvent.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck; newElement->event.xHvLpEvent.xFlags.xAckInd = HvLpEvent_AckInd_DoAck; newElement->event.xHvLpEvent.xFlags.xFunction = HvLpEvent_Function_Int; newElement->event.xHvLpEvent.xType = HvLpEvent_Type_MachineFac; newElement->event.xHvLpEvent.xSourceLp = HvLpConfig_getLpIndex(); newElement->event.xHvLpEvent.xTargetLp = primaryLp; newElement->event.xHvLpEvent.xSizeMinus1 = sizeof(newElement->event)-1; newElement->event.xHvLpEvent.xRc = HvLpEvent_Rc_Good; newElement->event.xHvLpEvent.xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); newElement->event.xHvLpEvent.xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); return newElement;}static int signalVspInstruction( struct VspCmdData *vspCmd ){ struct StackElement * newElement = newStackElement(); int rc = 0; struct VspRspData response; DECLARE_MUTEX_LOCKED(Semaphore); response.xSemaphore = &Semaphore; response.xResponse = vspCmd; if ( newElement == NULL ) rc = -ENOMEM; else { newElement->event.xHvLpEvent.xSubtype = 6; newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0); newElement->event.xUnion.xVspCmd.xTokenUnion.ptr = &response; newElement->event.xUnion.xVspCmd.xCmd = vspCmd->xCmd; newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex(); newElement->event.xUnion.xVspCmd.xRc = 0xFF; newElement->event.xUnion.xVspCmd.xReserved1 = 0; memcpy(&(newElement->event.xUnion.xVspCmd.xSubData),&(vspCmd->xSubData), sizeof(vspCmd->xSubData)); mb(); rc = signalEvent(newElement); } if (rc == 0) { down(&Semaphore); } return rc;}/* * Send a 12-byte CE message to the primary partition VSP object */static int signalCEMsg( char * ceMsg, void * token ){ struct StackElement * newElement = newStackElement(); int rc = 0; if ( newElement == NULL ) rc = -ENOMEM; else { newElement->event.xHvLpEvent.xSubtype = 0; newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); newElement->event.xUnion.xCEMsgData.xToken = token; rc = signalEvent(newElement); } return rc;}/* * Send a 12-byte CE message and DMA data to the primary partition VSP object */static int dmaAndSignalCEMsg( char * ceMsg, void * token, void * dmaData, unsigned dmaDataLength, unsigned remoteAddress ){ struct StackElement * newElement = newStackElement(); int rc = 0; if ( newElement == NULL ) rc = -ENOMEM; else { newElement->event.xHvLpEvent.xSubtype = 0; newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); newElement->event.xUnion.xCEMsgData.xToken = token; memcpy( newElement->dmaData, dmaData, dmaDataLength ); newElement->dmaDataLength = dmaDataLength; newElement->remoteAddress = remoteAddress; rc = signalEvent(newElement); } return rc;}/* * Initiate a nice (hopefully) shutdown of Linux. We simply are * going to try and send the init process a SIGINT signal. If * this fails (why?), we'll simply force it off in a not-so-nice * manner. */static int shutdown( void ){ int rc = kill_proc(1,SIGINT,1); if ( rc ) { printk( KERN_ALERT "mf.c: SIGINT to init failed (%d), hard shutdown commencing\n", rc ); mf_powerOff(); } else printk( KERN_INFO "mf.c: init has been successfully notified to proceed with shutdown\n" ); return rc;}/* * The primary partition VSP object is sending us a new * event flow. Handle it... */static void intReceived( struct IoMFLpEvent * event ){ int freeIt = 0; struct StackElement * two = NULL; /* ack the interrupt */ event->xHvLpEvent.xRc = HvLpEvent_Rc_Good; HvCallEvent_ackLpEvent( &event->xHvLpEvent ); /* process interrupt */ switch( event->xHvLpEvent.xSubtype ) { case 0: /* CE message */ switch( event->xUnion.xCEMsgData.xCEMsg[3] ) { case 0x5B: /* power control notification */ if ( (event->xUnion.xCEMsgData.xCEMsg[5]&0x20) != 0 ) { printk( KERN_INFO "mf.c: Commencing partition shutdown\n" ); if ( shutdown() == 0 ) signalCEMsg( "\x00\x00\x00\xDB\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); } break; case 0xC0: /* get time */ { if ( (head != NULL) && ( head->event.xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) ) { freeIt = 1; if ( head->event.xUnion.xCEMsgData.xToken != 0 ) { CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; void * token = head->event.xUnion.xCEMsgData.xToken->xToken; if (xHdlr != NULL) (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); } } } break; } /* remove from queue */ if ( freeIt == 1 ) { unsigned long flags; spin_lock_irqsave( &spinlock, flags ); if ( head != NULL ) { struct StackElement *oldHead = head; head = head->next; two = head; free( oldHead ); } spin_unlock_irqrestore( &spinlock, flags ); } /* send next waiting event */ if ( two != NULL ) signalEvent( NULL ); break; case 1: /* IT sys shutdown */ printk( KERN_INFO "mf.c: Commencing system shutdown\n" ); shutdown(); break; }}/* * The primary partition VSP object is acknowledging the receipt * of a flow we sent to them. If there are other flows queued * up, we must send another one now... */static void ackReceived( struct IoMFLpEvent * event ){ unsigned long flags; struct StackElement * two = NULL; unsigned long freeIt = 0; /* handle current event */ if ( head != NULL ) { switch( event->xHvLpEvent.xSubtype ) { case 0: /* CE msg */ if ( event->xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) { if ( event->xUnion.xCEMsgData.xCEMsg[2] != 0 ) { freeIt = 1; if ( head->event.xUnion.xCEMsgData.xToken != 0 ) { CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; void * token = head->event.xUnion.xCEMsgData.xToken->xToken; if (xHdlr != NULL) (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); } } } else { freeIt = 1; } break; case 4: /* allocate */ case 5: /* deallocate */ if ( head->hdlr != NULL ) { union SafeCast mySafeCast; mySafeCast.ptrAsU64 = event->xHvLpEvent.xCorrelationToken; (*head->hdlr)( mySafeCast.ptr, event->xUnion.xAllocData.xCount ); } freeIt = 1; break; case 6: { struct VspRspData *rsp = (struct VspRspData *)event->xUnion.xVspCmd.xTokenUnion.ptr; if (rsp != NULL) { if (rsp->xResponse != NULL) memcpy(rsp->xResponse, &(event->xUnion.xVspCmd), sizeof(event->xUnion.xVspCmd)); if (rsp->xSemaphore != NULL) up(rsp->xSemaphore); } else { printk( KERN_ERR "mf.c: no rsp\n"); } freeIt = 1; } break; } } else printk( KERN_ERR "mf.c: stack empty for receiving ack\n" ); /* remove from queue */ spin_lock_irqsave( &spinlock, flags ); if (( head != NULL ) && ( freeIt == 1 )) { struct StackElement *oldHead = head; head = head->next; two = head; free( oldHead ); } spin_unlock_irqrestore( &spinlock, flags ); /* send next waiting event */ if ( two != NULL ) signalEvent( NULL );}/* * This is the generic event handler we are registering with * the Hypervisor. Ensure the flows are for us, and then * parse it enough to know if it is an interrupt or an * acknowledge. */static void hvHandler( struct HvLpEvent * event, struct pt_regs * regs ){ if ( (event != NULL) && (event->xType == HvLpEvent_Type_MachineFac) ) { switch( event->xFlags.xFunction ) { case HvLpEvent_Function_Ack: ackReceived( (struct IoMFLpEvent *)event ); break; case HvLpEvent_Function_Int: intReceived( (struct IoMFLpEvent *)event ); break; default: printk( KERN_ERR "mf.c: non ack/int event received\n" ); break; } } else printk( KERN_ERR "mf.c: alien event received\n" );}/* * Global kernel interface to allocate and seed events into the * Hypervisor. */void mf_allocateLpEvents( HvLpIndex targetLp, HvLpEvent_Type type,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -