1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/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 #include "ntvdm.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #include "emulator.h"
18 #include "cpu/cpu.h"
19
20 #include "dos.h"
21 #include "dos/dem.h"
22 #include "dosfiles.h"
23 #include "handle.h"
24 #include "process.h"
25 #include "memory.h"
26
27 #include "bios/bios.h"
28
29 #include "io.h"
30 #include "hardware/ps2.h"
31
32 #include "vddsup.h"
33
34 /* PRIVATE FUNCTIONS **********************************************************/
35
DosInitPsp(IN WORD Segment,IN WORD EnvBlock,IN LPCSTR CommandLine,IN LPCSTR ProgramName)36 static VOID DosInitPsp(IN WORD Segment,
37 IN WORD EnvBlock,
38 IN LPCSTR CommandLine,
39 IN LPCSTR ProgramName)
40 {
41 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
42 PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1);
43 LPCSTR PspName;
44 USHORT i;
45
46 /* Link the environment block */
47 PspBlock->EnvBlock = EnvBlock;
48
49 /*
50 * Copy the command line.
51 * Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
52 */
53 PspBlock->CommandLineSize = min(*(PBYTE)CommandLine, DOS_CMDLINE_LENGTH);
54 CommandLine++;
55 RtlCopyMemory(PspBlock->CommandLine, CommandLine, DOS_CMDLINE_LENGTH);
56
57 /*
58 * Initialize the owner name of the MCB of the PSP.
59 */
60
61 /* Find the start of the file name, skipping all the path elements */
62 PspName = ProgramName;
63 while (*ProgramName)
64 {
65 switch (*ProgramName++)
66 {
67 /* Path delimiter, skip it */
68 case ':': case '\\': case '/':
69 PspName = ProgramName;
70 break;
71 }
72 }
73 /* Copy the file name up to the extension... */
74 for (i = 0; i < sizeof(Mcb->Name) && PspName[i] != '.' && PspName[i] != '\0'; ++i)
75 {
76 Mcb->Name[i] = RtlUpperChar(PspName[i]);
77 }
78 /* ... and NULL-terminate if needed */
79 if (i < sizeof(Mcb->Name)) Mcb->Name[i] = '\0';
80
81 // FIXME: Initialize the FCBs
82 }
83
DosSaveState(VOID)84 static inline VOID DosSaveState(VOID)
85 {
86 PDOS_REGISTER_STATE State;
87 WORD StackPointer = getSP();
88
89 #ifdef ADVANCED_DEBUGGING
90 DPRINT1("\n"
91 "DosSaveState(before) -- SS:SP == %04X:%04X\n"
92 "Original CPU State =\n"
93 "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
94 "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
95 "\n",
96 getSS(), getSP(),
97 getDS(), getES(), getAX(), getCX(),
98 getDX(), getBX(), getBP(), getSI(), getDI());
99 #endif
100
101 /*
102 * Allocate stack space for the registers. Note that we
103 * already have one word allocated (the interrupt number).
104 */
105 StackPointer -= sizeof(DOS_REGISTER_STATE) - sizeof(WORD);
106 State = SEG_OFF_TO_PTR(getSS(), StackPointer);
107 setSP(StackPointer);
108
109 /* Save */
110 State->DS = getDS();
111 State->ES = getES();
112 State->AX = getAX();
113 State->CX = getCX();
114 State->DX = getDX();
115 State->BX = getBX();
116 State->BP = getBP();
117 State->SI = getSI();
118 State->DI = getDI();
119
120 #ifdef ADVANCED_DEBUGGING
121 DPRINT1("\n"
122 "DosSaveState(after) -- SS:SP == %04X:%04X\n"
123 "Saved State =\n"
124 "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
125 "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
126 "\n",
127 getSS(), getSP(),
128 State->DS, State->ES, State->AX, State->CX,
129 State->DX, State->BX, State->BP, State->SI, State->DI);
130 #endif
131 }
132
DosRestoreState(VOID)133 static inline VOID DosRestoreState(VOID)
134 {
135 PDOS_REGISTER_STATE State;
136
137 /*
138 * Pop the state structure from the stack. Note that we
139 * already have one word allocated (the interrupt number).
140 */
141 State = SEG_OFF_TO_PTR(getSS(), getSP());
142
143 #ifdef ADVANCED_DEBUGGING
144 DPRINT1("\n"
145 "DosRestoreState(before) -- SS:SP == %04X:%04X\n"
146 "Saved State =\n"
147 "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
148 "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
149 "\n",
150 getSS(), getSP(),
151 State->DS, State->ES, State->AX, State->CX,
152 State->DX, State->BX, State->BP, State->SI, State->DI);
153 #endif
154
155 setSP(getSP() + sizeof(DOS_REGISTER_STATE) - sizeof(WORD));
156
157 /* Restore */
158 setDS(State->DS);
159 setES(State->ES);
160 setAX(State->AX);
161 setCX(State->CX);
162 setDX(State->DX);
163 setBX(State->BX);
164 setBP(State->BP);
165 setSI(State->SI);
166 setDI(State->DI);
167
168 #ifdef ADVANCED_DEBUGGING
169 DPRINT1("\n"
170 "DosRestoreState(after) -- SS:SP == %04X:%04X\n"
171 "Restored CPU State =\n"
172 "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
173 "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
174 "\n",
175 getSS(), getSP(),
176 getDS(), getES(), getAX(), getCX(),
177 getDX(), getBX(), getBP(), getSI(), getDI());
178 #endif
179 }
180
DosCopyEnvironmentBlock(IN LPCSTR Environment OPTIONAL,IN LPCSTR ProgramName)181 static WORD DosCopyEnvironmentBlock(IN LPCSTR Environment OPTIONAL,
182 IN LPCSTR ProgramName)
183 {
184 PCHAR Ptr, DestBuffer = NULL;
185 SIZE_T TotalSize = 0;
186 WORD DestSegment;
187
188 /* If we have an environment strings list, compute its size */
189 if (Environment)
190 {
191 /* Calculate the size of the environment block */
192 Ptr = (PCHAR)Environment;
193 while (*Ptr) Ptr += strlen(Ptr) + 1;
194 TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
195 }
196 else
197 {
198 /* Empty environment string */
199 TotalSize = 1;
200 }
201 /* Add the final environment block NULL-terminator */
202 TotalSize++;
203
204 /* Add the two bytes for the program name tag */
205 TotalSize += 2;
206
207 /* Add the string buffer size */
208 TotalSize += strlen(ProgramName) + 1;
209
210 /* Allocate the memory for the environment block */
211 DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
212 if (!DestSegment) return 0;
213
214 DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
215
216 /* If we have an environment strings list, copy it */
217 if (Environment)
218 {
219 Ptr = (PCHAR)Environment;
220 while (*Ptr)
221 {
222 /* Copy the string and NULL-terminate it */
223 strcpy(DestBuffer, Ptr);
224 DestBuffer += strlen(Ptr);
225 *(DestBuffer++) = '\0';
226
227 /* Move to the next string */
228 Ptr += strlen(Ptr) + 1;
229 }
230 }
231 else
232 {
233 /* Empty environment string */
234 *(DestBuffer++) = '\0';
235 }
236 /* NULL-terminate the environment block */
237 *(DestBuffer++) = '\0';
238
239 /* Store the special program name tag */
240 *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
241 *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
242
243 /* Copy the program name after the environment block */
244 strcpy(DestBuffer, ProgramName);
245
246 return DestSegment;
247 }
248
249 /* PUBLIC FUNCTIONS ***********************************************************/
250
DosClonePsp(WORD DestSegment,WORD SourceSegment)251 VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
252 {
253 PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
254 PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
255 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
256
257 /* Literally copy the PSP first */
258 RtlCopyMemory(DestPsp, SourcePsp, sizeof(*DestPsp));
259
260 /* Save the interrupt vectors */
261 DestPsp->TerminateAddress = IntVecTable[0x22];
262 DestPsp->BreakAddress = IntVecTable[0x23];
263 DestPsp->CriticalAddress = IntVecTable[0x24];
264
265 /* No parent PSP */
266 DestPsp->ParentPsp = 0;
267
268 /* Set the handle table pointers to the internal handle table */
269 DestPsp->HandleTableSize = DEFAULT_JFT_SIZE;
270 DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
271
272 /* Copy the parent handle table without referencing the SFT */
273 RtlCopyMemory(FAR_POINTER(DestPsp->HandleTablePtr),
274 FAR_POINTER(SourcePsp->HandleTablePtr),
275 DEFAULT_JFT_SIZE);
276 }
277
DosCreatePsp(WORD Segment,WORD ProgramSize)278 VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
279 {
280 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
281 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
282
283 RtlZeroMemory(PspBlock, sizeof(*PspBlock));
284
285 /* Set the exit interrupt */
286 PspBlock->Exit[0] = 0xCD; // int 0x20
287 PspBlock->Exit[1] = 0x20;
288
289 /* Set the number of the last paragraph */
290 PspBlock->LastParagraph = Segment + ProgramSize;
291
292 /* Save the interrupt vectors */
293 PspBlock->TerminateAddress = IntVecTable[0x22];
294 PspBlock->BreakAddress = IntVecTable[0x23];
295 PspBlock->CriticalAddress = IntVecTable[0x24];
296
297 /* Set the parent PSP */
298 PspBlock->ParentPsp = Sda->CurrentPsp;
299
300 if (Sda->CurrentPsp != SYSTEM_PSP)
301 {
302 /* Link to the parent's environment block */
303 PspBlock->EnvBlock = SEGMENT_TO_PSP(Sda->CurrentPsp)->EnvBlock;
304 }
305 /*
306 else
307 {
308 PspBlock->EnvBlock = SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0);
309 }
310 */
311
312 /* Copy the parent handle table */
313 DosCopyHandleTable(PspBlock->HandleTable);
314
315 /* Set the handle table pointers to the internal handle table */
316 PspBlock->HandleTableSize = DEFAULT_JFT_SIZE;
317 PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
318
319 /* Set the DOS version */
320 // FIXME: This is here that SETVER stuff enters into action!
321 PspBlock->DosVersion = DosData->DosVersion;
322
323 /* Set the far call opcodes */
324 PspBlock->FarCall[0] = 0xCD; // int 0x21
325 PspBlock->FarCall[1] = 0x21;
326 PspBlock->FarCall[2] = 0xCB; // retf
327 }
328
DosSetProcessContext(WORD Segment)329 VOID DosSetProcessContext(WORD Segment)
330 {
331 Sda->CurrentPsp = Segment;
332 Sda->DiskTransferArea = MAKELONG(0x80, Segment);
333 }
334
DosLoadExecutableInternal(IN DOS_EXEC_TYPE LoadType,IN LPBYTE ExeBuffer,IN DWORD ExeBufferSize,IN LPCSTR ExePath,IN PDOS_EXEC_PARAM_BLOCK Parameters,IN LPCSTR CommandLine OPTIONAL,IN LPCSTR Environment OPTIONAL,IN DWORD ReturnAddress OPTIONAL)335 DWORD DosLoadExecutableInternal(IN DOS_EXEC_TYPE LoadType,
336 IN LPBYTE ExeBuffer,
337 IN DWORD ExeBufferSize,
338 IN LPCSTR ExePath,
339 IN PDOS_EXEC_PARAM_BLOCK Parameters,
340 IN LPCSTR CommandLine OPTIONAL,
341 IN LPCSTR Environment OPTIONAL,
342 IN DWORD ReturnAddress OPTIONAL)
343 {
344 DWORD Result = ERROR_SUCCESS;
345 WORD Segment = 0;
346 WORD EnvBlock = 0;
347 WORD ExeSignature;
348 WORD LoadSegment;
349 WORD MaxAllocSize;
350
351 WORD FinalSS, FinalSP;
352 WORD FinalCS, FinalIP;
353
354 /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
355 CHAR CmdLineBuffer[1 + DOS_CMDLINE_LENGTH];
356
357 DPRINT1("DosLoadExecutableInternal(%d, 0x%p, '%s', 0x%p, 0x%p, 0x%p)\n",
358 LoadType, ExeBuffer, ExePath, Parameters, CommandLine, Environment);
359
360 if (LoadType != DOS_LOAD_OVERLAY)
361 {
362 /* If an optional Win32 command line is given... */
363 if (CommandLine)
364 {
365 /* ... convert it into DOS format */
366 BYTE CmdLineLen;
367
368 PBYTE CmdLineSize = (PBYTE)CmdLineBuffer;
369 LPSTR CmdLineStart = CmdLineBuffer + 1;
370 LPSTR CmdLinePtr = CmdLineStart;
371
372 // For debugging purposes
373 RtlFillMemory(CmdLineBuffer, sizeof(CmdLineBuffer), 0xFF);
374
375 /*
376 * Set the command line: it is either an empty command line or has
377 * the format: " foo bar ..." (with at least one leading whitespace),
378 * and is then always followed by '\r' (and optionally by '\n').
379 */
380 CmdLineLen = (BYTE)strlen(CommandLine);
381 *CmdLineSize = 0;
382
383 /*
384 * Add the leading space if the command line is not empty
385 * and doesn't already start with some whitespace...
386 */
387 if (*CommandLine && *CommandLine != '\r' && *CommandLine != '\n' &&
388 *CommandLine != ' ' && *CommandLine != '\t')
389 {
390 (*CmdLineSize)++;
391 *CmdLinePtr++ = ' ';
392 }
393
394 /* Compute the number of characters we need to copy from the original command line */
395 CmdLineLen = min(CmdLineLen, DOS_CMDLINE_LENGTH - *CmdLineSize);
396
397 /* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
398 while (CmdLineLen && (CommandLine[CmdLineLen - 1] == '\r' || CommandLine[CmdLineLen - 1] == '\n'))
399 {
400 CmdLineLen--;
401 }
402
403 /* Finally, set everything up */
404 *CmdLineSize += CmdLineLen;
405 RtlCopyMemory(CmdLinePtr, CommandLine, CmdLineLen);
406 CmdLineStart[*CmdLineSize] = '\r';
407
408 /* Finally make the pointer point to the static buffer */
409 CommandLine = CmdLineBuffer;
410 }
411 else
412 {
413 /*
414 * ... otherwise, get the one from the parameter block.
415 * Format of the command line: 1 byte for size; 127 bytes for contents.
416 */
417 ASSERT(Parameters);
418 CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
419 }
420
421 /* If no optional environment is given... */
422 if (Environment == NULL)
423 {
424 ASSERT(Parameters);
425 /* ... get the one from the parameter block (if not NULL)... */
426 if (Parameters->Environment)
427 Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
428 /* ... or the one from the parent (otherwise) */
429 else
430 Environment = (LPCSTR)SEG_OFF_TO_PTR(SEGMENT_TO_PSP(Sda->CurrentPsp)->EnvBlock, 0);
431 }
432
433 /* Copy the environment block to DOS memory */
434 EnvBlock = DosCopyEnvironmentBlock(Environment, ExePath);
435 if (EnvBlock == 0)
436 {
437 Result = Sda->LastErrorCode;
438 goto Cleanup;
439 }
440 }
441
442 /*
443 * Check if this is an EXE file or a COM file by looking
444 * at the MZ signature:
445 * 0x4D5A 'MZ': old signature (stored as 0x5A, 0x4D)
446 * 0x5A4D 'ZM': new signature (stored as 0x4D, 0x5A)
447 */
448 ExeSignature = *(PWORD)ExeBuffer;
449 if (ExeSignature == 'MZ' || ExeSignature == 'ZM')
450 {
451 /* EXE file */
452 PIMAGE_DOS_HEADER Header;
453 DWORD BaseSize;
454 PDWORD RelocationTable;
455 PWORD RelocWord;
456 WORD RelocFactor;
457 WORD i;
458
459 /* Get the MZ header */
460 Header = (PIMAGE_DOS_HEADER)ExeBuffer;
461
462 /* Get the base size of the file, in paragraphs (rounded up) */
463 #if 0 // Normally this is not needed to check for the number of bytes in the last pages.
464 BaseSize = ((((Header->e_cp - (Header->e_cblp != 0)) * 512) + Header->e_cblp) >> 4)
465 - Header->e_cparhdr;
466 #else
467 // e_cp is the number of 512-byte blocks. 512 == (1 << 9)
468 // so this corresponds to (1 << 5) number of paragraphs.
469 //
470 // For DOS compatibility we need to truncate BaseSize to a WORD value.
471 // This fact is exploited by some EXEs which are bigger than 1 Mb while
472 // being able to load on DOS, the main EXE code loads the remaining data.
473
474 BaseSize = ((Header->e_cp << 5) - Header->e_cparhdr) & 0xFFFF;
475 #endif
476
477 if (LoadType != DOS_LOAD_OVERLAY)
478 {
479 BOOLEAN LoadHigh = FALSE;
480 DWORD TotalSize;
481
482 /* Find the maximum amount of memory that can be allocated */
483 DosAllocateMemory(0xFFFF, &MaxAllocSize);
484
485 /* Compute the total needed size, in paragraphs */
486 TotalSize = BaseSize + (sizeof(DOS_PSP) >> 4);
487
488 /* We must have the required minimum amount of memory. If not, bail out. */
489 if (MaxAllocSize < TotalSize + Header->e_minalloc)
490 {
491 Result = ERROR_NOT_ENOUGH_MEMORY;
492 goto Cleanup;
493 }
494
495 /* Check if the program should be loaded high */
496 if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
497 {
498 /* Yes it should. Use all the available memory. */
499 LoadHigh = TRUE;
500 TotalSize = MaxAllocSize;
501 }
502 else
503 {
504 /* Compute the maximum memory size that can be allocated */
505 if (Header->e_maxalloc != 0)
506 TotalSize = min(TotalSize + Header->e_maxalloc, MaxAllocSize);
507 else
508 TotalSize = MaxAllocSize; // Use all the available memory
509 }
510
511 /* Try to allocate that much memory */
512 Segment = DosAllocateMemory((WORD)TotalSize, NULL);
513 if (Segment == 0)
514 {
515 Result = Sda->LastErrorCode;
516 goto Cleanup;
517 }
518
519 /* The process owns its memory */
520 DosChangeMemoryOwner(Segment , Segment);
521 DosChangeMemoryOwner(EnvBlock, Segment);
522
523 /* Set INT 22h to the return address */
524 ((PULONG)BaseAddress)[0x22] = ReturnAddress;
525
526 /* Create the PSP and initialize it */
527 DosCreatePsp(Segment, (WORD)TotalSize);
528 DosInitPsp(Segment, EnvBlock, CommandLine, ExePath);
529
530 /* Calculate the segment where the program should be loaded */
531 if (!LoadHigh)
532 LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
533 else
534 LoadSegment = Segment + TotalSize - BaseSize;
535
536 RelocFactor = LoadSegment;
537 }
538 else
539 {
540 ASSERT(Parameters);
541 LoadSegment = Parameters->Overlay.Segment;
542 RelocFactor = Parameters->Overlay.RelocationFactor;
543 }
544
545 /* Copy the program to the code segment */
546 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
547 ExeBuffer + (Header->e_cparhdr << 4),
548 min(ExeBufferSize - (Header->e_cparhdr << 4), BaseSize << 4));
549
550 /* Get the relocation table */
551 RelocationTable = (PDWORD)(ExeBuffer + Header->e_lfarlc);
552
553 /* Perform relocations */
554 for (i = 0; i < Header->e_crlc; i++)
555 {
556 /* Get a pointer to the word that needs to be patched */
557 RelocWord = (PWORD)SEG_OFF_TO_PTR(LoadSegment + HIWORD(RelocationTable[i]),
558 LOWORD(RelocationTable[i]));
559
560 /* Add the relocation factor to it */
561 *RelocWord += RelocFactor;
562 }
563
564 /* Set the stack to the location from the header */
565 FinalSS = LoadSegment + Header->e_ss;
566 FinalSP = Header->e_sp;
567
568 /* Set the code segment/pointer */
569 FinalCS = LoadSegment + Header->e_cs;
570 FinalIP = Header->e_ip;
571 }
572 else
573 {
574 /* COM file */
575
576 if (LoadType != DOS_LOAD_OVERLAY)
577 {
578 /* Find the maximum amount of memory that can be allocated */
579 DosAllocateMemory(0xFFFF, &MaxAllocSize);
580
581 /* Make sure it's enough for the whole program and the PSP */
582 if (((DWORD)MaxAllocSize << 4) < (ExeBufferSize + sizeof(DOS_PSP)))
583 {
584 Result = ERROR_NOT_ENOUGH_MEMORY;
585 goto Cleanup;
586 }
587
588 /* Allocate all of it */
589 Segment = DosAllocateMemory(MaxAllocSize, NULL);
590 if (Segment == 0)
591 {
592 Result = Sda->LastErrorCode;
593 goto Cleanup;
594 }
595
596 /* The process owns its memory */
597 DosChangeMemoryOwner(Segment , Segment);
598 DosChangeMemoryOwner(EnvBlock, Segment);
599
600 /* Set INT 22h to the return address */
601 ((PULONG)BaseAddress)[0x22] = ReturnAddress;
602
603 /* Create the PSP and initialize it */
604 DosCreatePsp(Segment, MaxAllocSize);
605 DosInitPsp(Segment, EnvBlock, CommandLine, ExePath);
606
607 /* Calculate the segment where the program should be loaded */
608 LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
609 }
610 else
611 {
612 ASSERT(Parameters);
613 LoadSegment = Parameters->Overlay.Segment;
614 }
615
616 /* Copy the program to the code segment */
617 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
618 ExeBuffer, ExeBufferSize);
619
620 /* Set the stack to the last word of the segment */
621 FinalSS = Segment;
622 FinalSP = 0xFFFE;
623
624 /*
625 * Set the value on the stack to 0x0000, so that a near return
626 * jumps to PSP:0000 which has the exit code.
627 */
628 *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0x0000;
629
630 /* Set the code segment/pointer */
631 FinalCS = Segment;
632 FinalIP = 0x0100;
633 }
634
635 if (LoadType == DOS_LOAD_AND_EXECUTE)
636 {
637 /* Save the program state */
638 if (Sda->CurrentPsp != SYSTEM_PSP)
639 {
640 /* Push the task state */
641 DosSaveState();
642
643 #ifdef ADVANCED_DEBUGGING
644 DPRINT1("Sda->CurrentPsp = 0x%04x; Old LastStack = 0x%08x, New LastStack = 0x%08x\n",
645 Sda->CurrentPsp, SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack, MAKELONG(getSP(), getSS()));
646 #endif
647
648 /* Update the last stack in the PSP */
649 SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
650 }
651
652 /* Set the initial segment registers */
653 setDS(Segment);
654 setES(Segment);
655
656 /* Set the stack */
657 setSS(FinalSS);
658 setSP(FinalSP);
659
660 /*
661 * Set the other registers as in real DOS: some demos expect them so!
662 * See http://www.fysnet.net/yourhelp.htm
663 * and http://www.beroset.com/asm/showregs.asm
664 */
665 setDX(Segment);
666 setDI(FinalSP);
667 setBP(0x091E); // DOS base stack pointer relic value. In MS-DOS 5.0 and Windows' NTVDM it's 0x091C. This is in fact the old SP value inside DosData disk stack.
668 setSI(FinalIP);
669
670 setAX(0/*0xFFFF*/); // FIXME: fcbcode
671 setBX(0/*0xFFFF*/); // FIXME: fcbcode
672 setCX(0x00FF);
673
674 /*
675 * Keep critical flags, clear test flags (OF, SF, ZF, AF, PF, CF)
676 * and explicitly set the interrupt flag.
677 */
678 setEFLAGS((getEFLAGS() & ~0x08D5) | 0x0200);
679
680 /* Notify VDDs of process execution */
681 VDDCreateUserHook(Segment);
682
683 /* Execute */
684 DosSetProcessContext(Segment);
685 CpuExecute(FinalCS, FinalIP);
686 }
687 else if (LoadType == DOS_LOAD_ONLY)
688 {
689 ASSERT(Parameters);
690 Parameters->StackLocation = MAKELONG(FinalSP, FinalSS);
691 Parameters->EntryPoint = MAKELONG(FinalIP, FinalCS);
692 }
693
694 Cleanup:
695 if (Result != ERROR_SUCCESS)
696 {
697 /* It was not successful, cleanup the DOS memory */
698 if (EnvBlock) DosFreeMemory(EnvBlock);
699 if (Segment) DosFreeMemory(Segment);
700 }
701
702 return Result;
703 }
704
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,IN LPCSTR ExecutablePath,IN PDOS_EXEC_PARAM_BLOCK Parameters,IN LPCSTR CommandLine OPTIONAL,IN LPCSTR Environment OPTIONAL,IN DWORD ReturnAddress OPTIONAL)705 DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
706 IN LPCSTR ExecutablePath,
707 IN PDOS_EXEC_PARAM_BLOCK Parameters,
708 IN LPCSTR CommandLine OPTIONAL,
709 IN LPCSTR Environment OPTIONAL,
710 IN DWORD ReturnAddress OPTIONAL)
711 {
712 DWORD Result = ERROR_SUCCESS;
713 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
714 DWORD FileSize;
715 LPBYTE Address = NULL;
716 CHAR FullPath[MAX_PATH];
717 CHAR ShortFullPath[MAX_PATH];
718
719 DPRINT1("DosLoadExecutable(%d, '%s', 0x%p, 0x%p, 0x%p)\n",
720 LoadType, ExecutablePath, Parameters, CommandLine, Environment);
721
722 /* Try to get the full path to the executable */
723 if (GetFullPathNameA(ExecutablePath, sizeof(FullPath), FullPath, NULL))
724 {
725 /* Get the corresponding short path */
726 if (GetShortPathNameA(FullPath, ShortFullPath, sizeof(ShortFullPath)))
727 {
728 /* Use the shortened full path from now on */
729 ExecutablePath = ShortFullPath;
730 }
731 }
732
733 /* Open a handle to the executable */
734 FileHandle = CreateFileA(ExecutablePath,
735 GENERIC_READ,
736 FILE_SHARE_READ,
737 NULL,
738 OPEN_EXISTING,
739 FILE_ATTRIBUTE_NORMAL,
740 NULL);
741 if (FileHandle == INVALID_HANDLE_VALUE)
742 {
743 Result = GetLastError();
744 goto Cleanup;
745 }
746
747 /* Get the file size */
748 FileSize = GetFileSize(FileHandle, NULL);
749
750 /* Create a mapping object for the file */
751 FileMapping = CreateFileMapping(FileHandle,
752 NULL,
753 PAGE_READONLY,
754 0,
755 0,
756 NULL);
757 if (FileMapping == NULL)
758 {
759 Result = GetLastError();
760 goto Cleanup;
761 }
762
763 /* Map the file into memory */
764 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
765 if (Address == NULL)
766 {
767 Result = GetLastError();
768 goto Cleanup;
769 }
770
771 Result = DosLoadExecutableInternal(LoadType,
772 Address,
773 FileSize,
774 ExecutablePath,
775 Parameters,
776 CommandLine,
777 Environment,
778 ReturnAddress);
779
780 Cleanup:
781 /* Unmap the file*/
782 if (Address != NULL) UnmapViewOfFile(Address);
783
784 /* Close the file mapping object */
785 if (FileMapping != NULL) CloseHandle(FileMapping);
786
787 /* Close the file handle */
788 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
789
790 return Result;
791 }
792
DosCreateProcess(IN LPCSTR ProgramName,IN PDOS_EXEC_PARAM_BLOCK Parameters,IN DWORD ReturnAddress OPTIONAL)793 WORD DosCreateProcess(IN LPCSTR ProgramName,
794 IN PDOS_EXEC_PARAM_BLOCK Parameters,
795 IN DWORD ReturnAddress OPTIONAL)
796 {
797 DWORD Result = ERROR_SUCCESS;
798 DWORD BinaryType;
799
800 /* Get the binary type */
801 if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
802
803 /* Check the type of the program */
804 switch (BinaryType)
805 {
806 /* Those are handled by NTVDM */
807 case SCS_WOW_BINARY:
808 {
809 static const PCSTR AppName = "\"%ProgramFiles%\\otvdm\\otvdmw.exe\" ";
810
811 STARTUPINFOA si;
812 PROCESS_INFORMATION pi;
813 union { DWORD Size; NTSTATUS Status; } Ret;
814 CHAR ExpName[MAX_PATH];
815
816 Ret.Size = ExpandEnvironmentStringsA(AppName, ExpName, _countof(ExpName));
817 if ((Ret.Size == 0) || (Ret.Size > _countof(ExpName)))
818 {
819 /* We failed or buffer too small, fall back to DOS execution */
820 goto RunAsDOS;
821 }
822 Ret.Size--; // Remove NULL-terminator from count
823
824 /* Add double-quotes before and after ProgramName */
825 Ret.Status = RtlStringCchPrintfA(ExpName + Ret.Size, _countof(ExpName) - Ret.Size,
826 "\"%s\"", ProgramName);
827 if (!NT_SUCCESS(Ret.Status))
828 {
829 /* We failed or buffer too small, fall back to DOS execution */
830 goto RunAsDOS;
831 }
832
833 ZeroMemory(&pi, sizeof(pi));
834 ZeroMemory(&si, sizeof(si));
835 si.cb = sizeof(si);
836
837 /* Create the process */
838 if (CreateProcessA(NULL, // No Application Name
839 ExpName, // Just our Command Line
840 NULL, // Cannot inherit Process Handle
841 NULL, // Cannot inherit Thread Handle
842 FALSE, // No handle inheritance
843 0, // No extra creation flags
844 NULL, // No environment block
845 NULL, // No starting directory
846 &si,
847 &pi))
848 {
849 /* Close the handles */
850 CloseHandle(pi.hThread);
851 CloseHandle(pi.hProcess);
852 break;
853 }
854 else
855 {
856 /* Retrieve the actual path to the "Program Files" directory for displaying the error */
857 ExpandEnvironmentStringsA("%ProgramFiles%", ExpName, _countof(ExpName));
858
859 DisplayMessage(L"Trying to load '%S'.\n"
860 L"WOW16 applications are not supported internally by NTVDM at the moment.\n"
861 L"Consider installing WineVDM from the ReactOS Applications Manager in\n'%S'.\n\n"
862 L"Click on OK to continue.",
863 ProgramName, ExpName);
864 }
865 // Fall through
866 }
867 RunAsDOS:
868 case SCS_DOS_BINARY:
869 {
870 /* Load the executable */
871 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
872 ProgramName,
873 Parameters,
874 NULL,
875 NULL,
876 ReturnAddress);
877 if (Result != ERROR_SUCCESS)
878 {
879 DisplayMessage(L"Could not load '%S'. Error: %u", ProgramName, Result);
880 }
881
882 break;
883 }
884
885 /* Not handled by NTVDM */
886 default:
887 {
888 LPSTR Environment = NULL;
889 CHAR CmdLine[MAX_PATH + DOS_CMDLINE_LENGTH + 1];
890 LPSTR CmdLinePtr;
891 ULONG CmdLineSize;
892
893 /* Did the caller specify an environment segment? */
894 if (Parameters->Environment)
895 {
896 /* Yes, use it instead of the parent one */
897 Environment = (LPSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
898 }
899
900 /*
901 * Convert the DOS command line to Win32-compatible format, by concatenating
902 * the program name with the converted command line.
903 * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
904 */
905 CmdLinePtr = CmdLine;
906 strncpy(CmdLinePtr, ProgramName, MAX_PATH); // Concatenate the program name
907 CmdLinePtr += strlen(CmdLinePtr);
908 *CmdLinePtr++ = ' '; // Add separating space
909
910 CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
911 RtlCopyMemory(CmdLinePtr,
912 (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
913 CmdLineSize);
914 /* NULL-terminate it */
915 CmdLinePtr[CmdLineSize] = '\0';
916
917 /* Remove any trailing return carriage character and NULL-terminate the command line */
918 while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
919 *CmdLinePtr = '\0';
920
921 Result = DosStartProcess32(ProgramName, CmdLine,
922 Environment, ReturnAddress,
923 TRUE);
924 if (Result != ERROR_SUCCESS)
925 {
926 DisplayMessage(L"Could not load 32-bit '%S'. Error: %u", ProgramName, Result);
927 }
928
929 break;
930 }
931 }
932
933 return Result;
934 }
935
DosTerminateProcess(WORD Psp,BYTE ReturnCode,WORD KeepResident)936 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
937 {
938 WORD McbSegment = SysVars->FirstMcb;
939 PDOS_MCB CurrentMcb;
940 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
941 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
942 LPWORD Stack;
943 BYTE TerminationType;
944
945 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
946 Psp, ReturnCode, KeepResident);
947
948 /* Notify VDDs of process termination */
949 VDDTerminateUserHook(Psp);
950
951 /* Check if this PSP is its own parent */
952 if (PspBlock->ParentPsp == Psp) goto Done;
953
954 if (KeepResident == 0)
955 {
956 WORD i;
957 for (i = 0; i < PspBlock->HandleTableSize; i++)
958 {
959 /* Close the handle */
960 DosCloseHandle(i);
961 }
962 }
963
964 /* Free the memory used by the process */
965 while (TRUE)
966 {
967 /* Get a pointer to the MCB */
968 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
969
970 /* Make sure the MCB is valid */
971 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
972
973 /* Check if this block was allocated by the process */
974 if (CurrentMcb->OwnerPsp == Psp)
975 {
976 if (KeepResident)
977 {
978 /* Check if this is the PSP block and we should reduce its size */
979 if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
980 {
981 /* Reduce the size of the block */
982 DosResizeMemory(McbSegment + 1, KeepResident, NULL);
983 break;
984 }
985 }
986 else
987 {
988 /* Free this entire block */
989 DosFreeMemory(McbSegment + 1);
990 }
991 }
992
993 /* If this was the last block, quit */
994 if (CurrentMcb->BlockType == 'Z') break;
995
996 /* Update the segment and continue */
997 McbSegment += CurrentMcb->Size + 1;
998 }
999
1000 Done:
1001 /* Restore the interrupt vectors */
1002 IntVecTable[0x22] = PspBlock->TerminateAddress;
1003 IntVecTable[0x23] = PspBlock->BreakAddress;
1004 IntVecTable[0x24] = PspBlock->CriticalAddress;
1005
1006 /* Update the current PSP with the parent's one */
1007 if (Psp == Sda->CurrentPsp)
1008 {
1009 DosSetProcessContext(PspBlock->ParentPsp);
1010 if (Sda->CurrentPsp == SYSTEM_PSP)
1011 {
1012 // NOTE: we can also use the DOS BIOS exit code.
1013 CpuUnsimulate();
1014 return;
1015 }
1016 }
1017
1018 /* Save the return code - Normal termination or TSR */
1019 TerminationType = (KeepResident != 0 ? 0x03 : 0x00);
1020 Sda->ErrorLevel = MAKEWORD(ReturnCode, TerminationType);
1021
1022 #ifdef ADVANCED_DEBUGGING
1023 DPRINT1("PspBlock->ParentPsp = 0x%04x; Sda->CurrentPsp = 0x%04x\n",
1024 PspBlock->ParentPsp, Sda->CurrentPsp);
1025 #endif
1026
1027 if (Sda->CurrentPsp != SYSTEM_PSP)
1028 {
1029 #ifdef ADVANCED_DEBUGGING
1030 DPRINT1("Sda->CurrentPsp = 0x%04x; Old SS:SP = %04X:%04X going to be LastStack = 0x%08x\n",
1031 Sda->CurrentPsp, getSS(), getSP(), SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack);
1032 #endif
1033
1034 /* Restore the parent's stack */
1035 setSS(HIWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack));
1036 setSP(LOWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack));
1037
1038 /* Pop the task state */
1039 DosRestoreState();
1040 }
1041
1042 /* Return control to the parent process */
1043 Stack = (LPWORD)SEG_OFF_TO_PTR(getSS(), getSP());
1044 Stack[STACK_CS] = HIWORD(PspBlock->TerminateAddress);
1045 Stack[STACK_IP] = LOWORD(PspBlock->TerminateAddress);
1046 }
1047
1048 /* EOF */
1049