| /* |
| * Win32 VxD functions |
| * |
| * Copyright 1998 Marcus Meissner |
| * Copyright 1998 Ulrich Weigand |
| * Copyright 1998 Patrik Stridvall |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <stdlib.h> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| #include <sys/types.h> |
| #ifdef HAVE_SYS_STAT_H |
| # include <sys/stat.h> |
| #endif |
| #include <string.h> |
| #include <stdarg.h> |
| |
| #define NONAMELESSUNION |
| #define NONAMELESSSTRUCT |
| #include "ntstatus.h" |
| #define WIN32_NO_STATUS |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winerror.h" |
| #include "winternl.h" |
| #include "winioctl.h" |
| #include "kernel16_private.h" |
| #include "dosexe.h" |
| #include "wine/library.h" |
| #include "wine/unicode.h" |
| #include "wine/server.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(vxd); |
| |
| typedef DWORD (WINAPI *VxDCallProc)(DWORD, CONTEXT *); |
| typedef BOOL (WINAPI *DeviceIoProc)(DWORD, LPVOID, DWORD, LPVOID, DWORD, LPDWORD, LPOVERLAPPED); |
| |
| struct vxd_module |
| { |
| LARGE_INTEGER index; |
| HANDLE handle; |
| HMODULE module; |
| DeviceIoProc proc; |
| }; |
| |
| struct vxdcall_service |
| { |
| WCHAR name[12]; |
| DWORD service; |
| HMODULE module; |
| VxDCallProc proc; |
| }; |
| |
| #define MAX_VXD_MODULES 32 |
| |
| static struct vxd_module vxd_modules[MAX_VXD_MODULES]; |
| |
| static struct vxdcall_service vxd_services[] = |
| { |
| { {'v','m','m','.','v','x','d',0}, 0x0001, NULL, NULL }, |
| { {'v','w','i','n','3','2','.','v','x','d',0}, 0x002a, NULL, NULL } |
| }; |
| |
| #define NB_VXD_SERVICES (sizeof(vxd_services)/sizeof(vxd_services[0])) |
| |
| #define W32S_APP2WINE(addr) ((addr)? (DWORD)(addr) + W32S_offset : 0) |
| #define W32S_WINE2APP(addr) ((addr)? (DWORD)(addr) - W32S_offset : 0) |
| |
| #define VXD_BARF(context,name) \ |
| TRACE( "vxd %s: unknown/not implemented parameters:\n" \ |
| "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \ |
| "SI %04x, DI %04x, DS %04x, ES %04x\n", \ |
| (name), (name), AX_reg(context), BX_reg(context), \ |
| CX_reg(context), DX_reg(context), SI_reg(context), \ |
| DI_reg(context), (WORD)context->SegDs, (WORD)context->SegEs ) |
| |
| static CRITICAL_SECTION vxd_section; |
| static CRITICAL_SECTION_DEBUG critsect_debug = |
| { |
| 0, 0, &vxd_section, |
| { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, |
| 0, 0, { (DWORD_PTR)(__FILE__ ": vxd_section") } |
| }; |
| static CRITICAL_SECTION vxd_section = { &critsect_debug, -1, 0, 0, 0, 0 }; |
| |
| static UINT W32S_offset; |
| |
| static WORD VXD_WinVersion(void) |
| { |
| WORD version = LOWORD(GetVersion16()); |
| return (version >> 8) | (version << 8); |
| } |
| |
| /* create a file handle to represent a VxD, by opening a dummy file in the wineserver directory */ |
| static HANDLE open_vxd_handle( LPCWSTR name ) |
| { |
| static const WCHAR prefixW[] = {'\\','?','?','\\','u','n','i','x'}; |
| const char *dir = wine_get_server_dir(); |
| int len; |
| HANDLE ret; |
| NTSTATUS status; |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW; |
| IO_STATUS_BLOCK io; |
| |
| len = MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, NULL, 0 ); |
| nameW.Length = sizeof(prefixW) + (len + strlenW( name )) * sizeof(WCHAR); |
| nameW.MaximumLength = nameW.Length + sizeof(WCHAR); |
| if (!(nameW.Buffer = HeapAlloc( GetProcessHeap(), 0, nameW.MaximumLength ))) |
| { |
| SetLastError( ERROR_NOT_ENOUGH_MEMORY ); |
| return 0; |
| } |
| memcpy( nameW.Buffer, prefixW, sizeof(prefixW) ); |
| MultiByteToWideChar( CP_UNIXCP, 0, dir, -1, nameW.Buffer + sizeof(prefixW)/sizeof(WCHAR), len ); |
| len += sizeof(prefixW) / sizeof(WCHAR); |
| nameW.Buffer[len-1] = '/'; |
| strcpyW( nameW.Buffer + len, name ); |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.Attributes = 0; |
| attr.ObjectName = &nameW; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| |
| status = NtCreateFile( &ret, 0, &attr, &io, NULL, 0, |
| FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN_IF, |
| FILE_SYNCHRONOUS_IO_ALERT, NULL, 0 ); |
| if (status) |
| { |
| ret = 0; |
| SetLastError( RtlNtStatusToDosError(status) ); |
| } |
| RtlFreeUnicodeString( &nameW ); |
| return ret; |
| } |
| |
| /* retrieve the DeviceIoControl function for a Vxd given a file handle */ |
| DeviceIoProc __wine_vxd_get_proc( HANDLE handle ) |
| { |
| DeviceIoProc ret = NULL; |
| int status, i; |
| IO_STATUS_BLOCK io; |
| FILE_INTERNAL_INFORMATION info; |
| |
| status = NtQueryInformationFile( handle, &io, &info, sizeof(info), FileInternalInformation ); |
| if (status) |
| { |
| SetLastError( RtlNtStatusToDosError(status) ); |
| return NULL; |
| } |
| |
| RtlEnterCriticalSection( &vxd_section ); |
| |
| for (i = 0; i < MAX_VXD_MODULES; i++) |
| { |
| if (!vxd_modules[i].module) break; |
| if (vxd_modules[i].index.QuadPart == info.IndexNumber.QuadPart) |
| { |
| if (!(ret = vxd_modules[i].proc)) SetLastError( ERROR_INVALID_FUNCTION ); |
| goto done; |
| } |
| } |
| /* FIXME: Here we could go through the directory to find the VxD name and load it. */ |
| /* Let's wait to find out if there are actually apps out there that try to share */ |
| /* VxD handles between processes, before we go to the trouble of implementing it. */ |
| ERR( "handle %p not found in module list, inherited from another process?\n", handle ); |
| |
| done: |
| RtlLeaveCriticalSection( &vxd_section ); |
| return ret; |
| } |
| |
| |
| /* load a VxD and return a file handle to it */ |
| HANDLE __wine_vxd_open( LPCWSTR filenameW, DWORD access, SECURITY_ATTRIBUTES *sa ) |
| { |
| static const WCHAR dotVxDW[] = {'.','v','x','d',0}; |
| int i; |
| HANDLE handle; |
| HMODULE module; |
| WCHAR *p, name[16]; |
| |
| /* normalize the filename */ |
| |
| if (strlenW( filenameW ) >= sizeof(name)/sizeof(WCHAR) - 4 || |
| strchrW( filenameW, '/' ) || strchrW( filenameW, '\\' )) |
| { |
| SetLastError( ERROR_FILE_NOT_FOUND ); |
| return 0; |
| } |
| strcpyW( name, filenameW ); |
| strlwrW( name ); |
| p = strchrW( name, '.' ); |
| if (!p) strcatW( name, dotVxDW ); |
| else if (strcmpiW( p, dotVxDW )) /* existing extension has to be .vxd */ |
| { |
| SetLastError( ERROR_FILE_NOT_FOUND ); |
| return 0; |
| } |
| |
| /* try to load the module first */ |
| |
| if (!(module = LoadLibraryW( name ))) |
| { |
| FIXME( "Unknown/unsupported VxD %s. Try setting Windows version to 'nt40' or 'win31'.\n", |
| debugstr_w(name) ); |
| SetLastError( ERROR_FILE_NOT_FOUND ); |
| return 0; |
| } |
| |
| /* register the module in the global list if necessary */ |
| |
| RtlEnterCriticalSection( &vxd_section ); |
| |
| for (i = 0; i < MAX_VXD_MODULES; i++) |
| { |
| if (vxd_modules[i].module == module) |
| { |
| handle = vxd_modules[i].handle; |
| goto done; /* already registered */ |
| } |
| if (!vxd_modules[i].module) /* new one, register it */ |
| { |
| IO_STATUS_BLOCK io; |
| FILE_INTERNAL_INFORMATION info; |
| |
| /* get a file handle to the dummy file */ |
| if (!(handle = open_vxd_handle( name ))) |
| { |
| FreeLibrary( module ); |
| goto done; |
| } |
| if (!NtQueryInformationFile( handle, &io, &info, sizeof(info), FileInternalInformation )) |
| vxd_modules[i].index = info.IndexNumber; |
| |
| vxd_modules[i].module = module; |
| vxd_modules[i].handle = handle; |
| vxd_modules[i].proc = (DeviceIoProc)GetProcAddress( module, "DeviceIoControl" ); |
| goto done; |
| } |
| } |
| |
| ERR("too many open VxD modules, please report\n" ); |
| FreeLibrary( module ); |
| handle = 0; |
| |
| done: |
| RtlLeaveCriticalSection( &vxd_section ); |
| if (!DuplicateHandle( GetCurrentProcess(), handle, GetCurrentProcess(), &handle, 0, |
| (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle), |
| DUP_HANDLE_SAME_ACCESS )) |
| handle = 0; |
| return handle; |
| } |
| |
| |
| /*********************************************************************** |
| * VxDCall0 (KERNEL32.1) |
| * VxDCall1 (KERNEL32.2) |
| * VxDCall2 (KERNEL32.3) |
| * VxDCall3 (KERNEL32.4) |
| * VxDCall4 (KERNEL32.5) |
| * VxDCall5 (KERNEL32.6) |
| * VxDCall6 (KERNEL32.7) |
| * VxDCall7 (KERNEL32.8) |
| * VxDCall8 (KERNEL32.9) |
| */ |
| void WINAPI __regs_VxDCall( DWORD service, CONTEXT *context ) |
| { |
| unsigned int i; |
| VxDCallProc proc = NULL; |
| |
| RtlEnterCriticalSection( &vxd_section ); |
| for (i = 0; i < NB_VXD_SERVICES; i++) |
| { |
| if (HIWORD(service) != vxd_services[i].service) continue; |
| if (!vxd_services[i].module) /* need to load it */ |
| { |
| if ((vxd_services[i].module = LoadLibraryW( vxd_services[i].name ))) |
| vxd_services[i].proc = (VxDCallProc)GetProcAddress( vxd_services[i].module, "VxDCall" ); |
| } |
| proc = vxd_services[i].proc; |
| break; |
| } |
| RtlLeaveCriticalSection( &vxd_section ); |
| |
| if (proc) context->Eax = proc( service, context ); |
| else |
| { |
| FIXME( "Unknown/unimplemented VxD (%08x)\n", service); |
| context->Eax = 0xffffffff; /* FIXME */ |
| } |
| } |
| #ifdef DEFINE_REGS_ENTRYPOINT |
| DEFINE_REGS_ENTRYPOINT( VxDCall, 1 ) |
| #endif |
| |
| |
| /*********************************************************************** |
| * __wine_vxd_vmm (WPROCS.401) |
| */ |
| void WINAPI __wine_vxd_vmm ( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] VMM\n", (UINT16)service); |
| |
| switch(service) |
| { |
| case 0x0000: /* version */ |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x026d: /* Get_Debug_Flag '/m' */ |
| case 0x026e: /* Get_Debug_Flag '/n' */ |
| SET_AL( context, 0 ); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| VXD_BARF( context, "VMM" ); |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_pagefile (WPROCS.433) |
| */ |
| void WINAPI __wine_vxd_pagefile( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| /* taken from Ralf Brown's Interrupt List */ |
| |
| TRACE("[%04x] PageFile\n", (UINT16)service ); |
| |
| switch(service) |
| { |
| case 0x00: /* get version, is this windows version? */ |
| TRACE("returning version\n"); |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x01: /* get swap file info */ |
| TRACE("VxD PageFile: returning swap file info\n"); |
| SET_AX( context, 0x00 ); /* paging disabled */ |
| context->Ecx = 0; /* maximum size of paging file */ |
| /* FIXME: do I touch DS:SI or DS:DI? */ |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x02: /* delete permanent swap on exit */ |
| TRACE("VxD PageFile: supposed to delete swap\n"); |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x03: /* current temporary swap file size */ |
| TRACE("VxD PageFile: what is current temp. swap size\n"); |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x04: /* read or write?? INTERRUP.D */ |
| case 0x05: /* cancel?? INTERRUP.D */ |
| case 0x06: /* test I/O valid INTERRUP.D */ |
| default: |
| VXD_BARF( context, "pagefile" ); |
| break; |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_reboot (WPROCS.409) |
| */ |
| void WINAPI __wine_vxd_reboot( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] Reboot\n", (UINT16)service); |
| |
| switch(service) |
| { |
| case 0x0000: /* version */ |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| VXD_BARF( context, "REBOOT" ); |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_vdd (WPROCS.410) |
| */ |
| void WINAPI __wine_vxd_vdd( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] VDD\n", (UINT16)service); |
| |
| switch(service) |
| { |
| case 0x0000: /* version */ |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| VXD_BARF( context, "VDD" ); |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_vmd (WPROCS.412) |
| */ |
| void WINAPI __wine_vxd_vmd( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] VMD\n", (UINT16)service); |
| |
| switch(service) |
| { |
| case 0x0000: /* version */ |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| VXD_BARF( context, "VMD" ); |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_vxdloader (WPROCS.439) |
| */ |
| void WINAPI __wine_vxd_vxdloader( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] VXDLoader\n", (UINT16)service); |
| |
| switch (service) |
| { |
| case 0x0000: /* get version */ |
| TRACE("returning version\n"); |
| SET_AX( context, 0x0000 ); |
| SET_DX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x0001: /* load device */ |
| FIXME("load device %04x:%04x (%s)\n", |
| context->SegDs, DX_reg(context), |
| debugstr_a(MapSL(MAKESEGPTR(context->SegDs, DX_reg(context))))); |
| SET_AX( context, 0x0000 ); |
| context->SegEs = 0x0000; |
| SET_DI( context, 0x0000 ); |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x0002: /* unload device */ |
| FIXME("unload device (%08x)\n", context->Ebx); |
| SET_AX( context, 0x0000 ); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| VXD_BARF( context, "VXDLDR" ); |
| SET_AX( context, 0x000B ); /* invalid function number */ |
| SET_CFLAG(context); |
| break; |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_shell (WPROCS.423) |
| */ |
| void WINAPI __wine_vxd_shell( CONTEXT *context ) |
| { |
| unsigned service = DX_reg(context); |
| |
| TRACE("[%04x] Shell\n", (UINT16)service); |
| |
| switch (service) /* Ralf Brown says EDX, but I use DX instead */ |
| { |
| case 0x0000: |
| TRACE("returning version\n"); |
| SET_AX( context, VXD_WinVersion() ); |
| context->Ebx = 1; /* system VM Handle */ |
| break; |
| |
| case 0x0001: |
| case 0x0002: |
| case 0x0003: |
| /* SHELL_SYSMODAL_Message |
| ebx virtual machine handle |
| eax message box flags |
| ecx address of message |
| edi address of caption |
| return response in eax |
| */ |
| case 0x0004: |
| /* SHELL_Message |
| ebx virtual machine handle |
| eax message box flags |
| ecx address of message |
| edi address of caption |
| esi address callback |
| edx reference data for callback |
| return response in eax |
| */ |
| case 0x0005: |
| VXD_BARF( context, "shell" ); |
| break; |
| |
| case 0x0006: /* SHELL_Get_VM_State */ |
| TRACE("VxD Shell: returning VM state\n"); |
| /* Actually we don't, not yet. We have to return a structure |
| * and I am not to sure how to set it up and return it yet, |
| * so for now let's do nothing. I can (hopefully) get this |
| * by the next release |
| */ |
| /* RESET_CFLAG(context); */ |
| break; |
| |
| case 0x0007: |
| case 0x0008: |
| case 0x0009: |
| case 0x000A: |
| case 0x000B: |
| case 0x000C: |
| case 0x000D: |
| case 0x000E: |
| case 0x000F: |
| case 0x0010: |
| case 0x0011: |
| case 0x0012: |
| case 0x0013: |
| case 0x0014: |
| case 0x0015: |
| case 0x0016: |
| VXD_BARF( context, "SHELL" ); |
| break; |
| |
| /* the new Win95 shell API */ |
| case 0x0100: /* get version */ |
| SET_AX( context, VXD_WinVersion() ); |
| break; |
| |
| case 0x0104: /* retrieve Hook_Properties list */ |
| case 0x0105: /* call Hook_Properties callbacks */ |
| VXD_BARF( context, "SHELL" ); |
| break; |
| |
| case 0x0106: /* install timeout callback */ |
| TRACE("VxD Shell: ignoring shell callback (%d sec.)\n", context->Ebx); |
| SET_CFLAG(context); |
| break; |
| |
| case 0x0107: /* get version of any VxD */ |
| default: |
| VXD_BARF( context, "SHELL" ); |
| break; |
| } |
| } |
| |
| |
| /*********************************************************************** |
| * __wine_vxd_comm (WPROCS.414) |
| */ |
| void WINAPI __wine_vxd_comm( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] Comm\n", (UINT16)service); |
| |
| switch (service) |
| { |
| case 0x0000: /* get version */ |
| TRACE("returning version\n"); |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x0001: /* set port global */ |
| case 0x0002: /* get focus */ |
| case 0x0003: /* virtualise port */ |
| default: |
| VXD_BARF( context, "comm" ); |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_timer (WPROCS.405) |
| */ |
| void WINAPI __wine_vxd_timer( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] Virtual Timer\n", (UINT16)service); |
| |
| switch(service) |
| { |
| case 0x0000: /* version */ |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x0100: /* clock tick time, in 840nsecs */ |
| context->Eax = GetTickCount(); |
| |
| context->Edx = context->Eax >> 22; |
| context->Eax <<= 10; /* not very precise */ |
| break; |
| |
| case 0x0101: /* current Windows time, msecs */ |
| case 0x0102: /* current VM time, msecs */ |
| context->Eax = GetTickCount(); |
| break; |
| |
| default: |
| VXD_BARF( context, "VTD" ); |
| } |
| } |
| |
| |
| /*********************************************************************** |
| * timer_thread |
| */ |
| static DWORD CALLBACK timer_thread( void *arg ) |
| { |
| DWORD *system_time = arg; |
| |
| for (;;) |
| { |
| *system_time = GetTickCount(); |
| Sleep( 55 ); |
| } |
| |
| return 0; |
| } |
| |
| |
| /*********************************************************************** |
| * __wine_vxd_timerapi (WPROCS.1490) |
| */ |
| void WINAPI __wine_vxd_timerapi( CONTEXT *context ) |
| { |
| static WORD System_Time_Selector; |
| |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] TimerAPI\n", (UINT16)service); |
| |
| switch(service) |
| { |
| case 0x0000: /* version */ |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| case 0x0009: /* get system time selector */ |
| if ( !System_Time_Selector ) |
| { |
| HANDLE16 handle = GlobalAlloc16( GMEM_FIXED, sizeof(DWORD) ); |
| System_Time_Selector = handle | 7; |
| CloseHandle( CreateThread( NULL, 0, timer_thread, GlobalLock16(handle), 0, NULL ) ); |
| } |
| SET_AX( context, System_Time_Selector ); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| VXD_BARF( context, "VTDAPI" ); |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_configmg (WPROCS.451) |
| */ |
| void WINAPI __wine_vxd_configmg( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] ConfigMG\n", (UINT16)service); |
| |
| switch(service) |
| { |
| case 0x0000: /* version */ |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| VXD_BARF( context, "CONFIGMG" ); |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_enable (WPROCS.455) |
| */ |
| void WINAPI __wine_vxd_enable( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] Enable\n", (UINT16)service); |
| |
| switch(service) |
| { |
| case 0x0000: /* version */ |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| VXD_BARF( context, "ENABLE" ); |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_apm (WPROCS.438) |
| */ |
| void WINAPI __wine_vxd_apm( CONTEXT *context ) |
| { |
| unsigned service = AX_reg(context); |
| |
| TRACE("[%04x] APM\n", (UINT16)service); |
| |
| switch(service) |
| { |
| case 0x0000: /* version */ |
| SET_AX( context, VXD_WinVersion() ); |
| RESET_CFLAG(context); |
| break; |
| |
| default: |
| VXD_BARF( context, "APM" ); |
| } |
| } |
| |
| /*********************************************************************** |
| * __wine_vxd_win32s (WPROCS.445) |
| * |
| * This is an implementation of the services of the Win32s VxD. |
| * Since official documentation of these does not seem to be available, |
| * certain arguments of some of the services remain unclear. |
| * |
| * FIXME: The following services are currently unimplemented: |
| * Exception handling (0x01, 0x1C) |
| * Debugger support (0x0C, 0x14, 0x17) |
| * Low-level memory access (0x02, 0x03, 0x0A, 0x0B) |
| * Memory Statistics (0x1B) |
| * |
| * |
| * We have a specific problem running Win32s on Linux (and probably also |
| * the other x86 unixes), since Win32s tries to allocate its main 'flat |
| * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB). |
| * The rationale for this seems to be that they want one the one hand to |
| * be able to leave the Win 3.1 memory (starting with the main DOS memory) |
| * at linear address 0, but want at other hand to have offset 0 of the |
| * flat data/code segment point to an unmapped page (to catch NULL pointer |
| * accesses). Hence they allocate the flat segments with a base of 0xffff0000 |
| * so that the Win 3.1 memory area at linear address zero shows up in the |
| * flat segments at offset 0x10000 (since linear addresses wrap around at |
| * 4GB). To compensate for that discrepancy between flat segment offsets |
| * and plain linear addresses, all flat pointers passed between the 32-bit |
| * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate |
| * direction by the glue code (mainly) in W32SKRNL and WIN32S16. |
| * |
| * The problem for us is now that Linux does not allow a LDT selector with |
| * base 0xffff0000 to be created, since it would 'see' a part of the kernel |
| * address space. To address this problem we introduce *another* offset: |
| * We add 0x10000 to every linear address we get as an argument from Win32s. |
| * This means especially that the flat code/data selectors get actually |
| * allocated with base 0x0, so that flat offsets and (real) linear addresses |
| * do again agree! In fact, every call e.g. of a Win32s VxD service now |
| * has all pointer arguments (which are offsets in the flat data segment) |
| * first reduced by 0x10000 by the W32SKRNL glue code, and then again |
| * increased by 0x10000 by *our* code. |
| * |
| * Note that to keep everything consistent, this offset has to be applied by |
| * every Wine function that operates on 'linear addresses' passed to it by |
| * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit |
| * API routines, this affects only two locations: this VxD and the DPMI |
| * handler. (NOTE: Should any Win32s application pass a linear address to |
| * any routine apart from those, e.g. some other VxD handler, that code |
| * would have to take the offset into account as well!) |
| * |
| * The offset is set the first time any application calls the GetVersion() |
| * service of the Win32s VxD. (Note that the offset is never reset.) |
| * |
| */ |
| void WINAPI __wine_vxd_win32s( CONTEXT *context ) |
| { |
| switch (AX_reg(context)) |
| { |
| case 0x0000: /* Get Version */ |
| /* |
| * Input: None |
| * |
| * Output: EAX: LoWord: Win32s Version (1.30) |
| * HiWord: VxD Version (200) |
| * |
| * EBX: Build (172) |
| * |
| * ECX: ??? (1) |
| * |
| * EDX: Debugging Flags |
| * |
| * EDI: Error Flag |
| * 0 if OK, |
| * 1 if VMCPD VxD not found |
| */ |
| |
| TRACE("GetVersion()\n"); |
| |
| context->Eax = VXD_WinVersion() | (200 << 16); |
| context->Ebx = 0; |
| context->Ecx = 0; |
| context->Edx = 0; |
| context->Edi = 0; |
| |
| /* |
| * If this is the first time we are called for this process, |
| * hack the memory image of WIN32S16 so that it doesn't try |
| * to access the GDT directly ... |
| * |
| * The first code segment of WIN32S16 (version 1.30) contains |
| * an unexported function somewhere between the exported functions |
| * SetFS and StackLinearToSegmented that tries to find a selector |
| * in the LDT that maps to the memory image of the LDT itself. |
| * If it succeeds, it stores this selector into a global variable |
| * which will be used to speed up execution by using this selector |
| * to modify the LDT directly instead of using the DPMI calls. |
| * |
| * To perform this search of the LDT, this function uses the |
| * sgdt and sldt instructions to find the linear address of |
| * the (GDT and then) LDT. While those instructions themselves |
| * execute without problem, the linear address that sgdt returns |
| * points (at least under Linux) to the kernel address space, so |
| * that any subsequent access leads to a segfault. |
| * |
| * Fortunately, WIN32S16 still contains as a fallback option the |
| * mechanism of using DPMI calls to modify LDT selectors instead |
| * of direct writes to the LDT. Thus we can circumvent the problem |
| * by simply replacing the first byte of the offending function |
| * with an 'retf' instruction. This means that the global variable |
| * supposed to contain the LDT alias selector will remain zero, |
| * and hence WIN32S16 will fall back to using DPMI calls. |
| * |
| * The heuristic we employ to _find_ that function is as follows: |
| * We search between the addresses of the exported symbols SetFS |
| * and StackLinearToSegmented for the byte sequence '0F 01 04' |
| * (this is the opcode of 'sgdt [si]'). We then search backwards |
| * from this address for the last occurrence of 'CB' (retf) that marks |
| * the end of the preceding function. The following byte (which |
| * should now be the first byte of the function we are looking for) |
| * will be replaced by 'CB' (retf). |
| * |
| * This heuristic works for the retail as well as the debug version |
| * of Win32s version 1.30. For versions earlier than that this |
| * hack should not be necessary at all, since the whole mechanism |
| * ('PERF130') was introduced only in 1.30 to improve the overall |
| * performance of Win32s. |
| */ |
| |
| if (!W32S_offset) |
| { |
| HMODULE16 hModule = GetModuleHandle16("win32s16"); |
| SEGPTR func1 = (SEGPTR)GetProcAddress16(hModule, "SetFS"); |
| SEGPTR func2 = (SEGPTR)GetProcAddress16(hModule, "StackLinearToSegmented"); |
| |
| if ( hModule && func1 && func2 |
| && SELECTOROF(func1) == SELECTOROF(func2)) |
| { |
| BYTE *start = MapSL(func1); |
| BYTE *end = MapSL(func2); |
| BYTE *p, *retv = NULL; |
| int found = 0; |
| |
| for (p = start; p < end; p++) |
| if (*p == 0xCB) found = 0, retv = p; |
| else if (*p == 0x0F) found = 1; |
| else if (*p == 0x01 && found == 1) found = 2; |
| else if (*p == 0x04 && found == 2) { found = 3; break; } |
| else found = 0; |
| |
| if (found == 3 && retv) |
| { |
| TRACE("PERF130 hack: " |
| "Replacing byte %02X at offset %04X:%04X\n", |
| *(retv+1), SELECTOROF(func1), |
| OFFSETOF(func1) + retv+1-start); |
| |
| *(retv+1) = (BYTE)0xCB; |
| } |
| } |
| } |
| |
| /* |
| * Mark process as Win32s, so that subsequent DPMI calls |
| * will perform the W32S_APP2WINE/W32S_WINE2APP address shift. |
| */ |
| W32S_offset = 0x10000; |
| break; |
| |
| |
| case 0x0001: /* Install Exception Handling */ |
| /* |
| * Input: EBX: Flat address of W32SKRNL Exception Data |
| * |
| * ECX: LoWord: Flat Code Selector |
| * HiWord: Flat Data Selector |
| * |
| * EDX: Flat address of W32SKRNL Exception Handler |
| * (this is equal to W32S_BackTo32 + 0x40) |
| * |
| * ESI: SEGPTR KERNEL.HASGPHANDLER |
| * |
| * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10) |
| * |
| * Output: EAX: 0 if OK |
| */ |
| |
| TRACE("[0001] EBX=%x ECX=%x EDX=%x ESI=%x EDI=%x\n", |
| context->Ebx, context->Ecx, context->Edx, |
| context->Esi, context->Edi); |
| |
| /* FIXME */ |
| |
| context->Eax = 0; |
| break; |
| |
| |
| case 0x0002: /* Set Page Access Flags */ |
| /* |
| * Input: EBX: New access flags |
| * Bit 2: User Page if set, Supervisor Page if clear |
| * Bit 1: Read-Write if set, Read-Only if clear |
| * |
| * ECX: Size of memory area to change |
| * |
| * EDX: Flat start address of memory area |
| * |
| * Output: EAX: Size of area changed |
| */ |
| |
| TRACE("[0002] EBX=%x ECX=%x EDX=%x\n", |
| context->Ebx, context->Ecx, context->Edx); |
| |
| /* FIXME */ |
| |
| context->Eax = context->Ecx; |
| break; |
| |
| |
| case 0x0003: /* Get Page Access Flags */ |
| /* |
| * Input: EDX: Flat address of page to query |
| * |
| * Output: EAX: Page access flags |
| * Bit 2: User Page if set, Supervisor Page if clear |
| * Bit 1: Read-Write if set, Read-Only if clear |
| */ |
| |
| TRACE("[0003] EDX=%x\n", context->Edx); |
| |
| /* FIXME */ |
| |
| context->Eax = 6; |
| break; |
| |
| |
| case 0x0004: /* Map Module */ |
| /* |
| * Input: ECX: IMTE (offset in Module Table) of new module |
| * |
| * EDX: Flat address of Win32s Module Table |
| * |
| * Output: EAX: 0 if OK |
| */ |
| |
| if (!context->Edx || CX_reg(context) == 0xFFFF) |
| { |
| TRACE("MapModule: Initialization call\n"); |
| context->Eax = 0; |
| } |
| else |
| { |
| /* |
| * Structure of a Win32s Module Table Entry: |
| */ |
| struct Win32sModule |
| { |
| DWORD flags; |
| DWORD flatBaseAddr; |
| LPCSTR moduleName; |
| LPCSTR pathName; |
| LPCSTR unknown; |
| LPBYTE baseAddr; |
| DWORD hModule; |
| DWORD relocDelta; |
| }; |
| |
| /* |
| * Note: This function should set up a demand-paged memory image |
| * of the given module. Since mmap does not allow file offsets |
| * not aligned at 1024 bytes, we simply load the image fully |
| * into memory. |
| */ |
| |
| struct Win32sModule *moduleTable = |
| (struct Win32sModule *)W32S_APP2WINE(context->Edx); |
| struct Win32sModule *module = moduleTable + context->Ecx; |
| |
| IMAGE_NT_HEADERS *nt_header = RtlImageNtHeader( (HMODULE)module->baseAddr ); |
| IMAGE_SECTION_HEADER *pe_seg = (IMAGE_SECTION_HEADER*)((char *)&nt_header->OptionalHeader + |
| nt_header->FileHeader.SizeOfOptionalHeader); |
| |
| |
| HFILE image = _lopen(module->pathName, OF_READ); |
| BOOL error = (image == HFILE_ERROR); |
| UINT i; |
| |
| TRACE("MapModule: Loading %s\n", module->pathName); |
| |
| for (i = 0; |
| !error && i < nt_header->FileHeader.NumberOfSections; |
| i++, pe_seg++) |
| if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)) |
| { |
| DWORD off = pe_seg->PointerToRawData; |
| DWORD len = pe_seg->SizeOfRawData; |
| LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress; |
| |
| TRACE("MapModule: " |
| "Section %d at %08x from %08x len %08x\n", |
| i, (DWORD)addr, off, len); |
| |
| if ( _llseek(image, off, SEEK_SET) != off |
| || _lread(image, addr, len) != len) |
| error = TRUE; |
| } |
| |
| _lclose(image); |
| |
| if (error) |
| ERR("MapModule: Unable to load %s\n", module->pathName); |
| |
| else if (module->relocDelta != 0) |
| { |
| IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory |
| + IMAGE_DIRECTORY_ENTRY_BASERELOC; |
| IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *) |
| (dir->Size? module->baseAddr + dir->VirtualAddress : 0); |
| |
| TRACE("MapModule: Reloc delta %08x\n", module->relocDelta); |
| |
| while (r && r->VirtualAddress) |
| { |
| LPBYTE page = module->baseAddr + r->VirtualAddress; |
| WORD *TypeOffset = (WORD *)(r + 1); |
| unsigned int count = (r->SizeOfBlock - sizeof(*r)) / sizeof(*TypeOffset); |
| |
| TRACE("MapModule: %d relocations for page %08x\n", |
| count, (DWORD)page); |
| |
| for(i = 0; i < count; i++) |
| { |
| int offset = TypeOffset[i] & 0xFFF; |
| int type = TypeOffset[i] >> 12; |
| switch(type) |
| { |
| case IMAGE_REL_BASED_ABSOLUTE: |
| break; |
| case IMAGE_REL_BASED_HIGH: |
| *(WORD *)(page+offset) += HIWORD(module->relocDelta); |
| break; |
| case IMAGE_REL_BASED_LOW: |
| *(WORD *)(page+offset) += LOWORD(module->relocDelta); |
| break; |
| case IMAGE_REL_BASED_HIGHLOW: |
| *(DWORD*)(page+offset) += module->relocDelta; |
| break; |
| default: |
| WARN("MapModule: Unsupported fixup type\n"); |
| break; |
| } |
| } |
| |
| r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock); |
| } |
| } |
| |
| context->Eax = 0; |
| RESET_CFLAG(context); |
| } |
| break; |
| |
| |
| case 0x0005: /* UnMap Module */ |
| /* |
| * Input: EDX: Flat address of module image |
| * |
| * Output: EAX: 1 if OK |
| */ |
| |
| TRACE("UnMapModule: %x\n", W32S_APP2WINE(context->Edx)); |
| |
| /* As we didn't map anything, there's nothing to unmap ... */ |
| |
| context->Eax = 1; |
| break; |
| |
| |
| case 0x0006: /* VirtualAlloc */ |
| /* |
| * Input: ECX: Current Process |
| * |
| * EDX: Flat address of arguments on stack |
| * |
| * DWORD *retv [out] Flat base address of allocated region |
| * LPVOID base [in] Flat address of region to reserve/commit |
| * DWORD size [in] Size of region |
| * DWORD type [in] Type of allocation |
| * DWORD prot [in] Type of access protection |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]); |
| LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]); |
| DWORD size = stack[2]; |
| DWORD type = stack[3]; |
| DWORD prot = stack[4]; |
| DWORD result; |
| |
| TRACE("VirtualAlloc(%x, %x, %x, %x, %x)\n", |
| (DWORD)retv, (DWORD)base, size, type, prot); |
| |
| if (type & 0x80000000) |
| { |
| WARN("VirtualAlloc: strange type %x\n", type); |
| type &= 0x7fffffff; |
| } |
| |
| if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY) |
| { |
| WARN("VirtualAlloc: NLS hack, allowing write access!\n"); |
| prot = PAGE_READWRITE; |
| } |
| |
| result = (DWORD)VirtualAlloc(base, size, type, prot); |
| |
| if (W32S_WINE2APP(result)) |
| *retv = W32S_WINE2APP(result), |
| context->Eax = STATUS_SUCCESS; |
| else |
| *retv = 0, |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x0007: /* VirtualFree */ |
| /* |
| * Input: ECX: Current Process |
| * |
| * EDX: Flat address of arguments on stack |
| * |
| * DWORD *retv [out] TRUE if success, FALSE if failure |
| * LPVOID base [in] Flat address of region |
| * DWORD size [in] Size of region |
| * DWORD type [in] Type of operation |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]); |
| LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]); |
| DWORD size = stack[2]; |
| DWORD type = stack[3]; |
| DWORD result; |
| |
| TRACE("VirtualFree(%x, %x, %x, %x)\n", |
| (DWORD)retv, (DWORD)base, size, type); |
| |
| result = VirtualFree(base, size, type); |
| |
| if (result) |
| *retv = TRUE, |
| context->Eax = STATUS_SUCCESS; |
| else |
| *retv = FALSE, |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x0008: /* VirtualProtect */ |
| /* |
| * Input: ECX: Current Process |
| * |
| * EDX: Flat address of arguments on stack |
| * |
| * DWORD *retv [out] TRUE if success, FALSE if failure |
| * LPVOID base [in] Flat address of region |
| * DWORD size [in] Size of region |
| * DWORD new_prot [in] Desired access protection |
| * DWORD *old_prot [out] Previous access protection |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]); |
| LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]); |
| DWORD size = stack[2]; |
| DWORD new_prot = stack[3]; |
| DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4]); |
| DWORD result; |
| |
| TRACE("VirtualProtect(%x, %x, %x, %x, %x)\n", |
| (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot); |
| |
| result = VirtualProtect(base, size, new_prot, old_prot); |
| |
| if (result) |
| *retv = TRUE, |
| context->Eax = STATUS_SUCCESS; |
| else |
| *retv = FALSE, |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x0009: /* VirtualQuery */ |
| /* |
| * Input: ECX: Current Process |
| * |
| * EDX: Flat address of arguments on stack |
| * |
| * DWORD *retv [out] Nr. bytes returned |
| * LPVOID base [in] Flat address of region |
| * LPMEMORY_BASIC_INFORMATION info [out] Info buffer |
| * DWORD len [in] Size of buffer |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]); |
| LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]); |
| PMEMORY_BASIC_INFORMATION info = |
| (PMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2]); |
| DWORD len = stack[3]; |
| DWORD result; |
| |
| TRACE("VirtualQuery(%x, %x, %x, %x)\n", |
| (DWORD)retv, (DWORD)base, (DWORD)info, len); |
| |
| result = VirtualQuery(base, info, len); |
| |
| *retv = result; |
| context->Eax = STATUS_SUCCESS; |
| } |
| break; |
| |
| |
| case 0x000A: /* SetVirtMemProcess */ |
| /* |
| * Input: ECX: Process Handle |
| * |
| * EDX: Flat address of region |
| * |
| * Output: EAX: NtStatus |
| */ |
| |
| TRACE("[000a] ECX=%x EDX=%x\n", |
| context->Ecx, context->Edx); |
| |
| /* FIXME */ |
| |
| context->Eax = STATUS_SUCCESS; |
| break; |
| |
| |
| case 0x000B: /* ??? some kind of cleanup */ |
| /* |
| * Input: ECX: Process Handle |
| * |
| * Output: EAX: NtStatus |
| */ |
| |
| TRACE("[000b] ECX=%x\n", context->Ecx); |
| |
| /* FIXME */ |
| |
| context->Eax = STATUS_SUCCESS; |
| break; |
| |
| |
| case 0x000C: /* Set Debug Flags */ |
| /* |
| * Input: EDX: Debug Flags |
| * |
| * Output: EDX: Previous Debug Flags |
| */ |
| |
| FIXME("[000c] EDX=%x\n", context->Edx); |
| |
| /* FIXME */ |
| |
| context->Edx = 0; |
| break; |
| |
| |
| case 0x000D: /* NtCreateSection */ |
| /* |
| * Input: EDX: Flat address of arguments on stack |
| * |
| * HANDLE32 *retv [out] Handle of Section created |
| * DWORD flags1 [in] (?? unknown ??) |
| * DWORD atom [in] Name of Section to create |
| * LARGE_INTEGER *size [in] Size of Section |
| * DWORD protect [in] Access protection |
| * DWORD flags2 [in] (?? unknown ??) |
| * HFILE32 hFile [in] Handle of file to map |
| * DWORD psp [in] (Win32s: PSP that hFile belongs to) |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *) W32S_APP2WINE(context->Edx); |
| HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]); |
| DWORD flags1 = stack[1]; |
| DWORD atom = stack[2]; |
| LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3]); |
| DWORD protect = stack[4]; |
| DWORD flags2 = stack[5]; |
| HANDLE hFile = DosFileHandleToWin32Handle(stack[6]); |
| DWORD psp = stack[7]; |
| |
| HANDLE result = INVALID_HANDLE_VALUE; |
| char name[128]; |
| |
| TRACE("NtCreateSection(%x, %x, %x, %x, %x, %x, %x, %x)\n", |
| (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2, |
| (DWORD)hFile, psp); |
| |
| if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name))) |
| { |
| TRACE("NtCreateSection: name=%s\n", atom? name : NULL); |
| |
| result = CreateFileMappingA(hFile, NULL, protect, |
| size? size->u.HighPart : 0, |
| size? size->u.LowPart : 0, |
| atom? name : NULL); |
| } |
| |
| if (result == INVALID_HANDLE_VALUE) |
| WARN("NtCreateSection: failed!\n"); |
| else |
| TRACE("NtCreateSection: returned %x\n", (DWORD)result); |
| |
| if (result != INVALID_HANDLE_VALUE) |
| *retv = result, |
| context->Eax = STATUS_SUCCESS; |
| else |
| *retv = result, |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x000E: /* NtOpenSection */ |
| /* |
| * Input: EDX: Flat address of arguments on stack |
| * |
| * HANDLE32 *retv [out] Handle of Section opened |
| * DWORD protect [in] Access protection |
| * DWORD atom [in] Name of Section to create |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]); |
| DWORD protect = stack[1]; |
| DWORD atom = stack[2]; |
| |
| HANDLE result = INVALID_HANDLE_VALUE; |
| char name[128]; |
| |
| TRACE("NtOpenSection(%x, %x, %x)\n", |
| (DWORD)retv, protect, atom); |
| |
| if (atom && GlobalGetAtomNameA(atom, name, sizeof(name))) |
| { |
| TRACE("NtOpenSection: name=%s\n", name); |
| |
| result = OpenFileMappingA(protect, FALSE, name); |
| } |
| |
| if (result == INVALID_HANDLE_VALUE) |
| WARN("NtOpenSection: failed!\n"); |
| else |
| TRACE("NtOpenSection: returned %x\n", (DWORD)result); |
| |
| if (result != INVALID_HANDLE_VALUE) |
| *retv = result, |
| context->Eax = STATUS_SUCCESS; |
| else |
| *retv = result, |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x000F: /* NtCloseSection */ |
| /* |
| * Input: EDX: Flat address of arguments on stack |
| * |
| * HANDLE32 handle [in] Handle of Section to close |
| * DWORD *id [out] Unique ID (?? unclear ??) |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| HANDLE handle = (HANDLE)stack[0]; |
| DWORD *id = (DWORD *)W32S_APP2WINE(stack[1]); |
| |
| TRACE("NtCloseSection(%x, %x)\n", (DWORD)handle, (DWORD)id); |
| |
| CloseHandle(handle); |
| if (id) *id = 0; /* FIXME */ |
| |
| context->Eax = STATUS_SUCCESS; |
| } |
| break; |
| |
| |
| case 0x0010: /* NtDupSection */ |
| /* |
| * Input: EDX: Flat address of arguments on stack |
| * |
| * HANDLE32 handle [in] Handle of Section to duplicate |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| HANDLE handle = (HANDLE)stack[0]; |
| HANDLE new_handle; |
| |
| TRACE("NtDupSection(%x)\n", (DWORD)handle); |
| |
| DuplicateHandle( GetCurrentProcess(), handle, |
| GetCurrentProcess(), &new_handle, |
| 0, FALSE, DUPLICATE_SAME_ACCESS ); |
| context->Eax = STATUS_SUCCESS; |
| } |
| break; |
| |
| |
| case 0x0011: /* NtMapViewOfSection */ |
| /* |
| * Input: EDX: Flat address of arguments on stack |
| * |
| * HANDLE32 SectionHandle [in] Section to be mapped |
| * DWORD ProcessHandle [in] Process to be mapped into |
| * DWORD * BaseAddress [in/out] Address to be mapped at |
| * DWORD ZeroBits [in] (?? unclear ??) |
| * DWORD CommitSize [in] (?? unclear ??) |
| * LARGE_INTEGER *SectionOffset [in] Offset within section |
| * DWORD * ViewSize [in] Size of view |
| * DWORD InheritDisposition [in] (?? unclear ??) |
| * DWORD AllocationType [in] (?? unclear ??) |
| * DWORD Protect [in] Access protection |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD * stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| HANDLE SectionHandle = (HANDLE)stack[0]; |
| DWORD ProcessHandle = stack[1]; /* ignored */ |
| DWORD * BaseAddress = (DWORD *)W32S_APP2WINE(stack[2]); |
| DWORD ZeroBits = stack[3]; |
| DWORD CommitSize = stack[4]; |
| LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5]); |
| DWORD * ViewSize = (DWORD *)W32S_APP2WINE(stack[6]); |
| DWORD InheritDisposition = stack[7]; |
| DWORD AllocationType = stack[8]; |
| DWORD Protect = stack[9]; |
| |
| LPBYTE address = (LPBYTE)(BaseAddress? |
| W32S_APP2WINE(*BaseAddress) : 0); |
| DWORD access = 0, result; |
| |
| switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE)) |
| { |
| case PAGE_READONLY: access = FILE_MAP_READ; break; |
| case PAGE_READWRITE: access = FILE_MAP_WRITE; break; |
| case PAGE_WRITECOPY: access = FILE_MAP_COPY; break; |
| |
| case PAGE_EXECUTE_READ: access = FILE_MAP_READ; break; |
| case PAGE_EXECUTE_READWRITE: access = FILE_MAP_WRITE; break; |
| case PAGE_EXECUTE_WRITECOPY: access = FILE_MAP_COPY; break; |
| } |
| |
| TRACE("NtMapViewOfSection" |
| "(%x, %x, %x, %x, %x, %x, %x, %x, %x, %x)\n", |
| (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress, |
| ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize, |
| InheritDisposition, AllocationType, Protect); |
| TRACE("NtMapViewOfSection: " |
| "base=%x, offset=%x, size=%x, access=%x\n", |
| (DWORD)address, SectionOffset? SectionOffset->u.LowPart : 0, |
| ViewSize? *ViewSize : 0, access); |
| |
| result = (DWORD)MapViewOfFileEx(SectionHandle, access, |
| SectionOffset? SectionOffset->u.HighPart : 0, |
| SectionOffset? SectionOffset->u.LowPart : 0, |
| ViewSize? *ViewSize : 0, address); |
| |
| TRACE("NtMapViewOfSection: result=%x\n", result); |
| |
| if (W32S_WINE2APP(result)) |
| { |
| if (BaseAddress) *BaseAddress = W32S_WINE2APP(result); |
| context->Eax = STATUS_SUCCESS; |
| } |
| else |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x0012: /* NtUnmapViewOfSection */ |
| /* |
| * Input: EDX: Flat address of arguments on stack |
| * |
| * DWORD ProcessHandle [in] Process (defining address space) |
| * LPBYTE BaseAddress [in] Base address of view to be unmapped |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| DWORD ProcessHandle = stack[0]; /* ignored */ |
| LPBYTE BaseAddress = (LPBYTE)W32S_APP2WINE(stack[1]); |
| |
| TRACE("NtUnmapViewOfSection(%x, %x)\n", |
| ProcessHandle, (DWORD)BaseAddress); |
| |
| UnmapViewOfFile(BaseAddress); |
| |
| context->Eax = STATUS_SUCCESS; |
| } |
| break; |
| |
| |
| case 0x0013: /* NtFlushVirtualMemory */ |
| /* |
| * Input: EDX: Flat address of arguments on stack |
| * |
| * DWORD ProcessHandle [in] Process (defining address space) |
| * LPBYTE *BaseAddress [in?] Base address of range to be flushed |
| * DWORD *ViewSize [in?] Number of bytes to be flushed |
| * DWORD *unknown [???] (?? unknown ??) |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| DWORD ProcessHandle = stack[0]; /* ignored */ |
| DWORD *BaseAddress = (DWORD *)W32S_APP2WINE(stack[1]); |
| DWORD *ViewSize = (DWORD *)W32S_APP2WINE(stack[2]); |
| DWORD *unknown = (DWORD *)W32S_APP2WINE(stack[3]); |
| |
| LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress) : 0); |
| DWORD size = ViewSize? *ViewSize : 0; |
| |
| TRACE("NtFlushVirtualMemory(%x, %x, %x, %x)\n", |
| ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize, |
| (DWORD)unknown); |
| TRACE("NtFlushVirtualMemory: base=%x, size=%x\n", |
| (DWORD)address, size); |
| |
| FlushViewOfFile(address, size); |
| |
| context->Eax = STATUS_SUCCESS; |
| } |
| break; |
| |
| |
| case 0x0014: /* Get/Set Debug Registers */ |
| /* |
| * Input: ECX: 0 if Get, 1 if Set |
| * |
| * EDX: Get: Flat address of buffer to receive values of |
| * debug registers DR0 .. DR7 |
| * Set: Flat address of buffer containing values of |
| * debug registers DR0 .. DR7 to be set |
| * Output: None |
| */ |
| |
| FIXME("[0014] ECX=%x EDX=%x\n", |
| context->Ecx, context->Edx); |
| |
| /* FIXME */ |
| break; |
| |
| |
| case 0x0015: /* Set Coprocessor Emulation Flag */ |
| /* |
| * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation |
| * |
| * Output: None |
| */ |
| |
| TRACE("[0015] EDX=%x\n", context->Edx); |
| |
| /* We don't care, as we always have a coprocessor anyway */ |
| break; |
| |
| |
| case 0x0016: /* Init Win32S VxD PSP */ |
| /* |
| * If called to query required PSP size: |
| * |
| * Input: EBX: 0 |
| * Output: EDX: Required size of Win32s VxD PSP |
| * |
| * If called to initialize allocated PSP: |
| * |
| * Input: EBX: LoWord: Selector of Win32s VxD PSP |
| * HiWord: Paragraph of Win32s VxD PSP (DOSMEM) |
| * Output: None |
| */ |
| |
| if (context->Ebx == 0) |
| context->Edx = 0x80; |
| else |
| { |
| PDB16 *psp = MapSL( MAKESEGPTR( BX_reg(context), 0 )); |
| psp->nbFiles = 32; |
| psp->fileHandlesPtr = MAKELONG(HIWORD(context->Ebx), 0x5c); |
| memset((LPBYTE)psp + 0x5c, '\xFF', 32); |
| } |
| break; |
| |
| |
| case 0x0017: /* Set Break Point */ |
| /* |
| * Input: EBX: Offset of Break Point |
| * CX: Selector of Break Point |
| * |
| * Output: None |
| */ |
| |
| FIXME("[0017] EBX=%x CX=%x\n", |
| context->Ebx, CX_reg(context)); |
| |
| /* FIXME */ |
| break; |
| |
| |
| case 0x0018: /* VirtualLock */ |
| /* |
| * Input: ECX: Current Process |
| * |
| * EDX: Flat address of arguments on stack |
| * |
| * DWORD *retv [out] TRUE if success, FALSE if failure |
| * LPVOID base [in] Flat address of range to lock |
| * DWORD size [in] Size of range |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]); |
| LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]); |
| DWORD size = stack[2]; |
| DWORD result; |
| |
| TRACE("VirtualLock(%x, %x, %x)\n", |
| (DWORD)retv, (DWORD)base, size); |
| |
| result = VirtualLock(base, size); |
| |
| if (result) |
| *retv = TRUE, |
| context->Eax = STATUS_SUCCESS; |
| else |
| *retv = FALSE, |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x0019: /* VirtualUnlock */ |
| /* |
| * Input: ECX: Current Process |
| * |
| * EDX: Flat address of arguments on stack |
| * |
| * DWORD *retv [out] TRUE if success, FALSE if failure |
| * LPVOID base [in] Flat address of range to unlock |
| * DWORD size [in] Size of range |
| * |
| * Output: EAX: NtStatus |
| */ |
| { |
| DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx); |
| DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]); |
| LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]); |
| DWORD size = stack[2]; |
| DWORD result; |
| |
| TRACE("VirtualUnlock(%x, %x, %x)\n", |
| (DWORD)retv, (DWORD)base, size); |
| |
| result = VirtualUnlock(base, size); |
| |
| if (result) |
| *retv = TRUE, |
| context->Eax = STATUS_SUCCESS; |
| else |
| *retv = FALSE, |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x001A: /* KGetSystemInfo */ |
| /* |
| * Input: None |
| * |
| * Output: ECX: Start of sparse memory arena |
| * EDX: End of sparse memory arena |
| */ |
| |
| TRACE("KGetSystemInfo()\n"); |
| |
| /* |
| * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as |
| * sparse memory arena. We do it the other way around, since |
| * we have to reserve 3GB - 4GB for Linux, and thus use |
| * 0GB - 3GB as sparse memory arena. |
| * |
| * FIXME: What about other OSes ? |
| */ |
| |
| context->Ecx = W32S_WINE2APP(0x00000000); |
| context->Edx = W32S_WINE2APP(0xbfffffff); |
| break; |
| |
| |
| case 0x001B: /* KGlobalMemStat */ |
| /* |
| * Input: ESI: Flat address of buffer to receive memory info |
| * |
| * Output: None |
| */ |
| { |
| struct Win32sMemoryInfo |
| { |
| DWORD DIPhys_Count; /* Total physical pages */ |
| DWORD DIFree_Count; /* Free physical pages */ |
| DWORD DILin_Total_Count; /* Total virtual pages (private arena) */ |
| DWORD DILin_Total_Free; /* Free virtual pages (private arena) */ |
| |
| DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */ |
| DWORD SparseFree; /* Free size of sparse arena (bytes ?) */ |
| }; |
| |
| struct Win32sMemoryInfo *info = |
| (struct Win32sMemoryInfo *)W32S_APP2WINE(context->Esi); |
| |
| FIXME("KGlobalMemStat(%x)\n", (DWORD)info); |
| |
| /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x001C: /* Enable/Disable Exceptions */ |
| /* |
| * Input: ECX: 0 to disable, 1 to enable exception handling |
| * |
| * Output: None |
| */ |
| |
| TRACE("[001c] ECX=%x\n", context->Ecx); |
| |
| /* FIXME */ |
| break; |
| |
| |
| case 0x001D: /* VirtualAlloc called from 16-bit code */ |
| /* |
| * Input: EDX: Segmented address of arguments on stack |
| * |
| * LPVOID base [in] Flat address of region to reserve/commit |
| * DWORD size [in] Size of region |
| * DWORD type [in] Type of allocation |
| * DWORD prot [in] Type of access protection |
| * |
| * Output: EAX: NtStatus |
| * EDX: Flat base address of allocated region |
| */ |
| { |
| DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) )); |
| LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]); |
| DWORD size = stack[1]; |
| DWORD type = stack[2]; |
| DWORD prot = stack[3]; |
| DWORD result; |
| |
| TRACE("VirtualAlloc16(%x, %x, %x, %x)\n", |
| (DWORD)base, size, type, prot); |
| |
| if (type & 0x80000000) |
| { |
| WARN("VirtualAlloc16: strange type %x\n", type); |
| type &= 0x7fffffff; |
| } |
| |
| result = (DWORD)VirtualAlloc(base, size, type, prot); |
| |
| if (W32S_WINE2APP(result)) |
| context->Edx = W32S_WINE2APP(result), |
| context->Eax = STATUS_SUCCESS; |
| else |
| context->Edx = 0, |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| TRACE("VirtualAlloc16: returning base %x\n", context->Edx); |
| } |
| break; |
| |
| |
| case 0x001E: /* VirtualFree called from 16-bit code */ |
| /* |
| * Input: EDX: Segmented address of arguments on stack |
| * |
| * LPVOID base [in] Flat address of region |
| * DWORD size [in] Size of region |
| * DWORD type [in] Type of operation |
| * |
| * Output: EAX: NtStatus |
| * EDX: TRUE if success, FALSE if failure |
| */ |
| { |
| DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) )); |
| LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]); |
| DWORD size = stack[1]; |
| DWORD type = stack[2]; |
| DWORD result; |
| |
| TRACE("VirtualFree16(%x, %x, %x)\n", |
| (DWORD)base, size, type); |
| |
| result = VirtualFree(base, size, type); |
| |
| if (result) |
| context->Edx = TRUE, |
| context->Eax = STATUS_SUCCESS; |
| else |
| context->Edx = FALSE, |
| context->Eax = STATUS_NO_MEMORY; /* FIXME */ |
| } |
| break; |
| |
| |
| case 0x001F: /* FWorkingSetSize */ |
| /* |
| * Input: EDX: 0 if Get, 1 if Set |
| * |
| * ECX: Get: Buffer to receive Working Set Size |
| * Set: Buffer containing Working Set Size |
| * |
| * Output: NtStatus |
| */ |
| { |
| DWORD *ptr = (DWORD *)W32S_APP2WINE(context->Ecx); |
| BOOL set = context->Edx; |
| |
| TRACE("FWorkingSetSize(%x, %x)\n", (DWORD)ptr, (DWORD)set); |
| |
| if (set) |
| /* We do it differently ... */; |
| else |
| *ptr = 0x100; |
| |
| context->Eax = STATUS_SUCCESS; |
| } |
| break; |
| |
| default: |
| VXD_BARF( context, "W32S" ); |
| } |
| } |