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

📄 mf.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 3 页
字号:
/*  * mf.c  * Copyright (C) 2001 Troy D. Armstrong  IBM Corporation  * Copyright (C) 2004-2005 Stephen Rothwell  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 <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/completion.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/bcd.h>#include <asm/time.h>#include <asm/uaccess.h>#include <asm/paca.h>#include <asm/iSeries/vio.h>#include <asm/iSeries/mf.h>#include <asm/iSeries/HvLpConfig.h>#include <asm/iSeries/ItLpQueue.h>/* * This is the structure layout for the Machine Facilites LPAR event * flows. */struct vsp_cmd_data {	u64 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 vsp_rsp_data {	struct completion com;	struct vsp_cmd_data *response;};struct alloc_data {	u16 size;	u16 type;	u32 count;	u16 reserved1;	u8 reserved2;	HvLpIndex target_lp;};struct ce_msg_data;typedef void (*ce_msg_comp_hdlr)(void *token, struct ce_msg_data *vsp_cmd_rsp);struct ce_msg_comp_data {	ce_msg_comp_hdlr handler;	void *token;};struct ce_msg_data {	u8 ce_msg[12];	char reserved[4];	struct ce_msg_comp_data *completion;};struct io_mf_lp_event {	struct HvLpEvent hp_lp_event;	u16 subtype_result_code;	u16 reserved1;	u32 reserved2;	union {		struct alloc_data alloc;		struct ce_msg_data ce_msg;		struct vsp_cmd_data 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 io_mf_lp_event 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 hv_rc;	/* 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);		hv_rc = HvCallEvent_signalLpEvent(				&pending_event_head->event.hp_lp_event);		if (hv_rc != HvLpEvent_Rc_Good) {			printk(KERN_ERR "mf.c: HvCallEvent_signalLpEvent() "					"failed with %d\n", (int)hv_rc);			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)				(*ev1->hdlr)((void *)ev1->event.hp_lp_event.xCorrelationToken, -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 primary_lp = 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 = primary_lp;	hev->xSizeMinus1 = sizeof(ev->event) - 1;	hev->xRc = HvLpEvent_Rc_Good;	hev->xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primary_lp,			HvLpEvent_Type_MachineFac);	hev->xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primary_lp,			HvLpEvent_Type_MachineFac);	return ev;}static int signal_vsp_instruction(struct vsp_cmd_data *vsp_cmd){	struct pending_event *ev = new_pending_event();	int rc;	struct vsp_rsp_data response;	if (ev == NULL)		return -ENOMEM;	init_completion(&response.com);	response.response = vsp_cmd;	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 = (u64)&response;	ev->event.data.vsp_cmd.cmd = vsp_cmd->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),			&(vsp_cmd->sub_data), sizeof(vsp_cmd->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 ce_msg_comp_data *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 (with no data) to the primary partition VSP object */static int signal_ce_msg_simple(u8 ce_op, struct ce_msg_comp_data *completion){	u8 ce_msg[12];	memset(ce_msg, 0, sizeof(ce_msg));	ce_msg[3] = ce_op;	return signal_ce_msg(ce_msg, completion);}/* * 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 ce_msg_comp_data *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_power_off();	} 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 handle_int(struct io_mf_lp_event *event){	struct ce_msg_data *ce_msg_data;	struct ce_msg_data *pce_msg_data;	unsigned long flags;	struct pending_event *pev;	/* 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 */		ce_msg_data = &event->data.ce_msg;		switch (ce_msg_data->ce_msg[3]) {		case 0x5B:	/* power control notification */			if ((ce_msg_data->ce_msg[5] & 0x20) != 0) {				printk(KERN_INFO "mf.c: Commencing partition shutdown\n");				if (shutdown() == 0)					signal_ce_msg_simple(0xDB, NULL);			}			break;		case 0xC0:	/* get time */			spin_lock_irqsave(&pending_event_spinlock, flags);			pev = pending_event_head;			if (pev != NULL)				pending_event_head = pending_event_head->next;			spin_unlock_irqrestore(&pending_event_spinlock, flags);			if (pev == NULL)				break;			pce_msg_data = &pev->event.data.ce_msg;			if (pce_msg_data->ce_msg[3] != 0x40)				break;			if (pce_msg_data->completion != NULL) {				ce_msg_comp_hdlr handler =					pce_msg_data->completion->handler;				void *token = pce_msg_data->completion->token;				if (handler != NULL)					(*handler)(token, ce_msg_data);			}			spin_lock_irqsave(&pending_event_spinlock, flags);			free_pending_event(pev);			spin_unlock_irqrestore(&pending_event_spinlock, flags);			/* send next waiting event */			if (pending_event_head != NULL)				signal_event(NULL);			break;		}		break;	case 1:	/* IT sys shutdown */		printk(KERN_INFO "mf.c: Commencing system shutdown\n");		shutdown();		break;	}

⌨️ 快捷键说明

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