📄 mf.c
字号:
/* * 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 + -