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
22 #include "wine/port.h"
27 #include <sys/types.h>
36 #include "wine/winbase16.h"
37 #include "wine/winuser16.h"
41 #include "selectors.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(vxd);
49 #define VXD_BARF(context,name) \
50 DPRINTF( "vxd %s: unknown/not implemented parameters:\n" \
51 "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
52 "SI %04x, DI %04x, DS %04x, ES %04x\n", \
53 (name), (name), AX_reg(context), BX_reg(context), \
54 CX_reg(context), DX_reg(context), SI_reg(context), \
55 DI_reg(context), (WORD)context->SegDs, (WORD)context->SegEs )
59 static WORD VXD_WinVersion(void)
61 WORD version = LOWORD(GetVersion16());
62 return (version >> 8) | (version << 8);
65 /***********************************************************************
66 * VXD_VMM (WPROCS.401)
68 void WINAPI VXD_VMM ( CONTEXT86 *context )
70 unsigned service = AX_reg(context);
72 TRACE("[%04x] VMM\n", (UINT16)service);
76 case 0x0000: /* version */
77 SET_AX( context, VXD_WinVersion() );
81 case 0x026d: /* Get_Debug_Flag '/m' */
82 case 0x026e: /* Get_Debug_Flag '/n' */
88 VXD_BARF( context, "VMM" );
92 /***********************************************************************
93 * VXD_PageFile (WPROCS.433)
95 void WINAPI VXD_PageFile( CONTEXT86 *context )
97 unsigned service = AX_reg(context);
99 /* taken from Ralf Brown's Interrupt List */
101 TRACE("[%04x] PageFile\n", (UINT16)service );
105 case 0x00: /* get version, is this windows version? */
106 TRACE("returning version\n");
107 SET_AX( context, VXD_WinVersion() );
108 RESET_CFLAG(context);
111 case 0x01: /* get swap file info */
112 TRACE("VxD PageFile: returning swap file info\n");
113 SET_AX( context, 0x00 ); /* paging disabled */
114 context->Ecx = 0; /* maximum size of paging file */
115 /* FIXME: do I touch DS:SI or DS:DI? */
116 RESET_CFLAG(context);
119 case 0x02: /* delete permanent swap on exit */
120 TRACE("VxD PageFile: supposed to delete swap\n");
121 RESET_CFLAG(context);
124 case 0x03: /* current temporary swap file size */
125 TRACE("VxD PageFile: what is current temp. swap size\n");
126 RESET_CFLAG(context);
129 case 0x04: /* read or write?? INTERRUP.D */
130 case 0x05: /* cancel?? INTERRUP.D */
131 case 0x06: /* test I/O valid INTERRUP.D */
133 VXD_BARF( context, "pagefile" );
138 /***********************************************************************
139 * VXD_Reboot (WPROCS.409)
141 void WINAPI VXD_Reboot ( CONTEXT86 *context )
143 unsigned service = AX_reg(context);
145 TRACE("[%04x] Reboot\n", (UINT16)service);
149 case 0x0000: /* version */
150 SET_AX( context, VXD_WinVersion() );
151 RESET_CFLAG(context);
155 VXD_BARF( context, "REBOOT" );
159 /***********************************************************************
160 * VXD_VDD (WPROCS.410)
162 void WINAPI VXD_VDD ( CONTEXT86 *context )
164 unsigned service = AX_reg(context);
166 TRACE("[%04x] VDD\n", (UINT16)service);
170 case 0x0000: /* version */
171 SET_AX( context, VXD_WinVersion() );
172 RESET_CFLAG(context);
176 VXD_BARF( context, "VDD" );
180 /***********************************************************************
181 * VXD_VMD (WPROCS.412)
183 void WINAPI VXD_VMD ( CONTEXT86 *context )
185 unsigned service = AX_reg(context);
187 TRACE("[%04x] VMD\n", (UINT16)service);
191 case 0x0000: /* version */
192 SET_AX( context, VXD_WinVersion() );
193 RESET_CFLAG(context);
197 VXD_BARF( context, "VMD" );
201 /***********************************************************************
202 * VXD_VXDLoader (WPROCS.439)
204 void WINAPI VXD_VXDLoader( CONTEXT86 *context )
206 unsigned service = AX_reg(context);
208 TRACE("[%04x] VXDLoader\n", (UINT16)service);
212 case 0x0000: /* get version */
213 TRACE("returning version\n");
214 SET_AX( context, 0x0000 );
215 SET_DX( context, VXD_WinVersion() );
216 RESET_CFLAG(context);
219 case 0x0001: /* load device */
220 FIXME("load device %04lx:%04x (%s)\n",
221 context->SegDs, DX_reg(context),
222 debugstr_a(MapSL(MAKESEGPTR(context->SegDs, DX_reg(context)))));
223 SET_AX( context, 0x0000 );
224 context->SegEs = 0x0000;
225 SET_DI( context, 0x0000 );
226 RESET_CFLAG(context);
229 case 0x0002: /* unload device */
230 FIXME("unload device (%08lx)\n", context->Ebx);
231 SET_AX( context, 0x0000 );
232 RESET_CFLAG(context);
236 VXD_BARF( context, "VXDLDR" );
237 SET_AX( context, 0x000B ); /* invalid function number */
243 /***********************************************************************
244 * VXD_Shell (WPROCS.423)
246 void WINAPI VXD_Shell( CONTEXT86 *context )
248 unsigned service = DX_reg(context);
250 TRACE("[%04x] Shell\n", (UINT16)service);
252 switch (service) /* Ralf Brown says EDX, but I use DX instead */
255 TRACE("returning version\n");
256 SET_AX( context, VXD_WinVersion() );
257 context->Ebx = 1; /* system VM Handle */
263 /* SHELL_SYSMODAL_Message
264 ebx virtual maschine handle
265 eax message box flags
266 ecx address of message
267 edi address of caption
268 return response in eax
272 ebx virtual maschine handle
273 eax message box flags
274 ecx address of message
275 edi address of caption
277 edx reference data for callback
278 return response in eax
281 VXD_BARF( context, "shell" );
284 case 0x0006: /* SHELL_Get_VM_State */
285 TRACE("VxD Shell: returning VM state\n");
286 /* Actually we don't, not yet. We have to return a structure
287 * and I am not to sure how to set it up and return it yet,
288 * so for now let's do nothing. I can (hopefully) get this
289 * by the next release
291 /* RESET_CFLAG(context); */
310 VXD_BARF( context, "SHELL" );
313 /* the new Win95 shell API */
314 case 0x0100: /* get version */
315 SET_AX( context, VXD_WinVersion() );
318 case 0x0104: /* retrieve Hook_Properties list */
319 case 0x0105: /* call Hook_Properties callbacks */
320 VXD_BARF( context, "SHELL" );
323 case 0x0106: /* install timeout callback */
324 TRACE("VxD Shell: ignoring shell callback (%ld sec.)\n", context->Ebx);
328 case 0x0107: /* get version of any VxD */
330 VXD_BARF( context, "SHELL" );
336 /***********************************************************************
337 * VXD_Comm (WPROCS.414)
339 void WINAPI VXD_Comm( CONTEXT86 *context )
341 unsigned service = AX_reg(context);
343 TRACE("[%04x] Comm\n", (UINT16)service);
347 case 0x0000: /* get version */
348 TRACE("returning version\n");
349 SET_AX( context, VXD_WinVersion() );
350 RESET_CFLAG(context);
353 case 0x0001: /* set port global */
354 case 0x0002: /* get focus */
355 case 0x0003: /* virtualise port */
357 VXD_BARF( context, "comm" );
361 /***********************************************************************
362 * VXD_Timer (WPROCS.405)
364 void WINAPI VXD_Timer( CONTEXT86 *context )
366 unsigned service = AX_reg(context);
368 TRACE("[%04x] Virtual Timer\n", (UINT16)service);
372 case 0x0000: /* version */
373 SET_AX( context, VXD_WinVersion() );
374 RESET_CFLAG(context);
377 case 0x0100: /* clock tick time, in 840nsecs */
378 context->Eax = GetTickCount();
380 context->Edx = context->Eax >> 22;
381 context->Eax <<= 10; /* not very precise */
384 case 0x0101: /* current Windows time, msecs */
385 case 0x0102: /* current VM time, msecs */
386 context->Eax = GetTickCount();
390 VXD_BARF( context, "VTD" );
394 /***********************************************************************
395 * VXD_TimerAPI (WPROCS.1490)
397 static DWORD System_Time = 0;
398 static WORD System_Time_Selector = 0;
399 static void System_Time_Tick( WORD timer ) { System_Time += 55; }
400 void WINAPI VXD_TimerAPI ( CONTEXT86 *context )
402 unsigned service = AX_reg(context);
404 TRACE("[%04x] TimerAPI\n", (UINT16)service);
408 case 0x0000: /* version */
409 SET_AX( context, VXD_WinVersion() );
410 RESET_CFLAG(context);
413 case 0x0009: /* get system time selector */
414 if ( !System_Time_Selector )
416 System_Time_Selector = SELECTOR_AllocBlock( &System_Time, sizeof(DWORD), WINE_LDT_FLAGS_DATA );
417 CreateSystemTimer( 55, System_Time_Tick );
420 SET_AX( context, System_Time_Selector );
421 RESET_CFLAG(context);
425 VXD_BARF( context, "VTDAPI" );
429 /***********************************************************************
430 * VXD_ConfigMG (WPROCS.451)
432 void WINAPI VXD_ConfigMG ( CONTEXT86 *context )
434 unsigned service = AX_reg(context);
436 TRACE("[%04x] ConfigMG\n", (UINT16)service);
440 case 0x0000: /* version */
441 SET_AX( context, VXD_WinVersion() );
442 RESET_CFLAG(context);
446 VXD_BARF( context, "CONFIGMG" );
450 /***********************************************************************
451 * VXD_Enable (WPROCS.455)
453 void WINAPI VXD_Enable ( CONTEXT86 *context )
455 unsigned service = AX_reg(context);
457 TRACE("[%04x] Enable\n", (UINT16)service);
461 case 0x0000: /* version */
462 SET_AX( context, VXD_WinVersion() );
463 RESET_CFLAG(context);
467 VXD_BARF( context, "ENABLE" );
471 /***********************************************************************
472 * VXD_APM (WPROCS.438)
474 void WINAPI VXD_APM ( CONTEXT86 *context )
476 unsigned service = AX_reg(context);
478 TRACE("[%04x] APM\n", (UINT16)service);
482 case 0x0000: /* version */
483 SET_AX( context, VXD_WinVersion() );
484 RESET_CFLAG(context);
488 VXD_BARF( context, "APM" );
492 /***********************************************************************
493 * VXD_Win32s (WPROCS.445)
495 * This is an implementation of the services of the Win32s VxD.
496 * Since official documentation of these does not seem to be available,
497 * certain arguments of some of the services remain unclear.
499 * FIXME: The following services are currently unimplemented:
500 * Exception handling (0x01, 0x1C)
501 * Debugger support (0x0C, 0x14, 0x17)
502 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
503 * Memory Statistics (0x1B)
506 * We have a specific problem running Win32s on Linux (and probably also
507 * the other x86 unixes), since Win32s tries to allocate its main 'flat
508 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
509 * The rationale for this seems to be that they want one the one hand to
510 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
511 * at linear address 0, but want at other hand to have offset 0 of the
512 * flat data/code segment point to an unmapped page (to catch NULL pointer
513 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
514 * so that the Win 3.1 memory area at linear address zero shows up in the
515 * flat segments at offset 0x10000 (since linear addresses wrap around at
516 * 4GB). To compensate for that discrepancy between flat segment offsets
517 * and plain linear addresses, all flat pointers passed between the 32-bit
518 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
519 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
521 * The problem for us is now that Linux does not allow a LDT selector with
522 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
523 * address space. To address this problem we introduce *another* offset:
524 * We add 0x10000 to every linear address we get as an argument from Win32s.
525 * This means especially that the flat code/data selectors get actually
526 * allocated with base 0x0, so that flat offsets and (real) linear addresses
527 * do again agree! In fact, every call e.g. of a Win32s VxD service now
528 * has all pointer arguments (which are offsets in the flat data segement)
529 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
530 * increased by 0x10000 by *our* code.
532 * Note that to keep everything consistent, this offset has to be applied by
533 * every Wine function that operates on 'linear addresses' passed to it by
534 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
535 * API routines, this affects only two locations: this VxD and the DPMI
536 * handler. (NOTE: Should any Win32s application pass a linear address to
537 * any routine apart from those, e.g. some other VxD handler, that code
538 * would have to take the offset into account as well!)
540 * The offset is set the first time any application calls the GetVersion()
541 * service of the Win32s VxD. (Note that the offset is never reset.)
545 void WINAPI VXD_Win32s( CONTEXT86 *context )
547 switch (AX_reg(context))
549 case 0x0000: /* Get Version */
553 * Output: EAX: LoWord: Win32s Version (1.30)
554 * HiWord: VxD Version (200)
560 * EDX: Debugging Flags
564 * 1 if VMCPD VxD not found
567 TRACE("GetVersion()\n");
569 context->Eax = VXD_WinVersion() | (200 << 16);
576 * If this is the first time we are called for this process,
577 * hack the memory image of WIN32S16 so that it doesn't try
578 * to access the GDT directly ...
580 * The first code segment of WIN32S16 (version 1.30) contains
581 * an unexported function somewhere between the exported functions
582 * SetFS and StackLinearToSegmented that tries to find a selector
583 * in the LDT that maps to the memory image of the LDT itself.
584 * If it succeeds, it stores this selector into a global variable
585 * which will be used to speed up execution by using this selector
586 * to modify the LDT directly instead of using the DPMI calls.
588 * To perform this search of the LDT, this function uses the
589 * sgdt and sldt instructions to find the linear address of
590 * the (GDT and then) LDT. While those instructions themselves
591 * execute without problem, the linear address that sgdt returns
592 * points (at least under Linux) to the kernel address space, so
593 * that any subsequent access leads to a segfault.
595 * Fortunately, WIN32S16 still contains as a fallback option the
596 * mechanism of using DPMI calls to modify LDT selectors instead
597 * of direct writes to the LDT. Thus we can circumvent the problem
598 * by simply replacing the first byte of the offending function
599 * with an 'retf' instruction. This means that the global variable
600 * supposed to contain the LDT alias selector will remain zero,
601 * and hence WIN32S16 will fall back to using DPMI calls.
603 * The heuristic we employ to _find_ that function is as follows:
604 * We search between the addresses of the exported symbols SetFS
605 * and StackLinearToSegmented for the byte sequence '0F 01 04'
606 * (this is the opcode of 'sgdt [si]'). We then search backwards
607 * from this address for the last occurrence of 'CB' (retf) that marks
608 * the end of the preceeding function. The following byte (which
609 * should now be the first byte of the function we are looking for)
610 * will be replaced by 'CB' (retf).
612 * This heuristic works for the retail as well as the debug version
613 * of Win32s version 1.30. For versions earlier than that this
614 * hack should not be necessary at all, since the whole mechanism
615 * ('PERF130') was introduced only in 1.30 to improve the overall
616 * performance of Win32s.
621 HMODULE16 hModule = GetModuleHandle16("win32s16");
622 SEGPTR func1 = (SEGPTR)GetProcAddress16(hModule, "SetFS");
623 SEGPTR func2 = (SEGPTR)GetProcAddress16(hModule, "StackLinearToSegmented");
625 if ( hModule && func1 && func2
626 && SELECTOROF(func1) == SELECTOROF(func2))
628 BYTE *start = MapSL(func1);
629 BYTE *end = MapSL(func2);
630 BYTE *p, *retv = NULL;
633 for (p = start; p < end; p++)
634 if (*p == 0xCB) found = 0, retv = p;
635 else if (*p == 0x0F) found = 1;
636 else if (*p == 0x01 && found == 1) found = 2;
637 else if (*p == 0x04 && found == 2) { found = 3; break; }
640 if (found == 3 && retv)
642 TRACE("PERF130 hack: "
643 "Replacing byte %02X at offset %04X:%04X\n",
644 *(retv+1), SELECTOROF(func1),
645 OFFSETOF(func1) + retv+1-start);
647 *(retv+1) = (BYTE)0xCB;
653 * Mark process as Win32s, so that subsequent DPMI calls
654 * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
656 W32S_offset = 0x10000;
660 case 0x0001: /* Install Exception Handling */
662 * Input: EBX: Flat address of W32SKRNL Exception Data
664 * ECX: LoWord: Flat Code Selector
665 * HiWord: Flat Data Selector
667 * EDX: Flat address of W32SKRNL Exception Handler
668 * (this is equal to W32S_BackTo32 + 0x40)
670 * ESI: SEGPTR KERNEL.HASGPHANDLER
672 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
674 * Output: EAX: 0 if OK
677 TRACE("[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
678 context->Ebx, context->Ecx, context->Edx,
679 context->Esi, context->Edi);
687 case 0x0002: /* Set Page Access Flags */
689 * Input: EBX: New access flags
690 * Bit 2: User Page if set, Supervisor Page if clear
691 * Bit 1: Read-Write if set, Read-Only if clear
693 * ECX: Size of memory area to change
695 * EDX: Flat start address of memory area
697 * Output: EAX: Size of area changed
700 TRACE("[0002] EBX=%lx ECX=%lx EDX=%lx\n",
701 context->Ebx, context->Ecx, context->Edx);
705 context->Eax = context->Ecx;
709 case 0x0003: /* Get Page Access Flags */
711 * Input: EDX: Flat address of page to query
713 * Output: EAX: Page access flags
714 * Bit 2: User Page if set, Supervisor Page if clear
715 * Bit 1: Read-Write if set, Read-Only if clear
718 TRACE("[0003] EDX=%lx\n", context->Edx);
726 case 0x0004: /* Map Module */
728 * Input: ECX: IMTE (offset in Module Table) of new module
730 * EDX: Flat address of Win32s Module Table
732 * Output: EAX: 0 if OK
735 if (!context->Edx || CX_reg(context) == 0xFFFF)
737 TRACE("MapModule: Initialization call\n");
743 * Structure of a Win32s Module Table Entry:
758 * Note: This function should set up a demand-paged memory image
759 * of the given module. Since mmap does not allow file offsets
760 * not aligned at 1024 bytes, we simply load the image fully
764 struct Win32sModule *moduleTable =
765 (struct Win32sModule *)W32S_APP2WINE(context->Edx);
766 struct Win32sModule *module = moduleTable + context->Ecx;
768 IMAGE_NT_HEADERS *nt_header = RtlImageNtHeader( (HMODULE)module->baseAddr );
769 IMAGE_SECTION_HEADER *pe_seg = (IMAGE_SECTION_HEADER*)((char *)&nt_header->OptionalHeader +
770 nt_header->FileHeader.SizeOfOptionalHeader);
773 HFILE image = _lopen(module->pathName, OF_READ);
774 BOOL error = (image == HFILE_ERROR);
777 TRACE("MapModule: Loading %s\n", module->pathName);
780 !error && i < nt_header->FileHeader.NumberOfSections;
782 if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
784 DWORD off = pe_seg->PointerToRawData;
785 DWORD len = pe_seg->SizeOfRawData;
786 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
789 "Section %d at %08lx from %08lx len %08lx\n",
790 i, (DWORD)addr, off, len);
792 if ( _llseek(image, off, SEEK_SET) != off
793 || _lread(image, addr, len) != len)
800 ERR("MapModule: Unable to load %s\n", module->pathName);
802 else if (module->relocDelta != 0)
804 IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
805 + IMAGE_DIRECTORY_ENTRY_BASERELOC;
806 IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
807 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
809 TRACE("MapModule: Reloc delta %08lx\n", module->relocDelta);
811 while (r && r->VirtualAddress)
813 LPBYTE page = module->baseAddr + r->VirtualAddress;
814 WORD *TypeOffset = (WORD *)(r + 1);
815 int count = (r->SizeOfBlock - sizeof(*r)) / sizeof(*TypeOffset);
817 TRACE("MapModule: %d relocations for page %08lx\n",
820 for(i = 0; i < count; i++)
822 int offset = TypeOffset[i] & 0xFFF;
823 int type = TypeOffset[i] >> 12;
826 case IMAGE_REL_BASED_ABSOLUTE:
828 case IMAGE_REL_BASED_HIGH:
829 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
831 case IMAGE_REL_BASED_LOW:
832 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
834 case IMAGE_REL_BASED_HIGHLOW:
835 *(DWORD*)(page+offset) += module->relocDelta;
838 WARN("MapModule: Unsupported fixup type\n");
843 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
848 RESET_CFLAG(context);
853 case 0x0005: /* UnMap Module */
855 * Input: EDX: Flat address of module image
857 * Output: EAX: 1 if OK
860 TRACE("UnMapModule: %lx\n", (DWORD)W32S_APP2WINE(context->Edx));
862 /* As we didn't map anything, there's nothing to unmap ... */
868 case 0x0006: /* VirtualAlloc */
870 * Input: ECX: Current Process
872 * EDX: Flat address of arguments on stack
874 * DWORD *retv [out] Flat base address of allocated region
875 * LPVOID base [in] Flat address of region to reserve/commit
876 * DWORD size [in] Size of region
877 * DWORD type [in] Type of allocation
878 * DWORD prot [in] Type of access protection
880 * Output: EAX: NtStatus
883 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
884 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
885 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
886 DWORD size = stack[2];
887 DWORD type = stack[3];
888 DWORD prot = stack[4];
891 TRACE("VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
892 (DWORD)retv, (DWORD)base, size, type, prot);
894 if (type & 0x80000000)
896 WARN("VirtualAlloc: strange type %lx\n", type);
900 if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
902 WARN("VirtualAlloc: NLS hack, allowing write access!\n");
903 prot = PAGE_READWRITE;
906 result = (DWORD)VirtualAlloc(base, size, type, prot);
908 if (W32S_WINE2APP(result))
909 *retv = W32S_WINE2APP(result),
910 context->Eax = STATUS_SUCCESS;
913 context->Eax = STATUS_NO_MEMORY; /* FIXME */
918 case 0x0007: /* VirtualFree */
920 * Input: ECX: Current Process
922 * EDX: Flat address of arguments on stack
924 * DWORD *retv [out] TRUE if success, FALSE if failure
925 * LPVOID base [in] Flat address of region
926 * DWORD size [in] Size of region
927 * DWORD type [in] Type of operation
929 * Output: EAX: NtStatus
932 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
933 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
934 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
935 DWORD size = stack[2];
936 DWORD type = stack[3];
939 TRACE("VirtualFree(%lx, %lx, %lx, %lx)\n",
940 (DWORD)retv, (DWORD)base, size, type);
942 result = VirtualFree(base, size, type);
946 context->Eax = STATUS_SUCCESS;
949 context->Eax = STATUS_NO_MEMORY; /* FIXME */
954 case 0x0008: /* VirtualProtect */
956 * Input: ECX: Current Process
958 * EDX: Flat address of arguments on stack
960 * DWORD *retv [out] TRUE if success, FALSE if failure
961 * LPVOID base [in] Flat address of region
962 * DWORD size [in] Size of region
963 * DWORD new_prot [in] Desired access protection
964 * DWORD *old_prot [out] Previous access protection
966 * Output: EAX: NtStatus
969 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
970 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
971 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
972 DWORD size = stack[2];
973 DWORD new_prot = stack[3];
974 DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4]);
977 TRACE("VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
978 (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
980 result = VirtualProtect(base, size, new_prot, old_prot);
984 context->Eax = STATUS_SUCCESS;
987 context->Eax = STATUS_NO_MEMORY; /* FIXME */
992 case 0x0009: /* VirtualQuery */
994 * Input: ECX: Current Process
996 * EDX: Flat address of arguments on stack
998 * DWORD *retv [out] Nr. bytes returned
999 * LPVOID base [in] Flat address of region
1000 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
1001 * DWORD len [in] Size of buffer
1003 * Output: EAX: NtStatus
1006 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1007 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1008 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1009 LPMEMORY_BASIC_INFORMATION info =
1010 (LPMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2]);
1011 DWORD len = stack[3];
1014 TRACE("VirtualQuery(%lx, %lx, %lx, %lx)\n",
1015 (DWORD)retv, (DWORD)base, (DWORD)info, len);
1017 result = VirtualQuery(base, info, len);
1020 context->Eax = STATUS_SUCCESS;
1025 case 0x000A: /* SetVirtMemProcess */
1027 * Input: ECX: Process Handle
1029 * EDX: Flat address of region
1031 * Output: EAX: NtStatus
1034 TRACE("[000a] ECX=%lx EDX=%lx\n",
1035 context->Ecx, context->Edx);
1039 context->Eax = STATUS_SUCCESS;
1043 case 0x000B: /* ??? some kind of cleanup */
1045 * Input: ECX: Process Handle
1047 * Output: EAX: NtStatus
1050 TRACE("[000b] ECX=%lx\n", context->Ecx);
1054 context->Eax = STATUS_SUCCESS;
1058 case 0x000C: /* Set Debug Flags */
1060 * Input: EDX: Debug Flags
1062 * Output: EDX: Previous Debug Flags
1065 FIXME("[000c] EDX=%lx\n", context->Edx);
1073 case 0x000D: /* NtCreateSection */
1075 * Input: EDX: Flat address of arguments on stack
1077 * HANDLE32 *retv [out] Handle of Section created
1078 * DWORD flags1 [in] (?? unknown ??)
1079 * DWORD atom [in] Name of Section to create
1080 * LARGE_INTEGER *size [in] Size of Section
1081 * DWORD protect [in] Access protection
1082 * DWORD flags2 [in] (?? unknown ??)
1083 * HFILE32 hFile [in] Handle of file to map
1084 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
1086 * Output: EAX: NtStatus
1089 DWORD *stack = (DWORD *) W32S_APP2WINE(context->Edx);
1090 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1091 DWORD flags1 = stack[1];
1092 DWORD atom = stack[2];
1093 LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3]);
1094 DWORD protect = stack[4];
1095 DWORD flags2 = stack[5];
1096 HANDLE hFile = DosFileHandleToWin32Handle(stack[6]);
1097 DWORD psp = stack[7];
1099 HANDLE result = INVALID_HANDLE_VALUE;
1102 TRACE("NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1103 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1106 if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1108 TRACE("NtCreateSection: name=%s\n", atom? name : NULL);
1110 result = CreateFileMappingA(hFile, NULL, protect,
1111 size? size->s.HighPart : 0,
1112 size? size->s.LowPart : 0,
1116 if (result == INVALID_HANDLE_VALUE)
1117 WARN("NtCreateSection: failed!\n");
1119 TRACE("NtCreateSection: returned %lx\n", (DWORD)result);
1121 if (result != INVALID_HANDLE_VALUE)
1123 context->Eax = STATUS_SUCCESS;
1126 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1131 case 0x000E: /* NtOpenSection */
1133 * Input: EDX: Flat address of arguments on stack
1135 * HANDLE32 *retv [out] Handle of Section opened
1136 * DWORD protect [in] Access protection
1137 * DWORD atom [in] Name of Section to create
1139 * Output: EAX: NtStatus
1142 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1143 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0]);
1144 DWORD protect = stack[1];
1145 DWORD atom = stack[2];
1147 HANDLE result = INVALID_HANDLE_VALUE;
1150 TRACE("NtOpenSection(%lx, %lx, %lx)\n",
1151 (DWORD)retv, protect, atom);
1153 if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1155 TRACE("NtOpenSection: name=%s\n", name);
1157 result = OpenFileMappingA(protect, FALSE, name);
1160 if (result == INVALID_HANDLE_VALUE)
1161 WARN("NtOpenSection: failed!\n");
1163 TRACE("NtOpenSection: returned %lx\n", (DWORD)result);
1165 if (result != INVALID_HANDLE_VALUE)
1167 context->Eax = STATUS_SUCCESS;
1170 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1175 case 0x000F: /* NtCloseSection */
1177 * Input: EDX: Flat address of arguments on stack
1179 * HANDLE32 handle [in] Handle of Section to close
1180 * DWORD *id [out] Unique ID (?? unclear ??)
1182 * Output: EAX: NtStatus
1185 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1186 HANDLE handle = (HANDLE)stack[0];
1187 DWORD *id = (DWORD *)W32S_APP2WINE(stack[1]);
1189 TRACE("NtCloseSection(%lx, %lx)\n", (DWORD)handle, (DWORD)id);
1191 CloseHandle(handle);
1192 if (id) *id = 0; /* FIXME */
1194 context->Eax = STATUS_SUCCESS;
1199 case 0x0010: /* NtDupSection */
1201 * Input: EDX: Flat address of arguments on stack
1203 * HANDLE32 handle [in] Handle of Section to duplicate
1205 * Output: EAX: NtStatus
1208 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1209 HANDLE handle = (HANDLE)stack[0];
1212 TRACE("NtDupSection(%lx)\n", (DWORD)handle);
1214 DuplicateHandle( GetCurrentProcess(), handle,
1215 GetCurrentProcess(), &new_handle,
1216 0, FALSE, DUPLICATE_SAME_ACCESS );
1217 context->Eax = STATUS_SUCCESS;
1222 case 0x0011: /* NtMapViewOfSection */
1224 * Input: EDX: Flat address of arguments on stack
1226 * HANDLE32 SectionHandle [in] Section to be mapped
1227 * DWORD ProcessHandle [in] Process to be mapped into
1228 * DWORD * BaseAddress [in/out] Address to be mapped at
1229 * DWORD ZeroBits [in] (?? unclear ??)
1230 * DWORD CommitSize [in] (?? unclear ??)
1231 * LARGE_INTEGER *SectionOffset [in] Offset within section
1232 * DWORD * ViewSize [in] Size of view
1233 * DWORD InheritDisposition [in] (?? unclear ??)
1234 * DWORD AllocationType [in] (?? unclear ??)
1235 * DWORD Protect [in] Access protection
1237 * Output: EAX: NtStatus
1240 DWORD * stack = (DWORD *)W32S_APP2WINE(context->Edx);
1241 HANDLE SectionHandle = (HANDLE)stack[0];
1242 DWORD ProcessHandle = stack[1]; /* ignored */
1243 DWORD * BaseAddress = (DWORD *)W32S_APP2WINE(stack[2]);
1244 DWORD ZeroBits = stack[3];
1245 DWORD CommitSize = stack[4];
1246 LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5]);
1247 DWORD * ViewSize = (DWORD *)W32S_APP2WINE(stack[6]);
1248 DWORD InheritDisposition = stack[7];
1249 DWORD AllocationType = stack[8];
1250 DWORD Protect = stack[9];
1252 LPBYTE address = (LPBYTE)(BaseAddress?
1253 W32S_APP2WINE(*BaseAddress) : 0);
1254 DWORD access = 0, result;
1256 switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1258 case PAGE_READONLY: access = FILE_MAP_READ; break;
1259 case PAGE_READWRITE: access = FILE_MAP_WRITE; break;
1260 case PAGE_WRITECOPY: access = FILE_MAP_COPY; break;
1262 case PAGE_EXECUTE_READ: access = FILE_MAP_READ; break;
1263 case PAGE_EXECUTE_READWRITE: access = FILE_MAP_WRITE; break;
1264 case PAGE_EXECUTE_WRITECOPY: access = FILE_MAP_COPY; break;
1267 TRACE("NtMapViewOfSection"
1268 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1269 (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1270 ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1271 InheritDisposition, AllocationType, Protect);
1272 TRACE("NtMapViewOfSection: "
1273 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1274 (DWORD)address, SectionOffset? SectionOffset->s.LowPart : 0,
1275 ViewSize? *ViewSize : 0, access);
1277 result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1278 SectionOffset? SectionOffset->s.HighPart : 0,
1279 SectionOffset? SectionOffset->s.LowPart : 0,
1280 ViewSize? *ViewSize : 0, address);
1282 TRACE("NtMapViewOfSection: result=%lx\n", result);
1284 if (W32S_WINE2APP(result))
1286 if (BaseAddress) *BaseAddress = W32S_WINE2APP(result);
1287 context->Eax = STATUS_SUCCESS;
1290 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1295 case 0x0012: /* NtUnmapViewOfSection */
1297 * Input: EDX: Flat address of arguments on stack
1299 * DWORD ProcessHandle [in] Process (defining address space)
1300 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1302 * Output: EAX: NtStatus
1305 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1306 DWORD ProcessHandle = stack[0]; /* ignored */
1307 LPBYTE BaseAddress = (LPBYTE)W32S_APP2WINE(stack[1]);
1309 TRACE("NtUnmapViewOfSection(%lx, %lx)\n",
1310 ProcessHandle, (DWORD)BaseAddress);
1312 UnmapViewOfFile(BaseAddress);
1314 context->Eax = STATUS_SUCCESS;
1319 case 0x0013: /* NtFlushVirtualMemory */
1321 * Input: EDX: Flat address of arguments on stack
1323 * DWORD ProcessHandle [in] Process (defining address space)
1324 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1325 * DWORD *ViewSize [in?] Number of bytes to be flushed
1326 * DWORD *unknown [???] (?? unknown ??)
1328 * Output: EAX: NtStatus
1331 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1332 DWORD ProcessHandle = stack[0]; /* ignored */
1333 DWORD *BaseAddress = (DWORD *)W32S_APP2WINE(stack[1]);
1334 DWORD *ViewSize = (DWORD *)W32S_APP2WINE(stack[2]);
1335 DWORD *unknown = (DWORD *)W32S_APP2WINE(stack[3]);
1337 LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress) : 0);
1338 DWORD size = ViewSize? *ViewSize : 0;
1340 TRACE("NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1341 ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1343 TRACE("NtFlushVirtualMemory: base=%lx, size=%lx\n",
1344 (DWORD)address, size);
1346 FlushViewOfFile(address, size);
1348 context->Eax = STATUS_SUCCESS;
1353 case 0x0014: /* Get/Set Debug Registers */
1355 * Input: ECX: 0 if Get, 1 if Set
1357 * EDX: Get: Flat address of buffer to receive values of
1358 * debug registers DR0 .. DR7
1359 * Set: Flat address of buffer containing values of
1360 * debug registers DR0 .. DR7 to be set
1364 FIXME("[0014] ECX=%lx EDX=%lx\n",
1365 context->Ecx, context->Edx);
1371 case 0x0015: /* Set Coprocessor Emulation Flag */
1373 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1378 TRACE("[0015] EDX=%lx\n", context->Edx);
1380 /* We don't care, as we always have a coprocessor anyway */
1384 case 0x0016: /* Init Win32S VxD PSP */
1386 * If called to query required PSP size:
1389 * Output: EDX: Required size of Win32s VxD PSP
1391 * If called to initialize allocated PSP:
1393 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1394 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1398 if (context->Ebx == 0)
1399 context->Edx = 0x80;
1402 PDB16 *psp = MapSL( MAKESEGPTR( BX_reg(context), 0 ));
1404 psp->fileHandlesPtr = MAKELONG(HIWORD(context->Ebx), 0x5c);
1405 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1410 case 0x0017: /* Set Break Point */
1412 * Input: EBX: Offset of Break Point
1413 * CX: Selector of Break Point
1418 FIXME("[0017] EBX=%lx CX=%x\n",
1419 context->Ebx, CX_reg(context));
1425 case 0x0018: /* VirtualLock */
1427 * Input: ECX: Current Process
1429 * EDX: Flat address of arguments on stack
1431 * DWORD *retv [out] TRUE if success, FALSE if failure
1432 * LPVOID base [in] Flat address of range to lock
1433 * DWORD size [in] Size of range
1435 * Output: EAX: NtStatus
1438 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1439 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1440 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1441 DWORD size = stack[2];
1444 TRACE("VirtualLock(%lx, %lx, %lx)\n",
1445 (DWORD)retv, (DWORD)base, size);
1447 result = VirtualLock(base, size);
1451 context->Eax = STATUS_SUCCESS;
1454 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1459 case 0x0019: /* VirtualUnlock */
1461 * Input: ECX: Current Process
1463 * EDX: Flat address of arguments on stack
1465 * DWORD *retv [out] TRUE if success, FALSE if failure
1466 * LPVOID base [in] Flat address of range to unlock
1467 * DWORD size [in] Size of range
1469 * Output: EAX: NtStatus
1472 DWORD *stack = (DWORD *)W32S_APP2WINE(context->Edx);
1473 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0]);
1474 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1]);
1475 DWORD size = stack[2];
1478 TRACE("VirtualUnlock(%lx, %lx, %lx)\n",
1479 (DWORD)retv, (DWORD)base, size);
1481 result = VirtualUnlock(base, size);
1485 context->Eax = STATUS_SUCCESS;
1488 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1493 case 0x001A: /* KGetSystemInfo */
1497 * Output: ECX: Start of sparse memory arena
1498 * EDX: End of sparse memory arena
1501 TRACE("KGetSystemInfo()\n");
1504 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1505 * sparse memory arena. We do it the other way around, since
1506 * we have to reserve 3GB - 4GB for Linux, and thus use
1507 * 0GB - 3GB as sparse memory arena.
1509 * FIXME: What about other OSes ?
1512 context->Ecx = W32S_WINE2APP(0x00000000);
1513 context->Edx = W32S_WINE2APP(0xbfffffff);
1517 case 0x001B: /* KGlobalMemStat */
1519 * Input: ESI: Flat address of buffer to receive memory info
1524 struct Win32sMemoryInfo
1526 DWORD DIPhys_Count; /* Total physical pages */
1527 DWORD DIFree_Count; /* Free physical pages */
1528 DWORD DILin_Total_Count; /* Total virtual pages (private arena) */
1529 DWORD DILin_Total_Free; /* Free virtual pages (private arena) */
1531 DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */
1532 DWORD SparseFree; /* Free size of sparse arena (bytes ?) */
1535 struct Win32sMemoryInfo *info =
1536 (struct Win32sMemoryInfo *)W32S_APP2WINE(context->Esi);
1538 FIXME("KGlobalMemStat(%lx)\n", (DWORD)info);
1545 case 0x001C: /* Enable/Disable Exceptions */
1547 * Input: ECX: 0 to disable, 1 to enable exception handling
1552 TRACE("[001c] ECX=%lx\n", context->Ecx);
1558 case 0x001D: /* VirtualAlloc called from 16-bit code */
1560 * Input: EDX: Segmented address of arguments on stack
1562 * LPVOID base [in] Flat address of region to reserve/commit
1563 * DWORD size [in] Size of region
1564 * DWORD type [in] Type of allocation
1565 * DWORD prot [in] Type of access protection
1567 * Output: EAX: NtStatus
1568 * EDX: Flat base address of allocated region
1571 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1572 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1573 DWORD size = stack[1];
1574 DWORD type = stack[2];
1575 DWORD prot = stack[3];
1578 TRACE("VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1579 (DWORD)base, size, type, prot);
1581 if (type & 0x80000000)
1583 WARN("VirtualAlloc16: strange type %lx\n", type);
1587 result = (DWORD)VirtualAlloc(base, size, type, prot);
1589 if (W32S_WINE2APP(result))
1590 context->Edx = W32S_WINE2APP(result),
1591 context->Eax = STATUS_SUCCESS;
1594 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1595 TRACE("VirtualAlloc16: returning base %lx\n", context->Edx);
1600 case 0x001E: /* VirtualFree called from 16-bit code */
1602 * Input: EDX: Segmented address of arguments on stack
1604 * LPVOID base [in] Flat address of region
1605 * DWORD size [in] Size of region
1606 * DWORD type [in] Type of operation
1608 * Output: EAX: NtStatus
1609 * EDX: TRUE if success, FALSE if failure
1612 DWORD *stack = MapSL( MAKESEGPTR( LOWORD(context->Edx), HIWORD(context->Edx) ));
1613 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0]);
1614 DWORD size = stack[1];
1615 DWORD type = stack[2];
1618 TRACE("VirtualFree16(%lx, %lx, %lx)\n",
1619 (DWORD)base, size, type);
1621 result = VirtualFree(base, size, type);
1624 context->Edx = TRUE,
1625 context->Eax = STATUS_SUCCESS;
1627 context->Edx = FALSE,
1628 context->Eax = STATUS_NO_MEMORY; /* FIXME */
1633 case 0x001F: /* FWorkingSetSize */
1635 * Input: EDX: 0 if Get, 1 if Set
1637 * ECX: Get: Buffer to receive Working Set Size
1638 * Set: Buffer containing Working Set Size
1643 DWORD *ptr = (DWORD *)W32S_APP2WINE(context->Ecx);
1644 BOOL set = context->Edx;
1646 TRACE("FWorkingSetSize(%lx, %lx)\n", (DWORD)ptr, (DWORD)set);
1649 /* We do it differently ... */;
1653 context->Eax = STATUS_SUCCESS;
1659 VXD_BARF( context, "W32S" );