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