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