46f644d000f0355836c852b25bb6b869610f8de4
[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 - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
413
414 if (LoadType != DOS_LOAD_OVERLAY)
415 {
416 DWORD TotalSize = BaseSize;
417
418 /* Add the PSP size, in paragraphs */
419 TotalSize += sizeof(DOS_PSP) >> 4;
420
421 /* Add the maximum size that should be allocated */
422 TotalSize += Header->e_maxalloc;
423
424 if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
425 {
426 /* This program should be loaded high */
427 LoadHigh = TRUE;
428 TotalSize = 0xFFFF;
429 }
430
431 /* Make sure it does not pass 0xFFFF */
432 if (TotalSize > 0xFFFF) TotalSize = 0xFFFF;
433
434 /* Try to allocate that much memory */
435 Segment = DosAllocateMemory((WORD)TotalSize, &MaxAllocSize);
436
437 if (Segment == 0)
438 {
439 /* Check if there's at least enough memory for the minimum size */
440 if (MaxAllocSize < (BaseSize + (sizeof(DOS_PSP) >> 4) + Header->e_minalloc))
441 {
442 Result = DosLastError;
443 goto Cleanup;
444 }
445
446 /* Allocate that minimum amount */
447 TotalSize = MaxAllocSize;
448 Segment = DosAllocateMemory((WORD)TotalSize, NULL);
449 ASSERT(Segment != 0);
450 }
451
452 /* The process owns its own memory */
453 DosChangeMemoryOwner(Segment, Segment);
454 DosChangeMemoryOwner(EnvBlock, Segment);
455
456 /* Set INT 22h to the return address */
457 ((PULONG)BaseAddress)[0x22] = ReturnAddress;
458
459 /* Create the PSP */
460 DosCreatePsp(Segment, (WORD)TotalSize);
461 DosSetPspCommandLine(Segment, CommandLine);
462 SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
463
464 /* Calculate the segment where the program should be loaded */
465 if (!LoadHigh) LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
466 else LoadSegment = Segment + TotalSize - BaseSize;
467
468 RelocFactor = LoadSegment;
469 }
470 else
471 {
472 ASSERT(Parameters);
473 LoadSegment = Parameters->Overlay.Segment;
474 RelocFactor = Parameters->Overlay.RelocationFactor;
475 }
476
477 /* Copy the program to the code segment */
478 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
479 Address + (Header->e_cparhdr << 4),
480 min(FileSize - (Header->e_cparhdr << 4), BaseSize << 4));
481
482 /* Get the relocation table */
483 RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
484
485 /* Perform relocations */
486 for (i = 0; i < Header->e_crlc; i++)
487 {
488 /* Get a pointer to the word that needs to be patched */
489 RelocWord = (PWORD)SEG_OFF_TO_PTR(LoadSegment + HIWORD(RelocationTable[i]),
490 LOWORD(RelocationTable[i]));
491
492 /* Add the relocation factor to it */
493 *RelocWord += RelocFactor;
494 }
495
496 if (LoadType == DOS_LOAD_AND_EXECUTE)
497 {
498 /* Save the program state */
499 if (CurrentPsp != SYSTEM_PSP) DosSaveState();
500
501 /* Set the initial segment registers */
502 setDS(Segment);
503 setES(Segment);
504
505 /* Set the stack to the location from the header */
506 setSS(LoadSegment + Header->e_ss);
507 setSP(Header->e_sp);
508
509 /* Execute */
510 DosSetProcessContext(Segment);
511 CpuExecute(LoadSegment + Header->e_cs, Header->e_ip);
512 }
513 else if (LoadType == DOS_LOAD_ONLY)
514 {
515 ASSERT(Parameters);
516 Parameters->StackLocation = MAKELONG(Header->e_sp, LoadSegment + Header->e_ss);
517 Parameters->EntryPoint = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
518 }
519 }
520 else
521 {
522 /* COM file */
523
524 if (LoadType != DOS_LOAD_OVERLAY)
525 {
526 /* Find the maximum amount of memory that can be allocated */
527 DosAllocateMemory(0xFFFF, &MaxAllocSize);
528
529 /* Make sure it's enough for the whole program and the PSP */
530 if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
531 {
532 Result = ERROR_NOT_ENOUGH_MEMORY;
533 goto Cleanup;
534 }
535
536 /* Allocate all of it */
537 Segment = DosAllocateMemory(MaxAllocSize, NULL);
538 if (Segment == 0)
539 {
540 Result = DosLastError;
541 goto Cleanup;
542 }
543
544 /* The process owns its own memory */
545 DosChangeMemoryOwner(Segment, Segment);
546 DosChangeMemoryOwner(EnvBlock, Segment);
547
548 /* Set INT 22h to the return address */
549 ((PULONG)BaseAddress)[0x22] = ReturnAddress;
550
551 /* Create the PSP */
552 DosCreatePsp(Segment, MaxAllocSize);
553 DosSetPspCommandLine(Segment, CommandLine);
554 SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
555
556 /* Calculate the segment where the program should be loaded */
557 LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
558 }
559 else
560 {
561 ASSERT(Parameters);
562 LoadSegment = Parameters->Overlay.Segment;
563 }
564
565 /* Copy the program to the code segment */
566 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
567 Address,
568 FileSize);
569
570 if (LoadType == DOS_LOAD_AND_EXECUTE)
571 {
572 /* Set the initial segment registers */
573 setDS(Segment);
574 setES(Segment);
575
576 /* Set the stack to the last word of the segment */
577 setSS(Segment);
578 setSP(0xFFFE);
579
580 /*
581 * Set the value on the stack to 0, so that a near return
582 * jumps to PSP:0000 which has the exit code.
583 */
584 *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
585
586 /* Execute */
587 DosSetProcessContext(Segment);
588 CpuExecute(Segment, 0x100);
589 }
590 else if (LoadType == DOS_LOAD_ONLY)
591 {
592 ASSERT(Parameters);
593 Parameters->StackLocation = MAKELONG(0xFFFE, Segment);
594 Parameters->EntryPoint = MAKELONG(0x0100, Segment);
595 }
596 }
597
598 Cleanup:
599 if (Result != ERROR_SUCCESS)
600 {
601 /* It was not successful, cleanup the DOS memory */
602 if (EnvBlock) DosFreeMemory(EnvBlock);
603 if (Segment) DosFreeMemory(Segment);
604 }
605
606 /* Unmap the file*/
607 if (Address != NULL) UnmapViewOfFile(Address);
608
609 /* Close the file mapping object */
610 if (FileMapping != NULL) CloseHandle(FileMapping);
611
612 /* Close the file handle */
613 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
614
615 return Result;
616 }
617
618 DWORD DosStartProcess(IN LPCSTR ExecutablePath,
619 IN LPCSTR CommandLine,
620 IN LPCSTR Environment OPTIONAL)
621 {
622 DWORD Result;
623 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
624
625 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
626 ExecutablePath,
627 NULL,
628 CommandLine,
629 Environment,
630 IntVecTable[0x20]);
631
632 if (Result != ERROR_SUCCESS) goto Quit;
633
634 /* Attach to the console */
635 ConsoleAttach();
636 VidBiosAttachToConsole();
637
638 // HACK: Simulate a ENTER key release scancode on the PS/2 port because
639 // some apps expect to read a key release scancode (> 0x80) when they
640 // are started.
641 // (hbelusca 2 May 2015: I'm not sure it's really useful. See r65012)
642 // IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
643 // IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
644
645 /* Start simulation */
646 SetEvent(VdmTaskEvent);
647 CpuSimulate();
648
649 /* Detach from the console */
650 VidBiosDetachFromConsole();
651 ConsoleDetach();
652
653 Quit:
654 return Result;
655 }
656
657 #ifndef STANDALONE
658 WORD DosCreateProcess(LPCSTR ProgramName,
659 PDOS_EXEC_PARAM_BLOCK Parameters,
660 DWORD ReturnAddress)
661 {
662 DWORD Result;
663 DWORD BinaryType;
664 LPVOID Environment = NULL;
665 VDM_COMMAND_INFO CommandInfo;
666 CHAR CmdLine[MAX_PATH]; // DOS_CMDLINE_LENGTH + 1
667 CHAR AppName[MAX_PATH];
668 CHAR PifFile[MAX_PATH];
669 CHAR Desktop[MAX_PATH];
670 CHAR Title[MAX_PATH];
671 LPSTR CmdLinePtr;
672 ULONG CmdLineSize;
673 ULONG EnvSize = 256;
674 PVOID Env;
675 STARTUPINFOA StartupInfo;
676 PROCESS_INFORMATION ProcessInfo;
677
678 /* Get the binary type */
679 if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
680
681 /* Initialize Win32-VDM environment */
682 Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
683 if (Env == NULL) return GetLastError();
684
685 /* Did the caller specify an environment segment? */
686 if (Parameters->Environment)
687 {
688 /* Yes, use it instead of the parent one */
689 Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
690 }
691
692 /* Set up the startup info structure */
693 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
694 StartupInfo.cb = sizeof(StartupInfo);
695
696 /*
697 * Convert the DOS command line to Win32-compatible format.
698 * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
699 */
700 CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
701 RtlCopyMemory(CmdLine,
702 (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
703 CmdLineSize);
704 /* NULL-terminate it */
705 CmdLine[CmdLineSize] = '\0';
706
707 /* Remove any trailing return carriage character and NULL-terminate the command line */
708 CmdLinePtr = CmdLine;
709 while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
710 *CmdLinePtr = '\0';
711
712 /* Create the process */
713 if (!CreateProcessA(ProgramName,
714 CmdLine,
715 NULL,
716 NULL,
717 FALSE,
718 0,
719 Environment,
720 NULL,
721 &StartupInfo,
722 &ProcessInfo))
723 {
724 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
725 return GetLastError();
726 }
727
728 /* Check the type of the program */
729 switch (BinaryType)
730 {
731 /* These are handled by NTVDM */
732 case SCS_DOS_BINARY:
733 case SCS_WOW_BINARY:
734 {
735 /* Clear the structure */
736 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
737
738 /* Initialize the structure members */
739 CommandInfo.TaskId = SessionId;
740 CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
741 CommandInfo.CmdLine = CmdLine;
742 CommandInfo.CmdLen = sizeof(CmdLine);
743 CommandInfo.AppName = AppName;
744 CommandInfo.AppLen = sizeof(AppName);
745 CommandInfo.PifFile = PifFile;
746 CommandInfo.PifLen = sizeof(PifFile);
747 CommandInfo.Desktop = Desktop;
748 CommandInfo.DesktopLen = sizeof(Desktop);
749 CommandInfo.Title = Title;
750 CommandInfo.TitleLen = sizeof(Title);
751 CommandInfo.Env = Env;
752 CommandInfo.EnvLen = EnvSize;
753
754 Command:
755 /* Get the VDM command information */
756 if (!GetNextVDMCommand(&CommandInfo))
757 {
758 if (CommandInfo.EnvLen > EnvSize)
759 {
760 /* Expand the environment size */
761 EnvSize = CommandInfo.EnvLen;
762 CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
763
764 /* Repeat the request */
765 CommandInfo.VDMState |= VDM_FLAG_RETRY;
766 goto Command;
767 }
768
769 /* Shouldn't happen */
770 ASSERT(FALSE);
771 }
772
773 /* Load the executable */
774 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
775 AppName,
776 Parameters,
777 CmdLine,
778 Env,
779 ReturnAddress);
780 if (Result == ERROR_SUCCESS)
781 {
782 /* Increment the re-entry count */
783 CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
784 GetNextVDMCommand(&CommandInfo);
785 }
786 else
787 {
788 DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
789 }
790
791 break;
792 }
793
794 /* Not handled by NTVDM */
795 default:
796 {
797 /* Wait for the process to finish executing */
798 WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
799 }
800 }
801
802 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
803
804 /* Close the handles */
805 CloseHandle(ProcessInfo.hProcess);
806 CloseHandle(ProcessInfo.hThread);
807
808 return ERROR_SUCCESS;
809 }
810 #endif
811
812 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
813 {
814 WORD i;
815 WORD McbSegment = FIRST_MCB_SEGMENT;
816 PDOS_MCB CurrentMcb;
817 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
818 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
819 #ifndef STANDALONE
820 VDM_COMMAND_INFO CommandInfo;
821 #endif
822
823 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
824 Psp,
825 ReturnCode,
826 KeepResident);
827
828 /* Check if this PSP is it's own parent */
829 if (PspBlock->ParentPsp == Psp) goto Done;
830
831 if (KeepResident == 0)
832 {
833 for (i = 0; i < PspBlock->HandleTableSize; i++)
834 {
835 /* Close the handle */
836 DosCloseHandle(i);
837 }
838 }
839
840 /* Free the memory used by the process */
841 while (TRUE)
842 {
843 /* Get a pointer to the MCB */
844 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
845
846 /* Make sure the MCB is valid */
847 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
848
849 /* Check if this block was allocated by the process */
850 if (CurrentMcb->OwnerPsp == Psp)
851 {
852 if (KeepResident)
853 {
854 /* Check if this is the PSP block and we should reduce its size */
855 if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
856 {
857 /* Reduce the size of the block */
858 DosResizeMemory(McbSegment + 1, KeepResident, NULL);
859 break;
860 }
861 }
862 else
863 {
864 /* Free this entire block */
865 DosFreeMemory(McbSegment + 1);
866 }
867 }
868
869 /* If this was the last block, quit */
870 if (CurrentMcb->BlockType == 'Z') break;
871
872 /* Update the segment and continue */
873 McbSegment += CurrentMcb->Size + 1;
874 }
875
876 Done:
877 /* Restore the interrupt vectors */
878 IntVecTable[0x22] = PspBlock->TerminateAddress;
879 IntVecTable[0x23] = PspBlock->BreakAddress;
880 IntVecTable[0x24] = PspBlock->CriticalAddress;
881
882 /* Update the current PSP */
883 if (Psp == CurrentPsp)
884 {
885 CurrentPsp = PspBlock->ParentPsp;
886 if (CurrentPsp == SYSTEM_PSP)
887 {
888 ResetEvent(VdmTaskEvent);
889 CpuUnsimulate();
890 return;
891 }
892 }
893
894 #ifndef STANDALONE
895
896 /* Decrement the re-entry count */
897 CommandInfo.TaskId = SessionId;
898 CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
899 GetNextVDMCommand(&CommandInfo);
900
901 /* Clear the structure */
902 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
903
904 /* Update the VDM state of the task */
905 CommandInfo.TaskId = SessionId;
906 CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
907 GetNextVDMCommand(&CommandInfo);
908
909 #endif
910
911 /* Save the return code - Normal termination */
912 DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
913
914 /* Restore the old stack */
915 setSS(HIWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
916 setSP(LOWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
917
918 /* Restore the program state */
919 DosRestoreState();
920
921 /* Return control to the parent process */
922 CpuExecute(HIWORD(PspBlock->TerminateAddress),
923 LOWORD(PspBlock->TerminateAddress));
924 }
925