linux-old/arch/ppc64/kernel/mf.c
<<
>>
Prefs
   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
  44extern struct pci_dev * iSeries_vio_dev;
  45
  46/*
  47 * This is the structure layout for the Machine Facilities LPAR event
  48 * flows.
  49 */
  50struct VspCmdData;
  51struct CeMsgData;
  52union SafeCast
  53{
  54        u64 ptrAsU64;
  55        void *ptr;
  56};
  57
  58
  59typedef void (*CeMsgCompleteHandler)( void *token, struct CeMsgData *vspCmdRsp );
  60
  61struct CeMsgCompleteData
  62{
  63        CeMsgCompleteHandler xHdlr;
  64        void *xToken;
  65};
  66
  67struct VspRspData
  68{
  69        struct semaphore *xSemaphore;
  70        struct VspCmdData *xResponse;
  71};
  72
  73struct 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 */
 173struct StackElement
 174{
 175        struct StackElement * next;
 176        struct IoMFLpEvent event;
 177        MFCompleteHandler hdlr;
 178        char dmaData[72];
 179        unsigned dmaDataLength;
 180        unsigned remoteAddress;
 181};
 182static spinlock_t spinlock;
 183static struct StackElement * head = NULL;
 184static struct StackElement * tail = NULL;
 185static struct StackElement * avail = NULL;
 186static 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 */
 192void 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 */
 209static 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 */
 276static 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
 318static 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 */
 355static 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 */
 376static 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 */
 403static 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 */
 423static 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 */
 494static 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 */
 577static 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 */
 602void 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 */
 636void 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 */
 668void 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 */
 679void 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 */
 689void 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 */
 704void 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 */
 737void 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 */
 745void 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
 764void 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
 786char 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
 818void 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
 874void 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
 900int 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
 936int 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
 977int 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
1012int 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
1021struct RtcTimeData
1022{
1023        struct semaphore *xSemaphore;
1024        struct CeMsgData xCeMsg;
1025        int xRc;
1026};
1027
1028void 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
1038static unsigned long lastsec = 1;
1039
1040int 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
1082int 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
1164int 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.