reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c

982 lines
32 KiB
C
Raw Normal View History

/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: subsystems/mvdm/ntvdm/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 *******************************************************************/
#include "ntvdm.h"
#define NDEBUG
#include <debug.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"
#include "vddsup.h"
/* PRIVATE FUNCTIONS **********************************************************/
static VOID DosInitPsp(IN WORD Segment,
IN WORD EnvBlock,
IN LPCSTR CommandLine,
IN LPCSTR ProgramName)
{
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1);
LPCSTR PspName;
USHORT i;
/* Link the environment block */
PspBlock->EnvBlock = EnvBlock;
/*
* Copy the command line.
* Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
*/
PspBlock->CommandLineSize = min(*(PBYTE)CommandLine, DOS_CMDLINE_LENGTH);
CommandLine++;
RtlCopyMemory(PspBlock->CommandLine, CommandLine, DOS_CMDLINE_LENGTH);
/*
* Initialize the owner name of the MCB of the PSP.
*/
/* Find the start of the file name, skipping all the path elements */
PspName = ProgramName;
while (*ProgramName)
{
switch (*ProgramName++)
{
/* Path delimiter, skip it */
case ':': case '\\': case '/':
PspName = ProgramName;
break;
}
}
/* Copy the file name up to the extension... */
for (i = 0; i < sizeof(Mcb->Name) && PspName[i] != '.' && PspName[i] != '\0'; ++i)
{
Mcb->Name[i] = RtlUpperChar(PspName[i]);
}
/* ... and NULL-terminate if needed */
if (i < sizeof(Mcb->Name)) Mcb->Name[i] = '\0';
// FIXME: Initialize the FCBs
}
static inline VOID DosSaveState(VOID)
{
PDOS_REGISTER_STATE State;
WORD StackPointer = getSP();
DPRINT1("\n"
"DosSaveState(before) -- SS:SP == %04X:%04X\n"
"Original CPU State =\n"
"DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
"DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
"\n",
getSS(), getSP(),
getDS(), getES(), getAX(), getCX(),
getDX(), getBX(), getBP(), getSI(), getDI());
/*
* Allocate stack space for the registers. Note that we
* already have one word allocated (the interrupt number).
*/
StackPointer -= sizeof(DOS_REGISTER_STATE) - sizeof(WORD);
State = SEG_OFF_TO_PTR(getSS(), StackPointer);
setSP(StackPointer);
/* Save */
State->DS = getDS();
State->ES = getES();
State->AX = getAX();
State->CX = getCX();
State->DX = getDX();
State->BX = getBX();
State->BP = getBP();
State->SI = getSI();
State->DI = getDI();
DPRINT1("\n"
"DosSaveState(after) -- SS:SP == %04X:%04X\n"
"Saved State =\n"
"DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
"DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
"\n",
getSS(), getSP(),
State->DS, State->ES, State->AX, State->CX,
State->DX, State->BX, State->BP, State->SI, State->DI);
}
static inline VOID DosRestoreState(VOID)
{
PDOS_REGISTER_STATE State;
/*
* Pop the state structure from the stack. Note that we
* already have one word allocated (the interrupt number).
*/
State = SEG_OFF_TO_PTR(getSS(), getSP());
DPRINT1("\n"
"DosRestoreState(before) -- SS:SP == %04X:%04X\n"
"Saved State =\n"
"DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
"DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
"\n",
getSS(), getSP(),
State->DS, State->ES, State->AX, State->CX,
State->DX, State->BX, State->BP, State->SI, State->DI);
setSP(getSP() + sizeof(DOS_REGISTER_STATE) - sizeof(WORD));
/* Restore */
setDS(State->DS);
setES(State->ES);
setAX(State->AX);
setCX(State->CX);
setDX(State->DX);
setBX(State->BX);
setBP(State->BP);
setSI(State->SI);
setDI(State->DI);
DPRINT1("\n"
"DosRestoreState(after) -- SS:SP == %04X:%04X\n"
"Restored CPU State =\n"
"DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
"DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
"\n",
getSS(), getSP(),
getDS(), getES(), getAX(), getCX(),
getDX(), getBX(), getBP(), getSI(), getDI());
}
static WORD DosCopyEnvironmentBlock(IN LPCSTR Environment OPTIONAL,
IN 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(*DestPsp));
/* 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;
/* Save the interrupt vectors */
PspBlock->TerminateAddress = IntVecTable[0x22];
PspBlock->BreakAddress = IntVecTable[0x23];
PspBlock->CriticalAddress = IntVecTable[0x24];
/* Set the parent PSP */
PspBlock->ParentPsp = Sda->CurrentPsp;
if (Sda->CurrentPsp != SYSTEM_PSP)
{
/* Link to the parent's environment block */
PspBlock->EnvBlock = SEGMENT_TO_PSP(Sda->CurrentPsp)->EnvBlock;
}
/*
else
{
PspBlock->EnvBlock = SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 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 */
// FIXME: This is here that SETVER stuff enters into action!
PspBlock->DosVersion = DosData->DosVersion;
/* Set the far call opcodes */
PspBlock->FarCall[0] = 0xCD; // int 0x21
PspBlock->FarCall[1] = 0x21;
PspBlock->FarCall[2] = 0xCB; // retf
}
VOID DosSetProcessContext(WORD Segment)
{
Sda->CurrentPsp = Segment;
Sda->DiskTransferArea = MAKELONG(0x80, Segment);
}
DWORD DosLoadExecutableInternal(IN DOS_EXEC_TYPE LoadType,
IN LPBYTE ExeBuffer,
IN DWORD ExeBufferSize,
IN LPCSTR ExePath,
IN PDOS_EXEC_PARAM_BLOCK Parameters,
IN LPCSTR CommandLine OPTIONAL,
IN LPCSTR Environment OPTIONAL,
IN DWORD ReturnAddress OPTIONAL)
{
DWORD Result = ERROR_SUCCESS;
WORD Segment = 0;
WORD EnvBlock = 0;
WORD ExeSignature;
WORD LoadSegment;
WORD MaxAllocSize;
WORD FinalSS, FinalSP;
WORD FinalCS, FinalIP;
/* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
CHAR CmdLineBuffer[1 + DOS_CMDLINE_LENGTH];
DPRINT1("DosLoadExecutableInternal(%d, 0x%p, '%s', 0x%p, 0x%p, 0x%p)\n",
LoadType, ExeBuffer, ExePath, Parameters, CommandLine, Environment);
if (LoadType != DOS_LOAD_OVERLAY)
{
/* If an optional Win32 command line is given... */
if (CommandLine)
{
/* ... convert it into DOS format */
BYTE CmdLineLen;
PBYTE CmdLineSize = (PBYTE)CmdLineBuffer;
LPSTR CmdLineStart = CmdLineBuffer + 1;
LPSTR CmdLinePtr = CmdLineStart;
// For debugging purposes
RtlFillMemory(CmdLineBuffer, sizeof(CmdLineBuffer), 0xFF);
/*
* Set the command line: it is either an empty command line or has
* the format: " foo bar ..." (with at least one leading whitespace),
* and is then always followed by '\r' (and optionally by '\n').
*/
CmdLineLen = (BYTE)strlen(CommandLine);
*CmdLineSize = 0;
/*
* Add the leading space if the command line is not empty
* and doesn't already start with some whitespace...
*/
if (*CommandLine && *CommandLine != '\r' && *CommandLine != '\n' &&
*CommandLine != ' ' && *CommandLine != '\t')
{
(*CmdLineSize)++;
*CmdLinePtr++ = ' ';
}
/* Compute the number of characters we need to copy from the original command line */
CmdLineLen = min(CmdLineLen, DOS_CMDLINE_LENGTH - *CmdLineSize);
/* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
while (CmdLineLen && (CommandLine[CmdLineLen - 1] == '\r' || CommandLine[CmdLineLen - 1] == '\n'))
{
CmdLineLen--;
}
/* Finally, set everything up */
*CmdLineSize += CmdLineLen;
RtlCopyMemory(CmdLinePtr, CommandLine, CmdLineLen);
CmdLineStart[*CmdLineSize] = '\r';
/* Finally make the pointer point to the static buffer */
CommandLine = CmdLineBuffer;
}
else
{
/*
* ... otherwise, get the one from the parameter block.
* Format of the command line: 1 byte for size; 127 bytes for contents.
*/
ASSERT(Parameters);
CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
}
/* If no optional environment is given... */
if (Environment == NULL)
{
ASSERT(Parameters);
/* ... get the one from the parameter block (if not NULL)... */
if (Parameters->Environment)
Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
/* ... or the one from the parent (otherwise) */
else
Environment = (LPCSTR)SEG_OFF_TO_PTR(SEGMENT_TO_PSP(Sda->CurrentPsp)->EnvBlock, 0);
}
/* Copy the environment block to DOS memory */
EnvBlock = DosCopyEnvironmentBlock(Environment, ExePath);
if (EnvBlock == 0)
{
Result = Sda->LastErrorCode;
goto Cleanup;
}
}
/*
* Check if this is an EXE file or a COM file by looking
* at the MZ signature:
* 0x4D5A 'MZ': old signature (stored as 0x5A, 0x4D)
* 0x5A4D 'ZM': new signature (stored as 0x4D, 0x5A)
*/
ExeSignature = *(PWORD)ExeBuffer;
if (ExeSignature == 'MZ' || ExeSignature == 'ZM')
{
/* EXE file */
PIMAGE_DOS_HEADER Header;
DWORD BaseSize;
PDWORD RelocationTable;
PWORD RelocWord;
WORD RelocFactor;
WORD i;
/* Get the MZ header */
Header = (PIMAGE_DOS_HEADER)ExeBuffer;
/* Get the base size of the file, in paragraphs (rounded up) */
#if 0 // Normally this is not needed to check for the number of bytes in the last pages.
BaseSize = ((((Header->e_cp - (Header->e_cblp != 0)) * 512) + Header->e_cblp) >> 4)
- Header->e_cparhdr;
#else
// e_cp is the number of 512-byte blocks. 512 == (1 << 9)
// so this corresponds to (1 << 5) number of paragraphs.
//
// For DOS compatibility we need to truncate BaseSize to a WORD value.
// This fact is exploited by some EXEs which are bigger than 1 Mb while
// being able to load on DOS, the main EXE code loads the remaining data.
BaseSize = ((Header->e_cp << 5) - Header->e_cparhdr) & 0xFFFF;
#endif
if (LoadType != DOS_LOAD_OVERLAY)
{
BOOLEAN LoadHigh = FALSE;
DWORD TotalSize;
/* Find the maximum amount of memory that can be allocated */
DosAllocateMemory(0xFFFF, &MaxAllocSize);
/* Compute the total needed size, in paragraphs */
TotalSize = BaseSize + (sizeof(DOS_PSP) >> 4);
/* We must have the required minimum amount of memory. If not, bail out. */
if (MaxAllocSize < TotalSize + Header->e_minalloc)
{
Result = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
/* Check if the program should be loaded high */
if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
{
/* Yes it should. Use all the available memory. */
LoadHigh = TRUE;
TotalSize = MaxAllocSize;
}
else
{
/* Compute the maximum memory size that can be allocated */
if (Header->e_maxalloc != 0)
TotalSize = min(TotalSize + Header->e_maxalloc, MaxAllocSize);
else
TotalSize = MaxAllocSize; // Use all the available memory
}
/* Try to allocate that much memory */
Segment = DosAllocateMemory((WORD)TotalSize, NULL);
if (Segment == 0)
{
Result = Sda->LastErrorCode;
goto Cleanup;
}
/* The process owns its memory */
DosChangeMemoryOwner(Segment , Segment);
DosChangeMemoryOwner(EnvBlock, Segment);
/* Set INT 22h to the return address */
((PULONG)BaseAddress)[0x22] = ReturnAddress;
/* Create the PSP and initialize it */
DosCreatePsp(Segment, (WORD)TotalSize);
DosInitPsp(Segment, EnvBlock, CommandLine, ExePath);
/* 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
{
ASSERT(Parameters);
LoadSegment = Parameters->Overlay.Segment;
RelocFactor = Parameters->Overlay.RelocationFactor;
}
/* Copy the program to the code segment */
RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
ExeBuffer + (Header->e_cparhdr << 4),
min(ExeBufferSize - (Header->e_cparhdr << 4), BaseSize << 4));
/* Get the relocation table */
RelocationTable = (PDWORD)(ExeBuffer + 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;
}
/* Set the stack to the location from the header */
FinalSS = LoadSegment + Header->e_ss;
FinalSP = Header->e_sp;
/* Set the code segment/pointer */
FinalCS = LoadSegment + Header->e_cs;
FinalIP = Header->e_ip;
}
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) < (ExeBufferSize + sizeof(DOS_PSP)))
{
Result = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
/* Allocate all of it */
Segment = DosAllocateMemory(MaxAllocSize, NULL);
if (Segment == 0)
{
Result = Sda->LastErrorCode;
goto Cleanup;
}
/* The process owns its memory */
DosChangeMemoryOwner(Segment , Segment);
DosChangeMemoryOwner(EnvBlock, Segment);
/* Set INT 22h to the return address */
((PULONG)BaseAddress)[0x22] = ReturnAddress;
/* Create the PSP and initialize it */
DosCreatePsp(Segment, MaxAllocSize);
DosInitPsp(Segment, EnvBlock, CommandLine, ExePath);
/* Calculate the segment where the program should be loaded */
LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
}
else
{
ASSERT(Parameters);
LoadSegment = Parameters->Overlay.Segment;
}
/* Copy the program to the code segment */
RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
ExeBuffer, ExeBufferSize);
/* Set the stack to the last word of the segment */
FinalSS = Segment;
FinalSP = 0xFFFE;
/*
* Set the value on the stack to 0x0000, so that a near return
* jumps to PSP:0000 which has the exit code.
*/
*((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0x0000;
/* Set the code segment/pointer */
FinalCS = Segment;
FinalIP = 0x0100;
}
if (LoadType == DOS_LOAD_AND_EXECUTE)
{
/* Save the program state */
if (Sda->CurrentPsp != SYSTEM_PSP)
{
/* Push the task state */
DosSaveState();
DPRINT1("Sda->CurrentPsp = 0x%04x; Old LastStack = 0x%08x, New LastStack = 0x%08x\n",
Sda->CurrentPsp, SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack, MAKELONG(getSP(), getSS()));
/* Update the last stack in the PSP */
SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
}
/* Set the initial segment registers */
setDS(Segment);
setES(Segment);
/* Set the stack */
setSS(FinalSS);
setSP(FinalSP);
/*
* Set the other registers as in real DOS: some demos expect them so!
* See http://www.fysnet.net/yourhelp.htm
* and http://www.beroset.com/asm/showregs.asm
*/
setDX(Segment);
setDI(FinalSP);
setBP(0x091E); // DOS base stack pointer relic value. In MS-DOS 5.0 and Windows' NTVDM it's 0x091C. This is in fact the old SP value inside DosData disk stack.
setSI(FinalIP);
setAX(0/*0xFFFF*/); // FIXME: fcbcode
setBX(0/*0xFFFF*/); // FIXME: fcbcode
setCX(0x00FF);
/*
* Keep critical flags, clear test flags (OF, SF, ZF, AF, PF, CF)
* and explicitely set the interrupt flag.
*/
setEFLAGS((getEFLAGS() & ~0x08D5) | 0x0200);
/* Notify VDDs of process execution */
VDDCreateUserHook(Segment);
/* Execute */
DosSetProcessContext(Segment);
CpuExecute(FinalCS, FinalIP);
}
else if (LoadType == DOS_LOAD_ONLY)
{
ASSERT(Parameters);
Parameters->StackLocation = MAKELONG(FinalSP, FinalSS);
Parameters->EntryPoint = MAKELONG(FinalIP, FinalCS);
}
Cleanup:
if (Result != ERROR_SUCCESS)
{
/* It was not successful, cleanup the DOS memory */
if (EnvBlock) DosFreeMemory(EnvBlock);
if (Segment) DosFreeMemory(Segment);
}
return Result;
}
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;
DWORD FileSize;
LPBYTE Address = NULL;
CHAR FullPath[MAX_PATH];
CHAR ShortFullPath[MAX_PATH];
DPRINT1("DosLoadExecutable(%d, '%s', 0x%p, 0x%p, 0x%p)\n",
LoadType, ExecutablePath, Parameters, CommandLine, Environment);
/* Try to get the full path to the executable */
if (GetFullPathNameA(ExecutablePath, sizeof(FullPath), FullPath, NULL))
{
/* Get the corresponding short path */
if (GetShortPathNameA(FullPath, ShortFullPath, sizeof(ShortFullPath)))
{
/* Use the shortened full path from now on */
ExecutablePath = ShortFullPath;
}
}
/* 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;
}
Result = DosLoadExecutableInternal(LoadType,
Address,
FileSize,
ExecutablePath,
Parameters,
CommandLine,
Environment,
ReturnAddress);
Cleanup:
/* 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;
}
WORD DosCreateProcess(IN LPCSTR ProgramName,
IN PDOS_EXEC_PARAM_BLOCK Parameters,
IN DWORD ReturnAddress OPTIONAL)
{
DWORD Result = ERROR_SUCCESS;
DWORD BinaryType;
/* Get the binary type */
if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
/* Check the type of the program */
switch (BinaryType)
{
/* Those are handled by NTVDM */
case SCS_WOW_BINARY:
{
DisplayMessage(L"Trying to load '%S'.\n"
L"WOW16 applications are not supported internally by NTVDM at the moment.\n"
L"Press 'OK' to continue.",
ProgramName);
// Fall through
}
case SCS_DOS_BINARY:
{
/* Load the executable */
Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
ProgramName,
Parameters,
NULL,
NULL,
ReturnAddress);
if (Result != ERROR_SUCCESS)
{
DisplayMessage(L"Could not load '%S'. Error: %u", ProgramName, Result);
}
break;
}
/* Not handled by NTVDM */
default:
{
LPSTR Environment = NULL;
CHAR CmdLine[MAX_PATH + DOS_CMDLINE_LENGTH + 1];
LPSTR CmdLinePtr;
ULONG CmdLineSize;
/* Did the caller specify an environment segment? */
if (Parameters->Environment)
{
/* Yes, use it instead of the parent one */
Environment = (LPSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
}
/*
* Convert the DOS command line to Win32-compatible format, by concatenating
* the program name with the converted command line.
* Format of the DOS command line: 1 byte for size; 127 bytes for contents.
*/
CmdLinePtr = CmdLine;
strncpy(CmdLinePtr, ProgramName, MAX_PATH); // Concatenate the program name
CmdLinePtr += strlen(CmdLinePtr);
*CmdLinePtr++ = ' '; // Add separating space
CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
RtlCopyMemory(CmdLinePtr,
(LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
CmdLineSize);
/* NULL-terminate it */
CmdLinePtr[CmdLineSize] = '\0';
/* Remove any trailing return carriage character and NULL-terminate the command line */
while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
*CmdLinePtr = '\0';
Result = DosStartProcess32(ProgramName, CmdLine,
Environment, ReturnAddress,
TRUE);
if (Result != ERROR_SUCCESS)
{
DisplayMessage(L"Could not load 32-bit '%S'. Error: %u", ProgramName, Result);
}
break;
}
}
return Result;
}
VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
{
[NTVDM] EMS: - Introduce (and use) helpers for checking validity of EMS handles. - Do not hardcode the EMS page frame segment, but instead allow it to be changed (for now, it is still set to a default value; in the future, via some configuration file or via the registry). This is done by acquiring a UMB block (see after). XMS: - Implement functions 0x01 "Request HMA" and 0x02 "Release HMA". - Fix the return value of functions 0x04 "Global disable A20" and 0x08 "Query free Extended Memory"; simplify code of function 0x0B "Move EMB". - Halfplement function 0x0F "Reallocate Extended Memory Block" in the simple case of size reduction (size expansion is left to the programmer as an exercise :PP ) - Rewrite the UMB provider support (functions 0x10, 0x11, 0x12) by calling the Upper Memory Area manager helpers (see after) (this is closer to reality: UMBs are either provided by XMS driver itself, or by an EMS driver which hooks into the XMS driver chain -- as it is done with MS' himem+EMM386; sometimes all that stuff is contained inside one driver only --) instead of calling back into DOS. This is the DOS which calls XMS for getting the UMB blocks and initializing them! (and not the other way around as it was done in r68001!). NTVDM: - Introduce an "Upper Memory Area manager" which maintains a list of areas of upper memory (>= A000:0000 and <= FFFF:000F) that can be used as RAM blocks. It is intended to work closely with the NTVDM memory manager and be used by XMS for getting possible free UMBs, and by VDDs for implementing the VDDInclude/ExcludeMem APIs (which adds/remove blocks in/from the UMB pool; those are unaccessible to DOS if those APIs are called after NTVDM have been started, but are accessible by XMS). DOS: - Add a helper function for detecting early DOS arena corruptions (for debugging purposes only). - Make the DOS memory manager really UMB-compatible. This means: * not hardcoding the start of the UMB chain; * getting all the available UMB blocks from XMS and initializing them, marking the reserved blocks as read-only (with a correct header; reserved blocks are eg. VGA memory area, ROM blocks...). There is room for improvements obviously (see the FIXMEs in the code). Used documentation is mentioned in comments in the code. This commit should fix quite some apps, as well as it fixes corruptions of loaded ROMs in upper memory: that's how I came into working on fixing the UMB support. In other words, during those two last weeks, I was like in: http://i.imgur.com/zoWpqEB.gifv CORE-9969 #resolve svn path=/trunk/; revision=68586
2015-08-01 17:07:07 +00:00
WORD McbSegment = SysVars->FirstMcb;
PDOS_MCB CurrentMcb;
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
LPWORD Stack;
BYTE TerminationType;
DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
Psp, ReturnCode, KeepResident);
/* Notify VDDs of process termination */
VDDTerminateUserHook(Psp);
/* Check if this PSP is its own parent */
if (PspBlock->ParentPsp == Psp) goto Done;
if (KeepResident == 0)
{
WORD i;
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)
{
/* Check if this is the PSP block and we should reduce its size */
if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
{
/* Reduce the size of the block */
DosResizeMemory(McbSegment + 1, KeepResident, NULL);
break;
}
}
else
{
/* Free this entire block */
DosFreeMemory(McbSegment + 1);
}
}
/* 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 with the parent's one */
if (Psp == Sda->CurrentPsp)
{
DosSetProcessContext(PspBlock->ParentPsp);
if (Sda->CurrentPsp == SYSTEM_PSP)
{
// NOTE: we can also use the DOS BIOS exit code.
CpuUnsimulate();
return;
}
}
/* Save the return code - Normal termination or TSR */
TerminationType = (KeepResident != 0 ? 0x03 : 0x00);
Sda->ErrorLevel = MAKEWORD(ReturnCode, TerminationType);
DPRINT1("PspBlock->ParentPsp = 0x%04x; Sda->CurrentPsp = 0x%04x\n",
PspBlock->ParentPsp, Sda->CurrentPsp);
if (Sda->CurrentPsp != SYSTEM_PSP)
{
DPRINT1("Sda->CurrentPsp = 0x%04x; Old SS:SP = %04X:%04X going to be LastStack = 0x%08x\n",
Sda->CurrentPsp, getSS(), getSP(), SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack);
/* Restore the parent's stack */
setSS(HIWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack));
setSP(LOWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack));
/* Pop the task state */
DosRestoreState();
}
/* Return control to the parent process */
Stack = (LPWORD)SEG_OFF_TO_PTR(getSS(), getSP());
Stack[STACK_CS] = HIWORD(PspBlock->TerminateAddress);
Stack[STACK_IP] = LOWORD(PspBlock->TerminateAddress);
}
/* EOF */