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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <sys/types.h>
34 #include "wine/winbase16.h"
35 #include "wine/winuser16.h"
39 #include "selectors.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(vxd);
47 #define VXD_BARF(context,name) \
48 DPRINTF( "vxd %s: unknown/not implemented parameters:\n" \
49 "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
50 "SI %04x, DI %04x, DS %04x, ES %04x\n", \
51 (name), (name), AX_reg(context), BX_reg(context), \
52 CX_reg(context), DX_reg(context), SI_reg(context), \
53 DI_reg(context), (WORD)context->SegDs, (WORD)context->SegEs )
57 static WORD VXD_WinVersion(void)
59 WORD version = LOWORD(GetVersion16());
60 return (version >> 8) | (version << 8);
63 /***********************************************************************
64 * VXD_VMM (WPROCS.401)
66 void WINAPI VXD_VMM ( CONTEXT86 *context )
68 unsigned service = AX_reg(context);
70 TRACE("[%04x] VMM\n", (UINT16)service);
74 case 0x0000: /* version */
75 SET_AX( context, VXD_WinVersion() );
79 case 0x026d: /* Get_Debug_Flag '/m' */
80 case 0x026e: /* Get_Debug_Flag '/n' */
86 VXD_BARF( context, "VMM" );
90 /***********************************************************************
91 * VXD_PageFile (WPROCS.433)
93 void WINAPI VXD_PageFile( CONTEXT86 *context )
95 unsigned service = AX_reg(context);
97 /* taken from Ralf Brown's Interrupt List */
99 TRACE("[%04x] PageFile\n", (UINT16)service );
103 case 0x00: /* get version, is this windows version? */
104 TRACE("returning version\n");
105 SET_AX( context, VXD_WinVersion() );
106 RESET_CFLAG(context);
109 case 0x01: /* get swap file info */
110 TRACE("VxD PageFile: returning swap file info\n");
111 SET_AX( context, 0x00 ); /* paging disabled */
112 context->Ecx = 0; /* maximum size of paging file */
113 /* FIXME: do I touch DS:SI or DS:DI? */
114 RESET_CFLAG(context);
117 case 0x02: /* delete permanent swap on exit */
118 TRACE("VxD PageFile: supposed to delete swap\n");
119 RESET_CFLAG(context);
122 case 0x03: /* current temporary swap file size */
123 TRACE("VxD PageFile: what is current temp. swap size\n");
124 RESET_CFLAG(context);
127 case 0x04: /* read or write?? INTERRUP.D */
128 case 0x05: /* cancel?? INTERRUP.D */
129 case 0x06: /* test I/O valid INTERRUP.D */
131 VXD_BARF( context, "pagefile" );
136 /***********************************************************************
137 * VXD_Reboot (WPROCS.409)
139 void WINAPI VXD_Reboot ( CONTEXT86 *context )
141 unsigned service = AX_reg(context);
143 TRACE("[%04x] Reboot\n", (UINT16)service);
147 case 0x0000: /* version */
148 SET_AX( context, VXD_WinVersion() );
149 RESET_CFLAG(context);
153 VXD_BARF( context, "REBOOT" );
157 /***********************************************************************
158 * VXD_VDD (WPROCS.410)
160 void WINAPI VXD_VDD ( CONTEXT86 *context )
162 unsigned service = AX_reg(context);
164 TRACE("[%04x] VDD\n", (UINT16)service);
168 case 0x0000: /* version */
169 SET_AX( context, VXD_WinVersion() );
170 RESET_CFLAG(context);
174 VXD_BARF( context, "VDD" );
178 /***********************************************************************
179 * VXD_VMD (WPROCS.412)
181 void WINAPI VXD_VMD ( CONTEXT86 *context )
183 unsigned service = AX_reg(context);
185 TRACE("[%04x] VMD\n", (UINT16)service);
189 case 0x0000: /* version */
190 SET_AX( context, VXD_WinVersion() );
191 RESET_CFLAG(context);
195 VXD_BARF( context, "VMD" );
199 /***********************************************************************
200 * VXD_VXDLoader (WPROCS.439)
202 void WINAPI VXD_VXDLoader( CONTEXT86 *context )
204 unsigned service = AX_reg(context);
206 TRACE("[%04x] VXDLoader\n", (UINT16)service);
210 case 0x0000: /* get version */
211 TRACE("returning version\n");
212 SET_AX( context, 0x0000 );
213 SET_DX( context, VXD_WinVersion() );
214 RESET_CFLAG(context);
217 case 0x0001: /* load device */
218 FIXME("load device %04lx:%04x (%s)\n",
219 context->SegDs, DX_reg(context),
220 debugstr_a(MapSL(MAKESEGPTR(context->SegDs, DX_reg(context)))));
221 SET_AX( context, 0x0000 );
222 context->SegEs = 0x0000;
223 SET_DI( context, 0x0000 );
224 RESET_CFLAG(context);
227 case 0x0002: /* unload device */
228 FIXME("unload device (%08lx)\n", context->Ebx);
229 SET_AX( context, 0x0000 );
230 RESET_CFLAG(context);
234 VXD_BARF( context, "VXDLDR" );
235 SET_AX( context, 0x000B ); /* invalid function number */
241 /***********************************************************************
242 * VXD_Shell (WPROCS.423)
244 void WINAPI VXD_Shell( CONTEXT86 *context )
246 unsigned service = DX_reg(context);
248 TRACE("[%04x] Shell\n", (UINT16)service);
250 switch (service) /* Ralf Brown says EDX, but I use DX instead */
253 TRACE("returning version\n");
254 SET_AX( context, VXD_WinVersion() );
255 context->Ebx = 1; /* system VM Handle */
261 /* SHELL_SYSMODAL_Message
262 ebx virtual maschine handle
263 eax message box flags
264 ecx address of message
265 edi address of caption
266 return response in eax
270 ebx virtual maschine handle
271 eax message box flags
272 ecx address of message
273 edi address of caption
275 edx reference data for callback
276 return response in eax
279 VXD_BARF( context, "shell" );
282 case 0x0006: /* SHELL_Get_VM_State */
283 TRACE("VxD Shell: returning VM state\n");
284 /* Actually we don't, not yet. We have to return a structure
285 * and I am not to sure how to set it up and return it yet,
286 * so for now let's do nothing. I can (hopefully) get this
287 * by the next release
289 /* RESET_CFLAG(context); */
308 VXD_BARF( context, "SHELL" );
311 /* the new Win95 shell API */
312 case 0x0100: /* get version */
313 SET_AX( context, VXD_WinVersion() );
316 case 0x0104: /* retrieve Hook_Properties list */
317 case 0x0105: /* call Hook_Properties callbacks */
318 VXD_BARF( context, "SHELL" );
321 case 0x0106: /* install timeout callback */
322 TRACE("VxD Shell: ignoring shell callback (%ld sec.)\n", context->Ebx);
326 case 0x0107: /* get version of any VxD */
328 VXD_BARF( context, "SHELL" );
334 /***********************************************************************
335 * VXD_Comm (WPROCS.414)
337 void WINAPI VXD_Comm( CONTEXT86 *context )
339 unsigned service = AX_reg(context);
341 TRACE("[%04x] Comm\n", (UINT16)service);
345 case 0x0000: /* get version */
346 TRACE("returning version\n");
347 SET_AX( context, VXD_WinVersion() );
348 RESET_CFLAG(context);
351 case 0x0001: /* set port global */
352 case 0x0002: /* get focus */
353 case 0x0003: /* virtualise port */
355 VXD_BARF( context, "comm" );
359 /***********************************************************************
360 * VXD_Timer (WPROCS.405)
362 void WINAPI VXD_Timer( CONTEXT86 *context )
364 unsigned service = AX_reg(context);
366 TRACE("[%04x] Virtual Timer\n", (UINT16)service);
370 case 0x0000: /* version */
371 SET_AX( context, VXD_WinVersion() );
372 RESET_CFLAG(context);
375 case 0x0100: /* clock tick time, in 840nsecs */
376 context->Eax = GetTickCount();
378 context->Edx = context->Eax >> 22;
379 context->Eax <<= 10; /* not very precise */
382 case 0x0101: /* current Windows time, msecs */
383 case 0x0102: /* current VM time, msecs */
384 context->Eax = GetTickCount();
388 VXD_BARF( context, "VTD" );
392 /***********************************************************************
393 * VXD_TimerAPI (WPROCS.1490)
395 static DWORD System_Time = 0;
396 static WORD System_Time_Selector = 0;
397 static void System_Time_Tick( WORD timer ) { System_Time += 55; }
398 void WINAPI VXD_TimerAPI ( CONTEXT86 *context )
400 unsigned service = AX_reg(context);
402 TRACE("[%04x] TimerAPI\n", (UINT16)service);
406 case 0x0000: /* version */
407 SET_AX( context, VXD_WinVersion() );
408 RESET_CFLAG(context);
411 case 0x0009: /* get system time selector */
412 if ( !System_Time_Selector )
414 System_Time_Selector = SELECTOR_AllocBlock( &System_Time, sizeof(DWORD), WINE_LDT_FLAGS_DATA );
415 CreateSystemTimer( 55, System_Time_Tick );
418 SET_AX( context, System_Time_Selector );
419 RESET_CFLAG(context);
423 VXD_BARF( context, "VTDAPI" );
427 /***********************************************************************
428 * VXD_ConfigMG (WPROCS.451)
430 void WINAPI VXD_ConfigMG ( CONTEXT86 *context )
432 unsigned service = AX_reg(context);
434 TRACE("[%04x] ConfigMG\n", (UINT16)service);
438 case 0x0000: /* version */
439 SET_AX( context, VXD_WinVersion() );
440 RESET_CFLAG(context);
444 VXD_BARF( context, "CONFIGMG" );
448 /***********************************************************************
449 * VXD_Enable (WPROCS.455)
451 void WINAPI VXD_Enable ( CONTEXT86 *context )
453 unsigned service = AX_reg(context);
455 TRACE("[%04x] Enable\n", (UINT16)service);
459 case 0x0000: /* version */
460 SET_AX( context, VXD_WinVersion() );
461 RESET_CFLAG(context);
465 VXD_BARF( context, "ENABLE" );
469 /***********************************************************************
470 * VXD_APM (WPROCS.438)
472 void WINAPI VXD_APM ( CONTEXT86 *context )
474 unsigned service = AX_reg(context);
476 TRACE("[%04x] APM\n", (UINT16)service);
480 case 0x0000: /* version */
481 SET_AX( context, VXD_WinVersion() );
482 RESET_CFLAG(context);
486 VXD_BARF( context, "APM" );
490 /***********************************************************************
491 * VXD_Win32s (WPROCS.445)
493 * This is an implementation of the services of the Win32s VxD.
494 * Since official documentation of these does not seem to be available,
495 * certain arguments of some of the services remain unclear.
497 * FIXME: The following services are currently unimplemented:
498 * Exception handling (0x01, 0x1C)
499 * Debugger support (0x0C, 0x14, 0x17)
500 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
501 * Memory Statistics (0x1B)
504 * We have a specific problem running Win32s on Linux (and probably also
505 * the other x86 unixes), since Win32s tries to allocate its main 'flat
506 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
507 * The rationale for this seems to be that they want one the one hand to
508 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
509 * at linear address 0, but want at other hand to have offset 0 of the
510 * flat data/code segment point to an unmapped page (to catch NULL pointer
511 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
512 * so that the Win 3.1 memory area at linear address zero shows up in the
513 * flat segments at offset 0x10000 (since linear addresses wrap around at
514 * 4GB). To compensate for that discrepancy between flat segment offsets
515 * and plain linear addresses, all flat pointers passed between the 32-bit
516 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
517 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
519 * The problem for us is now that Linux does not allow a LDT selector with
520 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
521 * address space. To address this problem we introduce *another* offset:
522 * We add 0x10000 to every linear address we get as an argument from Win32s.
523 * This means especially that the flat code/data selectors get actually
524 * allocated with base 0x0, so that flat offsets and (real) linear addresses
525 * do again agree! In fact, every call e.g. of a Win32s VxD service now
526 * has all pointer arguments (which are offsets in the flat data segement)
527 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
528 * increased by 0x10000 by *our* code.
530 * Note that to keep everything consistent, this offset has to be applied by
531 * every Wine function that operates on 'linear addresses' passed to it by
532 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
533 * API routines, this affects only two locations: this VxD and the DPMI
534 * handler. (NOTE: Should any Win32s application pass a linear address to
535 * any routine apart from those, e.g. some other VxD handler, that code
536 * would have to take the offset into account as well!)
538 * The offset is set the first time any application calls the GetVersion()
539 * service of the Win32s VxD. (Note that the offset is never reset.)
543 void WINAPI VXD_Win32s( CONTEXT86 *context )
545 switch (AX_reg(context))
547 case 0x0000: /* Get Version */
551 * Output: EAX: LoWord: Win32s Version (1.30)
552 * HiWord: VxD Version (200)
558 * EDX: Debugging Flags
562 * 1 if VMCPD VxD not found
565 TRACE("GetVersion()\n");
567 context->Eax = VXD_WinVersion() | (200 << 16);
574 * If this is the first time we are called for this process,
575 * hack the memory image of WIN32S16 so that it doesn't try
576 * to access the GDT directly ...
578 * The first code segment of WIN32S16 (version 1.30) contains
579 * an unexported function somewhere between the exported functions
580 * SetFS and StackLinearToSegmented that tries to find a selector
581 * in the LDT that maps to the memory image of the LDT itself.
582 * If it succeeds, it stores this selector into a global variable
583 * which will be used to speed up execution by using this selector
584 * to modify the LDT directly instead of using the DPMI calls.
586 * To perform this search of the LDT, this function uses the
587 * sgdt and sldt instructions to find the linear address of
588 * the (GDT and then) LDT. While those instructions themselves
589 * execute without problem, the linear address that sgdt returns
590 * points (at least under Linux) to the kernel address space, so
591 * that any subsequent access leads to a segfault.
593 * Fortunately, WIN32S16 still contains as a fallback option the
594 * mechanism of using DPMI calls to modify LDT selectors instead
595 * of direct writes to the LDT. Thus we can circumvent the problem
596 * by simply replacing the first byte of the offending function
597 * with an 'retf' instruction. This means that the global variable
598 * supposed to contain the LDT alias selector will remain zero,
599 * and hence WIN32S16 will fall back to using DPMI calls.
601 * The heuristic we employ to _find_ that function is as follows:
602 * We search between the addresses of the exported symbols SetFS
603 * and StackLinearToSegmented for the byte sequence '0F 01 04'
604 * (this is the opcode of 'sgdt [si]'). We then search backwards
605 * from this address for the last occurrence of 'CB' (retf) that marks
606 * the end of the preceeding function. The following byte (which
607 * should now be the first byte of the function we are looking for)
608 * will be replaced by 'CB' (retf).
610 * This heuristic works for the retail as well as the debug version
611 * of Win32s version 1.30. For versions earlier than that this
612 * hack should not be necessary at all, since the whole mechanism
613 * ('PERF130') was introduced only in 1.30 to improve the overall
614 * performance of Win32s.
619 HMODULE16 hModule = GetModuleHandle16("win32s16");
620 SEGPTR func1 = (SEGPTR)GetProcAddress16(hModule, "SetFS");
621 SEGPTR func2 = (SEGPTR)GetProcAddress16(hModule, "StackLinearToSegmented");
623 if ( hModule && func1 && func2
624 && SELECTOROF(func1) == SELECTOROF(func2))
626 BYTE *start = MapSL(func1);
627 BYTE *end = MapSL(func2);
628 BYTE *p, *retv = NULL;
631 for (p = start; p < end; p++)
632 if (*p == 0xCB) found = 0, retv = p;
633 else if (*p == 0x0F) found = 1;
634 else if (*p == 0x01 && found == 1) found = 2;
635 else if (*p == 0x04 && found == 2) { found = 3; break; }
638 if (found == 3 && retv)
640 TRACE("PERF130 hack: "
641 "Replacing byte %02X at offset %04X:%04X\n",
642 *(retv+1), SELECTOROF(func1),
643 OFFSETOF(func1) + retv+1-start);
645 *(retv+1) = (BYTE)0xCB;
651 * Mark process as Win32s, so that subsequent DPMI calls
652 * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
654 W32S_offset = 0x10000;
658 case 0x0001: /* Install Exception Handling */
660 * Input: EBX: Flat address of W32SKRNL Exception Data
662 * ECX: LoWord: Flat Code Selector
663 * HiWord: Flat Data Selector
665 * EDX: Flat address of W32SKRNL Exception Handler
666 * (this is equal to W32S_BackTo32 + 0x40)
668 * ESI: SEGPTR KERNEL.HASGPHANDLER
670 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
672 * Output: EAX: 0 if OK
675 TRACE("[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
676 context->Ebx, context->Ecx, context->Edx,
677 context->Esi, context->Edi);
685 case 0x0002: /* Set Page Access Flags */
687 * Input: EBX: New access flags
688 * Bit 2: User Page if set, Supervisor Page if clear
689 * Bit 1: Read-Write if set, Read-Only if clear
691 * ECX: Size of memory area to change
693 * EDX: Flat start address of memory area
695 * Output: EAX: Size of area changed
698 TRACE("[0002] EBX=%lx ECX=%lx EDX=%lx\n",
699 context->Ebx, context->Ecx, context->Edx);
703 context->Eax = context->Ecx;
707 case 0x0003: /* Get Page Access Flags */
709 * Input: EDX: Flat address of page to query
711 * Output: EAX: Page access flags
712 * Bit 2: User Page if set, Supervisor Page if clear
713 * Bit 1: Read-Write if set, Read-Only if clear
716 TRACE("[0003] EDX=%lx\n", context->Edx);
724 case 0x0004: /* Map Module */
726 * Input: ECX: IMTE (offset in Module Table) of new module
728 * EDX: Flat address of Win32s Module Table
730 * Output: EAX: 0 if OK
733 if (!context->Edx || CX_reg(context) == 0xFFFF)
735 TRACE("MapModule: Initialization call\n");
741 * Structure of a Win32s Module Table Entry:
756 * Note: This function should set up a demand-paged memory image
757 * of the given module. Since mmap does not allow file offsets
758 * not aligned at 1024 bytes, we simply load the image fully
762 struct Win32sModule *moduleTable =
763 (struct Win32sModule *)W32S_APP2WINE(context->Edx);
764 struct Win32sModule *module = moduleTable + context->Ecx;
766 IMAGE_NT_HEADERS *nt_header = RtlImageNtHeader( (HMODULE)module->baseAddr );
767 IMAGE_SECTION_HEADER *pe_seg = (IMAGE_SECTION_HEADER*)((char *)&nt_header->OptionalHeader +
768 nt_header->FileHeader.SizeOfOptionalHeader);
771 HFILE image = _lopen(module->pathName, OF_READ);
772 BOOL error = (image == HFILE_ERROR);
775 TRACE("MapModule: Loading %s\n", module->pathName);
778 !error && i < nt_header->FileHeader.NumberOfSections;
780 if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
782 DWORD off = pe_seg->PointerToRawData;
783 DWORD len = pe_seg->SizeOfRawData;
784 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
787 "Section %d at %08lx from %08lx len %08lx\n",
788 i, (DWORD)addr, off, len);
790 if ( _llseek(image, off, SEEK_SET) != off
791 || _lread(image, addr, len) != len)
798 ERR("MapModule: Unable to load %s\n", module->pathName);
800 else if (module->relocDelta != 0)
802 IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
803 + IMAGE_DIRECTORY_ENTRY_BASERELOC;
804 IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
805 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
807 TRACE("MapModule: Reloc delta %08lx\n", module->relocDelta);
809 while (r && r->VirtualAddress)
811 LPBYTE page = module->baseAddr + r->VirtualAddress;
812 WORD *TypeOffset = (WORD *)(r + 1);
813 int count = (r->SizeOfBlock - sizeof(*r)) / sizeof(*TypeOffset);
815 TRACE("MapModule: %d relocations for page %08lx\n",
818 for(i = 0; i < count; i++)
820 int offset = TypeOffset[i] & 0xFFF;
821 int type = TypeOffset[i] >> 12;
824 case IMAGE_REL_BASED_ABSOLUTE:
826 case IMAGE_REL_BASED_HIGH:
827 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
829 case IMAGE_REL_BASED_LOW:
830 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
832 case IMAGE_REL_BASED_HIGHLOW:
833 *(DWORD*)(page+offset) += module->relocDelta;
836 WARN("MapModule: Unsupported fixup type\n");
841 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
846 RESET_CFLAG(context);
851 case 0x0005: /* UnMap Module */
853 * Input: EDX: Flat address of module image
855 * Output: EAX: 1 if OK
858 TRACE("UnMapModule: %lx\n", (DWORD)W32S_APP2WINE(context->Edx));
860 /* As we didn't map anything, there's nothing to unmap ... */
866 case 0x0006: /* VirtualAlloc */
868 * Input: ECX: Current Process
870 * EDX: Flat address of arguments on stack
872 * DWORD *retv [out] Flat base address of allocated region
873 * LPVOID base [in] Flat address of region to reserve/commit
874 * DWORD size [in] Size of region
875 * DWORD type [in] Type of allocation
876 * DWORD prot [in] Type of access protection
878 * Output: EAX: NtStatus
881 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
882 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
883 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
884 DWORD size = stack[2];
885 DWORD type = stack[3];
886 DWORD prot = stack[4];
889 TRACE("VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
890 (DWORD)retv, (DWORD)base, size, type, prot);
892 if (type & 0x80000000)
894 WARN("VirtualAlloc: strange type %lx\n", type);
898 if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
900 WARN("VirtualAlloc: NLS hack, allowing write access!\n");
901 prot = PAGE_READWRITE;
904 result = (DWORD)VirtualAlloc(base, size, type, prot);
906 if (W32S_WINE2APP(result))
907 *retv = W32S_WINE2APP(result),
908 context->Eax = STATUS_SUCCESS;
911 context->Eax = STATUS_NO_MEMORY; /* FIXME */
916 case 0x0007: /* VirtualFree */
918 * Input: ECX: Current Process
920 * EDX: Flat address of arguments on stack
922 * DWORD *retv [out] TRUE if success, FALSE if failure
923 * LPVOID base [in] Flat address of region
924 * DWORD size [in] Size of region
925 * DWORD type [in] Type of operation
927 * Output: EAX: NtStatus
930 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
931 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
932 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
933 DWORD size = stack[2];
934 DWORD type = stack[3];
937 TRACE("VirtualFree(%lx, %lx, %lx, %lx)\n",
938 (DWORD)retv, (DWORD)base, size, type);
940 result = VirtualFree(base, size, type);
944 context->Eax = STATUS_SUCCESS;
947 context->Eax = STATUS_NO_MEMORY; /* FIXME */
952 case 0x0008: /* VirtualProtect */
954 * Input: ECX: Current Process
956 * EDX: Flat address of arguments on stack
958 * DWORD *retv [out] TRUE if success, FALSE if failure
959 * LPVOID base [in] Flat address of region
960 * DWORD size [in] Size of region
961 * DWORD new_prot [in] Desired access protection
962 * DWORD *old_prot [out] Previous access protection
964 * Output: EAX: NtStatus
967 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
968 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
969 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
970 DWORD size = stack[2];
971 DWORD new_prot = stack[3];
972 DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4]);
975 TRACE("VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
976 (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
978 result = VirtualProtect(base, size, new_prot, old_prot);
982 context->Eax = STATUS_SUCCESS;
985 context->Eax = STATUS_NO_MEMORY; /* FIXME */
990 case 0x0009: /* VirtualQuery */
992 * Input: ECX: Current Process
994 * EDX: Flat address of arguments on stack
996 * DWORD *retv [out] Nr. bytes returned
997 * LPVOID base [in] Flat address of region
998 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
999 * DWORD len [in] Size of buffer
1001 * Output: EAX: NtStatus
1004 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1005 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1006 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1007 LPMEMORY_BASIC_INFORMATION info =
1008 (LPMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2]);
1009 DWORD len = stack[3];
1012 TRACE("VirtualQuery(%lx, %lx, %lx, %lx)\n",
1013 (DWORD)retv, (DWORD)base, (DWORD)info, len);
1015 result = VirtualQuery(base, info, len);
1018 context->Eax = STATUS_SUCCESS;
1023 case 0x000A: /* SetVirtMemProcess */
1025 * Input: ECX: Process Handle
1027 * EDX: Flat address of region
1029 * Output: EAX: NtStatus
1032 TRACE("[000a] ECX=%lx EDX=%lx\n",
1033 context->Ecx, context->Edx);
1037 context->Eax = STATUS_SUCCESS;
1041 case 0x000B: /* ??? some kind of cleanup */
1043 * Input: ECX: Process Handle
1045 * Output: EAX: NtStatus
1048 TRACE("[000b] ECX=%lx\n", context->Ecx);
1052 context->Eax = STATUS_SUCCESS;
1056 case 0x000C: /* Set Debug Flags */
1058 * Input: EDX: Debug Flags
1060 * Output: EDX: Previous Debug Flags
1063 FIXME("[000c] EDX=%lx\n", context->Edx);
1071 case 0x000D: /* NtCreateSection */
1073 * Input: EDX: Flat address of arguments on stack
1075 * HANDLE32 *retv [out] Handle of Section created
1076 * DWORD flags1 [in] (?? unknown ??)
1077 * DWORD atom [in] Name of Section to create
1078 * LARGE_INTEGER *size [in] Size of Section
1079 * DWORD protect [in] Access protection
1080 * DWORD flags2 [in] (?? unknown ??)
1081 * HFILE32 hFile [in] Handle of file to map
1082 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
1084 * Output: EAX: NtStatus
1087 DWORD *stack = (DWORD *) W32S_APP2WINE(context->Edx);
1088 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1089 DWORD flags1 = stack[1];
1090 DWORD atom = stack[2];
1091 LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3]);
1092 DWORD protect = stack[4];
1093 DWORD flags2 = stack[5];
1094 HANDLE hFile = DosFileHandleToWin32Handle(stack[6]);
1095 DWORD psp = stack[7];
1097 HANDLE result = INVALID_HANDLE_VALUE;
1100 TRACE("NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1101 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1104 if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1106 TRACE("NtCreateSection: name=%s\n", atom? name : NULL);
1108 result = CreateFileMappingA(hFile, NULL, protect,
1109 size? size->s.HighPart : 0,
1110 size? size->s.LowPart : 0,
1114 if (result == INVALID_HANDLE_VALUE)
1115 WARN("NtCreateSection: failed!\n");
1117 TRACE("NtCreateSection: returned %lx\n", (DWORD)result);
1119 if (result != INVALID_HANDLE_VALUE)
1121 context->Eax = STATUS_SUCCESS;
1124 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1129 case 0x000E: /* NtOpenSection */
1131 * Input: EDX: Flat address of arguments on stack
1133 * HANDLE32 *retv [out] Handle of Section opened
1134 * DWORD protect [in] Access protection
1135 * DWORD atom [in] Name of Section to create
1137 * Output: EAX: NtStatus
1140 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1141 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1142 DWORD protect = stack[1];
1143 DWORD atom = stack[2];
1145 HANDLE result = INVALID_HANDLE_VALUE;
1148 TRACE("NtOpenSection(%lx, %lx, %lx)\n",
1149 (DWORD)retv, protect, atom);
1151 if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1153 TRACE("NtOpenSection: name=%s\n", name);
1155 result = OpenFileMappingA(protect, FALSE, name);
1158 if (result == INVALID_HANDLE_VALUE)
1159 WARN("NtOpenSection: failed!\n");
1161 TRACE("NtOpenSection: returned %lx\n", (DWORD)result);
1163 if (result != INVALID_HANDLE_VALUE)
1165 context->Eax = STATUS_SUCCESS;
1168 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1173 case 0x000F: /* NtCloseSection */
1175 * Input: EDX: Flat address of arguments on stack
1177 * HANDLE32 handle [in] Handle of Section to close
1178 * DWORD *id [out] Unique ID (?? unclear ??)
1180 * Output: EAX: NtStatus
1183 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1184 HANDLE handle = (HANDLE)stack[0];
1185 DWORD *id = (DWORD *)W32S_APP2WINE(stack[1]);
1187 TRACE("NtCloseSection(%lx, %lx)\n", (DWORD)handle, (DWORD)id);
1189 CloseHandle(handle);
1190 if (id) *id = 0; /* FIXME */
1192 context->Eax = STATUS_SUCCESS;
1197 case 0x0010: /* NtDupSection */
1199 * Input: EDX: Flat address of arguments on stack
1201 * HANDLE32 handle [in] Handle of Section to duplicate
1203 * Output: EAX: NtStatus
1206 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1207 HANDLE handle = (HANDLE)stack[0];
1210 TRACE("NtDupSection(%lx)\n", (DWORD)handle);
1212 DuplicateHandle( GetCurrentProcess(), handle,
1213 GetCurrentProcess(), &new_handle,
1214 0, FALSE, DUPLICATE_SAME_ACCESS );
1215 context->Eax = STATUS_SUCCESS;
1220 case 0x0011: /* NtMapViewOfSection */
1222 * Input: EDX: Flat address of arguments on stack
1224 * HANDLE32 SectionHandle [in] Section to be mapped
1225 * DWORD ProcessHandle [in] Process to be mapped into
1226 * DWORD * BaseAddress [in/out] Address to be mapped at
1227 * DWORD ZeroBits [in] (?? unclear ??)
1228 * DWORD CommitSize [in] (?? unclear ??)
1229 * LARGE_INTEGER *SectionOffset [in] Offset within section
1230 * DWORD * ViewSize [in] Size of view
1231 * DWORD InheritDisposition [in] (?? unclear ??)
1232 * DWORD AllocationType [in] (?? unclear ??)
1233 * DWORD Protect [in] Access protection
1235 * Output: EAX: NtStatus
1238 DWORD * stack = (DWORD *)W32S_APP2WINE(context->Edx);
1239 HANDLE SectionHandle = (HANDLE)stack[0];
1240 DWORD ProcessHandle = stack[1]; /* ignored */
1241 DWORD * BaseAddress = (DWORD *)W32S_APP2WINE(stack[2]);
1242 DWORD ZeroBits = stack[3];
1243 DWORD CommitSize = stack[4];
1244 LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5]);
1245 DWORD * ViewSize = (DWORD *)W32S_APP2WINE(stack[6]);
1246 DWORD InheritDisposition = stack[7];
1247 DWORD AllocationType = stack[8];
1248 DWORD Protect = stack[9];
1250 LPBYTE address = (LPBYTE)(BaseAddress?
1251 W32S_APP2WINE(*BaseAddress) : 0);
1252 DWORD access = 0, result;
1254 switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1256 case PAGE_READONLY: access = FILE_MAP_READ; break;
1257 case PAGE_READWRITE: access = FILE_MAP_WRITE; break;
1258 case PAGE_WRITECOPY: access = FILE_MAP_COPY; break;
1260 case PAGE_EXECUTE_READ: access = FILE_MAP_READ; break;
1261 case PAGE_EXECUTE_READWRITE: access = FILE_MAP_WRITE; break;
1262 case PAGE_EXECUTE_WRITECOPY: access = FILE_MAP_COPY; break;
1265 TRACE("NtMapViewOfSection"
1266 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1267 (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1268 ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1269 InheritDisposition, AllocationType, Protect);
1270 TRACE("NtMapViewOfSection: "
1271 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1272 (DWORD)address, SectionOffset? SectionOffset->s.LowPart : 0,
1273 ViewSize? *ViewSize : 0, access);
1275 result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1276 SectionOffset? SectionOffset->s.HighPart : 0,
1277 SectionOffset? SectionOffset->s.LowPart : 0,
1278 ViewSize? *ViewSize : 0, address);
1280 TRACE("NtMapViewOfSection: result=%lx\n", result);
1282 if (W32S_WINE2APP(result))
1284 if (BaseAddress) *BaseAddress = W32S_WINE2APP(result);
1285 context->Eax = STATUS_SUCCESS;
1288 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1293 case 0x0012: /* NtUnmapViewOfSection */
1295 * Input: EDX: Flat address of arguments on stack
1297 * DWORD ProcessHandle [in] Process (defining address space)
1298 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1300 * Output: EAX: NtStatus
1303 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1304 DWORD ProcessHandle = stack[0]; /* ignored */
1305 LPBYTE BaseAddress = (LPBYTE)W32S_APP2WINE(stack[1]);
1307 TRACE("NtUnmapViewOfSection(%lx, %lx)\n",
1308 ProcessHandle, (DWORD)BaseAddress);
1310 UnmapViewOfFile(BaseAddress);
1312 context->Eax = STATUS_SUCCESS;
1317 case 0x0013: /* NtFlushVirtualMemory */
1319 * Input: EDX: Flat address of arguments on stack
1321 * DWORD ProcessHandle [in] Process (defining address space)
1322 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1323 * DWORD *ViewSize [in?] Number of bytes to be flushed
1324 * DWORD *unknown [???] (?? unknown ??)
1326 * Output: EAX: NtStatus
1329 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1330 DWORD ProcessHandle = stack[0]; /* ignored */
1331 DWORD *BaseAddress = (DWORD *)W32S_APP2WINE(stack[1]);
1332 DWORD *ViewSize = (DWORD *)W32S_APP2WINE(stack[2]);
1333 DWORD *unknown = (DWORD *)W32S_APP2WINE(stack[3]);
1335 LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress) : 0);
1336 DWORD size = ViewSize? *ViewSize : 0;
1338 TRACE("NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1339 ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1341 TRACE("NtFlushVirtualMemory: base=%lx, size=%lx\n",
1342 (DWORD)address, size);
1344 FlushViewOfFile(address, size);
1346 context->Eax = STATUS_SUCCESS;
1351 case 0x0014: /* Get/Set Debug Registers */
1353 * Input: ECX: 0 if Get, 1 if Set
1355 * EDX: Get: Flat address of buffer to receive values of
1356 * debug registers DR0 .. DR7
1357 * Set: Flat address of buffer containing values of
1358 * debug registers DR0 .. DR7 to be set
1362 FIXME("[0014] ECX=%lx EDX=%lx\n",
1363 context->Ecx, context->Edx);
1369 case 0x0015: /* Set Coprocessor Emulation Flag */
1371 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1376 TRACE("[0015] EDX=%lx\n", context->Edx);
1378 /* We don't care, as we always have a coprocessor anyway */
1382 case 0x0016: /* Init Win32S VxD PSP */
1384 * If called to query required PSP size:
1387 * Output: EDX: Required size of Win32s VxD PSP
1389 * If called to initialize allocated PSP:
1391 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1392 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1396 if (context->Ebx == 0)
1397 context->Edx = 0x80;
1400 PDB16 *psp = MapSL( MAKESEGPTR( BX_reg(context), 0 ));
1402 psp->fileHandlesPtr = MAKELONG(HIWORD(context->Ebx), 0x5c);
1403 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1408 case 0x0017: /* Set Break Point */
1410 * Input: EBX: Offset of Break Point
1411 * CX: Selector of Break Point
1416 FIXME("[0017] EBX=%lx CX=%x\n",
1417 context->Ebx, CX_reg(context));
1423 case 0x0018: /* VirtualLock */
1425 * Input: ECX: Current Process
1427 * EDX: Flat address of arguments on stack
1429 * DWORD *retv [out] TRUE if success, FALSE if failure
1430 * LPVOID base [in] Flat address of range to lock
1431 * DWORD size [in] Size of range
1433 * Output: EAX: NtStatus
1436 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1437 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1438 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1439 DWORD size = stack[2];
1442 TRACE("VirtualLock(%lx, %lx, %lx)\n",
1443 (DWORD)retv, (DWORD)base, size);
1445 result = VirtualLock(base, size);
1449 context->Eax = STATUS_SUCCESS;
1452 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1457 case 0x0019: /* VirtualUnlock */
1459 * Input: ECX: Current Process
1461 * EDX: Flat address of arguments on stack
1463 * DWORD *retv [out] TRUE if success, FALSE if failure
1464 * LPVOID base [in] Flat address of range to unlock
1465 * DWORD size [in] Size of range
1467 * Output: EAX: NtStatus
1470 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1471 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1472 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1473 DWORD size = stack[2];
1476 TRACE("VirtualUnlock(%lx, %lx, %lx)\n",
1477 (DWORD)retv, (DWORD)base, size);
1479 result = VirtualUnlock(base, size);
1483 context->Eax = STATUS_SUCCESS;
1486 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1491 case 0x001A: /* KGetSystemInfo */
1495 * Output: ECX: Start of sparse memory arena
1496 * EDX: End of sparse memory arena
1499 TRACE("KGetSystemInfo()\n");
1502 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1503 * sparse memory arena. We do it the other way around, since
1504 * we have to reserve 3GB - 4GB for Linux, and thus use
1505 * 0GB - 3GB as sparse memory arena.
1507 * FIXME: What about other OSes ?
1510 context->Ecx = W32S_WINE2APP(0x00000000);
1511 context->Edx = W32S_WINE2APP(0xbfffffff);
1515 case 0x001B: /* KGlobalMemStat */
1517 * Input: ESI: Flat address of buffer to receive memory info
1522 struct Win32sMemoryInfo
1524 DWORD DIPhys_Count; /* Total physical pages */
1525 DWORD DIFree_Count; /* Free physical pages */
1526 DWORD DILin_Total_Count; /* Total virtual pages (private arena) */
1527 DWORD DILin_Total_Free; /* Free virtual pages (private arena) */
1529 DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */
1530 DWORD SparseFree; /* Free size of sparse arena (bytes ?) */
1533 struct Win32sMemoryInfo *info =
1534 (struct Win32sMemoryInfo *)W32S_APP2WINE(context->Esi);
1536 FIXME("KGlobalMemStat(%lx)\n", (DWORD)info);
1543 case 0x001C: /* Enable/Disable Exceptions */
1545 * Input: ECX: 0 to disable, 1 to enable exception handling
1550 TRACE("[001c] ECX=%lx\n", context->Ecx);
1556 case 0x001D: /* VirtualAlloc called from 16-bit code */
1558 * Input: EDX: Segmented address of arguments on stack
1560 * LPVOID base [in] Flat address of region to reserve/commit
1561 * DWORD size [in] Size of region
1562 * DWORD type [in] Type of allocation
1563 * DWORD prot [in] Type of access protection
1565 * Output: EAX: NtStatus
1566 * EDX: Flat base address of allocated region
1569 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1570 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1571 DWORD size = stack[1];
1572 DWORD type = stack[2];
1573 DWORD prot = stack[3];
1576 TRACE("VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1577 (DWORD)base, size, type, prot);
1579 if (type & 0x80000000)
1581 WARN("VirtualAlloc16: strange type %lx\n", type);
1585 result = (DWORD)VirtualAlloc(base, size, type, prot);
1587 if (W32S_WINE2APP(result))
1588 context->Edx = W32S_WINE2APP(result),
1589 context->Eax = STATUS_SUCCESS;
1592 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1593 TRACE("VirtualAlloc16: returning base %lx\n", context->Edx);
1598 case 0x001E: /* VirtualFree called from 16-bit code */
1600 * Input: EDX: Segmented address of arguments on stack
1602 * LPVOID base [in] Flat address of region
1603 * DWORD size [in] Size of region
1604 * DWORD type [in] Type of operation
1606 * Output: EAX: NtStatus
1607 * EDX: TRUE if success, FALSE if failure
1610 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1611 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1612 DWORD size = stack[1];
1613 DWORD type = stack[2];
1616 TRACE("VirtualFree16(%lx, %lx, %lx)\n",
1617 (DWORD)base, size, type);
1619 result = VirtualFree(base, size, type);
1622 context->Edx = TRUE,
1623 context->Eax = STATUS_SUCCESS;
1625 context->Edx = FALSE,
1626 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1631 case 0x001F: /* FWorkingSetSize */
1633 * Input: EDX: 0 if Get, 1 if Set
1635 * ECX: Get: Buffer to receive Working Set Size
1636 * Set: Buffer containing Working Set Size
1641 DWORD *ptr = (DWORD *)W32S_APP2WINE(context->Ecx);
1642 BOOL set = context->Edx;
1644 TRACE("FWorkingSetSize(%lx, %lx)\n", (DWORD)ptr, (DWORD)set);
1647 /* We do it differently ... */;
1651 context->Eax = STATUS_SUCCESS;
1657 VXD_BARF( context, "W32S" );