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