281dc79744af1549d6605f85f2e41cd0459dd030
[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 "utils.h"
20
21 #include "dem.h"
22 #include "dos/dos32krnl/device.h"
23 #include "dos/dos32krnl/process.h"
24 #include "cpu/bop.h"
25
26 #include "bios/bios.h"
27 #include "mouse32.h"
28
29 /* PRIVATE VARIABLES **********************************************************/
30
31 extern PDOS_DATA DosData;
32
33 /* PRIVATE FUNCTIONS **********************************************************/
34
35 static VOID WINAPI DosSystemBop(LPWORD Stack)
36 {
37 /* Get the Function Number and skip it */
38 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
39 setIP(getIP() + 1);
40
41 switch (FuncNum)
42 {
43 case 0x11: // Load the DOS kernel
44 {
45 BOOLEAN Success = FALSE;
46 HANDLE hDosKernel;
47 ULONG ulDosKernelSize = 0;
48
49 DPRINT1("You are loading Windows NT DOS!\n");
50
51 /* Open the DOS kernel file */
52 hDosKernel = FileOpen("ntdos.sys", &ulDosKernelSize);
53
54 /* If we failed, bail out */
55 if (hDosKernel == NULL) goto Quit;
56
57 /*
58 * Attempt to load the DOS kernel into memory.
59 * The segment where to load the DOS kernel is defined
60 * by the DOS BIOS and is found in DI:0000 .
61 */
62 Success = FileLoadByHandle(hDosKernel,
63 REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
64 ulDosKernelSize,
65 &ulDosKernelSize);
66
67 DPRINT1("Windows NT DOS loading %s at %04X:%04X, size 0x%X ; GetLastError() = %u\n",
68 (Success ? "succeeded" : "failed"),
69 getDI(), 0x0000,
70 ulDosKernelSize,
71 GetLastError());
72
73 /* Close the DOS kernel file */
74 FileClose(hDosKernel);
75
76 Quit:
77 if (!Success)
78 {
79 /* We failed everything, stop the VDM */
80 EmulatorTerminate();
81 return;
82 }
83
84 break;
85 }
86
87 /* Call 32-bit Driver Strategy Routine */
88 case BOP_DRV_STRATEGY:
89 {
90 DeviceStrategyBop();
91 break;
92 }
93
94 /* Call 32-bit Driver Interrupt Routine */
95 case BOP_DRV_INTERRUPT:
96 {
97 DeviceInterruptBop();
98 break;
99 }
100
101 default:
102 {
103 DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum);
104 // setCF(1); // Disable, otherwise we enter an infinite loop
105 break;
106 }
107 }
108 }
109
110 static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
111 {
112 /* Get the Function Number and skip it */
113 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
114 setIP(getIP() + 1);
115
116 switch (FuncNum)
117 {
118 case 0x08: // Launch external command
119 {
120 BOOL Result;
121 DWORD dwExitCode;
122
123 LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI());
124 CHAR CmdLine[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH + 1] = "";
125 LPSTR CmdLinePtr;
126 ULONG CmdLineLen;
127 STARTUPINFOA StartupInfo;
128 PROCESS_INFORMATION ProcessInformation;
129
130 /* Spawn a user-defined 32-bit command preprocessor */
131
132 // FIXME: Use COMSPEC env var!!
133 CmdLinePtr = CmdLine;
134 strcpy(CmdLinePtr, "cmd.exe /c ");
135 CmdLinePtr += strlen(CmdLinePtr);
136
137 /* Build a Win32-compatible command-line */
138 CmdLineLen = min(strlen(Command), sizeof(CmdLine) - strlen(CmdLinePtr) - 1);
139 RtlCopyMemory(CmdLinePtr, Command, CmdLineLen);
140 CmdLinePtr[CmdLineLen] = '\0';
141
142 /* Remove any trailing return carriage character and NULL-terminate the command line */
143 while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
144 *CmdLinePtr = '\0';
145
146 DPRINT1("CMD Run Command '%s' ('%s')\n", Command, CmdLine);
147
148 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
149 RtlZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
150
151 StartupInfo.cb = sizeof(StartupInfo);
152
153 VidBiosDetachFromConsole();
154
155 Result = CreateProcessA(NULL,
156 CmdLine,
157 NULL,
158 NULL,
159 TRUE,
160 0,
161 NULL,
162 NULL,
163 &StartupInfo,
164 &ProcessInformation);
165 if (Result)
166 {
167 DPRINT1("Command '%s' ('%s') launched successfully\n", Command, CmdLine);
168
169 /* Wait for process termination */
170 WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
171
172 /* Get the exit code */
173 GetExitCodeProcess(ProcessInformation.hProcess, &dwExitCode);
174
175 /* Close handles */
176 CloseHandle(ProcessInformation.hThread);
177 CloseHandle(ProcessInformation.hProcess);
178 }
179 else
180 {
181 DPRINT1("Failed when launched command '%s' ('%s')\n", Command, CmdLine);
182 dwExitCode = GetLastError();
183 }
184
185 VidBiosAttachToConsole();
186
187 setAL((UCHAR)dwExitCode);
188
189 break;
190 }
191
192 default:
193 {
194 DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum);
195 // setCF(1); // Disable, otherwise we enter an infinite loop
196 break;
197 }
198 }
199 }
200
201 #ifndef STANDALONE
202 static DWORD
203 WINAPI
204 CommandThreadProc(LPVOID Parameter)
205 {
206 BOOLEAN First = TRUE;
207 DWORD Result;
208 VDM_COMMAND_INFO CommandInfo;
209 CHAR CmdLine[MAX_PATH];
210 CHAR AppName[MAX_PATH];
211 CHAR PifFile[MAX_PATH];
212 CHAR Desktop[MAX_PATH];
213 CHAR Title[MAX_PATH];
214 ULONG EnvSize = 256;
215 PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
216
217 UNREFERENCED_PARAMETER(Parameter);
218 ASSERT(Env);
219
220 /* Clear the structure */
221 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
222
223 /* Get the initial information */
224 CommandInfo.TaskId = SessionId;
225 CommandInfo.VDMState = VDM_GET_FIRST_COMMAND | VDM_FLAG_DOS;
226 GetNextVDMCommand(&CommandInfo);
227
228 do
229 {
230 /* Clear the structure */
231 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
232
233 /* Initialize the structure members */
234 CommandInfo.TaskId = SessionId;
235 CommandInfo.VDMState = VDM_FLAG_DOS;
236 CommandInfo.CmdLine = CmdLine;
237 CommandInfo.CmdLen = sizeof(CmdLine);
238 CommandInfo.AppName = AppName;
239 CommandInfo.AppLen = sizeof(AppName);
240 CommandInfo.PifFile = PifFile;
241 CommandInfo.PifLen = sizeof(PifFile);
242 CommandInfo.Desktop = Desktop;
243 CommandInfo.DesktopLen = sizeof(Desktop);
244 CommandInfo.Title = Title;
245 CommandInfo.TitleLen = sizeof(Title);
246 CommandInfo.Env = Env;
247 CommandInfo.EnvLen = EnvSize;
248
249 if (First) CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK;
250
251 Command:
252 if (!GetNextVDMCommand(&CommandInfo))
253 {
254 if (CommandInfo.EnvLen > EnvSize)
255 {
256 /* Expand the environment size */
257 EnvSize = CommandInfo.EnvLen;
258 CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
259
260 /* Repeat the request */
261 CommandInfo.VDMState |= VDM_FLAG_RETRY;
262 goto Command;
263 }
264
265 break;
266 }
267
268 /* Start the process from the command line */
269 Result = DosStartProcess(AppName, CmdLine, Env, 0);
270 if (Result != ERROR_SUCCESS)
271 {
272 DisplayMessage(L"Could not start '%S'. Error: %u", AppName, Result);
273 // break;
274 continue;
275 }
276
277 First = FALSE;
278 }
279 while (AcceptCommands);
280
281 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
282 return 0;
283 }
284 #endif
285
286 /* PUBLIC VARIABLES ***********************************************************/
287
288 #ifndef STANDALONE
289 BOOLEAN AcceptCommands = TRUE;
290 HANDLE CommandThread = NULL;
291 ULONG SessionId = 0;
292 #endif
293
294 /* PUBLIC FUNCTIONS ***********************************************************/
295
296 //
297 // This function (equivalent of the DOS bootsector) is called by the bootstrap
298 // loader *BEFORE* jumping at 0000:7C00. What we should do is to write at 0000:7C00
299 // a BOP call that calls DosInitialize back. Then the bootstrap loader jumps at
300 // 0000:7C00, our BOP gets called and then we can initialize the 32-bit part of the DOS.
301 //
302
303 /* 16-bit bootstrap code at 0000:7C00 */
304 /* Of course, this is not in real bootsector format, because we don't care */
305 static BYTE Bootsector1[] =
306 {
307 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_LOAD_DOS, // Call DOS Loading
308 };
309 /* This portion of code is run if we failed to load the DOS */
310 static BYTE Bootsector2[] =
311 {
312 0xEA, // jmp far ptr
313 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B /** HACK! What to do instead?? **/
314 };
315
316 static VOID WINAPI DosInitialize(LPWORD Stack);
317
318 VOID DosBootsectorInitialize(VOID)
319 {
320 /* We write the bootsector at 0000:7C00 */
321 ULONG_PTR Address = (ULONG_PTR)SEG_OFF_TO_PTR(0x0000, 0x7C00);
322 CHAR DosKernelFileName[] = ""; // No DOS file name, therefore we'll load DOS32
323
324 DPRINT("DosBootsectorInitialize\n");
325
326 /* Write the "bootsector" */
327 RtlCopyMemory((PVOID)Address, Bootsector1, sizeof(Bootsector1));
328 Address += sizeof(Bootsector1);
329 RtlCopyMemory((PVOID)Address, DosKernelFileName, sizeof(DosKernelFileName));
330 Address += sizeof(DosKernelFileName);
331 RtlCopyMemory((PVOID)Address, Bootsector2, sizeof(Bootsector2));
332
333 /* Register the DOS Loading BOP */
334 RegisterBop(BOP_LOAD_DOS, DosInitialize);
335 }
336
337
338 //
339 // This function is called by the DOS bootsector. We finish to load
340 // the DOS, then we jump to 0070:0000.
341 //
342
343 /* 16-bit startup code at 0070:0000 */
344 static BYTE Startup[] =
345 {
346 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_START_DOS, // Call DOS Start
347 };
348
349 static VOID WINAPI DosStart(LPWORD Stack);
350
351 static VOID WINAPI DosInitialize(LPWORD Stack)
352 {
353 BOOLEAN Success = FALSE;
354
355 /* Get the DOS kernel file name (NULL-terminated) */
356 // FIXME: Isn't it possible to use some DS:SI instead??
357 LPCSTR DosKernelFileName = (LPCSTR)SEG_OFF_TO_PTR(getCS(), getIP());
358 setIP(getIP() + strlen(DosKernelFileName) + 1); // Skip it
359
360 DPRINT("DosInitialize('%s')\n", DosKernelFileName);
361
362 /* Register the DOS BOPs */
363 RegisterBop(BOP_DOS, DosSystemBop );
364 RegisterBop(BOP_CMD, DosCmdInterpreterBop);
365
366 if (DosKernelFileName && DosKernelFileName[0] != '\0')
367 {
368 HANDLE hDosBios;
369 ULONG ulDosBiosSize = 0;
370
371 /* Open the DOS BIOS file */
372 hDosBios = FileOpen(DosKernelFileName, &ulDosBiosSize);
373
374 /* If we failed, bail out */
375 if (hDosBios == NULL) goto QuitCustom;
376
377 /* Attempt to load the DOS BIOS into memory */
378 Success = FileLoadByHandle(hDosBios,
379 REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)),
380 ulDosBiosSize,
381 &ulDosBiosSize);
382
383 DPRINT1("DOS BIOS loading %s at %04X:%04X, size 0x%X ; GetLastError() = %u\n",
384 (Success ? "succeeded" : "failed"),
385 0x0070, 0x0000,
386 ulDosBiosSize,
387 GetLastError());
388
389 /* Close the DOS BIOS file */
390 FileClose(hDosBios);
391
392 if (!Success) goto QuitCustom;
393
394 /* Position execution pointers and return */
395 setCS(0x0070);
396 setIP(0x0000);
397
398 /* Return control */
399 QuitCustom:
400 if (!Success)
401 DisplayMessage(L"Custom DOS '%S' loading failed, what to do??", DosKernelFileName);
402 }
403 else
404 {
405 Success = DosBIOSInitialize();
406 // Success &= DosKRNLInitialize();
407
408 if (!Success) goto Quit32;
409
410 /* Write the "bootsector" */
411 RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup, sizeof(Startup));
412
413 /* Register the DOS Starting BOP */
414 RegisterBop(BOP_START_DOS, DosStart);
415
416 /* Position execution pointers and return */
417 setCS(0x0070);
418 setIP(0x0000);
419
420 /* Return control */
421 Quit32:
422 if (!Success)
423 DisplayMessage(L"DOS32 loading failed, what to do??");
424 }
425
426 if (Success)
427 {
428 /*
429 * We succeeded, deregister the DOS Loading BOP
430 * so that no app will be able to call us back.
431 */
432 RegisterBop(BOP_LOAD_DOS, NULL);
433 }
434 }
435
436 static VOID WINAPI DosStart(LPWORD Stack)
437 {
438 #ifdef STANDALONE
439 DWORD Result;
440 CHAR ApplicationName[MAX_PATH];
441 CHAR CommandLine[DOS_CMDLINE_LENGTH];
442 #else
443 INT i;
444 #endif
445
446 DPRINT("DosStart\n");
447
448 /*
449 * We succeeded, deregister the DOS Starting BOP
450 * so that no app will be able to call us back.
451 */
452 RegisterBop(BOP_START_DOS, NULL);
453
454 /* Load the mouse driver */
455 DosMouseInitialize();
456
457 #ifndef STANDALONE
458
459 /* Parse the command line arguments */
460 for (i = 1; i < NtVdmArgc; i++)
461 {
462 if (wcsncmp(NtVdmArgv[i], L"-i", 2) == 0)
463 {
464 /* This is the session ID */
465 SessionId = wcstoul(NtVdmArgv[i] + 2, NULL, 10);
466
467 /* The VDM hasn't been started from a console, so quit when the task is done */
468 AcceptCommands = FALSE;
469 }
470 }
471
472 /* Create the GetNextVDMCommand thread */
473 CommandThread = CreateThread(NULL, 0, &CommandThreadProc, NULL, 0, NULL);
474 if (CommandThread == NULL)
475 {
476 wprintf(L"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
477 goto Quit;
478 }
479
480 /* Wait for the command thread to exit */
481 WaitForSingleObject(CommandThread, INFINITE);
482
483 /* Close the thread handle */
484 CloseHandle(CommandThread);
485
486 #else
487
488 if (NtVdmArgc >= 2)
489 {
490 WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, ApplicationName, sizeof(ApplicationName), NULL, NULL);
491
492 if (NtVdmArgc >= 3)
493 WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
494 else
495 strcpy(CommandLine, "");
496 }
497 else
498 {
499 DisplayMessage(L"Invalid DOS command line\n");
500 goto Quit;
501 }
502
503 /* Start the process from the command line */
504 Result = DosStartProcess(ApplicationName, CommandLine,
505 SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0));
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 */