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