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