Separate the process-related code from the DOS kernel.
Enable starting processes from other processes in STANDALONE mode.
Implement INT 21h, AH = 55h and INT 21h, AH = 26h (Create/Clone PSP).
Implement overlay loading.


svn path=/trunk/; revision=67498
This commit is contained in:
Aleksandar Andrejevic 2015-05-01 15:42:54 +00:00
parent 2aab9460a2
commit 613b74b43d
10 changed files with 923 additions and 712 deletions

View file

@ -36,6 +36,7 @@ list(APPEND SOURCE
dos/dos32krnl/handle.c
dos/dos32krnl/himem.c
dos/dos32krnl/memory.c
dos/dos32krnl/process.c
dos/mouse32.c
dos/dem.c
clock.c

View file

@ -20,6 +20,7 @@
#include "dem.h"
#include "dos/dos32krnl/device.h"
#include "dos/dos32krnl/process.h"
#include "cpu/bop.h"
#include "bios/bios.h"

View file

@ -22,6 +22,7 @@
#include "handle.h"
#include "dosfiles.h"
#include "memory.h"
#include "process.h"
#include "himem.h"
#include "bios/bios.h"
@ -37,11 +38,9 @@
CALLBACK16 DosContext;
static DWORD DiskTransferArea;
/*static*/ BYTE CurrentDrive;
static CHAR LastDrive = 'E';
static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
static WORD DosErrorLevel = 0x0000;
static PBYTE InDos;
/* PUBLIC VARIABLES ***********************************************************/
@ -50,79 +49,13 @@ PDOS_SYSVARS SysVars;
/* Echo state for INT 21h, AH = 01h and AH = 3Fh */
BOOLEAN DoEcho = FALSE;
WORD CurrentPsp = SYSTEM_PSP;
DWORD DiskTransferArea;
WORD DosErrorLevel = 0x0000;
WORD DosLastError = 0;
/* PRIVATE FUNCTIONS **********************************************************/
static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
LPCSTR ProgramName)
{
PCHAR Ptr, DestBuffer = NULL;
ULONG TotalSize = 0;
WORD DestSegment;
/* If we have an environment strings list, compute its size */
if (Environment)
{
/* Calculate the size of the environment block */
Ptr = (PCHAR)Environment;
while (*Ptr) Ptr += strlen(Ptr) + 1;
TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
}
else
{
/* Empty environment string */
TotalSize = 1;
}
/* Add the final environment block NULL-terminator */
TotalSize++;
/* Add the two bytes for the program name tag */
TotalSize += 2;
/* Add the string buffer size */
TotalSize += strlen(ProgramName) + 1;
/* Allocate the memory for the environment block */
DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
if (!DestSegment) return 0;
DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
/* If we have an environment strings list, copy it */
if (Environment)
{
Ptr = (PCHAR)Environment;
while (*Ptr)
{
/* Copy the string and NULL-terminate it */
strcpy(DestBuffer, Ptr);
DestBuffer += strlen(Ptr);
*(DestBuffer++) = '\0';
/* Move to the next string */
Ptr += strlen(Ptr) + 1;
}
}
else
{
/* Empty environment string */
*(DestBuffer++) = '\0';
}
/* NULL-terminate the environment block */
*(DestBuffer++) = '\0';
/* Store the special program name tag */
*(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
*(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
/* Copy the program name after the environment block */
strcpy(DestBuffer, ProgramName);
return DestSegment;
}
static BOOLEAN DosChangeDrive(BYTE Drive)
{
WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
@ -272,556 +205,13 @@ VOID DosInitializePsp(WORD PspSegment,
PspBlock->FarCall[1] = 0x21;
PspBlock->FarCall[2] = 0xCB; // retf
/* Set the command line */
PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
}
DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
IN LPCSTR ExecutablePath,
IN LPCSTR CommandLine,
IN LPCSTR Environment OPTIONAL,
IN DWORD ReturnAddress OPTIONAL,
OUT PDWORD StackLocation OPTIONAL,
OUT PDWORD EntryPoint OPTIONAL)
{
DWORD Result = ERROR_SUCCESS;
HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
LPBYTE Address = NULL;
WORD Segment = 0;
WORD EnvBlock = 0;
WORD MaxAllocSize;
WORD ExeSegment;
DWORD i, FileSize, BaseSize, TotalSize;
PIMAGE_DOS_HEADER Header;
PDWORD RelocationTable;
PWORD RelocWord;
LPSTR CmdLinePtr = (LPSTR)CommandLine;
BOOLEAN LoadHigh = FALSE;
DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
LoadType,
ExecutablePath,
CommandLine,
Environment ? Environment : "n/a",
StackLocation,
EntryPoint);
if (LoadType == DOS_LOAD_OVERLAY)
if (CommandLine)
{
DPRINT1("Overlay loading is not supported yet.\n");
return ERROR_NOT_SUPPORTED;
/* Set the command line */
PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
}
/* NULL-terminate the command line by removing the return carriage character */
while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
*CmdLinePtr = '\0';
/* Open a handle to the executable */
FileHandle = CreateFileA(ExecutablePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (FileHandle == INVALID_HANDLE_VALUE)
{
Result = GetLastError();
goto Cleanup;
}
/* Get the file size */
FileSize = GetFileSize(FileHandle, NULL);
/* Create a mapping object for the file */
FileMapping = CreateFileMapping(FileHandle,
NULL,
PAGE_READONLY,
0,
0,
NULL);
if (FileMapping == NULL)
{
Result = GetLastError();
goto Cleanup;
}
/* Map the file into memory */
Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
if (Address == NULL)
{
Result = GetLastError();
goto Cleanup;
}
/* Copy the environment block to DOS memory */
EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
if (EnvBlock == 0)
{
Result = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
/* Check if this is an EXE file or a COM file */
if (Address[0] == 'M' && Address[1] == 'Z')
{
/* EXE file */
/* Get the MZ header */
Header = (PIMAGE_DOS_HEADER)Address;
/* Get the base size of the file, in paragraphs (rounded up) */
BaseSize = TotalSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
/* Add the PSP size, in paragraphs */
TotalSize += sizeof(DOS_PSP) >> 4;
/* Add the maximum size that should be allocated */
TotalSize += Header->e_maxalloc;
if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
{
/* This program should be loaded high */
LoadHigh = TRUE;
TotalSize = 0xFFFF;
}
/* Make sure it does not pass 0xFFFF */
if (TotalSize > 0xFFFF) TotalSize = 0xFFFF;
/* Try to allocate that much memory */
Segment = DosAllocateMemory((WORD)TotalSize, &MaxAllocSize);
if (Segment == 0)
{
/* Check if there's at least enough memory for the minimum size */
if (MaxAllocSize < (BaseSize + (sizeof(DOS_PSP) >> 4) + Header->e_minalloc))
{
Result = DosLastError;
goto Cleanup;
}
/* Allocate that minimum amount */
TotalSize = MaxAllocSize;
Segment = DosAllocateMemory((WORD)TotalSize, NULL);
ASSERT(Segment != 0);
}
/* Initialize the PSP */
DosInitializePsp(Segment,
CommandLine,
(WORD)TotalSize,
EnvBlock,
ReturnAddress);
/* The process owns its own memory */
DosChangeMemoryOwner(Segment, Segment);
DosChangeMemoryOwner(EnvBlock, Segment);
/* Find the EXE segment */
if (!LoadHigh) ExeSegment = Segment + (sizeof(DOS_PSP) >> 4);
else ExeSegment = Segment + TotalSize - BaseSize;
/* Copy the program to the code segment */
RtlCopyMemory(SEG_OFF_TO_PTR(ExeSegment, 0),
Address + (Header->e_cparhdr << 4),
min(FileSize - (Header->e_cparhdr << 4), BaseSize << 4));
/* Get the relocation table */
RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
/* Perform relocations */
for (i = 0; i < Header->e_crlc; i++)
{
/* Get a pointer to the word that needs to be patched */
RelocWord = (PWORD)SEG_OFF_TO_PTR(ExeSegment + HIWORD(RelocationTable[i]),
LOWORD(RelocationTable[i]));
/* Add the number of the EXE segment to it */
*RelocWord += ExeSegment;
}
if (LoadType == DOS_LOAD_AND_EXECUTE)
{
/* Set the initial segment registers */
setDS(Segment);
setES(Segment);
/* Set the stack to the location from the header */
setSS(ExeSegment + Header->e_ss);
setSP(Header->e_sp);
/* Execute */
CurrentPsp = Segment;
DiskTransferArea = MAKELONG(0x80, Segment);
CpuExecute(ExeSegment + Header->e_cs, Header->e_ip);
}
}
else
{
/* COM file */
/* Find the maximum amount of memory that can be allocated */
DosAllocateMemory(0xFFFF, &MaxAllocSize);
/* Make sure it's enough for the whole program and the PSP */
if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
{
Result = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
/* Allocate all of it */
Segment = DosAllocateMemory(MaxAllocSize, NULL);
if (Segment == 0)
{
Result = DosLastError;
goto Cleanup;
}
/* The process owns its own memory */
DosChangeMemoryOwner(Segment, Segment);
DosChangeMemoryOwner(EnvBlock, Segment);
/* Copy the program to Segment:0100 */
RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
Address,
FileSize);
/* Initialize the PSP */
DosInitializePsp(Segment,
CommandLine,
MaxAllocSize,
EnvBlock,
ReturnAddress);
if (LoadType == DOS_LOAD_AND_EXECUTE)
{
/* Set the initial segment registers */
setDS(Segment);
setES(Segment);
/* Set the stack to the last word of the segment */
setSS(Segment);
setSP(0xFFFE);
/*
* Set the value on the stack to 0, so that a near return
* jumps to PSP:0000 which has the exit code.
*/
*((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
/* Execute */
CurrentPsp = Segment;
DiskTransferArea = MAKELONG(0x80, Segment);
CpuExecute(Segment, 0x100);
}
}
Cleanup:
if (Result != ERROR_SUCCESS)
{
/* It was not successful, cleanup the DOS memory */
if (EnvBlock) DosFreeMemory(EnvBlock);
if (Segment) DosFreeMemory(Segment);
}
/* Unmap the file*/
if (Address != NULL) UnmapViewOfFile(Address);
/* Close the file mapping object */
if (FileMapping != NULL) CloseHandle(FileMapping);
/* Close the file handle */
if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
return Result;
}
DWORD DosStartProcess(IN LPCSTR ExecutablePath,
IN LPCSTR CommandLine,
IN LPCSTR Environment OPTIONAL)
{
DWORD Result;
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
ExecutablePath,
CommandLine,
Environment,
IntVecTable[0x20], // Use INT 20h
NULL,
NULL);
if (Result != ERROR_SUCCESS) goto Quit;
/* Attach to the console */
ConsoleAttach();
VidBiosAttachToConsole();
// HACK: Simulate a ENTER key release scancode on the PS/2 port because
// some apps expect to read a key release scancode (> 0x80) when they
// are started.
IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
/* Start simulation */
SetEvent(VdmTaskEvent);
CpuSimulate();
/* Detach from the console */
VidBiosDetachFromConsole();
ConsoleDetach();
Quit:
return Result;
}
#ifndef STANDALONE
WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
LPCSTR ProgramName,
PDOS_EXEC_PARAM_BLOCK Parameters,
DWORD ReturnAddress)
{
DWORD Result;
DWORD BinaryType;
LPVOID Environment = NULL;
VDM_COMMAND_INFO CommandInfo;
CHAR CmdLine[MAX_PATH];
CHAR AppName[MAX_PATH];
CHAR PifFile[MAX_PATH];
CHAR Desktop[MAX_PATH];
CHAR Title[MAX_PATH];
ULONG EnvSize = 256;
PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
STARTUPINFOA StartupInfo;
PROCESS_INFORMATION ProcessInfo;
/* Get the binary type */
if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
/* Did the caller specify an environment segment? */
if (Parameters->Environment)
{
/* Yes, use it instead of the parent one */
Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
}
/* Set up the startup info structure */
RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
/* Create the process */
if (!CreateProcessA(ProgramName,
FAR_POINTER(Parameters->CommandLine),
NULL,
NULL,
FALSE,
0,
Environment,
NULL,
&StartupInfo,
&ProcessInfo))
{
return GetLastError();
}
/* Check the type of the program */
switch (BinaryType)
{
/* These are handled by NTVDM */
case SCS_DOS_BINARY:
case SCS_WOW_BINARY:
{
/* Clear the structure */
RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
/* Initialize the structure members */
CommandInfo.TaskId = SessionId;
CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
CommandInfo.CmdLine = CmdLine;
CommandInfo.CmdLen = sizeof(CmdLine);
CommandInfo.AppName = AppName;
CommandInfo.AppLen = sizeof(AppName);
CommandInfo.PifFile = PifFile;
CommandInfo.PifLen = sizeof(PifFile);
CommandInfo.Desktop = Desktop;
CommandInfo.DesktopLen = sizeof(Desktop);
CommandInfo.Title = Title;
CommandInfo.TitleLen = sizeof(Title);
CommandInfo.Env = Env;
CommandInfo.EnvLen = EnvSize;
Command:
/* Get the VDM command information */
if (!GetNextVDMCommand(&CommandInfo))
{
if (CommandInfo.EnvLen > EnvSize)
{
/* Expand the environment size */
EnvSize = CommandInfo.EnvLen;
CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
/* Repeat the request */
CommandInfo.VDMState |= VDM_FLAG_RETRY;
goto Command;
}
/* Shouldn't happen */
ASSERT(FALSE);
}
/* Load the executable */
Result = DosLoadExecutable(LoadType,
AppName,
CmdLine,
Env,
ReturnAddress,
&Parameters->StackLocation,
&Parameters->EntryPoint);
if (Result == ERROR_SUCCESS)
{
/* Increment the re-entry count */
CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
GetNextVDMCommand(&CommandInfo);
}
else
{
DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
}
break;
}
/* Not handled by NTVDM */
default:
{
/* Wait for the process to finish executing */
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
}
}
RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
/* Close the handles */
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
return ERROR_SUCCESS;
}
#endif
VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
{
WORD i;
WORD McbSegment = FIRST_MCB_SEGMENT;
PDOS_MCB CurrentMcb;
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
Psp,
ReturnCode,
KeepResident);
/* Check if this PSP is it's own parent */
if (PspBlock->ParentPsp == Psp) goto Done;
if (KeepResident == 0)
{
for (i = 0; i < PspBlock->HandleTableSize; i++)
{
/* Close the handle */
DosCloseHandle(i);
}
}
/* Free the memory used by the process */
while (TRUE)
{
/* Get a pointer to the MCB */
CurrentMcb = SEGMENT_TO_MCB(McbSegment);
/* Make sure the MCB is valid */
if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
/* Check if this block was allocated by the process */
if (CurrentMcb->OwnerPsp == Psp)
{
if (KeepResident == 0)
{
/* Free this entire block */
DosFreeMemory(McbSegment + 1);
}
else if (KeepResident < CurrentMcb->Size)
{
/* Reduce the size of the block */
DosResizeMemory(McbSegment + 1, KeepResident, NULL);
/* No further paragraphs need to stay resident */
KeepResident = 0;
}
else
{
/* Just reduce the amount of paragraphs we need to keep resident */
KeepResident -= CurrentMcb->Size;
}
}
/* If this was the last block, quit */
if (CurrentMcb->BlockType == 'Z') break;
/* Update the segment and continue */
McbSegment += CurrentMcb->Size + 1;
}
Done:
/* Restore the interrupt vectors */
IntVecTable[0x22] = PspBlock->TerminateAddress;
IntVecTable[0x23] = PspBlock->BreakAddress;
IntVecTable[0x24] = PspBlock->CriticalAddress;
/* Update the current PSP */
if (Psp == CurrentPsp)
{
CurrentPsp = PspBlock->ParentPsp;
if (CurrentPsp == SYSTEM_PSP)
{
ResetEvent(VdmTaskEvent);
CpuUnsimulate();
}
}
#ifndef STANDALONE
// FIXME: This is probably not the best way to do it
/* Check if this was a nested DOS task */
if (CurrentPsp != SYSTEM_PSP)
{
VDM_COMMAND_INFO CommandInfo;
/* Decrement the re-entry count */
CommandInfo.TaskId = SessionId;
CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
GetNextVDMCommand(&CommandInfo);
/* Clear the structure */
RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
/* Update the VDM state of the task */
CommandInfo.TaskId = SessionId;
CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
GetNextVDMCommand(&CommandInfo);
}
#endif
/* Save the return code - Normal termination */
DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
/* Return control to the parent process */
CpuExecute(HIWORD(PspBlock->TerminateAddress),
LOWORD(PspBlock->TerminateAddress));
}
VOID WINAPI DosInt20h(LPWORD Stack)
@ -1205,7 +595,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
/* Create New PSP */
case 0x26:
{
DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
DosClonePsp(getDX(), getCS());
break;
}
@ -1963,7 +1353,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
break;
}
#ifndef STANDALONE
/* Execute */
case 0x4B:
{
@ -1973,19 +1362,24 @@ VOID WINAPI DosInt21h(LPWORD Stack)
DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
WORD ErrorCode;
if (LoadType != DOS_LOAD_OVERLAY)
#ifndef STANDALONE
if (LoadType == DOS_LOAD_AND_EXECUTE)
{
ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock, ReturnAddress);
/* Create a new process */
ErrorCode = DosCreateProcess(ProgramName, ParamBlock, ReturnAddress);
}
else
{
ErrorCode = DosLoadExecutable(DOS_LOAD_OVERLAY,
#else
{
#endif
/* Just load an executable */
ErrorCode = DosLoadExecutable(LoadType,
ProgramName,
FAR_POINTER(ParamBlock->CommandLine),
SEG_OFF_TO_PTR(ParamBlock->Environment, 0),
ReturnAddress,
ParamBlock,
NULL,
NULL);
NULL,
ReturnAddress);
}
if (ErrorCode == ERROR_SUCCESS)
@ -2000,7 +1394,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
break;
}
#endif
/* Terminate With Return Code */
case 0x4C:
@ -2057,8 +1450,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
/* Internal - Set Current Process ID (Set PSP Address) */
case 0x50:
{
// FIXME: Is it really what it's done ??
CurrentPsp = getBX();
DosSetProcessContext(getBX());
break;
}
@ -2093,6 +1485,13 @@ VOID WINAPI DosInt21h(LPWORD Stack)
break;
}
/* Create Child PSP */
case 0x55:
{
DosCreatePsp(getDX(), getSI());
break;
}
/* Rename File */
case 0x56:
{

View file

@ -39,28 +39,17 @@
#define DOS_ERROR_HANDLE 2
#define DOS_SFT_SIZE 255
#define SEGMENT_TO_MCB(seg) ((PDOS_MCB)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
#define SEGMENT_TO_PSP(seg) ((PDOS_PSP)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
#define UMB_START_SEGMENT 0xC000
#define UMB_END_SEGMENT 0xDFFF
#define DOS_ALLOC_HIGH 0x40
#define DOS_ALLOC_HIGH_LOW 0x80
#define DOS_CMDLINE_LENGTH 127
#define DOS_DIR_LENGTH 64
#define NUM_DRIVES ('Z' - 'A' + 1)
#define DOS_CHAR_ATTRIBUTE 0x07
#define DOS_PROGRAM_NAME_TAG 0x0001
/* 16 MB of EMS memory */
#define EMS_TOTAL_PAGES 1024
typedef enum
{
DOS_LOAD_AND_EXECUTE = 0x00,
DOS_LOAD_ONLY = 0x01,
DOS_LOAD_OVERLAY = 0x03
} DOS_EXEC_TYPE;
#pragma pack(push, 1)
typedef struct _DOS_FCB
@ -102,31 +91,6 @@ typedef struct _DOS_SYSVARS
BYTE NullDriverRoutine[7];
} DOS_SYSVARS, *PDOS_SYSVARS;
typedef struct _DOS_PSP
{
BYTE Exit[2];
WORD LastParagraph;
BYTE Reserved0[6];
DWORD TerminateAddress;
DWORD BreakAddress;
DWORD CriticalAddress;
WORD ParentPsp;
BYTE HandleTable[20];
WORD EnvBlock;
DWORD LastStack;
WORD HandleTableSize;
DWORD HandleTablePtr;
DWORD PreviousPsp;
DWORD Reserved1;
WORD DosVersion;
BYTE Reserved2[14];
BYTE FarCall[3];
BYTE Reserved3[9];
DOS_FCB Fcb;
BYTE CommandLineSize;
CHAR CommandLine[DOS_CMDLINE_LENGTH];
} DOS_PSP, *PDOS_PSP;
typedef struct _DOS_INPUT_BUFFER
{
BYTE MaxLength;
@ -150,19 +114,6 @@ typedef struct _DOS_FIND_FILE_BLOCK
CHAR FileName[13];
} DOS_FIND_FILE_BLOCK, *PDOS_FIND_FILE_BLOCK;
typedef struct _DOS_EXEC_PARAM_BLOCK
{
/* Input variables */
WORD Environment;
DWORD CommandLine;
DWORD FirstFcb;
DWORD SecondFcb;
/* Output variables */
DWORD StackLocation;
DWORD EntryPoint;
} DOS_EXEC_PARAM_BLOCK, *PDOS_EXEC_PARAM_BLOCK;
typedef struct _DOS_COUNTRY_CODE_BUFFER
{
WORD TimeFormat;
@ -176,7 +127,8 @@ typedef struct _DOS_COUNTRY_CODE_BUFFER
/* VARIABLES ******************************************************************/
extern BOOLEAN DoEcho;
extern WORD CurrentPsp;
extern DWORD DiskTransferArea;
extern WORD DosErrorLevel;
extern WORD DosLastError;
extern PDOS_SYSVARS SysVars;
@ -207,36 +159,6 @@ VOID ConDrvCleanup(VOID);
* See dos.c
*/
VOID DosInitializePsp(
WORD PspSegment,
LPCSTR CommandLine,
WORD ProgramSize,
WORD Environment,
DWORD ReturnAddress
);
DWORD DosLoadExecutable(
IN DOS_EXEC_TYPE LoadType,
IN LPCSTR ExecutablePath,
IN LPCSTR CommandLine,
IN LPCSTR Environment OPTIONAL,
IN DWORD ReturnAddress OPTIONAL,
OUT PDWORD StackLocation OPTIONAL,
OUT PDWORD EntryPoint OPTIONAL
);
WORD DosCreateProcess(
DOS_EXEC_TYPE LoadType,
LPCSTR ProgramName,
PDOS_EXEC_PARAM_BLOCK Parameters,
DWORD ReturnAddress
);
DWORD DosStartProcess(
IN LPCSTR ExecutablePath,
IN LPCSTR CommandLine,
IN LPCSTR Environment OPTIONAL
);
VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident);
BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle);
BOOLEAN DosKRNLInitialize(VOID);
#endif // _DOS_H_

View file

@ -19,6 +19,7 @@
#include "dos/dem.h"
#include "dosfiles.h"
#include "handle.h"
#include "process.h"
#include "bios/bios.h"

View file

@ -18,6 +18,7 @@
#include "dosfiles.h"
#include "handle.h"
#include "memory.h"
#include "process.h"
/* PRIVATE FUNCTIONS **********************************************************/

View file

@ -12,10 +12,11 @@
#include "ntvdm.h"
#include "emulator.h"
#include "memory.h"
#include "dos.h"
#include "dos/dem.h"
#include "memory.h"
#include "process.h"
/* PUBLIC VARIABLES ***********************************************************/

View file

@ -11,6 +11,8 @@
/* TYPEDEFS *******************************************************************/
#define SEGMENT_TO_MCB(seg) ((PDOS_MCB)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
enum DOS_ALLOC_STRATEGY
{
DOS_ALLOC_FIRST_FIT,

View file

@ -0,0 +1,773 @@
/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: dos/dos32krnl/process.c
* PURPOSE: DOS32 Processes
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
/* INCLUDES *******************************************************************/
#define NDEBUG
#include "ntvdm.h"
#include "emulator.h"
#include "cpu/cpu.h"
#include "dos.h"
#include "dos/dem.h"
#include "dosfiles.h"
#include "handle.h"
#include "process.h"
#include "memory.h"
#include "bios/bios.h"
#include "io.h"
#include "hardware/ps2.h"
/* PUBLIC VARIABLES ***********************************************************/
WORD CurrentPsp = SYSTEM_PSP;
/* PRIVATE FUNCTIONS **********************************************************/
static inline VOID DosSetPspCommandLine(WORD Segment, LPCSTR CommandLine)
{
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
/* Set the command line */
PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
}
static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
LPCSTR ProgramName)
{
PCHAR Ptr, DestBuffer = NULL;
ULONG TotalSize = 0;
WORD DestSegment;
/* If we have an environment strings list, compute its size */
if (Environment)
{
/* Calculate the size of the environment block */
Ptr = (PCHAR)Environment;
while (*Ptr) Ptr += strlen(Ptr) + 1;
TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
}
else
{
/* Empty environment string */
TotalSize = 1;
}
/* Add the final environment block NULL-terminator */
TotalSize++;
/* Add the two bytes for the program name tag */
TotalSize += 2;
/* Add the string buffer size */
TotalSize += strlen(ProgramName) + 1;
/* Allocate the memory for the environment block */
DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
if (!DestSegment) return 0;
DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
/* If we have an environment strings list, copy it */
if (Environment)
{
Ptr = (PCHAR)Environment;
while (*Ptr)
{
/* Copy the string and NULL-terminate it */
strcpy(DestBuffer, Ptr);
DestBuffer += strlen(Ptr);
*(DestBuffer++) = '\0';
/* Move to the next string */
Ptr += strlen(Ptr) + 1;
}
}
else
{
/* Empty environment string */
*(DestBuffer++) = '\0';
}
/* NULL-terminate the environment block */
*(DestBuffer++) = '\0';
/* Store the special program name tag */
*(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
*(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
/* Copy the program name after the environment block */
strcpy(DestBuffer, ProgramName);
return DestSegment;
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
{
PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
/* Literally copy the PSP first */
RtlCopyMemory(DestPsp, SourcePsp, sizeof(DOS_PSP));
/* Save the interrupt vectors */
DestPsp->TerminateAddress = IntVecTable[0x22];
DestPsp->BreakAddress = IntVecTable[0x23];
DestPsp->CriticalAddress = IntVecTable[0x24];
/* No parent PSP */
DestPsp->ParentPsp = 0;
/* Set the handle table pointers to the internal handle table */
DestPsp->HandleTableSize = DEFAULT_JFT_SIZE;
DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
/* Copy the parent handle table without referencing the SFT */
RtlCopyMemory(FAR_POINTER(DestPsp->HandleTablePtr),
FAR_POINTER(SourcePsp->HandleTablePtr),
DEFAULT_JFT_SIZE);
}
VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
{
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
RtlZeroMemory(PspBlock, sizeof(*PspBlock));
/* Set the exit interrupt */
PspBlock->Exit[0] = 0xCD; // int 0x20
PspBlock->Exit[1] = 0x20;
/* Set the number of the last paragraph */
PspBlock->LastParagraph = Segment + ProgramSize - 1;
/* Save the interrupt vectors */
PspBlock->TerminateAddress = IntVecTable[0x22];
PspBlock->BreakAddress = IntVecTable[0x23];
PspBlock->CriticalAddress = IntVecTable[0x24];
/* Set the parent PSP */
PspBlock->ParentPsp = CurrentPsp;
/* No environment block yet */
PspBlock->EnvBlock = 0;
/* Copy the parent handle table */
DosCopyHandleTable(PspBlock->HandleTable);
/* Set the handle table pointers to the internal handle table */
PspBlock->HandleTableSize = DEFAULT_JFT_SIZE;
PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
/* Set the DOS version */
PspBlock->DosVersion = DOS_VERSION;
/* Set the far call opcodes */
PspBlock->FarCall[0] = 0xCD; // int 0x21
PspBlock->FarCall[1] = 0x21;
PspBlock->FarCall[2] = 0xCB; // retf
}
VOID DosSetProcessContext(WORD Segment)
{
CurrentPsp = Segment;
DiskTransferArea = MAKELONG(0x80, Segment);
}
DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
IN LPCSTR ExecutablePath,
IN PDOS_EXEC_PARAM_BLOCK Parameters,
IN LPCSTR CommandLine OPTIONAL,
IN LPCSTR Environment OPTIONAL,
IN DWORD ReturnAddress OPTIONAL)
{
DWORD Result = ERROR_SUCCESS;
HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
LPBYTE Address = NULL;
WORD Segment = 0;
WORD EnvBlock = 0;
WORD LoadSegment;
WORD MaxAllocSize;
DWORD i, FileSize;
DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
LoadType,
ExecutablePath,
Parameters,
ReturnAddress);
/* Open a handle to the executable */
FileHandle = CreateFileA(ExecutablePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (FileHandle == INVALID_HANDLE_VALUE)
{
Result = GetLastError();
goto Cleanup;
}
/* Get the file size */
FileSize = GetFileSize(FileHandle, NULL);
/* Create a mapping object for the file */
FileMapping = CreateFileMapping(FileHandle,
NULL,
PAGE_READONLY,
0,
0,
NULL);
if (FileMapping == NULL)
{
Result = GetLastError();
goto Cleanup;
}
/* Map the file into memory */
Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
if (Address == NULL)
{
Result = GetLastError();
goto Cleanup;
}
if (LoadType != DOS_LOAD_OVERLAY)
{
LPSTR CmdLinePtr;
if (CommandLine == NULL)
{
/* Get the command line from the parameter block */
CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
}
if (Environment == NULL)
{
/* Get the environment from the parameter block */
Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
}
/* NULL-terminate the command line by removing the return carriage character */
CmdLinePtr = (LPSTR)CommandLine;
while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
*CmdLinePtr = '\0';
/* Copy the environment block to DOS memory */
EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
if (EnvBlock == 0)
{
Result = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
/* Check if this is an EXE file or a COM file */
if (Address[0] == 'M' && Address[1] == 'Z')
{
/* EXE file */
PIMAGE_DOS_HEADER Header;
DWORD BaseSize;
PDWORD RelocationTable;
PWORD RelocWord;
WORD RelocFactor;
BOOLEAN LoadHigh = FALSE;
/* Get the MZ header */
Header = (PIMAGE_DOS_HEADER)Address;
/* Get the base size of the file, in paragraphs (rounded up) */
BaseSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
if (LoadType != DOS_LOAD_OVERLAY)
{
DWORD TotalSize = BaseSize;
/* Add the PSP size, in paragraphs */
TotalSize += sizeof(DOS_PSP) >> 4;
/* Add the maximum size that should be allocated */
TotalSize += Header->e_maxalloc;
if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
{
/* This program should be loaded high */
LoadHigh = TRUE;
TotalSize = 0xFFFF;
}
/* Make sure it does not pass 0xFFFF */
if (TotalSize > 0xFFFF) TotalSize = 0xFFFF;
/* Try to allocate that much memory */
Segment = DosAllocateMemory((WORD)TotalSize, &MaxAllocSize);
if (Segment == 0)
{
/* Check if there's at least enough memory for the minimum size */
if (MaxAllocSize < (BaseSize + (sizeof(DOS_PSP) >> 4) + Header->e_minalloc))
{
Result = DosLastError;
goto Cleanup;
}
/* Allocate that minimum amount */
TotalSize = MaxAllocSize;
Segment = DosAllocateMemory((WORD)TotalSize, NULL);
ASSERT(Segment != 0);
}
/* The process owns its own memory */
DosChangeMemoryOwner(Segment, Segment);
DosChangeMemoryOwner(EnvBlock, Segment);
/* Set INT 22h to the return address */
((PULONG)BaseAddress)[0x22] = ReturnAddress;
/* Create the PSP */
DosCreatePsp(Segment, (WORD)TotalSize);
DosSetPspCommandLine(Segment, CommandLine);
SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
/* Calculate the segment where the program should be loaded */
if (!LoadHigh) LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
else LoadSegment = Segment + TotalSize - BaseSize;
RelocFactor = LoadSegment;
}
else
{
LoadSegment = Parameters->Overlay.Segment;
RelocFactor = Parameters->Overlay.RelocationFactor;
}
/* Copy the program to the code segment */
RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
Address + (Header->e_cparhdr << 4),
min(FileSize - (Header->e_cparhdr << 4), BaseSize << 4));
/* Get the relocation table */
RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
/* Perform relocations */
for (i = 0; i < Header->e_crlc; i++)
{
/* Get a pointer to the word that needs to be patched */
RelocWord = (PWORD)SEG_OFF_TO_PTR(LoadSegment + HIWORD(RelocationTable[i]),
LOWORD(RelocationTable[i]));
/* Add the relocation factor to it */
*RelocWord += RelocFactor;
}
if (LoadType == DOS_LOAD_AND_EXECUTE)
{
/* Set the initial segment registers */
setDS(Segment);
setES(Segment);
/* Set the stack to the location from the header */
setSS(LoadSegment + Header->e_ss);
setSP(Header->e_sp);
/* Execute */
DosSetProcessContext(Segment);
CpuExecute(LoadSegment + Header->e_cs, Header->e_ip);
}
else if (LoadType == DOS_LOAD_ONLY)
{
Parameters->StackLocation = MAKELONG(Header->e_sp, LoadSegment + Header->e_ss);
Parameters->EntryPoint = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
}
}
else
{
/* COM file */
if (LoadType != DOS_LOAD_OVERLAY)
{
/* Find the maximum amount of memory that can be allocated */
DosAllocateMemory(0xFFFF, &MaxAllocSize);
/* Make sure it's enough for the whole program and the PSP */
if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
{
Result = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
/* Allocate all of it */
Segment = DosAllocateMemory(MaxAllocSize, NULL);
if (Segment == 0)
{
Result = DosLastError;
goto Cleanup;
}
/* The process owns its own memory */
DosChangeMemoryOwner(Segment, Segment);
DosChangeMemoryOwner(EnvBlock, Segment);
/* Set INT 22h to the return address */
((PULONG)BaseAddress)[0x22] = ReturnAddress;
/* Create the PSP */
DosCreatePsp(Segment, MaxAllocSize);
DosSetPspCommandLine(Segment, CommandLine);
SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
/* Calculate the segment where the program should be loaded */
LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
}
else
{
LoadSegment = Parameters->Overlay.Segment;
}
RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
Address,
FileSize);
if (LoadType == DOS_LOAD_AND_EXECUTE)
{
/* Set the initial segment registers */
setDS(Segment);
setES(Segment);
/* Set the stack to the last word of the segment */
setSS(Segment);
setSP(0xFFFE);
/*
* Set the value on the stack to 0, so that a near return
* jumps to PSP:0000 which has the exit code.
*/
*((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
/* Execute */
DosSetProcessContext(Segment);
CpuExecute(Segment, 0x100);
}
else if (LoadType == DOS_LOAD_ONLY)
{
Parameters->StackLocation = MAKELONG(0xFFFE, Segment);
Parameters->EntryPoint = MAKELONG(0x0100, Segment);
}
}
Cleanup:
if (Result != ERROR_SUCCESS)
{
/* It was not successful, cleanup the DOS memory */
if (EnvBlock) DosFreeMemory(EnvBlock);
if (Segment) DosFreeMemory(Segment);
}
/* Unmap the file*/
if (Address != NULL) UnmapViewOfFile(Address);
/* Close the file mapping object */
if (FileMapping != NULL) CloseHandle(FileMapping);
/* Close the file handle */
if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
return Result;
}
DWORD DosStartProcess(IN LPCSTR ExecutablePath,
IN LPCSTR CommandLine,
IN LPCSTR Environment OPTIONAL)
{
DWORD Result;
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
ExecutablePath,
NULL,
CommandLine,
Environment,
IntVecTable[0x20]);
if (Result != ERROR_SUCCESS) goto Quit;
/* Attach to the console */
ConsoleAttach();
VidBiosAttachToConsole();
// HACK: Simulate a ENTER key release scancode on the PS/2 port because
// some apps expect to read a key release scancode (> 0x80) when they
// are started.
IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
/* Start simulation */
SetEvent(VdmTaskEvent);
CpuSimulate();
/* Detach from the console */
VidBiosDetachFromConsole();
ConsoleDetach();
Quit:
return Result;
}
#ifndef STANDALONE
WORD DosCreateProcess(LPCSTR ProgramName,
PDOS_EXEC_PARAM_BLOCK Parameters,
DWORD ReturnAddress)
{
DWORD Result;
DWORD BinaryType;
LPVOID Environment = NULL;
VDM_COMMAND_INFO CommandInfo;
CHAR CmdLine[MAX_PATH];
CHAR AppName[MAX_PATH];
CHAR PifFile[MAX_PATH];
CHAR Desktop[MAX_PATH];
CHAR Title[MAX_PATH];
ULONG EnvSize = 256;
PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
STARTUPINFOA StartupInfo;
PROCESS_INFORMATION ProcessInfo;
/* Get the binary type */
if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
/* Did the caller specify an environment segment? */
if (Parameters->Environment)
{
/* Yes, use it instead of the parent one */
Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
}
/* Set up the startup info structure */
RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
/* Create the process */
if (!CreateProcessA(ProgramName,
FAR_POINTER(Parameters->CommandLine),
NULL,
NULL,
FALSE,
0,
Environment,
NULL,
&StartupInfo,
&ProcessInfo))
{
return GetLastError();
}
/* Check the type of the program */
switch (BinaryType)
{
/* These are handled by NTVDM */
case SCS_DOS_BINARY:
case SCS_WOW_BINARY:
{
/* Clear the structure */
RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
/* Initialize the structure members */
CommandInfo.TaskId = SessionId;
CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
CommandInfo.CmdLine = CmdLine;
CommandInfo.CmdLen = sizeof(CmdLine);
CommandInfo.AppName = AppName;
CommandInfo.AppLen = sizeof(AppName);
CommandInfo.PifFile = PifFile;
CommandInfo.PifLen = sizeof(PifFile);
CommandInfo.Desktop = Desktop;
CommandInfo.DesktopLen = sizeof(Desktop);
CommandInfo.Title = Title;
CommandInfo.TitleLen = sizeof(Title);
CommandInfo.Env = Env;
CommandInfo.EnvLen = EnvSize;
Command:
/* Get the VDM command information */
if (!GetNextVDMCommand(&CommandInfo))
{
if (CommandInfo.EnvLen > EnvSize)
{
/* Expand the environment size */
EnvSize = CommandInfo.EnvLen;
CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
/* Repeat the request */
CommandInfo.VDMState |= VDM_FLAG_RETRY;
goto Command;
}
/* Shouldn't happen */
ASSERT(FALSE);
}
/* Load the executable */
Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
AppName,
Parameters,
CmdLine,
Env,
ReturnAddress);
if (Result == ERROR_SUCCESS)
{
/* Increment the re-entry count */
CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
GetNextVDMCommand(&CommandInfo);
}
else
{
DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
}
break;
}
/* Not handled by NTVDM */
default:
{
/* Wait for the process to finish executing */
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
}
}
RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
/* Close the handles */
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
return ERROR_SUCCESS;
}
#endif
VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
{
WORD i;
WORD McbSegment = FIRST_MCB_SEGMENT;
PDOS_MCB CurrentMcb;
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
Psp,
ReturnCode,
KeepResident);
/* Check if this PSP is it's own parent */
if (PspBlock->ParentPsp == Psp) goto Done;
if (KeepResident == 0)
{
for (i = 0; i < PspBlock->HandleTableSize; i++)
{
/* Close the handle */
DosCloseHandle(i);
}
}
/* Free the memory used by the process */
while (TRUE)
{
/* Get a pointer to the MCB */
CurrentMcb = SEGMENT_TO_MCB(McbSegment);
/* Make sure the MCB is valid */
if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
/* Check if this block was allocated by the process */
if (CurrentMcb->OwnerPsp == Psp)
{
if (KeepResident == 0)
{
/* Free this entire block */
DosFreeMemory(McbSegment + 1);
}
else if (KeepResident < CurrentMcb->Size)
{
/* Reduce the size of the block */
DosResizeMemory(McbSegment + 1, KeepResident, NULL);
/* No further paragraphs need to stay resident */
KeepResident = 0;
}
else
{
/* Just reduce the amount of paragraphs we need to keep resident */
KeepResident -= CurrentMcb->Size;
}
}
/* If this was the last block, quit */
if (CurrentMcb->BlockType == 'Z') break;
/* Update the segment and continue */
McbSegment += CurrentMcb->Size + 1;
}
Done:
/* Restore the interrupt vectors */
IntVecTable[0x22] = PspBlock->TerminateAddress;
IntVecTable[0x23] = PspBlock->BreakAddress;
IntVecTable[0x24] = PspBlock->CriticalAddress;
/* Update the current PSP */
if (Psp == CurrentPsp)
{
CurrentPsp = PspBlock->ParentPsp;
if (CurrentPsp == SYSTEM_PSP)
{
ResetEvent(VdmTaskEvent);
CpuUnsimulate();
}
}
#ifndef STANDALONE
// FIXME: This is probably not the best way to do it
/* Check if this was a nested DOS task */
if (CurrentPsp != SYSTEM_PSP)
{
VDM_COMMAND_INFO CommandInfo;
/* Decrement the re-entry count */
CommandInfo.TaskId = SessionId;
CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
GetNextVDMCommand(&CommandInfo);
/* Clear the structure */
RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
/* Update the VDM state of the task */
CommandInfo.TaskId = SessionId;
CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
GetNextVDMCommand(&CommandInfo);
}
#endif
/* Save the return code - Normal termination */
DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
/* Return control to the parent process */
CpuExecute(HIWORD(PspBlock->TerminateAddress),
LOWORD(PspBlock->TerminateAddress));
}

View file

@ -0,0 +1,110 @@
/*
* COPYRIGHT: GPLv2 - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: dos/dos32krnl/process.h
* PURPOSE: DOS32 Processes
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
/* DEFINITIONS ****************************************************************/
#define DOS_CMDLINE_LENGTH 127
#define DOS_PROGRAM_NAME_TAG 0x0001
#define SEGMENT_TO_PSP(seg) ((PDOS_PSP)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
typedef enum
{
DOS_LOAD_AND_EXECUTE = 0x00,
DOS_LOAD_ONLY = 0x01,
DOS_LOAD_OVERLAY = 0x03
} DOS_EXEC_TYPE;
#pragma pack(push, 1)
typedef struct _DOS_PSP
{
BYTE Exit[2];
WORD LastParagraph;
BYTE Reserved0[6];
DWORD TerminateAddress;
DWORD BreakAddress;
DWORD CriticalAddress;
WORD ParentPsp;
BYTE HandleTable[20];
WORD EnvBlock;
DWORD LastStack;
WORD HandleTableSize;
DWORD HandleTablePtr;
DWORD PreviousPsp;
DWORD Reserved1;
WORD DosVersion;
BYTE Reserved2[14];
BYTE FarCall[3];
BYTE Reserved3[9];
DOS_FCB Fcb;
BYTE CommandLineSize;
CHAR CommandLine[DOS_CMDLINE_LENGTH];
} DOS_PSP, *PDOS_PSP;
typedef struct _DOS_EXEC_PARAM_BLOCK
{
union
{
struct
{
/* Input variables */
WORD Environment;
DWORD CommandLine;
DWORD FirstFcb;
DWORD SecondFcb;
/* Output variables */
DWORD StackLocation;
DWORD EntryPoint;
};
struct
{
WORD Segment;
WORD RelocationFactor;
} Overlay;
};
} DOS_EXEC_PARAM_BLOCK, *PDOS_EXEC_PARAM_BLOCK;
#pragma pack(pop)
/* VARIABLES ******************************************************************/
extern WORD CurrentPsp;
/* FUNCTIONS ******************************************************************/
VOID DosClonePsp(WORD DestSegment, WORD SourceSegment);
VOID DosCreatePsp(WORD Segment, WORD ProgramSize);
VOID DosSetProcessContext(WORD Segment);
DWORD DosLoadExecutable
(
IN DOS_EXEC_TYPE LoadType,
IN LPCSTR ExecutablePath,
IN PDOS_EXEC_PARAM_BLOCK Parameters,
IN LPCSTR CommandLine OPTIONAL,
IN LPCSTR Environment OPTIONAL,
IN DWORD ReturnAddress OPTIONAL
);
DWORD DosStartProcess(
IN LPCSTR ExecutablePath,
IN LPCSTR CommandLine,
IN LPCSTR Environment OPTIONAL
);
WORD DosCreateProcess
(
LPCSTR ProgramName,
PDOS_EXEC_PARAM_BLOCK Parameters,
DWORD ReturnAddress
);
VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident);