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
23 #include <sys/types.h>
29 #include "wine/winbase16.h"
30 #include "wine/winuser16.h"
34 #include "selectors.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(vxd);
42 #define VXD_BARF(context,name) \
43 DPRINTF( "vxd %s: unknown/not implemented parameters:\n" \
44 "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
45 "SI %04x, DI %04x, DS %04x, ES %04x\n", \
46 (name), (name), AX_reg(context), BX_reg(context), \
47 CX_reg(context), DX_reg(context), SI_reg(context), \
48 DI_reg(context), (WORD)context->SegDs, (WORD)context->SegEs )
52 static WORD VXD_WinVersion(void)
54 WORD version = LOWORD(GetVersion16());
55 return (version >> 8) | (version << 8);
58 /***********************************************************************
59 * VXD_VMM (WPROCS.401)
61 void WINAPI VXD_VMM ( CONTEXT86 *context )
63 unsigned service = AX_reg(context);
65 TRACE("[%04x] VMM\n", (UINT16)service);
69 case 0x0000: /* version */
70 AX_reg(context) = VXD_WinVersion();
74 case 0x026d: /* Get_Debug_Flag '/m' */
75 case 0x026e: /* Get_Debug_Flag '/n' */
81 VXD_BARF( context, "VMM" );
85 /***********************************************************************
86 * VXD_PageFile (WPROCS.433)
88 void WINAPI VXD_PageFile( CONTEXT86 *context )
90 unsigned service = AX_reg(context);
92 /* taken from Ralf Brown's Interrupt List */
94 TRACE("[%04x] PageFile\n", (UINT16)service );
98 case 0x00: /* get version, is this windows version? */
99 TRACE("returning version\n");
100 AX_reg(context) = VXD_WinVersion();
101 RESET_CFLAG(context);
104 case 0x01: /* get swap file info */
105 TRACE("VxD PageFile: returning swap file info\n");
106 AX_reg(context) = 0x00; /* paging disabled */
107 context->Ecx = 0; /* maximum size of paging file */
108 /* FIXME: do I touch DS:SI or DS:DI? */
109 RESET_CFLAG(context);
112 case 0x02: /* delete permanent swap on exit */
113 TRACE("VxD PageFile: supposed to delete swap\n");
114 RESET_CFLAG(context);
117 case 0x03: /* current temporary swap file size */
118 TRACE("VxD PageFile: what is current temp. swap size\n");
119 RESET_CFLAG(context);
122 case 0x04: /* read or write?? INTERRUP.D */
123 case 0x05: /* cancel?? INTERRUP.D */
124 case 0x06: /* test I/O valid INTERRUP.D */
126 VXD_BARF( context, "pagefile" );
131 /***********************************************************************
132 * VXD_Reboot (WPROCS.409)
134 void WINAPI VXD_Reboot ( CONTEXT86 *context )
136 unsigned service = AX_reg(context);
138 TRACE("[%04x] Reboot\n", (UINT16)service);
142 case 0x0000: /* version */
143 AX_reg(context) = VXD_WinVersion();
144 RESET_CFLAG(context);
148 VXD_BARF( context, "REBOOT" );
152 /***********************************************************************
153 * VXD_VDD (WPROCS.410)
155 void WINAPI VXD_VDD ( CONTEXT86 *context )
157 unsigned service = AX_reg(context);
159 TRACE("[%04x] VDD\n", (UINT16)service);
163 case 0x0000: /* version */
164 AX_reg(context) = VXD_WinVersion();
165 RESET_CFLAG(context);
169 VXD_BARF( context, "VDD" );
173 /***********************************************************************
174 * VXD_VMD (WPROCS.412)
176 void WINAPI VXD_VMD ( CONTEXT86 *context )
178 unsigned service = AX_reg(context);
180 TRACE("[%04x] VMD\n", (UINT16)service);
184 case 0x0000: /* version */
185 AX_reg(context) = VXD_WinVersion();
186 RESET_CFLAG(context);
190 VXD_BARF( context, "VMD" );
194 /***********************************************************************
195 * VXD_VXDLoader (WPROCS.439)
197 void WINAPI VXD_VXDLoader( CONTEXT86 *context )
199 unsigned service = AX_reg(context);
201 TRACE("[%04x] VXDLoader\n", (UINT16)service);
205 case 0x0000: /* get version */
206 TRACE("returning version\n");
207 AX_reg(context) = 0x0000;
208 DX_reg(context) = VXD_WinVersion();
209 RESET_CFLAG(context);
212 case 0x0001: /* load device */
213 FIXME("load device %04lx:%04x (%s)\n",
214 context->SegDs, DX_reg(context),
215 debugstr_a(MapSL(MAKESEGPTR(context->SegDs, DX_reg(context)))));
216 AX_reg(context) = 0x0000;
217 context->SegEs = 0x0000;
218 DI_reg(context) = 0x0000;
219 RESET_CFLAG(context);
222 case 0x0002: /* unload device */
223 FIXME("unload device (%08lx)\n", context->Ebx);
224 AX_reg(context) = 0x0000;
225 RESET_CFLAG(context);
229 VXD_BARF( context, "VXDLDR" );
230 AX_reg(context) = 0x000B; /* invalid function number */
236 /***********************************************************************
237 * VXD_Shell (WPROCS.423)
239 void WINAPI VXD_Shell( CONTEXT86 *context )
241 unsigned service = DX_reg(context);
243 TRACE("[%04x] Shell\n", (UINT16)service);
245 switch (service) /* Ralf Brown says EDX, but I use DX instead */
248 TRACE("returning version\n");
249 AX_reg(context) = VXD_WinVersion();
250 context->Ebx = 1; /* system VM Handle */
256 /* SHELL_SYSMODAL_Message
257 ebx virtual maschine handle
258 eax message box flags
259 ecx address of message
260 edi address of caption
261 return response in eax
265 ebx virtual maschine handle
266 eax message box flags
267 ecx address of message
268 edi address of caption
270 edx reference data for callback
271 return response in eax
274 VXD_BARF( context, "shell" );
277 case 0x0006: /* SHELL_Get_VM_State */
278 TRACE("VxD Shell: returning VM state\n");
279 /* Actually we don't, not yet. We have to return a structure
280 * and I am not to sure how to set it up and return it yet,
281 * so for now let's do nothing. I can (hopefully) get this
282 * by the next release
284 /* RESET_CFLAG(context); */
303 VXD_BARF( context, "SHELL" );
306 /* the new Win95 shell API */
307 case 0x0100: /* get version */
308 AX_reg(context) = VXD_WinVersion();
311 case 0x0104: /* retrieve Hook_Properties list */
312 case 0x0105: /* call Hook_Properties callbacks */
313 VXD_BARF( context, "SHELL" );
316 case 0x0106: /* install timeout callback */
317 TRACE("VxD Shell: ignoring shell callback (%ld sec.)\n", context->Ebx);
321 case 0x0107: /* get version of any VxD */
323 VXD_BARF( context, "SHELL" );
329 /***********************************************************************
330 * VXD_Comm (WPROCS.414)
332 void WINAPI VXD_Comm( CONTEXT86 *context )
334 unsigned service = AX_reg(context);
336 TRACE("[%04x] Comm\n", (UINT16)service);
340 case 0x0000: /* get version */
341 TRACE("returning version\n");
342 AX_reg(context) = VXD_WinVersion();
343 RESET_CFLAG(context);
346 case 0x0001: /* set port global */
347 case 0x0002: /* get focus */
348 case 0x0003: /* virtualise port */
350 VXD_BARF( context, "comm" );
354 /***********************************************************************
355 * VXD_Timer (WPROCS.405)
357 void WINAPI VXD_Timer( CONTEXT86 *context )
359 unsigned service = AX_reg(context);
361 TRACE("[%04x] Virtual Timer\n", (UINT16)service);
365 case 0x0000: /* version */
366 AX_reg(context) = VXD_WinVersion();
367 RESET_CFLAG(context);
370 case 0x0100: /* clock tick time, in 840nsecs */
371 context->Eax = GetTickCount();
373 context->Edx = context->Eax >> 22;
374 context->Eax <<= 10; /* not very precise */
377 case 0x0101: /* current Windows time, msecs */
378 case 0x0102: /* current VM time, msecs */
379 context->Eax = GetTickCount();
383 VXD_BARF( context, "VTD" );
387 /***********************************************************************
388 * VXD_TimerAPI (WPROCS.1490)
390 static DWORD System_Time = 0;
391 static WORD System_Time_Selector = 0;
392 static void System_Time_Tick( WORD timer ) { System_Time += 55; }
393 void WINAPI VXD_TimerAPI ( CONTEXT86 *context )
395 unsigned service = AX_reg(context);
397 TRACE("[%04x] TimerAPI\n", (UINT16)service);
401 case 0x0000: /* version */
402 AX_reg(context) = VXD_WinVersion();
403 RESET_CFLAG(context);
406 case 0x0009: /* get system time selector */
407 if ( !System_Time_Selector )
409 System_Time_Selector = SELECTOR_AllocBlock( &System_Time, sizeof(DWORD), WINE_LDT_FLAGS_DATA );
410 CreateSystemTimer( 55, System_Time_Tick );
413 AX_reg(context) = System_Time_Selector;
414 RESET_CFLAG(context);
418 VXD_BARF( context, "VTDAPI" );
422 /***********************************************************************
423 * VXD_ConfigMG (WPROCS.451)
425 void WINAPI VXD_ConfigMG ( CONTEXT86 *context )
427 unsigned service = AX_reg(context);
429 TRACE("[%04x] ConfigMG\n", (UINT16)service);
433 case 0x0000: /* version */
434 AX_reg(context) = VXD_WinVersion();
435 RESET_CFLAG(context);
439 VXD_BARF( context, "CONFIGMG" );
443 /***********************************************************************
444 * VXD_Enable (WPROCS.455)
446 void WINAPI VXD_Enable ( CONTEXT86 *context )
448 unsigned service = AX_reg(context);
450 TRACE("[%04x] Enable\n", (UINT16)service);
454 case 0x0000: /* version */
455 AX_reg(context) = VXD_WinVersion();
456 RESET_CFLAG(context);
460 VXD_BARF( context, "ENABLE" );
464 /***********************************************************************
465 * VXD_APM (WPROCS.438)
467 void WINAPI VXD_APM ( CONTEXT86 *context )
469 unsigned service = AX_reg(context);
471 TRACE("[%04x] APM\n", (UINT16)service);
475 case 0x0000: /* version */
476 AX_reg(context) = VXD_WinVersion();
477 RESET_CFLAG(context);
481 VXD_BARF( context, "APM" );
485 /***********************************************************************
486 * VXD_Win32s (WPROCS.445)
488 * This is an implementation of the services of the Win32s VxD.
489 * Since official documentation of these does not seem to be available,
490 * certain arguments of some of the services remain unclear.
492 * FIXME: The following services are currently unimplemented:
493 * Exception handling (0x01, 0x1C)
494 * Debugger support (0x0C, 0x14, 0x17)
495 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
496 * Memory Statistics (0x1B)
499 * We have a specific problem running Win32s on Linux (and probably also
500 * the other x86 unixes), since Win32s tries to allocate its main 'flat
501 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
502 * The rationale for this seems to be that they want one the one hand to
503 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
504 * at linear address 0, but want at other hand to have offset 0 of the
505 * flat data/code segment point to an unmapped page (to catch NULL pointer
506 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
507 * so that the Win 3.1 memory area at linear address zero shows up in the
508 * flat segments at offset 0x10000 (since linear addresses wrap around at
509 * 4GB). To compensate for that discrepancy between flat segment offsets
510 * and plain linear addresses, all flat pointers passed between the 32-bit
511 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
512 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
514 * The problem for us is now that Linux does not allow a LDT selector with
515 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
516 * address space. To address this problem we introduce *another* offset:
517 * We add 0x10000 to every linear address we get as an argument from Win32s.
518 * This means especially that the flat code/data selectors get actually
519 * allocated with base 0x0, so that flat offsets and (real) linear addresses
520 * do again agree! In fact, every call e.g. of a Win32s VxD service now
521 * has all pointer arguments (which are offsets in the flat data segement)
522 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
523 * increased by 0x10000 by *our* code.
525 * Note that to keep everything consistent, this offset has to be applied by
526 * every Wine function that operates on 'linear addresses' passed to it by
527 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
528 * API routines, this affects only two locations: this VxD and the DPMI
529 * handler. (NOTE: Should any Win32s application pass a linear address to
530 * any routine apart from those, e.g. some other VxD handler, that code
531 * would have to take the offset into account as well!)
533 * The offset is set the first time any application calls the GetVersion()
534 * service of the Win32s VxD. (Note that the offset is never reset.)
538 void WINAPI VXD_Win32s( CONTEXT86 *context )
540 switch (AX_reg(context))
542 case 0x0000: /* Get Version */
546 * Output: EAX: LoWord: Win32s Version (1.30)
547 * HiWord: VxD Version (200)
553 * EDX: Debugging Flags
557 * 1 if VMCPD VxD not found
560 TRACE("GetVersion()\n");
562 context->Eax = VXD_WinVersion() | (200 << 16);
569 * If this is the first time we are called for this process,
570 * hack the memory image of WIN32S16 so that it doesn't try
571 * to access the GDT directly ...
573 * The first code segment of WIN32S16 (version 1.30) contains
574 * an unexported function somewhere between the exported functions
575 * SetFS and StackLinearToSegmented that tries to find a selector
576 * in the LDT that maps to the memory image of the LDT itself.
577 * If it succeeds, it stores this selector into a global variable
578 * which will be used to speed up execution by using this selector
579 * to modify the LDT directly instead of using the DPMI calls.
581 * To perform this search of the LDT, this function uses the
582 * sgdt and sldt instructions to find the linear address of
583 * the (GDT and then) LDT. While those instructions themselves
584 * execute without problem, the linear address that sgdt returns
585 * points (at least under Linux) to the kernel address space, so
586 * that any subsequent access leads to a segfault.
588 * Fortunately, WIN32S16 still contains as a fallback option the
589 * mechanism of using DPMI calls to modify LDT selectors instead
590 * of direct writes to the LDT. Thus we can circumvent the problem
591 * by simply replacing the first byte of the offending function
592 * with an 'retf' instruction. This means that the global variable
593 * supposed to contain the LDT alias selector will remain zero,
594 * and hence WIN32S16 will fall back to using DPMI calls.
596 * The heuristic we employ to _find_ that function is as follows:
597 * We search between the addresses of the exported symbols SetFS
598 * and StackLinearToSegmented for the byte sequence '0F 01 04'
599 * (this is the opcode of 'sgdt [si]'). We then search backwards
600 * from this address for the last occurrence of 'CB' (retf) that marks
601 * the end of the preceeding function. The following byte (which
602 * should now be the first byte of the function we are looking for)
603 * will be replaced by 'CB' (retf).
605 * This heuristic works for the retail as well as the debug version
606 * of Win32s version 1.30. For versions earlier than that this
607 * hack should not be necessary at all, since the whole mechanism
608 * ('PERF130') was introduced only in 1.30 to improve the overall
609 * performance of Win32s.
614 HMODULE16 hModule = GetModuleHandle16("win32s16");
615 SEGPTR func1 = (SEGPTR)GetProcAddress16(hModule, "SetFS");
616 SEGPTR func2 = (SEGPTR)GetProcAddress16(hModule, "StackLinearToSegmented");
618 if ( hModule && func1 && func2
619 && SELECTOROF(func1) == SELECTOROF(func2))
621 BYTE *start = MapSL(func1);
622 BYTE *end = MapSL(func2);
623 BYTE *p, *retv = NULL;
626 for (p = start; p < end; p++)
627 if (*p == 0xCB) found = 0, retv = p;
628 else if (*p == 0x0F) found = 1;
629 else if (*p == 0x01 && found == 1) found = 2;
630 else if (*p == 0x04 && found == 2) { found = 3; break; }
633 if (found == 3 && retv)
635 TRACE("PERF130 hack: "
636 "Replacing byte %02X at offset %04X:%04X\n",
637 *(retv+1), SELECTOROF(func1),
638 OFFSETOF(func1) + retv+1-start);
640 *(retv+1) = (BYTE)0xCB;
646 * Mark process as Win32s, so that subsequent DPMI calls
647 * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
649 W32S_offset = 0x10000;
653 case 0x0001: /* Install Exception Handling */
655 * Input: EBX: Flat address of W32SKRNL Exception Data
657 * ECX: LoWord: Flat Code Selector
658 * HiWord: Flat Data Selector
660 * EDX: Flat address of W32SKRNL Exception Handler
661 * (this is equal to W32S_BackTo32 + 0x40)
663 * ESI: SEGPTR KERNEL.HASGPHANDLER
665 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
667 * Output: EAX: 0 if OK
670 TRACE("[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
671 context->Ebx, context->Ecx, context->Edx,
672 context->Esi, context->Edi);
680 case 0x0002: /* Set Page Access Flags */
682 * Input: EBX: New access flags
683 * Bit 2: User Page if set, Supervisor Page if clear
684 * Bit 1: Read-Write if set, Read-Only if clear
686 * ECX: Size of memory area to change
688 * EDX: Flat start address of memory area
690 * Output: EAX: Size of area changed
693 TRACE("[0002] EBX=%lx ECX=%lx EDX=%lx\n",
694 context->Ebx, context->Ecx, context->Edx);
698 context->Eax = context->Ecx;
702 case 0x0003: /* Get Page Access Flags */
704 * Input: EDX: Flat address of page to query
706 * Output: EAX: Page access flags
707 * Bit 2: User Page if set, Supervisor Page if clear
708 * Bit 1: Read-Write if set, Read-Only if clear
711 TRACE("[0003] EDX=%lx\n", context->Edx);
719 case 0x0004: /* Map Module */
721 * Input: ECX: IMTE (offset in Module Table) of new module
723 * EDX: Flat address of Win32s Module Table
725 * Output: EAX: 0 if OK
728 if (!context->Edx || CX_reg(context) == 0xFFFF)
730 TRACE("MapModule: Initialization call\n");
736 * Structure of a Win32s Module Table Entry:
751 * Note: This function should set up a demand-paged memory image
752 * of the given module. Since mmap does not allow file offsets
753 * not aligned at 1024 bytes, we simply load the image fully
757 struct Win32sModule *moduleTable =
758 (struct Win32sModule *)W32S_APP2WINE(context->Edx);
759 struct Win32sModule *module = moduleTable + context->Ecx;
761 IMAGE_NT_HEADERS *nt_header = PE_HEADER(module->baseAddr);
762 IMAGE_SECTION_HEADER *pe_seg = PE_SECTIONS(module->baseAddr);
764 HFILE image = _lopen(module->pathName, OF_READ);
765 BOOL error = (image == HFILE_ERROR);
768 TRACE("MapModule: Loading %s\n", module->pathName);
771 !error && i < nt_header->FileHeader.NumberOfSections;
773 if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
775 DWORD off = pe_seg->PointerToRawData;
776 DWORD len = pe_seg->SizeOfRawData;
777 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
780 "Section %d at %08lx from %08lx len %08lx\n",
781 i, (DWORD)addr, off, len);
783 if ( _llseek(image, off, SEEK_SET) != off
784 || _lread(image, addr, len) != len)
791 ERR("MapModule: Unable to load %s\n", module->pathName);
793 else if (module->relocDelta != 0)
795 IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
796 + IMAGE_DIRECTORY_ENTRY_BASERELOC;
797 IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
798 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
800 TRACE("MapModule: Reloc delta %08lx\n", module->relocDelta);
802 while (r && r->VirtualAddress)
804 LPBYTE page = module->baseAddr + r->VirtualAddress;
805 WORD *TypeOffset = (WORD *)(r + 1);
806 int count = (r->SizeOfBlock - sizeof(*r)) / sizeof(*TypeOffset);
808 TRACE("MapModule: %d relocations for page %08lx\n",
811 for(i = 0; i < count; i++)
813 int offset = TypeOffset[i] & 0xFFF;
814 int type = TypeOffset[i] >> 12;
817 case IMAGE_REL_BASED_ABSOLUTE:
819 case IMAGE_REL_BASED_HIGH:
820 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
822 case IMAGE_REL_BASED_LOW:
823 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
825 case IMAGE_REL_BASED_HIGHLOW:
826 *(DWORD*)(page+offset) += module->relocDelta;
829 WARN("MapModule: Unsupported fixup type\n");
834 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
839 RESET_CFLAG(context);
844 case 0x0005: /* UnMap Module */
846 * Input: EDX: Flat address of module image
848 * Output: EAX: 1 if OK
851 TRACE("UnMapModule: %lx\n", (DWORD)W32S_APP2WINE(context->Edx));
853 /* As we didn't map anything, there's nothing to unmap ... */
859 case 0x0006: /* VirtualAlloc */
861 * Input: ECX: Current Process
863 * EDX: Flat address of arguments on stack
865 * DWORD *retv [out] Flat base address of allocated region
866 * LPVOID base [in] Flat address of region to reserve/commit
867 * DWORD size [in] Size of region
868 * DWORD type [in] Type of allocation
869 * DWORD prot [in] Type of access protection
871 * Output: EAX: NtStatus
874 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
875 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
876 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
877 DWORD size = stack[2];
878 DWORD type = stack[3];
879 DWORD prot = stack[4];
882 TRACE("VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
883 (DWORD)retv, (DWORD)base, size, type, prot);
885 if (type & 0x80000000)
887 WARN("VirtualAlloc: strange type %lx\n", type);
891 if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
893 WARN("VirtualAlloc: NLS hack, allowing write access!\n");
894 prot = PAGE_READWRITE;
897 result = (DWORD)VirtualAlloc(base, size, type, prot);
899 if (W32S_WINE2APP(result))
900 *retv = W32S_WINE2APP(result),
901 context->Eax = STATUS_SUCCESS;
904 context->Eax = STATUS_NO_MEMORY; /* FIXME */
909 case 0x0007: /* VirtualFree */
911 * Input: ECX: Current Process
913 * EDX: Flat address of arguments on stack
915 * DWORD *retv [out] TRUE if success, FALSE if failure
916 * LPVOID base [in] Flat address of region
917 * DWORD size [in] Size of region
918 * DWORD type [in] Type of operation
920 * Output: EAX: NtStatus
923 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
924 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
925 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
926 DWORD size = stack[2];
927 DWORD type = stack[3];
930 TRACE("VirtualFree(%lx, %lx, %lx, %lx)\n",
931 (DWORD)retv, (DWORD)base, size, type);
933 result = VirtualFree(base, size, type);
937 context->Eax = STATUS_SUCCESS;
940 context->Eax = STATUS_NO_MEMORY; /* FIXME */
945 case 0x0008: /* VirtualProtect */
947 * Input: ECX: Current Process
949 * EDX: Flat address of arguments on stack
951 * DWORD *retv [out] TRUE if success, FALSE if failure
952 * LPVOID base [in] Flat address of region
953 * DWORD size [in] Size of region
954 * DWORD new_prot [in] Desired access protection
955 * DWORD *old_prot [out] Previous access protection
957 * Output: EAX: NtStatus
960 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
961 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
962 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
963 DWORD size = stack[2];
964 DWORD new_prot = stack[3];
965 DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4]);
968 TRACE("VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
969 (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
971 result = VirtualProtect(base, size, new_prot, old_prot);
975 context->Eax = STATUS_SUCCESS;
978 context->Eax = STATUS_NO_MEMORY; /* FIXME */
983 case 0x0009: /* VirtualQuery */
985 * Input: ECX: Current Process
987 * EDX: Flat address of arguments on stack
989 * DWORD *retv [out] Nr. bytes returned
990 * LPVOID base [in] Flat address of region
991 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
992 * DWORD len [in] Size of buffer
994 * Output: EAX: NtStatus
997 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
998 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
999 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1000 LPMEMORY_BASIC_INFORMATION info =
1001 (LPMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2]);
1002 DWORD len = stack[3];
1005 TRACE("VirtualQuery(%lx, %lx, %lx, %lx)\n",
1006 (DWORD)retv, (DWORD)base, (DWORD)info, len);
1008 result = VirtualQuery(base, info, len);
1011 context->Eax = STATUS_SUCCESS;
1016 case 0x000A: /* SetVirtMemProcess */
1018 * Input: ECX: Process Handle
1020 * EDX: Flat address of region
1022 * Output: EAX: NtStatus
1025 TRACE("[000a] ECX=%lx EDX=%lx\n",
1026 context->Ecx, context->Edx);
1030 context->Eax = STATUS_SUCCESS;
1034 case 0x000B: /* ??? some kind of cleanup */
1036 * Input: ECX: Process Handle
1038 * Output: EAX: NtStatus
1041 TRACE("[000b] ECX=%lx\n", context->Ecx);
1045 context->Eax = STATUS_SUCCESS;
1049 case 0x000C: /* Set Debug Flags */
1051 * Input: EDX: Debug Flags
1053 * Output: EDX: Previous Debug Flags
1056 FIXME("[000c] EDX=%lx\n", context->Edx);
1064 case 0x000D: /* NtCreateSection */
1066 * Input: EDX: Flat address of arguments on stack
1068 * HANDLE32 *retv [out] Handle of Section created
1069 * DWORD flags1 [in] (?? unknown ??)
1070 * DWORD atom [in] Name of Section to create
1071 * LARGE_INTEGER *size [in] Size of Section
1072 * DWORD protect [in] Access protection
1073 * DWORD flags2 [in] (?? unknown ??)
1074 * HFILE32 hFile [in] Handle of file to map
1075 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
1077 * Output: EAX: NtStatus
1080 DWORD *stack = (DWORD *) W32S_APP2WINE(context->Edx);
1081 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1082 DWORD flags1 = stack[1];
1083 DWORD atom = stack[2];
1084 LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3]);
1085 DWORD protect = stack[4];
1086 DWORD flags2 = stack[5];
1087 HANDLE hFile = DosFileHandleToWin32Handle(stack[6]);
1088 DWORD psp = stack[7];
1090 HANDLE result = INVALID_HANDLE_VALUE;
1093 TRACE("NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1094 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1097 if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1099 TRACE("NtCreateSection: name=%s\n", atom? name : NULL);
1101 result = CreateFileMappingA(hFile, NULL, protect,
1102 size? size->s.HighPart : 0,
1103 size? size->s.LowPart : 0,
1107 if (result == INVALID_HANDLE_VALUE)
1108 WARN("NtCreateSection: failed!\n");
1110 TRACE("NtCreateSection: returned %lx\n", (DWORD)result);
1112 if (result != INVALID_HANDLE_VALUE)
1114 context->Eax = STATUS_SUCCESS;
1117 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1122 case 0x000E: /* NtOpenSection */
1124 * Input: EDX: Flat address of arguments on stack
1126 * HANDLE32 *retv [out] Handle of Section opened
1127 * DWORD protect [in] Access protection
1128 * DWORD atom [in] Name of Section to create
1130 * Output: EAX: NtStatus
1133 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1134 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1135 DWORD protect = stack[1];
1136 DWORD atom = stack[2];
1138 HANDLE result = INVALID_HANDLE_VALUE;
1141 TRACE("NtOpenSection(%lx, %lx, %lx)\n",
1142 (DWORD)retv, protect, atom);
1144 if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1146 TRACE("NtOpenSection: name=%s\n", name);
1148 result = OpenFileMappingA(protect, FALSE, name);
1151 if (result == INVALID_HANDLE_VALUE)
1152 WARN("NtOpenSection: failed!\n");
1154 TRACE("NtOpenSection: returned %lx\n", (DWORD)result);
1156 if (result != INVALID_HANDLE_VALUE)
1158 context->Eax = STATUS_SUCCESS;
1161 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1166 case 0x000F: /* NtCloseSection */
1168 * Input: EDX: Flat address of arguments on stack
1170 * HANDLE32 handle [in] Handle of Section to close
1171 * DWORD *id [out] Unique ID (?? unclear ??)
1173 * Output: EAX: NtStatus
1176 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1177 HANDLE handle = (HANDLE)stack[0];
1178 DWORD *id = (DWORD *)W32S_APP2WINE(stack[1]);
1180 TRACE("NtCloseSection(%lx, %lx)\n", (DWORD)handle, (DWORD)id);
1182 CloseHandle(handle);
1183 if (id) *id = 0; /* FIXME */
1185 context->Eax = STATUS_SUCCESS;
1190 case 0x0010: /* NtDupSection */
1192 * Input: EDX: Flat address of arguments on stack
1194 * HANDLE32 handle [in] Handle of Section to duplicate
1196 * Output: EAX: NtStatus
1199 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1200 HANDLE handle = (HANDLE)stack[0];
1203 TRACE("NtDupSection(%lx)\n", (DWORD)handle);
1205 DuplicateHandle( GetCurrentProcess(), handle,
1206 GetCurrentProcess(), &new_handle,
1207 0, FALSE, DUPLICATE_SAME_ACCESS );
1208 context->Eax = STATUS_SUCCESS;
1213 case 0x0011: /* NtMapViewOfSection */
1215 * Input: EDX: Flat address of arguments on stack
1217 * HANDLE32 SectionHandle [in] Section to be mapped
1218 * DWORD ProcessHandle [in] Process to be mapped into
1219 * DWORD * BaseAddress [in/out] Address to be mapped at
1220 * DWORD ZeroBits [in] (?? unclear ??)
1221 * DWORD CommitSize [in] (?? unclear ??)
1222 * LARGE_INTEGER *SectionOffset [in] Offset within section
1223 * DWORD * ViewSize [in] Size of view
1224 * DWORD InheritDisposition [in] (?? unclear ??)
1225 * DWORD AllocationType [in] (?? unclear ??)
1226 * DWORD Protect [in] Access protection
1228 * Output: EAX: NtStatus
1231 DWORD * stack = (DWORD *)W32S_APP2WINE(context->Edx);
1232 HANDLE SectionHandle = (HANDLE)stack[0];
1233 DWORD ProcessHandle = stack[1]; /* ignored */
1234 DWORD * BaseAddress = (DWORD *)W32S_APP2WINE(stack[2]);
1235 DWORD ZeroBits = stack[3];
1236 DWORD CommitSize = stack[4];
1237 LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5]);
1238 DWORD * ViewSize = (DWORD *)W32S_APP2WINE(stack[6]);
1239 DWORD InheritDisposition = stack[7];
1240 DWORD AllocationType = stack[8];
1241 DWORD Protect = stack[9];
1243 LPBYTE address = (LPBYTE)(BaseAddress?
1244 W32S_APP2WINE(*BaseAddress) : 0);
1245 DWORD access = 0, result;
1247 switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1249 case PAGE_READONLY: access = FILE_MAP_READ; break;
1250 case PAGE_READWRITE: access = FILE_MAP_WRITE; break;
1251 case PAGE_WRITECOPY: access = FILE_MAP_COPY; break;
1253 case PAGE_EXECUTE_READ: access = FILE_MAP_READ; break;
1254 case PAGE_EXECUTE_READWRITE: access = FILE_MAP_WRITE; break;
1255 case PAGE_EXECUTE_WRITECOPY: access = FILE_MAP_COPY; break;
1258 TRACE("NtMapViewOfSection"
1259 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1260 (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1261 ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1262 InheritDisposition, AllocationType, Protect);
1263 TRACE("NtMapViewOfSection: "
1264 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1265 (DWORD)address, SectionOffset? SectionOffset->s.LowPart : 0,
1266 ViewSize? *ViewSize : 0, access);
1268 result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1269 SectionOffset? SectionOffset->s.HighPart : 0,
1270 SectionOffset? SectionOffset->s.LowPart : 0,
1271 ViewSize? *ViewSize : 0, address);
1273 TRACE("NtMapViewOfSection: result=%lx\n", result);
1275 if (W32S_WINE2APP(result))
1277 if (BaseAddress) *BaseAddress = W32S_WINE2APP(result);
1278 context->Eax = STATUS_SUCCESS;
1281 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1286 case 0x0012: /* NtUnmapViewOfSection */
1288 * Input: EDX: Flat address of arguments on stack
1290 * DWORD ProcessHandle [in] Process (defining address space)
1291 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1293 * Output: EAX: NtStatus
1296 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1297 DWORD ProcessHandle = stack[0]; /* ignored */
1298 LPBYTE BaseAddress = (LPBYTE)W32S_APP2WINE(stack[1]);
1300 TRACE("NtUnmapViewOfSection(%lx, %lx)\n",
1301 ProcessHandle, (DWORD)BaseAddress);
1303 UnmapViewOfFile(BaseAddress);
1305 context->Eax = STATUS_SUCCESS;
1310 case 0x0013: /* NtFlushVirtualMemory */
1312 * Input: EDX: Flat address of arguments on stack
1314 * DWORD ProcessHandle [in] Process (defining address space)
1315 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1316 * DWORD *ViewSize [in?] Number of bytes to be flushed
1317 * DWORD *unknown [???] (?? unknown ??)
1319 * Output: EAX: NtStatus
1322 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1323 DWORD ProcessHandle = stack[0]; /* ignored */
1324 DWORD *BaseAddress = (DWORD *)W32S_APP2WINE(stack[1]);
1325 DWORD *ViewSize = (DWORD *)W32S_APP2WINE(stack[2]);
1326 DWORD *unknown = (DWORD *)W32S_APP2WINE(stack[3]);
1328 LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress) : 0);
1329 DWORD size = ViewSize? *ViewSize : 0;
1331 TRACE("NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1332 ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1334 TRACE("NtFlushVirtualMemory: base=%lx, size=%lx\n",
1335 (DWORD)address, size);
1337 FlushViewOfFile(address, size);
1339 context->Eax = STATUS_SUCCESS;
1344 case 0x0014: /* Get/Set Debug Registers */
1346 * Input: ECX: 0 if Get, 1 if Set
1348 * EDX: Get: Flat address of buffer to receive values of
1349 * debug registers DR0 .. DR7
1350 * Set: Flat address of buffer containing values of
1351 * debug registers DR0 .. DR7 to be set
1355 FIXME("[0014] ECX=%lx EDX=%lx\n",
1356 context->Ecx, context->Edx);
1362 case 0x0015: /* Set Coprocessor Emulation Flag */
1364 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1369 TRACE("[0015] EDX=%lx\n", context->Edx);
1371 /* We don't care, as we always have a coprocessor anyway */
1375 case 0x0016: /* Init Win32S VxD PSP */
1377 * If called to query required PSP size:
1380 * Output: EDX: Required size of Win32s VxD PSP
1382 * If called to initialize allocated PSP:
1384 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1385 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1389 if (context->Ebx == 0)
1390 context->Edx = 0x80;
1393 PDB16 *psp = MapSL( MAKESEGPTR( BX_reg(context), 0 ));
1395 psp->fileHandlesPtr = MAKELONG(HIWORD(context->Ebx), 0x5c);
1396 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1401 case 0x0017: /* Set Break Point */
1403 * Input: EBX: Offset of Break Point
1404 * CX: Selector of Break Point
1409 FIXME("[0017] EBX=%lx CX=%x\n",
1410 context->Ebx, CX_reg(context));
1416 case 0x0018: /* VirtualLock */
1418 * Input: ECX: Current Process
1420 * EDX: Flat address of arguments on stack
1422 * DWORD *retv [out] TRUE if success, FALSE if failure
1423 * LPVOID base [in] Flat address of range to lock
1424 * DWORD size [in] Size of range
1426 * Output: EAX: NtStatus
1429 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1430 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1431 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1432 DWORD size = stack[2];
1435 TRACE("VirtualLock(%lx, %lx, %lx)\n",
1436 (DWORD)retv, (DWORD)base, size);
1438 result = VirtualLock(base, size);
1442 context->Eax = STATUS_SUCCESS;
1445 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1450 case 0x0019: /* VirtualUnlock */
1452 * Input: ECX: Current Process
1454 * EDX: Flat address of arguments on stack
1456 * DWORD *retv [out] TRUE if success, FALSE if failure
1457 * LPVOID base [in] Flat address of range to unlock
1458 * DWORD size [in] Size of range
1460 * Output: EAX: NtStatus
1463 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1464 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1465 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1466 DWORD size = stack[2];
1469 TRACE("VirtualUnlock(%lx, %lx, %lx)\n",
1470 (DWORD)retv, (DWORD)base, size);
1472 result = VirtualUnlock(base, size);
1476 context->Eax = STATUS_SUCCESS;
1479 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1484 case 0x001A: /* KGetSystemInfo */
1488 * Output: ECX: Start of sparse memory arena
1489 * EDX: End of sparse memory arena
1492 TRACE("KGetSystemInfo()\n");
1495 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1496 * sparse memory arena. We do it the other way around, since
1497 * we have to reserve 3GB - 4GB for Linux, and thus use
1498 * 0GB - 3GB as sparse memory arena.
1500 * FIXME: What about other OSes ?
1503 context->Ecx = W32S_WINE2APP(0x00000000);
1504 context->Edx = W32S_WINE2APP(0xbfffffff);
1508 case 0x001B: /* KGlobalMemStat */
1510 * Input: ESI: Flat address of buffer to receive memory info
1515 struct Win32sMemoryInfo
1517 DWORD DIPhys_Count; /* Total physical pages */
1518 DWORD DIFree_Count; /* Free physical pages */
1519 DWORD DILin_Total_Count; /* Total virtual pages (private arena) */
1520 DWORD DILin_Total_Free; /* Free virtual pages (private arena) */
1522 DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */
1523 DWORD SparseFree; /* Free size of sparse arena (bytes ?) */
1526 struct Win32sMemoryInfo *info =
1527 (struct Win32sMemoryInfo *)W32S_APP2WINE(context->Esi);
1529 FIXME("KGlobalMemStat(%lx)\n", (DWORD)info);
1536 case 0x001C: /* Enable/Disable Exceptions */
1538 * Input: ECX: 0 to disable, 1 to enable exception handling
1543 TRACE("[001c] ECX=%lx\n", context->Ecx);
1549 case 0x001D: /* VirtualAlloc called from 16-bit code */
1551 * Input: EDX: Segmented address of arguments on stack
1553 * LPVOID base [in] Flat address of region to reserve/commit
1554 * DWORD size [in] Size of region
1555 * DWORD type [in] Type of allocation
1556 * DWORD prot [in] Type of access protection
1558 * Output: EAX: NtStatus
1559 * EDX: Flat base address of allocated region
1562 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1563 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1564 DWORD size = stack[1];
1565 DWORD type = stack[2];
1566 DWORD prot = stack[3];
1569 TRACE("VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1570 (DWORD)base, size, type, prot);
1572 if (type & 0x80000000)
1574 WARN("VirtualAlloc16: strange type %lx\n", type);
1578 result = (DWORD)VirtualAlloc(base, size, type, prot);
1580 if (W32S_WINE2APP(result))
1581 context->Edx = W32S_WINE2APP(result),
1582 context->Eax = STATUS_SUCCESS;
1585 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1586 TRACE("VirtualAlloc16: returning base %lx\n", context->Edx);
1591 case 0x001E: /* VirtualFree called from 16-bit code */
1593 * Input: EDX: Segmented address of arguments on stack
1595 * LPVOID base [in] Flat address of region
1596 * DWORD size [in] Size of region
1597 * DWORD type [in] Type of operation
1599 * Output: EAX: NtStatus
1600 * EDX: TRUE if success, FALSE if failure
1603 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1604 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1605 DWORD size = stack[1];
1606 DWORD type = stack[2];
1609 TRACE("VirtualFree16(%lx, %lx, %lx)\n",
1610 (DWORD)base, size, type);
1612 result = VirtualFree(base, size, type);
1615 context->Edx = TRUE,
1616 context->Eax = STATUS_SUCCESS;
1618 context->Edx = FALSE,
1619 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1624 case 0x001F: /* FWorkingSetSize */
1626 * Input: EDX: 0 if Get, 1 if Set
1628 * ECX: Get: Buffer to receive Working Set Size
1629 * Set: Buffer containing Working Set Size
1634 DWORD *ptr = (DWORD *)W32S_APP2WINE(context->Ecx);
1635 BOOL set = context->Edx;
1637 TRACE("FWorkingSetSize(%lx, %lx)\n", (DWORD)ptr, (DWORD)set);
1640 /* We do it differently ... */;
1644 context->Eax = STATUS_SUCCESS;
1650 VXD_BARF( context, "W32S" );