4 * Copyright 1995 Anand Kumria
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
28 #include <sys/types.h>
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
36 #define WIN32_NO_STATUS
43 #include "wine/winbase16.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(vxd);
49 #define W32S_APP2WINE(addr) ((addr)? (DWORD)(addr) + W32S_offset : 0)
50 #define W32S_WINE2APP(addr) ((addr)? (DWORD)(addr) - W32S_offset : 0)
52 #define VXD_BARF(context,name) \
53 TRACE( "vxd %s: unknown/not implemented parameters:\n" \
54 "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
55 "SI %04x, DI %04x, DS %04x, ES %04x\n", \
56 (name), (name), AX_reg(context), BX_reg(context), \
57 CX_reg(context), DX_reg(context), SI_reg(context), \
58 DI_reg(context), (WORD)context->SegDs, (WORD)context->SegEs )
60 static UINT W32S_offset;
62 static WORD VXD_WinVersion(void)
64 WORD version = LOWORD(GetVersion16());
65 return (version >> 8) | (version << 8);
68 /***********************************************************************
69 * VXD_VMM (WPROCS.401)
71 void WINAPI VXD_VMM ( CONTEXT86 *context )
73 unsigned service = AX_reg(context);
75 TRACE("[%04x] VMM\n", (UINT16)service);
79 case 0x0000: /* version */
80 SET_AX( context, VXD_WinVersion() );
84 case 0x026d: /* Get_Debug_Flag '/m' */
85 case 0x026e: /* Get_Debug_Flag '/n' */
91 VXD_BARF( context, "VMM" );
95 /***********************************************************************
96 * VXD_PageFile (WPROCS.433)
98 void WINAPI VXD_PageFile( CONTEXT86 *context )
100 unsigned service = AX_reg(context);
102 /* taken from Ralf Brown's Interrupt List */
104 TRACE("[%04x] PageFile\n", (UINT16)service );
108 case 0x00: /* get version, is this windows version? */
109 TRACE("returning version\n");
110 SET_AX( context, VXD_WinVersion() );
111 RESET_CFLAG(context);
114 case 0x01: /* get swap file info */
115 TRACE("VxD PageFile: returning swap file info\n");
116 SET_AX( context, 0x00 ); /* paging disabled */
117 context->Ecx = 0; /* maximum size of paging file */
118 /* FIXME: do I touch DS:SI or DS:DI? */
119 RESET_CFLAG(context);
122 case 0x02: /* delete permanent swap on exit */
123 TRACE("VxD PageFile: supposed to delete swap\n");
124 RESET_CFLAG(context);
127 case 0x03: /* current temporary swap file size */
128 TRACE("VxD PageFile: what is current temp. swap size\n");
129 RESET_CFLAG(context);
132 case 0x04: /* read or write?? INTERRUP.D */
133 case 0x05: /* cancel?? INTERRUP.D */
134 case 0x06: /* test I/O valid INTERRUP.D */
136 VXD_BARF( context, "pagefile" );
141 /***********************************************************************
142 * VXD_Reboot (WPROCS.409)
144 void WINAPI VXD_Reboot ( CONTEXT86 *context )
146 unsigned service = AX_reg(context);
148 TRACE("[%04x] Reboot\n", (UINT16)service);
152 case 0x0000: /* version */
153 SET_AX( context, VXD_WinVersion() );
154 RESET_CFLAG(context);
158 VXD_BARF( context, "REBOOT" );
162 /***********************************************************************
163 * VXD_VDD (WPROCS.410)
165 void WINAPI VXD_VDD ( CONTEXT86 *context )
167 unsigned service = AX_reg(context);
169 TRACE("[%04x] VDD\n", (UINT16)service);
173 case 0x0000: /* version */
174 SET_AX( context, VXD_WinVersion() );
175 RESET_CFLAG(context);
179 VXD_BARF( context, "VDD" );
183 /***********************************************************************
184 * VXD_VMD (WPROCS.412)
186 void WINAPI VXD_VMD ( CONTEXT86 *context )
188 unsigned service = AX_reg(context);
190 TRACE("[%04x] VMD\n", (UINT16)service);
194 case 0x0000: /* version */
195 SET_AX( context, VXD_WinVersion() );
196 RESET_CFLAG(context);
200 VXD_BARF( context, "VMD" );
204 /***********************************************************************
205 * VXD_VXDLoader (WPROCS.439)
207 void WINAPI VXD_VXDLoader( CONTEXT86 *context )
209 unsigned service = AX_reg(context);
211 TRACE("[%04x] VXDLoader\n", (UINT16)service);
215 case 0x0000: /* get version */
216 TRACE("returning version\n");
217 SET_AX( context, 0x0000 );
218 SET_DX( context, VXD_WinVersion() );
219 RESET_CFLAG(context);
222 case 0x0001: /* load device */
223 FIXME("load device %04x:%04x (%s)\n",
224 context->SegDs, DX_reg(context),
225 debugstr_a(MapSL(MAKESEGPTR(context->SegDs, DX_reg(context)))));
226 SET_AX( context, 0x0000 );
227 context->SegEs = 0x0000;
228 SET_DI( context, 0x0000 );
229 RESET_CFLAG(context);
232 case 0x0002: /* unload device */
233 FIXME("unload device (%08x)\n", context->Ebx);
234 SET_AX( context, 0x0000 );
235 RESET_CFLAG(context);
239 VXD_BARF( context, "VXDLDR" );
240 SET_AX( context, 0x000B ); /* invalid function number */
246 /***********************************************************************
247 * VXD_Shell (WPROCS.423)
249 void WINAPI VXD_Shell( CONTEXT86 *context )
251 unsigned service = DX_reg(context);
253 TRACE("[%04x] Shell\n", (UINT16)service);
255 switch (service) /* Ralf Brown says EDX, but I use DX instead */
258 TRACE("returning version\n");
259 SET_AX( context, VXD_WinVersion() );
260 context->Ebx = 1; /* system VM Handle */
266 /* SHELL_SYSMODAL_Message
267 ebx virtual machine handle
268 eax message box flags
269 ecx address of message
270 edi address of caption
271 return response in eax
275 ebx virtual machine handle
276 eax message box flags
277 ecx address of message
278 edi address of caption
280 edx reference data for callback
281 return response in eax
284 VXD_BARF( context, "shell" );
287 case 0x0006: /* SHELL_Get_VM_State */
288 TRACE("VxD Shell: returning VM state\n");
289 /* Actually we don't, not yet. We have to return a structure
290 * and I am not to sure how to set it up and return it yet,
291 * so for now let's do nothing. I can (hopefully) get this
292 * by the next release
294 /* RESET_CFLAG(context); */
313 VXD_BARF( context, "SHELL" );
316 /* the new Win95 shell API */
317 case 0x0100: /* get version */
318 SET_AX( context, VXD_WinVersion() );
321 case 0x0104: /* retrieve Hook_Properties list */
322 case 0x0105: /* call Hook_Properties callbacks */
323 VXD_BARF( context, "SHELL" );
326 case 0x0106: /* install timeout callback */
327 TRACE("VxD Shell: ignoring shell callback (%d sec.)\n", context->Ebx);
331 case 0x0107: /* get version of any VxD */
333 VXD_BARF( context, "SHELL" );
339 /***********************************************************************
340 * VXD_Comm (WPROCS.414)
342 void WINAPI VXD_Comm( CONTEXT86 *context )
344 unsigned service = AX_reg(context);
346 TRACE("[%04x] Comm\n", (UINT16)service);
350 case 0x0000: /* get version */
351 TRACE("returning version\n");
352 SET_AX( context, VXD_WinVersion() );
353 RESET_CFLAG(context);
356 case 0x0001: /* set port global */
357 case 0x0002: /* get focus */
358 case 0x0003: /* virtualise port */
360 VXD_BARF( context, "comm" );
364 /***********************************************************************
365 * VXD_Timer (WPROCS.405)
367 void WINAPI VXD_Timer( CONTEXT86 *context )
369 unsigned service = AX_reg(context);
371 TRACE("[%04x] Virtual Timer\n", (UINT16)service);
375 case 0x0000: /* version */
376 SET_AX( context, VXD_WinVersion() );
377 RESET_CFLAG(context);
380 case 0x0100: /* clock tick time, in 840nsecs */
381 context->Eax = GetTickCount();
383 context->Edx = context->Eax >> 22;
384 context->Eax <<= 10; /* not very precise */
387 case 0x0101: /* current Windows time, msecs */
388 case 0x0102: /* current VM time, msecs */
389 context->Eax = GetTickCount();
393 VXD_BARF( context, "VTD" );
398 /***********************************************************************
401 static DWORD CALLBACK timer_thread( void *arg )
403 DWORD *system_time = arg;
407 *system_time = GetTickCount();
413 /***********************************************************************
414 * VXD_TimerAPI (WPROCS.1490)
416 void WINAPI VXD_TimerAPI ( CONTEXT86 *context )
418 static WORD System_Time_Selector;
420 unsigned service = AX_reg(context);
422 TRACE("[%04x] TimerAPI\n", (UINT16)service);
426 case 0x0000: /* version */
427 SET_AX( context, VXD_WinVersion() );
428 RESET_CFLAG(context);
431 case 0x0009: /* get system time selector */
432 if ( !System_Time_Selector )
434 HANDLE16 handle = GlobalAlloc16( GMEM_FIXED, sizeof(DWORD) );
435 System_Time_Selector = handle | 7;
436 CloseHandle( CreateThread( NULL, 0, timer_thread, GlobalLock16(handle), 0, NULL ) );
438 SET_AX( context, System_Time_Selector );
439 RESET_CFLAG(context);
443 VXD_BARF( context, "VTDAPI" );
447 /***********************************************************************
448 * VXD_ConfigMG (WPROCS.451)
450 void WINAPI VXD_ConfigMG ( CONTEXT86 *context )
452 unsigned service = AX_reg(context);
454 TRACE("[%04x] ConfigMG\n", (UINT16)service);
458 case 0x0000: /* version */
459 SET_AX( context, VXD_WinVersion() );
460 RESET_CFLAG(context);
464 VXD_BARF( context, "CONFIGMG" );
468 /***********************************************************************
469 * VXD_Enable (WPROCS.455)
471 void WINAPI VXD_Enable ( CONTEXT86 *context )
473 unsigned service = AX_reg(context);
475 TRACE("[%04x] Enable\n", (UINT16)service);
479 case 0x0000: /* version */
480 SET_AX( context, VXD_WinVersion() );
481 RESET_CFLAG(context);
485 VXD_BARF( context, "ENABLE" );
489 /***********************************************************************
490 * VXD_APM (WPROCS.438)
492 void WINAPI VXD_APM ( CONTEXT86 *context )
494 unsigned service = AX_reg(context);
496 TRACE("[%04x] APM\n", (UINT16)service);
500 case 0x0000: /* version */
501 SET_AX( context, VXD_WinVersion() );
502 RESET_CFLAG(context);
506 VXD_BARF( context, "APM" );
510 /***********************************************************************
511 * VXD_Win32s (WPROCS.445)
513 * This is an implementation of the services of the Win32s VxD.
514 * Since official documentation of these does not seem to be available,
515 * certain arguments of some of the services remain unclear.
517 * FIXME: The following services are currently unimplemented:
518 * Exception handling (0x01, 0x1C)
519 * Debugger support (0x0C, 0x14, 0x17)
520 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
521 * Memory Statistics (0x1B)
524 * We have a specific problem running Win32s on Linux (and probably also
525 * the other x86 unixes), since Win32s tries to allocate its main 'flat
526 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
527 * The rationale for this seems to be that they want one the one hand to
528 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
529 * at linear address 0, but want at other hand to have offset 0 of the
530 * flat data/code segment point to an unmapped page (to catch NULL pointer
531 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
532 * so that the Win 3.1 memory area at linear address zero shows up in the
533 * flat segments at offset 0x10000 (since linear addresses wrap around at
534 * 4GB). To compensate for that discrepancy between flat segment offsets
535 * and plain linear addresses, all flat pointers passed between the 32-bit
536 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
537 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
539 * The problem for us is now that Linux does not allow a LDT selector with
540 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
541 * address space. To address this problem we introduce *another* offset:
542 * We add 0x10000 to every linear address we get as an argument from Win32s.
543 * This means especially that the flat code/data selectors get actually
544 * allocated with base 0x0, so that flat offsets and (real) linear addresses
545 * do again agree! In fact, every call e.g. of a Win32s VxD service now
546 * has all pointer arguments (which are offsets in the flat data segment)
547 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
548 * increased by 0x10000 by *our* code.
550 * Note that to keep everything consistent, this offset has to be applied by
551 * every Wine function that operates on 'linear addresses' passed to it by
552 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
553 * API routines, this affects only two locations: this VxD and the DPMI
554 * handler. (NOTE: Should any Win32s application pass a linear address to
555 * any routine apart from those, e.g. some other VxD handler, that code
556 * would have to take the offset into account as well!)
558 * The offset is set the first time any application calls the GetVersion()
559 * service of the Win32s VxD. (Note that the offset is never reset.)
563 void WINAPI VXD_Win32s( CONTEXT86 *context )
565 switch (AX_reg(context))
567 case 0x0000: /* Get Version */
571 * Output: EAX: LoWord: Win32s Version (1.30)
572 * HiWord: VxD Version (200)
578 * EDX: Debugging Flags
582 * 1 if VMCPD VxD not found
585 TRACE("GetVersion()\n");
587 context->Eax = VXD_WinVersion() | (200 << 16);
594 * If this is the first time we are called for this process,
595 * hack the memory image of WIN32S16 so that it doesn't try
596 * to access the GDT directly ...
598 * The first code segment of WIN32S16 (version 1.30) contains
599 * an unexported function somewhere between the exported functions
600 * SetFS and StackLinearToSegmented that tries to find a selector
601 * in the LDT that maps to the memory image of the LDT itself.
602 * If it succeeds, it stores this selector into a global variable
603 * which will be used to speed up execution by using this selector
604 * to modify the LDT directly instead of using the DPMI calls.
606 * To perform this search of the LDT, this function uses the
607 * sgdt and sldt instructions to find the linear address of
608 * the (GDT and then) LDT. While those instructions themselves
609 * execute without problem, the linear address that sgdt returns
610 * points (at least under Linux) to the kernel address space, so
611 * that any subsequent access leads to a segfault.
613 * Fortunately, WIN32S16 still contains as a fallback option the
614 * mechanism of using DPMI calls to modify LDT selectors instead
615 * of direct writes to the LDT. Thus we can circumvent the problem
616 * by simply replacing the first byte of the offending function
617 * with an 'retf' instruction. This means that the global variable
618 * supposed to contain the LDT alias selector will remain zero,
619 * and hence WIN32S16 will fall back to using DPMI calls.
621 * The heuristic we employ to _find_ that function is as follows:
622 * We search between the addresses of the exported symbols SetFS
623 * and StackLinearToSegmented for the byte sequence '0F 01 04'
624 * (this is the opcode of 'sgdt [si]'). We then search backwards
625 * from this address for the last occurrence of 'CB' (retf) that marks
626 * the end of the preceding function. The following byte (which
627 * should now be the first byte of the function we are looking for)
628 * will be replaced by 'CB' (retf).
630 * This heuristic works for the retail as well as the debug version
631 * of Win32s version 1.30. For versions earlier than that this
632 * hack should not be necessary at all, since the whole mechanism
633 * ('PERF130') was introduced only in 1.30 to improve the overall
634 * performance of Win32s.
639 HMODULE16 hModule = GetModuleHandle16("win32s16");
640 SEGPTR func1 = (SEGPTR)GetProcAddress16(hModule, "SetFS");
641 SEGPTR func2 = (SEGPTR)GetProcAddress16(hModule, "StackLinearToSegmented");
643 if ( hModule && func1 && func2
644 && SELECTOROF(func1) == SELECTOROF(func2))
646 BYTE *start = MapSL(func1);
647 BYTE *end = MapSL(func2);
648 BYTE *p, *retv = NULL;
651 for (p = start; p < end; p++)
652 if (*p == 0xCB) found = 0, retv = p;
653 else if (*p == 0x0F) found = 1;
654 else if (*p == 0x01 && found == 1) found = 2;
655 else if (*p == 0x04 && found == 2) { found = 3; break; }
658 if (found == 3 && retv)
660 TRACE("PERF130 hack: "
661 "Replacing byte %02X at offset %04X:%04X\n",
662 *(retv+1), SELECTOROF(func1),
663 OFFSETOF(func1) + retv+1-start);
665 *(retv+1) = (BYTE)0xCB;
671 * Mark process as Win32s, so that subsequent DPMI calls
672 * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
674 W32S_offset = 0x10000;
678 case 0x0001: /* Install Exception Handling */
680 * Input: EBX: Flat address of W32SKRNL Exception Data
682 * ECX: LoWord: Flat Code Selector
683 * HiWord: Flat Data Selector
685 * EDX: Flat address of W32SKRNL Exception Handler
686 * (this is equal to W32S_BackTo32 + 0x40)
688 * ESI: SEGPTR KERNEL.HASGPHANDLER
690 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
692 * Output: EAX: 0 if OK
695 TRACE("[0001] EBX=%x ECX=%x EDX=%x ESI=%x EDI=%x\n",
696 context->Ebx, context->Ecx, context->Edx,
697 context->Esi, context->Edi);
705 case 0x0002: /* Set Page Access Flags */
707 * Input: EBX: New access flags
708 * Bit 2: User Page if set, Supervisor Page if clear
709 * Bit 1: Read-Write if set, Read-Only if clear
711 * ECX: Size of memory area to change
713 * EDX: Flat start address of memory area
715 * Output: EAX: Size of area changed
718 TRACE("[0002] EBX=%x ECX=%x EDX=%x\n",
719 context->Ebx, context->Ecx, context->Edx);
723 context->Eax = context->Ecx;
727 case 0x0003: /* Get Page Access Flags */
729 * Input: EDX: Flat address of page to query
731 * Output: EAX: Page access flags
732 * Bit 2: User Page if set, Supervisor Page if clear
733 * Bit 1: Read-Write if set, Read-Only if clear
736 TRACE("[0003] EDX=%x\n", context->Edx);
744 case 0x0004: /* Map Module */
746 * Input: ECX: IMTE (offset in Module Table) of new module
748 * EDX: Flat address of Win32s Module Table
750 * Output: EAX: 0 if OK
753 if (!context->Edx || CX_reg(context) == 0xFFFF)
755 TRACE("MapModule: Initialization call\n");
761 * Structure of a Win32s Module Table Entry:
776 * Note: This function should set up a demand-paged memory image
777 * of the given module. Since mmap does not allow file offsets
778 * not aligned at 1024 bytes, we simply load the image fully
782 struct Win32sModule *moduleTable =
783 (struct Win32sModule *)W32S_APP2WINE(context->Edx);
784 struct Win32sModule *module = moduleTable + context->Ecx;
786 IMAGE_NT_HEADERS *nt_header = RtlImageNtHeader( (HMODULE)module->baseAddr );
787 IMAGE_SECTION_HEADER *pe_seg = (IMAGE_SECTION_HEADER*)((char *)&nt_header->OptionalHeader +
788 nt_header->FileHeader.SizeOfOptionalHeader);
791 HFILE image = _lopen(module->pathName, OF_READ);
792 BOOL error = (image == HFILE_ERROR);
795 TRACE("MapModule: Loading %s\n", module->pathName);
798 !error && i < nt_header->FileHeader.NumberOfSections;
800 if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
802 DWORD off = pe_seg->PointerToRawData;
803 DWORD len = pe_seg->SizeOfRawData;
804 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
807 "Section %d at %08x from %08x len %08x\n",
808 i, (DWORD)addr, off, len);
810 if ( _llseek(image, off, SEEK_SET) != off
811 || _lread(image, addr, len) != len)
818 ERR("MapModule: Unable to load %s\n", module->pathName);
820 else if (module->relocDelta != 0)
822 IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
823 + IMAGE_DIRECTORY_ENTRY_BASERELOC;
824 IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
825 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
827 TRACE("MapModule: Reloc delta %08x\n", module->relocDelta);
829 while (r && r->VirtualAddress)
831 LPBYTE page = module->baseAddr + r->VirtualAddress;
832 WORD *TypeOffset = (WORD *)(r + 1);
833 unsigned int count = (r->SizeOfBlock - sizeof(*r)) / sizeof(*TypeOffset);
835 TRACE("MapModule: %d relocations for page %08x\n",
838 for(i = 0; i < count; i++)
840 int offset = TypeOffset[i] & 0xFFF;
841 int type = TypeOffset[i] >> 12;
844 case IMAGE_REL_BASED_ABSOLUTE:
846 case IMAGE_REL_BASED_HIGH:
847 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
849 case IMAGE_REL_BASED_LOW:
850 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
852 case IMAGE_REL_BASED_HIGHLOW:
853 *(DWORD*)(page+offset) += module->relocDelta;
856 WARN("MapModule: Unsupported fixup type\n");
861 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
866 RESET_CFLAG(context);
871 case 0x0005: /* UnMap Module */
873 * Input: EDX: Flat address of module image
875 * Output: EAX: 1 if OK
878 TRACE("UnMapModule: %x\n", W32S_APP2WINE(context->Edx));
880 /* As we didn't map anything, there's nothing to unmap ... */
886 case 0x0006: /* VirtualAlloc */
888 * Input: ECX: Current Process
890 * EDX: Flat address of arguments on stack
892 * DWORD *retv [out] Flat base address of allocated region
893 * LPVOID base [in] Flat address of region to reserve/commit
894 * DWORD size [in] Size of region
895 * DWORD type [in] Type of allocation
896 * DWORD prot [in] Type of access protection
898 * Output: EAX: NtStatus
901 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
902 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
903 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
904 DWORD size = stack[2];
905 DWORD type = stack[3];
906 DWORD prot = stack[4];
909 TRACE("VirtualAlloc(%x, %x, %x, %x, %x)\n",
910 (DWORD)retv, (DWORD)base, size, type, prot);
912 if (type & 0x80000000)
914 WARN("VirtualAlloc: strange type %x\n", type);
918 if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
920 WARN("VirtualAlloc: NLS hack, allowing write access!\n");
921 prot = PAGE_READWRITE;
924 result = (DWORD)VirtualAlloc(base, size, type, prot);
926 if (W32S_WINE2APP(result))
927 *retv = W32S_WINE2APP(result),
928 context->Eax = STATUS_SUCCESS;
931 context->Eax = STATUS_NO_MEMORY; /* FIXME */
936 case 0x0007: /* VirtualFree */
938 * Input: ECX: Current Process
940 * EDX: Flat address of arguments on stack
942 * DWORD *retv [out] TRUE if success, FALSE if failure
943 * LPVOID base [in] Flat address of region
944 * DWORD size [in] Size of region
945 * DWORD type [in] Type of operation
947 * Output: EAX: NtStatus
950 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
951 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
952 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
953 DWORD size = stack[2];
954 DWORD type = stack[3];
957 TRACE("VirtualFree(%x, %x, %x, %x)\n",
958 (DWORD)retv, (DWORD)base, size, type);
960 result = VirtualFree(base, size, type);
964 context->Eax = STATUS_SUCCESS;
967 context->Eax = STATUS_NO_MEMORY; /* FIXME */
972 case 0x0008: /* VirtualProtect */
974 * Input: ECX: Current Process
976 * EDX: Flat address of arguments on stack
978 * DWORD *retv [out] TRUE if success, FALSE if failure
979 * LPVOID base [in] Flat address of region
980 * DWORD size [in] Size of region
981 * DWORD new_prot [in] Desired access protection
982 * DWORD *old_prot [out] Previous access protection
984 * Output: EAX: NtStatus
987 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
988 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
989 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
990 DWORD size = stack[2];
991 DWORD new_prot = stack[3];
992 DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4]);
995 TRACE("VirtualProtect(%x, %x, %x, %x, %x)\n",
996 (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
998 result = VirtualProtect(base, size, new_prot, old_prot);
1002 context->Eax = STATUS_SUCCESS;
1005 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1010 case 0x0009: /* VirtualQuery */
1012 * Input: ECX: Current Process
1014 * EDX: Flat address of arguments on stack
1016 * DWORD *retv [out] Nr. bytes returned
1017 * LPVOID base [in] Flat address of region
1018 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
1019 * DWORD len [in] Size of buffer
1021 * Output: EAX: NtStatus
1024 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1025 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1026 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1027 PMEMORY_BASIC_INFORMATION info =
1028 (PMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2]);
1029 DWORD len = stack[3];
1032 TRACE("VirtualQuery(%x, %x, %x, %x)\n",
1033 (DWORD)retv, (DWORD)base, (DWORD)info, len);
1035 result = VirtualQuery(base, info, len);
1038 context->Eax = STATUS_SUCCESS;
1043 case 0x000A: /* SetVirtMemProcess */
1045 * Input: ECX: Process Handle
1047 * EDX: Flat address of region
1049 * Output: EAX: NtStatus
1052 TRACE("[000a] ECX=%x EDX=%x\n",
1053 context->Ecx, context->Edx);
1057 context->Eax = STATUS_SUCCESS;
1061 case 0x000B: /* ??? some kind of cleanup */
1063 * Input: ECX: Process Handle
1065 * Output: EAX: NtStatus
1068 TRACE("[000b] ECX=%x\n", context->Ecx);
1072 context->Eax = STATUS_SUCCESS;
1076 case 0x000C: /* Set Debug Flags */
1078 * Input: EDX: Debug Flags
1080 * Output: EDX: Previous Debug Flags
1083 FIXME("[000c] EDX=%x\n", context->Edx);
1091 case 0x000D: /* NtCreateSection */
1093 * Input: EDX: Flat address of arguments on stack
1095 * HANDLE32 *retv [out] Handle of Section created
1096 * DWORD flags1 [in] (?? unknown ??)
1097 * DWORD atom [in] Name of Section to create
1098 * LARGE_INTEGER *size [in] Size of Section
1099 * DWORD protect [in] Access protection
1100 * DWORD flags2 [in] (?? unknown ??)
1101 * HFILE32 hFile [in] Handle of file to map
1102 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
1104 * Output: EAX: NtStatus
1107 DWORD *stack = (DWORD *) W32S_APP2WINE(context->Edx);
1108 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1109 DWORD flags1 = stack[1];
1110 DWORD atom = stack[2];
1111 LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3]);
1112 DWORD protect = stack[4];
1113 DWORD flags2 = stack[5];
1114 HANDLE hFile = DosFileHandleToWin32Handle(stack[6]);
1115 DWORD psp = stack[7];
1117 HANDLE result = INVALID_HANDLE_VALUE;
1120 TRACE("NtCreateSection(%x, %x, %x, %x, %x, %x, %x, %x)\n",
1121 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1124 if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1126 TRACE("NtCreateSection: name=%s\n", atom? name : NULL);
1128 result = CreateFileMappingA(hFile, NULL, protect,
1129 size? size->u.HighPart : 0,
1130 size? size->u.LowPart : 0,
1134 if (result == INVALID_HANDLE_VALUE)
1135 WARN("NtCreateSection: failed!\n");
1137 TRACE("NtCreateSection: returned %x\n", (DWORD)result);
1139 if (result != INVALID_HANDLE_VALUE)
1141 context->Eax = STATUS_SUCCESS;
1144 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1149 case 0x000E: /* NtOpenSection */
1151 * Input: EDX: Flat address of arguments on stack
1153 * HANDLE32 *retv [out] Handle of Section opened
1154 * DWORD protect [in] Access protection
1155 * DWORD atom [in] Name of Section to create
1157 * Output: EAX: NtStatus
1160 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1161 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1162 DWORD protect = stack[1];
1163 DWORD atom = stack[2];
1165 HANDLE result = INVALID_HANDLE_VALUE;
1168 TRACE("NtOpenSection(%x, %x, %x)\n",
1169 (DWORD)retv, protect, atom);
1171 if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1173 TRACE("NtOpenSection: name=%s\n", name);
1175 result = OpenFileMappingA(protect, FALSE, name);
1178 if (result == INVALID_HANDLE_VALUE)
1179 WARN("NtOpenSection: failed!\n");
1181 TRACE("NtOpenSection: returned %x\n", (DWORD)result);
1183 if (result != INVALID_HANDLE_VALUE)
1185 context->Eax = STATUS_SUCCESS;
1188 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1193 case 0x000F: /* NtCloseSection */
1195 * Input: EDX: Flat address of arguments on stack
1197 * HANDLE32 handle [in] Handle of Section to close
1198 * DWORD *id [out] Unique ID (?? unclear ??)
1200 * Output: EAX: NtStatus
1203 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1204 HANDLE handle = (HANDLE)stack[0];
1205 DWORD *id = (DWORD *)W32S_APP2WINE(stack[1]);
1207 TRACE("NtCloseSection(%x, %x)\n", (DWORD)handle, (DWORD)id);
1209 CloseHandle(handle);
1210 if (id) *id = 0; /* FIXME */
1212 context->Eax = STATUS_SUCCESS;
1217 case 0x0010: /* NtDupSection */
1219 * Input: EDX: Flat address of arguments on stack
1221 * HANDLE32 handle [in] Handle of Section to duplicate
1223 * Output: EAX: NtStatus
1226 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1227 HANDLE handle = (HANDLE)stack[0];
1230 TRACE("NtDupSection(%x)\n", (DWORD)handle);
1232 DuplicateHandle( GetCurrentProcess(), handle,
1233 GetCurrentProcess(), &new_handle,
1234 0, FALSE, DUPLICATE_SAME_ACCESS );
1235 context->Eax = STATUS_SUCCESS;
1240 case 0x0011: /* NtMapViewOfSection */
1242 * Input: EDX: Flat address of arguments on stack
1244 * HANDLE32 SectionHandle [in] Section to be mapped
1245 * DWORD ProcessHandle [in] Process to be mapped into
1246 * DWORD * BaseAddress [in/out] Address to be mapped at
1247 * DWORD ZeroBits [in] (?? unclear ??)
1248 * DWORD CommitSize [in] (?? unclear ??)
1249 * LARGE_INTEGER *SectionOffset [in] Offset within section
1250 * DWORD * ViewSize [in] Size of view
1251 * DWORD InheritDisposition [in] (?? unclear ??)
1252 * DWORD AllocationType [in] (?? unclear ??)
1253 * DWORD Protect [in] Access protection
1255 * Output: EAX: NtStatus
1258 DWORD * stack = (DWORD *)W32S_APP2WINE(context->Edx);
1259 HANDLE SectionHandle = (HANDLE)stack[0];
1260 DWORD ProcessHandle = stack[1]; /* ignored */
1261 DWORD * BaseAddress = (DWORD *)W32S_APP2WINE(stack[2]);
1262 DWORD ZeroBits = stack[3];
1263 DWORD CommitSize = stack[4];
1264 LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5]);
1265 DWORD * ViewSize = (DWORD *)W32S_APP2WINE(stack[6]);
1266 DWORD InheritDisposition = stack[7];
1267 DWORD AllocationType = stack[8];
1268 DWORD Protect = stack[9];
1270 LPBYTE address = (LPBYTE)(BaseAddress?
1271 W32S_APP2WINE(*BaseAddress) : 0);
1272 DWORD access = 0, result;
1274 switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1276 case PAGE_READONLY: access = FILE_MAP_READ; break;
1277 case PAGE_READWRITE: access = FILE_MAP_WRITE; break;
1278 case PAGE_WRITECOPY: access = FILE_MAP_COPY; break;
1280 case PAGE_EXECUTE_READ: access = FILE_MAP_READ; break;
1281 case PAGE_EXECUTE_READWRITE: access = FILE_MAP_WRITE; break;
1282 case PAGE_EXECUTE_WRITECOPY: access = FILE_MAP_COPY; break;
1285 TRACE("NtMapViewOfSection"
1286 "(%x, %x, %x, %x, %x, %x, %x, %x, %x, %x)\n",
1287 (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1288 ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1289 InheritDisposition, AllocationType, Protect);
1290 TRACE("NtMapViewOfSection: "
1291 "base=%x, offset=%x, size=%x, access=%x\n",
1292 (DWORD)address, SectionOffset? SectionOffset->u.LowPart : 0,
1293 ViewSize? *ViewSize : 0, access);
1295 result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1296 SectionOffset? SectionOffset->u.HighPart : 0,
1297 SectionOffset? SectionOffset->u.LowPart : 0,
1298 ViewSize? *ViewSize : 0, address);
1300 TRACE("NtMapViewOfSection: result=%x\n", result);
1302 if (W32S_WINE2APP(result))
1304 if (BaseAddress) *BaseAddress = W32S_WINE2APP(result);
1305 context->Eax = STATUS_SUCCESS;
1308 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1313 case 0x0012: /* NtUnmapViewOfSection */
1315 * Input: EDX: Flat address of arguments on stack
1317 * DWORD ProcessHandle [in] Process (defining address space)
1318 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1320 * Output: EAX: NtStatus
1323 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1324 DWORD ProcessHandle = stack[0]; /* ignored */
1325 LPBYTE BaseAddress = (LPBYTE)W32S_APP2WINE(stack[1]);
1327 TRACE("NtUnmapViewOfSection(%x, %x)\n",
1328 ProcessHandle, (DWORD)BaseAddress);
1330 UnmapViewOfFile(BaseAddress);
1332 context->Eax = STATUS_SUCCESS;
1337 case 0x0013: /* NtFlushVirtualMemory */
1339 * Input: EDX: Flat address of arguments on stack
1341 * DWORD ProcessHandle [in] Process (defining address space)
1342 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1343 * DWORD *ViewSize [in?] Number of bytes to be flushed
1344 * DWORD *unknown [???] (?? unknown ??)
1346 * Output: EAX: NtStatus
1349 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1350 DWORD ProcessHandle = stack[0]; /* ignored */
1351 DWORD *BaseAddress = (DWORD *)W32S_APP2WINE(stack[1]);
1352 DWORD *ViewSize = (DWORD *)W32S_APP2WINE(stack[2]);
1353 DWORD *unknown = (DWORD *)W32S_APP2WINE(stack[3]);
1355 LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress) : 0);
1356 DWORD size = ViewSize? *ViewSize : 0;
1358 TRACE("NtFlushVirtualMemory(%x, %x, %x, %x)\n",
1359 ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1361 TRACE("NtFlushVirtualMemory: base=%x, size=%x\n",
1362 (DWORD)address, size);
1364 FlushViewOfFile(address, size);
1366 context->Eax = STATUS_SUCCESS;
1371 case 0x0014: /* Get/Set Debug Registers */
1373 * Input: ECX: 0 if Get, 1 if Set
1375 * EDX: Get: Flat address of buffer to receive values of
1376 * debug registers DR0 .. DR7
1377 * Set: Flat address of buffer containing values of
1378 * debug registers DR0 .. DR7 to be set
1382 FIXME("[0014] ECX=%x EDX=%x\n",
1383 context->Ecx, context->Edx);
1389 case 0x0015: /* Set Coprocessor Emulation Flag */
1391 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1396 TRACE("[0015] EDX=%x\n", context->Edx);
1398 /* We don't care, as we always have a coprocessor anyway */
1402 case 0x0016: /* Init Win32S VxD PSP */
1404 * If called to query required PSP size:
1407 * Output: EDX: Required size of Win32s VxD PSP
1409 * If called to initialize allocated PSP:
1411 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1412 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1416 if (context->Ebx == 0)
1417 context->Edx = 0x80;
1420 PDB16 *psp = MapSL( MAKESEGPTR( BX_reg(context), 0 ));
1422 psp->fileHandlesPtr = MAKELONG(HIWORD(context->Ebx), 0x5c);
1423 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1428 case 0x0017: /* Set Break Point */
1430 * Input: EBX: Offset of Break Point
1431 * CX: Selector of Break Point
1436 FIXME("[0017] EBX=%x CX=%x\n",
1437 context->Ebx, CX_reg(context));
1443 case 0x0018: /* VirtualLock */
1445 * Input: ECX: Current Process
1447 * EDX: Flat address of arguments on stack
1449 * DWORD *retv [out] TRUE if success, FALSE if failure
1450 * LPVOID base [in] Flat address of range to lock
1451 * DWORD size [in] Size of range
1453 * Output: EAX: NtStatus
1456 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1457 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1458 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1459 DWORD size = stack[2];
1462 TRACE("VirtualLock(%x, %x, %x)\n",
1463 (DWORD)retv, (DWORD)base, size);
1465 result = VirtualLock(base, size);
1469 context->Eax = STATUS_SUCCESS;
1472 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1477 case 0x0019: /* VirtualUnlock */
1479 * Input: ECX: Current Process
1481 * EDX: Flat address of arguments on stack
1483 * DWORD *retv [out] TRUE if success, FALSE if failure
1484 * LPVOID base [in] Flat address of range to unlock
1485 * DWORD size [in] Size of range
1487 * Output: EAX: NtStatus
1490 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1491 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1492 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1493 DWORD size = stack[2];
1496 TRACE("VirtualUnlock(%x, %x, %x)\n",
1497 (DWORD)retv, (DWORD)base, size);
1499 result = VirtualUnlock(base, size);
1503 context->Eax = STATUS_SUCCESS;
1506 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1511 case 0x001A: /* KGetSystemInfo */
1515 * Output: ECX: Start of sparse memory arena
1516 * EDX: End of sparse memory arena
1519 TRACE("KGetSystemInfo()\n");
1522 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1523 * sparse memory arena. We do it the other way around, since
1524 * we have to reserve 3GB - 4GB for Linux, and thus use
1525 * 0GB - 3GB as sparse memory arena.
1527 * FIXME: What about other OSes ?
1530 context->Ecx = W32S_WINE2APP(0x00000000);
1531 context->Edx = W32S_WINE2APP(0xbfffffff);
1535 case 0x001B: /* KGlobalMemStat */
1537 * Input: ESI: Flat address of buffer to receive memory info
1542 struct Win32sMemoryInfo
1544 DWORD DIPhys_Count; /* Total physical pages */
1545 DWORD DIFree_Count; /* Free physical pages */
1546 DWORD DILin_Total_Count; /* Total virtual pages (private arena) */
1547 DWORD DILin_Total_Free; /* Free virtual pages (private arena) */
1549 DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */
1550 DWORD SparseFree; /* Free size of sparse arena (bytes ?) */
1553 struct Win32sMemoryInfo *info =
1554 (struct Win32sMemoryInfo *)W32S_APP2WINE(context->Esi);
1556 FIXME("KGlobalMemStat(%x)\n", (DWORD)info);
1563 case 0x001C: /* Enable/Disable Exceptions */
1565 * Input: ECX: 0 to disable, 1 to enable exception handling
1570 TRACE("[001c] ECX=%x\n", context->Ecx);
1576 case 0x001D: /* VirtualAlloc called from 16-bit code */
1578 * Input: EDX: Segmented address of arguments on stack
1580 * LPVOID base [in] Flat address of region to reserve/commit
1581 * DWORD size [in] Size of region
1582 * DWORD type [in] Type of allocation
1583 * DWORD prot [in] Type of access protection
1585 * Output: EAX: NtStatus
1586 * EDX: Flat base address of allocated region
1589 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1590 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1591 DWORD size = stack[1];
1592 DWORD type = stack[2];
1593 DWORD prot = stack[3];
1596 TRACE("VirtualAlloc16(%x, %x, %x, %x)\n",
1597 (DWORD)base, size, type, prot);
1599 if (type & 0x80000000)
1601 WARN("VirtualAlloc16: strange type %x\n", type);
1605 result = (DWORD)VirtualAlloc(base, size, type, prot);
1607 if (W32S_WINE2APP(result))
1608 context->Edx = W32S_WINE2APP(result),
1609 context->Eax = STATUS_SUCCESS;
1612 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1613 TRACE("VirtualAlloc16: returning base %x\n", context->Edx);
1618 case 0x001E: /* VirtualFree called from 16-bit code */
1620 * Input: EDX: Segmented address of arguments on stack
1622 * LPVOID base [in] Flat address of region
1623 * DWORD size [in] Size of region
1624 * DWORD type [in] Type of operation
1626 * Output: EAX: NtStatus
1627 * EDX: TRUE if success, FALSE if failure
1630 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1631 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1632 DWORD size = stack[1];
1633 DWORD type = stack[2];
1636 TRACE("VirtualFree16(%x, %x, %x)\n",
1637 (DWORD)base, size, type);
1639 result = VirtualFree(base, size, type);
1642 context->Edx = TRUE,
1643 context->Eax = STATUS_SUCCESS;
1645 context->Edx = FALSE,
1646 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1651 case 0x001F: /* FWorkingSetSize */
1653 * Input: EDX: 0 if Get, 1 if Set
1655 * ECX: Get: Buffer to receive Working Set Size
1656 * Set: Buffer containing Working Set Size
1661 DWORD *ptr = (DWORD *)W32S_APP2WINE(context->Ecx);
1662 BOOL set = context->Edx;
1664 TRACE("FWorkingSetSize(%x, %x)\n", (DWORD)ptr, (DWORD)set);
1667 /* We do it differently ... */;
1671 context->Eax = STATUS_SUCCESS;
1677 VXD_BARF( context, "W32S" );