[NTVDM]
[reactos.git] / reactos / subsystems / ntvdm / vddsup.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: vddsup.c
5 * PURPOSE: Virtual Device Drivers (VDD) Support
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14 #include "vddsup.h"
15
16 #include "cpu/bop.h"
17 #include <isvbop.h>
18
19 typedef VOID (WINAPI *VDD_PROC)(VOID);
20
21 typedef struct _VDD_MODULE
22 {
23 HMODULE hDll;
24 VDD_PROC DispatchRoutine;
25 } VDD_MODULE, *PVDD_MODULE;
26
27 /* PRIVATE VARIABLES **********************************************************/
28
29 // TODO: Maybe use a linked list.
30 // But the number of elements must be <= MAXUSHORT (MAXWORD)
31 #define MAX_VDD_MODULES 0xFF + 1
32 VDD_MODULE VDDList[MAX_VDD_MODULES] = {{NULL}};
33
34 // Valid handles of VDD DLLs start at 1 and finish at MAX_VDD_MODULES
35 #define ENTRY_TO_HANDLE(Entry) ((Entry) + 1)
36 #define HANDLE_TO_ENTRY(Handle) ((Handle) - 1)
37 #define IS_VALID_HANDLE(Handle) ((Handle) > 0 && (Handle) <= MAX_VDD_MODULES)
38
39 /* PRIVATE FUNCTIONS **********************************************************/
40
41 USHORT GetNextFreeVDDEntry(VOID)
42 {
43 USHORT Entry = MAX_VDD_MODULES;
44 for (Entry = 0; Entry < sizeof(VDDList)/sizeof(VDDList[0]); ++Entry)
45 {
46 if (VDDList[Entry].hDll == NULL) break;
47 }
48 return Entry;
49 }
50
51 VOID WINAPI ThirdPartyVDDBop(LPWORD Stack)
52 {
53 /* Get the Function Number and skip it */
54 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
55 setIP(getIP() + 1);
56
57 switch (FuncNum)
58 {
59 /* RegisterModule */
60 case 0:
61 {
62 BOOL Success = TRUE;
63 WORD RetVal = 0;
64 WORD Entry = 0;
65 LPCSTR DllName = NULL,
66 InitRoutineName = NULL,
67 DispatchRoutineName = NULL;
68 HMODULE hDll = NULL;
69 VDD_PROC InitRoutine = NULL,
70 DispatchRoutine = NULL;
71
72 DPRINT("RegisterModule() called\n");
73
74 /* Clear the Carry Flag (no error happened so far) */
75 setCF(0);
76
77 /* Retrieve the next free entry in the table (used later on) */
78 Entry = GetNextFreeVDDEntry();
79 if (Entry >= MAX_VDD_MODULES)
80 {
81 DPRINT1("Failed to create a new VDD module entry\n");
82 Success = FALSE;
83 RetVal = 4;
84 goto Quit;
85 }
86
87 /* Retrieve the VDD name in DS:SI */
88 DllName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI());
89
90 /* Retrieve the initialization routine API name in ES:DI (optional --> ES=DI=0) */
91 if (TO_LINEAR(getES(), getDI()) != 0)
92 InitRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getES(), getDI());
93
94 /* Retrieve the dispatch routine API name in DS:BX */
95 DispatchRoutineName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getBX());
96
97 DPRINT1("DllName = '%s' - InitRoutineName = '%s' - DispatchRoutineName = '%s'\n",
98 (DllName ? DllName : "n/a"),
99 (InitRoutineName ? InitRoutineName : "n/a"),
100 (DispatchRoutineName ? DispatchRoutineName : "n/a"));
101
102 /* Load the VDD DLL */
103 hDll = LoadLibraryA(DllName);
104 if (hDll == NULL)
105 {
106 DWORD LastError = GetLastError();
107 Success = FALSE;
108
109 if (LastError == ERROR_NOT_ENOUGH_MEMORY)
110 {
111 DPRINT1("Not enough memory to load DLL '%s'\n", DllName);
112 RetVal = 4;
113 goto Quit;
114 }
115 else
116 {
117 DPRINT1("Failed to load DLL '%s'; last error = %d\n", DllName, LastError);
118 RetVal = 1;
119 goto Quit;
120 }
121 }
122
123 /* Load the initialization routine if needed */
124 if (InitRoutineName)
125 {
126 InitRoutine = (VDD_PROC)GetProcAddress(hDll, InitRoutineName);
127 if (InitRoutine == NULL)
128 {
129 DPRINT1("Failed to load the initialization routine '%s'\n", InitRoutineName);
130 Success = FALSE;
131 RetVal = 3;
132 goto Quit;
133 }
134 }
135
136 /* Load the dispatch routine */
137 DispatchRoutine = (VDD_PROC)GetProcAddress(hDll, DispatchRoutineName);
138 if (DispatchRoutine == NULL)
139 {
140 DPRINT1("Failed to load the dispatch routine '%s'\n", DispatchRoutineName);
141 Success = FALSE;
142 RetVal = 2;
143 goto Quit;
144 }
145
146 /* If we arrived there, that means everything is OK */
147
148 /* Register the VDD DLL */
149 VDDList[Entry].hDll = hDll;
150 VDDList[Entry].DispatchRoutine = DispatchRoutine;
151
152 /* Call the initialization routine if needed */
153 if (InitRoutine) InitRoutine();
154
155 /* We succeeded. RetVal will contain a valid VDD DLL handle */
156 Success = TRUE;
157 RetVal = ENTRY_TO_HANDLE(Entry); // Convert the entry to a valid handle
158
159 Quit:
160 if (!Success)
161 {
162 /* Unload the VDD DLL */
163 if (hDll) FreeLibrary(hDll);
164
165 /* Set the Carry Flag to indicate that an error happened */
166 setCF(1);
167 }
168 // else
169 // {
170 // /* Clear the Carry Flag (success) */
171 // setCF(0);
172 // }
173 setAX(RetVal);
174 break;
175 }
176
177 /* UnRegisterModule */
178 case 1:
179 {
180 WORD Handle = getAX();
181 WORD Entry = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry
182
183 DPRINT("UnRegisterModule() called\n");
184
185 /* Sanity checks */
186 if (!IS_VALID_HANDLE(Handle) || VDDList[Entry].hDll == NULL)
187 {
188 DPRINT1("Invalid VDD DLL Handle: %d\n", Entry);
189 /* Stop the VDM */
190 EmulatorTerminate();
191 return;
192 }
193
194 /* Unregister the VDD DLL */
195 FreeLibrary(VDDList[Entry].hDll);
196 VDDList[Entry].hDll = NULL;
197 VDDList[Entry].DispatchRoutine = NULL;
198 break;
199 }
200
201 /* DispatchCall */
202 case 2:
203 {
204 WORD Handle = getAX();
205 WORD Entry = HANDLE_TO_ENTRY(Handle); // Convert the handle to a valid entry
206
207 DPRINT("DispatchCall() called\n");
208
209 /* Sanity checks */
210 if (!IS_VALID_HANDLE(Handle) ||
211 VDDList[Entry].hDll == NULL ||
212 VDDList[Entry].DispatchRoutine == NULL)
213 {
214 DPRINT1("Invalid VDD DLL Handle: %d\n", Entry);
215 /* Stop the VDM */
216 EmulatorTerminate();
217 return;
218 }
219
220 /* Call the dispatch routine */
221 VDDList[Entry].DispatchRoutine();
222 break;
223 }
224
225 default:
226 {
227 DPRINT1("Unknown 3rd-party VDD BOP Function: 0x%02X\n", FuncNum);
228 setCF(1);
229 break;
230 }
231 }
232 }
233
234 BOOL LoadInstallableVDD(VOID)
235 {
236 #define ERROR_MEMORYVDD L"Insufficient memory to load installable Virtual Device Drivers."
237 #define ERROR_REGVDD L"Virtual Device Driver format in the registry is invalid."
238 #define ERROR_LOADVDD L"An installable Virtual Device Driver failed Dll initialization."
239
240 BOOL Success = TRUE;
241 LONG Error = 0;
242 DWORD Type = 0;
243 DWORD BufSize = 0;
244
245 HKEY hVDDKey;
246 LPCWSTR VDDKeyName = L"SYSTEM\\CurrentControlSet\\Control\\VirtualDeviceDrivers";
247 LPWSTR VDDValueName = L"VDD";
248 LPWSTR VDDList = NULL;
249
250 HANDLE hVDD;
251
252 /* Try to open the VDD registry key */
253 Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
254 VDDKeyName,
255 0,
256 KEY_QUERY_VALUE,
257 &hVDDKey);
258 if (Error == ERROR_FILE_NOT_FOUND)
259 {
260 /* If the key just doesn't exist, don't do anything else */
261 return TRUE;
262 }
263 else if (Error != ERROR_SUCCESS)
264 {
265 /* The key exists but there was an access error: display an error and quit */
266 DisplayMessage(ERROR_REGVDD);
267 return FALSE;
268 }
269
270 /*
271 * Retrieve the size of the VDD registry value
272 * and check that it's of REG_MULTI_SZ type.
273 */
274 Error = RegQueryValueExW(hVDDKey,
275 VDDValueName,
276 NULL,
277 &Type,
278 NULL,
279 &BufSize);
280 if (Error == ERROR_FILE_NOT_FOUND)
281 {
282 /* If the value just doesn't exist, don't do anything else */
283 Success = TRUE;
284 goto Quit;
285 }
286 else if (Error != ERROR_SUCCESS || Type != REG_MULTI_SZ)
287 {
288 /*
289 * The value exists but there was an access error or
290 * is of the wrong type: display an error and quit.
291 */
292 DisplayMessage(ERROR_REGVDD);
293 Success = FALSE;
294 goto Quit;
295 }
296
297 /* Allocate the buffer */
298 BufSize = (BufSize < 2*sizeof(WCHAR) ? 2*sizeof(WCHAR) : BufSize);
299 VDDList = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BufSize);
300 if (VDDList == NULL)
301 {
302 DisplayMessage(ERROR_MEMORYVDD);
303 Success = FALSE;
304 goto Quit;
305 }
306
307 /* Retrieve the list of VDDs to load */
308 if (RegQueryValueExW(hVDDKey,
309 VDDValueName,
310 NULL,
311 NULL,
312 (LPBYTE)VDDList,
313 &BufSize) != ERROR_SUCCESS)
314 {
315 DisplayMessage(ERROR_REGVDD);
316 Success = FALSE;
317 goto Quit;
318 }
319
320 /* Load the VDDs */
321 VDDValueName = VDDList;
322 while (*VDDList)
323 {
324 DPRINT1("Loading VDD '%S'...", VDDList);
325 hVDD = LoadLibraryW(VDDList);
326 if (hVDD == NULL)
327 {
328 DbgPrint("Failed\n");
329 DisplayMessage(ERROR_LOADVDD);
330 }
331 else
332 {
333 DbgPrint("Succeeded\n");
334 }
335 /* Go to next string */
336 VDDList += wcslen(VDDList) + 1;
337 }
338 VDDList = VDDValueName;
339
340 Quit:
341 if (VDDList) HeapFree(GetProcessHeap(), 0, VDDList);
342 RegCloseKey(hVDDKey);
343 return Success;
344 }
345
346 /* PUBLIC FUNCTIONS ***********************************************************/
347
348 VOID VDDSupInitialize(VOID)
349 {
350 /* Register the 3rd-party VDD BOP Handler */
351 RegisterBop(BOP_3RDPARTY, ThirdPartyVDDBop);
352
353 /* Load the installable VDDs from the registry */
354 LoadInstallableVDD();
355 }
356
357 /* EOF */