4 * Copyright 1995 Anand Kumria
11 #include "wine/winbase16.h"
12 #include "wine/winuser16.h"
15 #include "selectors.h"
22 DEFAULT_DEBUG_CHANNEL(vxd)
25 #define VXD_BARF(context,name) \
26 DUMP( "vxd %s: unknown/not implemented parameters:\n" \
27 "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
28 "SI %04x, DI %04x, DS %04x, ES %04x\n", \
29 (name), (name), AX_reg(context), BX_reg(context), \
30 CX_reg(context), DX_reg(context), SI_reg(context), \
31 DI_reg(context), (WORD)DS_reg(context), (WORD)ES_reg(context) )
34 static WORD VXD_WinVersion(void)
36 WORD version = LOWORD(GetVersion16());
37 return (version >> 8) | (version << 8);
40 /***********************************************************************
43 void VXD_VMM ( CONTEXT *context )
45 unsigned service = AX_reg(context);
47 TRACE(vxd,"[%04x] VMM \n", (UINT16)service);
51 case 0x0000: /* version */
52 AX_reg(context) = VXD_WinVersion();
56 case 0x026d: /* Get_Debug_Flag '/m' */
57 case 0x026e: /* Get_Debug_Flag '/n' */
63 VXD_BARF( context, "VMM" );
67 /***********************************************************************
70 void WINAPI VXD_PageFile( CONTEXT *context )
72 unsigned service = AX_reg(context);
74 /* taken from Ralf Brown's Interrupt List */
76 TRACE(vxd,"[%04x] PageFile\n", (UINT16)service );
80 case 0x00: /* get version, is this windows version? */
81 TRACE(vxd,"returning version\n");
82 AX_reg(context) = VXD_WinVersion();
86 case 0x01: /* get swap file info */
87 TRACE(vxd,"VxD PageFile: returning swap file info\n");
88 AX_reg(context) = 0x00; /* paging disabled */
89 ECX_reg(context) = 0; /* maximum size of paging file */
90 /* FIXME: do I touch DS:SI or DS:DI? */
94 case 0x02: /* delete permanent swap on exit */
95 TRACE(vxd,"VxD PageFile: supposed to delete swap\n");
99 case 0x03: /* current temporary swap file size */
100 TRACE(vxd,"VxD PageFile: what is current temp. swap size\n");
101 RESET_CFLAG(context);
104 case 0x04: /* read or write?? INTERRUP.D */
105 case 0x05: /* cancel?? INTERRUP.D */
106 case 0x06: /* test I/O valid INTERRUP.D */
108 VXD_BARF( context, "pagefile" );
113 /***********************************************************************
116 void VXD_Reboot ( CONTEXT *context )
118 unsigned service = AX_reg(context);
120 TRACE(vxd,"[%04x] VMM \n", (UINT16)service);
124 case 0x0000: /* version */
125 AX_reg(context) = VXD_WinVersion();
126 RESET_CFLAG(context);
130 VXD_BARF( context, "REBOOT" );
134 /***********************************************************************
137 void VXD_VDD ( CONTEXT *context )
139 unsigned service = AX_reg(context);
141 TRACE(vxd,"[%04x] VDD \n", (UINT16)service);
145 case 0x0000: /* version */
146 AX_reg(context) = VXD_WinVersion();
147 RESET_CFLAG(context);
151 VXD_BARF( context, "VDD" );
155 /***********************************************************************
158 void VXD_VMD ( CONTEXT *context )
160 unsigned service = AX_reg(context);
162 TRACE(vxd,"[%04x] VMD \n", (UINT16)service);
166 case 0x0000: /* version */
167 AX_reg(context) = VXD_WinVersion();
168 RESET_CFLAG(context);
172 VXD_BARF( context, "VMD" );
176 /***********************************************************************
179 void WINAPI VXD_Shell( CONTEXT *context )
181 unsigned service = DX_reg(context);
183 TRACE(vxd,"[%04x] Shell\n", (UINT16)service);
185 switch (service) /* Ralf Brown says EDX, but I use DX instead */
188 TRACE(vxd,"returning version\n");
189 AX_reg(context) = VXD_WinVersion();
190 EBX_reg(context) = 1; /* system VM Handle */
196 /* SHELL_SYSMODAL_Message
197 ebx virtual maschine handle
198 eax message box flags
199 ecx address of message
200 edi address of caption
201 return response in eax
205 ebx virtual maschine handle
206 eax message box flags
207 ecx address of message
208 edi address of caption
210 edx reference data for callback
211 return response in eax
214 VXD_BARF( context, "shell" );
217 case 0x0006: /* SHELL_Get_VM_State */
218 TRACE(vxd,"VxD Shell: returning VM state\n");
219 /* Actually we don't, not yet. We have to return a structure
220 * and I am not to sure how to set it up and return it yet,
221 * so for now let's do nothing. I can (hopefully) get this
222 * by the next release
224 /* RESET_CFLAG(context); */
243 VXD_BARF( context, "SHELL" );
246 /* the new Win95 shell API */
247 case 0x0100: /* get version */
248 AX_reg(context) = VXD_WinVersion();
251 case 0x0104: /* retrieve Hook_Properties list */
252 case 0x0105: /* call Hook_Properties callbacks */
253 VXD_BARF( context, "SHELL" );
256 case 0x0106: /* install timeout callback */
257 TRACE( vxd, "VxD Shell: ignoring shell callback (%ld sec.)\n",
258 EBX_reg( context ) );
262 case 0x0107: /* get version of any VxD */
264 VXD_BARF( context, "SHELL" );
270 /***********************************************************************
273 void WINAPI VXD_Comm( CONTEXT *context )
275 unsigned service = AX_reg(context);
277 TRACE(vxd,"[%04x] Comm\n", (UINT16)service);
281 case 0x0000: /* get version */
282 TRACE(vxd,"returning version\n");
283 AX_reg(context) = VXD_WinVersion();
284 RESET_CFLAG(context);
287 case 0x0001: /* set port global */
288 case 0x0002: /* get focus */
289 case 0x0003: /* virtualise port */
291 VXD_BARF( context, "comm" );
295 /***********************************************************************
298 void VXD_Timer( CONTEXT *context )
300 unsigned service = AX_reg(context);
302 TRACE(vxd,"[%04x] Virtual Timer\n", (UINT16)service);
306 case 0x0000: /* version */
307 AX_reg(context) = VXD_WinVersion();
308 RESET_CFLAG(context);
311 case 0x0100: /* clock tick time, in 840nsecs */
312 EAX_reg(context) = GetTickCount();
314 EDX_reg(context) = EAX_reg(context) >> 22;
315 EAX_reg(context) <<= 10; /* not very precise */
318 case 0x0101: /* current Windows time, msecs */
319 case 0x0102: /* current VM time, msecs */
320 EAX_reg(context) = GetTickCount();
324 VXD_BARF( context, "VTD" );
328 /***********************************************************************
331 static DWORD System_Time = 0;
332 static WORD System_Time_Selector = 0;
333 static void System_Time_Tick( WORD timer ) { System_Time += 55; }
334 void VXD_TimerAPI ( CONTEXT *context )
336 unsigned service = AX_reg(context);
338 TRACE(vxd,"[%04x] TimerAPI \n", (UINT16)service);
342 case 0x0000: /* version */
343 AX_reg(context) = VXD_WinVersion();
344 RESET_CFLAG(context);
347 case 0x0009: /* get system time selector */
348 if ( !System_Time_Selector )
350 System_Time_Selector = SELECTOR_AllocBlock( &System_Time, sizeof(DWORD),
351 SEGMENT_DATA, FALSE, TRUE );
352 CreateSystemTimer( 55, System_Time_Tick );
355 AX_reg(context) = System_Time_Selector;
356 RESET_CFLAG(context);
360 VXD_BARF( context, "VTDAPI" );
364 /***********************************************************************
367 void VXD_ConfigMG ( CONTEXT *context )
369 unsigned service = AX_reg(context);
371 TRACE(vxd,"[%04x] ConfigMG \n", (UINT16)service);
375 case 0x0000: /* version */
376 AX_reg(context) = VXD_WinVersion();
377 RESET_CFLAG(context);
381 VXD_BARF( context, "CONFIGMG" );
385 /***********************************************************************
388 void VXD_Enable ( CONTEXT *context )
390 unsigned service = AX_reg(context);
392 TRACE(vxd,"[%04x] Enable \n", (UINT16)service);
396 case 0x0000: /* version */
397 AX_reg(context) = VXD_WinVersion();
398 RESET_CFLAG(context);
402 VXD_BARF( context, "ENABLE" );
406 /***********************************************************************
409 void VXD_APM ( CONTEXT *context )
411 unsigned service = AX_reg(context);
413 TRACE(vxd,"[%04x] APM \n", (UINT16)service);
417 case 0x0000: /* version */
418 AX_reg(context) = VXD_WinVersion();
419 RESET_CFLAG(context);
423 VXD_BARF( context, "APM" );
427 /***********************************************************************
430 * This is an implementation of the services of the Win32s VxD.
431 * Since official documentation of these does not seem to be available,
432 * certain arguments of some of the services remain unclear.
434 * FIXME: The following services are currently unimplemented:
435 * Exception handling (0x01, 0x1C)
436 * Debugger support (0x0C, 0x14, 0x17)
437 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
438 * Memory Statistics (0x1B)
441 * We have a specific problem running Win32s on Linux (and probably also
442 * the other x86 unixes), since Win32s tries to allocate its main 'flat
443 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
444 * The rationale for this seems to be that they want one the one hand to
445 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
446 * at linear address 0, but want at other hand to have offset 0 of the
447 * flat data/code segment point to an unmapped page (to catch NULL pointer
448 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
449 * so that the Win 3.1 memory area at linear address zero shows up in the
450 * flat segments at offset 0x10000 (since linear addresses wrap around at
451 * 4GB). To compensate for that discrepancy between flat segment offsets
452 * and plain linear addresses, all flat pointers passed between the 32-bit
453 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
454 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
456 * The problem for us is now that Linux does not allow a LDT selector with
457 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
458 * address space. To address this problem we introduce *another* offset:
459 * We add 0x10000 to every linear address we get as an argument from Win32s.
460 * This means especially that the flat code/data selectors get actually
461 * allocated with base 0x0, so that flat offsets and (real) linear addresses
462 * do again agree! In fact, every call e.g. of a Win32s VxD service now
463 * has all pointer arguments (which are offsets in the flat data segement)
464 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
465 * increased by 0x10000 by *our* code.
467 * Note that to keep everything consistent, this offset has to be applied by
468 * every Wine function that operates on 'linear addresses' passed to it by
469 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
470 * API routines, this affects only two locations: this VxD and the DPMI
471 * handler. (NOTE: Should any Win32s application pass a linear address to
472 * any routine apart from those, e.g. some other VxD handler, that code
473 * would have to take the offset into account as well!)
475 * The application of the offset is triggered by marking the current process
476 * as a Win32s process by setting the PDB32_WIN32S_PROC flag in the process
477 * database. This is done the first time any application calls the GetVersion()
478 * service of the Win32s VxD. (Note that the flag is never removed.)
482 void VXD_Win32s( CONTEXT *context )
484 switch (AX_reg(context))
486 case 0x0000: /* Get Version */
490 * Output: EAX: LoWord: Win32s Version (1.30)
491 * HiWord: VxD Version (200)
497 * EDX: Debugging Flags
501 * 1 if VMCPD VxD not found
504 TRACE(vxd, "GetVersion()\n");
506 EAX_reg(context) = VXD_WinVersion() | (200 << 16);
507 EBX_reg(context) = 0;
508 ECX_reg(context) = 0;
509 EDX_reg(context) = 0;
510 EDI_reg(context) = 0;
513 * If this is the first time we are called for this process,
514 * hack the memory image of WIN32S16 so that it doesn't try
515 * to access the GDT directly ...
517 * The first code segment of WIN32S16 (version 1.30) contains
518 * an unexported function somewhere between the exported functions
519 * SetFS and StackLinearToSegmented that tries to find a selector
520 * in the LDT that maps to the memory image of the LDT itself.
521 * If it succeeds, it stores this selector into a global variable
522 * which will be used to speed up execution by using this selector
523 * to modify the LDT directly instead of using the DPMI calls.
525 * To perform this search of the LDT, this function uses the
526 * sgdt and sldt instructions to find the linear address of
527 * the (GDT and then) LDT. While those instructions themselves
528 * execute without problem, the linear address that sgdt returns
529 * points (at least under Linux) to the kernel address space, so
530 * that any subsequent access leads to a segfault.
532 * Fortunately, WIN32S16 still contains as a fallback option the
533 * mechanism of using DPMI calls to modify LDT selectors instead
534 * of direct writes to the LDT. Thus we can circumvent the problem
535 * by simply replacing the first byte of the offending function
536 * with an 'retf' instruction. This means that the global variable
537 * supposed to contain the LDT alias selector will remain zero,
538 * and hence WIN32S16 will fall back to using DPMI calls.
540 * The heuristic we employ to _find_ that function is as follows:
541 * We search between the addresses of the exported symbols SetFS
542 * and StackLinearToSegmented for the byte sequence '0F 01 04'
543 * (this is the opcode of 'sgdt [si]'). We then search backwards
544 * from this address for the last occurrance of 'CB' (retf) that marks
545 * the end of the preceeding function. The following byte (which
546 * should now be the first byte of the function we are looking for)
547 * will be replaced by 'CB' (retf).
549 * This heuristic works for the retail as well as the debug version
550 * of Win32s version 1.30. For versions earlier than that this
551 * hack should not be necessary at all, since the whole mechanism
552 * ('PERF130') was introduced only in 1.30 to improve the overall
553 * performance of Win32s.
556 if (!(PROCESS_Current()->flags & PDB32_WIN32S_PROC))
558 HMODULE16 hModule = GetModuleHandle16("win32s16");
559 SEGPTR func1 = (SEGPTR)WIN32_GetProcAddress16(hModule, "SetFS");
560 SEGPTR func2 = (SEGPTR)WIN32_GetProcAddress16(hModule,
561 "StackLinearToSegmented");
563 if ( hModule && func1 && func2
564 && SELECTOROF(func1) == SELECTOROF(func2))
566 BYTE *start = PTR_SEG_TO_LIN(func1);
567 BYTE *end = PTR_SEG_TO_LIN(func2);
568 BYTE *p, *retv = NULL;
571 for (p = start; p < end; p++)
572 if (*p == 0xCB) found = 0, retv = p;
573 else if (*p == 0x0F) found = 1;
574 else if (*p == 0x01 && found == 1) found = 2;
575 else if (*p == 0x04 && found == 2) { found = 3; break; }
578 if (found == 3 && retv)
580 TRACE(vxd, "PERF130 hack: "
581 "Replacing byte %02X at offset %04X:%04X\n",
582 *(retv+1), SELECTOROF(func1),
583 OFFSETOF(func1) + retv+1-start);
585 *(retv+1) = (BYTE)0xCB;
591 * Mark process as Win32s, so that subsequent DPMI calls
592 * will perform the W32S_APP2WINE/W32S_WINE2APP address shift.
595 PROCESS_Current()->flags |= PDB32_WIN32S_PROC;
599 case 0x0001: /* Install Exception Handling */
601 * Input: EBX: Flat address of W32SKRNL Exception Data
603 * ECX: LoWord: Flat Code Selector
604 * HiWord: Flat Data Selector
606 * EDX: Flat address of W32SKRNL Exception Handler
607 * (this is equal to W32S_BackTo32 + 0x40)
609 * ESI: SEGPTR KERNEL.HASGPHANDLER
611 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
613 * Output: EAX: 0 if OK
616 TRACE(vxd, "[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
617 EBX_reg(context), ECX_reg(context), EDX_reg(context),
618 ESI_reg(context), EDI_reg(context));
622 EAX_reg(context) = 0;
626 case 0x0002: /* Set Page Access Flags */
628 * Input: EBX: New access flags
629 * Bit 2: User Page if set, Supervisor Page if clear
630 * Bit 1: Read-Write if set, Read-Only if clear
632 * ECX: Size of memory area to change
634 * EDX: Flat start address of memory area
636 * Output: EAX: Size of area changed
639 TRACE(vxd, "[0002] EBX=%lx ECX=%lx EDX=%lx\n",
640 EBX_reg(context), ECX_reg(context), EDX_reg(context));
644 EAX_reg(context) = ECX_reg(context);
648 case 0x0003: /* Get Page Access Flags */
650 * Input: EDX: Flat address of page to query
652 * Output: EAX: Page access flags
653 * Bit 2: User Page if set, Supervisor Page if clear
654 * Bit 1: Read-Write if set, Read-Only if clear
657 TRACE(vxd, "[0003] EDX=%lx\n", EDX_reg(context));
661 EAX_reg(context) = 6;
665 case 0x0004: /* Map Module */
667 * Input: ECX: IMTE (offset in Module Table) of new module
669 * EDX: Flat address of Win32s Module Table
671 * Output: EAX: 0 if OK
674 if (!EDX_reg(context) || CX_reg(context) == 0xFFFF)
676 TRACE(vxd, "MapModule: Initialization call\n");
677 EAX_reg(context) = 0;
682 * Structure of a Win32s Module Table Entry:
697 * Note: This function should set up a demand-paged memory image
698 * of the given module. Since mmap does not allow file offsets
699 * not aligned at 1024 bytes, we simply load the image fully
703 struct Win32sModule *moduleTable =
704 (struct Win32sModule *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
705 struct Win32sModule *module = moduleTable + ECX_reg(context);
707 IMAGE_NT_HEADERS *nt_header = PE_HEADER(module->baseAddr);
708 IMAGE_SECTION_HEADER *pe_seg = PE_SECTIONS(module->baseAddr);
710 HFILE image = _lopen(module->pathName, OF_READ);
711 BOOL error = (image == INVALID_HANDLE_VALUE);
714 TRACE(vxd, "MapModule: Loading %s\n", module->pathName);
717 !error && i < nt_header->FileHeader.NumberOfSections;
719 if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
721 DWORD off = pe_seg->PointerToRawData;
722 DWORD len = pe_seg->SizeOfRawData;
723 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
725 TRACE(vxd, "MapModule: "
726 "Section %d at %08lx from %08lx len %08lx\n",
727 i, (DWORD)addr, off, len);
729 if ( _llseek(image, off, SEEK_SET) != off
730 || _lread(image, addr, len) != len)
737 ERR(vxd, "MapModule: Unable to load %s\n", module->pathName);
739 else if (module->relocDelta != 0)
741 IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
742 + IMAGE_DIRECTORY_ENTRY_BASERELOC;
743 IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
744 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
746 TRACE(vxd, "MapModule: Reloc delta %08lx\n", module->relocDelta);
748 while (r && r->VirtualAddress)
750 LPBYTE page = module->baseAddr + r->VirtualAddress;
751 int count = (r->SizeOfBlock - 8) / 2;
753 TRACE(vxd, "MapModule: %d relocations for page %08lx\n",
756 for(i = 0; i < count; i++)
758 int offset = r->TypeOffset[i] & 0xFFF;
759 int type = r->TypeOffset[i] >> 12;
762 case IMAGE_REL_BASED_ABSOLUTE:
764 case IMAGE_REL_BASED_HIGH:
765 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
767 case IMAGE_REL_BASED_LOW:
768 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
770 case IMAGE_REL_BASED_HIGHLOW:
771 *(DWORD*)(page+offset) += module->relocDelta;
774 WARN(vxd, "MapModule: Unsupported fixup type\n");
779 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
783 EAX_reg(context) = 0;
784 RESET_CFLAG(context);
789 case 0x0005: /* UnMap Module */
791 * Input: EDX: Flat address of module image
793 * Output: EAX: 1 if OK
796 TRACE(vxd, "UnMapModule: %lx\n", (DWORD)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET));
798 /* As we didn't map anything, there's nothing to unmap ... */
800 EAX_reg(context) = 1;
804 case 0x0006: /* VirtualAlloc */
806 * Input: ECX: Current Process
808 * EDX: Flat address of arguments on stack
810 * DWORD *retv [out] Flat base address of allocated region
811 * LPVOID base [in] Flat address of region to reserve/commit
812 * DWORD size [in] Size of region
813 * DWORD type [in] Type of allocation
814 * DWORD prot [in] Type of access protection
816 * Output: EAX: NtStatus
819 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
820 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
821 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
822 DWORD size = stack[2];
823 DWORD type = stack[3];
824 DWORD prot = stack[4];
827 TRACE(vxd, "VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
828 (DWORD)retv, (DWORD)base, size, type, prot);
830 if (type & 0x80000000)
832 WARN(vxd, "VirtualAlloc: strange type %lx\n", type);
836 if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
838 WARN(vxd, "VirtualAlloc: NLS hack, allowing write access!\n");
839 prot = PAGE_READWRITE;
842 result = (DWORD)VirtualAlloc(base, size, type, prot);
844 if (W32S_WINE2APP(result, W32S_OFFSET))
845 *retv = W32S_WINE2APP(result, W32S_OFFSET),
846 EAX_reg(context) = STATUS_SUCCESS;
849 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
854 case 0x0007: /* VirtualFree */
856 * Input: ECX: Current Process
858 * EDX: Flat address of arguments on stack
860 * DWORD *retv [out] TRUE if success, FALSE if failure
861 * LPVOID base [in] Flat address of region
862 * DWORD size [in] Size of region
863 * DWORD type [in] Type of operation
865 * Output: EAX: NtStatus
868 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
869 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
870 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
871 DWORD size = stack[2];
872 DWORD type = stack[3];
875 TRACE(vxd, "VirtualFree(%lx, %lx, %lx, %lx)\n",
876 (DWORD)retv, (DWORD)base, size, type);
878 result = VirtualFree(base, size, type);
882 EAX_reg(context) = STATUS_SUCCESS;
885 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
890 case 0x0008: /* VirtualProtect */
892 * Input: ECX: Current Process
894 * EDX: Flat address of arguments on stack
896 * DWORD *retv [out] TRUE if success, FALSE if failure
897 * LPVOID base [in] Flat address of region
898 * DWORD size [in] Size of region
899 * DWORD new_prot [in] Desired access protection
900 * DWORD *old_prot [out] Previous access protection
902 * Output: EAX: NtStatus
905 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
906 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
907 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
908 DWORD size = stack[2];
909 DWORD new_prot = stack[3];
910 DWORD *old_prot = (DWORD *)W32S_APP2WINE(stack[4], W32S_OFFSET);
913 TRACE(vxd, "VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
914 (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
916 result = VirtualProtect(base, size, new_prot, old_prot);
920 EAX_reg(context) = STATUS_SUCCESS;
923 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
928 case 0x0009: /* VirtualQuery */
930 * Input: ECX: Current Process
932 * EDX: Flat address of arguments on stack
934 * DWORD *retv [out] Nr. bytes returned
935 * LPVOID base [in] Flat address of region
936 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
937 * DWORD len [in] Size of buffer
939 * Output: EAX: NtStatus
942 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
943 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
944 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
945 LPMEMORY_BASIC_INFORMATION info =
946 (LPMEMORY_BASIC_INFORMATION)W32S_APP2WINE(stack[2], W32S_OFFSET);
947 DWORD len = stack[3];
950 TRACE(vxd, "VirtualQuery(%lx, %lx, %lx, %lx)\n",
951 (DWORD)retv, (DWORD)base, (DWORD)info, len);
953 result = VirtualQuery(base, info, len);
956 EAX_reg(context) = STATUS_SUCCESS;
961 case 0x000A: /* SetVirtMemProcess */
963 * Input: ECX: Process Handle
965 * EDX: Flat address of region
967 * Output: EAX: NtStatus
970 TRACE(vxd, "[000a] ECX=%lx EDX=%lx\n",
971 ECX_reg(context), EDX_reg(context));
975 EAX_reg(context) = STATUS_SUCCESS;
979 case 0x000B: /* ??? some kind of cleanup */
981 * Input: ECX: Process Handle
983 * Output: EAX: NtStatus
986 TRACE(vxd, "[000b] ECX=%lx\n", ECX_reg(context));
990 EAX_reg(context) = STATUS_SUCCESS;
994 case 0x000C: /* Set Debug Flags */
996 * Input: EDX: Debug Flags
998 * Output: EDX: Previous Debug Flags
1001 FIXME(vxd, "[000c] EDX=%lx\n", EDX_reg(context));
1005 EDX_reg(context) = 0;
1009 case 0x000D: /* NtCreateSection */
1011 * Input: EDX: Flat address of arguments on stack
1013 * HANDLE32 *retv [out] Handle of Section created
1014 * DWORD flags1 [in] (?? unknown ??)
1015 * DWORD atom [in] Name of Section to create
1016 * LARGE_INTEGER *size [in] Size of Section
1017 * DWORD protect [in] Access protection
1018 * DWORD flags2 [in] (?? unknown ??)
1019 * HFILE32 hFile [in] Handle of file to map
1020 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
1022 * Output: EAX: NtStatus
1025 DWORD *stack = (DWORD *) W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1026 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0], W32S_OFFSET);
1027 DWORD flags1 = stack[1];
1028 DWORD atom = stack[2];
1029 LARGE_INTEGER *size = (LARGE_INTEGER *)W32S_APP2WINE(stack[3], W32S_OFFSET);
1030 DWORD protect = stack[4];
1031 DWORD flags2 = stack[5];
1032 HFILE hFile = FILE_GetHandle(stack[6]);
1033 DWORD psp = stack[7];
1035 HANDLE result = INVALID_HANDLE_VALUE;
1038 TRACE(vxd, "NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1039 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
1042 if (!atom || GlobalGetAtomNameA(atom, name, sizeof(name)))
1044 TRACE(vxd, "NtCreateSection: name=%s\n", atom? name : NULL);
1046 result = CreateFileMappingA(hFile, NULL, protect,
1047 size? size->HighPart : 0,
1048 size? size->LowPart : 0,
1052 if (result == INVALID_HANDLE_VALUE)
1053 WARN(vxd, "NtCreateSection: failed!\n");
1055 TRACE(vxd, "NtCreateSection: returned %lx\n", (DWORD)result);
1057 if (result != INVALID_HANDLE_VALUE)
1059 EAX_reg(context) = STATUS_SUCCESS;
1062 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1067 case 0x000E: /* NtOpenSection */
1069 * Input: EDX: Flat address of arguments on stack
1071 * HANDLE32 *retv [out] Handle of Section opened
1072 * DWORD protect [in] Access protection
1073 * DWORD atom [in] Name of Section to create
1075 * Output: EAX: NtStatus
1078 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1079 HANDLE *retv = (HANDLE *)W32S_APP2WINE(stack[0], W32S_OFFSET);
1080 DWORD protect = stack[1];
1081 DWORD atom = stack[2];
1083 HANDLE result = INVALID_HANDLE_VALUE;
1086 TRACE(vxd, "NtOpenSection(%lx, %lx, %lx)\n",
1087 (DWORD)retv, protect, atom);
1089 if (atom && GlobalGetAtomNameA(atom, name, sizeof(name)))
1091 TRACE(vxd, "NtOpenSection: name=%s\n", name);
1093 result = OpenFileMappingA(protect, FALSE, name);
1096 if (result == INVALID_HANDLE_VALUE)
1097 WARN(vxd, "NtOpenSection: failed!\n");
1099 TRACE(vxd, "NtOpenSection: returned %lx\n", (DWORD)result);
1101 if (result != INVALID_HANDLE_VALUE)
1103 EAX_reg(context) = STATUS_SUCCESS;
1106 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1111 case 0x000F: /* NtCloseSection */
1113 * Input: EDX: Flat address of arguments on stack
1115 * HANDLE32 handle [in] Handle of Section to close
1116 * DWORD *id [out] Unique ID (?? unclear ??)
1118 * Output: EAX: NtStatus
1121 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1122 HANDLE handle = stack[0];
1123 DWORD *id = (DWORD *)W32S_APP2WINE(stack[1], W32S_OFFSET);
1125 TRACE(vxd, "NtCloseSection(%lx, %lx)\n", (DWORD)handle, (DWORD)id);
1127 CloseHandle(handle);
1128 if (id) *id = 0; /* FIXME */
1130 EAX_reg(context) = STATUS_SUCCESS;
1135 case 0x0010: /* NtDupSection */
1137 * Input: EDX: Flat address of arguments on stack
1139 * HANDLE32 handle [in] Handle of Section to duplicate
1141 * Output: EAX: NtStatus
1144 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1145 HANDLE handle = stack[0];
1148 TRACE(vxd, "NtDupSection(%lx)\n", (DWORD)handle);
1150 DuplicateHandle( GetCurrentProcess(), handle,
1151 GetCurrentProcess(), &new_handle,
1152 0, FALSE, DUPLICATE_SAME_ACCESS );
1153 EAX_reg(context) = STATUS_SUCCESS;
1158 case 0x0011: /* NtMapViewOfSection */
1160 * Input: EDX: Flat address of arguments on stack
1162 * HANDLE32 SectionHandle [in] Section to be mapped
1163 * DWORD ProcessHandle [in] Process to be mapped into
1164 * DWORD * BaseAddress [in/out] Address to be mapped at
1165 * DWORD ZeroBits [in] (?? unclear ??)
1166 * DWORD CommitSize [in] (?? unclear ??)
1167 * LARGE_INTEGER *SectionOffset [in] Offset within section
1168 * DWORD * ViewSize [in] Size of view
1169 * DWORD InheritDisposition [in] (?? unclear ??)
1170 * DWORD AllocationType [in] (?? unclear ??)
1171 * DWORD Protect [in] Access protection
1173 * Output: EAX: NtStatus
1176 DWORD * stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1177 HANDLE SectionHandle = stack[0];
1178 DWORD ProcessHandle = stack[1]; /* ignored */
1179 DWORD * BaseAddress = (DWORD *)W32S_APP2WINE(stack[2], W32S_OFFSET);
1180 DWORD ZeroBits = stack[3];
1181 DWORD CommitSize = stack[4];
1182 LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)W32S_APP2WINE(stack[5], W32S_OFFSET);
1183 DWORD * ViewSize = (DWORD *)W32S_APP2WINE(stack[6], W32S_OFFSET);
1184 DWORD InheritDisposition = stack[7];
1185 DWORD AllocationType = stack[8];
1186 DWORD Protect = stack[9];
1188 LPBYTE address = (LPBYTE)(BaseAddress?
1189 W32S_APP2WINE(*BaseAddress, W32S_OFFSET) : 0);
1190 DWORD access = 0, result;
1192 switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1194 case PAGE_READONLY: access = FILE_MAP_READ; break;
1195 case PAGE_READWRITE: access = FILE_MAP_WRITE; break;
1196 case PAGE_WRITECOPY: access = FILE_MAP_COPY; break;
1198 case PAGE_EXECUTE_READ: access = FILE_MAP_READ; break;
1199 case PAGE_EXECUTE_READWRITE: access = FILE_MAP_WRITE; break;
1200 case PAGE_EXECUTE_WRITECOPY: access = FILE_MAP_COPY; break;
1203 TRACE(vxd, "NtMapViewOfSection"
1204 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1205 (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1206 ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1207 InheritDisposition, AllocationType, Protect);
1208 TRACE(vxd, "NtMapViewOfSection: "
1209 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1210 (DWORD)address, SectionOffset? SectionOffset->LowPart : 0,
1211 ViewSize? *ViewSize : 0, access);
1213 result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1214 SectionOffset? SectionOffset->HighPart : 0,
1215 SectionOffset? SectionOffset->LowPart : 0,
1216 ViewSize? *ViewSize : 0, address);
1218 TRACE(vxd, "NtMapViewOfSection: result=%lx\n", result);
1220 if (W32S_WINE2APP(result, W32S_OFFSET))
1222 if (BaseAddress) *BaseAddress = W32S_WINE2APP(result, W32S_OFFSET);
1223 EAX_reg(context) = STATUS_SUCCESS;
1226 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1231 case 0x0012: /* NtUnmapViewOfSection */
1233 * Input: EDX: Flat address of arguments on stack
1235 * DWORD ProcessHandle [in] Process (defining address space)
1236 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1238 * Output: EAX: NtStatus
1241 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1242 DWORD ProcessHandle = stack[0]; /* ignored */
1243 LPBYTE BaseAddress = (LPBYTE)W32S_APP2WINE(stack[1], W32S_OFFSET);
1245 TRACE(vxd, "NtUnmapViewOfSection(%lx, %lx)\n",
1246 ProcessHandle, (DWORD)BaseAddress);
1248 UnmapViewOfFile(BaseAddress);
1250 EAX_reg(context) = STATUS_SUCCESS;
1255 case 0x0013: /* NtFlushVirtualMemory */
1257 * Input: EDX: Flat address of arguments on stack
1259 * DWORD ProcessHandle [in] Process (defining address space)
1260 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1261 * DWORD *ViewSize [in?] Number of bytes to be flushed
1262 * DWORD *unknown [???] (?? unknown ??)
1264 * Output: EAX: NtStatus
1267 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1268 DWORD ProcessHandle = stack[0]; /* ignored */
1269 DWORD *BaseAddress = (DWORD *)W32S_APP2WINE(stack[1], W32S_OFFSET);
1270 DWORD *ViewSize = (DWORD *)W32S_APP2WINE(stack[2], W32S_OFFSET);
1271 DWORD *unknown = (DWORD *)W32S_APP2WINE(stack[3], W32S_OFFSET);
1273 LPBYTE address = (LPBYTE)(BaseAddress? W32S_APP2WINE(*BaseAddress, W32S_OFFSET) : 0);
1274 DWORD size = ViewSize? *ViewSize : 0;
1276 TRACE(vxd, "NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1277 ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1279 TRACE(vxd, "NtFlushVirtualMemory: base=%lx, size=%lx\n",
1280 (DWORD)address, size);
1282 FlushViewOfFile(address, size);
1284 EAX_reg(context) = STATUS_SUCCESS;
1289 case 0x0014: /* Get/Set Debug Registers */
1291 * Input: ECX: 0 if Get, 1 if Set
1293 * EDX: Get: Flat address of buffer to receive values of
1294 * debug registers DR0 .. DR7
1295 * Set: Flat address of buffer containing values of
1296 * debug registers DR0 .. DR7 to be set
1300 FIXME(vxd, "[0014] ECX=%lx EDX=%lx\n",
1301 ECX_reg(context), EDX_reg(context));
1307 case 0x0015: /* Set Coprocessor Emulation Flag */
1309 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1314 TRACE(vxd, "[0015] EDX=%lx\n", EDX_reg(context));
1316 /* We don't care, as we always have a coprocessor anyway */
1320 case 0x0016: /* Init Win32S VxD PSP */
1322 * If called to query required PSP size:
1325 * Output: EDX: Required size of Win32s VxD PSP
1327 * If called to initialize allocated PSP:
1329 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1330 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1334 if (EBX_reg(context) == 0)
1335 EDX_reg(context) = 0x80;
1338 PDB16 *psp = PTR_SEG_OFF_TO_LIN(BX_reg(context), 0);
1340 psp->fileHandlesPtr = MAKELONG(HIWORD(EBX_reg(context)), 0x5c);
1341 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1346 case 0x0017: /* Set Break Point */
1348 * Input: EBX: Offset of Break Point
1349 * CX: Selector of Break Point
1354 FIXME(vxd, "[0017] EBX=%lx CX=%x\n",
1355 EBX_reg(context), CX_reg(context));
1361 case 0x0018: /* VirtualLock */
1363 * Input: ECX: Current Process
1365 * EDX: Flat address of arguments on stack
1367 * DWORD *retv [out] TRUE if success, FALSE if failure
1368 * LPVOID base [in] Flat address of range to lock
1369 * DWORD size [in] Size of range
1371 * Output: EAX: NtStatus
1374 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1375 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
1376 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
1377 DWORD size = stack[2];
1380 TRACE(vxd, "VirtualLock(%lx, %lx, %lx)\n",
1381 (DWORD)retv, (DWORD)base, size);
1383 result = VirtualLock(base, size);
1387 EAX_reg(context) = STATUS_SUCCESS;
1390 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1395 case 0x0019: /* VirtualUnlock */
1397 * Input: ECX: Current Process
1399 * EDX: Flat address of arguments on stack
1401 * DWORD *retv [out] TRUE if success, FALSE if failure
1402 * LPVOID base [in] Flat address of range to unlock
1403 * DWORD size [in] Size of range
1405 * Output: EAX: NtStatus
1408 DWORD *stack = (DWORD *)W32S_APP2WINE(EDX_reg(context), W32S_OFFSET);
1409 DWORD *retv = (DWORD *)W32S_APP2WINE(stack[0], W32S_OFFSET);
1410 LPVOID base = (LPVOID) W32S_APP2WINE(stack[1], W32S_OFFSET);
1411 DWORD size = stack[2];
1414 TRACE(vxd, "VirtualUnlock(%lx, %lx, %lx)\n",
1415 (DWORD)retv, (DWORD)base, size);
1417 result = VirtualUnlock(base, size);
1421 EAX_reg(context) = STATUS_SUCCESS;
1424 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1429 case 0x001A: /* KGetSystemInfo */
1433 * Output: ECX: Start of sparse memory arena
1434 * EDX: End of sparse memory arena
1437 TRACE(vxd, "KGetSystemInfo()\n");
1440 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1441 * sparse memory arena. We do it the other way around, since
1442 * we have to reserve 3GB - 4GB for Linux, and thus use
1443 * 0GB - 3GB as sparse memory arena.
1445 * FIXME: What about other OSes ?
1448 ECX_reg(context) = W32S_WINE2APP(0x00000000, W32S_OFFSET);
1449 EDX_reg(context) = W32S_WINE2APP(0xbfffffff, W32S_OFFSET);
1453 case 0x001B: /* KGlobalMemStat */
1455 * Input: ESI: Flat address of buffer to receive memory info
1460 struct Win32sMemoryInfo
1462 DWORD DIPhys_Count; /* Total physical pages */
1463 DWORD DIFree_Count; /* Free physical pages */
1464 DWORD DILin_Total_Count; /* Total virtual pages (private arena) */
1465 DWORD DILin_Total_Free; /* Free virtual pages (private arena) */
1467 DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */
1468 DWORD SparseFree; /* Free size of sparse arena (bytes ?) */
1471 struct Win32sMemoryInfo *info =
1472 (struct Win32sMemoryInfo *)W32S_APP2WINE(ESI_reg(context), W32S_OFFSET);
1474 FIXME(vxd, "KGlobalMemStat(%lx)\n", (DWORD)info);
1481 case 0x001C: /* Enable/Disable Exceptions */
1483 * Input: ECX: 0 to disable, 1 to enable exception handling
1488 TRACE(vxd, "[001c] ECX=%lx\n", ECX_reg(context));
1494 case 0x001D: /* VirtualAlloc called from 16-bit code */
1496 * Input: EDX: Segmented address of arguments on stack
1498 * LPVOID base [in] Flat address of region to reserve/commit
1499 * DWORD size [in] Size of region
1500 * DWORD type [in] Type of allocation
1501 * DWORD prot [in] Type of access protection
1503 * Output: EAX: NtStatus
1504 * EDX: Flat base address of allocated region
1507 DWORD *stack = PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context)),
1508 HIWORD(EDX_reg(context)));
1509 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0], W32S_OFFSET);
1510 DWORD size = stack[1];
1511 DWORD type = stack[2];
1512 DWORD prot = stack[3];
1515 TRACE(vxd, "VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1516 (DWORD)base, size, type, prot);
1518 if (type & 0x80000000)
1520 WARN(vxd, "VirtualAlloc16: strange type %lx\n", type);
1524 result = (DWORD)VirtualAlloc(base, size, type, prot);
1526 if (W32S_WINE2APP(result, W32S_OFFSET))
1527 EDX_reg(context) = W32S_WINE2APP(result, W32S_OFFSET),
1528 EAX_reg(context) = STATUS_SUCCESS;
1530 EDX_reg(context) = 0,
1531 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1532 TRACE(vxd, "VirtualAlloc16: returning base %lx\n", EDX_reg(context));
1537 case 0x001E: /* VirtualFree called from 16-bit code */
1539 * Input: EDX: Segmented address of arguments on stack
1541 * LPVOID base [in] Flat address of region
1542 * DWORD size [in] Size of region
1543 * DWORD type [in] Type of operation
1545 * Output: EAX: NtStatus
1546 * EDX: TRUE if success, FALSE if failure
1549 DWORD *stack = PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context)),
1550 HIWORD(EDX_reg(context)));
1551 LPVOID base = (LPVOID)W32S_APP2WINE(stack[0], W32S_OFFSET);
1552 DWORD size = stack[1];
1553 DWORD type = stack[2];
1556 TRACE(vxd, "VirtualFree16(%lx, %lx, %lx)\n",
1557 (DWORD)base, size, type);
1559 result = VirtualFree(base, size, type);
1562 EDX_reg(context) = TRUE,
1563 EAX_reg(context) = STATUS_SUCCESS;
1565 EDX_reg(context) = FALSE,
1566 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1571 case 0x001F: /* FWorkingSetSize */
1573 * Input: EDX: 0 if Get, 1 if Set
1575 * ECX: Get: Buffer to receive Working Set Size
1576 * Set: Buffer containing Working Set Size
1581 DWORD *ptr = (DWORD *)W32S_APP2WINE(ECX_reg(context), W32S_OFFSET);
1582 BOOL set = EDX_reg(context);
1584 TRACE(vxd, "FWorkingSetSize(%lx, %lx)\n", (DWORD)ptr, (DWORD)set);
1587 /* We do it differently ... */;
1591 EAX_reg(context) = STATUS_SUCCESS;
1597 VXD_BARF( context, "W32S" );