1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/vddsup.c 5 * PURPOSE: Virtual Device Drivers (VDD) Support 6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include "ntvdm.h" 12 13 #define NDEBUG 14 #include <debug.h> 15 16 #include "emulator.h" 17 #include "vddsup.h" 18 19 #include "cpu/bop.h" 20 #include <isvbop.h> 21 22 typedef VOID (WINAPI *VDD_PROC)(VOID); 23 24 typedef struct _VDD_MODULE 25 { 26 HMODULE hDll; 27 VDD_PROC DispatchRoutine; 28 } VDD_MODULE, *PVDD_MODULE; 29 30 // WARNING: A structure with the same name exists in nt_vdd.h, 31 // however it is not declared because its inclusion was prevented 32 // with #define NO_NTVDD_COMPAT, see ntvdm.h 33 typedef struct _VDD_USER_HANDLERS 34 { 35 LIST_ENTRY Entry; 36 37 HANDLE hVdd; 38 PFNVDD_UCREATE Ucr_Handler; 39 PFNVDD_UTERMINATE Uterm_Handler; 40 PFNVDD_UBLOCK Ublock_Handler; 41 PFNVDD_URESUME Uresume_Handler; 42 } VDD_USER_HANDLERS, *PVDD_USER_HANDLERS; 43 44 /* PRIVATE VARIABLES **********************************************************/ 45 46 // TODO: Maybe use a linked list. 47 // But the number of elements must be <= MAXUSHORT (MAXWORD) 48 #define MAX_VDD_MODULES 0xFF + 1 49 static VDD_MODULE VDDList[MAX_VDD_MODULES] = {{NULL}}; 50 51 // Valid handles of VDD DLLs start at 1 and finish at MAX_VDD_MODULES 52 #define ENTRY_TO_HANDLE(Entry) ((Entry) + 1) 53 #define HANDLE_TO_ENTRY(Handle) ((Handle) - 1) 54 #define IS_VALID_HANDLE(Handle) ((Handle) > 0 && (Handle) <= MAX_VDD_MODULES) 55 56 static LIST_ENTRY VddUserHooksList = {&VddUserHooksList, &VddUserHooksList}; 57 58 /* PRIVATE FUNCTIONS **********************************************************/ 59 60 static USHORT GetNextFreeVDDEntry(VOID) 61 { 62 USHORT Entry; 63 for (Entry = 0; Entry < ARRAYSIZE(VDDList); ++Entry) 64 { 65 if (VDDList[Entry].hDll == NULL) break; 66 } 67 return Entry; 68 } 69 70 static VOID WINAPI ThirdPartyVDDBop(LPWORD Stack) 71 { 72 /* Get the Function Number and skip it */ 73 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP()); 74 setIP(getIP() + 1); 75 76 switch (FuncNum) 77 { 78 /* RegisterModule */ 79 case 0: 80 { 81 BOOL Success = TRUE; 82 WORD RetVal = 0; 83 WORD Entry = 0; 84 LPCSTR DllName = NULL, 85 InitRoutineName = NULL, 86 DispatchRoutineName = NULL; 87 HMODULE hDll = NULL; 88 VDD_PROC InitRoutine = NULL, 89 DispatchRoutine = NULL; 90 91 DPRINT("RegisterModule() called\n"); 92 93 /* Clear the Carry Flag (no error happened so far) */ 94 setCF(0); 95 96 /* Retrieve the next free entry in the table (used later on) */ 97 Entry = GetNextFreeVDDEntry(); 98 if (Entry >= MAX_VDD_MODULES) 99 { 100 DPRINT1("Failed to create a new VDD module entry\n"); 101 Success = FALSE; 102 RetVal = 4; 103 goto Quit; 104 } 105 106 /* Retrieve the VDD name in DS:SI */ 107 DllName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()); 108 109 /* Retrieve the initialization routine API name in ES:DI (optional --> ES=DI=0) */ 110 if (TO_LINEAR(getES(), getDI()) != 0) 111 InitRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getES(), getDI()); 112 113 /* Retrieve the dispatch routine API name in DS:BX */ 114 DispatchRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getBX()); 115 116 DPRINT1("DllName = '%s' - InitRoutineName = '%s' - DispatchRoutineName = '%s'\n", 117 (DllName ? DllName : "n/a"), 118 (InitRoutineName ? InitRoutineName : "n/a"), 119 (DispatchRoutineName ? DispatchRoutineName : "n/a")); 120 121 /* Load the VDD DLL */ 122 hDll = LoadLibraryA(DllName); 123 if (hDll == NULL) 124 { 125 DWORD LastError = GetLastError(); 126 Success = FALSE; 127 128 if (LastError == ERROR_NOT_ENOUGH_MEMORY) 129 { 130 DPRINT1("Not enough memory to load DLL '%s'\n", DllName); 131 RetVal = 4; 132 goto Quit; 133 } 134 else 135 { 136 DPRINT1("Failed to load DLL '%s'; last error = %d\n", DllName, LastError); 137 RetVal = 1; 138 goto Quit; 139 } 140 } 141 142 /* Load the initialization routine if needed */ 143 if (InitRoutineName) 144 { 145 InitRoutine = (VDD_PROC)GetProcAddress(hDll, InitRoutineName); 146 if (InitRoutine == NULL) 147 { 148 DPRINT1("Failed to load the initialization routine '%s'\n", InitRoutineName); 149 Success = FALSE; 150 RetVal = 3; 151 goto Quit; 152 } 153 } 154 155 /* Load the dispatch routine */ 156 DispatchRoutine = (VDD_PROC)GetProcAddress(hDll, DispatchRoutineName); 157 if (DispatchRoutine == NULL) 158 { 159 DPRINT1("Failed to load the dispatch routine '%s'\n", DispatchRoutineName); 160 Success = FALSE; 161 RetVal = 2; 162 goto Quit; 163 } 164 165 /* If we reached this point, that means everything is OK */ 166 167 /* Register the VDD DLL */ 168 VDDList[Entry].hDll = hDll; 169 VDDList[Entry].DispatchRoutine = DispatchRoutine; 170 171 /* Call the initialization routine if needed */ 172 if (InitRoutine) InitRoutine(); 173 174 /* We succeeded. RetVal will contain a valid VDD DLL handle */ 175 Success = TRUE; 176 RetVal = ENTRY_TO_HANDLE(Entry); // Convert the entry to a valid handle 177 178 Quit: 179 if (!Success) 180 { 181 /* Unload the VDD DLL */ 182 if (hDll) FreeLibrary(hDll); 183 184 /* Set the Carry Flag to indicate that an error happened */ 185 setCF(1); 186 } 187 // else 188 // { 189 // /* Clear the Carry Flag (success) */ 190 // setCF(0); 191 // } 192 setAX(RetVal); 193 break; 194 } 195 196 /* UnRegisterModule */ 197 case 1: 198 { 199 WORD Handle = getAX(); 200 WORD Entry = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry 201 202 DPRINT("UnRegisterModule() called\n"); 203 204 /* Sanity checks */ 205 if (!IS_VALID_HANDLE(Handle) || VDDList[Entry].hDll == NULL) 206 { 207 DPRINT1("Invalid VDD DLL Handle: %d\n", Entry); 208 /* Stop the VDM */ 209 EmulatorTerminate(); 210 return; 211 } 212 213 /* Unregister the VDD DLL */ 214 FreeLibrary(VDDList[Entry].hDll); 215 VDDList[Entry].hDll = NULL; 216 VDDList[Entry].DispatchRoutine = NULL; 217 break; 218 } 219 220 /* DispatchCall */ 221 case 2: 222 { 223 WORD Handle = getAX(); 224 WORD Entry = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry 225 226 DPRINT("DispatchCall() called\n"); 227 228 /* Sanity checks */ 229 if (!IS_VALID_HANDLE(Handle) || 230 VDDList[Entry].hDll == NULL || 231 VDDList[Entry].DispatchRoutine == NULL) 232 { 233 DPRINT1("Invalid VDD DLL Handle: %d\n", Entry); 234 /* Stop the VDM */ 235 EmulatorTerminate(); 236 return; 237 } 238 239 /* Call the dispatch routine */ 240 VDDList[Entry].DispatchRoutine(); 241 break; 242 } 243 244 default: 245 { 246 DPRINT1("Unknown 3rd-party VDD BOP Function: 0x%02X\n", FuncNum); 247 setCF(1); 248 break; 249 } 250 } 251 } 252 253 static BOOL LoadInstallableVDD(VOID) 254 { 255 // FIXME: These strings should be localized. 256 #define ERROR_MEMORYVDD L"Insufficient memory to load installable Virtual Device Drivers." 257 #define ERROR_REGVDD L"Virtual Device Driver format in the registry is invalid." 258 #define ERROR_LOADVDD L"An installable Virtual Device Driver failed Dll initialization." 259 260 BOOL Success = TRUE; 261 LONG Error = 0; 262 DWORD Type = 0; 263 DWORD BufSize = 0; 264 265 HKEY hVDDKey; 266 LPCWSTR VDDKeyName = L"SYSTEM\\CurrentControlSet\\Control\\VirtualDeviceDrivers"; 267 LPWSTR VDDValueName = L"VDD"; 268 LPWSTR VDDList = NULL; 269 270 HANDLE hVDD; 271 272 /* Try to open the VDD registry key */ 273 Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 274 VDDKeyName, 275 0, 276 KEY_QUERY_VALUE, 277 &hVDDKey); 278 if (Error == ERROR_FILE_NOT_FOUND) 279 { 280 /* If the key just doesn't exist, don't do anything else */ 281 return TRUE; 282 } 283 else if (Error != ERROR_SUCCESS) 284 { 285 /* The key exists but there was an access error: display an error and quit */ 286 DisplayMessage(ERROR_REGVDD); 287 return FALSE; 288 } 289 290 /* 291 * Retrieve the size of the VDD registry value 292 * and check that it's of REG_MULTI_SZ type. 293 */ 294 Error = RegQueryValueExW(hVDDKey, 295 VDDValueName, 296 NULL, 297 &Type, 298 NULL, 299 &BufSize); 300 if (Error == ERROR_FILE_NOT_FOUND) 301 { 302 /* If the value just doesn't exist, don't do anything else */ 303 Success = TRUE; 304 goto Quit; 305 } 306 else if (Error != ERROR_SUCCESS || Type != REG_MULTI_SZ) 307 { 308 /* 309 * The value exists but there was an access error or 310 * is of the wrong type: display an error and quit. 311 */ 312 DisplayMessage(ERROR_REGVDD); 313 Success = FALSE; 314 goto Quit; 315 } 316 317 /* Allocate the buffer */ 318 BufSize = (BufSize < 2*sizeof(WCHAR) ? 2*sizeof(WCHAR) : BufSize); 319 VDDList = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, BufSize); 320 if (VDDList == NULL) 321 { 322 DisplayMessage(ERROR_MEMORYVDD); 323 Success = FALSE; 324 goto Quit; 325 } 326 327 /* Retrieve the list of VDDs to load */ 328 if (RegQueryValueExW(hVDDKey, 329 VDDValueName, 330 NULL, 331 NULL, 332 (LPBYTE)VDDList, 333 &BufSize) != ERROR_SUCCESS) 334 { 335 DisplayMessage(ERROR_REGVDD); 336 Success = FALSE; 337 goto Quit; 338 } 339 340 /* Load the VDDs */ 341 VDDValueName = VDDList; 342 while (*VDDList) 343 { 344 DPRINT1("Loading VDD '%S'... ", VDDList); 345 hVDD = LoadLibraryW(VDDList); 346 if (hVDD == NULL) 347 { 348 DbgPrint("Failed\n"); 349 DisplayMessage(ERROR_LOADVDD); 350 } 351 else 352 { 353 DbgPrint("Succeeded\n"); 354 } 355 /* Go to next string */ 356 VDDList += wcslen(VDDList) + 1; 357 } 358 VDDList = VDDValueName; 359 360 Quit: 361 if (VDDList) RtlFreeHeap(RtlGetProcessHeap(), 0, VDDList); 362 RegCloseKey(hVDDKey); 363 return Success; 364 } 365 366 /* PUBLIC FUNCTIONS ***********************************************************/ 367 368 /* 369 * NOTE: This function can be called multiple times by the same VDD, if 370 * it wants to install different hooks for a same action. The most recent 371 * registered hooks are called first. 372 */ 373 BOOL 374 WINAPI 375 VDDInstallUserHook(IN HANDLE hVdd, 376 IN PFNVDD_UCREATE Ucr_Handler, 377 IN PFNVDD_UTERMINATE Uterm_Handler, 378 IN PFNVDD_UBLOCK Ublock_Handler, 379 IN PFNVDD_URESUME Uresume_Handler) 380 { 381 PVDD_USER_HANDLERS UserHook; 382 383 /* Check validity of the VDD handle */ 384 if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE) 385 { 386 SetLastError(ERROR_INVALID_PARAMETER); 387 return FALSE; 388 } 389 390 // NOTE: If we want that a VDD can install hooks only once, it's here 391 // that we need to check whether a hook entry is already registered. 392 393 /* Create and initialize a new hook entry... */ 394 UserHook = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*UserHook)); 395 if (UserHook == NULL) 396 { 397 SetLastError(ERROR_OUTOFMEMORY); 398 return FALSE; 399 } 400 401 UserHook->hVdd = hVdd; 402 UserHook->Ucr_Handler = Ucr_Handler; 403 UserHook->Uterm_Handler = Uterm_Handler; 404 UserHook->Ublock_Handler = Ublock_Handler; 405 UserHook->Uresume_Handler = Uresume_Handler; 406 407 /* ... and add it at the top of the list of hooks */ 408 InsertHeadList(&VddUserHooksList, &UserHook->Entry); 409 410 return TRUE; 411 } 412 413 /* 414 * NOTE: This function uninstalls the latest installed hooks for a given VDD. 415 * It can be called multiple times by the same VDD to uninstall many hooks 416 * installed by multiple invocations of VDDInstallUserHook. 417 */ 418 BOOL 419 WINAPI 420 VDDDeInstallUserHook(IN HANDLE hVdd) 421 { 422 PLIST_ENTRY Pointer; 423 PVDD_USER_HANDLERS UserHook; 424 425 /* Check validity of the VDD handle */ 426 if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE) 427 { 428 SetLastError(ERROR_INVALID_PARAMETER); 429 return FALSE; 430 } 431 432 /* Uninstall the latest installed hooks */ 433 for (Pointer = VddUserHooksList.Flink; Pointer != &VddUserHooksList; Pointer = Pointer->Flink) 434 { 435 UserHook = CONTAINING_RECORD(Pointer, VDD_USER_HANDLERS, Entry); 436 if (UserHook->hVdd == hVdd) 437 { 438 RemoveEntryList(&UserHook->Entry); 439 RtlFreeHeap(RtlGetProcessHeap(), 0, UserHook); 440 return TRUE; 441 } 442 } 443 444 SetLastError(ERROR_INVALID_PARAMETER); 445 return FALSE; 446 } 447 448 /* 449 * Internal functions for calling the VDD user hooks. 450 * Their names come directly from the Windows 2kX DDK. 451 */ 452 453 VOID VDDCreateUserHook(USHORT DosPDB) 454 { 455 PLIST_ENTRY Pointer; 456 PVDD_USER_HANDLERS UserHook; 457 458 /* Call the hooks starting from the most recent ones */ 459 for (Pointer = VddUserHooksList.Flink; Pointer != &VddUserHooksList; Pointer = Pointer->Flink) 460 { 461 UserHook = CONTAINING_RECORD(Pointer, VDD_USER_HANDLERS, Entry); 462 if (UserHook->Ucr_Handler) UserHook->Ucr_Handler(DosPDB); 463 } 464 } 465 466 VOID VDDTerminateUserHook(USHORT DosPDB) 467 { 468 PLIST_ENTRY Pointer; 469 PVDD_USER_HANDLERS UserHook; 470 471 /* Call the hooks starting from the most recent ones */ 472 for (Pointer = VddUserHooksList.Flink; Pointer != &VddUserHooksList; Pointer = Pointer->Flink) 473 { 474 UserHook = CONTAINING_RECORD(Pointer, VDD_USER_HANDLERS, Entry); 475 if (UserHook->Uterm_Handler) UserHook->Uterm_Handler(DosPDB); 476 } 477 } 478 479 VOID VDDBlockUserHook(VOID) 480 { 481 PLIST_ENTRY Pointer; 482 PVDD_USER_HANDLERS UserHook; 483 484 /* Call the hooks starting from the most recent ones */ 485 for (Pointer = VddUserHooksList.Flink; Pointer != &VddUserHooksList; Pointer = Pointer->Flink) 486 { 487 UserHook = CONTAINING_RECORD(Pointer, VDD_USER_HANDLERS, Entry); 488 if (UserHook->Ublock_Handler) UserHook->Ublock_Handler(); 489 } 490 } 491 492 VOID VDDResumeUserHook(VOID) 493 { 494 PLIST_ENTRY Pointer; 495 PVDD_USER_HANDLERS UserHook; 496 497 /* Call the hooks starting from the most recent ones */ 498 for (Pointer = VddUserHooksList.Flink; Pointer != &VddUserHooksList; Pointer = Pointer->Flink) 499 { 500 UserHook = CONTAINING_RECORD(Pointer, VDD_USER_HANDLERS, Entry); 501 if (UserHook->Uresume_Handler) UserHook->Uresume_Handler(); 502 } 503 } 504 505 506 VOID VDDSupInitialize(VOID) 507 { 508 /* Register the 3rd-party VDD BOP Handler */ 509 RegisterBop(BOP_3RDPARTY, ThirdPartyVDDBop); 510 511 /* Load the installable VDDs from the registry */ 512 LoadInstallableVDD(); 513 } 514 515 /* EOF */ 516