#define SCCSID  "@(#)hybernat.c  6.1 94/07/13"
/*
 *      IBM Confidential
 *
 *      Copyright (c) IBM Corporation 1994
 *
 *      All Rights Reserved
 *
 * 150101   04/02/96 TPL   Update the ShutdownFlag after successful hybernation
 * 150281   04/11/96 TPL   Claim the hybernateSem to prevent other threads
 *                         from exiting the Kernel
 * 151409   04/19/96 TPL   Use the word 53 of the ATA Identity Data to
 *                         determine whether current translation settings
 *                         in words 54-56 should be used. see HYBERHD.H
 *                         for definition and explanation.
 * 154438   05/10/96 cgw   Add drive parm to hddumpsys call
 * 155605   05/25/96 cgw   clean up debug msgs to reduce memory use
 * 153620   05/30/96 TPL   Avoid trap 08 and other hibernation/resume anomalies
 * 156660   06/12/96 TPL   Restore normal I/O buffer size reduced in 153630
 *                         and don't send NotifyDDSaveRestore for TRAPDOOR
 * 156773   06/12/96 TPL   Remove SWAPPER.DAT dependencies to avoid occasional
 * 156140                  hibernation failures
 * 159979   07/18/96 TPL   Must not take page fault before restore notification
 */

#pragma code_seg("PROFSWAP_TEXT", "CODE")

#define FNO_NUM  FNO_PGAGE_C
#include <fnomem.h>

#define INCL_ERRORS
#include <bseerr.h>
#include <osconst.h>
#include <ostypes.h>
#include <osmac.h>
#include <mi.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <seldesc.h>
#include <pgexport.h>
#include <vmexport.h>
#include <selxport.h>
#include <tkexport.h>
#include <infoseg.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#include <smexport.h>
#include <sched.h>
#define INCL_TYPES
#include <basedef.h>
#include <mvdmexp.h>
#include <pglocal.h>
#include <mmph.h>
#include <string.h>
#include <pageio.h>
#include <strat2.h>
#include <smlocal.h>
#include <vmlocal.h>
#include <vmowner.h>
#include <hybernat.h>
#include <hyberhd.h>
#include <sft.h>
#include <mft.h>
#include <filemode.h>
#include <kmode.h>                                                /* 150281 */

#define ROUNDUP(value, boundary) (((value) + ((boundary)-1)) & ~((boundary)-1))
#define min(x, y)       (x <= y ? x : y)
#define ErrRet(errno)   return(0x40 | errno)
#define OLDR_LOC        0x20            // start of area where oldr is loaded
#define OLDR_SIZ        0x10            // size of area used by oldr
#define OLDRSPACE(pf)   ((pf >= OLDR_LOC) && (pf < (OLDR_LOC + OLDR_SIZ)))
#define LOWMEMORY       0x9f            // upper bound of real memory
//156773 #define MAXPAGEOUTEXPECTED 0x20         // swapout fudge factor   /* 153620 */

extern PTCB ptcbPriQReady;
extern PPTDA pPTDACur;
extern int FSD_FlushBuf();
extern long hdEnd;
extern int AllocBitGroup();
//156773 extern KSEMMTX SMSwapperSem;
extern hyberDelay();
extern ulong_t ShutdownFlag;                                      /* 150101 */
extern ulong_t flGlobalActions;                                   /* 150281 */
extern KSEMEVT hybernateSem;        // EVENT semaphore            /* 150281 */
extern struct tcb_s *pHybernateTCB; // hybernation thread's TCB   /* 150281 */
//156773 extern ulong_t hyberswapstart;                                     /* 153620 */
extern void NotifyDDSaveRestore();                                /* 153620 */

uchar_t hyberMaxPageCmds;
struct hyberInfo_s *pHyberInfo;
struct hyberInfoHdr_s *pHyberInfoHdr;
VMHOB  hyberInfoHob;
ulong_t *hyberPhysPT;
VMHOB  hyberPhysHob;
ulong_t hyberStat;
#ifdef HYBERI13
char * pHyberIoBuf;
ulong_t hyberIoBufSz;

int  hyberAllocIoBuf(void);                                       /* 153620 */
#endif /* HYBERI13 */
void SMTestSwapFile();
void hyberRestart();
void hyberRestartNow();                                           /* 159979 */
int  hyberInitInfo();                                             /* 153620 */
int  hyberMapPhysMem();                                           /* 153620 */
ulong_t hyberChksumPage();
void hyberGrabState();
void hyberPgCopy();
void hyberDone();
int hyberOK();
//156773 void hyberFreeSwapper();
void hyberGiveUp();
#ifndef HYBERI13
ulong_t hyberDumpTheSystem(struct mydata *mydata);
ulong_t hyberDumpSys(struct mydata *mydata);
ulong_t hyberStartio(struct mydata *mydata, ulong_t addr, long len, int read);
void hyberSetMultiple(int m);
void hyberIOinit(struct mydata *mydata);
#endif /* HYBERI13 */

void BootDos32();
void hyberReboot();
void hyberDummy();
ulong_t getSFTExtent();

long hyberOpCount;

#if !defined(INCDEBUG) || (HYBERDEBUG_S < 2)
#define hyberDispInfo(msg)
#define int3()
#define PRINT(fmt)
#define PRINT1(fmt, p1)
#define PRINT2(fmt, p1, p2)
#define PRINT3(fmt, p1, p2, p3)

#else
#define PRINT(fmt)              printf(fmt)
#define PRINT1(fmt, p1)         printf(fmt, p1)
#define PRINT2(fmt, p1, p2)     printf(fmt, p1, p2)
#define PRINT3(fmt, p1, p2, p3) printf(fmt, p1, p2, p3)

void
hyberDispInfo(msg)
char *msg;
{
   ulong_t  discardable, idle, pffree, mustswap, busy, inuse, resident, total;
   ulong_t  dirty, idlemustswap;
   ulong_t i,j;
   int prc;                                     // page range count
   struct vp_s *pvp;
   struct pf_s *ppf;
   struct pagerange_s *ppr;

   printf("-----%s\n",msg);
   total = resident = inuse = pffree = busy = mustswap = idle = discardable = 0;
   dirty = idlemustswap = 0;
   j = 0;

   ppr = pgPageablePAI.pai_pprt;
   for (prc=0; prc<pgPageablePAI.pai_nranges; ppr++,prc++) {
     i=ppr->pr_firstframe;
     ppf=pgFrameToPF(i);
     for(; i<=ppr->pr_lastframe; i++,ppf++) {
        total++;
        if (ppf->pf_flags & PF_FREE) {
                pffree++;
                continue;
        }

        if ((pvp = ppf->pf_pvp) == 0) {
                printf("pf_pvp=0, ppf=%lx\n", ppf);
                continue;
        }

        if (ppf->pf_flags & PF_BUSY)
                busy++;
        else
        if (pvp->vp_flags & VP_PFIDLE) {
                idle++;
                if (pgIsMustSwap(pvp))
                        idlemustswap++;
        } else
        if (ppf->pf_refcount != 0) {
                inuse++;
                if (pgIsMustSwap(pvp))
                        mustswap++;
                if (pvp->vp_flags & VP_DISCARDABLE)
                        discardable++;
                if (pvp->vp_flags & VP_RESIDENT)
                        resident++;
                if (pvp->vp_flags & VP_DIRTY)
                        dirty++;
        } else
                if (j < 10) {
                        printf("?? ppf=%lx, pf=%lx\n",ppf, i);
                        j++;
                }
     }
   }

        printf("pgPhysMax=%lx - activepages=%lx = %lx\n",
                pgPhysMax, total, pgPhysMax - total);
        printf("inuse=%lx + idle=%lx + free=%lx + busy=%lx = %lx\n",
                inuse, idle, pffree, busy, busy+inuse+idle+pffree);
        printf("resident=%lx, discardable=%lx, mustswap=%lx, dirty=%lx, ",
                resident, discardable, mustswap, dirty);
        printf("idlemustswap=%lx\n", idlemustswap);

        for (i=0,ppf=pgIdleList.li_headppf;;i++)
                if ((ppf = ppfBLINK(ppf)) == pgIdleList.li_headppf)
                        break;
        if (i != idle)
                printf("idle count and idle chain don't jive; chain = %lx\n",
                        i);
}
#endif /* INCDEBUG */

int
hyberYankIdlePF(pppf)
struct pf_s **pppf;
{
    struct pf_s *ppf;
    struct vp_s *pvp;
    int error = 0;

    ppf = pgIdleList.li_headppf;                /* head of idle list */
    do {
        ppf = ppfBLINK(ppf);
    } while (ppf->pf_busy || ppf->pf_pvp->vp_busy);

    if (ppf == pgIdleList.li_headppf)
        return(-1);

    pvp = ppf->pf_pvp;
    if (pgIsMustSwap(pvp)) {
        pvp->vp_flags &= ~(VP_SOW|VP_DISCARDABLE);
        pgSetSem(pvp);
        if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
                error = SMSwapOut(ppf);
        } else
        if ((error = SMSwapOut(ppf)) == NO_ERROR)  {
                SMTestSwapFile();
        }
        pgClearSemVP(pvp);
        if (error)
                return(error);
    }
    /*
     * Break the cross link between the vp and pf, and clear the dirty bit.
     */
    pvp->vp_flags &= ~(VP_PF|VP_PFIDLE|VP_DIRTY);
    if ((pvp->vp_flags & VP_DISCARDABLE) && ((pvp->vp_flags & VP_SOW) == 0))
            pgDiscardableInmem--;
    pvp->vp_block = ppf->pf_block;

    /*
     * Remove the pf from the idle list.
     */
    pgDeletePF(ppf);    /* clears lock counts */
    *pppf = ppf;

    return(error);
}

int
hyberPageOut()
{
    int rslt;
    struct pf_s                 *ppf;
    ulong_t target;

    SCHSetPriority(pTCBCur, (CLASS_TIME_CRITICAL << 8) + (LEVEL_MAX) - 1);

    hyberDispInfo("PageOut entry");

    hyberStat = HYBR_AGING;
    while (hyberStat == HYBR_AGING) {
        pgAgeRate(&pgAgeRateTable[0]);
        TKYield();
    }

    hyberDispInfo("after ager run #1");

    target = hyberStat+1;
    while (hyberStat != target) {
        pgAgeRate(&pgAgeRateTable[0]);
        TKYield();
    }

    hyberStat = HYBR_AGERDONE;                  // no more ager
    pTCBCur->TCBPriLevel = 0x2f;
    TKUpdatePriority(pTCBCur);
    hyberDispInfo("after ager run #2");

    while ((rslt = hyberYankIdlePF(SSToDS(&ppf))) == NO_ERROR) {
        pgFreePF(ppf, pgPFToFrame(ppf));
    }

    if (rslt != -1) {                   // -1 = idle list depleted
        hyberDispInfo("yank idle pf error");
        PRINT1("rslt = %x\n", rslt);
        hyberStat = 0;
        return(-4);
    }

    hyberDispInfo("after swap out");
    hyberStat = 0;
    return(0);
}

int
hyberGetOldrPage(ppf)
struct pf_s **ppf;
{
        int rslt;
        struct pf_s *myppf;

        rslt = pgGetPF(0, PG_1M, SSToDS(&myppf));
        if (rslt)
                return(rslt);
        myppf->pf_busy = 1;                     // mark it in use
        if (OLDRSPACE(pgPFToFrame(myppf))) {
                // no good - try again
                rslt = hyberGetOldrPage(ppf);
                myppf->pf_busy = 0;
                pgFreePF(myppf, pgPFToFrame(myppf));  // put it back on freelist
                return(rslt);
        }
        *ppf = myppf;
        return(NO_ERROR);
}

void
hyberGiveUp(why)
char *why;
{
    if (pHyberInfoHdr)
        VMFreeMem((ulong_t)pHyberInfoHdr, HOBNULL, 0);
    if (hyberPhysPT)
        VMFreeMem((ulong_t)hyberPhysPT, HOBNULL, 0);
#ifdef HYBERI13
    if (pHyberIoBuf)
        VMFreeMem((ulong_t)pHyberIoBuf, HOBNULL, 0);
#endif /* HYBERI13 */
    hyberDispInfo(why);
    hyberStat = 0;
}

int
hybernate(ulong_t hyberOpPkt[])
{
    struct mydata               Mydata;
    struct mydata               *mydata;
    struct pf_s                 *ppf;
    struct pf_s                 *ppf1;
    struct pf_s                 *ppf2;
    struct pte_s                *ppte;
    struct pagerange_s          *ppr;
    struct dlist_s              *dl;                    // scratch use
    struct dlist_s              *dlist;                 // ptr to dlist
    struct plist_s              *plist;
    int                         hopopFlgs;
    int                         prc;                    // page range count
    int                         rslt;
    int                         notdirty = 0;
    ulong_t                     pfcnt;                  // page frame count
    ulong_t                     i, j, k;
    ulong_t                     *p;
    ulong_t                     pli;                    // page list index
    ulong_t                     procflags;
    ulong_t                     infosize;               // info size in frames
#ifdef HYBERI13
    ulong_t dlistphy, plistphy, iobufphy;
#endif /* HYBERI13 */

    /* Make sure we have enough swap space left for potential swap outs */
//156773     if (smDFMac > (hyberswapstart-8))                             /* 153620 */
//156773         return (ERROR_SWAP_FILE_FULL);                            /* 153620 */

    hyberStat = HYBR_INIT;

    /* set us to a very high priority - we don't want the system changing */
    SCHSetPriority(pTCBCur, (CLASS_TIME_CRITICAL << 8) + (LEVEL_MAX) - 1);
    pTCBCur->TCBPriLevel = 0x2f;
    TKUpdatePriority(pTCBCur);

    hyberDispInfo("hybernation entry");

    // so hyberGiveUp doesn't do something it shouldn't
    hyberPhysPT = 0;
    pHyberInfoHdr = 0;
#ifdef HYBERI13
    pHyberIoBuf = 0;
#endif /* HYBERI13 */

    /* Split HOP_OP up */
    hopopFlgs = hyberOpPkt[HOP_OP] >> 16;
    hyberOpPkt[HOP_OP] &= HOPOP_MASK;

    /* setup the info frames */
    // allocate the space
    infosize = pgPhysPages * sizeof(struct hyberInfo_s);
    infosize += PAGESIZE;                               // infoHdr
    infosize = (infosize + PAGESIZE - 1) / PAGESIZE;
    if (rslt = hyberInitInfo(infosize))                           /* 153620 */
        return (rslt);                                            /* 153620 */
    PRINT1("info size = %ld frames\n", infosize);
#ifdef HYBERI13
    if (rslt = hyberAllocIoBuf()) {                               /* 153620 */
        hyberGiveUp("hyberInitInfo failed");                      /* 156660 */
        return (rslt);                                            /* 153620 */
    }
    PRINT1("IO buf size = %ld bytes\n", hyberIoBufSz);
#endif /* HYBERI13 */
    if (rslt = hyberMapPhysMem()) {                               /* 153620 */
        hyberGiveUp("hyberMapPhysMem failed");                    /* 156660 */
        return (rslt);                                            /* 153620 */
    }
    // Insert the size of the info blocks into the header so the loader can
    // find the beginning of the data area.

    // Set up space in the InfoHdr for first the dlist and then the plist
    pHyberInfoHdr->dlist = (struct dlist_s *)ROUNDUP(sizeof(hyberInfoHdr_t),
                                        sizeof(long));  // this is an offset
    dlist = (struct dlist_s *)((ulong_t)pHyberInfoHdr +
                                        (ulong_t)pHyberInfoHdr->dlist);

    /*  copy the disk list to hyberInfoHdr - note it is appended to the end of
        the structure.
        There are two possible situations here. 1) We are using the swap file
        to save our data. 2) We are to use a separately allocated file.
        In the first case we have to locate the end of the in use portion of the
        swap file and work from there. In the second we just have to do the copy
     */
    dl = (dlist_t *)hyberOpPkt[HOP_DLIST];
//156773     if (hyberOpPkt[HOP_OP] != HOPOP_STICKY) {   // we're using the swap file
        // k = the first available sector (the DLIST passed is the WHOLE file)
//156773         k = hyberswapstart * (PAGESIZE/SECTORSIZE);               /* 153620 */
//156773         for (i=j=0; dl[i].dl_count; i++) {
//156773                 if (k >= j+dl[i].dl_count)
//156773                         j += dl[i].dl_count;
//156773                 else {
//156773                         dlist[0].dl_sector = dl[i].dl_sector + (k-j);
//156773                         dlist[0].dl_count = dl[i].dl_count - (k-j);
//156773                         break;
//156773                 }
//156773         }
//156773         for (i++,j=1; dl[i].dl_count; i++,j++) {
//156773                 dlist[j].dl_count = dl[i].dl_count;
//156773                 dlist[j].dl_sector = dl[i].dl_sector;
//156773         }
//156773     } else {                    // we're using a separately allocated file
        for (i=j=0; dl[i].dl_count; i++,j++) {
                dlist[j].dl_count = dl[i].dl_count;
                dlist[j].dl_sector = dl[i].dl_sector;
        }
//156773     }
    dlist[j].dl_count = 0;                      // mark the end

    // now the plist
    j++;        // we add one for the info file's frame
    i = ROUNDUP(j * sizeof(struct dlist_s), sizeof(long));
    pHyberInfoHdr->plist = (struct plist_s *)((ulong_t)pHyberInfoHdr->dlist+i);
    plist = (struct plist_s *)((ulong_t)pHyberInfoHdr +
                                        (ulong_t)pHyberInfoHdr->plist);

    /* Make sure there are a couple of free pages so the hyberldr has room to
       work. These pages must NOT be in loader space (so there are no conflicts)
       and in real memory (< 1 meg).
     */
    ppf1 = ppf2 = (struct pf_s *)NULL;
    // first try the free list
    for (ppf = ppfFLINK(pgFreeList.li_headppf); ppf != pgFreeList.li_headppf;
                                ppf = ppfFLINK(ppf)) {
        if (pgPFToFrame(ppf) < LOWMEMORY && !OLDRSPACE(pgPFToFrame(ppf))) {
                if (ppf1 == (struct pf_s *)NULL) {
                        ppf1 = ppf;
                        ppf1->pf_busy = 1;
                } else {
                        ppf2 = ppf;
                        ppf2->pf_busy = 1;
                        break;
                }
        }
    }

    // if the freelist walk didn't work then we will try grabbing them
    if (ppf1 == (struct pf_s *)NULL) {
        rslt = hyberGetOldrPage(SSToDS(&ppf1));
        if (rslt) {
                hyberGiveUp("1st getPF failed");
                ErrRet(2);
        }
    } else
        pgDeletePF(ppf1);
    if (ppf2 == (struct pf_s *)NULL) {
        rslt = hyberGetOldrPage(SSToDS(&ppf2));
        if (rslt) {
                pgFreePF(ppf1, pgPFToFrame(ppf1));  // put it back on freelist
                hyberGiveUp("2nd getPF failed");
                ErrRet(3);
        }
    } else
        pgDeletePF(ppf2);

    p = (ulong_t *)hyberGrabState;
    i = *p;                                     // touch it to avoid pagefaults
    j = hdEnd;                                  // touch it to avoid pagefaults
    p = (ulong_t *)hyberDummy;
    k = *p + i;                                 // touch it to avoid pagefaults

    hyberStat = HYBR_FLUSHBUF3;
    i = 0;                                      // retry counter
    while (rslt = FSD_FlushBuf(0, 3)) {
        PRINT1("flushbuf retry: rslt = %x\n", rslt);
        if (++i == 5) {
                ppf1->pf_busy = ppf2->pf_busy = 0;
                pgFreePF(ppf1, pgPFToFrame(ppf1));
                pgFreePF(ppf2, pgPFToFrame(ppf2));
                hyberGiveUp("hybernate flushbuf failed");
                PRINT1("flushbuf rslt = %x\n", rslt);
                return(0x200 | rslt);
        }
        TKYield();
    }
#ifndef HYBERI13
    hdWaitNotBusy();
#endif /* HYBERI13 */

    procflags = TKDisable();                    // no more interrupts!!
    hyberDispInfo("interrupts disabled");

    /* ==============================================================
     *
     * The state of the system at this point is how we want it when
     * we do re-entry.
     *
     * ==============================================================*/

    /* save the machine state */
    hyberStat = HYBR_UNDERWAY;
    hyberGrabState(pHyberInfoHdr);
    ppte = pgPagePdTopPte(pHyberInfoHdr->hih_gdt2 >> PAGESHIFT, &pgData);
    pHyberInfoHdr->hih_GDTFrame = (ushort_t)ppte->pt_frame;
    pHyberInfoHdr->hih_reentry = (ulong_t)&hyberRestart;
    pHyberInfoHdr->hih_dataloc = (ushort_t)pgPFToFrame(ppf1);
    pHyberInfoHdr->hih_hldrloc = (ushort_t)pgPFToFrame(ppf2);
    PRINT2("loader memory holes are %x / %x\n",
                                pHyberInfoHdr->hih_dataloc,
                                pHyberInfoHdr->hih_hldrloc);

    /* we should be done allocating memory now so put the loader pages back
       on the free list */
    ppf1->pf_busy = ppf2->pf_busy = 0;
    pgFreePF(ppf1, pgPFToFrame(ppf1));
    pgFreePF(ppf2, pgPFToFrame(ppf2));

    if (hyberOpPkt[HOP_OP] == HOPOP_STICKY)     // note that its sticky
        pHyberInfoHdr->hih_hyberflags |= HF_STICKY;

    /* Build the info list. We will find all resident and
       inuse frames, record and check sum them. */
    pfcnt = 0;
    ppr = pgPageablePAI.pai_pprt;
    for (prc=0; prc<pgPageablePAI.pai_nranges; ppr++,prc++) {
      i = ppr->pr_firstframe;
      ppf = pgFrameToPF(i);
      for(; i<=ppr->pr_lastframe; i++,ppf++) {
        if (ppf->pf_flags & PF_FREE)
                continue;

        pHyberInfo[pfcnt].hi_pfnum = (ushort_t)i;
        pfcnt++;
      }
    }

    /* save pfcnt */
    pHyberInfoHdr->hih_pfcnt = (ushort_t)pfcnt;
    PRINT2("pfcnt = %d(%x)\n", pfcnt, pfcnt);

    /* make sure there is enough disk allocated */
    for (i=j=0; dlist[i].dl_count; i++)
        j += dlist[i].dl_count;
    PRINT2("diskframeCount = %d(%x)\n", j/8, j/8);
    if (j/(PAGESIZE/SECTORSIZE) < pfcnt) {
        hyberGiveUp("not enough space SWAPPER2.DAT");             /* 156773 */
        ErrRet(1);
    }

    /* generate the page frame list for the dump off */
    pli = 0;
    plist[pli].pl_frame = pHyberInfo[0].hi_pfnum;
    plist[pli].pl_count = 1;
    for (i=1; i<pHyberInfoHdr->hih_pfcnt; i++) {
        if (plist[pli].pl_frame + plist[pli].pl_count == pHyberInfo[i].hi_pfnum)
                plist[pli].pl_count++;
        else {
                pli++;
                if (&plist[pli] > (struct plist_s *)pHyberInfoHdr + PAGESIZE) {
                        hyberGiveUp("page list overflow");
                        ErrRet(6);
                }
                plist[pli].pl_frame = pHyberInfo[i].hi_pfnum;
                plist[pli].pl_count = 1;
        }
    }
    pHyberInfoHdr->hih_pli = pli;
    PRINT2("infoHdr size = %d(%x)\n",
        (&plist[pli] - (plist_t *)pHyberInfoHdr) * sizeof(plist_t),
                (&plist[pli] - (plist_t *)pHyberInfoHdr) * sizeof(plist_t));

    /* fix dlist to match actual memory */
    k = pHyberInfoHdr->hih_pfcnt * (PAGESIZE / SECTORSIZE);
    for (i=j=0; j<k; i++)
        j += dlist[i].dl_count;
    dlist[i-1].dl_count = k - (j - dlist[i-1].dl_count);
    pHyberInfoHdr->hih_dli = i-1;

    /* hih_pli & hih_dli are now pointing to the last live entries in
        their respective structures - in order to get Info Hdr to disk
        we will append an entry for it to the work lists so the last
        thing DumpTheSystem does is put out the header */
    VMLinToPhys((ulong_t)((char *)pHyberInfoHdr), SSToDS(&j));
    plist[pHyberInfoHdr->hih_pli + 1].pl_frame = (ushort_t)(j >> PAGESHIFT);
    plist[pHyberInfoHdr->hih_pli + 1].pl_count = 1;
    dlist[pHyberInfoHdr->hih_dli + 1].dl_sector =
                        ((dlist_t *)hyberOpPkt[HOP_INFOFILE])->dl_sector;
    dlist[pHyberInfoHdr->hih_dli + 1].dl_count =
                        ((dlist_t *)hyberOpPkt[HOP_INFOFILE])->dl_count;

    /* Set up the mydata structure */
    mydata = SSToDS(&Mydata);
    pagelist = plist;
    disklist = dlist;
    dlSiz = pHyberInfoHdr->hih_dli + 1;                 // add 1 for infohdr
    plSiz = pli + 1;                                    // ditto

#ifndef HYBERI13
    /* Initialize for I/O */
    hyberIOinit(mydata);
    pHyberInfoHdr->hih_maxblk = maxblk;
    pHyberInfoHdr->hih_SpC = SpC;
    pHyberInfoHdr->hih_SpT = SpT;
#endif /* HYBERI13 */

#if HYBERDEBUG_S >= 2
    /* generate check sums */
    for (i=0; i < pHyberInfoHdr->hih_pfcnt; i++)
        pHyberInfo[i].hi_chksum = hyberChksumPage(pHyberInfo[i].hi_pfnum);
#endif /* HYBERDEBUG_S >= 2 */

    /*
     * All of this will now get written to hybernat.dat
     */
    hyberStat = HYBR_DUMPING;
#ifndef HYBERI13
    maxio = MAXIO;
    rslt = hyberDumpTheSystem(mydata);
    while(rslt && maxio > 0) {
        PRINT1("DumpTheSystem rslt = %x\n", rslt);
        maxio /= 2;
        rslt = hyberDumpTheSystem(mydata);
    }
    if (rslt) {                                 // last gasp try
        hyberSetMultiple(0);
        maxblk = 1;
        maxio = 1;
        rslt = hyberDumpTheSystem(mydata);
        if (rslt) {
                hyberGiveUp("I/O failure");
                return(rslt);
        }
    }
#else /* HYBERI13 */
    VMLinToPhys((ulong_t)disklist, SSToDS(&dlistphy)); /* need to check rc */
    VMLinToPhys((ulong_t)pagelist, SSToDS(&plistphy));
    VMLinToPhys((ulong_t)pHyberIoBuf, SSToDS(&iobufphy));
    pHyberInfoHdr->hih_iobuf = (ushort_t)(iobufphy >> PAGESHIFT);
    pHyberInfoHdr->hih_iobuflen = (ushort_t)(hyberIoBufSz >> PAGESHIFT);

    /*
    **-------------------------------------------------------------
    ** Specify the drive id(s) of the physical disk(s) to dump the
    ** system.  The system file and swapper files may reside on
    ** different physical disks.  Pass the first parm as the
    ** disk to write the swapper file and the second parm as the disk
    ** to write the system file.  Bios expects the drive ids to start
    ** at 80h.
    **-------------------------------------------------------------
    */

    pHyberInfoHdr->hih_drive =
       (uchar_t) (0x80 | * (ulong_t *)hyberOpPkt[HOP_DLIST_CHS]);

    NotifyDDSaveRestore(0x0L);                                     /* 153620 */

    rslt = hdDumpSys(pHyberInfoHdr->hih_drive,
                       (uchar_t) (0x80 | * (ulong_t *)hyberOpPkt[HOP_INFO_CHS]),
                       dlistphy, dlSiz+1, plistphy, plSiz+1, iobufphy, hyberIoBufSz);

    /*
     * Restore all device states on I/O ERROR or HYBERNATION completion.
     *
     * NOTE: Disk I/O must be avoided before calling NotifyDDSaveRestore
     *       because the DISK device driver may reject the I/O or worst
     *       yet doing the I/O assuming device state is restored.
     */
    if ((hyberOpPkt[HOP_OP]!=HOPOP_TRAPDOOR) ||                   /* 156660 */
        (rslt))
      NotifyDDSaveRestore(0x1L);                                  /* 153620 */

    if (rslt) {
      hyberGiveUp("Int 13 I/O failure");
      return(rslt);
    }

#endif /* HYBERI13 */

#if HYBERDEBUG_S >= 2
    /* recheck check sums */
    for (i=0; i < pHyberInfoHdr->hih_pfcnt; i++)
        if (pHyberInfo[i].hi_chksum != hyberChksumPage(pHyberInfo[i].hi_pfnum))
                pHyberInfo[i].hi_sfnum = 1;
    for (i=0; i < pHyberInfoHdr->hih_pfcnt; i++)
        if (pHyberInfo[i].hi_sfnum == 1) {
                printf("chksum(%lx/%lx): page %x\n",
                        pHyberInfo[i].hi_chksum,
                        hyberChksumPage(pHyberInfo[i].hi_pfnum),
                        pHyberInfo[i].hi_pfnum);
                pHyberInfo[i].hi_sfnum = 0;
        }
#endif /* HYBERDEBUG_S >= 2 */

    hyberDispInfo("system dumped");

    /*
     * The system has now been dumped.
     */

    /* If trapdoor go do it */
    if (hyberOpPkt[HOP_OP]==HOPOP_TRAPDOOR) {
        p = (ulong_t *)(HYBERPHYSMEM + 0x500);          // dos initial dir image
        memcpy(p, (char *)hyberOpPkt[HOP_DOSDIR], 64);  // 2 entries
        p = (ulong_t *)(HYBERPHYSMEM + 0x700);          // dos startup bio
        memcpy(p, (char *)hyberOpPkt[HOP_BIOFILE], 512*3);
        p = (ulong_t *)(HYBERPHYSMEM + 0x7c00);         // dos BPB
        memcpy(p, (char *)hyberOpPkt[HOP_BPB], 512);
        BootDos32();
    }

    if (hopopFlgs & HOPOP_REBOOT)
                hyberReboot();

    /* Here we just kill the system */
    hyberStat = HYBR_COMPLETE;
    ShutdownFlag = 1;            /* allow Ctrl_Alt_Del to occur      150101 */
    return(1);                          // TBR TBR TBR
}

int                                                               /* 153620 */
hyberInitInfo(siz)
ulong_t siz;
{
   int  err;
   ulong_t infosize;
   VMAC    ac;

   infosize = siz * PAGESIZE;

   if (err = VMAllocMem(infosize,                                 /* 153620 */
                        infosize,                  /* Commit all */
#ifndef HYBERI13
                        PG_RESIDENT | PG_R | PG_W, /* allocation flags */
#else /* HYBERI13 */
                        PG_CONTIG | PG_RESIDENT |
                        PG_R | PG_W | PG_1M,       /* allocation flags */
#endif /* HYBERI13 */
                        HOBNULL,                   /* PTDA not applicable */
                        (VMHOB)HYBEROWNER,         /* owner */
                        HOBNULL,                   /* Not an MTE object */
                        NA,                        /* No Selector Mgr flags */
                        NA,                        /* No block number */
                        SSToDS(&ac)))              /* Return Buffer */
       return (err);                                              /* 153620 */
   pHyberInfoHdr = (struct hyberInfoHdr_s *)ac.ac_va;
   pHyberInfo = (struct hyberInfo_s *)(ac.ac_va + PAGESIZE);
   hyberInfoHob = ac.ac_hob;
   memset(pHyberInfoHdr, 0, infosize);
   strcpy(pHyberInfoHdr->hih_magicID,"Hybernating");
   return (0);                                                    /* 153620 */
}

#ifdef HYBERI13
int                                                               /* 153620 */
hyberAllocIoBuf(void)
{
   int      err;
   ulong_t  size = 17*PAGESIZE;                                   /* xxxxxx */
   VMAC     ac;

   do {                                                           /* xxxxxx */
       size -= PAGESIZE;            /* start from 16 pages */     /* xxxxxx */
       err = VMAllocMem(size,                                     /* xxxxxx */
                        size,                      /* Commit all     xxxxxx */
                        PG_CONTIG | PG_RESIDENT |
                        PG_R | PG_W | PG_1M,       /* allocation flags */
                        HOBNULL,                   /* PTDA not applicable */
                        (VMHOB)HYBEROWNER,         /* owner */
                        HOBNULL,                   /* Not an MTE object */
                        NA,                        /* No Selector Mgr flags */
                        NA,                        /* No block number */
                        SSToDS(&ac));              /* Return Buffer */
   } while (err && (size > PAGESIZE)); /* needs at least 1 page *//* xxxxxx */

   if (err)                                                       /* xxxxxx */
       return (err);                                              /* xxxxxx */

   pHyberIoBuf = (char *)ac.ac_va;
   hyberIoBufSz = size;                                           /* xxxxxx */
   return (0);                                                    /* 153620 */
}
#endif /* HYBERI13 */

int                                                               /* 153620 */
hyberMapPhysMem()
{
   ulong_t i;
   ulong_t siz;
   ulong_t err;
   ulong_t paddr;
   VMAC    ac;

   // 1024 = pte's per page
   siz = ((pgPhysMax+1024)/1024) * PAGESIZE;
   if (err = VMAllocMem(siz,                                      /* 153620 */
                        siz,                       /* Commit all */
                        PG_RESIDENT | PG_R | PG_W, /* allocation flags */
                        HOBNULL,                   /* PTDA not applicable */
                        (VMHOB)HYBEROWNER,         /* owner */
                        HOBNULL,                   /* Not an MTE object */
                        NA,                        /* No Selector Mgr flags */
                        NA,                        /* No block number */
                        SSToDS(&ac)))              /* Return Buffer */
       return (err);                                              /* 153620 */
   hyberPhysPT = (ulong_t *)ac.ac_va;
   hyberPhysHob = ac.ac_hob;
   // build the pte's
   memset(hyberPhysPT, 0, siz);
   for (i=0; i <= pgPhysMax; i++)
        hyberPhysPT[i] = (i << PAGESHIFT) | PT_PRESENT | PT_RING3WRITE;
   // make the pde entries
   for (i=0; i < siz/PAGESIZE; i++) {
        VMLinToPhys((ulong_t)((char *)hyberPhysPT + (i * PAGESIZE)),
                        SSToDS(&paddr));
        pgpPageDir[(HYBERPHYSMEM >> PAGEDIRSHIFT) + i].pt_long =
                        paddr | PT_PRESENT | PT_RING3WRITE;
   }
   return (0);                                                    /* 153620 */
}
#pragma code_seg()                                          /* 159979 Start */

#pragma code_seg("HIGH32CODE", "CODE")
void hyberRestart()
{
    ulong_t     procflags;

    /*
     * This routine must reside in Resident memory due to the fact
     * that the DASD device driver can not handle page faults without
     * the restore notification first.
     */

    procflags = TKDisable();     /* interrupt is disable on entry */
    NotifyDDSaveRestore(0x1L);   /* send restore notification to DDs */
    TKEnable(procflags);         /* keep interrupt disable before going */
                                 /*   to the real hyberRestart code     */
    hyberRestartNow();
}
#pragma code_seg()

#pragma code_seg("PROFSWAP_TEXT", "CODE")
void hyberRestartNow()                                        /* 159979 End */
{
    ulong_t     *p;
    ulong_t     retESP;

    /* note: interrupts disabled */

    /* first restore pd0 */
    p = (ulong_t *)pHyberInfoHdr->hih_cr3 + HYBERPHYSMEM;
    *p = pHyberInfoHdr->hih_pt0;

#ifdef HYBERI13
    /* next restore temp gdt entries */
    memcpy((char *)(pHyberInfoHdr->hih_gdt2)+TEMPGDT,
           pHyberInfoHdr->hih_gdtsave, 16);
#endif /* HYBERI13 */

    hyberStat = HYBR_COMINGUP;
#if HYBERDEBUG_S >= 2
    {
    ulong_t     i;
    struct pf_s *ppf;
    struct vp_s *pvp;
    for (i=0; i < pHyberInfoHdr->hih_pfcnt; i++)
        if (pHyberInfo[i].hi_chksum != hyberChksumPage(pHyberInfo[i].hi_pfnum))
                pHyberInfo[i].hi_sfnum = 1;
    for (i=0; i < pHyberInfoHdr->hih_pfcnt; i++) {
        if (pHyberInfo[i].hi_sfnum == 1) {
                ppf = pgFrameToPF(pHyberInfo[i].hi_pfnum);
                pvp = ppf->pf_pvp;
                printf("chksum(%lx/%lx): restored page %x - hob = %x\n",
                        pHyberInfo[i].hi_chksum,
                        hyberChksumPage(pHyberInfo[i].hi_pfnum),
                        pHyberInfo[i].hi_pfnum, pvp->vp_hob);
        }
    }
    hyberDispInfo("back in the kernel");
//  int3();
    }
#endif /* HYBERDEBUG_S >= 2 */

    retESP = pHyberInfoHdr->hih_ebp;
    VMFreeMem((ulong_t)pHyberInfoHdr, HOBNULL, 0);
#ifdef HYBERI13
    VMFreeMem((ulong_t)pHyberIoBuf, HOBNULL, 0);
#endif /* HYBERI13 */
    VMFreeMem((ulong_t)hyberPhysPT, HOBNULL, 0);
    hyberDispInfo("system released");
    hyberDone(retESP);
}

hyberOK(DFcnt)
ulong_t *DFcnt;
{
   /* DFcnt does double duty;
      - on invocation it tells us whether we are to actually do the
        allocation using the swap file or not (if not its a "sticky" hybernat)
      - on return it gives the new swapfile size if the swapfile was used else
        the size of the file which will needed for the hybernation
   */
   int sticky;
//156773   long availspace;
//156773   long needed;                                 // count of swapfile space
//156773   long onHand;
   ulong_t procflags;
//156773   ulong_t rc;

   sticky = (int)*DFcnt;
   // if not sticky we must have the swapper going
//156773    if (smInitialized == 0 && !sticky)
//156773         return(ERROR_SWAPPER_NOT_ACTIVE);

   // if it is sticky we cannot allow the swapfile to have any content (this
   // is because the allocation within the swapfile can change over time)
   if (sticky && smDFMac) {
        *DFcnt = SMcDFInuse;    // return the number of disk frames in use
        return(ERROR_BAD_ENVIRONMENT);
   }

   /* Only one at a time */
   procflags = TKDisable();
   if (hyberStat) {
        TKEnable(procflags);
        return(ERROR_INVALID_ACCESS);
   }
   hyberStat = HYBR_OK;
   TKEnable(procflags);

   /* This is a worst case sizing for disk space needed */
//156773    needed = pgPhysPages + MAXPAGEOUTEXPECTED;   // allow xtra     /* 153620 */

   /* from here on we deal with space allocation; if its sticky just return
      whats needed; if regular do the swapper allocation */

//156773    if (sticky) {
        *DFcnt = pgPhysPages;                                     /* 156773 */
        return(0);
//156773    }

   /* Check to see if we already have enough */
//156773    onHand = smFileSize - smDFMac;
//156773    if (onHand > needed)
//156773         goto done;

   /* Looks like we need to grow it - is there enough space */
//156773    if ((rc = smSIOSpaceQuery(SSToDS(&availspace))) != NO_ERROR) {
//156773         PRINT1("smSIOSpaceQuery failed - %x\n", rc);
//156773         return(rc | 0x300);
//156773    }

//156773    availspace -= (signed long)smFileSize;               // get the real picture
//156773    if ((needed - onHand) > availspace)
//156773         return(ERROR_DISK_FULL);

   /* It appears that we should have enough space available - now
      we will prevent the system from making any changes to the swap
      file, then expand the file. When we return from hybernation we will
      restore the file to its original size.
   */
//156773    if (PGGlobalFlags & PG_DO_OVERCOMMIT)
//156773         return(ERROR_NOT_SUPPORTED);
//156773    if ((rc = KSEMRequestMutex((HKSEM)&SMSwapperSem, 0)) == NO_ERROR) {
//156773         smSetSwapFlags(SM_TCB_SWAPPING);
//156773         rc = smSIOResize(smFileSize + needed);
//156773         smClearSwapFlags(SM_TCB_SWAPPING);
//156773         if (rc != NO_ERROR) {
//156773                 KSEMReleaseMutex((HKSEM)&SMSwapperSem);
//156773                 rc |= 0x200;
//156773         } else
//156773                 PGGlobalFlags |= PG_SWAPFILE_LOCKED;
//156773    }
//156773    if (rc)
//156773         return(rc);

//156773    PRINT1("Grew swap file to %lx\n", smFileSize + needed);

//156773 done:
//156773    hyberswapstart = smDFMac + MAXPAGEOUTEXPECTED;                 /* 153620 */
//156773    *DFcnt = smFileSize + needed;
//156773    return(0);
}

ulong_t
hyberFileCheck(buf)
char *buf;
{
        ulong_t         i;
        ulong_t         failcode;
        sf_entry        *psft;
        ulong_t         sft_len;
        MFT_Entry       *pmft;
        sfdFATFS        *pfat;
        sftsfi          *psfi;
#define REJECT(x)       { failcode = x; goto failed; }

        getSFTExtent(SSToDS(&psft), SSToDS(&sft_len));
        for (i=0; i<sft_len; i++, psft++) {
                // first cull out those which we don't care about
                if (psft->sf_ref_count == 0)            // not in use
                        continue;
                if (psft->sf_flags & SF_PIPE)           // pipe
                        continue;
                if (psft->sf_flags & DEVID_DEVICE)      // a device
                        continue;

                // now stuff we do care about
                if (psft->sf_flags & SF_ISNET)
                        REJECT(1);                      // remote file

                pfat = (sfdFATFS *)&psft->sf_fsd;
                psfi = (sftsfi *)&psft->sf_fsi;
                if (psfi->sfi_mode & open_for_write ||
                                        psfi->sfi_mode & open_for_both) {
                        if (!strncmp((char *)&pfat->sfdFAT_name, "SWAPPER DAT",
                                                        SFNAMELEN))
                                continue;
                        if (!strncmp((char *)&pfat->sfdFAT_name, "EA DATA  SF",
                                                        SFNAMELEN))
                                continue;
                        REJECT(2);                      // writable file
                }
        }
        return(NO_ERROR);
failed:
        pmft = (MFT_Entry *)psft->sf_MFT;
        if (buf && pmft != (MFT_Entry *)-1) {
                strcpy(buf, (char *)&pmft->mft_name);
#if HYBERDEBUG_S >= 2
                printf("%s\n", (char *)&pmft->mft_name);
#endif /* HYBERDEBUG_S >= 2 */
        }
        return(failcode);
}

// Free any space added to swapper.dat and free the swapper flags
//156773 void
//156773 hyberFreeSwapper() {
//156773
//156773     if (PGGlobalFlags & PG_SWAPFILE_LOCKED) {
//156773         smSetSwapFlags(SM_TCB_SWAPPING);
//156773         smSIOResize(smFileSize);
//156773         smClearSwapFlags(SM_TCB_SWAPPING);
//156773         KSEMReleaseMutex((HKSEM)&SMSwapperSem);
//156773         PGGlobalFlags &= ~PG_SWAPFILE_LOCKED;
//156773     }
//156773 }

int                                                         /* 150281 Start */
hyberFreezeSys(nreq)
ulong_t nreq;
{

#define  SEMREQUEST  0
#define  SEMCLEAR    1

    switch (nreq) {

    case SEMREQUEST:

        // Prevent other threads from running by grabing the hybernation sem

        if (!(flGlobalActions & KM_FF_HYBERNATE)) {    // owner dosn't exist?
            KSEMResetEvent((HKSEM)&hybernateSem);      // claim the KSEM
            flGlobalActions |= KM_FF_HYBERNATE;        // set force flag
            pHybernateTCB = pTCBCur;                   // use the TCB as ID
            return(NO_ERROR);
        }
        return(ERROR_ACCESS_DENIED);

    case SEMCLEAR:

        // Allow normal scheduling activities

        if (flGlobalActions & KM_FF_HYBERNATE) {   // owner exists?
            KSEMPostEvent((HKSEM)&hybernateSem);   // wakeup the waiters
            flGlobalActions &= ~KM_FF_HYBERNATE;   // reset the flag
            pHybernateTCB = 0;                     // clear the ID
            return(NO_ERROR);
        }
        return(ERROR_ACCESS_DENIED);


    default:
        return(ERROR_INVALID_PARAMETER);
    }
}                                                           /* 150281 End */


#ifndef HYBERI13

/*****************************************************************************/
/*                                                                           */
/* Get device information - Read the disk device Identity Data for           */
/*                          controller 0, disk 0.  The 256 word array        */
/*                          is defined in \DOSINC\HYBERHD.H.                 */
/*                                                                           */
/*****************************************************************************/

void
hyberIOinit(register struct mydata *mydata)
{
        ushort_t buf[SECTORSIZE/sizeof(short)];
        ulong_t heads;

        hdWaitNotBusy();
        outb(HD_DRV0, HD_C0 + HD_DHR);       // target controller 0, drive 0
        outb(HD_ID, HD_C0 + HD_CR);          // identify drive command
        hdWaitNotBusy();
        hdReadBlock((char *)SSToDS(buf), SECTORSIZE, 1); // read drive info
        hdWaitNotBusy();

        /* buf[] has the disk drive Identity Data */     // 151409 Start
        if ( (buf[ADV_MODE_INFO] & 1) &&     // current CHS supported and
             (buf[DEF_CYLINDERS] > 1023) ) { // > 1024 cylinders
            heads = buf[CUR_HEADS];          // use current settings
            SpT   = buf[CUR_SPT];
        } else {
            heads = buf[DEF_HEADS];          // use HW defaults
            SpT   = buf[DEF_SPT];
        }                                                // 151409 End
        SpC = heads * SpT;
        maxblk = buf[MAX_IO_BLKS] & 0xff;
        if (maxblk == 0)                     // MULTIPLE I/O command supported?
                maxblk = 1;                  // default to single sector I/O
}

ulong_t
hyberDumpTheSystem(struct mydata *mydata)
{
        int dcount;             /* tracks sectors completed per dlist entry */
        int dgrpsize;           /* sectors per dlist entry */
        int iosize;             /* in sectors */
        int mcount = 0;         /* bytes completed in this group */
        int mgrpsize = 0;       /* size of current memory group */
        int sector;             /* sector read address */
        int sectorsDone;
        int i, j, k;
        ulong_t address;        /* destination address */
        ulong_t status;

        PRINT2("dumpthesystem: maxblk = %x, maxio = %x\n", maxblk, maxio);
        hdWaitNotBusy();
        hyberSetMultiple(maxblk);
        /* We are going to be reading PIC2's IRRegister - point to it now */
        outb(0x0a, 0xa0);
        for (i=j=0; i<=dlSiz; i++) {
                dgrpsize = disklist[i].dl_count;
                dcount = 0;
                sector = disklist[i].dl_sector;
                while (dgrpsize != dcount) {
                        iosize = min(maxio, dgrpsize - dcount);
                        sectorsDone = 0;
                        status = hyberStartio(mydata, sector + dcount, iosize,
                                        FALSE);
                        if (status & HD_ERR) {
                                status = 0x810000 | (status << 8) |
                                                        inb(HD_C0 + HD_ER);
                                goto out;
                        }
                        dcount += iosize;

                        /* memory loop */
                        while (iosize != sectorsDone) {
                                if (mgrpsize == mcount) {
                                    address = HYBERPHYSMEM +
                                        (pagelist[j].pl_frame * PAGESIZE);
                                    mgrpsize = pagelist[j].pl_count * PAGESIZE;
                                    j++;
                                    mcount = 0;
                                }
                                k = min(mgrpsize-mcount,
                                        (iosize-sectorsDone) * SECTORSIZE);
                                status = hdWriteBlock((char *)address+mcount,
                                                                k, maxblk);
                                if (status & HD_ERR) {
                                        status = 0x840000 | (status << 8) |
                                                        inb(HD_C0 + HD_ER);
                                        goto out;
                                }
                                mcount += k;
                                sectorsDone += k / SECTORSIZE;
                        }
                        // The i/o is done - verify xfer was totally completed.
                        // The 500 is arbitrary.
                        for(k=0; status & HD_DRQ && k < 500; k++)
                                status = hdWaitNotBusy();
                        if (status & HD_DRQ) {  // data left - something wrong
                                status = (status << 8) | inb(HD_C0 + HD_ER);
                                status |= 0x830000;
                                goto out;
                        }
                }
        }
        status = 0;                     // indicate success
out:
        hyberSetMultiple(0);
        return(status);
}

void
hyberSetMultiple(int m) {

        if (m == 1)                             // can't do multiple
                return;
        outb(m, HD_C0 + HD_SCR);
        outb(HD_DRV0, HD_C0 + HD_DHR);
        outb(HD_SMM, HD_C0 + HD_CR);
        hdWaitNotBusy();
}

ulong_t
hyberStartio(struct mydata *mydata, unsigned long addr, long len, int read)
{
        long cylnum;
        long cyl;

        cylnum = addr / SpC;
        cyl = addr % SpC;
        hdWaitNotBusy();
        outb((cyl / SpT) | HD_DRV0, HD_C0 + HD_DHR);    /* head/drv */
        while((hdWaitNotBusy() & HD_DRDY) == 0);
        outb(len & 0xff, HD_C0 + HD_SCR);               /* sector count */
        outb(cylnum & 0xff, HD_C0 + HD_CLR);            /* cylinder low */
        outb((cylnum >> 8) & 0xff, HD_C0 + HD_CHR);     /* cylinder high */
        outb((cyl % SpT) + 1, HD_C0 + HD_SNR);          /* sector */
        if (maxblk == 1)
                outb(read ? HD_READ : HD_WRITE, HD_C0 + HD_CR);
        else
                outb(read ? HD_READM : HD_WRITEM, HD_C0 + HD_CR);
        /* for some drives (WD's) there needs to be a small amount of
           time (~1 usec) between the write to the command register
           and the reading of its status */
        hyberDelay();
        hyberOpCount = len < maxblk ? len : maxblk;
        return(hdWaitNotBusy());
}

#endif /* HYBERI13 */

/* just used to mark the end of the file */
void
hyberDummy() {}
