⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mf.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*  * 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 <linux/module.h>#include <linux/completion.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/uaccess.h>#include <linux/dma-mapping.h>#include <linux/bcd.h>#include <asm/iSeries/vio.h>/* * This is the structure layout for the Machine Facilites LPAR event * flows. */union safe_cast {	u64 ptr_as_u64;	void *ptr;};struct VspCmdData {	union safe_cast token;	u16 cmd;	HvLpIndex lp_index;	u8 result_code;	u32 reserved;	union {		u64 state;	/* GetStateOut */		u64 ipl_type;	/* GetIplTypeOut, Function02SelectIplTypeIn */		u64 ipl_mode;	/* GetIplModeOut, Function02SelectIplModeIn */		u64 page[4];	/* GetSrcHistoryIn */		u64 flag;	/* GetAutoIplWhenPrimaryIplsOut,				   SetAutoIplWhenPrimaryIplsIn,				   WhiteButtonPowerOffIn,				   Function08FastPowerOffIn,				   IsSpcnRackPowerIncompleteOut */		struct {			u64 token;			u64 address_type;			u64 side;			u32 length;			u32 offset;		} kern;		/* SetKernelImageIn, GetKernelImageIn,				   SetKernelCmdLineIn, GetKernelCmdLineIn */		u32 length_out;	/* GetKernelImageOut, GetKernelCmdLineOut */		u8 reserved[80];	} sub_data;};struct VspRspData {	struct completion com;	struct VspCmdData *response;};struct AllocData {	u16 size;	u16 type;	u32 count;	u16 reserved1;	u8 reserved2;	HvLpIndex target_lp;};struct CeMsgData;typedef void (*CeMsgCompleteHandler)(void *token, struct CeMsgData *vspCmdRsp);struct CeMsgCompleteData {	CeMsgCompleteHandler handler;	void *token;};struct CeMsgData {	u8 ce_msg[12];	char reserved[4];	struct CeMsgCompleteData *completion;};struct IoMFLpEvent {	struct HvLpEvent hp_lp_event;	u16 subtype_result_code;	u16 reserved1;	u32 reserved2;	union {		struct AllocData alloc;		struct CeMsgData ce_msg;		struct VspCmdData vsp_cmd;	} data;};#define subtype_data(a, b, c, d)	\		(((a) << 24) + ((b) << 16) + ((c) << 8) + (d))/* * 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 pending events so that we can operate very early in * the boot up sequence (before kmalloc is ready). */struct pending_event {	struct pending_event *next;	struct IoMFLpEvent event;	MFCompleteHandler hdlr;	char dma_data[72];	unsigned dma_data_length;	unsigned remote_address;};static spinlock_t pending_event_spinlock;static struct pending_event *pending_event_head;static struct pending_event *pending_event_tail;static struct pending_event *pending_event_avail;static struct pending_event pending_event_prealloc[16];/* * Put a pending event onto the available queue, so it can get reused. * Attention! You must have the pending_event_spinlock before calling! */static void free_pending_event(struct pending_event *ev){	if (ev != NULL) {		ev->next = pending_event_avail;		pending_event_avail = ev;	}}/* * 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 pending_event_spinlock. * This is OK, because we know that nobody else will be modifying * the first pointer when we do this. */static int signal_event(struct pending_event *ev){	int rc = 0;	unsigned long flags;	int go = 1;	struct pending_event *ev1;	HvLpEvent_Rc hvRc;	/* enqueue the event */	if (ev != NULL) {		ev->next = NULL;		spin_lock_irqsave(&pending_event_spinlock, flags);		if (pending_event_head == NULL)			pending_event_head = ev;		else {			go = 0;			pending_event_tail->next = ev;		}		pending_event_tail = ev;		spin_unlock_irqrestore(&pending_event_spinlock, flags);	}	/* send the event */	while (go) {		go = 0;		/* any DMA data to send beforehand? */		if (pending_event_head->dma_data_length > 0)			HvCallEvent_dmaToSp(pending_event_head->dma_data,					pending_event_head->remote_address,					pending_event_head->dma_data_length,					HvLpDma_Direction_LocalToRemote);		hvRc = HvCallEvent_signalLpEvent(				&pending_event_head->event.hp_lp_event);		if (hvRc != HvLpEvent_Rc_Good) {			printk(KERN_ERR "mf.c: HvCallEvent_signalLpEvent() failed with %d\n",					(int)hvRc);			spin_lock_irqsave(&pending_event_spinlock, flags);			ev1 = pending_event_head;			pending_event_head = pending_event_head->next;			if (pending_event_head != NULL)				go = 1;			spin_unlock_irqrestore(&pending_event_spinlock, flags);			if (ev1 == ev)				rc = -EIO;			else if (ev1->hdlr != NULL) {				union safe_cast mySafeCast;				mySafeCast.ptr_as_u64 = ev1->event.hp_lp_event.xCorrelationToken;				(*ev1->hdlr)(mySafeCast.ptr, -EIO);			}			spin_lock_irqsave(&pending_event_spinlock, flags);			free_pending_event(ev1);			spin_unlock_irqrestore(&pending_event_spinlock, flags);		}	}	return rc;}/* * Allocate a new pending_event structure, and initialize it. */static struct pending_event *new_pending_event(void){	struct pending_event *ev = NULL;	HvLpIndex primaryLp = HvLpConfig_getPrimaryLpIndex();	unsigned long flags;	struct HvLpEvent *hev;	spin_lock_irqsave(&pending_event_spinlock, flags);	if (pending_event_avail != NULL) {		ev = pending_event_avail;		pending_event_avail = pending_event_avail->next;	}	spin_unlock_irqrestore(&pending_event_spinlock, flags);	if (ev == NULL)		ev = kmalloc(sizeof(struct pending_event),GFP_ATOMIC);	if (ev == NULL) {		printk(KERN_ERR "mf.c: unable to kmalloc %ld bytes\n",				sizeof(struct pending_event));		return NULL;	}	memset(ev, 0, sizeof(struct pending_event));	hev = &ev->event.hp_lp_event;	hev->xFlags.xValid = 1;	hev->xFlags.xAckType = HvLpEvent_AckType_ImmediateAck;	hev->xFlags.xAckInd = HvLpEvent_AckInd_DoAck;	hev->xFlags.xFunction = HvLpEvent_Function_Int;	hev->xType = HvLpEvent_Type_MachineFac;	hev->xSourceLp = HvLpConfig_getLpIndex();	hev->xTargetLp = primaryLp;	hev->xSizeMinus1 = sizeof(ev->event)-1;	hev->xRc = HvLpEvent_Rc_Good;	hev->xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primaryLp,			HvLpEvent_Type_MachineFac);	hev->xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primaryLp,			HvLpEvent_Type_MachineFac);	return ev;}static int signal_vsp_instruction(struct VspCmdData *vspCmd){	struct pending_event *ev = new_pending_event();	int rc;	struct VspRspData response;	if (ev == NULL)		return -ENOMEM;	init_completion(&response.com);	response.response = vspCmd;	ev->event.hp_lp_event.xSubtype = 6;	ev->event.hp_lp_event.x.xSubtypeData =		subtype_data('M', 'F',  'V',  'I');	ev->event.data.vsp_cmd.token.ptr = &response;	ev->event.data.vsp_cmd.cmd = vspCmd->cmd;	ev->event.data.vsp_cmd.lp_index = HvLpConfig_getLpIndex();	ev->event.data.vsp_cmd.result_code = 0xFF;	ev->event.data.vsp_cmd.reserved = 0;	memcpy(&(ev->event.data.vsp_cmd.sub_data),			&(vspCmd->sub_data), sizeof(vspCmd->sub_data));	mb();	rc = signal_event(ev);	if (rc == 0)		wait_for_completion(&response.com);	return rc;}/* * Send a 12-byte CE message to the primary partition VSP object */static int signal_ce_msg(char *ce_msg, struct CeMsgCompleteData *completion){	struct pending_event *ev = new_pending_event();	if (ev == NULL)		return -ENOMEM;	ev->event.hp_lp_event.xSubtype = 0;	ev->event.hp_lp_event.x.xSubtypeData =		subtype_data('M',  'F',  'C',  'E');	memcpy(ev->event.data.ce_msg.ce_msg, ce_msg, 12);	ev->event.data.ce_msg.completion = completion;	return signal_event(ev);}/* * Send a 12-byte CE message and DMA data to the primary partition VSP object */static int dma_and_signal_ce_msg(char *ce_msg,		struct CeMsgCompleteData *completion, void *dma_data,		unsigned dma_data_length, unsigned remote_address){	struct pending_event *ev = new_pending_event();	if (ev == NULL)		return -ENOMEM;	ev->event.hp_lp_event.xSubtype = 0;	ev->event.hp_lp_event.x.xSubtypeData =		subtype_data('M', 'F', 'C', 'E');	memcpy(ev->event.data.ce_msg.ce_msg, ce_msg, 12);	ev->event.data.ce_msg.completion = completion;	memcpy(ev->dma_data, dma_data, dma_data_length);	ev->dma_data_length = dma_data_length;	ev->remote_address = remote_address;	return signal_event(ev);}/* * 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 pending_event *two = NULL;	/* ack the interrupt */	event->hp_lp_event.xRc = HvLpEvent_Rc_Good;	HvCallEvent_ackLpEvent(&event->hp_lp_event);	/* process interrupt */	switch (event->hp_lp_event.xSubtype) {	case 0:	/* CE message */		switch (event->data.ce_msg.ce_msg[3]) {		case 0x5B:	/* power control notification */			if ((event->data.ce_msg.ce_msg[5] & 0x20) != 0) {				printk(KERN_INFO "mf.c: Commencing partition shutdown\n");				if (shutdown() == 0)					signal_ce_msg("\x00\x00\x00\xDB\x00\x00\x00\x00\x00\x00\x00\x00", NULL);			}			break;		case 0xC0:	/* get time */			if ((pending_event_head == NULL) ||			    (pending_event_head->event.data.ce_msg.ce_msg[3]			     != 0x40))				break;			freeIt = 1;			if (pending_event_head->event.data.ce_msg.completion != 0) {				CeMsgCompleteHandler handler = pending_event_head->event.data.ce_msg.completion->handler;				void *token = pending_event_head->event.data.ce_msg.completion->token;				if (handler != NULL)					(*handler)(token, &(event->data.ce_msg));			}			break;		}		/* remove from queue */		if (freeIt == 1) {			unsigned long flags;			spin_lock_irqsave(&pending_event_spinlock, flags);			if (pending_event_head != NULL) {				struct pending_event *oldHead =					pending_event_head;				pending_event_head = pending_event_head->next;				two = pending_event_head;				free_pending_event(oldHead);			}			spin_unlock_irqrestore(&pending_event_spinlock, flags);		}		/* send next waiting event */		if (two != NULL)			signal_event(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 pending_event * two = NULL;	unsigned long freeIt = 0;	/* handle current event */	if (pending_event_head != NULL) {		switch (event->hp_lp_event.xSubtype) {		case 0:     /* CE msg */			if (event->data.ce_msg.ce_msg[3] == 0x40) {				if (event->data.ce_msg.ce_msg[2] != 0) {					freeIt = 1;					if (pending_event_head->event.data.ce_msg.completion							!= 0) {						CeMsgCompleteHandler handler = pending_event_head->event.data.ce_msg.completion->handler;						void *token = pending_event_head->event.data.ce_msg.completion->token;						if (handler != NULL)							(*handler)(token, &(event->data.ce_msg));					}				}			} else				freeIt = 1;			break;		case 4:	/* allocate */		case 5:	/* deallocate */			if (pending_event_head->hdlr != NULL) {				union safe_cast mySafeCast;				mySafeCast.ptr_as_u64 = event->hp_lp_event.xCorrelationToken;				(*pending_event_head->hdlr)(mySafeCast.ptr, event->data.alloc.count);			}			freeIt = 1;			break;		case 6:			{				struct VspRspData *rsp = (struct VspRspData *)event->data.vsp_cmd.token.ptr;				if (rsp != NULL) {					if (rsp->response != NULL)						memcpy(rsp->response, &(event->data.vsp_cmd), sizeof(event->data.vsp_cmd));					complete(&rsp->com);				} 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(&pending_event_spinlock, flags);	if ((pending_event_head != NULL) && (freeIt == 1)) {		struct pending_event *oldHead = pending_event_head;		pending_event_head = pending_event_head->next;		two = pending_event_head;		free_pending_event(oldHead);	} 	spin_unlock_irqrestore(&pending_event_spinlock, flags);	/* send next waiting event */	if (two != NULL)		signal_event(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,		unsigned size, unsigned count, MFCompleteHandler hdlr,		void *userToken){	struct pending_event *ev = new_pending_event();	int rc;

⌨️ 快捷键说明

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