4 * Copyright 1998 Marcus Meissner
5 * Copyright 1998 Ulrich Weigand
6 * Copyright 1998 Patrik Stridvall
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.
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.
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
24 #include "wine/port.h"
30 #include <sys/types.h>
31 #ifdef HAVE_SYS_STAT_H
32 # include <sys/stat.h>
37 #define NONAMELESSUNION
38 #define NONAMELESSSTRUCT
40 #define WIN32_NO_STATUS
46 #include "kernel16_private.h"
48 #include "wine/library.h"
49 #include "wine/unicode.h"
50 #include "wine/server.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(vxd);
55 typedef DWORD (WINAPI *VxDCallProc)(DWORD, CONTEXT86 *);
56 typedef BOOL (WINAPI *DeviceIoProc)(DWORD, LPVOID, DWORD, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
66 struct vxdcall_service
74 #define MAX_VXD_MODULES 32
76 static struct vxd_module vxd_modules[MAX_VXD_MODULES];
78 static struct vxdcall_service vxd_services[] =
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 }
84 #define NB_VXD_SERVICES (sizeof(vxd_services)/sizeof(vxd_services[0]))
86 #define W32S_APP2WINE(addr) ((addr)? (DWORD)(addr) + W32S_offset : 0)
87 #define W32S_WINE2APP(addr) ((addr)? (DWORD)(addr) - W32S_offset : 0)
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 )
97 static CRITICAL_SECTION vxd_section;
98 static CRITICAL_SECTION_DEBUG critsect_debug =
101 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
102 0, 0, { (DWORD_PTR)(__FILE__ ": vxd_section") }
104 static CRITICAL_SECTION vxd_section = { &critsect_debug, -1, 0, 0, 0, 0 };
106 static UINT W32S_offset;
108 static WORD VXD_WinVersion(void)
110 WORD version = LOWORD(GetVersion16());
111 return (version >> 8) | (version << 8);
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 )
117 static const WCHAR prefixW[] = {'\\','?','?','\\','u','n','i','x'};
118 const char *dir = wine_get_server_dir();
122 OBJECT_ATTRIBUTES attr;
123 UNICODE_STRING nameW;
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 )))
131 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
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 );
140 attr.Length = sizeof(attr);
141 attr.RootDirectory = 0;
143 attr.ObjectName = &nameW;
144 attr.SecurityDescriptor = NULL;
145 attr.SecurityQualityOfService = NULL;
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 );
153 SetLastError( RtlNtStatusToDosError(status) );
155 RtlFreeUnicodeString( &nameW );
159 /* retrieve the DeviceIoControl function for a Vxd given a file handle */
160 DeviceIoProc __wine_vxd_get_proc( HANDLE handle )
162 DeviceIoProc ret = NULL;
165 FILE_INTERNAL_INFORMATION info;
167 status = NtQueryInformationFile( handle, &io, &info, sizeof(info), FileInternalInformation );
170 SetLastError( RtlNtStatusToDosError(status) );
174 RtlEnterCriticalSection( &vxd_section );
176 for (i = 0; i < MAX_VXD_MODULES; i++)
178 if (!vxd_modules[i].module) break;
179 if (vxd_modules[i].index.QuadPart == info.IndexNumber.QuadPart)
181 if (!(ret = vxd_modules[i].proc)) SetLastError( ERROR_INVALID_FUNCTION );
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 );
191 RtlLeaveCriticalSection( &vxd_section );
196 /* load a VxD and return a file handle to it */
197 HANDLE __wine_vxd_open( LPCWSTR filenameW, DWORD access, SECURITY_ATTRIBUTES *sa )
199 static const WCHAR dotVxDW[] = {'.','v','x','d',0};
205 /* normalize the filename */
207 if (strlenW( filenameW ) >= sizeof(name)/sizeof(WCHAR) - 4 ||
208 strchrW( filenameW, '/' ) || strchrW( filenameW, '\\' ))
210 SetLastError( ERROR_FILE_NOT_FOUND );
213 strcpyW( name, filenameW );
215 p = strchrW( name, '.' );
216 if (!p) strcatW( name, dotVxDW );
217 else if (strcmpiW( p, dotVxDW )) /* existing extension has to be .vxd */
219 SetLastError( ERROR_FILE_NOT_FOUND );
223 /* try to load the module first */
225 if (!(module = LoadLibraryW( name )))
227 FIXME( "Unknown/unsupported VxD %s. Try setting Windows version to 'nt40' or 'win31'.\n",
229 SetLastError( ERROR_FILE_NOT_FOUND );
233 /* register the module in the global list if necessary */
235 RtlEnterCriticalSection( &vxd_section );
237 for (i = 0; i < MAX_VXD_MODULES; i++)
239 if (vxd_modules[i].module == module)
241 handle = vxd_modules[i].handle;
242 goto done; /* already registered */
244 if (!vxd_modules[i].module) /* new one, register it */
247 FILE_INTERNAL_INFORMATION info;
249 /* get a file handle to the dummy file */
250 if (!(handle = open_vxd_handle( name )))
252 FreeLibrary( module );
255 if (!NtQueryInformationFile( handle, &io, &info, sizeof(info), FileInternalInformation ))
256 vxd_modules[i].index = info.IndexNumber;
258 vxd_modules[i].module = module;
259 vxd_modules[i].handle = handle;
260 vxd_modules[i].proc = (DeviceIoProc)GetProcAddress( module, "DeviceIoControl" );
265 ERR("too many open VxD modules, please report\n" );
266 FreeLibrary( module );
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 ))
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)
290 void WINAPI __regs_VxDCall( DWORD service, CONTEXT86 *context )
293 VxDCallProc proc = NULL;
295 RtlEnterCriticalSection( &vxd_section );
296 for (i = 0; i < NB_VXD_SERVICES; i++)
298 if (HIWORD(service) != vxd_services[i].service) continue;
299 if (!vxd_services[i].module) /* need to load it */
301 if ((vxd_services[i].module = LoadLibraryW( vxd_services[i].name )))
302 vxd_services[i].proc = (VxDCallProc)GetProcAddress( vxd_services[i].module, "VxDCall" );
304 proc = vxd_services[i].proc;
307 RtlLeaveCriticalSection( &vxd_section );
309 if (proc) context->Eax = proc( service, context );
312 FIXME( "Unknown/unimplemented VxD (%08x)\n", service);
313 context->Eax = 0xffffffff; /* FIXME */
316 #ifdef DEFINE_REGS_ENTRYPOINT
317 DEFINE_REGS_ENTRYPOINT( VxDCall, 1 )
321 /***********************************************************************
322 * __wine_vxd_vmm (WPROCS.401)
324 void WINAPI __wine_vxd_vmm ( CONTEXT86 *context )
326 unsigned service = AX_reg(context);
328 TRACE("[%04x] VMM\n", (UINT16)service);
332 case 0x0000: /* version */
333 SET_AX( context, VXD_WinVersion() );
334 RESET_CFLAG(context);
337 case 0x026d: /* Get_Debug_Flag '/m' */
338 case 0x026e: /* Get_Debug_Flag '/n' */
339 SET_AL( context, 0 );
340 RESET_CFLAG(context);
344 VXD_BARF( context, "VMM" );
348 /***********************************************************************
349 * __wine_vxd_pagefile (WPROCS.433)
351 void WINAPI __wine_vxd_pagefile( CONTEXT86 *context )
353 unsigned service = AX_reg(context);
355 /* taken from Ralf Brown's Interrupt List */
357 TRACE("[%04x] PageFile\n", (UINT16)service );
361 case 0x00: /* get version, is this windows version? */
362 TRACE("returning version\n");
363 SET_AX( context, VXD_WinVersion() );
364 RESET_CFLAG(context);
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);
375 case 0x02: /* delete permanent swap on exit */
376 TRACE("VxD PageFile: supposed to delete swap\n");
377 RESET_CFLAG(context);
380 case 0x03: /* current temporary swap file size */
381 TRACE("VxD PageFile: what is current temp. swap size\n");
382 RESET_CFLAG(context);
385 case 0x04: /* read or write?? INTERRUP.D */
386 case 0x05: /* cancel?? INTERRUP.D */
387 case 0x06: /* test I/O valid INTERRUP.D */
389 VXD_BARF( context, "pagefile" );
394 /***********************************************************************
395 * __wine_vxd_reboot (WPROCS.409)
397 void WINAPI __wine_vxd_reboot( CONTEXT86 *context )
399 unsigned service = AX_reg(context);
401 TRACE("[%04x] Reboot\n", (UINT16)service);
405 case 0x0000: /* version */
406 SET_AX( context, VXD_WinVersion() );
407 RESET_CFLAG(context);
411 VXD_BARF( context, "REBOOT" );
415 /***********************************************************************
416 * __wine_vxd_vdd (WPROCS.410)
418 void WINAPI __wine_vxd_vdd( CONTEXT86 *context )
420 unsigned service = AX_reg(context);
422 TRACE("[%04x] VDD\n", (UINT16)service);
426 case 0x0000: /* version */
427 SET_AX( context, VXD_WinVersion() );
428 RESET_CFLAG(context);
432 VXD_BARF( context, "VDD" );
436 /***********************************************************************
437 * __wine_vxd_vmd (WPROCS.412)
439 void WINAPI __wine_vxd_vmd( CONTEXT86 *context )
441 unsigned service = AX_reg(context);
443 TRACE("[%04x] VMD\n", (UINT16)service);
447 case 0x0000: /* version */
448 SET_AX( context, VXD_WinVersion() );
449 RESET_CFLAG(context);
453 VXD_BARF( context, "VMD" );
457 /***********************************************************************
458 * __wine_vxd_vxdloader (WPROCS.439)
460 void WINAPI __wine_vxd_vxdloader( CONTEXT86 *context )
462 unsigned service = AX_reg(context);
464 TRACE("[%04x] VXDLoader\n", (UINT16)service);
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);
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);
485 case 0x0002: /* unload device */
486 FIXME("unload device (%08x)\n", context->Ebx);
487 SET_AX( context, 0x0000 );
488 RESET_CFLAG(context);
492 VXD_BARF( context, "VXDLDR" );
493 SET_AX( context, 0x000B ); /* invalid function number */
499 /***********************************************************************
500 * __wine_vxd_shell (WPROCS.423)
502 void WINAPI __wine_vxd_shell( CONTEXT86 *context )
504 unsigned service = DX_reg(context);
506 TRACE("[%04x] Shell\n", (UINT16)service);
508 switch (service) /* Ralf Brown says EDX, but I use DX instead */
511 TRACE("returning version\n");
512 SET_AX( context, VXD_WinVersion() );
513 context->Ebx = 1; /* system VM Handle */
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
528 ebx virtual machine handle
529 eax message box flags
530 ecx address of message
531 edi address of caption
533 edx reference data for callback
534 return response in eax
537 VXD_BARF( context, "shell" );
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
547 /* RESET_CFLAG(context); */
566 VXD_BARF( context, "SHELL" );
569 /* the new Win95 shell API */
570 case 0x0100: /* get version */
571 SET_AX( context, VXD_WinVersion() );
574 case 0x0104: /* retrieve Hook_Properties list */
575 case 0x0105: /* call Hook_Properties callbacks */
576 VXD_BARF( context, "SHELL" );
579 case 0x0106: /* install timeout callback */
580 TRACE("VxD Shell: ignoring shell callback (%d sec.)\n", context->Ebx);
584 case 0x0107: /* get version of any VxD */
586 VXD_BARF( context, "SHELL" );
592 /***********************************************************************
593 * __wine_vxd_comm (WPROCS.414)
595 void WINAPI __wine_vxd_comm( CONTEXT86 *context )
597 unsigned service = AX_reg(context);
599 TRACE("[%04x] Comm\n", (UINT16)service);
603 case 0x0000: /* get version */
604 TRACE("returning version\n");
605 SET_AX( context, VXD_WinVersion() );
606 RESET_CFLAG(context);
609 case 0x0001: /* set port global */
610 case 0x0002: /* get focus */
611 case 0x0003: /* virtualise port */
613 VXD_BARF( context, "comm" );
617 /***********************************************************************
618 * __wine_vxd_timer (WPROCS.405)
620 void WINAPI __wine_vxd_timer( CONTEXT86 *context )
622 unsigned service = AX_reg(context);
624 TRACE("[%04x] Virtual Timer\n", (UINT16)service);
628 case 0x0000: /* version */
629 SET_AX( context, VXD_WinVersion() );
630 RESET_CFLAG(context);
633 case 0x0100: /* clock tick time, in 840nsecs */
634 context->Eax = GetTickCount();
636 context->Edx = context->Eax >> 22;
637 context->Eax <<= 10; /* not very precise */
640 case 0x0101: /* current Windows time, msecs */
641 case 0x0102: /* current VM time, msecs */
642 context->Eax = GetTickCount();
646 VXD_BARF( context, "VTD" );
651 /***********************************************************************
654 static DWORD CALLBACK timer_thread( void *arg )
656 DWORD *system_time = arg;
660 *system_time = GetTickCount();
668 /***********************************************************************
669 * __wine_vxd_timerapi (WPROCS.1490)
671 void WINAPI __wine_vxd_timerapi( CONTEXT86 *context )
673 static WORD System_Time_Selector;
675 unsigned service = AX_reg(context);
677 TRACE("[%04x] TimerAPI\n", (UINT16)service);
681 case 0x0000: /* version */
682 SET_AX( context, VXD_WinVersion() );
683 RESET_CFLAG(context);
686 case 0x0009: /* get system time selector */
687 if ( !System_Time_Selector )
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 ) );
693 SET_AX( context, System_Time_Selector );
694 RESET_CFLAG(context);
698 VXD_BARF( context, "VTDAPI" );
702 /***********************************************************************
703 * __wine_vxd_configmg (WPROCS.451)
705 void WINAPI __wine_vxd_configmg( CONTEXT86 *context )
707 unsigned service = AX_reg(context);
709 TRACE("[%04x] ConfigMG\n", (UINT16)service);
713 case 0x0000: /* version */
714 SET_AX( context, VXD_WinVersion() );
715 RESET_CFLAG(context);
719 VXD_BARF( context, "CONFIGMG" );
723 /***********************************************************************
724 * __wine_vxd_enable (WPROCS.455)
726 void WINAPI __wine_vxd_enable( CONTEXT86 *context )
728 unsigned service = AX_reg(context);
730 TRACE("[%04x] Enable\n", (UINT16)service);
734 case 0x0000: /* version */
735 SET_AX( context, VXD_WinVersion() );
736 RESET_CFLAG(context);
740 VXD_BARF( context, "ENABLE" );
744 /***********************************************************************
745 * __wine_vxd_apm (WPROCS.438)
747 void WINAPI __wine_vxd_apm( CONTEXT86 *context )
749 unsigned service = AX_reg(context);
751 TRACE("[%04x] APM\n", (UINT16)service);
755 case 0x0000: /* version */
756 SET_AX( context, VXD_WinVersion() );
757 RESET_CFLAG(context);
761 VXD_BARF( context, "APM" );
765 /***********************************************************************
766 * __wine_vxd_win32s (WPROCS.445)
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.
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)
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.
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.
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!)
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.)
817 void WINAPI __wine_vxd_win32s( CONTEXT86 *context )
819 switch (AX_reg(context))
821 case 0x0000: /* Get Version */
825 * Output: EAX: LoWord: Win32s Version (1.30)
826 * HiWord: VxD Version (200)
832 * EDX: Debugging Flags
836 * 1 if VMCPD VxD not found
839 TRACE("GetVersion()\n");
841 context->Eax = VXD_WinVersion() | (200 << 16);
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 ...
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.
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.
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.
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).
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.
893 HMODULE16 hModule = GetModuleHandle16("win32s16");
894 SEGPTR func1 = (SEGPTR)GetProcAddress16(hModule, "SetFS");
895 SEGPTR func2 = (SEGPTR)GetProcAddress16(hModule, "StackLinearToSegmented");
897 if ( hModule && func1 && func2
898 && SELECTOROF(func1) == SELECTOROF(func2))
900 BYTE *start = MapSL(func1);
901 BYTE *end = MapSL(func2);
902 BYTE *p, *retv = NULL;
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; }
912 if (found == 3 && retv)
914 TRACE("PERF130 hack: "
915 "Replacing byte %02X at offset %04X:%04X\n",
916 *(retv+1), SELECTOROF(func1),
917 OFFSETOF(func1) + retv+1-start);
919 *(retv+1) = (BYTE)0xCB;
925 * Mark process as Win32s, so that subsequent DPMI calls
926 * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
928 W32S_offset = 0x10000;
932 case 0x0001: /* Install Exception Handling */
934 * Input: EBX: Flat address of W32SKRNL Exception Data
936 * ECX: LoWord: Flat Code Selector
937 * HiWord: Flat Data Selector
939 * EDX: Flat address of W32SKRNL Exception Handler
940 * (this is equal to W32S_BackTo32 + 0x40)
942 * ESI: SEGPTR KERNEL.HASGPHANDLER
944 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
946 * Output: EAX: 0 if OK
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);
959 case 0x0002: /* Set Page Access Flags */
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
965 * ECX: Size of memory area to change
967 * EDX: Flat start address of memory area
969 * Output: EAX: Size of area changed
972 TRACE("[0002] EBX=%x ECX=%x EDX=%x\n",
973 context->Ebx, context->Ecx, context->Edx);
977 context->Eax = context->Ecx;
981 case 0x0003: /* Get Page Access Flags */
983 * Input: EDX: Flat address of page to query
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
990 TRACE("[0003] EDX=%x\n", context->Edx);
998 case 0x0004: /* Map Module */
1000 * Input: ECX: IMTE (offset in Module Table) of new module
1002 * EDX: Flat address of Win32s Module Table
1004 * Output: EAX: 0 if OK
1007 if (!context->Edx || CX_reg(context) == 0xFFFF)
1009 TRACE("MapModule: Initialization call\n");
1015 * Structure of a Win32s Module Table Entry:
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
1036 struct Win32sModule *moduleTable =
1037 (struct Win32sModule *)W32S_APP2WINE(context->Edx);
1038 struct Win32sModule *module = moduleTable + context->Ecx;
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);
1045 HFILE image = _lopen(module->pathName, OF_READ);
1046 BOOL error = (image == HFILE_ERROR);
1049 TRACE("MapModule: Loading %s\n", module->pathName);
1052 !error && i < nt_header->FileHeader.NumberOfSections;
1054 if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
1056 DWORD off = pe_seg->PointerToRawData;
1057 DWORD len = pe_seg->SizeOfRawData;
1058 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
1061 "Section %d at %08x from %08x len %08x\n",
1062 i, (DWORD)addr, off, len);
1064 if ( _llseek(image, off, SEEK_SET) != off
1065 || _lread(image, addr, len) != len)
1072 ERR("MapModule: Unable to load %s\n", module->pathName);
1074 else if (module->relocDelta != 0)
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);
1081 TRACE("MapModule: Reloc delta %08x\n", module->relocDelta);
1083 while (r && r->VirtualAddress)
1085 LPBYTE page = module->baseAddr + r->VirtualAddress;
1086 WORD *TypeOffset = (WORD *)(r + 1);
1087 unsigned int count = (r->SizeOfBlock - sizeof(*r)) / sizeof(*TypeOffset);
1089 TRACE("MapModule: %d relocations for page %08x\n",
1090 count, (DWORD)page);
1092 for(i = 0; i < count; i++)
1094 int offset = TypeOffset[i] & 0xFFF;
1095 int type = TypeOffset[i] >> 12;
1098 case IMAGE_REL_BASED_ABSOLUTE:
1100 case IMAGE_REL_BASED_HIGH:
1101 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
1103 case IMAGE_REL_BASED_LOW:
1104 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
1106 case IMAGE_REL_BASED_HIGHLOW:
1107 *(DWORD*)(page+offset) += module->relocDelta;
1110 WARN("MapModule: Unsupported fixup type\n");
1115 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
1120 RESET_CFLAG(context);
1125 case 0x0005: /* UnMap Module */
1127 * Input: EDX: Flat address of module image
1129 * Output: EAX: 1 if OK
1132 TRACE("UnMapModule: %x\n", W32S_APP2WINE(context->Edx));
1134 /* As we didn't map anything, there's nothing to unmap ... */
1140 case 0x0006: /* VirtualAlloc */
1142 * Input: ECX: Current Process
1144 * EDX: Flat address of arguments on stack
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
1152 * Output: EAX: NtStatus
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];
1163 TRACE("VirtualAlloc(%x, %x, %x, %x, %x)\n",
1164 (DWORD)retv, (DWORD)base, size, type, prot);
1166 if (type & 0x80000000)
1168 WARN("VirtualAlloc: strange type %x\n", type);
1172 if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
1174 WARN("VirtualAlloc: NLS hack, allowing write access!\n");
1175 prot = PAGE_READWRITE;
1178 result = (DWORD)VirtualAlloc(base, size, type, prot);
1180 if (W32S_WINE2APP(result))
1181 *retv = W32S_WINE2APP(result),
1182 context->Eax = STATUS_SUCCESS;
1185 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1190 case 0x0007: /* VirtualFree */
1192 * Input: ECX: Current Process
1194 * EDX: Flat address of arguments on stack
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
1201 * Output: EAX: NtStatus
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];
1211 TRACE("VirtualFree(%x, %x, %x, %x)\n",
1212 (DWORD)retv, (DWORD)base, size, type);
1214 result = VirtualFree(base, size, type);
1218 context->Eax = STATUS_SUCCESS;
1221 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1226 case 0x0008: /* VirtualProtect */
1228 * Input: ECX: Current Process
1230 * EDX: Flat address of arguments on stack
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
1238 * Output: EAX: NtStatus
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]);
1249 TRACE("VirtualProtect(%x, %x, %x, %x, %x)\n",
1250 (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
1252 result = VirtualProtect(base, size, new_prot, old_prot);
1256 context->Eax = STATUS_SUCCESS;
1259 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1264 case 0x0009: /* VirtualQuery */
1266 * Input: ECX: Current Process
1268 * EDX: Flat address of arguments on stack
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
1275 * Output: EAX: NtStatus
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];
1286 TRACE("VirtualQuery(%x, %x, %x, %x)\n",
1287 (DWORD)retv, (DWORD)base, (DWORD)info, len);
1289 result = VirtualQuery(base, info, len);
1292 context->Eax = STATUS_SUCCESS;
1297 case 0x000A: /* SetVirtMemProcess */
1299 * Input: ECX: Process Handle
1301 * EDX: Flat address of region
1303 * Output: EAX: NtStatus
1306 TRACE("[000a] ECX=%x EDX=%x\n",
1307 context->Ecx, context->Edx);
1311 context->Eax = STATUS_SUCCESS;
1315 case 0x000B: /* ??? some kind of cleanup */
1317 * Input: ECX: Process Handle
1319 * Output: EAX: NtStatus
1322 TRACE("[000b] ECX=%x\n", context->Ecx);
1326 context->Eax = STATUS_SUCCESS;
1330 case 0x000C: /* Set Debug Flags */
1332 * Input: EDX: Debug Flags
1334 * Output: EDX: Previous Debug Flags
1337 FIXME("[000c] EDX=%x\n", context->Edx);
1345 case 0x000D: /* NtCreateSection */
1347 * Input: EDX: Flat address of arguments on stack
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)
1358 * Output: EAX: NtStatus
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];
1371 HANDLE result = INVALID_HANDLE_VALUE;
1374 TRACE("NtCreateSection(%x, %x, %x, %x, %x, %x, %x, %x)\n",
1375 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1378 if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1380 TRACE("NtCreateSection: name=%s\n", atom? name : NULL);
1382 result = CreateFileMappingA(hFile, NULL, protect,
1383 size? size->u.HighPart : 0,
1384 size? size->u.LowPart : 0,
1388 if (result == INVALID_HANDLE_VALUE)
1389 WARN("NtCreateSection: failed!\n");
1391 TRACE("NtCreateSection: returned %x\n", (DWORD)result);
1393 if (result != INVALID_HANDLE_VALUE)
1395 context->Eax = STATUS_SUCCESS;
1398 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1403 case 0x000E: /* NtOpenSection */
1405 * Input: EDX: Flat address of arguments on stack
1407 * HANDLE32 *retv [out] Handle of Section opened
1408 * DWORD protect [in] Access protection
1409 * DWORD atom [in] Name of Section to create
1411 * Output: EAX: NtStatus
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];
1419 HANDLE result = INVALID_HANDLE_VALUE;
1422 TRACE("NtOpenSection(%x, %x, %x)\n",
1423 (DWORD)retv, protect, atom);
1425 if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1427 TRACE("NtOpenSection: name=%s\n", name);
1429 result = OpenFileMappingA(protect, FALSE, name);
1432 if (result == INVALID_HANDLE_VALUE)
1433 WARN("NtOpenSection: failed!\n");
1435 TRACE("NtOpenSection: returned %x\n", (DWORD)result);
1437 if (result != INVALID_HANDLE_VALUE)
1439 context->Eax = STATUS_SUCCESS;
1442 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1447 case 0x000F: /* NtCloseSection */
1449 * Input: EDX: Flat address of arguments on stack
1451 * HANDLE32 handle [in] Handle of Section to close
1452 * DWORD *id [out] Unique ID (?? unclear ??)
1454 * Output: EAX: NtStatus
1457 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1458 HANDLE handle = (HANDLE)stack[0];
1459 DWORD *id = (DWORD *)W32S_APP2WINE(stack[1]);
1461 TRACE("NtCloseSection(%x, %x)\n", (DWORD)handle, (DWORD)id);
1463 CloseHandle(handle);
1464 if (id) *id = 0; /* FIXME */
1466 context->Eax = STATUS_SUCCESS;
1471 case 0x0010: /* NtDupSection */
1473 * Input: EDX: Flat address of arguments on stack
1475 * HANDLE32 handle [in] Handle of Section to duplicate
1477 * Output: EAX: NtStatus
1480 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1481 HANDLE handle = (HANDLE)stack[0];
1484 TRACE("NtDupSection(%x)\n", (DWORD)handle);
1486 DuplicateHandle( GetCurrentProcess(), handle,
1487 GetCurrentProcess(), &new_handle,
1488 0, FALSE, DUPLICATE_SAME_ACCESS );
1489 context->Eax = STATUS_SUCCESS;
1494 case 0x0011: /* NtMapViewOfSection */
1496 * Input: EDX: Flat address of arguments on stack
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
1509 * Output: EAX: NtStatus
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];
1524 LPBYTE address = (LPBYTE)(BaseAddress?
1525 W32S_APP2WINE(*BaseAddress) : 0);
1526 DWORD access = 0, result;
1528 switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
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;
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;
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);
1549 result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1550 SectionOffset? SectionOffset->u.HighPart : 0,
1551 SectionOffset? SectionOffset->u.LowPart : 0,
1552 ViewSize? *ViewSize : 0, address);
1554 TRACE("NtMapViewOfSection: result=%x\n", result);
1556 if (W32S_WINE2APP(result))
1558 if (BaseAddress) *BaseAddress = W32S_WINE2APP(result);
1559 context->Eax = STATUS_SUCCESS;
1562 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1567 case 0x0012: /* NtUnmapViewOfSection */
1569 * Input: EDX: Flat address of arguments on stack
1571 * DWORD ProcessHandle [in] Process (defining address space)
1572 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1574 * Output: EAX: NtStatus
1577 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1578 DWORD ProcessHandle = stack[0]; /* ignored */
1579 LPBYTE BaseAddress = (LPBYTE)W32S_APP2WINE(stack[1]);
1581 TRACE("NtUnmapViewOfSection(%x, %x)\n",
1582 ProcessHandle, (DWORD)BaseAddress);
1584 UnmapViewOfFile(BaseAddress);
1586 context->Eax = STATUS_SUCCESS;
1591 case 0x0013: /* NtFlushVirtualMemory */
1593 * Input: EDX: Flat address of arguments on stack
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 ??)
1600 * Output: EAX: NtStatus
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]);
1609 LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress) : 0);
1610 DWORD size = ViewSize? *ViewSize : 0;
1612 TRACE("NtFlushVirtualMemory(%x, %x, %x, %x)\n",
1613 ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1615 TRACE("NtFlushVirtualMemory: base=%x, size=%x\n",
1616 (DWORD)address, size);
1618 FlushViewOfFile(address, size);
1620 context->Eax = STATUS_SUCCESS;
1625 case 0x0014: /* Get/Set Debug Registers */
1627 * Input: ECX: 0 if Get, 1 if Set
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
1636 FIXME("[0014] ECX=%x EDX=%x\n",
1637 context->Ecx, context->Edx);
1643 case 0x0015: /* Set Coprocessor Emulation Flag */
1645 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1650 TRACE("[0015] EDX=%x\n", context->Edx);
1652 /* We don't care, as we always have a coprocessor anyway */
1656 case 0x0016: /* Init Win32S VxD PSP */
1658 * If called to query required PSP size:
1661 * Output: EDX: Required size of Win32s VxD PSP
1663 * If called to initialize allocated PSP:
1665 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1666 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1670 if (context->Ebx == 0)
1671 context->Edx = 0x80;
1674 PDB16 *psp = MapSL( MAKESEGPTR( BX_reg(context), 0 ));
1676 psp->fileHandlesPtr = MAKELONG(HIWORD(context->Ebx), 0x5c);
1677 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1682 case 0x0017: /* Set Break Point */
1684 * Input: EBX: Offset of Break Point
1685 * CX: Selector of Break Point
1690 FIXME("[0017] EBX=%x CX=%x\n",
1691 context->Ebx, CX_reg(context));
1697 case 0x0018: /* VirtualLock */
1699 * Input: ECX: Current Process
1701 * EDX: Flat address of arguments on stack
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
1707 * Output: EAX: NtStatus
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];
1716 TRACE("VirtualLock(%x, %x, %x)\n",
1717 (DWORD)retv, (DWORD)base, size);
1719 result = VirtualLock(base, size);
1723 context->Eax = STATUS_SUCCESS;
1726 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1731 case 0x0019: /* VirtualUnlock */
1733 * Input: ECX: Current Process
1735 * EDX: Flat address of arguments on stack
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
1741 * Output: EAX: NtStatus
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];
1750 TRACE("VirtualUnlock(%x, %x, %x)\n",
1751 (DWORD)retv, (DWORD)base, size);
1753 result = VirtualUnlock(base, size);
1757 context->Eax = STATUS_SUCCESS;
1760 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1765 case 0x001A: /* KGetSystemInfo */
1769 * Output: ECX: Start of sparse memory arena
1770 * EDX: End of sparse memory arena
1773 TRACE("KGetSystemInfo()\n");
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.
1781 * FIXME: What about other OSes ?
1784 context->Ecx = W32S_WINE2APP(0x00000000);
1785 context->Edx = W32S_WINE2APP(0xbfffffff);
1789 case 0x001B: /* KGlobalMemStat */
1791 * Input: ESI: Flat address of buffer to receive memory info
1796 struct Win32sMemoryInfo
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) */
1803 DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */
1804 DWORD SparseFree; /* Free size of sparse arena (bytes ?) */
1807 struct Win32sMemoryInfo *info =
1808 (struct Win32sMemoryInfo *)W32S_APP2WINE(context->Esi);
1810 FIXME("KGlobalMemStat(%x)\n", (DWORD)info);
1817 case 0x001C: /* Enable/Disable Exceptions */
1819 * Input: ECX: 0 to disable, 1 to enable exception handling
1824 TRACE("[001c] ECX=%x\n", context->Ecx);
1830 case 0x001D: /* VirtualAlloc called from 16-bit code */
1832 * Input: EDX: Segmented address of arguments on stack
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
1839 * Output: EAX: NtStatus
1840 * EDX: Flat base address of allocated region
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];
1850 TRACE("VirtualAlloc16(%x, %x, %x, %x)\n",
1851 (DWORD)base, size, type, prot);
1853 if (type & 0x80000000)
1855 WARN("VirtualAlloc16: strange type %x\n", type);
1859 result = (DWORD)VirtualAlloc(base, size, type, prot);
1861 if (W32S_WINE2APP(result))
1862 context->Edx = W32S_WINE2APP(result),
1863 context->Eax = STATUS_SUCCESS;
1866 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1867 TRACE("VirtualAlloc16: returning base %x\n", context->Edx);
1872 case 0x001E: /* VirtualFree called from 16-bit code */
1874 * Input: EDX: Segmented address of arguments on stack
1876 * LPVOID base [in] Flat address of region
1877 * DWORD size [in] Size of region
1878 * DWORD type [in] Type of operation
1880 * Output: EAX: NtStatus
1881 * EDX: TRUE if success, FALSE if failure
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];
1890 TRACE("VirtualFree16(%x, %x, %x)\n",
1891 (DWORD)base, size, type);
1893 result = VirtualFree(base, size, type);
1896 context->Edx = TRUE,
1897 context->Eax = STATUS_SUCCESS;
1899 context->Edx = FALSE,
1900 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1905 case 0x001F: /* FWorkingSetSize */
1907 * Input: EDX: 0 if Get, 1 if Set
1909 * ECX: Get: Buffer to receive Working Set Size
1910 * Set: Buffer containing Working Set Size
1915 DWORD *ptr = (DWORD *)W32S_APP2WINE(context->Ecx);
1916 BOOL set = context->Edx;
1918 TRACE("FWorkingSetSize(%x, %x)\n", (DWORD)ptr, (DWORD)set);
1921 /* We do it differently ... */;
1925 context->Eax = STATUS_SUCCESS;
1930 VXD_BARF( context, "W32S" );