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 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 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 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 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 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 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 329 VOID DosSetProcessContext(WORD Segment) 330 { 331 Sda->CurrentPsp = Segment; 332 Sda->DiskTransferArea = MAKELONG(0x80, Segment); 333 } 334 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 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 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 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