wined3d: Don't crash during initialization if the adapter doesn't have full GL info.
[wine] / dlls / krnl386.exe16 / vxd.c
1 /*
2  * Win32 VxD functions
3  *
4  * Copyright 1998 Marcus Meissner
5  * Copyright 1998 Ulrich Weigand
6  * Copyright 1998 Patrik Stridvall
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <stdlib.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <sys/types.h>
31 #ifdef HAVE_SYS_STAT_H
32 # include <sys/stat.h>
33 #endif
34 #include <string.h>
35 #include <stdarg.h>
36
37 #define NONAMELESSUNION
38 #define NONAMELESSSTRUCT
39 #include "ntstatus.h"
40 #define WIN32_NO_STATUS
41 #include "windef.h"
42 #include "winbase.h"
43 #include "winerror.h"
44 #include "winternl.h"
45 #include "winioctl.h"
46 #include "kernel16_private.h"
47 #include "dosexe.h"
48 #include "wine/library.h"
49 #include "wine/unicode.h"
50 #include "wine/server.h"
51 #include "wine/debug.h"
52
53 WINE_DEFAULT_DEBUG_CHANNEL(vxd);
54
55 typedef DWORD (WINAPI *VxDCallProc)(DWORD, CONTEXT86 *);
56 typedef BOOL (WINAPI *DeviceIoProc)(DWORD, LPVOID, DWORD, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
57
58 struct vxd_module
59 {
60     LARGE_INTEGER index;
61     HANDLE        handle;
62     HMODULE       module;
63     DeviceIoProc  proc;
64 };
65
66 struct vxdcall_service
67 {
68     WCHAR       name[12];
69     DWORD       service;
70     HMODULE     module;
71     VxDCallProc proc;
72 };
73
74 #define MAX_VXD_MODULES 32
75
76 static struct vxd_module vxd_modules[MAX_VXD_MODULES];
77
78 static struct vxdcall_service vxd_services[] =
79 {
80     { {'v','m','m','.','v','x','d',0},             0x0001, NULL, NULL },
81     { {'v','w','i','n','3','2','.','v','x','d',0}, 0x002a, NULL, NULL }
82 };
83
84 #define NB_VXD_SERVICES  (sizeof(vxd_services)/sizeof(vxd_services[0]))
85
86 #define W32S_APP2WINE(addr) ((addr)? (DWORD)(addr) + W32S_offset : 0)
87 #define W32S_WINE2APP(addr) ((addr)? (DWORD)(addr) - W32S_offset : 0)
88
89 #define VXD_BARF(context,name) \
90     TRACE( "vxd %s: unknown/not implemented parameters:\n" \
91                      "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
92                      "SI %04x, DI %04x, DS %04x, ES %04x\n", \
93              (name), (name), AX_reg(context), BX_reg(context), \
94              CX_reg(context), DX_reg(context), SI_reg(context), \
95              DI_reg(context), (WORD)context->SegDs, (WORD)context->SegEs )
96
97 static CRITICAL_SECTION vxd_section;
98 static CRITICAL_SECTION_DEBUG critsect_debug =
99 {
100     0, 0, &vxd_section,
101     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
102       0, 0, { (DWORD_PTR)(__FILE__ ": vxd_section") }
103 };
104 static CRITICAL_SECTION vxd_section = { &critsect_debug, -1, 0, 0, 0, 0 };
105
106 static UINT W32S_offset;
107
108 static WORD VXD_WinVersion(void)
109 {
110     WORD version = LOWORD(GetVersion16());
111     return (version >> 8) | (version << 8);
112 }
113
114 /* create a file handle to represent a VxD, by opening a dummy file in the wineserver directory */
115 static HANDLE open_vxd_handle( LPCWSTR name )
116 {
117     static const WCHAR prefixW[] = {'\\','?','?','\\','u','n','i','x'};
118     const char *dir = wine_get_server_dir();
119     int len;
120     HANDLE ret;
121     NTSTATUS status;
122     OBJECT_ATTRIBUTES attr;
123     UNICODE_STRING nameW;
124     IO_STATUS_BLOCK io;
125
126     len = MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, NULL, 0 );
127     nameW.Length = sizeof(prefixW) + (len + strlenW( name )) * sizeof(WCHAR);
128     nameW.MaximumLength = nameW.Length + sizeof(WCHAR);
129     if (!(nameW.Buffer = HeapAlloc( GetProcessHeap(), 0, nameW.MaximumLength )))
130     {
131         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
132         return 0;
133     }
134     memcpy( nameW.Buffer, prefixW, sizeof(prefixW) );
135     MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, nameW.Buffer + sizeof(prefixW)/sizeof(WCHAR), len );
136     len += sizeof(prefixW) / sizeof(WCHAR);
137     nameW.Buffer[len-1] = '/';
138     strcpyW( nameW.Buffer + len, name );
139
140     attr.Length = sizeof(attr);
141     attr.RootDirectory = 0;
142     attr.Attributes = 0;
143     attr.ObjectName = &nameW;
144     attr.SecurityDescriptor = NULL;
145     attr.SecurityQualityOfService = NULL;
146
147     status = NtCreateFile( &ret, 0, &attr, &io, NULL, 0,
148                            FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF,
149                            FILE_SYNCHRONOUS_IO_ALERT, NULL, 0 );
150     if (status)
151     {
152         ret = 0;
153         SetLastError( RtlNtStatusToDosError(status) );
154     }
155     RtlFreeUnicodeString( &nameW );
156     return ret;
157 }
158
159 /* retrieve the DeviceIoControl function for a Vxd given a file handle */
160 DeviceIoProc __wine_vxd_get_proc( HANDLE handle )
161 {
162     DeviceIoProc ret = NULL;
163     int status, i;
164     IO_STATUS_BLOCK io;
165     FILE_INTERNAL_INFORMATION info;
166
167     status = NtQueryInformationFile( handle, &io, &info, sizeof(info), FileInternalInformation );
168     if (status)
169     {
170         SetLastError( RtlNtStatusToDosError(status) );
171         return NULL;
172     }
173
174     RtlEnterCriticalSection( &vxd_section );
175
176     for (i = 0; i < MAX_VXD_MODULES; i++)
177     {
178         if (!vxd_modules[i].module) break;
179         if (vxd_modules[i].index.QuadPart == info.IndexNumber.QuadPart)
180         {
181             if (!(ret = vxd_modules[i].proc)) SetLastError( ERROR_INVALID_FUNCTION );
182             goto done;
183         }
184     }
185     /* FIXME: Here we could go through the directory to find the VxD name and load it. */
186     /* Let's wait to find out if there are actually apps out there that try to share   */
187     /* VxD handles between processes, before we go to the trouble of implementing it.  */
188     ERR( "handle %p not found in module list, inherited from another process?\n", handle );
189
190 done:
191     RtlLeaveCriticalSection( &vxd_section );
192     return ret;
193 }
194
195
196 /* load a VxD and return a file handle to it */
197 HANDLE __wine_vxd_open( LPCWSTR filenameW, DWORD access, SECURITY_ATTRIBUTES *sa )
198 {
199     static const WCHAR dotVxDW[] = {'.','v','x','d',0};
200     int i;
201     HANDLE handle;
202     HMODULE module;
203     WCHAR *p, name[16];
204
205     /* normalize the filename */
206
207     if (strlenW( filenameW ) >= sizeof(name)/sizeof(WCHAR) - 4 ||
208         strchrW( filenameW, '/' ) || strchrW( filenameW, '\\' ))
209     {
210         SetLastError( ERROR_FILE_NOT_FOUND );
211         return 0;
212     }
213     strcpyW( name, filenameW );
214     strlwrW( name );
215     p = strchrW( name, '.' );
216     if (!p) strcatW( name, dotVxDW );
217     else if (strcmpiW( p, dotVxDW ))  /* existing extension has to be .vxd */
218     {
219         SetLastError( ERROR_FILE_NOT_FOUND );
220         return 0;
221     }
222
223     /* try to load the module first */
224
225     if (!(module = LoadLibraryW( name )))
226     {
227         FIXME( "Unknown/unsupported VxD %s. Try setting Windows version to 'nt40' or 'win31'.\n",
228                debugstr_w(name) );
229         SetLastError( ERROR_FILE_NOT_FOUND );
230         return 0;
231     }
232
233     /* register the module in the global list if necessary */
234
235     RtlEnterCriticalSection( &vxd_section );
236
237     for (i = 0; i < MAX_VXD_MODULES; i++)
238     {
239         if (vxd_modules[i].module == module)
240         {
241             handle = vxd_modules[i].handle;
242             goto done;  /* already registered */
243         }
244         if (!vxd_modules[i].module)  /* new one, register it */
245         {
246             IO_STATUS_BLOCK io;
247             FILE_INTERNAL_INFORMATION info;
248
249             /* get a file handle to the dummy file */
250             if (!(handle = open_vxd_handle( name )))
251             {
252                 FreeLibrary( module );
253                 goto done;
254             }
255             if (!NtQueryInformationFile( handle, &io, &info, sizeof(info), FileInternalInformation ))
256                 vxd_modules[i].index = info.IndexNumber;
257
258             vxd_modules[i].module = module;
259             vxd_modules[i].handle = handle;
260             vxd_modules[i].proc = (DeviceIoProc)GetProcAddress( module, "DeviceIoControl" );
261             goto done;
262         }
263     }
264
265     ERR("too many open VxD modules, please report\n" );
266     FreeLibrary( module );
267     handle = 0;
268
269 done:
270     RtlLeaveCriticalSection( &vxd_section );
271     if (!DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), &handle, 0,
272                           (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle),
273                           DUP_HANDLE_SAME_ACCESS ))
274         handle = 0;
275     return handle;
276 }
277
278
279 /***********************************************************************
280  *              VxDCall0 (KERNEL32.1)
281  *              VxDCall1 (KERNEL32.2)
282  *              VxDCall2 (KERNEL32.3)
283  *              VxDCall3 (KERNEL32.4)
284  *              VxDCall4 (KERNEL32.5)
285  *              VxDCall5 (KERNEL32.6)
286  *              VxDCall6 (KERNEL32.7)
287  *              VxDCall7 (KERNEL32.8)
288  *              VxDCall8 (KERNEL32.9)
289  */
290 void WINAPI __regs_VxDCall( DWORD service, CONTEXT86 *context )
291 {
292     unsigned int i;
293     VxDCallProc proc = NULL;
294
295     RtlEnterCriticalSection( &vxd_section );
296     for (i = 0; i < NB_VXD_SERVICES; i++)
297     {
298         if (HIWORD(service) != vxd_services[i].service) continue;
299         if (!vxd_services[i].module)  /* need to load it */
300         {
301             if ((vxd_services[i].module = LoadLibraryW( vxd_services[i].name )))
302                 vxd_services[i].proc = (VxDCallProc)GetProcAddress( vxd_services[i].module, "VxDCall" );
303         }
304         proc = vxd_services[i].proc;
305         break;
306     }
307     RtlLeaveCriticalSection( &vxd_section );
308
309     if (proc) context->Eax = proc( service, context );
310     else
311     {
312         FIXME( "Unknown/unimplemented VxD (%08x)\n", service);
313         context->Eax = 0xffffffff; /* FIXME */
314     }
315 }
316 #ifdef DEFINE_REGS_ENTRYPOINT
317 DEFINE_REGS_ENTRYPOINT( VxDCall, 1 )
318 #endif
319
320
321 /***********************************************************************
322  *           __wine_vxd_vmm (WPROCS.401)
323  */
324 void WINAPI __wine_vxd_vmm ( CONTEXT86 *context )
325 {
326     unsigned service = AX_reg(context);
327
328     TRACE("[%04x] VMM\n", (UINT16)service);
329
330     switch(service)
331     {
332     case 0x0000: /* version */
333         SET_AX( context, VXD_WinVersion() );
334         RESET_CFLAG(context);
335         break;
336
337     case 0x026d: /* Get_Debug_Flag '/m' */
338     case 0x026e: /* Get_Debug_Flag '/n' */
339         SET_AL( context, 0 );
340         RESET_CFLAG(context);
341         break;
342
343     default:
344         VXD_BARF( context, "VMM" );
345     }
346 }
347
348 /***********************************************************************
349  *           __wine_vxd_pagefile (WPROCS.433)
350  */
351 void WINAPI __wine_vxd_pagefile( CONTEXT86 *context )
352 {
353     unsigned    service = AX_reg(context);
354
355     /* taken from Ralf Brown's Interrupt List */
356
357     TRACE("[%04x] PageFile\n", (UINT16)service );
358
359     switch(service)
360     {
361     case 0x00: /* get version, is this windows version? */
362         TRACE("returning version\n");
363         SET_AX( context, VXD_WinVersion() );
364         RESET_CFLAG(context);
365         break;
366
367     case 0x01: /* get swap file info */
368         TRACE("VxD PageFile: returning swap file info\n");
369         SET_AX( context, 0x00 ); /* paging disabled */
370         context->Ecx = 0;   /* maximum size of paging file */
371         /* FIXME: do I touch DS:SI or DS:DI? */
372         RESET_CFLAG(context);
373         break;
374
375     case 0x02: /* delete permanent swap on exit */
376         TRACE("VxD PageFile: supposed to delete swap\n");
377         RESET_CFLAG(context);
378         break;
379
380     case 0x03: /* current temporary swap file size */
381         TRACE("VxD PageFile: what is current temp. swap size\n");
382         RESET_CFLAG(context);
383         break;
384
385     case 0x04: /* read or write?? INTERRUP.D */
386     case 0x05: /* cancel?? INTERRUP.D */
387     case 0x06: /* test I/O valid INTERRUP.D */
388     default:
389         VXD_BARF( context, "pagefile" );
390         break;
391     }
392 }
393
394 /***********************************************************************
395  *           __wine_vxd_reboot (WPROCS.409)
396  */
397 void WINAPI __wine_vxd_reboot( CONTEXT86 *context )
398 {
399     unsigned service = AX_reg(context);
400
401     TRACE("[%04x] Reboot\n", (UINT16)service);
402
403     switch(service)
404     {
405     case 0x0000: /* version */
406         SET_AX( context, VXD_WinVersion() );
407         RESET_CFLAG(context);
408         break;
409
410     default:
411         VXD_BARF( context, "REBOOT" );
412     }
413 }
414
415 /***********************************************************************
416  *           __wine_vxd_vdd (WPROCS.410)
417  */
418 void WINAPI __wine_vxd_vdd( CONTEXT86 *context )
419 {
420     unsigned service = AX_reg(context);
421
422     TRACE("[%04x] VDD\n", (UINT16)service);
423
424     switch(service)
425     {
426     case 0x0000: /* version */
427         SET_AX( context, VXD_WinVersion() );
428         RESET_CFLAG(context);
429         break;
430
431     default:
432         VXD_BARF( context, "VDD" );
433     }
434 }
435
436 /***********************************************************************
437  *           __wine_vxd_vmd (WPROCS.412)
438  */
439 void WINAPI __wine_vxd_vmd( CONTEXT86 *context )
440 {
441     unsigned service = AX_reg(context);
442
443     TRACE("[%04x] VMD\n", (UINT16)service);
444
445     switch(service)
446     {
447     case 0x0000: /* version */
448         SET_AX( context, VXD_WinVersion() );
449         RESET_CFLAG(context);
450         break;
451
452     default:
453         VXD_BARF( context, "VMD" );
454     }
455 }
456
457 /***********************************************************************
458  *           __wine_vxd_vxdloader (WPROCS.439)
459  */
460 void WINAPI __wine_vxd_vxdloader( CONTEXT86 *context )
461 {
462     unsigned service = AX_reg(context);
463
464     TRACE("[%04x] VXDLoader\n", (UINT16)service);
465
466     switch (service)
467     {
468     case 0x0000: /* get version */
469         TRACE("returning version\n");
470         SET_AX( context, 0x0000 );
471         SET_DX( context, VXD_WinVersion() );
472         RESET_CFLAG(context);
473         break;
474
475     case 0x0001: /* load device */
476         FIXME("load device %04x:%04x (%s)\n",
477               context->SegDs, DX_reg(context),
478               debugstr_a(MapSL(MAKESEGPTR(context->SegDs, DX_reg(context)))));
479         SET_AX( context, 0x0000 );
480         context->SegEs = 0x0000;
481         SET_DI( context, 0x0000 );
482         RESET_CFLAG(context);
483         break;
484
485     case 0x0002: /* unload device */
486         FIXME("unload device (%08x)\n", context->Ebx);
487         SET_AX( context, 0x0000 );
488         RESET_CFLAG(context);
489         break;
490
491     default:
492         VXD_BARF( context, "VXDLDR" );
493         SET_AX( context, 0x000B ); /* invalid function number */
494         SET_CFLAG(context);
495         break;
496     }
497 }
498
499 /***********************************************************************
500  *           __wine_vxd_shell (WPROCS.423)
501  */
502 void WINAPI __wine_vxd_shell( CONTEXT86 *context )
503 {
504     unsigned    service = DX_reg(context);
505
506     TRACE("[%04x] Shell\n", (UINT16)service);
507
508     switch (service) /* Ralf Brown says EDX, but I use DX instead */
509     {
510     case 0x0000:
511         TRACE("returning version\n");
512         SET_AX( context, VXD_WinVersion() );
513         context->Ebx = 1; /* system VM Handle */
514         break;
515
516     case 0x0001:
517     case 0x0002:
518     case 0x0003:
519         /* SHELL_SYSMODAL_Message
520         ebx virtual machine handle
521         eax message box flags
522         ecx address of message
523         edi address of caption
524         return response in eax
525         */
526     case 0x0004:
527         /* SHELL_Message
528         ebx virtual machine handle
529         eax message box flags
530         ecx address of message
531         edi address of caption
532         esi address callback
533         edx reference data for callback
534         return response in eax
535         */
536     case 0x0005:
537         VXD_BARF( context, "shell" );
538         break;
539
540     case 0x0006: /* SHELL_Get_VM_State */
541         TRACE("VxD Shell: returning VM state\n");
542         /* Actually we don't, not yet. We have to return a structure
543          * and I am not to sure how to set it up and return it yet,
544          * so for now let's do nothing. I can (hopefully) get this
545          * by the next release
546          */
547         /* RESET_CFLAG(context); */
548         break;
549
550     case 0x0007:
551     case 0x0008:
552     case 0x0009:
553     case 0x000A:
554     case 0x000B:
555     case 0x000C:
556     case 0x000D:
557     case 0x000E:
558     case 0x000F:
559     case 0x0010:
560     case 0x0011:
561     case 0x0012:
562     case 0x0013:
563     case 0x0014:
564     case 0x0015:
565     case 0x0016:
566         VXD_BARF( context, "SHELL" );
567         break;
568
569     /* the new Win95 shell API */
570     case 0x0100:     /* get version */
571         SET_AX( context, VXD_WinVersion() );
572         break;
573
574     case 0x0104:   /* retrieve Hook_Properties list */
575     case 0x0105:   /* call Hook_Properties callbacks */
576         VXD_BARF( context, "SHELL" );
577         break;
578
579     case 0x0106:   /* install timeout callback */
580         TRACE("VxD Shell: ignoring shell callback (%d sec.)\n", context->Ebx);
581         SET_CFLAG(context);
582         break;
583
584     case 0x0107:   /* get version of any VxD */
585     default:
586         VXD_BARF( context, "SHELL" );
587         break;
588     }
589 }
590
591
592 /***********************************************************************
593  *           __wine_vxd_comm (WPROCS.414)
594  */
595 void WINAPI __wine_vxd_comm( CONTEXT86 *context )
596 {
597     unsigned    service = AX_reg(context);
598
599     TRACE("[%04x] Comm\n", (UINT16)service);
600
601     switch (service)
602     {
603     case 0x0000: /* get version */
604         TRACE("returning version\n");
605         SET_AX( context, VXD_WinVersion() );
606         RESET_CFLAG(context);
607         break;
608
609     case 0x0001: /* set port global */
610     case 0x0002: /* get focus */
611     case 0x0003: /* virtualise port */
612     default:
613         VXD_BARF( context, "comm" );
614     }
615 }
616
617 /***********************************************************************
618  *           __wine_vxd_timer (WPROCS.405)
619  */
620 void WINAPI __wine_vxd_timer( CONTEXT86 *context )
621 {
622     unsigned service = AX_reg(context);
623
624     TRACE("[%04x] Virtual Timer\n", (UINT16)service);
625
626     switch(service)
627     {
628     case 0x0000: /* version */
629         SET_AX( context, VXD_WinVersion() );
630         RESET_CFLAG(context);
631         break;
632
633     case 0x0100: /* clock tick time, in 840nsecs */
634         context->Eax = GetTickCount();
635
636         context->Edx = context->Eax >> 22;
637         context->Eax <<= 10; /* not very precise */
638         break;
639
640     case 0x0101: /* current Windows time, msecs */
641     case 0x0102: /* current VM time, msecs */
642         context->Eax = GetTickCount();
643         break;
644
645     default:
646         VXD_BARF( context, "VTD" );
647     }
648 }
649
650
651 /***********************************************************************
652  *           timer_thread
653  */
654 static DWORD CALLBACK timer_thread( void *arg )
655 {
656     DWORD *system_time = arg;
657
658     for (;;)
659     {
660         *system_time = GetTickCount();
661         Sleep( 55 );
662     }
663
664     return 0;
665 }
666
667
668 /***********************************************************************
669  *           __wine_vxd_timerapi (WPROCS.1490)
670  */
671 void WINAPI __wine_vxd_timerapi( CONTEXT86 *context )
672 {
673     static WORD System_Time_Selector;
674
675     unsigned service = AX_reg(context);
676
677     TRACE("[%04x] TimerAPI\n", (UINT16)service);
678
679     switch(service)
680     {
681     case 0x0000: /* version */
682         SET_AX( context, VXD_WinVersion() );
683         RESET_CFLAG(context);
684         break;
685
686     case 0x0009: /* get system time selector */
687         if ( !System_Time_Selector )
688         {
689             HANDLE16 handle = GlobalAlloc16( GMEM_FIXED, sizeof(DWORD) );
690             System_Time_Selector = handle | 7;
691             CloseHandle( CreateThread( NULL, 0, timer_thread, GlobalLock16(handle), 0, NULL ) );
692         }
693         SET_AX( context, System_Time_Selector );
694         RESET_CFLAG(context);
695         break;
696
697     default:
698         VXD_BARF( context, "VTDAPI" );
699     }
700 }
701
702 /***********************************************************************
703  *           __wine_vxd_configmg (WPROCS.451)
704  */
705 void WINAPI __wine_vxd_configmg( CONTEXT86 *context )
706 {
707     unsigned service = AX_reg(context);
708
709     TRACE("[%04x] ConfigMG\n", (UINT16)service);
710
711     switch(service)
712     {
713     case 0x0000: /* version */
714         SET_AX( context, VXD_WinVersion() );
715         RESET_CFLAG(context);
716         break;
717
718     default:
719         VXD_BARF( context, "CONFIGMG" );
720     }
721 }
722
723 /***********************************************************************
724  *           __wine_vxd_enable (WPROCS.455)
725  */
726 void WINAPI __wine_vxd_enable( CONTEXT86 *context )
727 {
728     unsigned service = AX_reg(context);
729
730     TRACE("[%04x] Enable\n", (UINT16)service);
731
732     switch(service)
733     {
734     case 0x0000: /* version */
735         SET_AX( context, VXD_WinVersion() );
736         RESET_CFLAG(context);
737         break;
738
739     default:
740         VXD_BARF( context, "ENABLE" );
741     }
742 }
743
744 /***********************************************************************
745  *           __wine_vxd_apm (WPROCS.438)
746  */
747 void WINAPI __wine_vxd_apm( CONTEXT86 *context )
748 {
749     unsigned service = AX_reg(context);
750
751     TRACE("[%04x] APM\n", (UINT16)service);
752
753     switch(service)
754     {
755     case 0x0000: /* version */
756         SET_AX( context, VXD_WinVersion() );
757         RESET_CFLAG(context);
758         break;
759
760     default:
761         VXD_BARF( context, "APM" );
762     }
763 }
764
765 /***********************************************************************
766  *           __wine_vxd_win32s (WPROCS.445)
767  *
768  * This is an implementation of the services of the Win32s VxD.
769  * Since official documentation of these does not seem to be available,
770  * certain arguments of some of the services remain unclear.
771  *
772  * FIXME: The following services are currently unimplemented:
773  *        Exception handling      (0x01, 0x1C)
774  *        Debugger support        (0x0C, 0x14, 0x17)
775  *        Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
776  *        Memory Statistics       (0x1B)
777  *
778  *
779  * We have a specific problem running Win32s on Linux (and probably also
780  * the other x86 unixes), since Win32s tries to allocate its main 'flat
781  * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
782  * The rationale for this seems to be that they want one the one hand to
783  * be able to leave the Win 3.1 memory (starting with the main DOS memory)
784  * at linear address 0, but want at other hand to have offset 0 of the
785  * flat data/code segment point to an unmapped page (to catch NULL pointer
786  * accesses). Hence they allocate the flat segments with a base of 0xffff0000
787  * so that the Win 3.1 memory area at linear address zero shows up in the
788  * flat segments at offset 0x10000 (since linear addresses wrap around at
789  * 4GB). To compensate for that discrepancy between flat segment offsets
790  * and plain linear addresses, all flat pointers passed between the 32-bit
791  * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
792  * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
793  *
794  * The problem for us is now that Linux does not allow a LDT selector with
795  * base 0xffff0000 to be created, since it would 'see' a part of the kernel
796  * address space. To address this problem we introduce *another* offset:
797  * We add 0x10000 to every linear address we get as an argument from Win32s.
798  * This means especially that the flat code/data selectors get actually
799  * allocated with base 0x0, so that flat offsets and (real) linear addresses
800  * do again agree!  In fact, every call e.g. of a Win32s VxD service now
801  * has all pointer arguments (which are offsets in the flat data segment)
802  * first reduced by 0x10000 by the W32SKRNL glue code, and then again
803  * increased by 0x10000 by *our* code.
804  *
805  * Note that to keep everything consistent, this offset has to be applied by
806  * every Wine function that operates on 'linear addresses' passed to it by
807  * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
808  * API routines, this affects only two locations: this VxD and the DPMI
809  * handler. (NOTE: Should any Win32s application pass a linear address to
810  * any routine apart from those, e.g. some other VxD handler, that code
811  * would have to take the offset into account as well!)
812  *
813  * The offset is set the first time any application calls the GetVersion()
814  * service of the Win32s VxD. (Note that the offset is never reset.)
815  *
816  */
817 void WINAPI __wine_vxd_win32s( CONTEXT86 *context )
818 {
819     switch (AX_reg(context))
820     {
821     case 0x0000: /* Get Version */
822         /*
823          * Input:   None
824          *
825          * Output:  EAX: LoWord: Win32s Version (1.30)
826          *               HiWord: VxD Version (200)
827          *
828          *          EBX: Build (172)
829          *
830          *          ECX: ???   (1)
831          *
832          *          EDX: Debugging Flags
833          *
834          *          EDI: Error Flag
835          *               0 if OK,
836          *               1 if VMCPD VxD not found
837          */
838
839         TRACE("GetVersion()\n");
840
841         context->Eax = VXD_WinVersion() | (200 << 16);
842         context->Ebx = 0;
843         context->Ecx = 0;
844         context->Edx = 0;
845         context->Edi = 0;
846
847         /*
848          * If this is the first time we are called for this process,
849          * hack the memory image of WIN32S16 so that it doesn't try
850          * to access the GDT directly ...
851          *
852          * The first code segment of WIN32S16 (version 1.30) contains
853          * an unexported function somewhere between the exported functions
854          * SetFS and StackLinearToSegmented that tries to find a selector
855          * in the LDT that maps to the memory image of the LDT itself.
856          * If it succeeds, it stores this selector into a global variable
857          * which will be used to speed up execution by using this selector
858          * to modify the LDT directly instead of using the DPMI calls.
859          *
860          * To perform this search of the LDT, this function uses the
861          * sgdt and sldt instructions to find the linear address of
862          * the (GDT and then) LDT. While those instructions themselves
863          * execute without problem, the linear address that sgdt returns
864          * points (at least under Linux) to the kernel address space, so
865          * that any subsequent access leads to a segfault.
866          *
867          * Fortunately, WIN32S16 still contains as a fallback option the
868          * mechanism of using DPMI calls to modify LDT selectors instead
869          * of direct writes to the LDT. Thus we can circumvent the problem
870          * by simply replacing the first byte of the offending function
871          * with an 'retf' instruction. This means that the global variable
872          * supposed to contain the LDT alias selector will remain zero,
873          * and hence WIN32S16 will fall back to using DPMI calls.
874          *
875          * The heuristic we employ to _find_ that function is as follows:
876          * We search between the addresses of the exported symbols SetFS
877          * and StackLinearToSegmented for the byte sequence '0F 01 04'
878          * (this is the opcode of 'sgdt [si]'). We then search backwards
879          * from this address for the last occurrence of 'CB' (retf) that marks
880          * the end of the preceding function. The following byte (which
881          * should now be the first byte of the function we are looking for)
882          * will be replaced by 'CB' (retf).
883          *
884          * This heuristic works for the retail as well as the debug version
885          * of Win32s version 1.30. For versions earlier than that this
886          * hack should not be necessary at all, since the whole mechanism
887          * ('PERF130') was introduced only in 1.30 to improve the overall
888          * performance of Win32s.
889          */
890
891         if (!W32S_offset)
892         {
893             HMODULE16 hModule = GetModuleHandle16("win32s16");
894             SEGPTR func1 = (SEGPTR)GetProcAddress16(hModule, "SetFS");
895             SEGPTR func2 = (SEGPTR)GetProcAddress16(hModule, "StackLinearToSegmented");
896
897             if (   hModule && func1 && func2
898                 && SELECTOROF(func1) == SELECTOROF(func2))
899             {
900                 BYTE *start = MapSL(func1);
901                 BYTE *end   = MapSL(func2);
902                 BYTE *p, *retv = NULL;
903                 int found = 0;
904
905                 for (p = start; p < end; p++)
906                     if (*p == 0xCB) found = 0, retv = p;
907                     else if (*p == 0x0F) found = 1;
908                     else if (*p == 0x01 && found == 1) found = 2;
909                     else if (*p == 0x04 && found == 2) { found = 3; break; }
910                     else found = 0;
911
912                 if (found == 3 && retv)
913                 {
914                     TRACE("PERF130 hack: "
915                                "Replacing byte %02X at offset %04X:%04X\n",
916                                *(retv+1), SELECTOROF(func1),
917                                           OFFSETOF(func1) + retv+1-start);
918
919                     *(retv+1) = (BYTE)0xCB;
920                 }
921             }
922         }
923
924         /*
925          * Mark process as Win32s, so that subsequent DPMI calls
926          * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
927          */
928         W32S_offset = 0x10000;
929         break;
930
931
932     case 0x0001: /* Install Exception Handling */
933         /*
934          * Input:   EBX: Flat address of W32SKRNL Exception Data
935          *
936          *          ECX: LoWord: Flat Code Selector
937          *               HiWord: Flat Data Selector
938          *
939          *          EDX: Flat address of W32SKRNL Exception Handler
940          *               (this is equal to W32S_BackTo32 + 0x40)
941          *
942          *          ESI: SEGPTR KERNEL.HASGPHANDLER
943          *
944          *          EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
945          *
946          * Output:  EAX: 0 if OK
947          */
948
949         TRACE("[0001] EBX=%x ECX=%x EDX=%x ESI=%x EDI=%x\n",
950                    context->Ebx, context->Ecx, context->Edx,
951                    context->Esi, context->Edi);
952
953         /* FIXME */
954
955         context->Eax = 0;
956         break;
957
958
959     case 0x0002: /* Set Page Access Flags */
960         /*
961          * Input:   EBX: New access flags
962          *               Bit 2: User Page if set, Supervisor Page if clear
963          *               Bit 1: Read-Write if set, Read-Only if clear
964          *
965          *          ECX: Size of memory area to change
966          *
967          *          EDX: Flat start address of memory area
968          *
969          * Output:  EAX: Size of area changed
970          */
971
972         TRACE("[0002] EBX=%x ECX=%x EDX=%x\n",
973                    context->Ebx, context->Ecx, context->Edx);
974
975         /* FIXME */
976
977         context->Eax = context->Ecx;
978         break;
979
980
981     case 0x0003: /* Get Page Access Flags */
982         /*
983          * Input:   EDX: Flat address of page to query
984          *
985          * Output:  EAX: Page access flags
986          *               Bit 2: User Page if set, Supervisor Page if clear
987          *               Bit 1: Read-Write if set, Read-Only if clear
988          */
989
990         TRACE("[0003] EDX=%x\n", context->Edx);
991
992         /* FIXME */
993
994         context->Eax = 6;
995         break;
996
997
998     case 0x0004: /* Map Module */
999         /*
1000          * Input:   ECX: IMTE (offset in Module Table) of new module
1001          *
1002          *          EDX: Flat address of Win32s Module Table
1003          *
1004          * Output:  EAX: 0 if OK
1005          */
1006
1007     if (!context->Edx || CX_reg(context) == 0xFFFF)
1008     {
1009         TRACE("MapModule: Initialization call\n");
1010         context->Eax = 0;
1011     }
1012     else
1013     {
1014         /*
1015          * Structure of a Win32s Module Table Entry:
1016          */
1017         struct Win32sModule
1018         {
1019             DWORD  flags;
1020             DWORD  flatBaseAddr;
1021             LPCSTR moduleName;
1022             LPCSTR pathName;
1023             LPCSTR unknown;
1024             LPBYTE baseAddr;
1025             DWORD  hModule;
1026             DWORD  relocDelta;
1027         };
1028
1029         /*
1030          * Note: This function should set up a demand-paged memory image
1031          *       of the given module. Since mmap does not allow file offsets
1032          *       not aligned at 1024 bytes, we simply load the image fully
1033          *       into memory.
1034          */
1035
1036         struct Win32sModule *moduleTable =
1037                             (struct Win32sModule *)W32S_APP2WINE(context->Edx);
1038         struct Win32sModule *module = moduleTable + context->Ecx;
1039
1040         IMAGE_NT_HEADERS *nt_header = RtlImageNtHeader( (HMODULE)module->baseAddr );
1041         IMAGE_SECTION_HEADER *pe_seg = (IMAGE_SECTION_HEADER*)((char *)&nt_header->OptionalHeader +
1042                                                          nt_header->FileHeader.SizeOfOptionalHeader);
1043
1044
1045         HFILE image = _lopen(module->pathName, OF_READ);
1046         BOOL error = (image == HFILE_ERROR);
1047         UINT i;
1048
1049         TRACE("MapModule: Loading %s\n", module->pathName);
1050
1051         for (i = 0;
1052              !error && i < nt_header->FileHeader.NumberOfSections;
1053              i++, pe_seg++)
1054             if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
1055             {
1056                 DWORD  off  = pe_seg->PointerToRawData;
1057                 DWORD  len  = pe_seg->SizeOfRawData;
1058                 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
1059
1060                 TRACE("MapModule: "
1061                            "Section %d at %08x from %08x len %08x\n",
1062                            i, (DWORD)addr, off, len);
1063
1064                 if (   _llseek(image, off, SEEK_SET) != off
1065                     || _lread(image, addr, len) != len)
1066                     error = TRUE;
1067             }
1068
1069         _lclose(image);
1070
1071         if (error)
1072             ERR("MapModule: Unable to load %s\n", module->pathName);
1073
1074         else if (module->relocDelta != 0)
1075         {
1076             IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
1077                                       + IMAGE_DIRECTORY_ENTRY_BASERELOC;
1078             IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
1079                 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
1080
1081             TRACE("MapModule: Reloc delta %08x\n", module->relocDelta);
1082
1083             while (r && r->VirtualAddress)
1084             {
1085                 LPBYTE page  = module->baseAddr + r->VirtualAddress;
1086                 WORD *TypeOffset = (WORD *)(r + 1);
1087                 unsigned int count = (r->SizeOfBlock - sizeof(*r)) / sizeof(*TypeOffset);
1088
1089                 TRACE("MapModule: %d relocations for page %08x\n",
1090                            count, (DWORD)page);
1091
1092                 for(i = 0; i < count; i++)
1093                 {
1094                     int offset = TypeOffset[i] & 0xFFF;
1095                     int type   = TypeOffset[i] >> 12;
1096                     switch(type)
1097                     {
1098                     case IMAGE_REL_BASED_ABSOLUTE:
1099                         break;
1100                     case IMAGE_REL_BASED_HIGH:
1101                         *(WORD *)(page+offset) += HIWORD(module->relocDelta);
1102                         break;
1103                     case IMAGE_REL_BASED_LOW:
1104                         *(WORD *)(page+offset) += LOWORD(module->relocDelta);
1105                         break;
1106                     case IMAGE_REL_BASED_HIGHLOW:
1107                         *(DWORD*)(page+offset) += module->relocDelta;
1108                         break;
1109                     default:
1110                         WARN("MapModule: Unsupported fixup type\n");
1111                         break;
1112                     }
1113                 }
1114
1115                 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
1116             }
1117         }
1118
1119         context->Eax = 0;
1120         RESET_CFLAG(context);
1121     }
1122     break;
1123
1124
1125     case 0x0005: /* UnMap Module */
1126         /*
1127          * Input:   EDX: Flat address of module image
1128          *
1129          * Output:  EAX: 1 if OK
1130          */
1131
1132         TRACE("UnMapModule: %x\n", W32S_APP2WINE(context->Edx));
1133
1134         /* As we didn't map anything, there's nothing to unmap ... */
1135
1136         context->Eax = 1;
1137         break;
1138
1139
1140     case 0x0006: /* VirtualAlloc */
1141         /*
1142          * Input:   ECX: Current Process
1143          *
1144          *          EDX: Flat address of arguments on stack
1145          *
1146          *   DWORD *retv     [out] Flat base address of allocated region
1147          *   LPVOID base     [in]  Flat address of region to reserve/commit
1148          *   DWORD  size     [in]  Size of region
1149          *   DWORD  type     [in]  Type of allocation
1150          *   DWORD  prot     [in]  Type of access protection
1151          *
1152          * Output:  EAX: NtStatus
1153          */
1154     {
1155         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
1156         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
1157         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
1158         DWORD  size   = stack[2];
1159         DWORD  type   = stack[3];
1160         DWORD  prot   = stack[4];
1161         DWORD  result;
1162
1163         TRACE("VirtualAlloc(%x, %x, %x, %x, %x)\n",
1164                    (DWORD)retv, (DWORD)base, size, type, prot);
1165
1166         if (type & 0x80000000)
1167         {
1168             WARN("VirtualAlloc: strange type %x\n", type);
1169             type &= 0x7fffffff;
1170         }
1171
1172         if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
1173         {
1174             WARN("VirtualAlloc: NLS hack, allowing write access!\n");
1175             prot = PAGE_READWRITE;
1176         }
1177
1178         result = (DWORD)VirtualAlloc(base, size, type, prot);
1179
1180         if (W32S_WINE2APP(result))
1181             *retv            = W32S_WINE2APP(result),
1182             context->Eax = STATUS_SUCCESS;
1183         else
1184             *retv            = 0,
1185             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1186     }
1187     break;
1188
1189
1190     case 0x0007: /* VirtualFree */
1191         /*
1192          * Input:   ECX: Current Process
1193          *
1194          *          EDX: Flat address of arguments on stack
1195          *
1196          *   DWORD *retv     [out] TRUE if success, FALSE if failure
1197          *   LPVOID base     [in]  Flat address of region
1198          *   DWORD  size     [in]  Size of region
1199          *   DWORD  type     [in]  Type of operation
1200          *
1201          * Output:  EAX: NtStatus
1202          */
1203     {
1204         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
1205         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
1206         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
1207         DWORD  size   = stack[2];
1208         DWORD  type   = stack[3];
1209         DWORD  result;
1210
1211         TRACE("VirtualFree(%x, %x, %x, %x)\n",
1212                    (DWORD)retv, (DWORD)base, size, type);
1213
1214         result = VirtualFree(base, size, type);
1215
1216         if (result)
1217             *retv            = TRUE,
1218             context->Eax = STATUS_SUCCESS;
1219         else
1220             *retv            = FALSE,
1221             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1222     }
1223     break;
1224
1225
1226     case 0x0008: /* VirtualProtect */
1227         /*
1228          * Input:   ECX: Current Process
1229          *
1230          *          EDX: Flat address of arguments on stack
1231          *
1232          *   DWORD *retv     [out] TRUE if success, FALSE if failure
1233          *   LPVOID base     [in]  Flat address of region
1234          *   DWORD  size     [in]  Size of region
1235          *   DWORD  new_prot [in]  Desired access protection
1236          *   DWORD *old_prot [out] Previous access protection
1237          *
1238          * Output:  EAX: NtStatus
1239          */
1240     {
1241         DWORD *stack    = (DWORD *)W32S_APP2WINE(context->Edx);
1242         DWORD *retv     = (DWORD *)W32S_APP2WINE(stack[0]);
1243         LPVOID base     = (LPVOID) W32S_APP2WINE(stack[1]);
1244         DWORD  size     = stack[2];
1245         DWORD  new_prot = stack[3];
1246         DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4]);
1247         DWORD  result;
1248
1249         TRACE("VirtualProtect(%x, %x, %x, %x, %x)\n",
1250                    (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
1251
1252         result = VirtualProtect(base, size, new_prot, old_prot);
1253
1254         if (result)
1255             *retv            = TRUE,
1256             context->Eax = STATUS_SUCCESS;
1257         else
1258             *retv            = FALSE,
1259             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1260     }
1261     break;
1262
1263
1264     case 0x0009: /* VirtualQuery */
1265         /*
1266          * Input:   ECX: Current Process
1267          *
1268          *          EDX: Flat address of arguments on stack
1269          *
1270          *   DWORD *retv                     [out] Nr. bytes returned
1271          *   LPVOID base                     [in]  Flat address of region
1272          *   LPMEMORY_BASIC_INFORMATION info [out] Info buffer
1273          *   DWORD  len                      [in]  Size of buffer
1274          *
1275          * Output:  EAX: NtStatus
1276          */
1277     {
1278         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
1279         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
1280         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
1281         PMEMORY_BASIC_INFORMATION info =
1282                         (PMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2]);
1283         DWORD  len    = stack[3];
1284         DWORD  result;
1285
1286         TRACE("VirtualQuery(%x, %x, %x, %x)\n",
1287                    (DWORD)retv, (DWORD)base, (DWORD)info, len);
1288
1289         result = VirtualQuery(base, info, len);
1290
1291         *retv            = result;
1292         context->Eax = STATUS_SUCCESS;
1293     }
1294     break;
1295
1296
1297     case 0x000A: /* SetVirtMemProcess */
1298         /*
1299          * Input:   ECX: Process Handle
1300          *
1301          *          EDX: Flat address of region
1302          *
1303          * Output:  EAX: NtStatus
1304          */
1305
1306         TRACE("[000a] ECX=%x EDX=%x\n",
1307                    context->Ecx, context->Edx);
1308
1309         /* FIXME */
1310
1311         context->Eax = STATUS_SUCCESS;
1312         break;
1313
1314
1315     case 0x000B: /* ??? some kind of cleanup */
1316         /*
1317          * Input:   ECX: Process Handle
1318          *
1319          * Output:  EAX: NtStatus
1320          */
1321
1322         TRACE("[000b] ECX=%x\n", context->Ecx);
1323
1324         /* FIXME */
1325
1326         context->Eax = STATUS_SUCCESS;
1327         break;
1328
1329
1330     case 0x000C: /* Set Debug Flags */
1331         /*
1332          * Input:   EDX: Debug Flags
1333          *
1334          * Output:  EDX: Previous Debug Flags
1335          */
1336
1337         FIXME("[000c] EDX=%x\n", context->Edx);
1338
1339         /* FIXME */
1340
1341         context->Edx = 0;
1342         break;
1343
1344
1345     case 0x000D: /* NtCreateSection */
1346         /*
1347          * Input:   EDX: Flat address of arguments on stack
1348          *
1349          *   HANDLE32 *retv      [out] Handle of Section created
1350          *   DWORD  flags1       [in]  (?? unknown ??)
1351          *   DWORD  atom         [in]  Name of Section to create
1352          *   LARGE_INTEGER *size [in]  Size of Section
1353          *   DWORD  protect      [in]  Access protection
1354          *   DWORD  flags2       [in]  (?? unknown ??)
1355          *   HFILE32 hFile       [in]  Handle of file to map
1356          *   DWORD  psp          [in]  (Win32s: PSP that hFile belongs to)
1357          *
1358          * Output:  EAX: NtStatus
1359          */
1360     {
1361         DWORD *stack    = (DWORD *)   W32S_APP2WINE(context->Edx);
1362         HANDLE *retv  = (HANDLE *)W32S_APP2WINE(stack[0]);
1363         DWORD  flags1   = stack[1];
1364         DWORD  atom     = stack[2];
1365         LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3]);
1366         DWORD  protect  = stack[4];
1367         DWORD  flags2   = stack[5];
1368         HANDLE hFile    = DosFileHandleToWin32Handle(stack[6]);
1369         DWORD  psp      = stack[7];
1370
1371         HANDLE result = INVALID_HANDLE_VALUE;
1372         char name[128];
1373
1374         TRACE("NtCreateSection(%x, %x, %x, %x, %x, %x, %x, %x)\n",
1375                    (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1376                    (DWORD)hFile, psp);
1377
1378         if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1379         {
1380             TRACE("NtCreateSection: name=%s\n", atom? name : NULL);
1381
1382             result = CreateFileMappingA(hFile, NULL, protect,
1383                                           size? size->u.HighPart : 0,
1384                                           size? size->u.LowPart  : 0,
1385                                           atom? name : NULL);
1386         }
1387
1388         if (result == INVALID_HANDLE_VALUE)
1389             WARN("NtCreateSection: failed!\n");
1390         else
1391             TRACE("NtCreateSection: returned %x\n", (DWORD)result);
1392
1393         if (result != INVALID_HANDLE_VALUE)
1394             *retv            = result,
1395             context->Eax = STATUS_SUCCESS;
1396         else
1397             *retv            = result,
1398             context->Eax = STATUS_NO_MEMORY;   /* FIXME */
1399     }
1400     break;
1401
1402
1403     case 0x000E: /* NtOpenSection */
1404         /*
1405          * Input:   EDX: Flat address of arguments on stack
1406          *
1407          *   HANDLE32 *retv  [out] Handle of Section opened
1408          *   DWORD  protect  [in]  Access protection
1409          *   DWORD  atom     [in]  Name of Section to create
1410          *
1411          * Output:  EAX: NtStatus
1412          */
1413     {
1414         DWORD *stack    = (DWORD *)W32S_APP2WINE(context->Edx);
1415         HANDLE *retv  = (HANDLE *)W32S_APP2WINE(stack[0]);
1416         DWORD  protect  = stack[1];
1417         DWORD  atom     = stack[2];
1418
1419         HANDLE result = INVALID_HANDLE_VALUE;
1420         char name[128];
1421
1422         TRACE("NtOpenSection(%x, %x, %x)\n",
1423                    (DWORD)retv, protect, atom);
1424
1425         if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1426         {
1427             TRACE("NtOpenSection: name=%s\n", name);
1428
1429             result = OpenFileMappingA(protect, FALSE, name);
1430         }
1431
1432         if (result == INVALID_HANDLE_VALUE)
1433             WARN("NtOpenSection: failed!\n");
1434         else
1435             TRACE("NtOpenSection: returned %x\n", (DWORD)result);
1436
1437         if (result != INVALID_HANDLE_VALUE)
1438             *retv            = result,
1439             context->Eax = STATUS_SUCCESS;
1440         else
1441             *retv            = result,
1442             context->Eax = STATUS_NO_MEMORY;   /* FIXME */
1443     }
1444     break;
1445
1446
1447     case 0x000F: /* NtCloseSection */
1448         /*
1449          * Input:   EDX: Flat address of arguments on stack
1450          *
1451          *   HANDLE32 handle  [in]  Handle of Section to close
1452          *   DWORD *id        [out] Unique ID  (?? unclear ??)
1453          *
1454          * Output:  EAX: NtStatus
1455          */
1456     {
1457         DWORD *stack    = (DWORD *)W32S_APP2WINE(context->Edx);
1458         HANDLE handle   = (HANDLE)stack[0];
1459         DWORD *id       = (DWORD *)W32S_APP2WINE(stack[1]);
1460
1461         TRACE("NtCloseSection(%x, %x)\n", (DWORD)handle, (DWORD)id);
1462
1463         CloseHandle(handle);
1464         if (id) *id = 0; /* FIXME */
1465
1466         context->Eax = STATUS_SUCCESS;
1467     }
1468     break;
1469
1470
1471     case 0x0010: /* NtDupSection */
1472         /*
1473          * Input:   EDX: Flat address of arguments on stack
1474          *
1475          *   HANDLE32 handle  [in]  Handle of Section to duplicate
1476          *
1477          * Output:  EAX: NtStatus
1478          */
1479     {
1480         DWORD *stack    = (DWORD *)W32S_APP2WINE(context->Edx);
1481         HANDLE handle   = (HANDLE)stack[0];
1482         HANDLE new_handle;
1483
1484         TRACE("NtDupSection(%x)\n", (DWORD)handle);
1485
1486         DuplicateHandle( GetCurrentProcess(), handle,
1487                          GetCurrentProcess(), &new_handle,
1488                          0, FALSE, DUPLICATE_SAME_ACCESS );
1489         context->Eax = STATUS_SUCCESS;
1490     }
1491     break;
1492
1493
1494     case 0x0011: /* NtMapViewOfSection */
1495         /*
1496          * Input:   EDX: Flat address of arguments on stack
1497          *
1498          *   HANDLE32 SectionHandle       [in]     Section to be mapped
1499          *   DWORD    ProcessHandle       [in]     Process to be mapped into
1500          *   DWORD *  BaseAddress         [in/out] Address to be mapped at
1501          *   DWORD    ZeroBits            [in]     (?? unclear ??)
1502          *   DWORD    CommitSize          [in]     (?? unclear ??)
1503          *   LARGE_INTEGER *SectionOffset [in]     Offset within section
1504          *   DWORD *  ViewSize            [in]     Size of view
1505          *   DWORD    InheritDisposition  [in]     (?? unclear ??)
1506          *   DWORD    AllocationType      [in]     (?? unclear ??)
1507          *   DWORD    Protect             [in]     Access protection
1508          *
1509          * Output:  EAX: NtStatus
1510          */
1511     {
1512         DWORD *  stack          = (DWORD *)W32S_APP2WINE(context->Edx);
1513         HANDLE   SectionHandle  = (HANDLE)stack[0];
1514         DWORD    ProcessHandle  = stack[1]; /* ignored */
1515         DWORD *  BaseAddress    = (DWORD *)W32S_APP2WINE(stack[2]);
1516         DWORD    ZeroBits       = stack[3];
1517         DWORD    CommitSize     = stack[4];
1518         LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5]);
1519         DWORD *  ViewSize       = (DWORD *)W32S_APP2WINE(stack[6]);
1520         DWORD    InheritDisposition = stack[7];
1521         DWORD    AllocationType = stack[8];
1522         DWORD    Protect        = stack[9];
1523
1524         LPBYTE address = (LPBYTE)(BaseAddress?
1525                         W32S_APP2WINE(*BaseAddress) : 0);
1526         DWORD  access = 0, result;
1527
1528         switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1529         {
1530             case PAGE_READONLY:           access = FILE_MAP_READ;  break;
1531             case PAGE_READWRITE:          access = FILE_MAP_WRITE; break;
1532             case PAGE_WRITECOPY:          access = FILE_MAP_COPY;  break;
1533
1534             case PAGE_EXECUTE_READ:       access = FILE_MAP_READ;  break;
1535             case PAGE_EXECUTE_READWRITE:  access = FILE_MAP_WRITE; break;
1536             case PAGE_EXECUTE_WRITECOPY:  access = FILE_MAP_COPY;  break;
1537         }
1538
1539         TRACE("NtMapViewOfSection"
1540                    "(%x, %x, %x, %x, %x, %x, %x, %x, %x, %x)\n",
1541                    (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1542                    ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1543                    InheritDisposition, AllocationType, Protect);
1544         TRACE("NtMapViewOfSection: "
1545                    "base=%x, offset=%x, size=%x, access=%x\n",
1546                    (DWORD)address, SectionOffset? SectionOffset->u.LowPart : 0,
1547                    ViewSize? *ViewSize : 0, access);
1548
1549         result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1550                             SectionOffset? SectionOffset->u.HighPart : 0,
1551                             SectionOffset? SectionOffset->u.LowPart  : 0,
1552                             ViewSize? *ViewSize : 0, address);
1553
1554         TRACE("NtMapViewOfSection: result=%x\n", result);
1555
1556         if (W32S_WINE2APP(result))
1557         {
1558             if (BaseAddress) *BaseAddress = W32S_WINE2APP(result);
1559             context->Eax = STATUS_SUCCESS;
1560         }
1561         else
1562             context->Eax = STATUS_NO_MEMORY; /* FIXME */
1563     }
1564     break;
1565
1566
1567     case 0x0012: /* NtUnmapViewOfSection */
1568         /*
1569          * Input:   EDX: Flat address of arguments on stack
1570          *
1571          *   DWORD  ProcessHandle  [in]  Process (defining address space)
1572          *   LPBYTE BaseAddress    [in]  Base address of view to be unmapped
1573          *
1574          * Output:  EAX: NtStatus
1575          */
1576     {
1577         DWORD *stack          = (DWORD *)W32S_APP2WINE(context->Edx);
1578         DWORD  ProcessHandle  = stack[0]; /* ignored */
1579         LPBYTE BaseAddress    = (LPBYTE)W32S_APP2WINE(stack[1]);
1580
1581         TRACE("NtUnmapViewOfSection(%x, %x)\n",
1582                    ProcessHandle, (DWORD)BaseAddress);
1583
1584         UnmapViewOfFile(BaseAddress);
1585
1586         context->Eax = STATUS_SUCCESS;
1587     }
1588     break;
1589
1590
1591     case 0x0013: /* NtFlushVirtualMemory */
1592         /*
1593          * Input:   EDX: Flat address of arguments on stack
1594          *
1595          *   DWORD   ProcessHandle  [in]  Process (defining address space)
1596          *   LPBYTE *BaseAddress    [in?] Base address of range to be flushed
1597          *   DWORD  *ViewSize       [in?] Number of bytes to be flushed
1598          *   DWORD  *unknown        [???] (?? unknown ??)
1599          *
1600          * Output:  EAX: NtStatus
1601          */
1602     {
1603         DWORD *stack          = (DWORD *)W32S_APP2WINE(context->Edx);
1604         DWORD  ProcessHandle  = stack[0]; /* ignored */
1605         DWORD *BaseAddress    = (DWORD *)W32S_APP2WINE(stack[1]);
1606         DWORD *ViewSize       = (DWORD *)W32S_APP2WINE(stack[2]);
1607         DWORD *unknown        = (DWORD *)W32S_APP2WINE(stack[3]);
1608
1609         LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress) : 0);
1610         DWORD  size    = ViewSize? *ViewSize : 0;
1611
1612         TRACE("NtFlushVirtualMemory(%x, %x, %x, %x)\n",
1613                    ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1614                    (DWORD)unknown);
1615         TRACE("NtFlushVirtualMemory: base=%x, size=%x\n",
1616                    (DWORD)address, size);
1617
1618         FlushViewOfFile(address, size);
1619
1620         context->Eax = STATUS_SUCCESS;
1621     }
1622     break;
1623
1624
1625     case 0x0014: /* Get/Set Debug Registers */
1626         /*
1627          * Input:   ECX: 0 if Get, 1 if Set
1628          *
1629          *          EDX: Get: Flat address of buffer to receive values of
1630          *                    debug registers DR0 .. DR7
1631          *               Set: Flat address of buffer containing values of
1632          *                    debug registers DR0 .. DR7 to be set
1633          * Output:  None
1634          */
1635
1636         FIXME("[0014] ECX=%x EDX=%x\n",
1637                    context->Ecx, context->Edx);
1638
1639         /* FIXME */
1640         break;
1641
1642
1643     case 0x0015: /* Set Coprocessor Emulation Flag */
1644         /*
1645          * Input:   EDX: 0 to deactivate, 1 to activate coprocessor emulation
1646          *
1647          * Output:  None
1648          */
1649
1650         TRACE("[0015] EDX=%x\n", context->Edx);
1651
1652         /* We don't care, as we always have a coprocessor anyway */
1653         break;
1654
1655
1656     case 0x0016: /* Init Win32S VxD PSP */
1657         /*
1658          * If called to query required PSP size:
1659          *
1660          *     Input:  EBX: 0
1661          *     Output: EDX: Required size of Win32s VxD PSP
1662          *
1663          * If called to initialize allocated PSP:
1664          *
1665          *     Input:  EBX: LoWord: Selector of Win32s VxD PSP
1666          *                  HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1667          *     Output: None
1668          */
1669
1670         if (context->Ebx == 0)
1671             context->Edx = 0x80;
1672         else
1673         {
1674             PDB16 *psp = MapSL( MAKESEGPTR( BX_reg(context), 0 ));
1675             psp->nbFiles = 32;
1676             psp->fileHandlesPtr = MAKELONG(HIWORD(context->Ebx), 0x5c);
1677             memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1678         }
1679         break;
1680
1681
1682     case 0x0017: /* Set Break Point */
1683         /*
1684          * Input:   EBX: Offset of Break Point
1685          *          CX:  Selector of Break Point
1686          *
1687          * Output:  None
1688          */
1689
1690         FIXME("[0017] EBX=%x CX=%x\n",
1691                    context->Ebx, CX_reg(context));
1692
1693         /* FIXME */
1694         break;
1695
1696
1697     case 0x0018: /* VirtualLock */
1698         /*
1699          * Input:   ECX: Current Process
1700          *
1701          *          EDX: Flat address of arguments on stack
1702          *
1703          *   DWORD *retv     [out] TRUE if success, FALSE if failure
1704          *   LPVOID base     [in]  Flat address of range to lock
1705          *   DWORD  size     [in]  Size of range
1706          *
1707          * Output:  EAX: NtStatus
1708          */
1709     {
1710         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
1711         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
1712         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
1713         DWORD  size   = stack[2];
1714         DWORD  result;
1715
1716         TRACE("VirtualLock(%x, %x, %x)\n",
1717                    (DWORD)retv, (DWORD)base, size);
1718
1719         result = VirtualLock(base, size);
1720
1721         if (result)
1722             *retv            = TRUE,
1723             context->Eax = STATUS_SUCCESS;
1724         else
1725             *retv            = FALSE,
1726             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1727     }
1728     break;
1729
1730
1731     case 0x0019: /* VirtualUnlock */
1732         /*
1733          * Input:   ECX: Current Process
1734          *
1735          *          EDX: Flat address of arguments on stack
1736          *
1737          *   DWORD *retv     [out] TRUE if success, FALSE if failure
1738          *   LPVOID base     [in]  Flat address of range to unlock
1739          *   DWORD  size     [in]  Size of range
1740          *
1741          * Output:  EAX: NtStatus
1742          */
1743     {
1744         DWORD *stack  = (DWORD *)W32S_APP2WINE(context->Edx);
1745         DWORD *retv   = (DWORD *)W32S_APP2WINE(stack[0]);
1746         LPVOID base   = (LPVOID) W32S_APP2WINE(stack[1]);
1747         DWORD  size   = stack[2];
1748         DWORD  result;
1749
1750         TRACE("VirtualUnlock(%x, %x, %x)\n",
1751                    (DWORD)retv, (DWORD)base, size);
1752
1753         result = VirtualUnlock(base, size);
1754
1755         if (result)
1756             *retv            = TRUE,
1757             context->Eax = STATUS_SUCCESS;
1758         else
1759             *retv            = FALSE,
1760             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1761     }
1762     break;
1763
1764
1765     case 0x001A: /* KGetSystemInfo */
1766         /*
1767          * Input:   None
1768          *
1769          * Output:  ECX:  Start of sparse memory arena
1770          *          EDX:  End of sparse memory arena
1771          */
1772
1773         TRACE("KGetSystemInfo()\n");
1774
1775         /*
1776          * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1777          *       sparse memory arena. We do it the other way around, since
1778          *       we have to reserve 3GB - 4GB for Linux, and thus use
1779          *       0GB - 3GB as sparse memory arena.
1780          *
1781          *       FIXME: What about other OSes ?
1782          */
1783
1784         context->Ecx = W32S_WINE2APP(0x00000000);
1785         context->Edx = W32S_WINE2APP(0xbfffffff);
1786         break;
1787
1788
1789     case 0x001B: /* KGlobalMemStat */
1790         /*
1791          * Input:   ESI: Flat address of buffer to receive memory info
1792          *
1793          * Output:  None
1794          */
1795     {
1796         struct Win32sMemoryInfo
1797         {
1798             DWORD DIPhys_Count;       /* Total physical pages */
1799             DWORD DIFree_Count;       /* Free physical pages */
1800             DWORD DILin_Total_Count;  /* Total virtual pages (private arena) */
1801             DWORD DILin_Total_Free;   /* Free virtual pages (private arena) */
1802
1803             DWORD SparseTotal;        /* Total size of sparse arena (bytes ?) */
1804             DWORD SparseFree;         /* Free size of sparse arena (bytes ?) */
1805         };
1806
1807         struct Win32sMemoryInfo *info =
1808                        (struct Win32sMemoryInfo *)W32S_APP2WINE(context->Esi);
1809
1810         FIXME("KGlobalMemStat(%x)\n", (DWORD)info);
1811
1812         /* FIXME */
1813     }
1814     break;
1815
1816
1817     case 0x001C: /* Enable/Disable Exceptions */
1818         /*
1819          * Input:   ECX: 0 to disable, 1 to enable exception handling
1820          *
1821          * Output:  None
1822          */
1823
1824         TRACE("[001c] ECX=%x\n", context->Ecx);
1825
1826         /* FIXME */
1827         break;
1828
1829
1830     case 0x001D: /* VirtualAlloc called from 16-bit code */
1831         /*
1832          * Input:   EDX: Segmented address of arguments on stack
1833          *
1834          *   LPVOID base     [in]  Flat address of region to reserve/commit
1835          *   DWORD  size     [in]  Size of region
1836          *   DWORD  type     [in]  Type of allocation
1837          *   DWORD  prot     [in]  Type of access protection
1838          *
1839          * Output:  EAX: NtStatus
1840          *          EDX: Flat base address of allocated region
1841          */
1842     {
1843         DWORD *stack  = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1844         LPVOID base   = (LPVOID)W32S_APP2WINE(stack[0]);
1845         DWORD  size   = stack[1];
1846         DWORD  type   = stack[2];
1847         DWORD  prot   = stack[3];
1848         DWORD  result;
1849
1850         TRACE("VirtualAlloc16(%x, %x, %x, %x)\n",
1851                    (DWORD)base, size, type, prot);
1852
1853         if (type & 0x80000000)
1854         {
1855             WARN("VirtualAlloc16: strange type %x\n", type);
1856             type &= 0x7fffffff;
1857         }
1858
1859         result = (DWORD)VirtualAlloc(base, size, type, prot);
1860
1861         if (W32S_WINE2APP(result))
1862             context->Edx = W32S_WINE2APP(result),
1863             context->Eax = STATUS_SUCCESS;
1864         else
1865             context->Edx = 0,
1866             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1867         TRACE("VirtualAlloc16: returning base %x\n", context->Edx);
1868     }
1869     break;
1870
1871
1872     case 0x001E: /* VirtualFree called from 16-bit code */
1873         /*
1874          * Input:   EDX: Segmented address of arguments on stack
1875          *
1876          *   LPVOID base     [in]  Flat address of region
1877          *   DWORD  size     [in]  Size of region
1878          *   DWORD  type     [in]  Type of operation
1879          *
1880          * Output:  EAX: NtStatus
1881          *          EDX: TRUE if success, FALSE if failure
1882          */
1883     {
1884         DWORD *stack  = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1885         LPVOID base   = (LPVOID)W32S_APP2WINE(stack[0]);
1886         DWORD  size   = stack[1];
1887         DWORD  type   = stack[2];
1888         DWORD  result;
1889
1890         TRACE("VirtualFree16(%x, %x, %x)\n",
1891                    (DWORD)base, size, type);
1892
1893         result = VirtualFree(base, size, type);
1894
1895         if (result)
1896             context->Edx = TRUE,
1897             context->Eax = STATUS_SUCCESS;
1898         else
1899             context->Edx = FALSE,
1900             context->Eax = STATUS_NO_MEMORY;  /* FIXME */
1901     }
1902     break;
1903
1904
1905     case 0x001F: /* FWorkingSetSize */
1906         /*
1907          * Input:   EDX: 0 if Get, 1 if Set
1908          *
1909          *          ECX: Get: Buffer to receive Working Set Size
1910          *               Set: Buffer containing Working Set Size
1911          *
1912          * Output:  NtStatus
1913          */
1914     {
1915         DWORD *ptr = (DWORD *)W32S_APP2WINE(context->Ecx);
1916         BOOL set = context->Edx;
1917
1918         TRACE("FWorkingSetSize(%x, %x)\n", (DWORD)ptr, (DWORD)set);
1919
1920         if (set)
1921             /* We do it differently ... */;
1922         else
1923             *ptr = 0x100;
1924
1925         context->Eax = STATUS_SUCCESS;
1926     }
1927     break;
1928
1929     default:
1930         VXD_BARF( context, "W32S" );
1931     }
1932 }