sys7.1-doc-wip/OS/NetBoot/NetBoot.c
2019-07-27 22:37:48 +08:00

361 lines
10 KiB
C

/*
File: NetBoot.c
Contains: The "guts" of the boot management driver.
The actual driver is in assembly and is in NetBoot.a.
Written by: Patrick Dyson
Copyright: © 1989-1990 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<5> 11/9/90 dba <JSM> change <NetBoot.h> to "NetBoot.h"
<4> 3/9/90 PWD For XO: Allocate all memory in the system heap now to
avoid crash when sys heap grows, Inline open for
pc-relative atboot driver, open atboot if pram = 0
(default protocol).
<3> 2/20/90 PWD For XO: Allocate all memory in the system heap now to avoid
crash when sys heap grows, Inline open for pc-relative atboot
driver, open atboot if pram = 0 (default protocol).
<2> 2/1/90 PWD Allocate all memory in the system heap now to avoid crash when
sys heap grows
<1.1> 12/12/89 PWD Added support for self-authenticating images
<1.0> 10/30/89 PWD Adding to EASE
5/1/89 Implemented and minor testing done. Looking at flags stuff now.
4/12/89 New Today
To Do:
*/
#include <Types.h>
#include <stdio.h>
#include <ErrMgr.h>
#include <Errors.h>
#include <Slots.h>
#include <ROMDefs.h>
#include <Memory.h>
#include <OSUtils.h>
#include <Devices.h>
#include <Start.h>
#include <QuickDraw.h>
#include <Traps.h>
#include "NetBoot.h"
/* read/write pram utility. the selector chooses read/write, the pointer
is to a buffer that the caller allocates.
*/
#define PRAMwrite 0
#define PRAMread 1
void TESTPRAM(Ptr buf,short selector);
short rwPRAM( char *prec,
short selector)
{
TESTPRAM((Ptr) prec,selector);
return 0;
};
short set_PRAM( bootVars *bv)
{
(void) rwPRAM((char *)bv,PRAMwrite);
return 0;
};
/* This allocates and builds a bootVars record from our pram, other pram, and low mem.
It will return any error encountered.
*/
short get_PRAM( bootVars **bv)
{
*bv = (bootVars *)NewPtrSysClear(sizeof(bootVars));
if (*bv == NULL) return MemError();
/* Get our pram record */
(void) rwPRAM((Ptr)*bv,PRAMread);
return 0;
};
/* this finds the first boot record matching the passed protocol and returns
the slot, ID and ExtID of said record. It also now fetches the driver name.
This function is called if the protocol is not on the motherboard.
The function result is 0 for success, an the non-zero slot manager error
otherwise.
*/
short find_BPTentry( unsigned char protocol,
char *slot,
char *ID,
char *ExtDev,
char *name)
{
#define SftwrMask 1 /* the 1 bit (don't care about hardware) (IMV5, p.444) */
short err;
SpBlock pb;
pdbzero((char *)&pb,sizeof(SpBlock));
pb.spCategory = CatBoot; /* only booters */
pb.spCType = TypRemote; /* only remote ones */
pb.spDrvrSW = protocol; /* only the protocol we care about */
pb.spTBMask = SftwrMask; /* don't care about hardware */
err = SNextTypeSRsrc(&pb);
#ifdef DEBUG
fprintf(stdout,"slot\tID\tExtDev\tres\trefNum\tcat\tType\n\n");
fprintf(stdout,"%d\t%d\t%d\t%d\t%d\t%d\t%d\n",pb.spSlot,pb.spID,
pb.spExtDev,pb.spIOReserved,pb.spRefNum,pb.spCategory,pb.spCType);
#endif
if (err != 0) return err;
pb.spResult = (Ptr)name;
err = SReadDrvrName(&pb);
#ifdef DEBUG
fprintf(stderr,"read returns: %d\n",err);
#endif
*slot = pb.spSlot;
*ID = pb.spID;
*ExtDev = pb.spExtDev;
return err;
};
/* This finds and opens the protocol boot driver for an associated protocol id.
it calls functions in this module as well as assembly (BootUtils.a).
The boot record can either reside in slot rom or our local table. If it is
a rom based protocol we special case and just look there.
If an error occurs during the initialization routines, we walk out
cleanly - disposing and closing as necessary - and return the error.
*/
#define driveSize 0x4000L /* an arbitrary drive size */
pascal short AddMyDrive(int drvSize, short drvrRef, Ptr storage);
short DoATBootOpen(ParamBlockRec *pb);
short FINDNOPENDRIVER(dGlobals *g)
{
bootVars *pramRec = NULL; /* allocated by get_PRAM */
short error;
unsigned char slot, /* the slot, ID and Ext ID of the boot protocol table entry */
ID,
ExtDev;
ParamBlockRec pb;
char name[34]; /* this will be a pstring of the driver name */
/* read pram */
(void) get_PRAM(&pramRec);
if (pramRec->protocol == DrSwATalk) { /* this is handled locally */
pdbzero((char *)&pb,sizeof(ParamBlockRec)); /* zero that puppy */
pb.ioParam.ioNamePtr = "\p.ATBOOT"; /* a pname */
pb.slotDevParam.ioRefNum = DoATBootOpen(&pb);
}
else { /* look in slot land */
if (NGetTrapAddress(_SlotManager,OSTrap) != GetTrapAddress(_Unimplemented)) {
/* find a boot protocol table entry matching PRAM */
error = find_BPTentry(pramRec->protocol,&slot,&ID,&ExtDev,name);
if (error != 0) goto error_exit;
/* open the associated driver */
pdbzero((char *)&pb,sizeof(SlotDevParam)); /* zero that puppy */
pb.slotDevParam.ioSlot = slot;
pb.slotDevParam.ioID = ID;
pb.slotDevParam.ioNamePtr = (Ptr)name; /* a pname */
error = OpenSlot(&pb,0); /* Open, IMMED */
g->dProtoSlot = slot;
g->dProtoID = ID;
}
else { // out of luck!
error = dProtocolNotFound;
goto error_exit;
};
};
if ((error != 0) || (pb.slotDevParam.ioRefNum == 0)) {
if (error == 0) error = openErr;
goto error_exit;
};
/* return our hard work! */
g->dProto = pramRec->protocol; /* set management driver globals */
g->dProtoRefNum = pb.slotDevParam.ioRefNum;
error_exit:
if (pramRec != NULL) DisposPtr((Ptr)pramRec);
return error;
};
/* DOOPEN
This is called by the open glue of the netboot driver. It needs to check to see
if booting is enabled - if so we install ourselves in the drive queue.
*/
short DOOPEN(short driveRefNum, dGlobals *g)
{ short driveNum;
char flags;
bootVars *pramRec; /* to be allocated by get_PRAM */
if (get_PRAM(&pramRec) || (pramRec == NULL)) return openErr; /* get pram, punt if error */
flags = pramRec->flags;
DisposPtr((Ptr)pramRec);
if ((flags & BOOT_ENABLE) == 0) return openErr; /* if not configured, punt */
/* install ourselves in the drive queue */
driveNum = AddMyDrive(driveSize,driveRefNum, (Ptr) g);
return 0;
};
/* Find the first drive queue entry with the passed refnum and remove it.
While we are at it, dispos of the entry.
*/
void findNRemoveDrive(short refNum)
{
DrvQElPtr dq ;
QHdrPtr QHead = 0x308;
dq = (DrvQElPtr) (QHead->qHead);
while ((dq != (DrvQElPtr)(QHead->qTail)) && (dq->dQRefNum != refNum))
dq = (DrvQElPtr)(dq->qLink);
if (dq->dQRefNum == refNum) {
Dequeue((QElemPtr)dq,QHead);
DisposPtr((Ptr)dq);
};
};
/* DOCLOSE (all caps for the linker!) walks the globals, closing the boot protocol
driver if open, removing any drive queue element that points to us (refNum), and
disposing of our globals.
We return any error as a function result - the assembly translates any error into
a closErr. We ignore any close errors on the boot protocol driver.
We will not be called if the driver storage is null.
*/
short DOCLOSE(dGlobals *g,short refnum)
{
/* find & remove any drive queue entry pointing to us */
findNRemoveDrive(refnum);
/* close the boot protocol driver, if open */
if (g->dProtoRefNum != 0) {
ParamBlockRec pb;
short error;
pb.ioParam.ioCompletion = NULL;
pb.ioParam.ioRefNum = g->dProtoRefNum;
error = PBClose(&pb,0);
};
/* close and dispose of our grafport, if any */
if (g->dMyPort != NULL) {
ClosePort(g->dMyPort);
DisposPtr((Ptr)(g->dMyPort));
g->dMyPort = (GrafPtr)0;
};
/* dispos of our globals */
DisposPtr((Ptr) g);
return 0;
};
/* This is the guts of the read call of the boot management driver. It is called
on response to a read or write to the disk drive. The driver checks for null
globals, then pushes our arguments and JSR's to us. Our responsibilities are:
- check that this is the read that we think that it is
- make sure that we have a protocol boot driver to call
- call the protocol boot driver to get the boot blocks
- map any error to #OffLinErr (non-fatal) or #NoDriveErr (fatal)
- call boot protocol drive a second time for getSysVol
- hook into extfs to trap mountvol call (next boot stage, past _InitFS)
*/
#define aRdCmd 2 /* a read call */
#define aWrCmd 3 /* a write call */
#define ToExtFS 0x3F2L // hook for external file systems
void myExtFSFilter(); // in netBoot.a
short DOREAD(dGlobals *g,ParamBlockRec *pb)
{ short error = 0;
ParamBlockRec callPb;
/* if the low byte of the trap word is read, we are golden! */
if ((pb->ioParam.ioTrap & 0x00FF) != aRdCmd) return FATAL;
/* this moved for sanity reasons... */
error = FINDNOPENDRIVER(g);
if (error) return FATAL;
if ((callPb.ioParam.ioRefNum = g->dProtoRefNum) == 0) return FATAL;
/* make the magic control call on the driver */
callPb.cntrlParam.csCode = getBootBlocks;
callPb.ioParam.ioCompletion = NULL; /* pass through most params */
callPb.ioParam.ioReqCount = pb->ioParam.ioReqCount;
callPb.ioParam.ioPosMode = pb->ioParam.ioPosMode;
callPb.ioParam.ioPosOffset = pb->ioParam.ioPosOffset;
callPb.ioParam.ioBuffer = pb->ioParam.ioBuffer;
callPb.ioParam.ioMisc = pb->ioParam.ioMisc;
error = PBControl(&callPb,0); /* synch call */
if (error == 0) { // if no error, go ahead and ask the boot
// protocol driver to get the sys vol and
// preflight the boot.
netBootPBRec getPB;
getPB.csCode = getSysVol;
getPB.ioCRefNum = g->dProtoRefNum;
error = PBControl((ParmBlkPtr)&getPB,false);
/* save the old toextfs hook (getBootBlocks may have changed it) */
g->doldToExtFS = *((int **) ToExtFS);
/* hook in our routine (assembly, in netBoot.a) */
// This will be the next, and last, hook through the netboot driver. The
// next call should be a close from somewhere.
*((int **) ToExtFS) = (int *) myExtFSFilter;
};
if (error != 0) {
/* save original error code */
g->dLastError = error;
/* map error to something reasonable */
/* negative errors will be non-fatal, positive fatal */
error = (error > 0) ? FATAL : NONFATAL;
};
pb->ioParam.ioActCount = callPb.ioParam.ioActCount;
return error;
};