1 /*
2 * mf.c
3 * Copyright (C) 2001 Troy D. Armstrong IBM Corporation
4 *
5 * This modules exists as an interface between a Linux secondary partition
6 * running on an iSeries and the primary partition's Virtual Service
7 * Processor (VSP) object. The VSP has final authority over powering on/off
8 * all partitions in the iSeries. It also provides miscellaneous low-level
9 * machine facility type operations.
10 *
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27 #include <asm/iSeries/mf.h>
28 #include <linux/types.h>
29 #include <linux/errno.h>
30 #include <linux/kernel.h>
31 #include <linux/init.h>
32 #include <linux/mm.h>
33 #include <asm/iSeries/HvLpConfig.h>
34 #include <linux/slab.h>
35 #include <linux/delay.h>
36 #include <asm/nvram.h>
37 #include <asm/time.h>
38 #include <asm/iSeries/ItSpCommArea.h>
39 #include <asm/iSeries/mf_proc.h>
40 #include <asm/iSeries/iSeries_proc.h>
41 #include <asm/uaccess.h>
42 #include <linux/pci.h>
43
44 extern struct pci_dev * iSeries_vio_dev;
45
46 /*
47 * This is the structure layout for the Machine Facilities LPAR event
48 * flows.
49 */
50 struct VspCmdData;
51 struct CeMsgData;
52 union SafeCast
53 {
54 u64 ptrAsU64;
55 void *ptr;
56 };
57
58
59 typedef void (*CeMsgCompleteHandler)( void *token, struct CeMsgData *vspCmdRsp );
60
61 struct CeMsgCompleteData
62 {
63 CeMsgCompleteHandler xHdlr;
64 void *xToken;
65 };
66
67 struct VspRspData
68 {
69 struct semaphore *xSemaphore;
70 struct VspCmdData *xResponse;
71 };
72
73 struct IoMFLpEvent
74 {
75 struct HvLpEvent xHvLpEvent;
76
77 u16 xSubtypeRc;
78 u16 xRsvd1;
79 u32 xRsvd2;
80
81 union
82 {
83
84 struct AllocData
85 {
86 u16 xSize;
87 u16 xType;
88 u32 xCount;
89 u16 xRsvd3;
90 u8 xRsvd4;
91 HvLpIndex xTargetLp;
92 } xAllocData;
93
94 struct CeMsgData
95 {
96 u8 xCEMsg[12];
97 char xReserved[4];
98 struct CeMsgCompleteData *xToken;
99 } xCEMsgData;
100
101 struct VspCmdData
102 {
103 union SafeCast xTokenUnion;
104 u16 xCmd;
105 HvLpIndex xLpIndex;
106 u8 xRc;
107 u32 xReserved1;
108
109 union VspCmdSubData
110 {
111 struct
112 {
113 u64 xState;
114 } xGetStateOut;
115
116 struct
117 {
118 u64 xIplType;
119 } xGetIplTypeOut, xFunction02SelectIplTypeIn;
120
121 struct
122 {
123 u64 xIplMode;
124 } xGetIplModeOut, xFunction02SelectIplModeIn;
125
126 struct
127 {
128 u64 xPage[4];
129 } xGetSrcHistoryIn;
130
131 struct
132 {
133 u64 xFlag;
134 } xGetAutoIplWhenPrimaryIplsOut,
135 xSetAutoIplWhenPrimaryIplsIn,
136 xWhiteButtonPowerOffIn,
137 xFunction08FastPowerOffIn,
138 xIsSpcnRackPowerIncompleteOut;
139
140 struct
141 {
142 u64 xToken;
143 u64 xAddressType;
144 u64 xSide;
145 u32 xTransferLength;
146 u32 xOffset;
147 } xSetKernelImageIn,
148 xGetKernelImageIn,
149 xSetKernelCmdLineIn,
150 xGetKernelCmdLineIn;
151
152 struct
153 {
154 u32 xTransferLength;
155 } xGetKernelImageOut,xGetKernelCmdLineOut;
156
157
158 u8 xReserved2[80];
159
160 } xSubData;
161 } xVspCmd;
162 } xUnion;
163 };
164
165
166 /*
167 * All outgoing event traffic is kept on a FIFO queue. The first
168 * pointer points to the one that is outstanding, and all new
169 * requests get stuck on the end. Also, we keep a certain number of
170 * preallocated stack elements so that we can operate very early in
171 * the boot up sequence (before kmalloc is ready).
172 */
173 struct StackElement
174 {
175 struct StackElement * next;
176 struct IoMFLpEvent event;
177 MFCompleteHandler hdlr;
178 char dmaData[72];
179 unsigned dmaDataLength;
180 unsigned remoteAddress;
181 };
182 static spinlock_t spinlock;
183 static struct StackElement * head = NULL;
184 static struct StackElement * tail = NULL;
185 static struct StackElement * avail = NULL;
186 static struct StackElement prealloc[16];
187
188 /*
189 * Put a stack element onto the available queue, so it can get reused.
190 * Attention! You must have the spinlock before calling!
191 */
free(struct StackElement * element)192 void free( struct StackElement * element )
193 {
194 if ( element != NULL )
195 {
196 element->next = avail;
197 avail = element;
198 }
199 }
200
201 /*
202 * Enqueue the outbound event onto the stack. If the queue was
203 * empty to begin with, we must also issue it via the Hypervisor
204 * interface. There is a section of code below that will touch
205 * the first stack pointer without the protection of the spinlock.
206 * This is OK, because we know that nobody else will be modifying
207 * the first pointer when we do this.
208 */
signalEvent(struct StackElement * newElement)209 static int signalEvent( struct StackElement * newElement )
210 {
211 int rc = 0;
212 unsigned long flags;
213 int go = 1;
214 struct StackElement * element;
215 HvLpEvent_Rc hvRc;
216
217 /* enqueue the event */
218 if ( newElement != NULL )
219 {
220 spin_lock_irqsave( &spinlock, flags );
221 if ( head == NULL )
222 head = newElement;
223 else {
224 go = 0;
225 tail->next = newElement;
226 }
227 newElement->next = NULL;
228 tail = newElement;
229 spin_unlock_irqrestore( &spinlock, flags );
230 }
231
232 /* send the event */
233 while ( go )
234 {
235 go = 0;
236
237 /* any DMA data to send beforehand? */
238 if ( head->dmaDataLength > 0 )
239 HvCallEvent_dmaToSp( head->dmaData, head->remoteAddress, head->dmaDataLength, HvLpDma_Direction_LocalToRemote );
240
241 hvRc = HvCallEvent_signalLpEvent(&head->event.xHvLpEvent);
242 if ( hvRc != HvLpEvent_Rc_Good )
243 {
244 printk( KERN_ERR "mf.c: HvCallEvent_signalLpEvent() failed with %d\n", (int)hvRc );
245
246 spin_lock_irqsave( &spinlock, flags );
247 element = head;
248 head = head->next;
249 if ( head != NULL )
250 go = 1;
251 spin_unlock_irqrestore( &spinlock, flags );
252
253 if ( element == newElement )
254 rc = -EIO;
255 else {
256 if ( element->hdlr != NULL )
257 {
258 union SafeCast mySafeCast;
259 mySafeCast.ptrAsU64 = element->event.xHvLpEvent.xCorrelationToken;
260 (*element->hdlr)( mySafeCast.ptr, -EIO );
261 }
262 }
263
264 spin_lock_irqsave( &spinlock, flags );
265 free( element );
266 spin_unlock_irqrestore( &spinlock, flags );
267 }
268 }
269
270 return rc;
271 }
272
273 /*
274 * Allocate a new StackElement structure, and initialize it.
275 */
newStackElement(void)276 static struct StackElement * newStackElement( void )
277 {
278 struct StackElement * newElement = NULL;
279 HvLpIndex primaryLp = HvLpConfig_getPrimaryLpIndex();
280 unsigned long flags;
281
282 if ( newElement == NULL )
283 {
284 spin_lock_irqsave( &spinlock, flags );
285 if ( avail != NULL )
286 {
287 newElement = avail;
288 avail = avail->next;
289 }
290 spin_unlock_irqrestore( &spinlock, flags );
291 }
292
293 if ( newElement == NULL )
294 newElement = kmalloc(sizeof(struct StackElement),GFP_ATOMIC);
295
296 if ( newElement == NULL )
297 {
298 printk( KERN_ERR "mf.c: unable to kmalloc %ld bytes\n", sizeof(struct StackElement) );
299 return NULL;
300 }
301
302 memset( newElement, 0, sizeof(struct StackElement) );
303 newElement->event.xHvLpEvent.xFlags.xValid = 1;
304 newElement->event.xHvLpEvent.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck;
305 newElement->event.xHvLpEvent.xFlags.xAckInd = HvLpEvent_AckInd_DoAck;
306 newElement->event.xHvLpEvent.xFlags.xFunction = HvLpEvent_Function_Int;
307 newElement->event.xHvLpEvent.xType = HvLpEvent_Type_MachineFac;
308 newElement->event.xHvLpEvent.xSourceLp = HvLpConfig_getLpIndex();
309 newElement->event.xHvLpEvent.xTargetLp = primaryLp;
310 newElement->event.xHvLpEvent.xSizeMinus1 = sizeof(newElement->event)-1;
311 newElement->event.xHvLpEvent.xRc = HvLpEvent_Rc_Good;
312 newElement->event.xHvLpEvent.xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac);
313 newElement->event.xHvLpEvent.xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac);
314
315 return newElement;
316 }
317
signalVspInstruction(struct VspCmdData * vspCmd)318 static int signalVspInstruction( struct VspCmdData *vspCmd )
319 {
320 struct StackElement * newElement = newStackElement();
321 int rc = 0;
322 struct VspRspData response;
323 DECLARE_MUTEX_LOCKED(Semaphore);
324 response.xSemaphore = &Semaphore;
325 response.xResponse = vspCmd;
326
327 if ( newElement == NULL )
328 rc = -ENOMEM;
329 else {
330 newElement->event.xHvLpEvent.xSubtype = 6;
331 newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0);
332 newElement->event.xUnion.xVspCmd.xTokenUnion.ptr = &response;
333 newElement->event.xUnion.xVspCmd.xCmd = vspCmd->xCmd;
334 newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex();
335 newElement->event.xUnion.xVspCmd.xRc = 0xFF;
336 newElement->event.xUnion.xVspCmd.xReserved1 = 0;
337 memcpy(&(newElement->event.xUnion.xVspCmd.xSubData),&(vspCmd->xSubData), sizeof(vspCmd->xSubData));
338 mb();
339
340 rc = signalEvent(newElement);
341 }
342
343 if (rc == 0)
344 {
345 down(&Semaphore);
346 }
347
348 return rc;
349 }
350
351
352 /*
353 * Send a 12-byte CE message to the primary partition VSP object
354 */
signalCEMsg(char * ceMsg,void * token)355 static int signalCEMsg( char * ceMsg, void * token )
356 {
357 struct StackElement * newElement = newStackElement();
358 int rc = 0;
359
360 if ( newElement == NULL )
361 rc = -ENOMEM;
362 else {
363 newElement->event.xHvLpEvent.xSubtype = 0;
364 newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0);
365 memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 );
366 newElement->event.xUnion.xCEMsgData.xToken = token;
367 rc = signalEvent(newElement);
368 }
369
370 return rc;
371 }
372
373 /*
374 * Send a 12-byte CE message and DMA data to the primary partition VSP object
375 */
dmaAndSignalCEMsg(char * ceMsg,void * token,void * dmaData,unsigned dmaDataLength,unsigned remoteAddress)376 static int dmaAndSignalCEMsg( char * ceMsg, void * token, void * dmaData, unsigned dmaDataLength, unsigned remoteAddress )
377 {
378 struct StackElement * newElement = newStackElement();
379 int rc = 0;
380
381 if ( newElement == NULL )
382 rc = -ENOMEM;
383 else {
384 newElement->event.xHvLpEvent.xSubtype = 0;
385 newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0);
386 memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 );
387 newElement->event.xUnion.xCEMsgData.xToken = token;
388 memcpy( newElement->dmaData, dmaData, dmaDataLength );
389 newElement->dmaDataLength = dmaDataLength;
390 newElement->remoteAddress = remoteAddress;
391 rc = signalEvent(newElement);
392 }
393
394 return rc;
395 }
396
397 /*
398 * Initiate a nice (hopefully) shutdown of Linux. We simply are
399 * going to try and send the init process a SIGINT signal. If
400 * this fails (why?), we'll simply force it off in a not-so-nice
401 * manner.
402 */
shutdown(void)403 static int shutdown( void )
404 {
405 extern int cad_pid; /* from kernel/sys.c */
406 int rc = kill_proc(cad_pid,SIGINT,1);
407
408 if ( rc )
409 {
410 printk( KERN_ALERT "mf.c: SIGINT to init failed (%d), hard shutdown commencing\n", rc );
411 mf_powerOff();
412 }
413 else
414 printk( KERN_INFO "mf.c: init has been successfully notified to proceed with shutdown\n" );
415
416 return rc;
417 }
418
419 /*
420 * The primary partition VSP object is sending us a new
421 * event flow. Handle it...
422 */
intReceived(struct IoMFLpEvent * event)423 static void intReceived( struct IoMFLpEvent * event )
424 {
425 int freeIt = 0;
426 struct StackElement * two = NULL;
427 /* ack the interrupt */
428 event->xHvLpEvent.xRc = HvLpEvent_Rc_Good;
429 HvCallEvent_ackLpEvent( &event->xHvLpEvent );
430
431 /* process interrupt */
432 switch( event->xHvLpEvent.xSubtype )
433 {
434 case 0: /* CE message */
435 switch( event->xUnion.xCEMsgData.xCEMsg[3] )
436 {
437 case 0x5B: /* power control notification */
438 if ( (event->xUnion.xCEMsgData.xCEMsg[5]&0x20) != 0 )
439 {
440 printk( KERN_INFO "mf.c: Commencing partition shutdown\n" );
441 if ( shutdown() == 0 )
442 signalCEMsg( "\x00\x00\x00\xDB\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
443 }
444 break;
445 case 0xC0: /* get time */
446 {
447 if ( (head != NULL) && ( head->event.xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) )
448 {
449 freeIt = 1;
450 if ( head->event.xUnion.xCEMsgData.xToken != 0 )
451 {
452 CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr;
453 void * token = head->event.xUnion.xCEMsgData.xToken->xToken;
454
455 if (xHdlr != NULL)
456 (*xHdlr)( token, &(event->xUnion.xCEMsgData) );
457 }
458 }
459 }
460 break;
461 }
462
463 /* remove from queue */
464 if ( freeIt == 1 )
465 {
466 unsigned long flags;
467 spin_lock_irqsave( &spinlock, flags );
468 if ( head != NULL )
469 {
470 struct StackElement *oldHead = head;
471 head = head->next;
472 two = head;
473 free( oldHead );
474 }
475 spin_unlock_irqrestore( &spinlock, flags );
476 }
477
478 /* send next waiting event */
479 if ( two != NULL )
480 signalEvent( NULL );
481 break;
482 case 1: /* IT sys shutdown */
483 printk( KERN_INFO "mf.c: Commencing system shutdown\n" );
484 shutdown();
485 break;
486 }
487 }
488
489 /*
490 * The primary partition VSP object is acknowledging the receipt
491 * of a flow we sent to them. If there are other flows queued
492 * up, we must send another one now...
493 */
ackReceived(struct IoMFLpEvent * event)494 static void ackReceived( struct IoMFLpEvent * event )
495 {
496 unsigned long flags;
497 struct StackElement * two = NULL;
498 unsigned long freeIt = 0;
499
500 /* handle current event */
501 if ( head != NULL )
502 {
503 switch( event->xHvLpEvent.xSubtype )
504 {
505 case 0: /* CE msg */
506 if ( event->xUnion.xCEMsgData.xCEMsg[3] == 0x40 )
507 {
508 if ( event->xUnion.xCEMsgData.xCEMsg[2] != 0 )
509 {
510 freeIt = 1;
511 if ( head->event.xUnion.xCEMsgData.xToken != 0 )
512 {
513 CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr;
514 void * token = head->event.xUnion.xCEMsgData.xToken->xToken;
515
516 if (xHdlr != NULL)
517 (*xHdlr)( token, &(event->xUnion.xCEMsgData) );
518 }
519 }
520 } else {
521 freeIt = 1;
522 }
523 break;
524 case 4: /* allocate */
525 case 5: /* deallocate */
526 if ( head->hdlr != NULL )
527 {
528 union SafeCast mySafeCast;
529 mySafeCast.ptrAsU64 = event->xHvLpEvent.xCorrelationToken;
530 (*head->hdlr)( mySafeCast.ptr, event->xUnion.xAllocData.xCount );
531 }
532 freeIt = 1;
533 break;
534 case 6:
535 {
536 struct VspRspData *rsp = (struct VspRspData *)event->xUnion.xVspCmd.xTokenUnion.ptr;
537
538 if (rsp != NULL)
539 {
540 if (rsp->xResponse != NULL)
541 memcpy(rsp->xResponse, &(event->xUnion.xVspCmd), sizeof(event->xUnion.xVspCmd));
542 if (rsp->xSemaphore != NULL)
543 up(rsp->xSemaphore);
544 } else {
545 printk( KERN_ERR "mf.c: no rsp\n");
546 }
547 freeIt = 1;
548 }
549 break;
550 }
551 }
552 else
553 printk( KERN_ERR "mf.c: stack empty for receiving ack\n" );
554
555 /* remove from queue */
556 spin_lock_irqsave( &spinlock, flags );
557 if (( head != NULL ) && ( freeIt == 1 ))
558 {
559 struct StackElement *oldHead = head;
560 head = head->next;
561 two = head;
562 free( oldHead );
563 }
564 spin_unlock_irqrestore( &spinlock, flags );
565
566 /* send next waiting event */
567 if ( two != NULL )
568 signalEvent( NULL );
569 }
570
571 /*
572 * This is the generic event handler we are registering with
573 * the Hypervisor. Ensure the flows are for us, and then
574 * parse it enough to know if it is an interrupt or an
575 * acknowledge.
576 */
hvHandler(struct HvLpEvent * event,struct pt_regs * regs)577 static void hvHandler( struct HvLpEvent * event, struct pt_regs * regs )
578 {
579 if ( (event != NULL) && (event->xType == HvLpEvent_Type_MachineFac) )
580 {
581 switch( event->xFlags.xFunction )
582 {
583 case HvLpEvent_Function_Ack:
584 ackReceived( (struct IoMFLpEvent *)event );
585 break;
586 case HvLpEvent_Function_Int:
587 intReceived( (struct IoMFLpEvent *)event );
588 break;
589 default:
590 printk( KERN_ERR "mf.c: non ack/int event received\n" );
591 break;
592 }
593 }
594 else
595 printk( KERN_ERR "mf.c: alien event received\n" );
596 }
597
598 /*
599 * Global kernel interface to allocate and seed events into the
600 * Hypervisor.
601 */
mf_allocateLpEvents(HvLpIndex targetLp,HvLpEvent_Type type,unsigned size,unsigned count,MFCompleteHandler hdlr,void * userToken)602 void mf_allocateLpEvents( HvLpIndex targetLp,
603 HvLpEvent_Type type,
604 unsigned size,
605 unsigned count,
606 MFCompleteHandler hdlr,
607 void * userToken )
608 {
609 struct StackElement * newElement = newStackElement();
610 int rc = 0;
611
612 if ( newElement == NULL )
613 rc = -ENOMEM;
614 else {
615 union SafeCast mine;
616 mine.ptr = userToken;
617 newElement->event.xHvLpEvent.xSubtype = 4;
618 newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64;
619 newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('A'<<0);
620 newElement->event.xUnion.xAllocData.xTargetLp = targetLp;
621 newElement->event.xUnion.xAllocData.xType = type;
622 newElement->event.xUnion.xAllocData.xSize = size;
623 newElement->event.xUnion.xAllocData.xCount = count;
624 newElement->hdlr = hdlr;
625 rc = signalEvent(newElement);
626 }
627
628 if ( (rc != 0) && (hdlr != NULL) )
629 (*hdlr)( userToken, rc );
630 }
631
632 /*
633 * Global kernel interface to unseed and deallocate events already in
634 * Hypervisor.
635 */
mf_deallocateLpEvents(HvLpIndex targetLp,HvLpEvent_Type type,unsigned count,MFCompleteHandler hdlr,void * userToken)636 void mf_deallocateLpEvents( HvLpIndex targetLp,
637 HvLpEvent_Type type,
638 unsigned count,
639 MFCompleteHandler hdlr,
640 void * userToken )
641 {
642 struct StackElement * newElement = newStackElement();
643 int rc = 0;
644
645 if ( newElement == NULL )
646 rc = -ENOMEM;
647 else {
648 union SafeCast mine;
649 mine.ptr = userToken;
650 newElement->event.xHvLpEvent.xSubtype = 5;
651 newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64;
652 newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('D'<<0);
653 newElement->event.xUnion.xAllocData.xTargetLp = targetLp;
654 newElement->event.xUnion.xAllocData.xType = type;
655 newElement->event.xUnion.xAllocData.xCount = count;
656 newElement->hdlr = hdlr;
657 rc = signalEvent(newElement);
658 }
659
660 if ( (rc != 0) && (hdlr != NULL) )
661 (*hdlr)( userToken, rc );
662 }
663
664 /*
665 * Global kernel interface to tell the VSP object in the primary
666 * partition to power this partition off.
667 */
mf_powerOff(void)668 void mf_powerOff( void )
669 {
670 printk( KERN_INFO "mf.c: Down it goes...\n" );
671 signalCEMsg( "\x00\x00\x00\x4D\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
672 for (;;);
673 }
674
675 /*
676 * Global kernel interface to tell the VSP object in the primary
677 * partition to reboot this partition.
678 */
mf_reboot(void)679 void mf_reboot( void )
680 {
681 printk( KERN_INFO "mf.c: Preparing to bounce...\n" );
682 signalCEMsg( "\x00\x00\x00\x4E\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
683 for (;;);
684 }
685
686 /*
687 * Display a single word SRC onto the VSP control panel.
688 */
mf_displaySrc(u32 word)689 void mf_displaySrc( u32 word )
690 {
691 u8 ce[12];
692
693 memcpy( ce, "\x00\x00\x00\x4A\x00\x00\x00\x01\x00\x00\x00\x00", 12 );
694 ce[8] = word>>24;
695 ce[9] = word>>16;
696 ce[10] = word>>8;
697 ce[11] = word;
698 signalCEMsg( ce, NULL );
699 }
700
701 /*
702 * Display a single word SRC of the form "PROGXXXX" on the VSP control panel.
703 */
mf_displayProgress(u16 value)704 void mf_displayProgress( u16 value )
705 {
706 u8 ce[12];
707 u8 src[72];
708
709 memcpy( ce, "\x00\x00\x04\x4A\x00\x00\x00\x48\x00\x00\x00\x00", 12 );
710 memcpy( src,
711 "\x01\x00\x00\x01"
712 "\x00\x00\x00\x00"
713 "\x00\x00\x00\x00"
714 "\x00\x00\x00\x00"
715 "\x00\x00\x00\x00"
716 "\x00\x00\x00\x00"
717 "\x00\x00\x00\x00"
718 "\x00\x00\x00\x00"
719 "\x00\x00\x00\x00"
720 "\x00\x00\x00\x00"
721 "PROGxxxx"
722 " ",
723 72 );
724 src[6] = value>>8;
725 src[7] = value&255;
726 src[44] = "0123456789ABCDEF"[(value>>12)&15];
727 src[45] = "0123456789ABCDEF"[(value>>8)&15];
728 src[46] = "0123456789ABCDEF"[(value>>4)&15];
729 src[47] = "0123456789ABCDEF"[value&15];
730 dmaAndSignalCEMsg( ce, NULL, src, sizeof(src), 9*64*1024 );
731 }
732
733 /*
734 * Clear the VSP control panel. Used to "erase" an SRC that was
735 * previously displayed.
736 */
mf_clearSrc(void)737 void mf_clearSrc( void )
738 {
739 signalCEMsg( "\x00\x00\x00\x4B\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
740 }
741
742 /*
743 * Initialization code here.
744 */
mf_init(void)745 void mf_init( void )
746 {
747 int i;
748
749 /* initialize */
750 spin_lock_init( &spinlock );
751 for ( i = 0; i < sizeof(prealloc)/sizeof(*prealloc); ++i )
752 free( &prealloc[i] );
753 HvLpEvent_registerHandler( HvLpEvent_Type_MachineFac, &hvHandler );
754
755 /* virtual continue ack */
756 signalCEMsg( "\x00\x00\x00\x57\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
757
758 /* initialization complete */
759 printk( KERN_NOTICE "mf.c: iSeries Linux LPAR Machine Facilities initialized\n" );
760
761 iSeries_proc_callback(&mf_proc_init);
762 }
763
mf_setSide(char side)764 void mf_setSide(char side)
765 {
766 int rc = 0;
767 u64 newSide = 0;
768 struct VspCmdData myVspCmd;
769
770 memset(&myVspCmd, 0, sizeof(myVspCmd));
771 if (side == 'A')
772 newSide = 0;
773 else if (side == 'B')
774 newSide = 1;
775 else if (side == 'C')
776 newSide = 2;
777 else
778 newSide = 3;
779
780 myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = newSide;
781 myVspCmd.xCmd = 10;
782
783 rc = signalVspInstruction(&myVspCmd);
784 }
785
mf_getSide(void)786 char mf_getSide(void)
787 {
788 char returnValue = ' ';
789 int rc = 0;
790 struct VspCmdData myVspCmd;
791
792 memset(&myVspCmd, 0, sizeof(myVspCmd));
793 myVspCmd.xCmd = 2;
794 myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = 0;
795 mb();
796 rc = signalVspInstruction(&myVspCmd);
797
798 if (rc != 0)
799 {
800 return returnValue;
801 } else {
802 if (myVspCmd.xRc == 0)
803 {
804 if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 0)
805 returnValue = 'A';
806 else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 1)
807 returnValue = 'B';
808 else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 2)
809 returnValue = 'C';
810 else
811 returnValue = 'D';
812 }
813 }
814
815 return returnValue;
816 }
817
mf_getSrcHistory(char * buffer,int size)818 void mf_getSrcHistory(char *buffer, int size)
819 {
820 /* struct IplTypeReturnStuff returnStuff;
821 struct StackElement * newElement = newStackElement();
822 int rc = 0;
823 char *pages[4];
824
825 pages[0] = kmalloc(4096, GFP_ATOMIC);
826 pages[1] = kmalloc(4096, GFP_ATOMIC);
827 pages[2] = kmalloc(4096, GFP_ATOMIC);
828 pages[3] = kmalloc(4096, GFP_ATOMIC);
829 if (( newElement == NULL ) || (pages[0] == NULL) || (pages[1] == NULL) || (pages[2] == NULL) || (pages[3] == NULL))
830 rc = -ENOMEM;
831 else
832 {
833 returnStuff.xType = 0;
834 returnStuff.xRc = 0;
835 returnStuff.xDone = 0;
836 newElement->event.xHvLpEvent.xSubtype = 6;
837 newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0);
838 newElement->event.xUnion.xVspCmd.xEvent = &returnStuff;
839 newElement->event.xUnion.xVspCmd.xCmd = 4;
840 newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex();
841 newElement->event.xUnion.xVspCmd.xRc = 0xFF;
842 newElement->event.xUnion.xVspCmd.xReserved1 = 0;
843 newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[0] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[0]));
844 newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[1] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[1]));
845 newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[2] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[2]));
846 newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[3] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[3]));
847 mb();
848 rc = signalEvent(newElement);
849 }
850
851 if (rc != 0)
852 {
853 return;
854 }
855 else
856 {
857 while (returnStuff.xDone != 1)
858 {
859 udelay(10);
860 }
861
862 if (returnStuff.xRc == 0)
863 {
864 memcpy(buffer, pages[0], size);
865 }
866 }
867
868 kfree(pages[0]);
869 kfree(pages[1]);
870 kfree(pages[2]);
871 kfree(pages[3]);*/
872 }
873
mf_setCmdLine(const char * cmdline,int size,u64 side)874 void mf_setCmdLine(const char *cmdline, int size, u64 side)
875 {
876 struct VspCmdData myVspCmd;
877 int rc = 0;
878 dma_addr_t dma_addr = 0;
879 char *page = pci_alloc_consistent(iSeries_vio_dev, size, &dma_addr);
880
881 if (page == NULL) {
882 printk(KERN_ERR "mf.c: couldn't allocate memory to set command line\n");
883 return;
884 }
885
886 copy_from_user(page, cmdline, size);
887
888 memset(&myVspCmd, 0, sizeof(myVspCmd));
889 myVspCmd.xCmd = 31;
890 myVspCmd.xSubData.xSetKernelCmdLineIn.xToken = dma_addr;
891 myVspCmd.xSubData.xSetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex;
892 myVspCmd.xSubData.xSetKernelCmdLineIn.xSide = side;
893 myVspCmd.xSubData.xSetKernelCmdLineIn.xTransferLength = size;
894 mb();
895 rc = signalVspInstruction(&myVspCmd);
896
897 pci_free_consistent(iSeries_vio_dev, size, page, dma_addr);
898 }
899
mf_getCmdLine(char * cmdline,int * size,u64 side)900 int mf_getCmdLine(char *cmdline, int *size, u64 side)
901 {
902 struct VspCmdData myVspCmd;
903 int rc = 0;
904 int len = *size;
905 dma_addr_t dma_addr = pci_map_single(iSeries_vio_dev, cmdline, *size, PCI_DMA_FROMDEVICE);
906
907 memset(cmdline, 0, *size);
908 memset(&myVspCmd, 0, sizeof(myVspCmd));
909 myVspCmd.xCmd = 33;
910 myVspCmd.xSubData.xGetKernelCmdLineIn.xToken = dma_addr;
911 myVspCmd.xSubData.xGetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex;
912 myVspCmd.xSubData.xGetKernelCmdLineIn.xSide = side;
913 myVspCmd.xSubData.xGetKernelCmdLineIn.xTransferLength = *size;
914 mb();
915 rc = signalVspInstruction(&myVspCmd);
916
917 if ( ! rc ) {
918
919 if (myVspCmd.xRc == 0)
920 {
921 len = myVspCmd.xSubData.xGetKernelCmdLineOut.xTransferLength;
922 }
923 /* else
924 {
925 memcpy(cmdline, "Bad cmdline", 11);
926 }
927 */
928 }
929
930 pci_unmap_single(iSeries_vio_dev, dma_addr, *size, PCI_DMA_FROMDEVICE);
931
932 return len;
933 }
934
935
mf_setVmlinuxChunk(const char * buffer,int size,int offset,u64 side)936 int mf_setVmlinuxChunk(const char *buffer, int size, int offset, u64 side)
937 {
938 struct VspCmdData myVspCmd;
939 int rc = 0;
940
941 dma_addr_t dma_addr = 0;
942
943 char *page = pci_alloc_consistent(iSeries_vio_dev, size, &dma_addr);
944
945 if (page == NULL) {
946 printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n");
947 return -ENOMEM;
948 }
949
950 copy_from_user(page, buffer, size);
951 memset(&myVspCmd, 0, sizeof(myVspCmd));
952
953 myVspCmd.xCmd = 30;
954 myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr;
955 myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex;
956 myVspCmd.xSubData.xGetKernelImageIn.xSide = side;
957 myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset;
958 myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = size;
959 mb();
960 rc = signalVspInstruction(&myVspCmd);
961
962 if (rc == 0)
963 {
964 if (myVspCmd.xRc == 0)
965 {
966 rc = 0;
967 } else {
968 rc = -ENOMEM;
969 }
970 }
971
972 pci_free_consistent(iSeries_vio_dev, size, page, dma_addr);
973
974 return rc;
975 }
976
mf_getVmlinuxChunk(char * buffer,int * size,int offset,u64 side)977 int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side)
978 {
979 struct VspCmdData myVspCmd;
980 int rc = 0;
981 int len = *size;
982
983 dma_addr_t dma_addr = pci_map_single(iSeries_vio_dev, buffer, *size, PCI_DMA_FROMDEVICE);
984
985 memset(buffer, 0, len);
986
987 memset(&myVspCmd, 0, sizeof(myVspCmd));
988 myVspCmd.xCmd = 32;
989 myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr;
990 myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex;
991 myVspCmd.xSubData.xGetKernelImageIn.xSide = side;
992 myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset;
993 myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = len;
994 mb();
995 rc = signalVspInstruction(&myVspCmd);
996
997 if (rc == 0)
998 {
999 if (myVspCmd.xRc == 0)
1000 {
1001 *size = myVspCmd.xSubData.xGetKernelImageOut.xTransferLength;
1002 } else {
1003 rc = -ENOMEM;
1004 }
1005 }
1006
1007 pci_unmap_single(iSeries_vio_dev, dma_addr, len, PCI_DMA_FROMDEVICE);
1008
1009 return rc;
1010 }
1011
mf_setRtcTime(unsigned long time)1012 int mf_setRtcTime(unsigned long time)
1013 {
1014 struct rtc_time tm;
1015
1016 to_tm(time, &tm);
1017
1018 return mf_setRtc( &tm );
1019 }
1020
1021 struct RtcTimeData
1022 {
1023 struct semaphore *xSemaphore;
1024 struct CeMsgData xCeMsg;
1025 int xRc;
1026 };
1027
getRtcTimeComplete(void * token,struct CeMsgData * ceMsg)1028 void getRtcTimeComplete(void * token, struct CeMsgData *ceMsg)
1029 {
1030 struct RtcTimeData *rtc = (struct RtcTimeData *)token;
1031
1032 memcpy(&(rtc->xCeMsg), ceMsg, sizeof(rtc->xCeMsg));
1033
1034 rtc->xRc = 0;
1035 up(rtc->xSemaphore);
1036 }
1037
1038 static unsigned long lastsec = 1;
1039
mf_getRtcTime(unsigned long * time)1040 int mf_getRtcTime(unsigned long *time)
1041 {
1042 /* unsigned long usec, tsec; */
1043
1044 u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart));
1045 u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1);
1046 int year = 1970;
1047 int year1 = ( dataWord1 >> 24 ) & 0x000000FF;
1048 int year2 = ( dataWord1 >> 16 ) & 0x000000FF;
1049 int sec = ( dataWord1 >> 8 ) & 0x000000FF;
1050 int min = dataWord1 & 0x000000FF;
1051 int hour = ( dataWord2 >> 24 ) & 0x000000FF;
1052 int day = ( dataWord2 >> 8 ) & 0x000000FF;
1053 int mon = dataWord2 & 0x000000FF;
1054
1055 BCD_TO_BIN(sec);
1056 BCD_TO_BIN(min);
1057 BCD_TO_BIN(hour);
1058 BCD_TO_BIN(day);
1059 BCD_TO_BIN(mon);
1060 BCD_TO_BIN(year1);
1061 BCD_TO_BIN(year2);
1062 year = year1 * 100 + year2;
1063
1064 *time = mktime(year, mon, day, hour, min, sec);
1065
1066 *time += ( jiffies / HZ );
1067
1068 /* Now THIS is a nasty hack!
1069 * It ensures that the first two calls to mf_getRtcTime get different
1070 * answers. That way the loop in init_time (time.c) will not think
1071 * the clock is stuck.
1072 */
1073 if ( lastsec ) {
1074 *time -= lastsec;
1075 --lastsec;
1076 }
1077
1078 return 0;
1079
1080 }
1081
mf_getRtc(struct rtc_time * tm)1082 int mf_getRtc( struct rtc_time * tm )
1083 {
1084
1085 struct CeMsgCompleteData ceComplete;
1086 struct RtcTimeData rtcData;
1087 int rc = 0;
1088 DECLARE_MUTEX_LOCKED(Semaphore);
1089
1090 memset(&ceComplete, 0, sizeof(ceComplete));
1091 memset(&rtcData, 0, sizeof(rtcData));
1092
1093 rtcData.xSemaphore = &Semaphore;
1094
1095 ceComplete.xHdlr = &getRtcTimeComplete;
1096 ceComplete.xToken = (void *)&rtcData;
1097
1098 rc = signalCEMsg( "\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00", &ceComplete );
1099
1100 if ( rc == 0 )
1101 {
1102 down(&Semaphore);
1103
1104 if ( rtcData.xRc == 0)
1105 {
1106 if ( ( rtcData.xCeMsg.xCEMsg[2] == 0xa9 ) ||
1107 ( rtcData.xCeMsg.xCEMsg[2] == 0xaf ) ) {
1108 /* TOD clock is not set */
1109 tm->tm_sec = 1;
1110 tm->tm_min = 1;
1111 tm->tm_hour = 1;
1112 tm->tm_mday = 10;
1113 tm->tm_mon = 8;
1114 tm->tm_year = 71;
1115 mf_setRtc( tm );
1116 }
1117 {
1118 u32 dataWord1 = *((u32 *)(rtcData.xCeMsg.xCEMsg+4));
1119 u32 dataWord2 = *((u32 *)(rtcData.xCeMsg.xCEMsg+8));
1120 u8 year = (dataWord1 >> 16 ) & 0x000000FF;
1121 u8 sec = ( dataWord1 >> 8 ) & 0x000000FF;
1122 u8 min = dataWord1 & 0x000000FF;
1123 u8 hour = ( dataWord2 >> 24 ) & 0x000000FF;
1124 u8 day = ( dataWord2 >> 8 ) & 0x000000FF;
1125 u8 mon = dataWord2 & 0x000000FF;
1126
1127 BCD_TO_BIN(sec);
1128 BCD_TO_BIN(min);
1129 BCD_TO_BIN(hour);
1130 BCD_TO_BIN(day);
1131 BCD_TO_BIN(mon);
1132 BCD_TO_BIN(year);
1133
1134 if ( year <= 69 )
1135 year += 100;
1136
1137 tm->tm_sec = sec;
1138 tm->tm_min = min;
1139 tm->tm_hour = hour;
1140 tm->tm_mday = day;
1141 tm->tm_mon = mon;
1142 tm->tm_year = year;
1143 }
1144 } else {
1145 rc = rtcData.xRc;
1146 tm->tm_sec = 0;
1147 tm->tm_min = 0;
1148 tm->tm_hour = 0;
1149 tm->tm_mday = 15;
1150 tm->tm_mon = 5;
1151 tm->tm_year = 52;
1152
1153 }
1154 tm->tm_wday = 0;
1155 tm->tm_yday = 0;
1156 tm->tm_isdst = 0;
1157
1158 }
1159
1160 return rc;
1161
1162 }
1163
mf_setRtc(struct rtc_time * tm)1164 int mf_setRtc(struct rtc_time * tm)
1165 {
1166 char ceTime[12] = "\x00\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00";
1167 int rc = 0;
1168 u8 day, mon, hour, min, sec, y1, y2;
1169 unsigned year;
1170
1171 year = 1900 + tm->tm_year;
1172 y1 = year / 100;
1173 y2 = year % 100;
1174
1175 sec = tm->tm_sec;
1176 min = tm->tm_min;
1177 hour = tm->tm_hour;
1178 day = tm->tm_mday;
1179 mon = tm->tm_mon + 1;
1180
1181 BIN_TO_BCD(sec);
1182 BIN_TO_BCD(min);
1183 BIN_TO_BCD(hour);
1184 BIN_TO_BCD(mon);
1185 BIN_TO_BCD(day);
1186 BIN_TO_BCD(y1);
1187 BIN_TO_BCD(y2);
1188
1189 ceTime[4] = y1;
1190 ceTime[5] = y2;
1191 ceTime[6] = sec;
1192 ceTime[7] = min;
1193 ceTime[8] = hour;
1194 ceTime[10] = day;
1195 ceTime[11] = mon;
1196
1197 rc = signalCEMsg( ceTime, NULL );
1198
1199 return rc;
1200 }
1201
1202
1203
1204