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