6f430eab278a7583598bfc11d2cc30a0fb1d91eb
[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 /* Attach to the console */
645 ConsoleAttach();
646 VidBiosAttachToConsole();
647
648 // HACK: Simulate a ENTER key release scancode on the PS/2 port because
649 // some apps expect to read a key release scancode (> 0x80) when they
650 // are started.
651 // (hbelusca 2 May 2015: I'm not sure it's really useful. See r65012)
652 // IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
653 // IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
654
655 /* Start simulation */
656 SetEvent(VdmTaskEvent);
657 CpuSimulate();
658
659 /* Detach from the console */
660 VidBiosDetachFromConsole();
661 ConsoleDetach();
662
663 Quit:
664 return Result;
665 }
666
667 #ifndef STANDALONE
668 WORD DosCreateProcess(LPCSTR ProgramName,
669 PDOS_EXEC_PARAM_BLOCK Parameters,
670 DWORD ReturnAddress)
671 {
672 DWORD Result;
673 DWORD BinaryType;
674 LPVOID Environment = NULL;
675 VDM_COMMAND_INFO CommandInfo;
676 CHAR CmdLine[MAX_PATH]; // DOS_CMDLINE_LENGTH + 1
677 CHAR AppName[MAX_PATH];
678 CHAR PifFile[MAX_PATH];
679 CHAR Desktop[MAX_PATH];
680 CHAR Title[MAX_PATH];
681 LPSTR CmdLinePtr;
682 ULONG CmdLineSize;
683 ULONG EnvSize = 256;
684 PVOID Env;
685 STARTUPINFOA StartupInfo;
686 PROCESS_INFORMATION ProcessInfo;
687
688 /* Get the binary type */
689 if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
690
691 /* Initialize Win32-VDM environment */
692 Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
693 if (Env == NULL) return GetLastError();
694
695 /* Did the caller specify an environment segment? */
696 if (Parameters->Environment)
697 {
698 /* Yes, use it instead of the parent one */
699 Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
700 }
701
702 /* Set up the startup info structure */
703 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
704 StartupInfo.cb = sizeof(StartupInfo);
705
706 /*
707 * Convert the DOS command line to Win32-compatible format.
708 * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
709 */
710 CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
711 RtlCopyMemory(CmdLine,
712 (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
713 CmdLineSize);
714 /* NULL-terminate it */
715 CmdLine[CmdLineSize] = '\0';
716
717 /* Remove any trailing return carriage character and NULL-terminate the command line */
718 CmdLinePtr = CmdLine;
719 while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
720 *CmdLinePtr = '\0';
721
722 /* Create the process */
723 if (!CreateProcessA(ProgramName,
724 CmdLine,
725 NULL,
726 NULL,
727 FALSE,
728 0,
729 Environment,
730 NULL,
731 &StartupInfo,
732 &ProcessInfo))
733 {
734 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
735 return GetLastError();
736 }
737
738 /* Check the type of the program */
739 switch (BinaryType)
740 {
741 /* These are handled by NTVDM */
742 case SCS_DOS_BINARY:
743 case SCS_WOW_BINARY:
744 {
745 /* Clear the structure */
746 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
747
748 /* Initialize the structure members */
749 CommandInfo.TaskId = SessionId;
750 CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
751 CommandInfo.CmdLine = CmdLine;
752 CommandInfo.CmdLen = sizeof(CmdLine);
753 CommandInfo.AppName = AppName;
754 CommandInfo.AppLen = sizeof(AppName);
755 CommandInfo.PifFile = PifFile;
756 CommandInfo.PifLen = sizeof(PifFile);
757 CommandInfo.Desktop = Desktop;
758 CommandInfo.DesktopLen = sizeof(Desktop);
759 CommandInfo.Title = Title;
760 CommandInfo.TitleLen = sizeof(Title);
761 CommandInfo.Env = Env;
762 CommandInfo.EnvLen = EnvSize;
763
764 Command:
765 /* Get the VDM command information */
766 if (!GetNextVDMCommand(&CommandInfo))
767 {
768 if (CommandInfo.EnvLen > EnvSize)
769 {
770 /* Expand the environment size */
771 EnvSize = CommandInfo.EnvLen;
772 CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
773
774 /* Repeat the request */
775 CommandInfo.VDMState |= VDM_FLAG_RETRY;
776 goto Command;
777 }
778
779 /* Shouldn't happen */
780 ASSERT(FALSE);
781 }
782
783 /* Load the executable */
784 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
785 AppName,
786 Parameters,
787 CmdLine,
788 Env,
789 ReturnAddress);
790 if (Result == ERROR_SUCCESS)
791 {
792 /* Increment the re-entry count */
793 CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
794 GetNextVDMCommand(&CommandInfo);
795 }
796 else
797 {
798 DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
799 }
800
801 break;
802 }
803
804 /* Not handled by NTVDM */
805 default:
806 {
807 /* Wait for the process to finish executing */
808 WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
809 }
810 }
811
812 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
813
814 /* Close the handles */
815 CloseHandle(ProcessInfo.hProcess);
816 CloseHandle(ProcessInfo.hThread);
817
818 return ERROR_SUCCESS;
819 }
820 #endif
821
822 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
823 {
824 WORD i;
825 WORD McbSegment = FIRST_MCB_SEGMENT;
826 PDOS_MCB CurrentMcb;
827 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
828 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
829 #ifndef STANDALONE
830 VDM_COMMAND_INFO CommandInfo;
831 #endif
832
833 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
834 Psp,
835 ReturnCode,
836 KeepResident);
837
838 /* Check if this PSP is it's own parent */
839 if (PspBlock->ParentPsp == Psp) goto Done;
840
841 if (KeepResident == 0)
842 {
843 for (i = 0; i < PspBlock->HandleTableSize; i++)
844 {
845 /* Close the handle */
846 DosCloseHandle(i);
847 }
848 }
849
850 /* Free the memory used by the process */
851 while (TRUE)
852 {
853 /* Get a pointer to the MCB */
854 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
855
856 /* Make sure the MCB is valid */
857 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
858
859 /* Check if this block was allocated by the process */
860 if (CurrentMcb->OwnerPsp == Psp)
861 {
862 if (KeepResident)
863 {
864 /* Check if this is the PSP block and we should reduce its size */
865 if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
866 {
867 /* Reduce the size of the block */
868 DosResizeMemory(McbSegment + 1, KeepResident, NULL);
869 break;
870 }
871 }
872 else
873 {
874 /* Free this entire block */
875 DosFreeMemory(McbSegment + 1);
876 }
877 }
878
879 /* If this was the last block, quit */
880 if (CurrentMcb->BlockType == 'Z') break;
881
882 /* Update the segment and continue */
883 McbSegment += CurrentMcb->Size + 1;
884 }
885
886 Done:
887 /* Restore the interrupt vectors */
888 IntVecTable[0x22] = PspBlock->TerminateAddress;
889 IntVecTable[0x23] = PspBlock->BreakAddress;
890 IntVecTable[0x24] = PspBlock->CriticalAddress;
891
892 /* Update the current PSP */
893 if (Psp == CurrentPsp)
894 {
895 CurrentPsp = PspBlock->ParentPsp;
896 if (CurrentPsp == SYSTEM_PSP)
897 {
898 ResetEvent(VdmTaskEvent);
899 CpuUnsimulate();
900 return;
901 }
902 }
903
904 #ifndef STANDALONE
905
906 /* Decrement the re-entry count */
907 CommandInfo.TaskId = SessionId;
908 CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
909 GetNextVDMCommand(&CommandInfo);
910
911 /* Clear the structure */
912 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
913
914 /* Update the VDM state of the task */
915 CommandInfo.TaskId = SessionId;
916 CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
917 GetNextVDMCommand(&CommandInfo);
918
919 #endif
920
921 /* Save the return code - Normal termination */
922 DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
923
924 /* Restore the old stack */
925 setSS(HIWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
926 setSP(LOWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
927
928 /* Restore the program state */
929 DosRestoreState();
930
931 /* Return control to the parent process */
932 CpuExecute(HIWORD(PspBlock->TerminateAddress),
933 LOWORD(PspBlock->TerminateAddress));
934 }
935