mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 23:12:56 +00:00
[NTVDM]
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:
parent
2aab9460a2
commit
613b74b43d
10 changed files with 923 additions and 712 deletions
|
@ -36,6 +36,7 @@ list(APPEND SOURCE
|
||||||
dos/dos32krnl/handle.c
|
dos/dos32krnl/handle.c
|
||||||
dos/dos32krnl/himem.c
|
dos/dos32krnl/himem.c
|
||||||
dos/dos32krnl/memory.c
|
dos/dos32krnl/memory.c
|
||||||
|
dos/dos32krnl/process.c
|
||||||
dos/mouse32.c
|
dos/mouse32.c
|
||||||
dos/dem.c
|
dos/dem.c
|
||||||
clock.c
|
clock.c
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "dem.h"
|
#include "dem.h"
|
||||||
#include "dos/dos32krnl/device.h"
|
#include "dos/dos32krnl/device.h"
|
||||||
|
#include "dos/dos32krnl/process.h"
|
||||||
#include "cpu/bop.h"
|
#include "cpu/bop.h"
|
||||||
|
|
||||||
#include "bios/bios.h"
|
#include "bios/bios.h"
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "handle.h"
|
#include "handle.h"
|
||||||
#include "dosfiles.h"
|
#include "dosfiles.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "process.h"
|
||||||
#include "himem.h"
|
#include "himem.h"
|
||||||
|
|
||||||
#include "bios/bios.h"
|
#include "bios/bios.h"
|
||||||
|
@ -37,11 +38,9 @@
|
||||||
|
|
||||||
CALLBACK16 DosContext;
|
CALLBACK16 DosContext;
|
||||||
|
|
||||||
static DWORD DiskTransferArea;
|
|
||||||
/*static*/ BYTE CurrentDrive;
|
/*static*/ BYTE CurrentDrive;
|
||||||
static CHAR LastDrive = 'E';
|
static CHAR LastDrive = 'E';
|
||||||
static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
|
static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
|
||||||
static WORD DosErrorLevel = 0x0000;
|
|
||||||
static PBYTE InDos;
|
static PBYTE InDos;
|
||||||
|
|
||||||
/* PUBLIC VARIABLES ***********************************************************/
|
/* PUBLIC VARIABLES ***********************************************************/
|
||||||
|
@ -50,79 +49,13 @@ PDOS_SYSVARS SysVars;
|
||||||
|
|
||||||
/* Echo state for INT 21h, AH = 01h and AH = 3Fh */
|
/* Echo state for INT 21h, AH = 01h and AH = 3Fh */
|
||||||
BOOLEAN DoEcho = FALSE;
|
BOOLEAN DoEcho = FALSE;
|
||||||
WORD CurrentPsp = SYSTEM_PSP;
|
|
||||||
|
DWORD DiskTransferArea;
|
||||||
|
WORD DosErrorLevel = 0x0000;
|
||||||
WORD DosLastError = 0;
|
WORD DosLastError = 0;
|
||||||
|
|
||||||
/* PRIVATE FUNCTIONS **********************************************************/
|
/* 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)
|
static BOOLEAN DosChangeDrive(BYTE Drive)
|
||||||
{
|
{
|
||||||
WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
|
WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
|
||||||
|
@ -272,556 +205,13 @@ VOID DosInitializePsp(WORD PspSegment,
|
||||||
PspBlock->FarCall[1] = 0x21;
|
PspBlock->FarCall[1] = 0x21;
|
||||||
PspBlock->FarCall[2] = 0xCB; // retf
|
PspBlock->FarCall[2] = 0xCB; // retf
|
||||||
|
|
||||||
/* Set the command line */
|
if (CommandLine)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
DPRINT1("Overlay loading is not supported yet.\n");
|
/* Set the command line */
|
||||||
return ERROR_NOT_SUPPORTED;
|
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)
|
VOID WINAPI DosInt20h(LPWORD Stack)
|
||||||
|
@ -1205,7 +595,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
|
||||||
/* Create New PSP */
|
/* Create New PSP */
|
||||||
case 0x26:
|
case 0x26:
|
||||||
{
|
{
|
||||||
DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
|
DosClonePsp(getDX(), getCS());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1963,7 +1353,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef STANDALONE
|
|
||||||
/* Execute */
|
/* Execute */
|
||||||
case 0x4B:
|
case 0x4B:
|
||||||
{
|
{
|
||||||
|
@ -1973,19 +1362,24 @@ VOID WINAPI DosInt21h(LPWORD Stack)
|
||||||
DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
|
DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
|
||||||
WORD ErrorCode;
|
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
|
else
|
||||||
{
|
{
|
||||||
ErrorCode = DosLoadExecutable(DOS_LOAD_OVERLAY,
|
#else
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
/* Just load an executable */
|
||||||
|
ErrorCode = DosLoadExecutable(LoadType,
|
||||||
ProgramName,
|
ProgramName,
|
||||||
FAR_POINTER(ParamBlock->CommandLine),
|
ParamBlock,
|
||||||
SEG_OFF_TO_PTR(ParamBlock->Environment, 0),
|
|
||||||
ReturnAddress,
|
|
||||||
NULL,
|
NULL,
|
||||||
NULL);
|
NULL,
|
||||||
|
ReturnAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ErrorCode == ERROR_SUCCESS)
|
if (ErrorCode == ERROR_SUCCESS)
|
||||||
|
@ -2000,7 +1394,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Terminate With Return Code */
|
/* Terminate With Return Code */
|
||||||
case 0x4C:
|
case 0x4C:
|
||||||
|
@ -2057,8 +1450,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
|
||||||
/* Internal - Set Current Process ID (Set PSP Address) */
|
/* Internal - Set Current Process ID (Set PSP Address) */
|
||||||
case 0x50:
|
case 0x50:
|
||||||
{
|
{
|
||||||
// FIXME: Is it really what it's done ??
|
DosSetProcessContext(getBX());
|
||||||
CurrentPsp = getBX();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2093,6 +1485,13 @@ VOID WINAPI DosInt21h(LPWORD Stack)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Create Child PSP */
|
||||||
|
case 0x55:
|
||||||
|
{
|
||||||
|
DosCreatePsp(getDX(), getSI());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Rename File */
|
/* Rename File */
|
||||||
case 0x56:
|
case 0x56:
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,28 +39,17 @@
|
||||||
#define DOS_ERROR_HANDLE 2
|
#define DOS_ERROR_HANDLE 2
|
||||||
|
|
||||||
#define DOS_SFT_SIZE 255
|
#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_START_SEGMENT 0xC000
|
||||||
#define UMB_END_SEGMENT 0xDFFF
|
#define UMB_END_SEGMENT 0xDFFF
|
||||||
#define DOS_ALLOC_HIGH 0x40
|
#define DOS_ALLOC_HIGH 0x40
|
||||||
#define DOS_ALLOC_HIGH_LOW 0x80
|
#define DOS_ALLOC_HIGH_LOW 0x80
|
||||||
#define DOS_CMDLINE_LENGTH 127
|
|
||||||
#define DOS_DIR_LENGTH 64
|
#define DOS_DIR_LENGTH 64
|
||||||
#define NUM_DRIVES ('Z' - 'A' + 1)
|
#define NUM_DRIVES ('Z' - 'A' + 1)
|
||||||
#define DOS_CHAR_ATTRIBUTE 0x07
|
#define DOS_CHAR_ATTRIBUTE 0x07
|
||||||
#define DOS_PROGRAM_NAME_TAG 0x0001
|
|
||||||
|
|
||||||
/* 16 MB of EMS memory */
|
/* 16 MB of EMS memory */
|
||||||
#define EMS_TOTAL_PAGES 1024
|
#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)
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
typedef struct _DOS_FCB
|
typedef struct _DOS_FCB
|
||||||
|
@ -102,31 +91,6 @@ typedef struct _DOS_SYSVARS
|
||||||
BYTE NullDriverRoutine[7];
|
BYTE NullDriverRoutine[7];
|
||||||
} DOS_SYSVARS, *PDOS_SYSVARS;
|
} 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
|
typedef struct _DOS_INPUT_BUFFER
|
||||||
{
|
{
|
||||||
BYTE MaxLength;
|
BYTE MaxLength;
|
||||||
|
@ -150,19 +114,6 @@ typedef struct _DOS_FIND_FILE_BLOCK
|
||||||
CHAR FileName[13];
|
CHAR FileName[13];
|
||||||
} DOS_FIND_FILE_BLOCK, *PDOS_FIND_FILE_BLOCK;
|
} 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
|
typedef struct _DOS_COUNTRY_CODE_BUFFER
|
||||||
{
|
{
|
||||||
WORD TimeFormat;
|
WORD TimeFormat;
|
||||||
|
@ -176,7 +127,8 @@ typedef struct _DOS_COUNTRY_CODE_BUFFER
|
||||||
/* VARIABLES ******************************************************************/
|
/* VARIABLES ******************************************************************/
|
||||||
|
|
||||||
extern BOOLEAN DoEcho;
|
extern BOOLEAN DoEcho;
|
||||||
extern WORD CurrentPsp;
|
extern DWORD DiskTransferArea;
|
||||||
|
extern WORD DosErrorLevel;
|
||||||
extern WORD DosLastError;
|
extern WORD DosLastError;
|
||||||
extern PDOS_SYSVARS SysVars;
|
extern PDOS_SYSVARS SysVars;
|
||||||
|
|
||||||
|
@ -207,36 +159,6 @@ VOID ConDrvCleanup(VOID);
|
||||||
* See dos.c
|
* 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);
|
BOOLEAN DosKRNLInitialize(VOID);
|
||||||
|
|
||||||
#endif // _DOS_H_
|
#endif // _DOS_H_
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "dos/dem.h"
|
#include "dos/dem.h"
|
||||||
#include "dosfiles.h"
|
#include "dosfiles.h"
|
||||||
#include "handle.h"
|
#include "handle.h"
|
||||||
|
#include "process.h"
|
||||||
|
|
||||||
#include "bios/bios.h"
|
#include "bios/bios.h"
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "dosfiles.h"
|
#include "dosfiles.h"
|
||||||
#include "handle.h"
|
#include "handle.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "process.h"
|
||||||
|
|
||||||
/* PRIVATE FUNCTIONS **********************************************************/
|
/* PRIVATE FUNCTIONS **********************************************************/
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,11 @@
|
||||||
|
|
||||||
#include "ntvdm.h"
|
#include "ntvdm.h"
|
||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
#include "memory.h"
|
|
||||||
|
|
||||||
#include "dos.h"
|
#include "dos.h"
|
||||||
#include "dos/dem.h"
|
#include "dos/dem.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "process.h"
|
||||||
|
|
||||||
/* PUBLIC VARIABLES ***********************************************************/
|
/* PUBLIC VARIABLES ***********************************************************/
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
/* TYPEDEFS *******************************************************************/
|
/* TYPEDEFS *******************************************************************/
|
||||||
|
|
||||||
|
#define SEGMENT_TO_MCB(seg) ((PDOS_MCB)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
|
||||||
|
|
||||||
enum DOS_ALLOC_STRATEGY
|
enum DOS_ALLOC_STRATEGY
|
||||||
{
|
{
|
||||||
DOS_ALLOC_FIRST_FIT,
|
DOS_ALLOC_FIRST_FIT,
|
||||||
|
|
773
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c
Normal file
773
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c
Normal 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));
|
||||||
|
}
|
||||||
|
|
110
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.h
Normal file
110
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.h
Normal 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);
|
Loading…
Add table
Add a link
Reference in a new issue