4 * Copyright 1995 Anand Kumria
15 #include "wine/winbase16.h"
16 #include "wine/winuser16.h"
19 #include "selectors.h"
24 #include "debugtools.h"
26 DEFAULT_DEBUG_CHANNEL(vxd)
29 #define VXD_BARF(context,name) \
30 DPRINTF( "vxd %s: unknown/not implemented parameters:\n" \
31 "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
32 "SI %04x, DI %04x, DS %04x, ES %04x\n", \
33 (name), (name), AX_reg(context), BX_reg(context), \
34 CX_reg(context), DX_reg(context), SI_reg(context), \
35 DI_reg(context), (WORD)DS_reg(context), (WORD)ES_reg(context) )
38 static WORD VXD_WinVersion(void)
40 WORD version = LOWORD(GetVersion16());
41 return (version >> 8) | (version << 8);
44 /***********************************************************************
47 void WINAPI VXD_VMM ( CONTEXT86 *context )
49 unsigned service = AX_reg(context);
51 TRACE("[%04x] VMM \n", (UINT16)service);
55 case 0x0000: /* version */
56 AX_reg(context) = VXD_WinVersion();
60 case 0x026d: /* Get_Debug_Flag '/m' */
61 case 0x026e: /* Get_Debug_Flag '/n' */
67 VXD_BARF( context, "VMM" );
71 /***********************************************************************
74 void WINAPI VXD_PageFile( CONTEXT86 *context )
76 unsigned service = AX_reg(context);
78 /* taken from Ralf Brown's Interrupt List */
80 TRACE("[%04x] PageFile\n", (UINT16)service );
84 case 0x00: /* get version, is this windows version? */
85 TRACE("returning version\n");
86 AX_reg(context) = VXD_WinVersion();
90 case 0x01: /* get swap file info */
91 TRACE("VxD PageFile: returning swap file info\n");
92 AX_reg(context) = 0x00; /* paging disabled */
93 ECX_reg(context) = 0; /* maximum size of paging file */
94 /* FIXME: do I touch DS:SI or DS:DI? */
98 case 0x02: /* delete permanent swap on exit */
99 TRACE("VxD PageFile: supposed to delete swap\n");
100 RESET_CFLAG(context);
103 case 0x03: /* current temporary swap file size */
104 TRACE("VxD PageFile: what is current temp. swap size\n");
105 RESET_CFLAG(context);
108 case 0x04: /* read or write?? INTERRUP.D */
109 case 0x05: /* cancel?? INTERRUP.D */
110 case 0x06: /* test I/O valid INTERRUP.D */
112 VXD_BARF( context, "pagefile" );
117 /***********************************************************************
120 void WINAPI VXD_Reboot ( CONTEXT86 *context )
122 unsigned service = AX_reg(context);
124 TRACE("[%04x] VMM \n", (UINT16)service);
128 case 0x0000: /* version */
129 AX_reg(context) = VXD_WinVersion();
130 RESET_CFLAG(context);
134 VXD_BARF( context, "REBOOT" );
138 /***********************************************************************
141 void WINAPI VXD_VDD ( CONTEXT86 *context )
143 unsigned service = AX_reg(context);
145 TRACE("[%04x] VDD \n", (UINT16)service);
149 case 0x0000: /* version */
150 AX_reg(context) = VXD_WinVersion();
151 RESET_CFLAG(context);
155 VXD_BARF( context, "VDD" );
159 /***********************************************************************
162 void WINAPI VXD_VMD ( CONTEXT86 *context )
164 unsigned service = AX_reg(context);
166 TRACE("[%04x] VMD \n", (UINT16)service);
170 case 0x0000: /* version */
171 AX_reg(context) = VXD_WinVersion();
172 RESET_CFLAG(context);
176 VXD_BARF( context, "VMD" );
180 /***********************************************************************
183 void WINAPI VXD_Shell( CONTEXT86 *context )
185 unsigned service = DX_reg(context);
187 TRACE("[%04x] Shell\n", (UINT16)service);
189 switch (service) /* Ralf Brown says EDX, but I use DX instead */
192 TRACE("returning version\n");
193 AX_reg(context) = VXD_WinVersion();
194 EBX_reg(context) = 1; /* system VM Handle */
200 /* SHELL_SYSMODAL_Message
201 ebx virtual maschine handle
202 eax message box flags
203 ecx address of message
204 edi address of caption
205 return response in eax
209 ebx virtual maschine handle
210 eax message box flags
211 ecx address of message
212 edi address of caption
214 edx reference data for callback
215 return response in eax
218 VXD_BARF( context, "shell" );
221 case 0x0006: /* SHELL_Get_VM_State */
222 TRACE("VxD Shell: returning VM state\n");
223 /* Actually we don't, not yet. We have to return a structure
224 * and I am not to sure how to set it up and return it yet,
225 * so for now let's do nothing. I can (hopefully) get this
226 * by the next release
228 /* RESET_CFLAG(context); */
247 VXD_BARF( context, "SHELL" );
250 /* the new Win95 shell API */
251 case 0x0100: /* get version */
252 AX_reg(context) = VXD_WinVersion();
255 case 0x0104: /* retrieve Hook_Properties list */
256 case 0x0105: /* call Hook_Properties callbacks */
257 VXD_BARF( context, "SHELL" );
260 case 0x0106: /* install timeout callback */
261 TRACE("VxD Shell: ignoring shell callback (%ld sec.)\n",
262 EBX_reg( context ) );
266 case 0x0107: /* get version of any VxD */
268 VXD_BARF( context, "SHELL" );
274 /***********************************************************************
277 void WINAPI VXD_Comm( CONTEXT86 *context )
279 unsigned service = AX_reg(context);
281 TRACE("[%04x] Comm\n", (UINT16)service);
285 case 0x0000: /* get version */
286 TRACE("returning version\n");
287 AX_reg(context) = VXD_WinVersion();
288 RESET_CFLAG(context);
291 case 0x0001: /* set port global */
292 case 0x0002: /* get focus */
293 case 0x0003: /* virtualise port */
295 VXD_BARF( context, "comm" );
299 /***********************************************************************
302 void WINAPI VXD_Timer( CONTEXT86 *context )
304 unsigned service = AX_reg(context);
306 TRACE("[%04x] Virtual Timer\n", (UINT16)service);
310 case 0x0000: /* version */
311 AX_reg(context) = VXD_WinVersion();
312 RESET_CFLAG(context);
315 case 0x0100: /* clock tick time, in 840nsecs */
316 EAX_reg(context) = GetTickCount();
318 EDX_reg(context) = EAX_reg(context) >> 22;
319 EAX_reg(context) <<= 10; /* not very precise */
322 case 0x0101: /* current Windows time, msecs */
323 case 0x0102: /* current VM time, msecs */
324 EAX_reg(context) = GetTickCount();
328 VXD_BARF( context, "VTD" );
332 /***********************************************************************
335 static DWORD System_Time = 0;
336 static WORD System_Time_Selector = 0;
337 static void System_Time_Tick( WORD timer ) { System_Time += 55; }
338 void WINAPI VXD_TimerAPI ( CONTEXT86 *context )
340 unsigned service = AX_reg(context);
342 TRACE("[%04x] TimerAPI \n", (UINT16)service);
346 case 0x0000: /* version */
347 AX_reg(context) = VXD_WinVersion();
348 RESET_CFLAG(context);
351 case 0x0009: /* get system time selector */
352 if ( !System_Time_Selector )
354 System_Time_Selector = SELECTOR_AllocBlock( &System_Time, sizeof(DWORD),
355 SEGMENT_DATA, FALSE, TRUE );
356 CreateSystemTimer( 55, System_Time_Tick );
359 AX_reg(context) = System_Time_Selector;
360 RESET_CFLAG(context);
364 VXD_BARF( context, "VTDAPI" );
368 /***********************************************************************
371 void WINAPI VXD_ConfigMG ( CONTEXT86 *context )
373 unsigned service = AX_reg(context);
375 TRACE("[%04x] ConfigMG \n", (UINT16)service);
379 case 0x0000: /* version */
380 AX_reg(context) = VXD_WinVersion();
381 RESET_CFLAG(context);
385 VXD_BARF( context, "CONFIGMG" );
389 /***********************************************************************
392 void WINAPI VXD_Enable ( CONTEXT86 *context )
394 unsigned service = AX_reg(context);
396 TRACE("[%04x] Enable \n", (UINT16)service);
400 case 0x0000: /* version */
401 AX_reg(context) = VXD_WinVersion();
402 RESET_CFLAG(context);
406 VXD_BARF( context, "ENABLE" );
410 /***********************************************************************
413 void WINAPI VXD_APM ( CONTEXT86 *context )
415 unsigned service = AX_reg(context);
417 TRACE("[%04x] APM \n", (UINT16)service);
421 case 0x0000: /* version */
422 AX_reg(context) = VXD_WinVersion();
423 RESET_CFLAG(context);
427 VXD_BARF( context, "APM" );
431 /***********************************************************************
434 * This is an implementation of the services of the Win32s VxD.
435 * Since official documentation of these does not seem to be available,
436 * certain arguments of some of the services remain unclear.
438 * FIXME: The following services are currently unimplemented:
439 * Exception handling (0x01, 0x1C)
440 * Debugger support (0x0C, 0x14, 0x17)
441 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
442 * Memory Statistics (0x1B)
445 * We have a specific problem running Win32s on Linux (and probably also
446 * the other x86 unixes), since Win32s tries to allocate its main 'flat
447 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
448 * The rationale for this seems to be that they want one the one hand to
449 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
450 * at linear address 0, but want at other hand to have offset 0 of the
451 * flat data/code segment point to an unmapped page (to catch NULL pointer
452 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
453 * so that the Win 3.1 memory area at linear address zero shows up in the
454 * flat segments at offset 0x10000 (since linear addresses wrap around at
455 * 4GB). To compensate for that discrepancy between flat segment offsets
456 * and plain linear addresses, all flat pointers passed between the 32-bit
457 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
458 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
460 * The problem for us is now that Linux does not allow a LDT selector with
461 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
462 * address space. To address this problem we introduce *another* offset:
463 * We add 0x10000 to every linear address we get as an argument from Win32s.
464 * This means especially that the flat code/data selectors get actually
465 * allocated with base 0x0, so that flat offsets and (real) linear addresses
466 * do again agree! In fact, every call e.g. of a Win32s VxD service now
467 * has all pointer arguments (which are offsets in the flat data segement)
468 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
469 * increased by 0x10000 by *our* code.
471 * Note that to keep everything consistent, this offset has to be applied by
472 * every Wine function that operates on 'linear addresses' passed to it by
473 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
474 * API routines, this affects only two locations: this VxD and the DPMI
475 * handler. (NOTE: Should any Win32s application pass a linear address to
476 * any routine apart from those, e.g. some other VxD handler, that code
477 * would have to take the offset into account as well!)
479 * The application of the offset is triggered by marking the current process
480 * as a Win32s process by setting the PDB32_WIN32S_PROC flag in the process
481 * database. This is done the first time any application calls the GetVersion()
482 * service of the Win32s VxD. (Note that the flag is never removed.)
486 void WINAPI VXD_Win32s( CONTEXT86 *context )
488 switch (AX_reg(context))
490 case 0x0000: /* Get Version */
494 * Output: EAX: LoWord: Win32s Version (1.30)
495 * HiWord: VxD Version (200)
501 * EDX: Debugging Flags
505 * 1 if VMCPD VxD not found
508 TRACE("GetVersion()\n");
510 EAX_reg(context) = VXD_WinVersion() | (200 << 16);
511 EBX_reg(context) = 0;
512 ECX_reg(context) = 0;
513 EDX_reg(context) = 0;
514 EDI_reg(context) = 0;
517 * If this is the first time we are called for this process,
518 * hack the memory image of WIN32S16 so that it doesn't try
519 * to access the GDT directly ...
521 * The first code segment of WIN32S16 (version 1.30) contains
522 * an unexported function somewhere between the exported functions
523 * SetFS and StackLinearToSegmented that tries to find a selector
524 * in the LDT that maps to the memory image of the LDT itself.
525 * If it succeeds, it stores this selector into a global variable
526 * which will be used to speed up execution by using this selector
527 * to modify the LDT directly instead of using the DPMI calls.
529 * To perform this search of the LDT, this function uses the
530 * sgdt and sldt instructions to find the linear address of
531 * the (GDT and then) LDT. While those instructions themselves
532 * execute without problem, the linear address that sgdt returns
533 * points (at least under Linux) to the kernel address space, so
534 * that any subsequent access leads to a segfault.
536 * Fortunately, WIN32S16 still contains as a fallback option the
537 * mechanism of using DPMI calls to modify LDT selectors instead
538 * of direct writes to the LDT. Thus we can circumvent the problem
539 * by simply replacing the first byte of the offending function
540 * with an 'retf' instruction. This means that the global variable
541 * supposed to contain the LDT alias selector will remain zero,
542 * and hence WIN32S16 will fall back to using DPMI calls.
544 * The heuristic we employ to _find_ that function is as follows:
545 * We search between the addresses of the exported symbols SetFS
546 * and StackLinearToSegmented for the byte sequence '0F 01 04'
547 * (this is the opcode of 'sgdt [si]'). We then search backwards
548 * from this address for the last occurrance of 'CB' (retf) that marks
549 * the end of the preceeding function. The following byte (which
550 * should now be the first byte of the function we are looking for)
551 * will be replaced by 'CB' (retf).
553 * This heuristic works for the retail as well as the debug version
554 * of Win32s version 1.30. For versions earlier than that this
555 * hack should not be necessary at all, since the whole mechanism
556 * ('PERF130') was introduced only in 1.30 to improve the overall
557 * performance of Win32s.
560 if (!(PROCESS_Current()->flags & PDB32_WIN32S_PROC))
562 HMODULE16 hModule = GetModuleHandle16("win32s16");
563 SEGPTR func1 = (SEGPTR)WIN32_GetProcAddress16(hModule, "SetFS");
564 SEGPTR func2 = (SEGPTR)WIN32_GetProcAddress16(hModule,
565 "StackLinearToSegmented");
567 if ( hModule && func1 && func2
568 && SELECTOROF(func1) == SELECTOROF(func2))
570 BYTE *start = PTR_SEG_TO_LIN(func1);
571 BYTE *end = PTR_SEG_TO_LIN(func2);
572 BYTE *p, *retv = NULL;
575 for (p = start; p < end; p++)
576 if (*p == 0xCB) found = 0, retv = p;
577 else if (*p == 0x0F) found = 1;
578 else if (*p == 0x01 && found == 1) found = 2;
579 else if (*p == 0x04 && found == 2) { found = 3; break; }
582 if (found == 3 && retv)
584 TRACE("PERF130 hack: "
585 "Replacing byte %02X at offset %04X:%04X\n",
586 *(retv+1), SELECTOROF(func1),
587 OFFSETOF(func1) + retv+1-start);
589 *(retv+1) = (BYTE)0xCB;
595 * Mark process as Win32s, so that subsequent DPMI calls
596 * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
599 PROCESS_Current()->flags |= PDB32_WIN32S_PROC;
603 case 0x0001: /* Install Exception Handling */
605 * Input: EBX: Flat address of W32SKRNL Exception Data
607 * ECX: LoWord: Flat Code Selector
608 * HiWord: Flat Data Selector
610 * EDX: Flat address of W32SKRNL Exception Handler
611 * (this is equal to W32S_BackTo32 + 0x40)
613 * ESI: SEGPTR KERNEL.HASGPHANDLER
615 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
617 * Output: EAX: 0 if OK
620 TRACE("[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
621 EBX_reg(context), ECX_reg(context), EDX_reg(context),
622 ESI_reg(context), EDI_reg(context));
626 EAX_reg(context) = 0;
630 case 0x0002: /* Set Page Access Flags */
632 * Input: EBX: New access flags
633 * Bit 2: User Page if set, Supervisor Page if clear
634 * Bit 1: Read-Write if set, Read-Only if clear
636 * ECX: Size of memory area to change
638 * EDX: Flat start address of memory area
640 * Output: EAX: Size of area changed
643 TRACE("[0002] EBX=%lx ECX=%lx EDX=%lx\n",
644 EBX_reg(context), ECX_reg(context), EDX_reg(context));
648 EAX_reg(context) = ECX_reg(context);
652 case 0x0003: /* Get Page Access Flags */
654 * Input: EDX: Flat address of page to query
656 * Output: EAX: Page access flags
657 * Bit 2: User Page if set, Supervisor Page if clear
658 * Bit 1: Read-Write if set, Read-Only if clear
661 TRACE("[0003] EDX=%lx\n", EDX_reg(context));
665 EAX_reg(context) = 6;
669 case 0x0004: /* Map Module */
671 * Input: ECX: IMTE (offset in Module Table) of new module
673 * EDX: Flat address of Win32s Module Table
675 * Output: EAX: 0 if OK
678 if (!EDX_reg(context) || CX_reg(context) == 0xFFFF)
680 TRACE("MapModule: Initialization call\n");
681 EAX_reg(context) = 0;
686 * Structure of a Win32s Module Table Entry:
701 * Note: This function should set up a demand-paged memory image
702 * of the given module. Since mmap does not allow file offsets
703 * not aligned at 1024 bytes, we simply load the image fully
707 struct Win32sModule *moduleTable =
708 (struct Win32sModule *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
709 struct Win32sModule *module = moduleTable + ECX_reg(context);
711 IMAGE_NT_HEADERS *nt_header = PE_HEADER(module->baseAddr);
712 IMAGE_SECTION_HEADER *pe_seg = PE_SECTIONS(module->baseAddr);
714 HFILE image = _lopen(module->pathName, OF_READ);
715 BOOL error = (image == INVALID_HANDLE_VALUE);
718 TRACE("MapModule: Loading %s\n", module->pathName);
721 !error && i < nt_header->FileHeader.NumberOfSections;
723 if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
725 DWORD off = pe_seg->PointerToRawData;
726 DWORD len = pe_seg->SizeOfRawData;
727 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
730 "Section %d at %08lx from %08lx len %08lx\n",
731 i, (DWORD)addr, off, len);
733 if ( _llseek(image, off, SEEK_SET) != off
734 || _lread(image, addr, len) != len)
741 ERR("MapModule: Unable to load %s\n", module->pathName);
743 else if (module->relocDelta != 0)
745 IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
746 + IMAGE_DIRECTORY_ENTRY_BASERELOC;
747 IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
748 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
750 TRACE("MapModule: Reloc delta %08lx\n", module->relocDelta);
752 while (r && r->VirtualAddress)
754 LPBYTE page = module->baseAddr + r->VirtualAddress;
755 int count = (r->SizeOfBlock - 8) / 2;
757 TRACE("MapModule: %d relocations for page %08lx\n",
760 for(i = 0; i < count; i++)
762 int offset = r->TypeOffset[i] & 0xFFF;
763 int type = r->TypeOffset[i] >> 12;
766 case IMAGE_REL_BASED_ABSOLUTE:
768 case IMAGE_REL_BASED_HIGH:
769 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
771 case IMAGE_REL_BASED_LOW:
772 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
774 case IMAGE_REL_BASED_HIGHLOW:
775 *(DWORD*)(page+offset) += module->relocDelta;
778 WARN("MapModule: Unsupported fixup type\n");
783 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
787 EAX_reg(context) = 0;
788 RESET_CFLAG(context);
793 case 0x0005: /* UnMap Module */
795 * Input: EDX: Flat address of module image
797 * Output: EAX: 1 if OK
800 TRACE("UnMapModule: %lx\n", (DWORD)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET));
802 /* As we didn't map anything, there's nothing to unmap ... */
804 EAX_reg(context) = 1;
808 case 0x0006: /* VirtualAlloc */
810 * Input: ECX: Current Process
812 * EDX: Flat address of arguments on stack
814 * DWORD *retv [out] Flat base address of allocated region
815 * LPVOID base [in] Flat address of region to reserve/commit
816 * DWORD size [in] Size of region
817 * DWORD type [in] Type of allocation
818 * DWORD prot [in] Type of access protection
820 * Output: EAX: NtStatus
823 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
824 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
825 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
826 DWORD size = stack[2];
827 DWORD type = stack[3];
828 DWORD prot = stack[4];
831 TRACE("VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
832 (DWORD)retv, (DWORD)base, size, type, prot);
834 if (type & 0x80000000)
836 WARN("VirtualAlloc: strange type %lx\n", type);
840 if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
842 WARN("VirtualAlloc: NLS hack, allowing write access!\n");
843 prot = PAGE_READWRITE;
846 result = (DWORD)VirtualAlloc(base, size, type, prot);
848 if (W32S_WINE2APP(result, W32S_OFFSET))
849 *retv = W32S_WINE2APP(result, W32S_OFFSET),
850 EAX_reg(context) = STATUS_SUCCESS;
853 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
858 case 0x0007: /* VirtualFree */
860 * Input: ECX: Current Process
862 * EDX: Flat address of arguments on stack
864 * DWORD *retv [out] TRUE if success, FALSE if failure
865 * LPVOID base [in] Flat address of region
866 * DWORD size [in] Size of region
867 * DWORD type [in] Type of operation
869 * Output: EAX: NtStatus
872 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
873 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
874 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
875 DWORD size = stack[2];
876 DWORD type = stack[3];
879 TRACE("VirtualFree(%lx, %lx, %lx, %lx)\n",
880 (DWORD)retv, (DWORD)base, size, type);
882 result = VirtualFree(base, size, type);
886 EAX_reg(context) = STATUS_SUCCESS;
889 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
894 case 0x0008: /* VirtualProtect */
896 * Input: ECX: Current Process
898 * EDX: Flat address of arguments on stack
900 * DWORD *retv [out] TRUE if success, FALSE if failure
901 * LPVOID base [in] Flat address of region
902 * DWORD size [in] Size of region
903 * DWORD new_prot [in] Desired access protection
904 * DWORD *old_prot [out] Previous access protection
906 * Output: EAX: NtStatus
909 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
910 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
911 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
912 DWORD size = stack[2];
913 DWORD new_prot = stack[3];
914 DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4], W32S_OFFSET);
917 TRACE("VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
918 (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
920 result = VirtualProtect(base, size, new_prot, old_prot);
924 EAX_reg(context) = STATUS_SUCCESS;
927 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
932 case 0x0009: /* VirtualQuery */
934 * Input: ECX: Current Process
936 * EDX: Flat address of arguments on stack
938 * DWORD *retv [out] Nr. bytes returned
939 * LPVOID base [in] Flat address of region
940 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
941 * DWORD len [in] Size of buffer
943 * Output: EAX: NtStatus
946 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
947 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
948 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
949 LPMEMORY_BASIC_INFORMATION info =
950 (LPMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2], W32S_OFFSET);
951 DWORD len = stack[3];
954 TRACE("VirtualQuery(%lx, %lx, %lx, %lx)\n",
955 (DWORD)retv, (DWORD)base, (DWORD)info, len);
957 result = VirtualQuery(base, info, len);
960 EAX_reg(context) = STATUS_SUCCESS;
965 case 0x000A: /* SetVirtMemProcess */
967 * Input: ECX: Process Handle
969 * EDX: Flat address of region
971 * Output: EAX: NtStatus
974 TRACE("[000a] ECX=%lx EDX=%lx\n",
975 ECX_reg(context), EDX_reg(context));
979 EAX_reg(context) = STATUS_SUCCESS;
983 case 0x000B: /* ??? some kind of cleanup */
985 * Input: ECX: Process Handle
987 * Output: EAX: NtStatus
990 TRACE("[000b] ECX=%lx\n", ECX_reg(context));
994 EAX_reg(context) = STATUS_SUCCESS;
998 case 0x000C: /* Set Debug Flags */
1000 * Input: EDX: Debug Flags
1002 * Output: EDX: Previous Debug Flags
1005 FIXME("[000c] EDX=%lx\n", EDX_reg(context));
1009 EDX_reg(context) = 0;
1013 case 0x000D: /* NtCreateSection */
1015 * Input: EDX: Flat address of arguments on stack
1017 * HANDLE32 *retv [out] Handle of Section created
1018 * DWORD flags1 [in] (?? unknown ??)
1019 * DWORD atom [in] Name of Section to create
1020 * LARGE_INTEGER *size [in] Size of Section
1021 * DWORD protect [in] Access protection
1022 * DWORD flags2 [in] (?? unknown ??)
1023 * HFILE32 hFile [in] Handle of file to map
1024 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
1026 * Output: EAX: NtStatus
1029 DWORD *stack = (DWORD *) W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1030 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0], W32S_OFFSET);
1031 DWORD flags1 = stack[1];
1032 DWORD atom = stack[2];
1033 LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3], W32S_OFFSET);
1034 DWORD protect = stack[4];
1035 DWORD flags2 = stack[5];
1036 HFILE hFile = FILE_GetHandle(stack[6]);
1037 DWORD psp = stack[7];
1039 HANDLE result = INVALID_HANDLE_VALUE;
1042 TRACE("NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1043 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1046 if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1048 TRACE("NtCreateSection: name=%s\n", atom? name : NULL);
1050 result = CreateFileMappingA(hFile, NULL, protect,
1051 size? size->s.HighPart : 0,
1052 size? size->s.LowPart : 0,
1056 if (result == INVALID_HANDLE_VALUE)
1057 WARN("NtCreateSection: failed!\n");
1059 TRACE("NtCreateSection: returned %lx\n", (DWORD)result);
1061 if (result != INVALID_HANDLE_VALUE)
1063 EAX_reg(context) = STATUS_SUCCESS;
1066 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1071 case 0x000E: /* NtOpenSection */
1073 * Input: EDX: Flat address of arguments on stack
1075 * HANDLE32 *retv [out] Handle of Section opened
1076 * DWORD protect [in] Access protection
1077 * DWORD atom [in] Name of Section to create
1079 * Output: EAX: NtStatus
1082 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1083 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0], W32S_OFFSET);
1084 DWORD protect = stack[1];
1085 DWORD atom = stack[2];
1087 HANDLE result = INVALID_HANDLE_VALUE;
1090 TRACE("NtOpenSection(%lx, %lx, %lx)\n",
1091 (DWORD)retv, protect, atom);
1093 if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1095 TRACE("NtOpenSection: name=%s\n", name);
1097 result = OpenFileMappingA(protect, FALSE, name);
1100 if (result == INVALID_HANDLE_VALUE)
1101 WARN("NtOpenSection: failed!\n");
1103 TRACE("NtOpenSection: returned %lx\n", (DWORD)result);
1105 if (result != INVALID_HANDLE_VALUE)
1107 EAX_reg(context) = STATUS_SUCCESS;
1110 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1115 case 0x000F: /* NtCloseSection */
1117 * Input: EDX: Flat address of arguments on stack
1119 * HANDLE32 handle [in] Handle of Section to close
1120 * DWORD *id [out] Unique ID (?? unclear ??)
1122 * Output: EAX: NtStatus
1125 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1126 HANDLE handle = stack[0];
1127 DWORD *id = (DWORD *)W32S_APP2WINE(stack[1], W32S_OFFSET);
1129 TRACE("NtCloseSection(%lx, %lx)\n", (DWORD)handle, (DWORD)id);
1131 CloseHandle(handle);
1132 if (id) *id = 0; /* FIXME */
1134 EAX_reg(context) = STATUS_SUCCESS;
1139 case 0x0010: /* NtDupSection */
1141 * Input: EDX: Flat address of arguments on stack
1143 * HANDLE32 handle [in] Handle of Section to duplicate
1145 * Output: EAX: NtStatus
1148 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1149 HANDLE handle = stack[0];
1152 TRACE("NtDupSection(%lx)\n", (DWORD)handle);
1154 DuplicateHandle( GetCurrentProcess(), handle,
1155 GetCurrentProcess(), &new_handle,
1156 0, FALSE, DUPLICATE_SAME_ACCESS );
1157 EAX_reg(context) = STATUS_SUCCESS;
1162 case 0x0011: /* NtMapViewOfSection */
1164 * Input: EDX: Flat address of arguments on stack
1166 * HANDLE32 SectionHandle [in] Section to be mapped
1167 * DWORD ProcessHandle [in] Process to be mapped into
1168 * DWORD * BaseAddress [in/out] Address to be mapped at
1169 * DWORD ZeroBits [in] (?? unclear ??)
1170 * DWORD CommitSize [in] (?? unclear ??)
1171 * LARGE_INTEGER *SectionOffset [in] Offset within section
1172 * DWORD * ViewSize [in] Size of view
1173 * DWORD InheritDisposition [in] (?? unclear ??)
1174 * DWORD AllocationType [in] (?? unclear ??)
1175 * DWORD Protect [in] Access protection
1177 * Output: EAX: NtStatus
1180 DWORD * stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1181 HANDLE SectionHandle = stack[0];
1182 DWORD ProcessHandle = stack[1]; /* ignored */
1183 DWORD * BaseAddress = (DWORD *)W32S_APP2WINE(stack[2], W32S_OFFSET);
1184 DWORD ZeroBits = stack[3];
1185 DWORD CommitSize = stack[4];
1186 LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5], W32S_OFFSET);
1187 DWORD * ViewSize = (DWORD *)W32S_APP2WINE(stack[6], W32S_OFFSET);
1188 DWORD InheritDisposition = stack[7];
1189 DWORD AllocationType = stack[8];
1190 DWORD Protect = stack[9];
1192 LPBYTE address = (LPBYTE)(BaseAddress?
1193 W32S_APP2WINE(*BaseAddress, W32S_OFFSET) : 0);
1194 DWORD access = 0, result;
1196 switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1198 case PAGE_READONLY: access = FILE_MAP_READ; break;
1199 case PAGE_READWRITE: access = FILE_MAP_WRITE; break;
1200 case PAGE_WRITECOPY: access = FILE_MAP_COPY; break;
1202 case PAGE_EXECUTE_READ: access = FILE_MAP_READ; break;
1203 case PAGE_EXECUTE_READWRITE: access = FILE_MAP_WRITE; break;
1204 case PAGE_EXECUTE_WRITECOPY: access = FILE_MAP_COPY; break;
1207 TRACE("NtMapViewOfSection"
1208 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1209 (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1210 ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1211 InheritDisposition, AllocationType, Protect);
1212 TRACE("NtMapViewOfSection: "
1213 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1214 (DWORD)address, SectionOffset? SectionOffset->s.LowPart : 0,
1215 ViewSize? *ViewSize : 0, access);
1217 result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1218 SectionOffset? SectionOffset->s.HighPart : 0,
1219 SectionOffset? SectionOffset->s.LowPart : 0,
1220 ViewSize? *ViewSize : 0, address);
1222 TRACE("NtMapViewOfSection: result=%lx\n", result);
1224 if (W32S_WINE2APP(result, W32S_OFFSET))
1226 if (BaseAddress) *BaseAddress = W32S_WINE2APP(result, W32S_OFFSET);
1227 EAX_reg(context) = STATUS_SUCCESS;
1230 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1235 case 0x0012: /* NtUnmapViewOfSection */
1237 * Input: EDX: Flat address of arguments on stack
1239 * DWORD ProcessHandle [in] Process (defining address space)
1240 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1242 * Output: EAX: NtStatus
1245 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1246 DWORD ProcessHandle = stack[0]; /* ignored */
1247 LPBYTE BaseAddress = (LPBYTE)W32S_APP2WINE(stack[1], W32S_OFFSET);
1249 TRACE("NtUnmapViewOfSection(%lx, %lx)\n",
1250 ProcessHandle, (DWORD)BaseAddress);
1252 UnmapViewOfFile(BaseAddress);
1254 EAX_reg(context) = STATUS_SUCCESS;
1259 case 0x0013: /* NtFlushVirtualMemory */
1261 * Input: EDX: Flat address of arguments on stack
1263 * DWORD ProcessHandle [in] Process (defining address space)
1264 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1265 * DWORD *ViewSize [in?] Number of bytes to be flushed
1266 * DWORD *unknown [???] (?? unknown ??)
1268 * Output: EAX: NtStatus
1271 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1272 DWORD ProcessHandle = stack[0]; /* ignored */
1273 DWORD *BaseAddress = (DWORD *)W32S_APP2WINE(stack[1], W32S_OFFSET);
1274 DWORD *ViewSize = (DWORD *)W32S_APP2WINE(stack[2], W32S_OFFSET);
1275 DWORD *unknown = (DWORD *)W32S_APP2WINE(stack[3], W32S_OFFSET);
1277 LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress, W32S_OFFSET) : 0);
1278 DWORD size = ViewSize? *ViewSize : 0;
1280 TRACE("NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1281 ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1283 TRACE("NtFlushVirtualMemory: base=%lx, size=%lx\n",
1284 (DWORD)address, size);
1286 FlushViewOfFile(address, size);
1288 EAX_reg(context) = STATUS_SUCCESS;
1293 case 0x0014: /* Get/Set Debug Registers */
1295 * Input: ECX: 0 if Get, 1 if Set
1297 * EDX: Get: Flat address of buffer to receive values of
1298 * debug registers DR0 .. DR7
1299 * Set: Flat address of buffer containing values of
1300 * debug registers DR0 .. DR7 to be set
1304 FIXME("[0014] ECX=%lx EDX=%lx\n",
1305 ECX_reg(context), EDX_reg(context));
1311 case 0x0015: /* Set Coprocessor Emulation Flag */
1313 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1318 TRACE("[0015] EDX=%lx\n", EDX_reg(context));
1320 /* We don't care, as we always have a coprocessor anyway */
1324 case 0x0016: /* Init Win32S VxD PSP */
1326 * If called to query required PSP size:
1329 * Output: EDX: Required size of Win32s VxD PSP
1331 * If called to initialize allocated PSP:
1333 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1334 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1338 if (EBX_reg(context) == 0)
1339 EDX_reg(context) = 0x80;
1342 PDB16 *psp = PTR_SEG_OFF_TO_LIN(BX_reg(context), 0);
1344 psp->fileHandlesPtr = MAKELONG(HIWORD(EBX_reg(context)), 0x5c);
1345 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1350 case 0x0017: /* Set Break Point */
1352 * Input: EBX: Offset of Break Point
1353 * CX: Selector of Break Point
1358 FIXME("[0017] EBX=%lx CX=%x\n",
1359 EBX_reg(context), CX_reg(context));
1365 case 0x0018: /* VirtualLock */
1367 * Input: ECX: Current Process
1369 * EDX: Flat address of arguments on stack
1371 * DWORD *retv [out] TRUE if success, FALSE if failure
1372 * LPVOID base [in] Flat address of range to lock
1373 * DWORD size [in] Size of range
1375 * Output: EAX: NtStatus
1378 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1379 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
1380 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
1381 DWORD size = stack[2];
1384 TRACE("VirtualLock(%lx, %lx, %lx)\n",
1385 (DWORD)retv, (DWORD)base, size);
1387 result = VirtualLock(base, size);
1391 EAX_reg(context) = STATUS_SUCCESS;
1394 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1399 case 0x0019: /* VirtualUnlock */
1401 * Input: ECX: Current Process
1403 * EDX: Flat address of arguments on stack
1405 * DWORD *retv [out] TRUE if success, FALSE if failure
1406 * LPVOID base [in] Flat address of range to unlock
1407 * DWORD size [in] Size of range
1409 * Output: EAX: NtStatus
1412 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1413 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
1414 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
1415 DWORD size = stack[2];
1418 TRACE("VirtualUnlock(%lx, %lx, %lx)\n",
1419 (DWORD)retv, (DWORD)base, size);
1421 result = VirtualUnlock(base, size);
1425 EAX_reg(context) = STATUS_SUCCESS;
1428 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1433 case 0x001A: /* KGetSystemInfo */
1437 * Output: ECX: Start of sparse memory arena
1438 * EDX: End of sparse memory arena
1441 TRACE("KGetSystemInfo()\n");
1444 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1445 * sparse memory arena. We do it the other way around, since
1446 * we have to reserve 3GB - 4GB for Linux, and thus use
1447 * 0GB - 3GB as sparse memory arena.
1449 * FIXME: What about other OSes ?
1452 ECX_reg(context) = W32S_WINE2APP(0x00000000, W32S_OFFSET);
1453 EDX_reg(context) = W32S_WINE2APP(0xbfffffff, W32S_OFFSET);
1457 case 0x001B: /* KGlobalMemStat */
1459 * Input: ESI: Flat address of buffer to receive memory info
1464 struct Win32sMemoryInfo
1466 DWORD DIPhys_Count; /* Total physical pages */
1467 DWORD DIFree_Count; /* Free physical pages */
1468 DWORD DILin_Total_Count; /* Total virtual pages (private arena) */
1469 DWORD DILin_Total_Free; /* Free virtual pages (private arena) */
1471 DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */
1472 DWORD SparseFree; /* Free size of sparse arena (bytes ?) */
1475 struct Win32sMemoryInfo *info =
1476 (struct Win32sMemoryInfo *)W32S_APP2WINE(ESI_reg(context), W32S_OFFSET);
1478 FIXME("KGlobalMemStat(%lx)\n", (DWORD)info);
1485 case 0x001C: /* Enable/Disable Exceptions */
1487 * Input: ECX: 0 to disable, 1 to enable exception handling
1492 TRACE("[001c] ECX=%lx\n", ECX_reg(context));
1498 case 0x001D: /* VirtualAlloc called from 16-bit code */
1500 * Input: EDX: Segmented address of arguments on stack
1502 * LPVOID base [in] Flat address of region to reserve/commit
1503 * DWORD size [in] Size of region
1504 * DWORD type [in] Type of allocation
1505 * DWORD prot [in] Type of access protection
1507 * Output: EAX: NtStatus
1508 * EDX: Flat base address of allocated region
1511 DWORD *stack = PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context)),
1512 HIWORD(EDX_reg(context)));
1513 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0], W32S_OFFSET);
1514 DWORD size = stack[1];
1515 DWORD type = stack[2];
1516 DWORD prot = stack[3];
1519 TRACE("VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1520 (DWORD)base, size, type, prot);
1522 if (type & 0x80000000)
1524 WARN("VirtualAlloc16: strange type %lx\n", type);
1528 result = (DWORD)VirtualAlloc(base, size, type, prot);
1530 if (W32S_WINE2APP(result, W32S_OFFSET))
1531 EDX_reg(context) = W32S_WINE2APP(result, W32S_OFFSET),
1532 EAX_reg(context) = STATUS_SUCCESS;
1534 EDX_reg(context) = 0,
1535 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1536 TRACE("VirtualAlloc16: returning base %lx\n", EDX_reg(context));
1541 case 0x001E: /* VirtualFree called from 16-bit code */
1543 * Input: EDX: Segmented address of arguments on stack
1545 * LPVOID base [in] Flat address of region
1546 * DWORD size [in] Size of region
1547 * DWORD type [in] Type of operation
1549 * Output: EAX: NtStatus
1550 * EDX: TRUE if success, FALSE if failure
1553 DWORD *stack = PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context)),
1554 HIWORD(EDX_reg(context)));
1555 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0], W32S_OFFSET);
1556 DWORD size = stack[1];
1557 DWORD type = stack[2];
1560 TRACE("VirtualFree16(%lx, %lx, %lx)\n",
1561 (DWORD)base, size, type);
1563 result = VirtualFree(base, size, type);
1566 EDX_reg(context) = TRUE,
1567 EAX_reg(context) = STATUS_SUCCESS;
1569 EDX_reg(context) = FALSE,
1570 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1575 case 0x001F: /* FWorkingSetSize */
1577 * Input: EDX: 0 if Get, 1 if Set
1579 * ECX: Get: Buffer to receive Working Set Size
1580 * Set: Buffer containing Working Set Size
1585 DWORD *ptr = (DWORD *)W32S_APP2WINE(ECX_reg(context), W32S_OFFSET);
1586 BOOL set = EDX_reg(context);
1588 TRACE("FWorkingSetSize(%lx, %lx)\n", (DWORD)ptr, (DWORD)set);
1591 /* We do it differently ... */;
1595 EAX_reg(context) = STATUS_SUCCESS;
1601 VXD_BARF( context, "W32S" );