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