[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / process.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dos/dos32krnl/process.c
5 * PURPOSE: DOS32 Processes
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #define NDEBUG
13
14 #include "ntvdm.h"
15 #include "emulator.h"
16 #include "cpu/cpu.h"
17
18 #include "dos.h"
19 #include "dos/dem.h"
20 #include "dosfiles.h"
21 #include "handle.h"
22 #include "process.h"
23 #include "memory.h"
24
25 #include "bios/bios.h"
26
27 #include "io.h"
28 #include "hardware/ps2.h"
29
30 /* PUBLIC VARIABLES ***********************************************************/
31
32 WORD CurrentPsp = SYSTEM_PSP;
33
34 /* PRIVATE FUNCTIONS **********************************************************/
35
36 static inline VOID DosSetPspCommandLine(WORD Segment, LPCSTR CommandLine)
37 {
38 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
39
40 /*
41 * Copy the command line block.
42 * Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
43 */
44 PspBlock->CommandLineSize = min(*(PBYTE)CommandLine, DOS_CMDLINE_LENGTH);
45 CommandLine++;
46 RtlCopyMemory(PspBlock->CommandLine, CommandLine, DOS_CMDLINE_LENGTH);
47 }
48
49 static inline VOID DosSaveState(VOID)
50 {
51 PDOS_REGISTER_STATE State;
52 WORD StackPointer = getSP();
53
54 /* Allocate stack space for the registers */
55 StackPointer -= sizeof(DOS_REGISTER_STATE);
56 State = SEG_OFF_TO_PTR(getSS(), StackPointer);
57
58 /* Save */
59 State->EAX = getEAX();
60 State->ECX = getECX();
61 State->EDX = getEDX();
62 State->EBX = getEBX();
63 State->ESP = getESP();
64 State->EBP = getEBP();
65 State->ESI = getESI();
66 State->EDI = getEDI();
67 State->DS = getDS();
68 State->ES = getES();
69 State->FS = getFS();
70 State->GS = getGS();
71 State->Flags = getEFLAGS();
72 }
73
74 static inline VOID DosRestoreState(VOID)
75 {
76 PDOS_REGISTER_STATE State;
77 WORD StackPointer = getSP();
78
79 /* SS:SP points to the stack on the last entry to INT 21h */
80 StackPointer -= (STACK_FLAGS + 1) * 2; /* Interrupt parameters */
81 StackPointer -= sizeof(DOS_REGISTER_STATE); /* Pushed state structure */
82 State = SEG_OFF_TO_PTR(getSS(), StackPointer);
83
84 /* Restore */
85 setEAX(State->EAX);
86 setECX(State->ECX);
87 setEDX(State->EDX);
88 setEBX(State->EBX);
89 setEBP(State->EBP);
90 setESI(State->ESI);
91 setEDI(State->EDI);
92 setDS(State->DS);
93 setES(State->ES);
94 setFS(State->FS);
95 setGS(State->GS);
96 setEFLAGS(State->Flags);
97 }
98
99 static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
100 LPCSTR ProgramName)
101 {
102 PCHAR Ptr, DestBuffer = NULL;
103 ULONG TotalSize = 0;
104 WORD DestSegment;
105
106 /* If we have an environment strings list, compute its size */
107 if (Environment)
108 {
109 /* Calculate the size of the environment block */
110 Ptr = (PCHAR)Environment;
111 while (*Ptr) Ptr += strlen(Ptr) + 1;
112 TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
113 }
114 else
115 {
116 /* Empty environment string */
117 TotalSize = 1;
118 }
119 /* Add the final environment block NULL-terminator */
120 TotalSize++;
121
122 /* Add the two bytes for the program name tag */
123 TotalSize += 2;
124
125 /* Add the string buffer size */
126 TotalSize += strlen(ProgramName) + 1;
127
128 /* Allocate the memory for the environment block */
129 DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
130 if (!DestSegment) return 0;
131
132 DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
133
134 /* If we have an environment strings list, copy it */
135 if (Environment)
136 {
137 Ptr = (PCHAR)Environment;
138 while (*Ptr)
139 {
140 /* Copy the string and NULL-terminate it */
141 strcpy(DestBuffer, Ptr);
142 DestBuffer += strlen(Ptr);
143 *(DestBuffer++) = '\0';
144
145 /* Move to the next string */
146 Ptr += strlen(Ptr) + 1;
147 }
148 }
149 else
150 {
151 /* Empty environment string */
152 *(DestBuffer++) = '\0';
153 }
154 /* NULL-terminate the environment block */
155 *(DestBuffer++) = '\0';
156
157 /* Store the special program name tag */
158 *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
159 *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
160
161 /* Copy the program name after the environment block */
162 strcpy(DestBuffer, ProgramName);
163
164 return DestSegment;
165 }
166
167 /* PUBLIC FUNCTIONS ***********************************************************/
168
169 VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
170 {
171 PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
172 PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
173 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
174
175 /* Literally copy the PSP first */
176 RtlCopyMemory(DestPsp, SourcePsp, sizeof(DOS_PSP));
177
178 /* Save the interrupt vectors */
179 DestPsp->TerminateAddress = IntVecTable[0x22];
180 DestPsp->BreakAddress = IntVecTable[0x23];
181 DestPsp->CriticalAddress = IntVecTable[0x24];
182
183 /* No parent PSP */
184 DestPsp->ParentPsp = 0;
185
186 /* Set the handle table pointers to the internal handle table */
187 DestPsp->HandleTableSize = DEFAULT_JFT_SIZE;
188 DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
189
190 /* Copy the parent handle table without referencing the SFT */
191 RtlCopyMemory(FAR_POINTER(DestPsp->HandleTablePtr),
192 FAR_POINTER(SourcePsp->HandleTablePtr),
193 DEFAULT_JFT_SIZE);
194 }
195
196 VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
197 {
198 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
199 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
200
201 RtlZeroMemory(PspBlock, sizeof(*PspBlock));
202
203 /* Set the exit interrupt */
204 PspBlock->Exit[0] = 0xCD; // int 0x20
205 PspBlock->Exit[1] = 0x20;
206
207 /* Set the number of the last paragraph */
208 PspBlock->LastParagraph = Segment + ProgramSize - 1;
209
210 /* Save the interrupt vectors */
211 PspBlock->TerminateAddress = IntVecTable[0x22];
212 PspBlock->BreakAddress = IntVecTable[0x23];
213 PspBlock->CriticalAddress = IntVecTable[0x24];
214
215 /* Set the parent PSP */
216 PspBlock->ParentPsp = CurrentPsp;
217
218 /* No environment block yet */
219 PspBlock->EnvBlock = 0;
220
221 /* Copy the parent handle table */
222 DosCopyHandleTable(PspBlock->HandleTable);
223
224 /* Set the handle table pointers to the internal handle table */
225 PspBlock->HandleTableSize = DEFAULT_JFT_SIZE;
226 PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
227
228 /* Set the DOS version */
229 PspBlock->DosVersion = DOS_VERSION;
230
231 /* Set the far call opcodes */
232 PspBlock->FarCall[0] = 0xCD; // int 0x21
233 PspBlock->FarCall[1] = 0x21;
234 PspBlock->FarCall[2] = 0xCB; // retf
235 }
236
237 VOID DosSetProcessContext(WORD Segment)
238 {
239 CurrentPsp = Segment;
240 DiskTransferArea = MAKELONG(0x80, Segment);
241 }
242
243 DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
244 IN LPCSTR ExecutablePath,
245 IN PDOS_EXEC_PARAM_BLOCK Parameters,
246 IN LPCSTR CommandLine OPTIONAL,
247 IN LPCSTR Environment OPTIONAL,
248 IN DWORD ReturnAddress OPTIONAL)
249 {
250 DWORD Result = ERROR_SUCCESS;
251 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
252 LPBYTE Address = NULL;
253 WORD Segment = 0;
254 WORD EnvBlock = 0;
255 WORD LoadSegment;
256 WORD MaxAllocSize;
257 DWORD i, FileSize;
258 CHAR FullPath[MAX_PATH];
259 CHAR ShortFullPath[MAX_PATH];
260
261 /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
262 CHAR CmdLineBuffer[1 + DOS_CMDLINE_LENGTH];
263
264 DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
265 LoadType,
266 ExecutablePath,
267 Parameters,
268 ReturnAddress);
269
270 /* Try to get the full path to the executable */
271 if (GetFullPathNameA(ExecutablePath, sizeof(FullPath), FullPath, NULL))
272 {
273 /* Try to shorten the full path */
274 if (GetShortPathNameA(FullPath, ShortFullPath, sizeof(ShortFullPath)))
275 {
276 /* Use the shortened full path from now on */
277 ExecutablePath = ShortFullPath;
278 }
279 }
280
281 /* Open a handle to the executable */
282 FileHandle = CreateFileA(ExecutablePath,
283 GENERIC_READ,
284 FILE_SHARE_READ,
285 NULL,
286 OPEN_EXISTING,
287 FILE_ATTRIBUTE_NORMAL,
288 NULL);
289 if (FileHandle == INVALID_HANDLE_VALUE)
290 {
291 Result = GetLastError();
292 goto Cleanup;
293 }
294
295 /* Get the file size */
296 FileSize = GetFileSize(FileHandle, NULL);
297
298 /* Create a mapping object for the file */
299 FileMapping = CreateFileMapping(FileHandle,
300 NULL,
301 PAGE_READONLY,
302 0,
303 0,
304 NULL);
305 if (FileMapping == NULL)
306 {
307 Result = GetLastError();
308 goto Cleanup;
309 }
310
311 /* Map the file into memory */
312 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
313 if (Address == NULL)
314 {
315 Result = GetLastError();
316 goto Cleanup;
317 }
318
319 if (LoadType != DOS_LOAD_OVERLAY)
320 {
321 /* If an optional Win32 command line is given... */
322 if (CommandLine)
323 {
324 /* ... convert it into DOS format */
325 BYTE CmdLineLen;
326
327 PBYTE CmdLineSize = (PBYTE)CmdLineBuffer;
328 LPSTR CmdLineStart = CmdLineBuffer + 1;
329 LPSTR CmdLinePtr = CmdLineStart;
330
331 // For debugging purposes
332 RtlFillMemory(CmdLineBuffer, sizeof(CmdLineBuffer), 0xFF);
333
334 /*
335 * Set the command line: it is either an empty command line or has
336 * the format: " foo bar ..." (with at least one leading whitespace),
337 * and is then always followed by '\r' (and optionally by '\n').
338 */
339 CmdLineLen = (BYTE)strlen(CommandLine);
340 *CmdLineSize = 0;
341
342 /*
343 * Add the leading space if the command line is not empty
344 * and doesn't already start with some whitespace...
345 */
346 if (*CommandLine && *CommandLine != '\r' && *CommandLine != '\n' &&
347 *CommandLine != ' ' && *CommandLine != '\t')
348 {
349 (*CmdLineSize)++;
350 *CmdLinePtr++ = ' ';
351 }
352
353 /* Compute the number of characters we need to copy from the original command line */
354 CmdLineLen = min(CmdLineLen, DOS_CMDLINE_LENGTH - *CmdLineSize);
355
356 /* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
357 while (CmdLineLen && (CommandLine[CmdLineLen - 1] == '\r' || CommandLine[CmdLineLen - 1] == '\n'))
358 {
359 CmdLineLen--;
360 }
361
362 /* Finally, set everything up */
363 *CmdLineSize += CmdLineLen;
364 RtlCopyMemory(CmdLinePtr, CommandLine, CmdLineLen);
365 CmdLineStart[*CmdLineSize] = '\r';
366
367 /* Finally make the pointer point to the static buffer */
368 CommandLine = CmdLineBuffer;
369 }
370 else
371 {
372 /*
373 * ... otherwise, get the one from the parameter block.
374 * Format of the command line: 1 byte for size; 127 bytes for contents.
375 */
376 ASSERT(Parameters);
377 CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
378 }
379
380 /* If no optional environment is given... */
381 if (Environment == NULL)
382 {
383 /* ... get the one from the parameter block */
384 ASSERT(Parameters);
385 Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
386 }
387
388 /* Copy the environment block to DOS memory */
389 EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
390 if (EnvBlock == 0)
391 {
392 Result = ERROR_NOT_ENOUGH_MEMORY;
393 goto Cleanup;
394 }
395 }
396
397 /* Check if this is an EXE file or a COM file */
398 if (Address[0] == 'M' && Address[1] == 'Z')
399 {
400 /* EXE file */
401 PIMAGE_DOS_HEADER Header;
402 DWORD BaseSize;
403 PDWORD RelocationTable;
404 PWORD RelocWord;
405 WORD RelocFactor;
406 BOOLEAN LoadHigh = FALSE;
407
408 /* Get the MZ header */
409 Header = (PIMAGE_DOS_HEADER)Address;
410
411 /* Get the base size of the file, in paragraphs (rounded up) */
412 BaseSize = ((((Header->e_cp - (Header->e_cblp != 0)) * 512)
413 + Header->e_cblp + 0x0F) >> 4) - Header->e_cparhdr;
414
415 if (LoadType != DOS_LOAD_OVERLAY)
416 {
417 DWORD TotalSize = BaseSize;
418
419 /* Add the PSP size, in paragraphs */
420 TotalSize += sizeof(DOS_PSP) >> 4;
421
422 /* Add the maximum size that should be allocated */
423 TotalSize += Header->e_maxalloc;
424
425 if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
426 {
427 /* This program should be loaded high */
428 LoadHigh = TRUE;
429 TotalSize = 0xFFFF;
430 }
431
432 /* Make sure it does not pass 0xFFFF */
433 if (TotalSize > 0xFFFF) TotalSize = 0xFFFF;
434
435 /* Try to allocate that much memory */
436 Segment = DosAllocateMemory((WORD)TotalSize, &MaxAllocSize);
437
438 if (Segment == 0)
439 {
440 /* Check if there's at least enough memory for the minimum size */
441 if (MaxAllocSize < (BaseSize + (sizeof(DOS_PSP) >> 4) + Header->e_minalloc))
442 {
443 Result = DosLastError;
444 goto Cleanup;
445 }
446
447 /* Allocate that minimum amount */
448 TotalSize = MaxAllocSize;
449 Segment = DosAllocateMemory((WORD)TotalSize, NULL);
450 ASSERT(Segment != 0);
451 }
452
453 /* The process owns its own memory */
454 DosChangeMemoryOwner(Segment, Segment);
455 DosChangeMemoryOwner(EnvBlock, Segment);
456
457 /* Set INT 22h to the return address */
458 ((PULONG)BaseAddress)[0x22] = ReturnAddress;
459
460 /* Create the PSP */
461 DosCreatePsp(Segment, (WORD)TotalSize);
462 DosSetPspCommandLine(Segment, CommandLine);
463 SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
464
465 /* Calculate the segment where the program should be loaded */
466 if (!LoadHigh) LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
467 else LoadSegment = Segment + TotalSize - BaseSize;
468
469 RelocFactor = LoadSegment;
470 }
471 else
472 {
473 ASSERT(Parameters);
474 LoadSegment = Parameters->Overlay.Segment;
475 RelocFactor = Parameters->Overlay.RelocationFactor;
476 }
477
478 /* Copy the program to the code segment */
479 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
480 Address + (Header->e_cparhdr << 4),
481 min(FileSize - (Header->e_cparhdr << 4), BaseSize << 4));
482
483 /* Get the relocation table */
484 RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
485
486 /* Perform relocations */
487 for (i = 0; i < Header->e_crlc; i++)
488 {
489 /* Get a pointer to the word that needs to be patched */
490 RelocWord = (PWORD)SEG_OFF_TO_PTR(LoadSegment + HIWORD(RelocationTable[i]),
491 LOWORD(RelocationTable[i]));
492
493 /* Add the relocation factor to it */
494 *RelocWord += RelocFactor;
495 }
496
497 if (LoadType == DOS_LOAD_AND_EXECUTE)
498 {
499 /* Save the program state */
500 if (CurrentPsp != SYSTEM_PSP) DosSaveState();
501
502 /* Set the initial segment registers */
503 setDS(Segment);
504 setES(Segment);
505
506 /* Set the stack to the location from the header */
507 setSS(LoadSegment + Header->e_ss);
508 setSP(Header->e_sp);
509
510 /* Execute */
511 DosSetProcessContext(Segment);
512 CpuExecute(LoadSegment + Header->e_cs, Header->e_ip);
513 }
514 else if (LoadType == DOS_LOAD_ONLY)
515 {
516 ASSERT(Parameters);
517 Parameters->StackLocation = MAKELONG(Header->e_sp, LoadSegment + Header->e_ss);
518 Parameters->EntryPoint = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
519 }
520 }
521 else
522 {
523 /* COM file */
524
525 if (LoadType != DOS_LOAD_OVERLAY)
526 {
527 /* Find the maximum amount of memory that can be allocated */
528 DosAllocateMemory(0xFFFF, &MaxAllocSize);
529
530 /* Make sure it's enough for the whole program and the PSP */
531 if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
532 {
533 Result = ERROR_NOT_ENOUGH_MEMORY;
534 goto Cleanup;
535 }
536
537 /* Allocate all of it */
538 Segment = DosAllocateMemory(MaxAllocSize, NULL);
539 if (Segment == 0)
540 {
541 Result = DosLastError;
542 goto Cleanup;
543 }
544
545 /* The process owns its own memory */
546 DosChangeMemoryOwner(Segment, Segment);
547 DosChangeMemoryOwner(EnvBlock, Segment);
548
549 /* Set INT 22h to the return address */
550 ((PULONG)BaseAddress)[0x22] = ReturnAddress;
551
552 /* Create the PSP */
553 DosCreatePsp(Segment, MaxAllocSize);
554 DosSetPspCommandLine(Segment, CommandLine);
555 SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
556
557 /* Calculate the segment where the program should be loaded */
558 LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
559 }
560 else
561 {
562 ASSERT(Parameters);
563 LoadSegment = Parameters->Overlay.Segment;
564 }
565
566 /* Copy the program to the code segment */
567 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
568 Address,
569 FileSize);
570
571 if (LoadType == DOS_LOAD_AND_EXECUTE)
572 {
573 /* Set the initial segment registers */
574 setDS(Segment);
575 setES(Segment);
576
577 /* Set the stack to the last word of the segment */
578 setSS(Segment);
579 setSP(0xFFFE);
580
581 /*
582 * Set the value on the stack to 0, so that a near return
583 * jumps to PSP:0000 which has the exit code.
584 */
585 *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
586
587 /* Execute */
588 DosSetProcessContext(Segment);
589 CpuExecute(Segment, 0x100);
590 }
591 else if (LoadType == DOS_LOAD_ONLY)
592 {
593 ASSERT(Parameters);
594 Parameters->StackLocation = MAKELONG(0xFFFE, Segment);
595 Parameters->EntryPoint = MAKELONG(0x0100, Segment);
596 }
597 }
598
599 Cleanup:
600 if (Result != ERROR_SUCCESS)
601 {
602 /* It was not successful, cleanup the DOS memory */
603 if (EnvBlock) DosFreeMemory(EnvBlock);
604 if (Segment) DosFreeMemory(Segment);
605 }
606
607 /* Unmap the file*/
608 if (Address != NULL) UnmapViewOfFile(Address);
609
610 /* Close the file mapping object */
611 if (FileMapping != NULL) CloseHandle(FileMapping);
612
613 /* Close the file handle */
614 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
615
616 return Result;
617 }
618
619 DWORD DosStartProcess(IN LPCSTR ExecutablePath,
620 IN LPCSTR CommandLine,
621 IN LPCSTR Environment OPTIONAL)
622 {
623 DWORD Result;
624 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
625
626 SIZE_T CmdLen = strlen(CommandLine);
627 DPRINT1("Starting '%s' ('%.*s')...\n",
628 ExecutablePath,
629 /* Display the command line without the terminating 0d 0a (and skip the terminating NULL) */
630 CmdLen >= 2 ? (CommandLine[CmdLen - 2] == '\r' ? CmdLen - 2
631 : CmdLen)
632 : CmdLen,
633 CommandLine);
634
635 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
636 ExecutablePath,
637 NULL,
638 CommandLine,
639 Environment,
640 IntVecTable[0x20]);
641
642 if (Result != ERROR_SUCCESS) goto Quit;
643
644 /* Update console title if we run in a separate console */
645 if (SessionId != 0)
646 SetConsoleTitleA(ExecutablePath);
647
648 /* Attach to the console */
649 ConsoleAttach();
650 VidBiosAttachToConsole();
651
652 /* Start simulation */
653 SetEvent(VdmTaskEvent);
654 CpuSimulate();
655
656 /* Detach from the console */
657 VidBiosDetachFromConsole();
658 ConsoleDetach();
659
660 Quit:
661 return Result;
662 }
663
664 #ifndef STANDALONE
665 WORD DosCreateProcess(LPCSTR ProgramName,
666 PDOS_EXEC_PARAM_BLOCK Parameters,
667 DWORD ReturnAddress)
668 {
669 DWORD Result;
670 DWORD BinaryType;
671 LPVOID Environment = NULL;
672 VDM_COMMAND_INFO CommandInfo;
673 CHAR CmdLine[MAX_PATH]; // DOS_CMDLINE_LENGTH + 1
674 CHAR AppName[MAX_PATH];
675 CHAR PifFile[MAX_PATH];
676 CHAR Desktop[MAX_PATH];
677 CHAR Title[MAX_PATH];
678 LPSTR CmdLinePtr;
679 ULONG CmdLineSize;
680 ULONG EnvSize = 256;
681 PVOID Env;
682 STARTUPINFOA StartupInfo;
683 PROCESS_INFORMATION ProcessInfo;
684
685 /* Get the binary type */
686 if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
687
688 /* Initialize Win32-VDM environment */
689 Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
690 if (Env == NULL) return GetLastError();
691
692 /* Did the caller specify an environment segment? */
693 if (Parameters->Environment)
694 {
695 /* Yes, use it instead of the parent one */
696 Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
697 }
698
699 /* Set up the startup info structure */
700 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
701 StartupInfo.cb = sizeof(StartupInfo);
702
703 /*
704 * Convert the DOS command line to Win32-compatible format.
705 * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
706 */
707 CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
708 RtlCopyMemory(CmdLine,
709 (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
710 CmdLineSize);
711 /* NULL-terminate it */
712 CmdLine[CmdLineSize] = '\0';
713
714 /* Remove any trailing return carriage character and NULL-terminate the command line */
715 CmdLinePtr = CmdLine;
716 while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
717 *CmdLinePtr = '\0';
718
719 /* Create the process */
720 if (!CreateProcessA(ProgramName,
721 CmdLine,
722 NULL,
723 NULL,
724 FALSE,
725 0,
726 Environment,
727 NULL,
728 &StartupInfo,
729 &ProcessInfo))
730 {
731 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
732 return GetLastError();
733 }
734
735 /* Check the type of the program */
736 switch (BinaryType)
737 {
738 /* These are handled by NTVDM */
739 case SCS_DOS_BINARY:
740 case SCS_WOW_BINARY:
741 {
742 /* Clear the structure */
743 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
744
745 /* Initialize the structure members */
746 CommandInfo.TaskId = SessionId;
747 CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
748 CommandInfo.CmdLine = CmdLine;
749 CommandInfo.CmdLen = sizeof(CmdLine);
750 CommandInfo.AppName = AppName;
751 CommandInfo.AppLen = sizeof(AppName);
752 CommandInfo.PifFile = PifFile;
753 CommandInfo.PifLen = sizeof(PifFile);
754 CommandInfo.Desktop = Desktop;
755 CommandInfo.DesktopLen = sizeof(Desktop);
756 CommandInfo.Title = Title;
757 CommandInfo.TitleLen = sizeof(Title);
758 CommandInfo.Env = Env;
759 CommandInfo.EnvLen = EnvSize;
760
761 Command:
762 /* Get the VDM command information */
763 if (!GetNextVDMCommand(&CommandInfo))
764 {
765 if (CommandInfo.EnvLen > EnvSize)
766 {
767 /* Expand the environment size */
768 EnvSize = CommandInfo.EnvLen;
769 CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
770
771 /* Repeat the request */
772 CommandInfo.VDMState |= VDM_FLAG_RETRY;
773 goto Command;
774 }
775
776 /* Shouldn't happen */
777 ASSERT(FALSE);
778 }
779
780 /* Load the executable */
781 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
782 AppName,
783 Parameters,
784 CmdLine,
785 Env,
786 ReturnAddress);
787 if (Result == ERROR_SUCCESS)
788 {
789 /* Increment the re-entry count */
790 CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
791 GetNextVDMCommand(&CommandInfo);
792 }
793 else
794 {
795 DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
796 }
797
798 break;
799 }
800
801 /* Not handled by NTVDM */
802 default:
803 {
804 /* Wait for the process to finish executing */
805 WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
806 }
807 }
808
809 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
810
811 /* Close the handles */
812 CloseHandle(ProcessInfo.hProcess);
813 CloseHandle(ProcessInfo.hThread);
814
815 return ERROR_SUCCESS;
816 }
817 #endif
818
819 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
820 {
821 WORD i;
822 WORD McbSegment = FIRST_MCB_SEGMENT;
823 PDOS_MCB CurrentMcb;
824 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
825 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
826 #ifndef STANDALONE
827 VDM_COMMAND_INFO CommandInfo;
828 #endif
829
830 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
831 Psp,
832 ReturnCode,
833 KeepResident);
834
835 /* Check if this PSP is it's own parent */
836 if (PspBlock->ParentPsp == Psp) goto Done;
837
838 if (KeepResident == 0)
839 {
840 for (i = 0; i < PspBlock->HandleTableSize; i++)
841 {
842 /* Close the handle */
843 DosCloseHandle(i);
844 }
845 }
846
847 /* Free the memory used by the process */
848 while (TRUE)
849 {
850 /* Get a pointer to the MCB */
851 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
852
853 /* Make sure the MCB is valid */
854 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
855
856 /* Check if this block was allocated by the process */
857 if (CurrentMcb->OwnerPsp == Psp)
858 {
859 if (KeepResident)
860 {
861 /* Check if this is the PSP block and we should reduce its size */
862 if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
863 {
864 /* Reduce the size of the block */
865 DosResizeMemory(McbSegment + 1, KeepResident, NULL);
866 break;
867 }
868 }
869 else
870 {
871 /* Free this entire block */
872 DosFreeMemory(McbSegment + 1);
873 }
874 }
875
876 /* If this was the last block, quit */
877 if (CurrentMcb->BlockType == 'Z') break;
878
879 /* Update the segment and continue */
880 McbSegment += CurrentMcb->Size + 1;
881 }
882
883 Done:
884 /* Restore the interrupt vectors */
885 IntVecTable[0x22] = PspBlock->TerminateAddress;
886 IntVecTable[0x23] = PspBlock->BreakAddress;
887 IntVecTable[0x24] = PspBlock->CriticalAddress;
888
889 /* Update the current PSP */
890 if (Psp == CurrentPsp)
891 {
892 CurrentPsp = PspBlock->ParentPsp;
893 if (CurrentPsp == SYSTEM_PSP)
894 {
895 ResetEvent(VdmTaskEvent);
896 CpuUnsimulate();
897 return;
898 }
899 }
900
901 #ifndef STANDALONE
902
903 /* Decrement the re-entry count */
904 CommandInfo.TaskId = SessionId;
905 CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
906 GetNextVDMCommand(&CommandInfo);
907
908 /* Clear the structure */
909 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
910
911 /* Update the VDM state of the task */
912 CommandInfo.TaskId = SessionId;
913 CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
914 GetNextVDMCommand(&CommandInfo);
915
916 #endif
917
918 /* Save the return code - Normal termination */
919 DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
920
921 /* Restore the old stack */
922 setSS(HIWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
923 setSP(LOWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
924
925 /* Restore the program state */
926 DosRestoreState();
927
928 /* Return control to the parent process */
929 CpuExecute(HIWORD(PspBlock->TerminateAddress),
930 LOWORD(PspBlock->TerminateAddress));
931 }
932