/****************************************************************/ /* */ /* task.c */ /* */ /* Task Manager for DOS Processes */ /* */ /* Copyright (c) 1995 */ /* Pasquale J. Villani */ /* All Rights Reserved */ /* */ /* This file is part of DOS-C. */ /* */ /* DOS-C is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version */ /* 2, or (at your option) any later version. */ /* */ /* DOS-C is distributed in the hope that it will be useful, but */ /* WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ /* the GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public */ /* License along with DOS-C; see the file COPYING. If not, */ /* write to the Free Software Foundation, 675 Mass Ave, */ /* Cambridge, MA 02139, USA. */ /****************************************************************/ #include "../../hdr/portab.h" #include "globals.h" /* $Logfile: C:/dos-c/src/kernel/task.c_v $ */ #ifndef IPL static BYTE *RcsId = "$Header: C:/dos-c/src/kernel/task.c_v 1.2 01 Sep 1995 17:54:22 patv $"; #endif /* * $Log: C:/dos-c/src/kernel/task.c_v $ * * Rev 1.2 01 Sep 1995 17:54:22 patv * First GPL release. * * Rev 1.1 30 Jul 1995 20:51:58 patv * Eliminated version strings in ipl * * Rev 1.0 02 Jul 1995 8:34:06 patv * Initial revision. */ #define LOADNGO 0 #define LOAD 1 #define OVERLAY 3 static exe_header header; #define CHUNK 16384 #define MAXENV 32768 #ifndef PROTO COUNT ChildEnv(exec_blk FAR *, UWORD *); #else COUNT ChildEnv(); #endif #ifndef IPL COUNT ChildEnv (exec_blk FAR *exp, UWORD *pChildEnvSeg) { BYTE FAR *pSrc; BYTE FAR *pDest; UWORD nEnvSize; COUNT RetCode; UWORD MaxEnvSize; /* create a new environment for the process */ if(exp -> exec.env_seg != 0) { for(nEnvSize = 0, pSrc = MK_FP(exp -> exec.env_seg,0); *pSrc != '\0'; ) { while(*pSrc != '\0') { ++pSrc; ++nEnvSize; } /* account for terminating null */ ++nEnvSize; ++pSrc; } } else { /** nEnvSize = 0; */ pChildEnvSeg = 0; return SUCCESS; } /* Test env size and abort if greater than max */ if(nEnvSize >= MAXENV) return DE_INVLDENV; /* allocate enough space for env + path */ if((RetCode = DosMemAlloc(long2para(nEnvSize + 65), FIRST_FIT, (seg FAR *)pChildEnvSeg, (UWORD FAR *)MaxEnvSize)) < 0) return RetCode; else pDest = MK_FP(*pChildEnvSeg + 1, 0); /* fill the new env and inform the process of its */ /* location throught the psp */ for(pSrc = MK_FP(exp -> exec.env_seg,0); *pSrc != '\0'; ) { fstrncpy(pDest, pSrc, BUFFERSIZE); while(*pSrc != '\0') ++pSrc; while(*pDest != '\0') ++pDest; ++pSrc; ++pDest; } *pDest = '\0'; return SUCCESS; } #endif /* The following code is 8086 dependant */ VOID new_psp (psp FAR *p, int psize) { REG COUNT i; BYTE FAR *lpPspBuffer; /* Clear out new psp first */ for(lpPspBuffer = p, i = 0; i < sizeof(psp) ; ++i) *lpPspBuffer = 0; /* initialize all entries and exits */ /* CP/M-like exit point */ p -> ps_exit = 0x20cb; /* CP/M-like entry point - jump to special entry */ p -> ps_farcall= 0xea; #ifndef IPL p -> ps_reentry = int21_entry; #endif /* unix style call - 0xcd 0x21 0xcb (int 21, retf) */ p -> ps_unix[0] = 0xcd; p -> ps_unix[1] = 0x21; p -> ps_unix[2] = 0xcb; /* Now for parent-child relationships */ /* parent psp segment */ p -> ps_parent = DOS_PSP; /* previous psp pointer */ p -> ps_prevpsp = (BYTE FAR *)-1; #ifndef IPL /* Environment and memory useage parameters */ /* memory size in paragraphs */ p -> ps_size = psize; /* environment paragraph */ p -> ps_environ = 0; /* process dta */ p -> ps_dta = (BYTE FAR *)(&p -> ps_cmd_count); /* terminate address */ p -> ps_isv22 = int22_handler; /* break address */ p -> ps_isv23 = int23_handler; /* critical error address */ p -> ps_isv24 = int24_handler; #endif /* File System parameters */ /* user stack pointer - int 21 */ p -> ps_stack = (BYTE FAR *)-1; /* file table - 0xff is unused */ for(i = 0; i < 20; i++) p -> ps_files[i] = 0xff; /* initialize stdin, stdout, etc */ p -> ps_files[STDIN] = 0; /* stdin */ p -> ps_files[STDOUT] = 1; /* stdout */ p -> ps_files[STDERR] = 2; /* stderr */ p -> ps_files[STDAUX] = 3; /* stdaux */ p -> ps_files[STDPRN] = 4; /* stdprn */ /* maximum open files */ p -> ps_maxfiles = 20; /* open file table pointer */ p -> ps_filetab = p -> ps_files; /* first command line argument */ p -> ps_fcb1.fcb_drive = 1; /* second command line argument */ p -> ps_fcb2.fcb_drive = 2; /* local command line */ p -> ps_cmd_count = 0; /* command tail */ p -> ps_cmd[0] = 0; /* command tail */ } #ifndef IPL static COUNT DosComLoader (BYTE FAR *namep, exec_blk FAR *exp, COUNT mode) { COUNT rc, env_size, nread; UWORD mem; UWORD env, asize; BYTE FAR *sp, FAR *dp; psp FAR *p; mcb FAR *mp; iregs FAR *irp; LONG com_size; if(mode != OVERLAY) { if((rc = ChildEnv(exp, &env)) != SUCCESS) return rc; } else mem = exp -> load.load_seg; /* Allocate our memory and pass back any errors */ if((rc = DosMemAlloc(0, LARGEST, (seg FAR *)&mem, (UWORD FAR *)&asize)) < 0) { if(env != 0) DosMemFree(env); return rc; } else { mp = MK_FP(mem, 0); ++mem; } /* Now load the executable */ /* If file not found - error */ /* NOTE - this is fatal because we lost it in transit */ /* from DosExec! */ if((rc = dos_open(namep, 0)) < 0) fatal("(DosComLoader) com file lost in transit"); /* do it in 32K chunks */ sp = MK_FP(mem, sizeof(psp)); for(com_size = 65536l, nread = 0; com_size > 0; ) { nread = dos_read(rc, sp, CHUNK); sp += nread; com_size -= nread; if(nread < CHUNK) break; } dos_close(rc); if(mode == OVERLAY) return SUCCESS; /* point to the PSP so we can build it */ p = MK_FP(mem, 0); new_psp(p, mem + asize); /* clone the file table */ if(InDOS > 0) { psp FAR *q; REG COUNT i; q = MK_FP(cu_psp, 0); for(i = 0; i < 20; i++) { REG COUNT ret; if(q -> ps_files[i] != 0xff && ((ret = CloneHandle(q -> ps_files[i])) >= 0)) p -> ps_files[i] = ret; else p -> ps_files[i] = 0xff; } } p -> ps_environ = env == 0 ? 0 : env + 1; /* complete the psp by adding the command line */ p -> ps_cmd_count = exp -> exec.cmd_line -> ctCount; fbcopy(exp -> exec.cmd_line -> ctBuffer, p -> ps_cmd, p -> ps_cmd_count > 127 ? 127 : p -> ps_cmd_count); /* stick a new line on the end for safe measure */ p -> ps_cmd[p -> ps_cmd_count] = '\r'; /* identify the mcb as this functions' */ /* use the file name less extension - left adjusted and */ /* space filled */ mp -> m_psp = mem + 1; for(asize = 0; asize < 8; asize++) { if(namep[asize] != '.') mp -> m_name[asize] = namep[asize]; else break; } for(; asize < 8; asize++) mp -> m_name[asize] = ' '; /* build the user area on the stack */ irp = MK_FP(mem, (0xfffe - sizeof(iregs))); /* start allocating REGs */ irp -> ES = irp -> DS = mem; irp -> CS = mem; irp -> IP = 0x100; irp -> AX = 0xffff; /* for now, until fcb code is in */ irp -> BX = irp -> CX = irp -> DX = irp -> SI = irp -> DI = irp -> BP = 0; irp -> FLAGS = 0; /* Transfer control to the executable */ p -> ps_parent = cu_psp; p -> ps_prevpsp = (BYTE FAR *)MK_FP(cu_psp, 0); p -> ps_stack = (BYTE FAR *)user_r; switch(mode) { case LOADNGO: cu_psp = mem; dta = p -> ps_dta; exec_user(irp); /* We should never be here */ fatal("KERNEL RETURNED!!!"); break; case LOAD: exp -> exec.stack = (BYTE FAR *)irp; exp -> exec.start_addr = MK_FP(irp -> CS, irp -> IP); return SUCCESS; } } VOID return_user (void) { psp FAR *p, FAR *q; REG COUNT i; /* Close all files */ for(i = 0; i < 20; i++) DosClose(i); /* restore parent */ p = MK_FP(cu_psp, 0); /* When process returns - restore the isv */ setvec(0x22, p -> ps_isv22); setvec(0x23, p -> ps_isv23); setvec(0x24, p -> ps_isv24); /* And free all process memory if not a TSR return */ if(!tsr) { if(p -> ps_environ != (UWORD)0) DosMemFree(--p -> ps_environ); DosMemFree(--cu_psp); } cu_psp = p -> ps_parent; q = MK_FP(cu_psp,0); dta = q -> ps_dta; exec_user(p -> ps_stack); } #endif static COUNT DosExeLoader (BYTE FAR *namep, exec_blk FAR *exp, COUNT mode) { COUNT rc, env_size, i, nBytesRead; UWORD mem, env, asize, start_seg; ULONG image_size; ULONG image_offset; BYTE FAR *sp, FAR * dp; psp FAR *p; mcb FAR *mp; iregs FAR *irp; UWORD reloc[2]; seg FAR *spot; LONG exe_size; #ifdef IPL BYTE szNameBuffer[64]; #endif #ifndef IPL /* Clone the environement and create a memory arena */ if(mode != OVERLAY) { if((rc = ChildEnv(exp, &env)) != SUCCESS) return rc; } else mem = exp -> load.load_seg; #endif /* compute image offset from the header */ #ifdef IPL fscopy(namep, (BYTE FAR *)szNameBuffer); printf("\nEXE loader loading: %s", szNameBuffer); #endif image_offset = (ULONG)header.exHeaderSize * 16l; /* compute image size by removing the offset from the */ /* number pages scaled to bytes plus the remainder and */ /* the psp */ /* First scale the size */ image_size = (ULONG)(header.exPages) * 512l; #if 0 /* remove the offset */ image_size -= image_offset; /* add in the remainder bytes */ if(header.exExtraBytes != 0) image_size -= (ULONG)(512 - header.exExtraBytes); #endif /* and finally add in the psp size */ if(mode != OVERLAY) image_size += (ULONG)long2para((LONG)sizeof(psp)); #ifndef IPL if(mode != OVERLAY) { /* Now find out how many paragraphs are available */ if((rc = DosMemLargest((seg FAR *)&asize)) != SUCCESS) return rc; else { exe_size = (LONG)long2para(image_size) + header.exMinAlloc + long2para((LONG)sizeof(psp)); if(exe_size > asize) return DE_NOMEM; else if(((LONG)long2para(image_size) + header.exMaxAlloc + long2para((LONG)sizeof(psp))) < asize) exe_size = (LONG)long2para(image_size) + header.exMaxAlloc + long2para((LONG)sizeof(psp)); else exe_size = asize; } /* Allocate our memory and pass back any errors */ /* We can still get an error on first fit if the above */ /* returned size was a bet fit case */ if((rc = DosMemAlloc((seg)exe_size, FIRST_FIT, (seg FAR *)&mem, (UWORD FAR *)&asize)) < 0) { if(rc == DE_NOMEM) { if((rc = DosMemAlloc(0, LARGEST, (seg FAR *)&mem, (UWORD FAR *)&asize)) < 0) { if(env != 0) DosMemFree(env); return rc; } /* This should never happen, but ... */ if(asize < exe_size) { if(env != 0) DosMemFree(env); DosMemFree(mem); return rc; } } else { if(env != 0) DosMemFree(env); return rc; } } else /* with no error, we got exactly what we asked for */ asize = exe_size; } #else mem = KERNSEG; #endif #ifndef IPL if(mode != OVERLAY) { #endif /* memory found large enough - continue processing */ mp = MK_FP(mem, 0); ++mem; #ifndef IPL } else mem = exp -> load.load_seg; #endif #ifdef IPL printf("."); #endif /* create the start seg for later computations */ if(mode == OVERLAY) start_seg = mem; else start_seg = mem + long2para((LONG)sizeof(psp)); /* Now load the executable */ /* If file not found - error */ /* NOTE - this is fatal because we lost it in transit */ /* from DosExec! */ if((rc = dos_open(namep, 0)) < 0) fatal("(DosExeLoader) exe file lost in transit"); #ifdef IPL printf("."); #endif /* offset to start of image */ if (dos_lseek(rc, image_offset, 0) != image_offset) { #ifndef IPL if(mode != OVERLAY) { DosMemFree(--mem); if(env != 0) DosMemFree(env); } #endif return DE_INVLDDATA; } #ifdef IPL printf("."); #endif /* read in the image in 32K chunks */ if(mode != OVERLAY) exe_size = image_size - long2para((LONG)sizeof(psp)); else exe_size = image_size; sp = MK_FP(start_seg, 0x0); while(exe_size > 0) { nBytesRead = dos_read((COUNT)rc, (VOID FAR *)sp, (COUNT)(exe_size < CHUNK ? exe_size : CHUNK)); sp = add_far((VOID FAR *)sp, (ULONG)CHUNK); exe_size -= nBytesRead; if(nBytesRead == 0 || exe_size <= 0) break; #ifdef IPL printf("."); #endif } #if 0 /* Error if we did not read the entire image */ if(exe_size != 0) fatal("Broken exe loader (exe_size != 0)"); #endif /* relocate the image for new segment */ dos_lseek(rc, (LONG)header.exRelocTable, 0); for (i=0; i < header.exRelocItems; i++) { if (dos_read(rc, (VOID FAR *)&reloc[0], sizeof(reloc)) != sizeof(reloc)) { return DE_INVLDDATA; } if(mode == OVERLAY) { spot = MK_FP(reloc[1] + mem, reloc[0]); *spot = *spot + exp -> load.reloc; } else { /* spot = MK_FP(reloc[1] + mem + 0x10, reloc[0]);*/ spot = MK_FP(reloc[1] + start_seg, reloc[0]); *spot = *spot + start_seg; } } #ifdef IPL printf("."); #endif /* and finally close the file */ dos_close(rc); /* exit here for overlay */ if(mode == OVERLAY) return SUCCESS; /* point to the PSP so we can build it */ p = MK_FP(mem, 0); new_psp(p, mem + asize); #ifndef IPL /* clone the file table */ if(InDOS > 0) { psp FAR *q; q = MK_FP(cu_psp, 0); for(i = 0; i < 20; i++) { REG COUNT ret; if(q -> ps_files[i] != 0xff && ((ret = CloneHandle(q -> ps_files[i])) >= 0)) p -> ps_files[i] = ret; else p -> ps_files[i] = 0xff; } } p -> ps_environ = env == 0 ? 0 : env + 1; #else p -> ps_environ = 0; #endif /* complete the psp by adding the command line */ p -> ps_cmd_count = exp -> exec.cmd_line -> ctCount; fbcopy(exp -> exec.cmd_line -> ctBuffer, p -> ps_cmd, p -> ps_cmd_count > 127 ? 127 : p -> ps_cmd_count); /* stick a new line on the end for safe measure */ p -> ps_cmd[p -> ps_cmd_count] = '\r'; /* identify the mcb as this functions' */ /* use the file name less extension - left adjusted and */ /* space filled */ mp -> m_psp = mem + 1; for(i = 0; i < 8; i++) { if(namep[i] != '.') mp -> m_name[i] = namep[i]; else break; } for(; i < 8; i++) mp -> m_name[i] = ' '; /* build the user area on the stack */ irp = MK_FP(header.exInitSS + start_seg, ((header.exInitSP - sizeof(iregs)) & 0xffff)); #ifdef IPL printf(".\n"); #endif /* start allocating REGs */ /* Note: must match es & ds memory segment */ irp -> ES = irp -> DS = mem; irp -> CS = header.exInitCS + start_seg; irp -> IP = header.exInitIP; irp -> AX = 0xffff; /* for now, until fcb code is in */ #ifdef IPL irp -> BX = BootDrive; irp -> CX = NumFloppies; #else irp -> BX = irp -> CX = #endif irp -> DX = irp -> SI = irp -> DI = irp -> BP = 0; irp -> FLAGS = 0; /* Transfer control to the executable */ p -> ps_parent = cu_psp; p -> ps_prevpsp = (BYTE FAR *)MK_FP(cu_psp, 0); p -> ps_stack = (BYTE FAR *)user_r; #ifdef IPL printf("Starting kernel ...\n"); #endif switch(mode) { case LOADNGO: cu_psp = mem; dta = p -> ps_dta; exec_user(irp); /* We should never be here */ fatal("KERNEL RETURNED!!!"); break; #ifndef IPL case LOAD: exp -> exec.stack = (BYTE FAR *)irp; exp -> exec.start_addr = MK_FP(irp -> CS, irp -> IP); return SUCCESS; #endif } } COUNT DosExec (COUNT mode, exec_blk FAR *ep, BYTE FAR *lp) { COUNT rc, nNumRead; /* If file not found - free ram and return error */ #ifdef IPL printf("."); #endif if((rc = dos_open(lp, 0)) < 0) return DE_FILENOTFND; if((nNumRead = dos_read(rc, (VOID FAR *)&header, sizeof(exe_header))) != sizeof(exe_header)) { #ifdef IPL printf("Internal IPL error - Read failure (read %d != %d)", nNumRead, sizeof(exe_header)); #endif return DE_INVLDDATA; } dos_close(rc); #ifdef IPL printf("."); #endif if(header.exSignature != MAGIC) #ifndef IPL return DosComLoader(lp, ep, mode); #else { char szFileName[32]; fmemcpy((BYTE FAR *)szFileName, lp); printf("\nFile: %s (MAGIC = %04x)", szFileName, header.exSignature); fatal("IPL can't load .com files!"); } #endif else return DosExeLoader(lp, ep, mode); }