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