Job Management Code
I
440
Part PV ■ The Operating System Source Code
After you get the kernel out of the way and when you want to begin loading things to run on
your system, this is where you'll end up. The program loader is in this chapter.
Before I wrote this code, pictures of the complications of writing a loader floated in my head
for quite a while. Writing the loader was every bit as difficult as I imagined. The process of
allocating all the resources, making sure that each was correctly set and filled out, and finally
starting the new task can be complicated. I have tried to break down the process logically and
keep it all in a high-level language.
Reclamation of Resources
Equally as difficult was the death of a job and the reclamation of its valuable resources. In the
functions ExitJobO andchain(), I drift in and out of assembly language to do things such as
switch to a temporary stack when the running task no longer has a valid stack. When things
don't work right, it is usually quite a mess.
The resources you reclaim include all the memory, exchanges, link blocks, request blocks, the
JCB, TSSs, and finally the operating system memory. Pieces of related code in the kernel, the
monitor, and other places provide assistance with resource reclamation.
Job Management Helpers
The file JobCode.ASM provides many small helper functions for job management. These calls,
many of them public functions, are used by almost every module in the operating system. See
Listing 22.1.
Listing 22.1. Job management subroutines.
.DATA
.INCLUDE MOSEDF.INC
.INCLUDE JOB. INC
.INCLUDE TSS.INC
PUBLIC pFreeJCB
DD 0
; Ptr to free Job Control Blocks
PUBLIC pJCBs
DD 0
; JCBs are in allocated memory
PUBLIC nJCBLeft
DD nJCBs
; For Monitor stats
EXTRN MonJCB DB ; Monitor JCB reference
EXTRN _BootDrive DD ; From Main. asm
;======= End data, Begin Code
.CODE
InitNewJCB is used initially by the OS to fill in the first two
jobs (Monitor & Debugger)
Chapter 22 ■ Job Management Code
|
443
MODIFIES : pFreeJCB
This routine will place the jcb pointed to by EAX back into the free
pool of JCBs pointed to by (pFreeJCB) if EAX is not NIL.
This invalidates the JCB by placing 0 in JcbPD.
CMP EAX, 0
JE DispJCBDone
MOV DWORD PTR [EAX+JcbPD]
MOV EBX, pFreeJCB
MOV [EAX+NextJCB],EBX
MOV pFreeJCB, EAX
INC DWORD PTR _nJCBLeft
DispJCBDone:
RETN :
If pJCBin = NIL THEN Return;
Invalidate JCB
EBX has OS ptr to free list
Move it into newly freed JCB
Move ptr to newly frred JCB to OS
GetpCrntJCB
Returns a pointer to the current Job Control Block in EAX.
This is based on which Task is executing. All TSSs are
assigned to a Job. A Job may have more than one Task.
INPUT: Nothing
OUTPUT: EAX — Linear Address of current JCB
USED: EAX, EFlags
2PUBLIC GetpCrntJCB:
MOV EAX, pRunTSS ; Current Task State Segment
MOV EAX, [EAX+TSS_pJCB] ; Pointer to JCB
RETN
GetCrntJobNum
Many OS functions deal with the Job number. The Job number
is a field in the JCB structure.
Returns the Job number for the currently executing task.
This is based on which Task is executing. All TSSs are
assigned to a Job! A Job may have more than one Task.
INPUT: Nothing
OUTPUT: EAX — Current Job Number
USED: EAX, EFlags
PUBLIC GetCrntJobNum:
CALL GetpCrntJCB
MOV EAX, [EAX+JobNum] ;Current JCB
RETN
GetpJCB
Returns a pointer to a Job Control Block identified by number
in EAX. All TSSs are assigned to a Job.
INPUT: EAX - Job Number of desired pJCB
Part PV ■ The Operating System Source Code Listing 22.1. continued OUTPUT: EAX — Linear Address of the JCB or 0 for invalid number USED: EAX, EFlags PUBLIC GetpJCB: PUSH EDX CMP EAX, 1 JNE GetpJCBI MOV EAX, OFFSET MonJCB POP EDX RETN GetpJCBI : CMP EAX, 2 JNE GetpJCB2 MOV EAX, OFFSET DbgJCB POP EDX RETN GetpJCB2: CMP EAX, nJCBs+2 JLE GetpJCB3 XOR EAX, EAX POP EDX RETN GetpJCB3: SUB EAX, 3 MOV EDX, SJCB MUL EDX ADD EAX, pJCBs POP EDX RETN ;Add in two static JCBs ;Within range of JCBs ;Take off static JCBs+1 (make it an offset) ;Times size of JCB ;Now points to desired JCB GetJobNum Many OS functions deal with the Job number. The Job number is a field in the JCB structure. Returns the Job number for the pJCB in EAX in EAX. INPUT: EAX pJCB we want job number from. OUTPUT: EAX - Current Job Number USED: EAX, EFlags PUBLIC GetJobNum: MOV EAX, [EAX+JobNum] RETN jCurrent JCB AllOCJCB (NEAR) This allocates a new JCB (from the pool). This is a NEAR call to support the public job management calls in high level languages. Procedural Interface : AllocJCB(pdJobNumRet, ppJCBRet) :ercType
Chapter 22 ■ Job Management Code S pdJobNumRet is the number of the new JCB. pJCBRet is a pointer where you want the pointer to the new JCB is returned. ErcNoMoreJCBs will be returned if no more JCBs are avaialble. ; pdJobNum EQU [EBP+12] ; pJCBRet EQU [EBP+8] PUBLIC AllocJCB: PUSH EBP MOV EBP, ESP CALL NewJCB Get a new JCB OR EAX, EAX JNZ SHORT AJCB01 We got one! MOV EAX, ErcNoMoreJCBs Sorry, out of them! MOV ESP, EBP POP EBP RETN 8 AJCB01: MOV ESI, [EBP+8] pJCBRet MOV [ESI], EAX MOV ESI, [EBP+12] Job Num CALL GetJobNum MOV [ESI], EAX XOR EAX, EAX No error MOV ESP, EBP POP EBP RETN 8 DeAllocJCB (NEAR) This Deallocates a JCB (returns it to the pool). This is a NEAR call to support the public job management calls in high level languages in the OS code. Procedural Interface : DeAllocJCB(pJCB) :ercType pJCB is a pointer the JCB to be deallocated. ErcNoMoreJCBs will be returned if no more JCBs are avaialble. pJCB EQU [EBP+8] PUBLIC _DeAllocJCB: PUSH EBP MOV EBP, ESP MOV EAX, [EBP+8] CALL DisposeJCB XOR EAX, EAX MOV ESP, EBP POP EBP pJCB Get a new JCB
I
446
Part IV ■ The Operating System Source Code
Listing 22.1. continued
BEGIN PUBLIC FAR JOB CALLS
GetpJCB
This PUBLIC returns a pointer to the JCB for the JobNum
you specifiy.
Procedural Interface :
GetpJCB(dJobNum, pJCBRet) :ercType
dJobNum is the number of the JCB you want.
pJCBRet is a pointer where you want the JCB returned.
ErcBadJobNum will be returned if dJobNum is out of range
ErcBadJobNum will be returned if dJobNum is invalid
or 0 will be returned with the data.
dJobNum EQU [EBP+16]
pJCBRet EQU [EBP+12]
PUBLIC _GetpJCB: ;
PUSH EBP ;
MOV EBP, ESP ;
MOV EAX, [EBP+16] ;Job Num
OR EAX, EAX
JZ GetpJcbBad
CMP EAX, nJCBs + 2;
JBE GetpJcbOK
GetpJcbBad:
MOV EAX, ErcBadJobNum
MOV ESP, EBP
POP EBP
RETF 8
GetpJcbOk:
CALL GetpJCB
MOV ESI, [EBP+12]
MOV [ESI], EAX
CMP DWORD PTR [EAX+JcbPD], 0
JNE GetpJCBOkl
MOV EAX, ErcInvalidJCB
MOV ESP, EBP
POP EBP
RETF 8
GetpJcbOkl :
XOR EAX, EAX
8 MOV ESP, EBP
POP EBP
RETF 8
0 is invalid
Dynamic + 2 static
;puts address of JCB in EAX
; pJCBRet
Is this a valid JCB
JCB we are pointing to is unused
Chapter 22 ■ Job Management Code H GetJobNum This PUBLIC returns the number for the current Job. This is the job that the task that called this belongs to. Procedural Interface : GetJobNum (pJobNumRet) :ercType pJCBRet is a pointer where you want the JCB returned. pJobNumRet EQU [EBP+12] PUBLIC _GetJobNum: PUSH EBP MOV EBP, ESP CALL GetCrntJobNum MOV ESI, [EBP+12] MOV [ESI], EAX XOR EAX, EAX POP EBP RETF 4 Leave jobnum in EAX pJobNumRet No Error GetSystemDisk This PUBLIC returns a single byte which represents the disk that the system booted from. This is from the public variable _BootDrive defined in Main. asm. It returns 0-n (which corresponds to drive A-x) This code is here for lack of a better place. It's really not a filesystem function either. And will still be needed if a loadable filesystem is installed. Procedural Interface : GetSystemDisk (pSysDiskRet) :ercType pSysDiskRet is a pointer to a byte where the number representing the system disk is returned. pSysDiskRet EQU [EBP+12] PUBLIC _GetSystemDisk PUSH EBP MOV EBP, ESP MOV EAX, _BootDrive AND EAX, 7Fh MOV ESI, [EBP+12] MOV [ESI], AL XOR EAX, EAX POP EBP RETF 4 . ================= MODULE END get rid of high bits pJobNumRet No Error
aaq I Part IV ■ The Operating System Source Code Job Management Listing This file, Jobc.c, contains all the major high-level functions for job management. Functions for starting and ending jobs and public functions for working with the job control block are contained in this module. See Listing 22.2. Listing 22.2. Job management code. /* This file contains functions and data used to support loading or terminating jobs (or services). It contains the public functions: Chain () Loads new job run file in current JCB & PD LoadNewJobO Loads a new job into a new JCB & PD ExitJob() Exits current job, loads ExitJob if specified GetExitJob() SetExitJob() Gets run file name that will be loaded upon ExitJob Sets run file name to load upon ExitJob SetCmdLine( ) GetCmdLine() Sets the command line for next job Gets the command line for the current job GetPath() Setpath() Gets the path prefix for the current job Sets the path prefix for the current job SetUserName( ) GetUserName() Sets the User-name for the current job Gets the Username for the current job #define U32 unsigned long #define S32 long #define U16 unsigned int #define S16 int #define U8 unsigned char #define S8 char #define TRUE 1 #define FALSE 1 #include “MKernel.h” #include “MMemory.h” #include “MData.h” #include “MTimer.h” #include “MVid.h” #include “MKbd.h” ^include “MJob.h” ^include “MFiles.h” ^include “runfile.h” #define MEMUSERD 7 /* User Writable (Data) */ ^define ErcOpCancel 4 /’ ^define ErcOutOfRange 10 /’ ^define ErcBadJobNum 70 /’ Operator cancel */ Bad Exchange specified in OS call */ A Bad job number was specified */
Chapter 22 ■ Job Management Code
|
449 |
#define ErcNoExitJob 76 /* No ExitJob specified on ExitJob(n) */
#define ErcBadRunFile 74 /* Couldn't load specified ExitRunFile */
/* Near Support Calls from JobCode.ASM */
extern long AllocJCB(long *pdJobNumRet , char *ppJCBRet);
extern long RemoveRdyJob(char *pJCB) ;
extern long GetExchOwner(long Exch, char *pJCBRet);
extern long SetExchOwner(long Exch, char *pNewJCB);
extern long SendAbort(long JobNum, long Exch);
/* Temporary NEAR externals from the monitor program for debugging */
extern long xprintf(char *fmt, ...);
extern U32 Dump(unsigned char *pb, long cb) ;
/* We switch to this stack when we clean out a user
PD before we rebuild it.
*/
static long TmpStackf 128] ; /* 512 byte temporary stack */
/* Used for allocating/filling in new JCB */
static struct JCBRec *pNewJCB; /* Used to access a JCB */
static struct JCBRec *pTmpJCB;
static struct JCBRec *pCrntJCB;
static long JobNum;
/* For ExitJob and Chain cause They can't have local vars! */
static long JobNumE, job_fhE;
static long ExchE, ercE, iE;
static long BogusMsg[2];
static long *pPDE;
static char *pExchJCBE
static long KeyCodeE;
static char aFileE[80];
static long cbFileE;
extern unsigned long KillExch; /* From the Monitor */
/* Run file data */
static char *pCode, *pData, *pStack; /* Ptrs in User mem to load to */
static long sCode, sData, sStack; /* Size of segments */
static unsigned long oCode, oData; /* Offset in file to Code & Data */
static long offCode = 0, /* Virtual Offset for Code & Data Segs */
offData = 0;
static unsigned long nCDFIX = 0,
oCDFIX = 0,
nCCFIX = 0,
OCCFIX = 0,
nDDFIX = 0,
Part PV ■ The Operating System Source Code Listing 22.2. continued ODDFIX = 0, nDCFIX = 0, ODCFIX = 0; static char pStart, filetype; static struct tagtype tag; /************ INTERNAL SUPPORT CALLS ******************/ This deallocates all user memory in a PD. This is used when we ExitJob to free up all the memory. We get the pointer to the PD and “walk” through all the User PTs deallocating linear memory. When all PTs have been cleaned, we eliminate the user PDEs. This leaves a blank PD for reuse if needed. NOTE: This must be called from a task that is running in the JCB that owns the memory! static void CleanUserPD(long *pPD) { long i, j , k, ere; unsigned long *pPT; char *pMem; for (i=768; i<1024; i++) { /* Look at each shadow PDE */ if (pPD[i]) { /* If it's a linear address (non 0)*/ pPT = pPD[i] & 0xFFFFF000; /* Point to Page Table */ for (j=0; j<1024; j++) { /* Get Beginning address for each run to deallocate */ k = 0; /* nPages to deallocate */ pMem = ((i-512) * 0x400000) + (J * 4096); while ((pPT[j++]) && (j<1024)) k++; /* one more page */ if (k) { /* we have pages (one or more) */ ere = DeAllocPage(pMem, k); } This opens, reads and validates the run file. If all is well, it leaves it open and returns the file handle, else it closes the run file and returns the error to the caller. The caller is responsible for closing the file 1 1 1
Chapter 22 ■ Job Management Code static long GetRunFile(char *pFileName, long cbFileName, long *pfhRet) { long ere, i, fh, dret; char fDone, junk; off Code = 0; offData = 0; nCDFIX = 0 oCDFIX = 0 nCCFIX = 0 oCCFIX = 0 nDDFIX = 0 ODDFIX = 0 nDCFIX = 0 oDCFIX = 0 pfhRet = 0; / default to 0 / / Mode Read, Stream type / ere = OpenFile(pFileName, cbFileName, 0, 1, &fh); if (!erc) { / File opened OK / fDone = 0; while ((!erc) && (IfDone)) { tag. id = 0; ere = ReadBytes (fh, &tag, 5, &dret); switch (tag. id) { case IDTAG: ere = ReadBytes (fh, &filetype, 1, &dret); if ((filetype < 1) || (filetype > 3)) ere = ErcBadRunFile; break; case SEGTAG: ere = ReadBytes (fh, &sStack, 4, &dret); if (!erc) ere = ReadBytes (fh, &sCode, 4, &dret); if (!erc) ere = ReadBytes (fh, SsData, 4, &dret); break; case DOFFTAG: ere = ReadBytes (fh, &offData, 4, &dret); break; case COFFTAG: ere = ReadBytes (fh, &offCode, 4, &dret); break; case STRTTAG: ere = ReadBytes (fh, SpStart, 4, &dret); break; case CODETAG: ere = GetFileLFA(fh, &oCode); if (!erc) ere = SetFileLFA(f h, oCode+tag.len) ; / skip it / break; case DAT AT AG: ere = GetFileLFA(fh, &oData); if (!erc) ere = SetFileLFA(fh, oData+tag.len) ; / skip it */ continues
452| Part IV ■ The Operating System Source Code Listing 22.2. continued break; case CDFIXTAG: ere = GetFileLFA(fh, &0CDFIX); nCDFIX = tag.len/4; if (!erc) ere = SetFileLFA(fh, oCDFIX+tag.len) ; /* skip it */ break; case CCFIXTAG: ere = GetFileLFA(fh, &0CCFIX); nCCFIX = tag.len/4; if (!erc) ere = SetFileLFA(fh, oCCFIX+tag.len) ; /* skip it */ break; case DDFIXTAG: ere = GetFileLFA(fh, &0DDFIX); nDDFIX = tag.len/4; if (!erc) ere = SetFileLFA(fh, oDDFIX+tag.len) ; /* skip it */ break; case DCFIXTAG: ere = GetFileLFA(fh, &0DCFIX); nDCFIX = tag.len/4; if (!erc) ere = SetFileLFA(fh, oDCFIX+tag.len) ; /* skip it */ break; case ENDTAG: fDone = TRUE; break; default: ere = GetFileLFA(fh, &i); if (tag.len > 1024) ere = ErcBadRunFile; if (!erc) ere = SetFileLFA(fh, i+tag.len); /* skip it */ if (ere) ere = ErcBadRunFile; break; } if (ere) CloseFile(fh); } if (!erc) *pfhRet = fh; if (ere) xprintf(“Erc from GetRunFile in JobC: %d\r\n”, ere); return (ere); } /*********** PUBLIC CALLS FOR JOB MANAGEMENT ************/ /••••••••••••••••• .••.•.•..*••**.*.**.*. /
Chapter 22 ■ Job Management Code |453 long far _SetExitJob(char pRunFile, long dcbRunFile) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job / if (dcbRunFile > 79) return (ErcBadJobParam) ; else if ( IdcbRunFile) pTmpJCB->JcbExitRF[0] = 0; else { CopyData(pRunFile, &pTmpJCB->JcbExitRF[ 1 ] , dcbRunFile); pTmpJCB->JcbExitRF[0] = dcbRunFile; } return(0) : long far _GetExitJob(char pRunRet, long pdcbRunRet) { long JobNum, i; GetJobNum(&JobNum) ; GetpJCB (JobNum, &pTmpJCB); / Get pJCB to current Job / i = pTmpJCB->JcbExitRF[0] ; if (i) CopyData(&pTmpJCB->JcbExitRF[1 ] , pRunRet, i) ; ♦pdcbRunRet = i; return(0) ; } long far _SetPath(char pPath, long dcbPath) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job / if (dcbPath > 69) return (ErcBadJobParam); else if (IdcbPath) pTmpJCB->sbPath[0] = 0; else { CopyData(pPath, &pTmpJCB->sbPath[ 1 ] , dcbPath); pTmpJCB->sbPath[0] = dcbPath; } return(0) ; } /it*********************************************/ long far _GetPath(long JobNum, char *pPathRet, long *pdcbPathRet) { long i, ere; ere = GetpJCB(JobNum, &pTmpJCB); /* Get pJCB to JobNum */ if (lerc) { i = pTmpJCB->sbPath[0];
ac / Part TV ■ The Operating System Source Code Listing 22.2. continued if (i) CopyData(&pTmpJCB->sbPath[1 ] , pPathRet, i) ; *pdcbPathRet = i; } return(erc) ; } long far _SetCmdLine(char pCmd, long dcbCmd) { long JobNum; GetJobNum(&JobNum) ; GetpJCB (JobNum, &pTmpJCB) ; / Get pJCB to current Job */ if (dcbCmd > 79) return (ErcBadJobParam) ; else if ( IdcbCmd) pTmpJCB->JcbCmdLine[0] = 0; else { CopyData(pCmd, &pTmpJCB->JcbCmdLine[1 ] , dcbCmd); pTmpJCB->JcbCmdLine[0] = dcbCmd; } return(0) ; } long far _GetCmdLine(char *pCmdRet, long pdcbCmdRet) { long JobNum, i; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB) ; / Get pJCB to current Job */ i = pTmpJCB->JcbCmdl_ine[0] ; if (i) CopyData(&pTmpJCB->JcbCmdLine[1 ] , pCmdRet, i); *pdcbCmdRet = i; return(0) ; long far _SetUserName(char pUser, long dcbUser) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job */ if (dcbUser > 29) return (ErcBadJobParam); else if ( IdcbUser) pTmpJCB->sbUserName[0] = 0; else { CopyData(pUser, &pTmpJCB->sbUserName[ 1 ] , dcbUser); pTmpJCB->sbUserName[0] = dcbUser; return(0)
Chapter 22 M Job Management Code s long far _GetUserName(char pUserRet, long pdcbUserRet) { long JobNum, i; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job / i = pTmpJCB->sbUserName[0] ; if (i) CopyData(&pTmpJCB->sbUserName[1 ] , pUserRet, i); pdcbUserRet = i; return(0) ; } long far _SetSysIn(char pName, long dcbName) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job / if ((dcbName > 49) J| (IdcbName)) return (ErcBadJobParam) ; else { CopyData(pName, &pTmpJCB->JcbSysIn[1 ] , dcbName); pTmpJCB->JcbSysIn[0] = dcbName; } return(0) ; } /A*********************************************/ long far _GetSysIn(char *pFileRet, long *pdcbFileRet) { long JobNum, i; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); /* Get pJCB to current Job */ i = pTmpJCB->JcbSysIn[0] ; if (i) CopyData(&pTmpJCB->JcbSysIn[1 ] , pFileRet, i); *pdcbFileRet = i; return(0) ; } long far _SetSysOut (char *pName, long dcbName) { long JobNum; GetJobNum(&JobNum) ; GetpJCB(JobNum, &pTmpJCB); /* Get pJCB to current Job */ if ((dcbName > 49) \ (IdcbName)) return (ErcBadJobParam); else { CopyData(pName, &pTmpJCB->JcbSys0ut[1 ] , dcbName); pTmpJCB->JcbSysOut[0] = dcbName;
|436 Part IV ■ The Operating System Source Code Listing 22.2. continued } return(0) ; } long far _GetSysOut(char *pFileRet, long pdcbFileRet) { long JobNum, i; GetJobNumf&JobNum) ; GetpJCB (JobNum, &pTmpJCB) ; / Get pJCB to current Job */ i = pTmpJCB->JcbSysOut[0] ; if (i) CopyData(&pTmpJCB->JcbSys0ut[1 ] , pFileRet, i); *pdcbFileRet = i; return (0) ; } long far _SetJobName(char pName, long dcbName) { long JobNum; GetJobNum(&JobNum); GetpJCB(JobNum, &pTmpJCB); / Get pJCB to current Job */ if (dcbName > 13) dcbName = 13; if (dcbName) CopyData(pName, &pTmpJCB->sbJobName[ 1 ] , dcbName); pTmpJCB->sbJobName[0] = dcbName; return(0) ; } This creates and loads a new Job from a RUN file. This returns ErcOK and new job number if loaded OK else an error is returned. long far _LoadNewJob(char *pFileName, long cbFileName, long *pJobNumRet) { long ere, i, fh, dret, nPages; unsigned long *pPD, *pPT, *pVid, *pOSPD; long pFix; U32 PhyAdd; ere = GetRunFile(pFileName, cbFileName, &fh); if (!erc) { / We set these to zero so we can tell if they have been allocated in case we fail so we know what to deallocate */ JobNum ■ 0; pNewJCB = 0;
Chapter 22 ■ Job Management Code pPD = 0; pPT = 0; pVid = 0; ere = AllocJCB(&JobNum, &pNewJCB); /* Alloc OS memory pages required for new job */ if (!erc) ere = AllocOSPage(3, &pPD) ; /* Job's PD, PT * pVirtVid */ pPT = pPD + 4096; /* 1 page later */ pVid = pPD +8192; /* 2 pages later */ if (!erc) { FillData(pPT, 4096, 0); /* Zero user PT */ FillData(pVid, 4096, 0); /* Zero user video */ GetpJCB(1, &pTmpJCB); /* Get OS pJCB */ pOSPD = pTmpJCB->pJcbPD; /* Pointer to OS PD */ GetPhyAdd(1, pPT, &PhyAdd); /* Get Phy Add for new PT */ PhyAdd |= MEMUSERD; /* Set user code bits in PDE */ pOSPD[256] = PhyAdd; /* Make User PDE in OS PD */ pOSPD[768] = pPT; /* Shadow Linear Address of PT */ /* xprintf (“pOSPD : %08x\r\n”, pOSPD) ; xprintf (“pUserPD: %08x\r\n”, pPD) ; xprintf (“pUserPT: %08x\r\n”, pPT); xprintf (“PhyAdd : %08x\r\n”, PhyAdd); ReadKbd(&KeyCodeE, 1); */ /* Now we can allocate User Job Memory */ /* Allocate user memory for Stack Code and Data */ /* This is STILL done in the OS PD */ nPages = sStack/4096; if (sStack%4096) nPages++; ere = AllocPage(nPages, SpStack); sStack = nPages * 4096; /* set to whole pages */ nPages = sCode/4096; if (sCode%4096) nPages++; if (!erc) ere = AllocPage(nPages, &pCode); nPages = sData/4096; if (sData%4096) nPages++; if (!erc) ere = AllocPage( nPages, &pData); /* Right now, the OS PD looks exacly like we want the User PD to look. We will now copy the entire OS PD into the User's New PD.
/col Part IV ■ The Operating System Source Code Listing 22.2. continued / CopyData(pOSPD, pPD, 4096); / Copy OS PD to User PD / / All Job memory is now allocated, so let's LOAD IT! / if (!erc) ere = SetFileLFA(fh, oCode); if (!erc) ere = ReadBytes (fh, pCode, sCode, &dret); if (!erc) ere = SetFileLFA(fh, oData); if (!erc) ere = ReadBytes (fh, pData, sData, &dret); / Now that we have read in the code and data we apply fixups to these segments from the runfile / if (!erc) { if (nCDFIX) { ere = SetFileLFA(fh, oCDFIX); / back to fixups / while ((nCDFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pCode + i; / Where in CSeg */ *pFix = pFix - offData + pData; } } if (nCCFIX) { ere = SetFileLFA(fh, oCCFIX); / back to fixups / while ((nCCFIX-) && (!erc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pCode + i; / Where in CSeg */ *pFix = pFix - off Code + pCode; } } if (nDCFIX) { ere = SetFileLFA(fh, oDCFIX); / back to fixups / while ((nDCFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pData + i; / Where in DSeg */ *pFix = pFix - off Code + pCode; } } if (nDDFIX) { ere = SetFileLFA(f h, oDDFIX); / back to fixups / while ((nDDFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pData + i; / Where in DSeg */ •pFix = *pFix - offData + pData; }
Chapter 22 ■ Job Management Code
/* Clean the OS PD of User memory */ FillData(&pOSPD[256], 1024, 0); /* Clean OS PD of User PDEs */ FillData(&pOSPD[768], 1024, 0); /* Clean OS PD of User Shadow */ /* Now we fill in the rest of the User's JCB */ pNewJCB->pJcbPD = pPD; /* Lin Add of PD */ pNewJCB->pJcbStack = pStack; /* Add of Stack */ pNewJCB->sJcbStack = sStack; /* Size of Code */ pNewJCB->pJcbCode = pCode; /* Add of Code */ pNewJCB->sJcbCode = sCode; /* Size of Code */ pNewJCB->pJcbData = pData; /* Add of Code */ pNewJCB->sJcbData = sData; /* Size of Code */ pNewJCB->sbUserName[0] pNewJCB->sbPath[0] = 0 pNewJCB->JcbExitRF[0] ■ pNewJCB ->JcbCmdLine [ 0 ] /* Zero UserName */ /* No Default path */ /* No Exit Run File */ /* No Cmd Line */ CopyData(“KBD”, &pNewJCB->JcbSysIn[1 ] , 3); pNewJCB->JcbSysIn[0] = 3; /* Size */ CopyData(“VID”, 4pNewJCB->JcbSys0ut[1 ] , 3): pNewJCB->JcbSysOut[0] = 3; /* Size */ pNewJCB->pVidMem = pVid; pNewJCB->pVirtVid = pVid; pNewJCB->CrntX = 0; pNewJCB->CrntY = 0; pNewJCB->nCols = 80; pNewJCB->nLines = 25; /* Default to Virt Vid */ /* Virtual Video memory */ /* Vid X Position */ /* Vid Y Position */ /* Columns */ /* Lines */ pNewJCB->VidMode = 0; pNewJCB->fCursOn = 1; pNewJCB->fCursType = 0; /* 80x25 VGA Color Text */ /* Cursor On */ /* UnderLine */ /* Finally, we crank up the new task and schedule it for execution! */ if (!erc) ere = AllocExch(&i) ; if (!erc) ere = NewTask(JobNum, 0x18, 25, 0, i, pStack+sStack-4, pStart+pCode -off Code) ; if (I ere) SetExchOwner(i, pNewJCB); /* Exch now belongs to new JCB */ CloseFile(fh); /* read run file OK */
I
460 I
Part W ■ The Operating System Source Code
Listing 22.2. continued
if (!erc)
*pJobNumRet = JobNum;
return(erc) ;
}
This loads a job into an existing PD and JCB. This is
called by Chain() and may also be called by ExitJob()
if an ExitJob was specified in the current JCB.
The PD, pVid & first PT still exist in OS memory.
(CleanPD left the first PT for us). ExitJob and Chain
are responsible for opening and validating the runfile
and setting up the run file variables.
static long LoadJob(char *pJCB, long fh)
{
long ere, i, dret, nPages;
long pFix;
pNewJCB = pJCB;
/ Allocate user memory for Stack, Code and Data /
/ This is done in the context of the USER PD /
nPages = sStack/4096;
if (sStack%4096) nPages++;
ere = AllocPage( nPages, &pStack);
sStack = nPages * 4096; / set to whole pages /
nPages = sCode/4096;
if (sCode%4096) nPages++;
if (lerc)
ere = AllocPage(nPages, &pCode);
nPages = sData/4096;
if (sData%4096) nPages++;
ere = AllocPage (nPages, &pData);
/ All Job memory is now allocated, so let's LOAD IT! /
if (lerc)
ere = SetFileLFA(fh, oCode);
if (lerc)
ere = ReadBytes (fh, pCode, sCode, &dret);
if (lerc)
ere = SetFilel_FA(fh, oData);
if (lerc)
ere = ReadBytes (fh, pData, sData, &dret);
/ Now that we have read in the code and data we
apply fixups to these segments from the runfile
Chapter 22 Job Management Code H (lerc) { if (nCDFIX) { ere = SetFileLFA(fh, oCDFIX); /* back to fixups */ while ((nCDFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pCode + i; /* Where in CSeg */ *pFix = *pFix - off Data + pData; } (nCCFIX) { ere = SetFileLFA(fh, oCCFIX); /* back to fixups */ while ((nCCFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pCode + i; /* Where in CSeg */ *pFix = *pFix - off Code + pCode; } /* back to fixups */ if (nDCFIX) { ere = SetFileLFA(fh, oDCFIX) while ((nDCFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pData + i; /* Where in DSeg */ *pFix = *pFix - off Code + pCode; } } if (nDDFIX) { ere = SetFileLFA(fh, oDDFIX) ; /* back to fixups */ while ((nDDFIX-) && (lerc)) { ere = ReadBytes (fh, &i, 4, &dret); pFix = pData + i; /* Where in DSeg */ *pFix = *pFix - off Data + pData; } /* Now we fill in the rest of the User's JCB */ pNewJCB->pJcbStack = pStack; /* Add of Stack */ pNewJCB->sJcbStack = sStack; /* Size of Code */ pNewJCB->pJcbCode = pCode; /* Add of Code */ pNewJCB->sJcbCode = sCode; /* Size of Code */ pNewJCB->pJcbData = pData; /* Add of Code */ pNewJCB->sJcbData = sData; /* Size of Code */ } CloseFile(fh); return(erc) ; This is started as a new task in the context of a job that is being killed off. This is done to allow memory access and also reuse the code for ExitJob in the monitor. This task, its exchange, and TSS
/^2 Part IV ■ The Operating System Source Code Listing 22.2. continued will be reclaimed by the monitor along with the JCB and all OS memory pages for the PD.PT and video. void _KillTask(void) { GetJobNum(&JobNumE) ; GetpJCB(JobNumE, &pTmpJCB) ; /* Get pJCB to this Job */ /* Clean the PD of all user memory leaving OS memory to be deallocated by the caller (monitor or whoever). */ pPDE = pTmpJCB->pJcbPD; CleanUserPD(pPDE); GetTSSExch(&ExchE); ercE = 0; while(lercE) /* clear the exchange */ ercE = CheckMsg(ExchE, BogusMsg); ISendMsg(KillExch, ExchE, ErcOpCancel) ; SetPriority(31); WaitMsg( ExchE, BogusMsg); White(1); /*In case we get scheduled again*/ /* He's History! */ This is called from one job to kill another job or service. This cleans up ALL resources that the job had allocated. This is used to kill run-away jobs, or terminate a job just for the fun of it. It results in a violent death for the job specified. This must never be called from a task within the job to be killed. A job may terminate itself with ExitJob(). long far _KillJob(long JobNum) { long ere; /* Make sure it's not the Monitor, Debugger or the current job. */ GetJobNum(&JobNumE) ; if ((JobNum == JobNumE) | \ (JobNum ==1) ! | (JobNum == 2)) return(ErcBadJobNum) ; ere = GetpJCB(JobNum, &pTmpJCB) ; /* Get pJCB to the Job */ if (ere)
Chapter 22 ■ Job Management ( .ode
|
463
return(erc) ;
pTmpJCB->ExitError = ErcOpCancel; /* Operator said DIE! */
/* Remove ALL tasks for this job that are at the ReadyQue.
The task we are in does not belong to the job we are
killing so we can remove them all.
*/
RemoveRdyJob(pTmpJCB) ; /* It always returns ErcOk */
/* Deallocate ALL exchanges for this job */
ercE = 0;
iE = 0;
while (ercE != ErcOutOfRange)
{
ercE = GetExchOwner(iE, &pExchJCBE);
if ((lercE) && (pExchJCBE == pTmpJCB))
DeAllocExch(iE);
iE++;
}
ercE = 0; /* Clear the error */
/* Now that the user can't make anymore requests,
We send “Abort” messages to all services.
This closes all files that were opened by the Job
and frees up any other resources held for this
job by any service.
We will allocate one exchange for the job
that is being killed so we can use it for SendAbort
and also as the exchange number we send to the
KillExch in the monitor which will kill of the JCB
completely (he also switches video and keyboard
if needed) .
*/
ere = AllocExch(&ExchE) ; /* Get an Exch */
SetExchOwner(ExchE, pTmpJCB) ; /* make him the owner */
SendAbort (JobNum, ExchE); /* Notify all services */
/*JobNum, CodeSeg, Priority, fDebug, Exch, ESP, EIP */
ere = NewTask (JobNum, 0x08, 3, 0, ExchE, &TmpStack[127] , &_KillTask)
return(erc) ;
/* He's History! */
This is called from Exit() in C or directly from a user
job or service. This cleans up ALL resources that the
job had allocated.
This also checks for an exit run file to load if
one is specified. If no exit run file is specified
we just kill the JCB entirely and if video and
keyboard are assigned we assign them to the Monitor.
asa I Part IV ■ The Operating System Source Code Listing 22.2. continued void far _ExitJob(long dError) { /* NO LOCAL VARIABLES BECAUSE WE SWITCH STACKS!! */ GetJobNum(&JobNumE) ; GetpJCB(JobNumE, SpCrntJCB); /* Get pJCB to current Job */ pCrntJCB->ExitError = dError; /* Remove ALL tasks for this job that are at the ReadyQue. The task we are in won't be removed because it's RUNNING! */ RemoveRdyJob(pCrntJCB) ; /* It always returns ErcOk */ /* Deallocate all exchanges for this job except the one belonging to current TSS! The Dealloc Exchange call will invalidate all TSSs found at exchanges belonging to this user, and will also free up RQBs and Link Blocks. The job will not be able to initiate requests or send messages after this unless it is done with the TSSExchange because it will get a kernel error (invalid exchange). /* Find out what our TSS exchange is so we don't deallocate it too! */ GetTSSExch(&ExchE); ercE = 0; iE = 0; while (ercE != ErcOutOfRange) { ercE = GetExchOwner(iE, &pExchJCBE); if ((!ercE) && (iE != ExchE) && (pExchJCBE == pCrntJCB)) DeAllocExch(iE); iE++; } /* Now that the user can't make anymore requests, Send Abort messages to all services. This closes all files that were opened by the Job and frees up any other resources held for this job by any service. SendAbort(JobNumE, ExchE); ercE = 0; /* Clear the error */ while(lercE) /* clear the exchange */ ercE ■ CheckMsg(ExchE, BogusMsg); ercE ■ 0; /* Clear the error */ /* We must now switch to a temporary stack so we can clean out the user PD (we are on his stack right now!) */ #asm MOV EAX, OFFSET _TmpStack
Chapter 22 M Job Management Code ADD EAX, 508 MOV ESP, EAX MOV EBP, EAX #endasm /* Clean the PD of all user memory leaving OS memory for next job if there is one. */ pPDE = pCrntJCB->pJcbPD; CleanUserPD(pPDE); /* Look for Exit Run file to load if any exists. If no exit run file, we deallocate the PD and JCB then return to JOB 1. */ GetExitJob(aFileE, AcbFileE); /* Exit Run File!! */ if (IcbFileE) ercE = ErcNoExitJob; if (!ercE) ercE = GetRunFile(aFileE, cbFileE, &job_fhE); if (lercE) ercE = LoadJob(pCrntJCB, job_fhE); if (lercE) { pStart = pStart+pCode -off Code; /* Now we RETURN to new job's address after we put him on his new stack. */ #asm MOV EAX, _pStack MOV EBX, sStack ADD EAX, EBX SUB EAX, 4 MOV ESP, EAX MOV EBP, EAX PUSH 18h MOV EAX, pStart PUSH EAX RETF #endasm } if (ercE) { /* somet ;We are history! something failed or we don't have an ExitRF */ /* In case there is no job to run or a fatal error has happened we send a message (ISendMsg) to the monitor status task with our TSSExch and the Error. Then he will WIPE US OUT! We use ISend (vice send) so he can't run before we get to the exchange otherwise we will be placed back on the readyQueue! */ ISendMsg(KillExch, ExchE, ercE);
Part IV ■ The Operating System Source Code Listing 22.2. continued SetPriority(31); WaitMsg(ExchE, BogusMsg); while(l); /In case we get scheduled again/ /* We are NO MORE */ } } This is called to execute a program without changing the ExitJob. This is so you can run program B from program A and return to program A when program B is done. This runs Job B in the “context” of Job A which means Job B inherits the JCB and PD of job A so it can use things like the command line and path that were set up by A. Information can be passed to Job B by calling SetCmdLine (if Job B reads it), and also by setting the ExitError value in the parameter. Chain will only return to you if there was an error loading the Job. In other words, if Chain fails in a critial section we try to load the ExitJob and pass it the error. long far _Chain(char *pFileName, long cbFileName, long dExitError) { /* NO LOCAL VARIABLES BECAUSE WE SWITCH STACKS!! */ CopyData(pFileName, aFileE, cbFileName); cbFileE = cbFileName; ercE = GetRunFile(pFileName, cbFileName, &job_fhE); if (ercE) { CloseFile( job_fhE) ; /* if it had a handle at all */ return(ercE) ; } CloseFile( job_fhE) ; /* we will open it again after SendAbort */ 2 GetJobNum(&JobNumE) ; GetpJCB(JobNumE, &pCrntJCB) ; /* Get pJCB to current Job */ pCrntJCB->ExitError = dExitError; /* Remove ALL tasks for this job that are at the ReadyQue. The task we are in won't be removed because it's RUNNING! */ RemoveRdyJob(pCrntJCB) ; /* It always returns ErcOk */ /* Deallocate all exchanges for this job except the one belonging to current TSS! The Dealloc Exchange call will invalidate all TSSs found at exchanges belonging to this user, and will also free up RQBs and Link Blocks. The job will not be able to initiate requests or send messages after this unless it is done with the TSSExchange because it will get a kernel
Chapter 22 ■ Job Management Code H error (invalid exchange). / / Find out what our TSS exchange is so we don't deallocate it too! / GetTSSExch(&ExchE); ercE = 0; iE = 0; while (ercE != ErcOutOfRange) { ercE = GetExchOwner(iE, &pExchJCBE); if ((lercE) && (iE != ExchE) && (pExchJCBE == pCrntJCB)) DeAllocExch(iE); iE++; } / Now that the user can't make anymore requests, Send Abort messages to all services. This closes all files that were opened by the Job and frees up any other resources held for this job by any services. / SendAbort(JobNumE, ExchE); ercE = 0; / Clear the error / while(lercE) / clear the exchange of abort responses*/ ercE = CheckMsg(ExchE, BogusMsg); ercE = 0; /* Clear the error */ /* We must now switch to a temporary stack so we can clean out the user PD (we are on his stack right now!). */ #asm MOV EAX, OFFSET _TmpStack ADD EAX, 508 MOV ESP, EAX MOV EBP, EAX #endasm /* Clean the PD of all user memory leaving OS memory for next job if there is one. */ pPDE = pCrntJCB->pJcbPD; CleanUserPD(pPDE); /* Try to load the Chain file. Don't bother checking error cause it was valid if we got here! */ GetRunFile(aFileE, cbFileE, &job_fhE); /* it was valid before!*/ ercE = LoadJob(pCrntJCB, job_fhE); if (ercE) {
I
468
Part P/ ■ The Operating System Source Code
Listing 22.2. continued
/* We have errored in a critical part of Chain (LoadJob).
The original user's job is destroyed, and we can't run the
chain file (bummer).
The only thing left to do is Look for Exit Run file to load
if any exists. If no exit run file, we kill this guy
and return to the monitor if he had the screen. */
GetExitJob(aFileE, &cbFileE); /* Exit Run File!! */
if (IcbFileE)
ercE = ErcNoExitJob;
if (lercE)
ercE = GetRunFile(aFileE, cbFileE, &job_fhE);
if (lercE)
ercE = LoadJob(pCrntJCB, job_fhE);
}
if (lercE) { /* No error */
pStart = pStart+pCode -off Code;
/* Now we RETURN to new job's address after we put him
on his new stack. */
MOV EAX,
_pStack
MOV EBX,
sStack
ADD EAX, EBX
SUB EAX, 4
MOV ESP, EAX
MOV EBP, EAX
PUSH 18h
MOV EAX,
_pStart
PUSH EAX
RETF
#endasm
}
if (ercE) {
/* S
;We are history!
Something failed loading the job (Chain or Exit) */
/* In case there is no job to run or a fatal error has happened
we send a message (ISendMsg) to the monitor status
task with our TSSExch and the Error. Then he will WIPE US OUT!
We use ISend (vice send) so he can't run before we get to
the exchange otherwise we will be placed back on the readyQueuel
*/
ISendMsg(KillExch, ExchE, ercE); /* ISend clears intsl */
#asm
ST I
flendasm
SetPriority(31 ) ;
Chapter 22 ■ Job Management Code } } WaitMsg(ExchE, BogusMsg); while(1); /In case we get scheduled again/ /* We are NO MORE */ r*** End of Module
Powered by TurnKey Linux.