[NTVDM]: Fix compilation in STANDALONE mode.
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dem.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: dem.c
5 * PURPOSE: DOS 32-bit Emulation Support Library -
6 * This library is used by the built-in NTVDM DOS32 and by
7 * the NT 16-bit DOS in Windows (via BOPs). It also exposes
8 * exported functions that can be used by VDDs.
9 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
10 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
11 */
12
13 /* INCLUDES *******************************************************************/
14
15 #define NDEBUG
16
17 #include "ntvdm.h"
18 #include "emulator.h"
19 #include <isvbop.h>
20
21 #include "utils.h"
22
23 #include "dem.h"
24 #include "dos/dos32krnl/device.h"
25 #include "dos/dos32krnl/process.h"
26 #include "cpu/bop.h"
27
28 #include "bios/bios.h"
29 #include "mouse32.h"
30
31 /* PRIVATE VARIABLES **********************************************************/
32
33 extern PDOS_DATA DosData;
34
35 /* PRIVATE FUNCTIONS **********************************************************/
36
37 static VOID WINAPI DosSystemBop(LPWORD Stack)
38 {
39 /* Get the Function Number and skip it */
40 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
41 setIP(getIP() + 1);
42
43 switch (FuncNum)
44 {
45 /* Load the DOS kernel */
46 case 0x11:
47 {
48 BOOLEAN Success = FALSE;
49 LPCSTR DosKernelFileName = "ntdos.sys";
50 HANDLE hDosKernel;
51 ULONG ulDosKernelSize = 0;
52
53 DPRINT1("You are loading Windows NT DOS!\n");
54
55 /* Open the DOS kernel file */
56 hDosKernel = FileOpen(DosKernelFileName, &ulDosKernelSize);
57 if (hDosKernel == NULL) goto Quit;
58
59 /*
60 * Attempt to load the DOS kernel into memory.
61 * The segment where to load the DOS kernel is defined
62 * by the DOS BIOS and is found in DI:0000 .
63 */
64 Success = FileLoadByHandle(hDosKernel,
65 REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
66 ulDosKernelSize,
67 &ulDosKernelSize);
68
69 DPRINT1("Windows NT DOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n",
70 DosKernelFileName,
71 (Success ? "succeeded" : "failed"),
72 getDI(), 0x0000,
73 ulDosKernelSize,
74 GetLastError());
75
76 /* Close the DOS kernel file */
77 FileClose(hDosKernel);
78
79 Quit:
80 if (!Success)
81 {
82 /* We failed everything, stop the VDM */
83 DisplayMessage(L"Windows NT DOS kernel file '%S' loading failed (Error: %u). The VDM will shut down.",
84 DosKernelFileName, GetLastError());
85 EmulatorTerminate();
86 return;
87 }
88
89 break;
90 }
91
92 /* Call 32-bit Driver Strategy Routine */
93 case BOP_DRV_STRATEGY:
94 {
95 DeviceStrategyBop();
96 break;
97 }
98
99 /* Call 32-bit Driver Interrupt Routine */
100 case BOP_DRV_INTERRUPT:
101 {
102 DeviceInterruptBop();
103 break;
104 }
105
106 default:
107 {
108 DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum);
109 // setCF(1); // Disable, otherwise we enter an infinite loop
110 break;
111 }
112 }
113 }
114
115 static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
116 {
117 /* Get the Function Number and skip it */
118 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
119 setIP(getIP() + 1);
120
121 switch (FuncNum)
122 {
123 case 0x08: // Launch external command
124 {
125 BOOL Result;
126 DWORD dwExitCode;
127
128 LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI());
129 CHAR CmdLine[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH + 1] = "";
130 LPSTR CmdLinePtr;
131 ULONG CmdLineLen;
132 STARTUPINFOA StartupInfo;
133 PROCESS_INFORMATION ProcessInformation;
134
135 /* Spawn a user-defined 32-bit command preprocessor */
136
137 // FIXME: Use COMSPEC env var!!
138 CmdLinePtr = CmdLine;
139 strcpy(CmdLinePtr, "cmd.exe /c ");
140 CmdLinePtr += strlen(CmdLinePtr);
141
142 /* Build a Win32-compatible command-line */
143 CmdLineLen = min(strlen(Command), sizeof(CmdLine) - strlen(CmdLinePtr) - 1);
144 RtlCopyMemory(CmdLinePtr, Command, CmdLineLen);
145 CmdLinePtr[CmdLineLen] = '\0';
146
147 /* Remove any trailing return carriage character and NULL-terminate the command line */
148 while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
149 *CmdLinePtr = '\0';
150
151 DPRINT1("CMD Run Command '%s' ('%s')\n", Command, CmdLine);
152
153 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
154 RtlZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
155
156 StartupInfo.cb = sizeof(StartupInfo);
157
158 VidBiosDetachFromConsole();
159
160 Result = CreateProcessA(NULL,
161 CmdLine,
162 NULL,
163 NULL,
164 TRUE,
165 0,
166 NULL,
167 NULL,
168 &StartupInfo,
169 &ProcessInformation);
170 if (Result)
171 {
172 DPRINT1("Command '%s' ('%s') launched successfully\n", Command, CmdLine);
173
174 /* Wait for process termination */
175 WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
176
177 /* Get the exit code */
178 GetExitCodeProcess(ProcessInformation.hProcess, &dwExitCode);
179
180 /* Close handles */
181 CloseHandle(ProcessInformation.hThread);
182 CloseHandle(ProcessInformation.hProcess);
183 }
184 else
185 {
186 DPRINT1("Failed when launched command '%s' ('%s')\n", Command, CmdLine);
187 dwExitCode = GetLastError();
188 }
189
190 VidBiosAttachToConsole();
191
192 setAL((UCHAR)dwExitCode);
193
194 break;
195 }
196
197 default:
198 {
199 DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum);
200 // setCF(1); // Disable, otherwise we enter an infinite loop
201 break;
202 }
203 }
204 }
205
206 #ifndef STANDALONE
207 static DWORD
208 WINAPI
209 CommandThreadProc(LPVOID Parameter)
210 {
211 BOOLEAN First = TRUE;
212 DWORD Result;
213 VDM_COMMAND_INFO CommandInfo;
214 CHAR CmdLine[MAX_PATH];
215 CHAR AppName[MAX_PATH];
216 CHAR PifFile[MAX_PATH];
217 CHAR Desktop[MAX_PATH];
218 CHAR Title[MAX_PATH];
219 ULONG EnvSize = 256;
220 PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
221
222 UNREFERENCED_PARAMETER(Parameter);
223 ASSERT(Env);
224
225 /* Clear the structure */
226 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
227
228 /* Get the initial information */
229 CommandInfo.TaskId = SessionId;
230 CommandInfo.VDMState = VDM_GET_FIRST_COMMAND | VDM_FLAG_DOS;
231 GetNextVDMCommand(&CommandInfo);
232
233 do
234 {
235 /* Clear the structure */
236 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
237
238 /* Initialize the structure members */
239 CommandInfo.TaskId = SessionId;
240 CommandInfo.VDMState = VDM_FLAG_DOS;
241 CommandInfo.CmdLine = CmdLine;
242 CommandInfo.CmdLen = sizeof(CmdLine);
243 CommandInfo.AppName = AppName;
244 CommandInfo.AppLen = sizeof(AppName);
245 CommandInfo.PifFile = PifFile;
246 CommandInfo.PifLen = sizeof(PifFile);
247 CommandInfo.Desktop = Desktop;
248 CommandInfo.DesktopLen = sizeof(Desktop);
249 CommandInfo.Title = Title;
250 CommandInfo.TitleLen = sizeof(Title);
251 CommandInfo.Env = Env;
252 CommandInfo.EnvLen = EnvSize;
253
254 if (First) CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK;
255
256 Command:
257 if (!GetNextVDMCommand(&CommandInfo))
258 {
259 if (CommandInfo.EnvLen > EnvSize)
260 {
261 /* Expand the environment size */
262 EnvSize = CommandInfo.EnvLen;
263 CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
264
265 /* Repeat the request */
266 CommandInfo.VDMState |= VDM_FLAG_RETRY;
267 goto Command;
268 }
269
270 break;
271 }
272
273 /* Start the process from the command line */
274 Result = DosStartProcess(AppName, CmdLine, Env, MAKELONG(getIP(), getCS()));
275 if (Result != ERROR_SUCCESS)
276 {
277 DisplayMessage(L"Could not start '%S'. Error: %u", AppName, Result);
278 // break;
279 continue;
280 }
281
282 First = FALSE;
283 }
284 while (AcceptCommands);
285
286 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
287 return 0;
288 }
289 #endif
290
291 /* PUBLIC VARIABLES ***********************************************************/
292
293 #ifndef STANDALONE
294 BOOLEAN AcceptCommands = TRUE;
295 HANDLE CommandThread = NULL;
296 ULONG SessionId = 0;
297 #endif
298
299 /* PUBLIC FUNCTIONS ***********************************************************/
300
301 //
302 // This function (equivalent of the DOS bootsector) is called by the bootstrap
303 // loader *BEFORE* jumping at 0000:7C00. What we should do is to write at 0000:7C00
304 // a BOP call that calls DosInitialize back. Then the bootstrap loader jumps at
305 // 0000:7C00, our BOP gets called and then we can initialize the 32-bit part of the DOS.
306 //
307
308 /* 16-bit bootstrap code at 0000:7C00 */
309 /* Of course, this is not in real bootsector format, because we don't care about it for now */
310 static BYTE Bootsector1[] =
311 {
312 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_LOAD_DOS
313 };
314 /* This portion of code is run if we failed to load the DOS */
315 // NOTE: This may also be done by the BIOS32.
316 static BYTE Bootsector2[] =
317 {
318 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
319 };
320
321 static VOID WINAPI DosInitialize(LPWORD Stack);
322
323 VOID DosBootsectorInitialize(VOID)
324 {
325 /* We write the bootsector at 0000:7C00 */
326 ULONG_PTR Address = (ULONG_PTR)SEG_OFF_TO_PTR(0x0000, 0x7C00);
327 CHAR DosKernelFileName[] = ""; // No DOS BIOS file name, therefore we will load DOS32
328
329 DPRINT("DosBootsectorInitialize\n");
330
331 /* Write the "bootsector" */
332 RtlCopyMemory((PVOID)Address, Bootsector1, sizeof(Bootsector1));
333 Address += sizeof(Bootsector1);
334 RtlCopyMemory((PVOID)Address, DosKernelFileName, sizeof(DosKernelFileName));
335 Address += sizeof(DosKernelFileName);
336 RtlCopyMemory((PVOID)Address, Bootsector2, sizeof(Bootsector2));
337
338 /* Register the DOS Loading BOP */
339 RegisterBop(BOP_LOAD_DOS, DosInitialize);
340 }
341
342
343 //
344 // This function is called by the DOS bootsector in case we load DOS32.
345 // It sets up the DOS32 start code then jumps to 0070:0000.
346 //
347
348 /* 16-bit startup code for DOS32 at 0070:0000 */
349 static BYTE Startup[] =
350 {
351 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_START_DOS,
352 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE
353 };
354
355 static VOID WINAPI DosStart(LPWORD Stack);
356
357 static VOID WINAPI DosInitialize(LPWORD Stack)
358 {
359 /* Get the DOS BIOS file name (NULL-terminated) */
360 // FIXME: Isn't it possible to use some DS:SI instead??
361 LPCSTR DosBiosFileName = (LPCSTR)SEG_OFF_TO_PTR(getCS(), getIP());
362 setIP(getIP() + strlen(DosBiosFileName) + 1); // Skip it
363
364 DPRINT("DosInitialize('%s')\n", DosBiosFileName);
365
366 /*
367 * We succeeded, deregister the DOS Loading BOP
368 * so that no app will be able to call us back.
369 */
370 RegisterBop(BOP_LOAD_DOS, NULL);
371
372 /* Register the DOS BOPs */
373 RegisterBop(BOP_DOS, DosSystemBop );
374 RegisterBop(BOP_CMD, DosCmdInterpreterBop);
375
376 if (DosBiosFileName && DosBiosFileName[0] != '\0')
377 {
378 BOOLEAN Success = FALSE;
379 HANDLE hDosBios;
380 ULONG ulDosBiosSize = 0;
381
382 /* Open the DOS BIOS file */
383 hDosBios = FileOpen(DosBiosFileName, &ulDosBiosSize);
384 if (hDosBios == NULL) goto Quit;
385
386 /* Attempt to load the DOS BIOS into memory */
387 Success = FileLoadByHandle(hDosBios,
388 REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)),
389 ulDosBiosSize,
390 &ulDosBiosSize);
391
392 DPRINT1("DOS BIOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n",
393 DosBiosFileName,
394 (Success ? "succeeded" : "failed"),
395 0x0070, 0x0000,
396 ulDosBiosSize,
397 GetLastError());
398
399 /* Close the DOS BIOS file */
400 FileClose(hDosBios);
401
402 Quit:
403 if (!Success)
404 {
405 DisplayMessage(L"DOS BIOS file '%S' loading failed (Error: %u). The VDM will shut down.",
406 DosBiosFileName, GetLastError());
407 return;
408 }
409 }
410 else
411 {
412 /* Load the 16-bit startup code for DOS32 and register its Starting BOP */
413 RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup, sizeof(Startup));
414
415 // This is the equivalent of BOP_LOAD_DOS, function 0x11 "Load the DOS kernel"
416 // for the Windows NT DOS.
417 RegisterBop(BOP_START_DOS, DosStart);
418 }
419
420 /* Position execution pointers for DOS startup and return */
421 setCS(0x0070);
422 setIP(0x0000);
423 }
424
425 static VOID WINAPI DosStart(LPWORD Stack)
426 {
427 BOOLEAN Success;
428 #ifdef STANDALONE
429 DWORD Result;
430 CHAR ApplicationName[MAX_PATH];
431 CHAR CommandLine[DOS_CMDLINE_LENGTH];
432 #else
433 INT i;
434 #endif
435
436 DPRINT("DosStart\n");
437
438 /*
439 * We succeeded, deregister the DOS Starting BOP
440 * so that no app will be able to call us back.
441 */
442 RegisterBop(BOP_START_DOS, NULL);
443
444 Success = DosBIOSInitialize();
445 // Success &= DosKRNLInitialize();
446 if (!Success)
447 {
448 DisplayMessage(L"DOS32 loading failed (Error: %u). The VDM will shut down.", GetLastError());
449 EmulatorTerminate();
450 return;
451 }
452
453 /* Load the mouse driver */
454 DosMouseInitialize();
455
456 #ifndef STANDALONE
457
458 /* Parse the command line arguments */
459 for (i = 1; i < NtVdmArgc; i++)
460 {
461 if (wcsncmp(NtVdmArgv[i], L"-i", 2) == 0)
462 {
463 /* This is the session ID (hex format) */
464 SessionId = wcstoul(NtVdmArgv[i] + 2, NULL, 16);
465
466 /* The VDM hasn't been started from a console, so quit when the task is done */
467 AcceptCommands = FALSE;
468 }
469 }
470
471 /* Create the GetNextVDMCommand thread */
472 CommandThread = CreateThread(NULL, 0, &CommandThreadProc, NULL, 0, NULL);
473 if (CommandThread == NULL)
474 {
475 wprintf(L"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
476 goto Quit;
477 }
478
479 /* Wait for the command thread to exit */
480 WaitForSingleObject(CommandThread, INFINITE);
481
482 /* Close the thread handle */
483 CloseHandle(CommandThread);
484
485 #else
486
487 if (NtVdmArgc >= 2)
488 {
489 WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, ApplicationName, sizeof(ApplicationName), NULL, NULL);
490
491 if (NtVdmArgc >= 3)
492 WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
493 else
494 strcpy(CommandLine, "");
495 }
496 else
497 {
498 DisplayMessage(L"Invalid DOS command line\n");
499 goto Quit;
500 }
501
502 /* Start the process from the command line */
503 Result = DosStartProcess(ApplicationName, CommandLine,
504 SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0),
505 MAKELONG(getIP(), getCS()));
506 if (Result != ERROR_SUCCESS)
507 {
508 DisplayMessage(L"Could not start '%S'. Error: %u", ApplicationName, Result);
509 goto Quit;
510 }
511
512 #endif
513
514 Quit:
515 /* Stop the VDM */
516 EmulatorTerminate();
517 }
518
519 /* PUBLIC EXPORTED APIS *******************************************************/
520
521 // demLFNCleanup
522 // demLFNGetCurrentDirectory
523
524 // demGetFileTimeByHandle_WOW
525 // demWOWLFNAllocateSearchHandle
526 // demWOWLFNCloseSearchHandle
527 // demWOWLFNEntry
528 // demWOWLFNGetSearchHandle
529 // demWOWLFNInit
530
531 DWORD
532 WINAPI
533 demClientErrorEx(IN HANDLE FileHandle,
534 IN CHAR Unknown,
535 IN BOOL Flag)
536 {
537 UNIMPLEMENTED;
538 return GetLastError();
539 }
540
541 DWORD
542 WINAPI
543 demFileDelete(IN LPCSTR FileName)
544 {
545 if (DeleteFileA(FileName)) SetLastError(ERROR_SUCCESS);
546
547 return GetLastError();
548 }
549
550 DWORD
551 WINAPI
552 demFileFindFirst(OUT PVOID lpFindFileData,
553 IN LPCSTR FileName,
554 IN WORD AttribMask)
555 {
556 BOOLEAN Success = TRUE;
557 WIN32_FIND_DATAA FindData;
558 HANDLE SearchHandle;
559 PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData;
560
561 /* Start a search */
562 SearchHandle = FindFirstFileA(FileName, &FindData);
563 if (SearchHandle == INVALID_HANDLE_VALUE) return GetLastError();
564
565 do
566 {
567 /* Check the attributes and retry as long as we haven't found a matching file */
568 if (!((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN |
569 FILE_ATTRIBUTE_SYSTEM |
570 FILE_ATTRIBUTE_DIRECTORY))
571 & ~AttribMask))
572 {
573 break;
574 }
575 }
576 while ((Success = FindNextFileA(SearchHandle, &FindData)));
577
578 /* If we failed at some point, close the search and return an error */
579 if (!Success)
580 {
581 FindClose(SearchHandle);
582 return GetLastError();
583 }
584
585 /* Fill the block */
586 FindFileBlock->DriveLetter = DosData->Sda.CurrentDrive + 'A';
587 FindFileBlock->AttribMask = AttribMask;
588 FindFileBlock->SearchHandle = SearchHandle;
589 FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes);
590 FileTimeToDosDateTime(&FindData.ftLastWriteTime,
591 &FindFileBlock->FileDate,
592 &FindFileBlock->FileTime);
593 FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF
594 : FindData.nFileSizeLow;
595 /* Build a short path name */
596 if (*FindData.cAlternateFileName)
597 strncpy(FindFileBlock->FileName, FindData.cAlternateFileName, sizeof(FindFileBlock->FileName));
598 else
599 GetShortPathNameA(FindData.cFileName, FindFileBlock->FileName, sizeof(FindFileBlock->FileName));
600
601 return ERROR_SUCCESS;
602 }
603
604 DWORD
605 WINAPI
606 demFileFindNext(OUT PVOID lpFindFileData)
607 {
608 WIN32_FIND_DATAA FindData;
609 PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData;
610
611 do
612 {
613 /* Continue searching as long as we haven't found a matching file */
614
615 /* If we failed at some point, close the search and return an error */
616 if (!FindNextFileA(FindFileBlock->SearchHandle, &FindData))
617 {
618 FindClose(FindFileBlock->SearchHandle);
619 return GetLastError();
620 }
621 }
622 while ((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN |
623 FILE_ATTRIBUTE_SYSTEM |
624 FILE_ATTRIBUTE_DIRECTORY))
625 & ~FindFileBlock->AttribMask);
626
627 /* Update the block */
628 FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes);
629 FileTimeToDosDateTime(&FindData.ftLastWriteTime,
630 &FindFileBlock->FileDate,
631 &FindFileBlock->FileTime);
632 FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF
633 : FindData.nFileSizeLow;
634 /* Build a short path name */
635 if (*FindData.cAlternateFileName)
636 strncpy(FindFileBlock->FileName, FindData.cAlternateFileName, sizeof(FindFileBlock->FileName));
637 else
638 GetShortPathNameA(FindData.cFileName, FindFileBlock->FileName, sizeof(FindFileBlock->FileName));
639
640 return ERROR_SUCCESS;
641 }
642
643 UCHAR
644 WINAPI
645 demGetPhysicalDriveType(IN UCHAR DriveNumber)
646 {
647 UNIMPLEMENTED;
648 return DOSDEVICE_DRIVE_UNKNOWN;
649 }
650
651 BOOL
652 WINAPI
653 demIsShortPathName(IN LPCSTR Path,
654 IN BOOL Unknown)
655 {
656 UNIMPLEMENTED;
657 return FALSE;
658 }
659
660 DWORD
661 WINAPI
662 demSetCurrentDirectoryGetDrive(IN LPCSTR CurrentDirectory,
663 OUT PUCHAR DriveNumber)
664 {
665 UNIMPLEMENTED;
666 return ERROR_SUCCESS;
667 }
668
669 /* EOF */