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