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>
30 #include "wine/winbase16.h"
31 #include "wine/winuser16.h"
35 #include "selectors.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(vxd);
43 #define VXD_BARF(context,name) \
44 DPRINTF( "vxd %s: unknown/not implemented parameters:\n" \
45 "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
46 "SI %04x, DI %04x, DS %04x, ES %04x\n", \
47 (name), (name), AX_reg(context), BX_reg(context), \
48 CX_reg(context), DX_reg(context), SI_reg(context), \
49 DI_reg(context), (WORD)context->SegDs, (WORD)context->SegEs )
53 static WORD VXD_WinVersion(void)
55 WORD version = LOWORD(GetVersion16());
56 return (version >> 8) | (version << 8);
59 /***********************************************************************
60 * VXD_VMM (WPROCS.401)
62 void WINAPI VXD_VMM ( CONTEXT86 *context )
64 unsigned service = AX_reg(context);
66 TRACE("[%04x] VMM\n", (UINT16)service);
70 case 0x0000: /* version */
71 AX_reg(context) = VXD_WinVersion();
75 case 0x026d: /* Get_Debug_Flag '/m' */
76 case 0x026e: /* Get_Debug_Flag '/n' */
82 VXD_BARF( context, "VMM" );
86 /***********************************************************************
87 * VXD_PageFile (WPROCS.433)
89 void WINAPI VXD_PageFile( CONTEXT86 *context )
91 unsigned service = AX_reg(context);
93 /* taken from Ralf Brown's Interrupt List */
95 TRACE("[%04x] PageFile\n", (UINT16)service );
99 case 0x00: /* get version, is this windows version? */
100 TRACE("returning version\n");
101 AX_reg(context) = VXD_WinVersion();
102 RESET_CFLAG(context);
105 case 0x01: /* get swap file info */
106 TRACE("VxD PageFile: returning swap file info\n");
107 AX_reg(context) = 0x00; /* paging disabled */
108 context->Ecx = 0; /* maximum size of paging file */
109 /* FIXME: do I touch DS:SI or DS:DI? */
110 RESET_CFLAG(context);
113 case 0x02: /* delete permanent swap on exit */
114 TRACE("VxD PageFile: supposed to delete swap\n");
115 RESET_CFLAG(context);
118 case 0x03: /* current temporary swap file size */
119 TRACE("VxD PageFile: what is current temp. swap size\n");
120 RESET_CFLAG(context);
123 case 0x04: /* read or write?? INTERRUP.D */
124 case 0x05: /* cancel?? INTERRUP.D */
125 case 0x06: /* test I/O valid INTERRUP.D */
127 VXD_BARF( context, "pagefile" );
132 /***********************************************************************
133 * VXD_Reboot (WPROCS.409)
135 void WINAPI VXD_Reboot ( CONTEXT86 *context )
137 unsigned service = AX_reg(context);
139 TRACE("[%04x] Reboot\n", (UINT16)service);
143 case 0x0000: /* version */
144 AX_reg(context) = VXD_WinVersion();
145 RESET_CFLAG(context);
149 VXD_BARF( context, "REBOOT" );
153 /***********************************************************************
154 * VXD_VDD (WPROCS.410)
156 void WINAPI VXD_VDD ( CONTEXT86 *context )
158 unsigned service = AX_reg(context);
160 TRACE("[%04x] VDD\n", (UINT16)service);
164 case 0x0000: /* version */
165 AX_reg(context) = VXD_WinVersion();
166 RESET_CFLAG(context);
170 VXD_BARF( context, "VDD" );
174 /***********************************************************************
175 * VXD_VMD (WPROCS.412)
177 void WINAPI VXD_VMD ( CONTEXT86 *context )
179 unsigned service = AX_reg(context);
181 TRACE("[%04x] VMD\n", (UINT16)service);
185 case 0x0000: /* version */
186 AX_reg(context) = VXD_WinVersion();
187 RESET_CFLAG(context);
191 VXD_BARF( context, "VMD" );
195 /***********************************************************************
196 * VXD_VXDLoader (WPROCS.439)
198 void WINAPI VXD_VXDLoader( CONTEXT86 *context )
200 unsigned service = AX_reg(context);
202 TRACE("[%04x] VXDLoader\n", (UINT16)service);
206 case 0x0000: /* get version */
207 TRACE("returning version\n");
208 AX_reg(context) = 0x0000;
209 DX_reg(context) = VXD_WinVersion();
210 RESET_CFLAG(context);
213 case 0x0001: /* load device */
214 FIXME("load device %04lx:%04x (%s)\n",
215 context->SegDs, DX_reg(context),
216 debugstr_a(MapSL(MAKESEGPTR(context->SegDs, DX_reg(context)))));
217 AX_reg(context) = 0x0000;
218 context->SegEs = 0x0000;
219 DI_reg(context) = 0x0000;
220 RESET_CFLAG(context);
223 case 0x0002: /* unload device */
224 FIXME("unload device (%08lx)\n", context->Ebx);
225 AX_reg(context) = 0x0000;
226 RESET_CFLAG(context);
230 VXD_BARF( context, "VXDLDR" );
231 AX_reg(context) = 0x000B; /* invalid function number */
237 /***********************************************************************
238 * VXD_Shell (WPROCS.423)
240 void WINAPI VXD_Shell( CONTEXT86 *context )
242 unsigned service = DX_reg(context);
244 TRACE("[%04x] Shell\n", (UINT16)service);
246 switch (service) /* Ralf Brown says EDX, but I use DX instead */
249 TRACE("returning version\n");
250 AX_reg(context) = VXD_WinVersion();
251 context->Ebx = 1; /* system VM Handle */
257 /* SHELL_SYSMODAL_Message
258 ebx virtual maschine handle
259 eax message box flags
260 ecx address of message
261 edi address of caption
262 return response in eax
266 ebx virtual maschine handle
267 eax message box flags
268 ecx address of message
269 edi address of caption
271 edx reference data for callback
272 return response in eax
275 VXD_BARF( context, "shell" );
278 case 0x0006: /* SHELL_Get_VM_State */
279 TRACE("VxD Shell: returning VM state\n");
280 /* Actually we don't, not yet. We have to return a structure
281 * and I am not to sure how to set it up and return it yet,
282 * so for now let's do nothing. I can (hopefully) get this
283 * by the next release
285 /* RESET_CFLAG(context); */
304 VXD_BARF( context, "SHELL" );
307 /* the new Win95 shell API */
308 case 0x0100: /* get version */
309 AX_reg(context) = VXD_WinVersion();
312 case 0x0104: /* retrieve Hook_Properties list */
313 case 0x0105: /* call Hook_Properties callbacks */
314 VXD_BARF( context, "SHELL" );
317 case 0x0106: /* install timeout callback */
318 TRACE("VxD Shell: ignoring shell callback (%ld sec.)\n", context->Ebx);
322 case 0x0107: /* get version of any VxD */
324 VXD_BARF( context, "SHELL" );
330 /***********************************************************************
331 * VXD_Comm (WPROCS.414)
333 void WINAPI VXD_Comm( CONTEXT86 *context )
335 unsigned service = AX_reg(context);
337 TRACE("[%04x] Comm\n", (UINT16)service);
341 case 0x0000: /* get version */
342 TRACE("returning version\n");
343 AX_reg(context) = VXD_WinVersion();
344 RESET_CFLAG(context);
347 case 0x0001: /* set port global */
348 case 0x0002: /* get focus */
349 case 0x0003: /* virtualise port */
351 VXD_BARF( context, "comm" );
355 /***********************************************************************
356 * VXD_Timer (WPROCS.405)
358 void WINAPI VXD_Timer( CONTEXT86 *context )
360 unsigned service = AX_reg(context);
362 TRACE("[%04x] Virtual Timer\n", (UINT16)service);
366 case 0x0000: /* version */
367 AX_reg(context) = VXD_WinVersion();
368 RESET_CFLAG(context);
371 case 0x0100: /* clock tick time, in 840nsecs */
372 context->Eax = GetTickCount();
374 context->Edx = context->Eax >> 22;
375 context->Eax <<= 10; /* not very precise */
378 case 0x0101: /* current Windows time, msecs */
379 case 0x0102: /* current VM time, msecs */
380 context->Eax = GetTickCount();
384 VXD_BARF( context, "VTD" );
388 /***********************************************************************
389 * VXD_TimerAPI (WPROCS.1490)
391 static DWORD System_Time = 0;
392 static WORD System_Time_Selector = 0;
393 static void System_Time_Tick( WORD timer ) { System_Time += 55; }
394 void WINAPI VXD_TimerAPI ( CONTEXT86 *context )
396 unsigned service = AX_reg(context);
398 TRACE("[%04x] TimerAPI\n", (UINT16)service);
402 case 0x0000: /* version */
403 AX_reg(context) = VXD_WinVersion();
404 RESET_CFLAG(context);
407 case 0x0009: /* get system time selector */
408 if ( !System_Time_Selector )
410 System_Time_Selector = SELECTOR_AllocBlock( &System_Time, sizeof(DWORD), WINE_LDT_FLAGS_DATA );
411 CreateSystemTimer( 55, System_Time_Tick );
414 AX_reg(context) = System_Time_Selector;
415 RESET_CFLAG(context);
419 VXD_BARF( context, "VTDAPI" );
423 /***********************************************************************
424 * VXD_ConfigMG (WPROCS.451)
426 void WINAPI VXD_ConfigMG ( CONTEXT86 *context )
428 unsigned service = AX_reg(context);
430 TRACE("[%04x] ConfigMG\n", (UINT16)service);
434 case 0x0000: /* version */
435 AX_reg(context) = VXD_WinVersion();
436 RESET_CFLAG(context);
440 VXD_BARF( context, "CONFIGMG" );
444 /***********************************************************************
445 * VXD_Enable (WPROCS.455)
447 void WINAPI VXD_Enable ( CONTEXT86 *context )
449 unsigned service = AX_reg(context);
451 TRACE("[%04x] Enable\n", (UINT16)service);
455 case 0x0000: /* version */
456 AX_reg(context) = VXD_WinVersion();
457 RESET_CFLAG(context);
461 VXD_BARF( context, "ENABLE" );
465 /***********************************************************************
466 * VXD_APM (WPROCS.438)
468 void WINAPI VXD_APM ( CONTEXT86 *context )
470 unsigned service = AX_reg(context);
472 TRACE("[%04x] APM\n", (UINT16)service);
476 case 0x0000: /* version */
477 AX_reg(context) = VXD_WinVersion();
478 RESET_CFLAG(context);
482 VXD_BARF( context, "APM" );
486 /***********************************************************************
487 * VXD_Win32s (WPROCS.445)
489 * This is an implementation of the services of the Win32s VxD.
490 * Since official documentation of these does not seem to be available,
491 * certain arguments of some of the services remain unclear.
493 * FIXME: The following services are currently unimplemented:
494 * Exception handling (0x01, 0x1C)
495 * Debugger support (0x0C, 0x14, 0x17)
496 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
497 * Memory Statistics (0x1B)
500 * We have a specific problem running Win32s on Linux (and probably also
501 * the other x86 unixes), since Win32s tries to allocate its main 'flat
502 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
503 * The rationale for this seems to be that they want one the one hand to
504 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
505 * at linear address 0, but want at other hand to have offset 0 of the
506 * flat data/code segment point to an unmapped page (to catch NULL pointer
507 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
508 * so that the Win 3.1 memory area at linear address zero shows up in the
509 * flat segments at offset 0x10000 (since linear addresses wrap around at
510 * 4GB). To compensate for that discrepancy between flat segment offsets
511 * and plain linear addresses, all flat pointers passed between the 32-bit
512 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
513 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
515 * The problem for us is now that Linux does not allow a LDT selector with
516 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
517 * address space. To address this problem we introduce *another* offset:
518 * We add 0x10000 to every linear address we get as an argument from Win32s.
519 * This means especially that the flat code/data selectors get actually
520 * allocated with base 0x0, so that flat offsets and (real) linear addresses
521 * do again agree! In fact, every call e.g. of a Win32s VxD service now
522 * has all pointer arguments (which are offsets in the flat data segement)
523 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
524 * increased by 0x10000 by *our* code.
526 * Note that to keep everything consistent, this offset has to be applied by
527 * every Wine function that operates on 'linear addresses' passed to it by
528 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
529 * API routines, this affects only two locations: this VxD and the DPMI
530 * handler. (NOTE: Should any Win32s application pass a linear address to
531 * any routine apart from those, e.g. some other VxD handler, that code
532 * would have to take the offset into account as well!)
534 * The offset is set the first time any application calls the GetVersion()
535 * service of the Win32s VxD. (Note that the offset is never reset.)
539 void WINAPI VXD_Win32s( CONTEXT86 *context )
541 switch (AX_reg(context))
543 case 0x0000: /* Get Version */
547 * Output: EAX: LoWord: Win32s Version (1.30)
548 * HiWord: VxD Version (200)
554 * EDX: Debugging Flags
558 * 1 if VMCPD VxD not found
561 TRACE("GetVersion()\n");
563 context->Eax = VXD_WinVersion() | (200 << 16);
570 * If this is the first time we are called for this process,
571 * hack the memory image of WIN32S16 so that it doesn't try
572 * to access the GDT directly ...
574 * The first code segment of WIN32S16 (version 1.30) contains
575 * an unexported function somewhere between the exported functions
576 * SetFS and StackLinearToSegmented that tries to find a selector
577 * in the LDT that maps to the memory image of the LDT itself.
578 * If it succeeds, it stores this selector into a global variable
579 * which will be used to speed up execution by using this selector
580 * to modify the LDT directly instead of using the DPMI calls.
582 * To perform this search of the LDT, this function uses the
583 * sgdt and sldt instructions to find the linear address of
584 * the (GDT and then) LDT. While those instructions themselves
585 * execute without problem, the linear address that sgdt returns
586 * points (at least under Linux) to the kernel address space, so
587 * that any subsequent access leads to a segfault.
589 * Fortunately, WIN32S16 still contains as a fallback option the
590 * mechanism of using DPMI calls to modify LDT selectors instead
591 * of direct writes to the LDT. Thus we can circumvent the problem
592 * by simply replacing the first byte of the offending function
593 * with an 'retf' instruction. This means that the global variable
594 * supposed to contain the LDT alias selector will remain zero,
595 * and hence WIN32S16 will fall back to using DPMI calls.
597 * The heuristic we employ to _find_ that function is as follows:
598 * We search between the addresses of the exported symbols SetFS
599 * and StackLinearToSegmented for the byte sequence '0F 01 04'
600 * (this is the opcode of 'sgdt [si]'). We then search backwards
601 * from this address for the last occurrence of 'CB' (retf) that marks
602 * the end of the preceeding function. The following byte (which
603 * should now be the first byte of the function we are looking for)
604 * will be replaced by 'CB' (retf).
606 * This heuristic works for the retail as well as the debug version
607 * of Win32s version 1.30. For versions earlier than that this
608 * hack should not be necessary at all, since the whole mechanism
609 * ('PERF130') was introduced only in 1.30 to improve the overall
610 * performance of Win32s.
615 HMODULE16 hModule = GetModuleHandle16("win32s16");
616 SEGPTR func1 = (SEGPTR)GetProcAddress16(hModule, "SetFS");
617 SEGPTR func2 = (SEGPTR)GetProcAddress16(hModule, "StackLinearToSegmented");
619 if ( hModule && func1 && func2
620 && SELECTOROF(func1) == SELECTOROF(func2))
622 BYTE *start = MapSL(func1);
623 BYTE *end = MapSL(func2);
624 BYTE *p, *retv = NULL;
627 for (p = start; p < end; p++)
628 if (*p == 0xCB) found = 0, retv = p;
629 else if (*p == 0x0F) found = 1;
630 else if (*p == 0x01 && found == 1) found = 2;
631 else if (*p == 0x04 && found == 2) { found = 3; break; }
634 if (found == 3 && retv)
636 TRACE("PERF130 hack: "
637 "Replacing byte %02X at offset %04X:%04X\n",
638 *(retv+1), SELECTOROF(func1),
639 OFFSETOF(func1) + retv+1-start);
641 *(retv+1) = (BYTE)0xCB;
647 * Mark process as Win32s, so that subsequent DPMI calls
648 * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
650 W32S_offset = 0x10000;
654 case 0x0001: /* Install Exception Handling */
656 * Input: EBX: Flat address of W32SKRNL Exception Data
658 * ECX: LoWord: Flat Code Selector
659 * HiWord: Flat Data Selector
661 * EDX: Flat address of W32SKRNL Exception Handler
662 * (this is equal to W32S_BackTo32 + 0x40)
664 * ESI: SEGPTR KERNEL.HASGPHANDLER
666 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
668 * Output: EAX: 0 if OK
671 TRACE("[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
672 context->Ebx, context->Ecx, context->Edx,
673 context->Esi, context->Edi);
681 case 0x0002: /* Set Page Access Flags */
683 * Input: EBX: New access flags
684 * Bit 2: User Page if set, Supervisor Page if clear
685 * Bit 1: Read-Write if set, Read-Only if clear
687 * ECX: Size of memory area to change
689 * EDX: Flat start address of memory area
691 * Output: EAX: Size of area changed
694 TRACE("[0002] EBX=%lx ECX=%lx EDX=%lx\n",
695 context->Ebx, context->Ecx, context->Edx);
699 context->Eax = context->Ecx;
703 case 0x0003: /* Get Page Access Flags */
705 * Input: EDX: Flat address of page to query
707 * Output: EAX: Page access flags
708 * Bit 2: User Page if set, Supervisor Page if clear
709 * Bit 1: Read-Write if set, Read-Only if clear
712 TRACE("[0003] EDX=%lx\n", context->Edx);
720 case 0x0004: /* Map Module */
722 * Input: ECX: IMTE (offset in Module Table) of new module
724 * EDX: Flat address of Win32s Module Table
726 * Output: EAX: 0 if OK
729 if (!context->Edx || CX_reg(context) == 0xFFFF)
731 TRACE("MapModule: Initialization call\n");
737 * Structure of a Win32s Module Table Entry:
752 * Note: This function should set up a demand-paged memory image
753 * of the given module. Since mmap does not allow file offsets
754 * not aligned at 1024 bytes, we simply load the image fully
758 struct Win32sModule *moduleTable =
759 (struct Win32sModule *)W32S_APP2WINE(context->Edx);
760 struct Win32sModule *module = moduleTable + context->Ecx;
762 IMAGE_NT_HEADERS *nt_header = RtlImageNtHeader( (HMODULE)module->baseAddr );
763 IMAGE_SECTION_HEADER *pe_seg = (IMAGE_SECTION_HEADER*)((char *)&nt_header->OptionalHeader +
764 nt_header->FileHeader.SizeOfOptionalHeader);
767 HFILE image = _lopen(module->pathName, OF_READ);
768 BOOL error = (image == HFILE_ERROR);
771 TRACE("MapModule: Loading %s\n", module->pathName);
774 !error && i < nt_header->FileHeader.NumberOfSections;
776 if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
778 DWORD off = pe_seg->PointerToRawData;
779 DWORD len = pe_seg->SizeOfRawData;
780 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
783 "Section %d at %08lx from %08lx len %08lx\n",
784 i, (DWORD)addr, off, len);
786 if ( _llseek(image, off, SEEK_SET) != off
787 || _lread(image, addr, len) != len)
794 ERR("MapModule: Unable to load %s\n", module->pathName);
796 else if (module->relocDelta != 0)
798 IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
799 + IMAGE_DIRECTORY_ENTRY_BASERELOC;
800 IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
801 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
803 TRACE("MapModule: Reloc delta %08lx\n", module->relocDelta);
805 while (r && r->VirtualAddress)
807 LPBYTE page = module->baseAddr + r->VirtualAddress;
808 WORD *TypeOffset = (WORD *)(r + 1);
809 int count = (r->SizeOfBlock - sizeof(*r)) / sizeof(*TypeOffset);
811 TRACE("MapModule: %d relocations for page %08lx\n",
814 for(i = 0; i < count; i++)
816 int offset = TypeOffset[i] & 0xFFF;
817 int type = TypeOffset[i] >> 12;
820 case IMAGE_REL_BASED_ABSOLUTE:
822 case IMAGE_REL_BASED_HIGH:
823 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
825 case IMAGE_REL_BASED_LOW:
826 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
828 case IMAGE_REL_BASED_HIGHLOW:
829 *(DWORD*)(page+offset) += module->relocDelta;
832 WARN("MapModule: Unsupported fixup type\n");
837 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
842 RESET_CFLAG(context);
847 case 0x0005: /* UnMap Module */
849 * Input: EDX: Flat address of module image
851 * Output: EAX: 1 if OK
854 TRACE("UnMapModule: %lx\n", (DWORD)W32S_APP2WINE(context->Edx));
856 /* As we didn't map anything, there's nothing to unmap ... */
862 case 0x0006: /* VirtualAlloc */
864 * Input: ECX: Current Process
866 * EDX: Flat address of arguments on stack
868 * DWORD *retv [out] Flat base address of allocated region
869 * LPVOID base [in] Flat address of region to reserve/commit
870 * DWORD size [in] Size of region
871 * DWORD type [in] Type of allocation
872 * DWORD prot [in] Type of access protection
874 * Output: EAX: NtStatus
877 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
878 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
879 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
880 DWORD size = stack[2];
881 DWORD type = stack[3];
882 DWORD prot = stack[4];
885 TRACE("VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
886 (DWORD)retv, (DWORD)base, size, type, prot);
888 if (type & 0x80000000)
890 WARN("VirtualAlloc: strange type %lx\n", type);
894 if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
896 WARN("VirtualAlloc: NLS hack, allowing write access!\n");
897 prot = PAGE_READWRITE;
900 result = (DWORD)VirtualAlloc(base, size, type, prot);
902 if (W32S_WINE2APP(result))
903 *retv = W32S_WINE2APP(result),
904 context->Eax = STATUS_SUCCESS;
907 context->Eax = STATUS_NO_MEMORY; /* FIXME */
912 case 0x0007: /* VirtualFree */
914 * Input: ECX: Current Process
916 * EDX: Flat address of arguments on stack
918 * DWORD *retv [out] TRUE if success, FALSE if failure
919 * LPVOID base [in] Flat address of region
920 * DWORD size [in] Size of region
921 * DWORD type [in] Type of operation
923 * Output: EAX: NtStatus
926 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
927 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
928 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
929 DWORD size = stack[2];
930 DWORD type = stack[3];
933 TRACE("VirtualFree(%lx, %lx, %lx, %lx)\n",
934 (DWORD)retv, (DWORD)base, size, type);
936 result = VirtualFree(base, size, type);
940 context->Eax = STATUS_SUCCESS;
943 context->Eax = STATUS_NO_MEMORY; /* FIXME */
948 case 0x0008: /* VirtualProtect */
950 * Input: ECX: Current Process
952 * EDX: Flat address of arguments on stack
954 * DWORD *retv [out] TRUE if success, FALSE if failure
955 * LPVOID base [in] Flat address of region
956 * DWORD size [in] Size of region
957 * DWORD new_prot [in] Desired access protection
958 * DWORD *old_prot [out] Previous access protection
960 * Output: EAX: NtStatus
963 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
964 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
965 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
966 DWORD size = stack[2];
967 DWORD new_prot = stack[3];
968 DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4]);
971 TRACE("VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
972 (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
974 result = VirtualProtect(base, size, new_prot, old_prot);
978 context->Eax = STATUS_SUCCESS;
981 context->Eax = STATUS_NO_MEMORY; /* FIXME */
986 case 0x0009: /* VirtualQuery */
988 * Input: ECX: Current Process
990 * EDX: Flat address of arguments on stack
992 * DWORD *retv [out] Nr. bytes returned
993 * LPVOID base [in] Flat address of region
994 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
995 * DWORD len [in] Size of buffer
997 * Output: EAX: NtStatus
1000 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1001 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1002 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1003 LPMEMORY_BASIC_INFORMATION info =
1004 (LPMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2]);
1005 DWORD len = stack[3];
1008 TRACE("VirtualQuery(%lx, %lx, %lx, %lx)\n",
1009 (DWORD)retv, (DWORD)base, (DWORD)info, len);
1011 result = VirtualQuery(base, info, len);
1014 context->Eax = STATUS_SUCCESS;
1019 case 0x000A: /* SetVirtMemProcess */
1021 * Input: ECX: Process Handle
1023 * EDX: Flat address of region
1025 * Output: EAX: NtStatus
1028 TRACE("[000a] ECX=%lx EDX=%lx\n",
1029 context->Ecx, context->Edx);
1033 context->Eax = STATUS_SUCCESS;
1037 case 0x000B: /* ??? some kind of cleanup */
1039 * Input: ECX: Process Handle
1041 * Output: EAX: NtStatus
1044 TRACE("[000b] ECX=%lx\n", context->Ecx);
1048 context->Eax = STATUS_SUCCESS;
1052 case 0x000C: /* Set Debug Flags */
1054 * Input: EDX: Debug Flags
1056 * Output: EDX: Previous Debug Flags
1059 FIXME("[000c] EDX=%lx\n", context->Edx);
1067 case 0x000D: /* NtCreateSection */
1069 * Input: EDX: Flat address of arguments on stack
1071 * HANDLE32 *retv [out] Handle of Section created
1072 * DWORD flags1 [in] (?? unknown ??)
1073 * DWORD atom [in] Name of Section to create
1074 * LARGE_INTEGER *size [in] Size of Section
1075 * DWORD protect [in] Access protection
1076 * DWORD flags2 [in] (?? unknown ??)
1077 * HFILE32 hFile [in] Handle of file to map
1078 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
1080 * Output: EAX: NtStatus
1083 DWORD *stack = (DWORD *) W32S_APP2WINE(context->Edx);
1084 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1085 DWORD flags1 = stack[1];
1086 DWORD atom = stack[2];
1087 LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3]);
1088 DWORD protect = stack[4];
1089 DWORD flags2 = stack[5];
1090 HANDLE hFile = DosFileHandleToWin32Handle(stack[6]);
1091 DWORD psp = stack[7];
1093 HANDLE result = INVALID_HANDLE_VALUE;
1096 TRACE("NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1097 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1100 if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1102 TRACE("NtCreateSection: name=%s\n", atom? name : NULL);
1104 result = CreateFileMappingA(hFile, NULL, protect,
1105 size? size->s.HighPart : 0,
1106 size? size->s.LowPart : 0,
1110 if (result == INVALID_HANDLE_VALUE)
1111 WARN("NtCreateSection: failed!\n");
1113 TRACE("NtCreateSection: returned %lx\n", (DWORD)result);
1115 if (result != INVALID_HANDLE_VALUE)
1117 context->Eax = STATUS_SUCCESS;
1120 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1125 case 0x000E: /* NtOpenSection */
1127 * Input: EDX: Flat address of arguments on stack
1129 * HANDLE32 *retv [out] Handle of Section opened
1130 * DWORD protect [in] Access protection
1131 * DWORD atom [in] Name of Section to create
1133 * Output: EAX: NtStatus
1136 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1137 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1138 DWORD protect = stack[1];
1139 DWORD atom = stack[2];
1141 HANDLE result = INVALID_HANDLE_VALUE;
1144 TRACE("NtOpenSection(%lx, %lx, %lx)\n",
1145 (DWORD)retv, protect, atom);
1147 if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1149 TRACE("NtOpenSection: name=%s\n", name);
1151 result = OpenFileMappingA(protect, FALSE, name);
1154 if (result == INVALID_HANDLE_VALUE)
1155 WARN("NtOpenSection: failed!\n");
1157 TRACE("NtOpenSection: returned %lx\n", (DWORD)result);
1159 if (result != INVALID_HANDLE_VALUE)
1161 context->Eax = STATUS_SUCCESS;
1164 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1169 case 0x000F: /* NtCloseSection */
1171 * Input: EDX: Flat address of arguments on stack
1173 * HANDLE32 handle [in] Handle of Section to close
1174 * DWORD *id [out] Unique ID (?? unclear ??)
1176 * Output: EAX: NtStatus
1179 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1180 HANDLE handle = (HANDLE)stack[0];
1181 DWORD *id = (DWORD *)W32S_APP2WINE(stack[1]);
1183 TRACE("NtCloseSection(%lx, %lx)\n", (DWORD)handle, (DWORD)id);
1185 CloseHandle(handle);
1186 if (id) *id = 0; /* FIXME */
1188 context->Eax = STATUS_SUCCESS;
1193 case 0x0010: /* NtDupSection */
1195 * Input: EDX: Flat address of arguments on stack
1197 * HANDLE32 handle [in] Handle of Section to duplicate
1199 * Output: EAX: NtStatus
1202 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1203 HANDLE handle = (HANDLE)stack[0];
1206 TRACE("NtDupSection(%lx)\n", (DWORD)handle);
1208 DuplicateHandle( GetCurrentProcess(), handle,
1209 GetCurrentProcess(), &new_handle,
1210 0, FALSE, DUPLICATE_SAME_ACCESS );
1211 context->Eax = STATUS_SUCCESS;
1216 case 0x0011: /* NtMapViewOfSection */
1218 * Input: EDX: Flat address of arguments on stack
1220 * HANDLE32 SectionHandle [in] Section to be mapped
1221 * DWORD ProcessHandle [in] Process to be mapped into
1222 * DWORD * BaseAddress [in/out] Address to be mapped at
1223 * DWORD ZeroBits [in] (?? unclear ??)
1224 * DWORD CommitSize [in] (?? unclear ??)
1225 * LARGE_INTEGER *SectionOffset [in] Offset within section
1226 * DWORD * ViewSize [in] Size of view
1227 * DWORD InheritDisposition [in] (?? unclear ??)
1228 * DWORD AllocationType [in] (?? unclear ??)
1229 * DWORD Protect [in] Access protection
1231 * Output: EAX: NtStatus
1234 DWORD * stack = (DWORD *)W32S_APP2WINE(context->Edx);
1235 HANDLE SectionHandle = (HANDLE)stack[0];
1236 DWORD ProcessHandle = stack[1]; /* ignored */
1237 DWORD * BaseAddress = (DWORD *)W32S_APP2WINE(stack[2]);
1238 DWORD ZeroBits = stack[3];
1239 DWORD CommitSize = stack[4];
1240 LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5]);
1241 DWORD * ViewSize = (DWORD *)W32S_APP2WINE(stack[6]);
1242 DWORD InheritDisposition = stack[7];
1243 DWORD AllocationType = stack[8];
1244 DWORD Protect = stack[9];
1246 LPBYTE address = (LPBYTE)(BaseAddress?
1247 W32S_APP2WINE(*BaseAddress) : 0);
1248 DWORD access = 0, result;
1250 switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1252 case PAGE_READONLY: access = FILE_MAP_READ; break;
1253 case PAGE_READWRITE: access = FILE_MAP_WRITE; break;
1254 case PAGE_WRITECOPY: access = FILE_MAP_COPY; break;
1256 case PAGE_EXECUTE_READ: access = FILE_MAP_READ; break;
1257 case PAGE_EXECUTE_READWRITE: access = FILE_MAP_WRITE; break;
1258 case PAGE_EXECUTE_WRITECOPY: access = FILE_MAP_COPY; break;
1261 TRACE("NtMapViewOfSection"
1262 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1263 (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1264 ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1265 InheritDisposition, AllocationType, Protect);
1266 TRACE("NtMapViewOfSection: "
1267 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1268 (DWORD)address, SectionOffset? SectionOffset->s.LowPart : 0,
1269 ViewSize? *ViewSize : 0, access);
1271 result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1272 SectionOffset? SectionOffset->s.HighPart : 0,
1273 SectionOffset? SectionOffset->s.LowPart : 0,
1274 ViewSize? *ViewSize : 0, address);
1276 TRACE("NtMapViewOfSection: result=%lx\n", result);
1278 if (W32S_WINE2APP(result))
1280 if (BaseAddress) *BaseAddress = W32S_WINE2APP(result);
1281 context->Eax = STATUS_SUCCESS;
1284 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1289 case 0x0012: /* NtUnmapViewOfSection */
1291 * Input: EDX: Flat address of arguments on stack
1293 * DWORD ProcessHandle [in] Process (defining address space)
1294 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1296 * Output: EAX: NtStatus
1299 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1300 DWORD ProcessHandle = stack[0]; /* ignored */
1301 LPBYTE BaseAddress = (LPBYTE)W32S_APP2WINE(stack[1]);
1303 TRACE("NtUnmapViewOfSection(%lx, %lx)\n",
1304 ProcessHandle, (DWORD)BaseAddress);
1306 UnmapViewOfFile(BaseAddress);
1308 context->Eax = STATUS_SUCCESS;
1313 case 0x0013: /* NtFlushVirtualMemory */
1315 * Input: EDX: Flat address of arguments on stack
1317 * DWORD ProcessHandle [in] Process (defining address space)
1318 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1319 * DWORD *ViewSize [in?] Number of bytes to be flushed
1320 * DWORD *unknown [???] (?? unknown ??)
1322 * Output: EAX: NtStatus
1325 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1326 DWORD ProcessHandle = stack[0]; /* ignored */
1327 DWORD *BaseAddress = (DWORD *)W32S_APP2WINE(stack[1]);
1328 DWORD *ViewSize = (DWORD *)W32S_APP2WINE(stack[2]);
1329 DWORD *unknown = (DWORD *)W32S_APP2WINE(stack[3]);
1331 LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress) : 0);
1332 DWORD size = ViewSize? *ViewSize : 0;
1334 TRACE("NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1335 ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1337 TRACE("NtFlushVirtualMemory: base=%lx, size=%lx\n",
1338 (DWORD)address, size);
1340 FlushViewOfFile(address, size);
1342 context->Eax = STATUS_SUCCESS;
1347 case 0x0014: /* Get/Set Debug Registers */
1349 * Input: ECX: 0 if Get, 1 if Set
1351 * EDX: Get: Flat address of buffer to receive values of
1352 * debug registers DR0 .. DR7
1353 * Set: Flat address of buffer containing values of
1354 * debug registers DR0 .. DR7 to be set
1358 FIXME("[0014] ECX=%lx EDX=%lx\n",
1359 context->Ecx, context->Edx);
1365 case 0x0015: /* Set Coprocessor Emulation Flag */
1367 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1372 TRACE("[0015] EDX=%lx\n", context->Edx);
1374 /* We don't care, as we always have a coprocessor anyway */
1378 case 0x0016: /* Init Win32S VxD PSP */
1380 * If called to query required PSP size:
1383 * Output: EDX: Required size of Win32s VxD PSP
1385 * If called to initialize allocated PSP:
1387 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1388 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1392 if (context->Ebx == 0)
1393 context->Edx = 0x80;
1396 PDB16 *psp = MapSL( MAKESEGPTR( BX_reg(context), 0 ));
1398 psp->fileHandlesPtr = MAKELONG(HIWORD(context->Ebx), 0x5c);
1399 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1404 case 0x0017: /* Set Break Point */
1406 * Input: EBX: Offset of Break Point
1407 * CX: Selector of Break Point
1412 FIXME("[0017] EBX=%lx CX=%x\n",
1413 context->Ebx, CX_reg(context));
1419 case 0x0018: /* VirtualLock */
1421 * Input: ECX: Current Process
1423 * EDX: Flat address of arguments on stack
1425 * DWORD *retv [out] TRUE if success, FALSE if failure
1426 * LPVOID base [in] Flat address of range to lock
1427 * DWORD size [in] Size of range
1429 * Output: EAX: NtStatus
1432 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1433 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1434 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1435 DWORD size = stack[2];
1438 TRACE("VirtualLock(%lx, %lx, %lx)\n",
1439 (DWORD)retv, (DWORD)base, size);
1441 result = VirtualLock(base, size);
1445 context->Eax = STATUS_SUCCESS;
1448 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1453 case 0x0019: /* VirtualUnlock */
1455 * Input: ECX: Current Process
1457 * EDX: Flat address of arguments on stack
1459 * DWORD *retv [out] TRUE if success, FALSE if failure
1460 * LPVOID base [in] Flat address of range to unlock
1461 * DWORD size [in] Size of range
1463 * Output: EAX: NtStatus
1466 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1467 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1468 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1469 DWORD size = stack[2];
1472 TRACE("VirtualUnlock(%lx, %lx, %lx)\n",
1473 (DWORD)retv, (DWORD)base, size);
1475 result = VirtualUnlock(base, size);
1479 context->Eax = STATUS_SUCCESS;
1482 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1487 case 0x001A: /* KGetSystemInfo */
1491 * Output: ECX: Start of sparse memory arena
1492 * EDX: End of sparse memory arena
1495 TRACE("KGetSystemInfo()\n");
1498 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1499 * sparse memory arena. We do it the other way around, since
1500 * we have to reserve 3GB - 4GB for Linux, and thus use
1501 * 0GB - 3GB as sparse memory arena.
1503 * FIXME: What about other OSes ?
1506 context->Ecx = W32S_WINE2APP(0x00000000);
1507 context->Edx = W32S_WINE2APP(0xbfffffff);
1511 case 0x001B: /* KGlobalMemStat */
1513 * Input: ESI: Flat address of buffer to receive memory info
1518 struct Win32sMemoryInfo
1520 DWORD DIPhys_Count; /* Total physical pages */
1521 DWORD DIFree_Count; /* Free physical pages */
1522 DWORD DILin_Total_Count; /* Total virtual pages (private arena) */
1523 DWORD DILin_Total_Free; /* Free virtual pages (private arena) */
1525 DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */
1526 DWORD SparseFree; /* Free size of sparse arena (bytes ?) */
1529 struct Win32sMemoryInfo *info =
1530 (struct Win32sMemoryInfo *)W32S_APP2WINE(context->Esi);
1532 FIXME("KGlobalMemStat(%lx)\n", (DWORD)info);
1539 case 0x001C: /* Enable/Disable Exceptions */
1541 * Input: ECX: 0 to disable, 1 to enable exception handling
1546 TRACE("[001c] ECX=%lx\n", context->Ecx);
1552 case 0x001D: /* VirtualAlloc called from 16-bit code */
1554 * Input: EDX: Segmented address of arguments on stack
1556 * LPVOID base [in] Flat address of region to reserve/commit
1557 * DWORD size [in] Size of region
1558 * DWORD type [in] Type of allocation
1559 * DWORD prot [in] Type of access protection
1561 * Output: EAX: NtStatus
1562 * EDX: Flat base address of allocated region
1565 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1566 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1567 DWORD size = stack[1];
1568 DWORD type = stack[2];
1569 DWORD prot = stack[3];
1572 TRACE("VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1573 (DWORD)base, size, type, prot);
1575 if (type & 0x80000000)
1577 WARN("VirtualAlloc16: strange type %lx\n", type);
1581 result = (DWORD)VirtualAlloc(base, size, type, prot);
1583 if (W32S_WINE2APP(result))
1584 context->Edx = W32S_WINE2APP(result),
1585 context->Eax = STATUS_SUCCESS;
1588 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1589 TRACE("VirtualAlloc16: returning base %lx\n", context->Edx);
1594 case 0x001E: /* VirtualFree called from 16-bit code */
1596 * Input: EDX: Segmented address of arguments on stack
1598 * LPVOID base [in] Flat address of region
1599 * DWORD size [in] Size of region
1600 * DWORD type [in] Type of operation
1602 * Output: EAX: NtStatus
1603 * EDX: TRUE if success, FALSE if failure
1606 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1607 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1608 DWORD size = stack[1];
1609 DWORD type = stack[2];
1612 TRACE("VirtualFree16(%lx, %lx, %lx)\n",
1613 (DWORD)base, size, type);
1615 result = VirtualFree(base, size, type);
1618 context->Edx = TRUE,
1619 context->Eax = STATUS_SUCCESS;
1621 context->Edx = FALSE,
1622 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1627 case 0x001F: /* FWorkingSetSize */
1629 * Input: EDX: 0 if Get, 1 if Set
1631 * ECX: Get: Buffer to receive Working Set Size
1632 * Set: Buffer containing Working Set Size
1637 DWORD *ptr = (DWORD *)W32S_APP2WINE(context->Ecx);
1638 BOOL set = context->Edx;
1640 TRACE("FWorkingSetSize(%lx, %lx)\n", (DWORD)ptr, (DWORD)set);
1643 /* We do it differently ... */;
1647 context->Eax = STATUS_SUCCESS;
1653 VXD_BARF( context, "W32S" );