4 * Copyright 1995 Anand Kumria
13 #include "selectors.h"
21 #define VXD_BARF(context,name) \
22 DUMP( "vxd %s: unknown/not implemented parameters:\n" \
23 "vxd %s: AX %04x, BX %04x, CX %04x, DX %04x, " \
24 "SI %04x, DI %04x, DS %04x, ES %04x\n", \
25 (name), (name), AX_reg(context), BX_reg(context), \
26 CX_reg(context), DX_reg(context), SI_reg(context), \
27 DI_reg(context), (WORD)DS_reg(context), (WORD)ES_reg(context) )
30 static WORD
VXD_WinVersion(void)
32 WORD version
= LOWORD(GetVersion16());
33 return (version
>> 8) | (version
<< 8);
36 /***********************************************************************
39 void VXD_VMM ( CONTEXT
*context
)
41 unsigned service
= AX_reg(context
);
43 TRACE(vxd
,"[%04x] VMM \n", (UINT16
)service
);
47 case 0x0000: /* version */
48 AX_reg(context
) = VXD_WinVersion();
52 case 0x026d: /* Get_Debug_Flag '/m' */
53 case 0x026e: /* Get_Debug_Flag '/n' */
59 VXD_BARF( context
, "VMM" );
63 /***********************************************************************
66 void WINAPI
VXD_PageFile( CONTEXT
*context
)
68 unsigned service
= AX_reg(context
);
70 /* taken from Ralf Brown's Interrupt List */
72 TRACE(vxd
,"[%04x] PageFile\n", (UINT16
)service
);
76 case 0x00: /* get version, is this windows version? */
77 TRACE(vxd
,"returning version\n");
78 AX_reg(context
) = VXD_WinVersion();
82 case 0x01: /* get swap file info */
83 TRACE(vxd
,"VxD PageFile: returning swap file info\n");
84 AX_reg(context
) = 0x00; /* paging disabled */
85 ECX_reg(context
) = 0; /* maximum size of paging file */
86 /* FIXME: do I touch DS:SI or DS:DI? */
90 case 0x02: /* delete permanent swap on exit */
91 TRACE(vxd
,"VxD PageFile: supposed to delete swap\n");
95 case 0x03: /* current temporary swap file size */
96 TRACE(vxd
,"VxD PageFile: what is current temp. swap size\n");
100 case 0x04: /* read or write?? INTERRUP.D */
101 case 0x05: /* cancel?? INTERRUP.D */
102 case 0x06: /* test I/O valid INTERRUP.D */
104 VXD_BARF( context
, "pagefile" );
109 /***********************************************************************
112 void VXD_Reboot ( CONTEXT
*context
)
114 unsigned service
= AX_reg(context
);
116 TRACE(vxd
,"[%04x] VMM \n", (UINT16
)service
);
120 case 0x0000: /* version */
121 AX_reg(context
) = VXD_WinVersion();
122 RESET_CFLAG(context
);
126 VXD_BARF( context
, "REBOOT" );
130 /***********************************************************************
133 void VXD_VDD ( CONTEXT
*context
)
135 unsigned service
= AX_reg(context
);
137 TRACE(vxd
,"[%04x] VDD \n", (UINT16
)service
);
141 case 0x0000: /* version */
142 AX_reg(context
) = VXD_WinVersion();
143 RESET_CFLAG(context
);
147 VXD_BARF( context
, "VDD" );
151 /***********************************************************************
154 void VXD_VMD ( CONTEXT
*context
)
156 unsigned service
= AX_reg(context
);
158 TRACE(vxd
,"[%04x] VMD \n", (UINT16
)service
);
162 case 0x0000: /* version */
163 AX_reg(context
) = VXD_WinVersion();
164 RESET_CFLAG(context
);
168 VXD_BARF( context
, "VMD" );
172 /***********************************************************************
175 void WINAPI
VXD_Shell( CONTEXT
*context
)
177 unsigned service
= DX_reg(context
);
179 TRACE(vxd
,"[%04x] Shell\n", (UINT16
)service
);
181 switch (service
) /* Ralf Brown says EDX, but I use DX instead */
184 TRACE(vxd
,"returning version\n");
185 AX_reg(context
) = VXD_WinVersion();
186 EBX_reg(context
) = 1; /* system VM Handle */
194 VXD_BARF( context
, "shell" );
197 case 0x0006: /* SHELL_Get_VM_State */
198 TRACE(vxd
,"VxD Shell: returning VM state\n");
199 /* Actually we don't, not yet. We have to return a structure
200 * and I am not to sure how to set it up and return it yet,
201 * so for now let's do nothing. I can (hopefully) get this
202 * by the next release
204 /* RESET_CFLAG(context); */
223 VXD_BARF( context
, "SHELL" );
226 /* the new Win95 shell API */
227 case 0x0100: /* get version */
228 AX_reg(context
) = VXD_WinVersion();
231 case 0x0104: /* retrieve Hook_Properties list */
232 case 0x0105: /* call Hook_Properties callbacks */
233 VXD_BARF( context
, "SHELL" );
236 case 0x0106: /* install timeout callback */
237 TRACE( vxd
, "VxD Shell: ignoring shell callback (%ld sec.)\n",
238 EBX_reg( context
) );
242 case 0x0107: /* get version of any VxD */
244 VXD_BARF( context
, "SHELL" );
250 /***********************************************************************
253 void WINAPI
VXD_Comm( CONTEXT
*context
)
255 unsigned service
= AX_reg(context
);
257 TRACE(vxd
,"[%04x] Comm\n", (UINT16
)service
);
261 case 0x0000: /* get version */
262 TRACE(vxd
,"returning version\n");
263 AX_reg(context
) = VXD_WinVersion();
264 RESET_CFLAG(context
);
267 case 0x0001: /* set port global */
268 case 0x0002: /* get focus */
269 case 0x0003: /* virtualise port */
271 VXD_BARF( context
, "comm" );
275 /***********************************************************************
278 void VXD_Timer( CONTEXT
*context
)
280 unsigned service
= AX_reg(context
);
282 TRACE(vxd
,"[%04x] Virtual Timer\n", (UINT16
)service
);
286 case 0x0000: /* version */
287 AX_reg(context
) = VXD_WinVersion();
288 RESET_CFLAG(context
);
291 case 0x0100: /* clock tick time, in 840nsecs */
292 EAX_reg(context
) = GetTickCount();
294 EDX_reg(context
) = EAX_reg(context
) >> 22;
295 EAX_reg(context
) <<= 10; /* not very precise */
298 case 0x0101: /* current Windows time, msecs */
299 case 0x0102: /* current VM time, msecs */
300 EAX_reg(context
) = GetTickCount();
304 VXD_BARF( context
, "VTD" );
308 /***********************************************************************
311 void VXD_TimerAPI ( CONTEXT
*context
)
313 static DWORD clockTicks
= 0;
314 static WORD clockTickSelector
= 0;
316 unsigned service
= AX_reg(context
);
318 TRACE(vxd
,"[%04x] TimerAPI \n", (UINT16
)service
);
322 case 0x0000: /* version */
323 AX_reg(context
) = VXD_WinVersion();
324 RESET_CFLAG(context
);
327 case 0x0009: /* get system time selector */
328 FIXME(vxd
, "Get_System_Time_Selector: this clock doesn't tick!\n");
330 if ( !clockTickSelector
)
331 clockTickSelector
= SELECTOR_AllocBlock( &clockTicks
, sizeof(DWORD
),
332 SEGMENT_DATA
, FALSE
, TRUE
);
333 AX_reg(context
) = clockTickSelector
;
334 RESET_CFLAG(context
);
338 VXD_BARF( context
, "VTDAPI" );
342 /***********************************************************************
345 void VXD_ConfigMG ( CONTEXT
*context
)
347 unsigned service
= AX_reg(context
);
349 TRACE(vxd
,"[%04x] ConfigMG \n", (UINT16
)service
);
353 case 0x0000: /* version */
354 AX_reg(context
) = VXD_WinVersion();
355 RESET_CFLAG(context
);
359 VXD_BARF( context
, "CONFIGMG" );
363 /***********************************************************************
366 void VXD_Enable ( CONTEXT
*context
)
368 unsigned service
= AX_reg(context
);
370 TRACE(vxd
,"[%04x] Enable \n", (UINT16
)service
);
374 case 0x0000: /* version */
375 AX_reg(context
) = VXD_WinVersion();
376 RESET_CFLAG(context
);
380 VXD_BARF( context
, "ENABLE" );
384 /***********************************************************************
387 void VXD_APM ( CONTEXT
*context
)
389 unsigned service
= AX_reg(context
);
391 TRACE(vxd
,"[%04x] APM \n", (UINT16
)service
);
395 case 0x0000: /* version */
396 AX_reg(context
) = VXD_WinVersion();
397 RESET_CFLAG(context
);
401 VXD_BARF( context
, "APM" );
405 /***********************************************************************
408 * This is an implementation of the services of the Win32s VxD.
409 * Since official documentation of these does not seem to be available,
410 * certain arguments of some of the services remain unclear.
412 * FIXME: The following services are currently unimplemented:
413 * Exception handling (0x01, 0x1C)
414 * Debugger support (0x0C, 0x14, 0x17)
415 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
416 * Memory Statistics (0x1B)
419 * We have a specific problem running Win32s on Linux (and probably also
420 * the other x86 unixes), since Win32s tries to allocate its main 'flat
421 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
422 * The rationale for this seems to be that they want one the one hand to
423 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
424 * at linear address 0, but want at other hand to have offset 0 of the
425 * flat data/code segment point to an unmapped page (to catch NULL pointer
426 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
427 * so that the Win 3.1 memory area at linear address zero shows up in the
428 * flat segments at offset 0x10000 (since linear addresses wrap around at
429 * 4GB). To compensate for that discrepancy between flat segment offsets
430 * and plain linear addresses, all flat pointers passed between the 32-bit
431 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
432 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
434 * The problem for us is now that Linux does not allow a LDT selector with
435 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
436 * address space. To address this problem we introduce *another* offset:
437 * We add 0x10000 to every linear address we get as an argument from Win32s.
438 * This means especially that the flat code/data selectors get actually
439 * allocated with base 0x0, so that flat offsets and (real) linear addresses
440 * do again agree! In fact, every call e.g. of a Win32s VxD service now
441 * has all pointer arguments (which are offsets in the flat data segement)
442 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
443 * increased by 0x10000 by *our* code.
445 * Note that to keep everything consistent, this offset has to be applied by
446 * every Wine function that operates on 'linear addresses' passed to it by
447 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
448 * API routines, this affects only two locations: this VxD and the DPMI
449 * handler. (NOTE: Should any Win32s application pass a linear address to
450 * any routine apart from those, e.g. some other VxD handler, that code
451 * would have to take the offset into account as well!)
453 * The application of the offset is triggered by marking the current process
454 * as a Win32s process by setting the PDB32_WIN32S_PROC flag in the process
455 * database. This is done the first time any application calls the GetVersion()
456 * service of the Win32s VxD. (Note that the flag is never removed.)
460 void VXD_Win32s( CONTEXT
*context
)
462 #define AppToWine(addr) ((addr)? ((LPBYTE)(addr)) + 0x10000 : NULL)
463 #define WineToApp(addr) ((addr)? ((DWORD) (addr)) - 0x10000 : 0)
465 switch (AX_reg(context
))
467 case 0x0000: /* Get Version */
471 * Output: EAX: LoWord: Win32s Version (1.30)
472 * HiWord: VxD Version (200)
478 * EDX: Debugging Flags
482 * 1 if VMCPD VxD not found
485 TRACE(vxd
, "GetVersion()\n");
487 EAX_reg(context
) = VXD_WinVersion() | (200 << 16);
488 EBX_reg(context
) = 0;
489 ECX_reg(context
) = 0;
490 EDX_reg(context
) = 0;
491 EDI_reg(context
) = 0;
494 * If this is the first time we are called for this process,
495 * hack the memory image of WIN32S16 so that it doesn't try
496 * to access the GDT directly ...
498 * The first code segment of WIN32S16 (version 1.30) contains
499 * an unexported function somewhere between the exported functions
500 * SetFS and StackLinearToSegmented that tries to find a selector
501 * in the LDT that maps to the memory image of the LDT itself.
502 * If it succeeds, it stores this selector into a global variable
503 * which will be used to speed up execution by using this selector
504 * to modify the LDT directly instead of using the DPMI calls.
506 * To perform this search of the LDT, this function uses the
507 * sgdt and sldt instructions to find the linear address of
508 * the (GDT and then) LDT. While those instructions themselves
509 * execute without problem, the linear address that sgdt returns
510 * points (at least under Linux) to the kernel address space, so
511 * that any subsequent access leads to a segfault.
513 * Fortunately, WIN32S16 still contains as a fallback option the
514 * mechanism of using DPMI calls to modify LDT selectors instead
515 * of direct writes to the LDT. Thus we can circumvent the problem
516 * by simply replacing the first byte of the offending function
517 * with an 'retf' instruction. This means that the global variable
518 * supposed to contain the LDT alias selector will remain zero,
519 * and hence WIN32S16 will fall back to using DPMI calls.
521 * The heuristic we employ to _find_ that function is as follows:
522 * We search between the addresses of the exported symbols SetFS
523 * and StackLinearToSegmented for the byte sequence '0F 01 04'
524 * (this is the opcode of 'sgdt [si]'). We then search backwards
525 * from this address for the last occurrance of 'CB' (retf) that marks
526 * the end of the preceeding function. The following byte (which
527 * should now be the first byte of the function we are looking for)
528 * will be replaced by 'CB' (retf).
530 * This heuristic works for the retail as well as the debug version
531 * of Win32s version 1.30. For versions earlier than that this
532 * hack should not be necessary at all, since the whole mechanism
533 * ('PERF130') was introduced only in 1.30 to improve the overall
534 * performance of Win32s.
537 if (!(PROCESS_Current()->flags
& PDB32_WIN32S_PROC
))
539 HMODULE16 hModule
= GetModuleHandle16("win32s16");
540 SEGPTR func1
= (SEGPTR
)WIN32_GetProcAddress16(hModule
, "SetFS");
541 SEGPTR func2
= (SEGPTR
)WIN32_GetProcAddress16(hModule
,
542 "StackLinearToSegmented");
544 if ( hModule
&& func1
&& func2
545 && SELECTOROF(func1
) == SELECTOROF(func2
))
547 BYTE
*start
= PTR_SEG_TO_LIN(func1
);
548 BYTE
*end
= PTR_SEG_TO_LIN(func2
);
549 BYTE
*p
, *retv
= NULL
;
552 for (p
= start
; p
< end
; p
++)
553 if (*p
== 0xCB) found
= 0, retv
= p
;
554 else if (*p
== 0x0F) found
= 1;
555 else if (*p
== 0x01 && found
== 1) found
= 2;
556 else if (*p
== 0x04 && found
== 2) { found
= 3; break; }
559 if (found
== 3 && retv
)
561 TRACE(vxd
, "PERF130 hack: "
562 "Replacing byte %02X at offset %04X:%04X\n",
563 *(retv
+1), SELECTOROF(func1
),
564 OFFSETOF(func1
) + retv
+1-start
);
566 *(retv
+1) = (BYTE
)0xCB;
572 * Mark process as Win32s, so that subsequent DPMI calls
573 * will perform the AppToWine/WineToApp address shift.
576 PROCESS_Current()->flags
|= PDB32_WIN32S_PROC
;
580 case 0x0001: /* Install Exception Handling */
582 * Input: EBX: Flat address of W32SKRNL Exception Data
584 * ECX: LoWord: Flat Code Selector
585 * HiWord: Flat Data Selector
587 * EDX: Flat address of W32SKRNL Exception Handler
588 * (this is equal to W32S_BackTo32 + 0x40)
590 * ESI: SEGPTR KERNEL.HASGPHANDLER
592 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
594 * Output: EAX: 0 if OK
597 TRACE(vxd
, "[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
598 EBX_reg(context
), ECX_reg(context
), EDX_reg(context
),
599 ESI_reg(context
), EDI_reg(context
));
603 EAX_reg(context
) = 0;
607 case 0x0002: /* Set Page Access Flags */
609 * Input: EBX: New access flags
610 * Bit 2: User Page if set, Supervisor Page if clear
611 * Bit 1: Read-Write if set, Read-Only if clear
613 * ECX: Size of memory area to change
615 * EDX: Flat start address of memory area
617 * Output: EAX: Size of area changed
620 TRACE(vxd
, "[0002] EBX=%lx ECX=%lx EDX=%lx\n",
621 EBX_reg(context
), ECX_reg(context
), EDX_reg(context
));
625 EAX_reg(context
) = ECX_reg(context
);
629 case 0x0003: /* Get Page Access Flags */
631 * Input: EDX: Flat address of page to query
633 * Output: EAX: Page access flags
634 * Bit 2: User Page if set, Supervisor Page if clear
635 * Bit 1: Read-Write if set, Read-Only if clear
638 TRACE(vxd
, "[0003] EDX=%lx\n", EDX_reg(context
));
642 EAX_reg(context
) = 6;
646 case 0x0004: /* Map Module */
648 * Input: ECX: IMTE (offset in Module Table) of new module
650 * EDX: Flat address of Win32s Module Table
652 * Output: EAX: 0 if OK
655 if (!EDX_reg(context
) || CX_reg(context
) == 0xFFFF)
657 TRACE(vxd
, "MapModule: Initialization call\n");
658 EAX_reg(context
) = 0;
663 * Structure of a Win32s Module Table Entry:
678 * Note: This function should set up a demand-paged memory image
679 * of the given module. Since mmap does not allow file offsets
680 * not aligned at 1024 bytes, we simply load the image fully
684 struct Win32sModule
*moduleTable
=
685 (struct Win32sModule
*)AppToWine(EDX_reg(context
));
686 struct Win32sModule
*module
= moduleTable
+ ECX_reg(context
);
688 IMAGE_NT_HEADERS
*nt_header
= PE_HEADER(module
->baseAddr
);
689 IMAGE_SECTION_HEADER
*pe_seg
= PE_SECTIONS(module
->baseAddr
);
691 HFILE32 image
= FILE_Open(module
->pathName
, O_RDONLY
,0);
692 BOOL32 error
= (image
== INVALID_HANDLE_VALUE32
);
695 TRACE(vxd
, "MapModule: Loading %s\n", module
->pathName
);
698 !error
&& i
< nt_header
->FileHeader
.NumberOfSections
;
700 if(!(pe_seg
->Characteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
))
702 DWORD off
= pe_seg
->PointerToRawData
;
703 DWORD len
= pe_seg
->SizeOfRawData
;
704 LPBYTE addr
= module
->baseAddr
+ pe_seg
->VirtualAddress
;
706 TRACE(vxd
, "MapModule: "
707 "Section %d at %08lx from %08lx len %08lx\n",
708 i
, (DWORD
)addr
, off
, len
);
710 if ( _llseek32(image
, off
, SEEK_SET
) != off
711 || _lread32(image
, addr
, len
) != len
)
718 ERR(vxd
, "MapModule: Unable to load %s\n", module
->pathName
);
720 else if (module
->relocDelta
!= 0)
722 IMAGE_DATA_DIRECTORY
*dir
= nt_header
->OptionalHeader
.DataDirectory
723 + IMAGE_DIRECTORY_ENTRY_BASERELOC
;
724 IMAGE_BASE_RELOCATION
*r
= (IMAGE_BASE_RELOCATION
*)
725 (dir
->Size
? module
->baseAddr
+ dir
->VirtualAddress
: 0);
727 TRACE(vxd
, "MapModule: Reloc delta %08lx\n", module
->relocDelta
);
729 while (r
&& r
->VirtualAddress
)
731 LPBYTE page
= module
->baseAddr
+ r
->VirtualAddress
;
732 int count
= (r
->SizeOfBlock
- 8) / 2;
734 TRACE(vxd
, "MapModule: %d relocations for page %08lx\n",
737 for(i
= 0; i
< count
; i
++)
739 int offset
= r
->TypeOffset
[i
] & 0xFFF;
740 int type
= r
->TypeOffset
[i
] >> 12;
743 case IMAGE_REL_BASED_ABSOLUTE
:
745 case IMAGE_REL_BASED_HIGH
:
746 *(WORD
*)(page
+offset
) += HIWORD(module
->relocDelta
);
748 case IMAGE_REL_BASED_LOW
:
749 *(WORD
*)(page
+offset
) += LOWORD(module
->relocDelta
);
751 case IMAGE_REL_BASED_HIGHLOW
:
752 *(DWORD
*)(page
+offset
) += module
->relocDelta
;
755 WARN(vxd
, "MapModule: Unsupported fixup type\n");
760 r
= (IMAGE_BASE_RELOCATION
*)((LPBYTE
)r
+ r
->SizeOfBlock
);
764 EAX_reg(context
) = 0;
765 RESET_CFLAG(context
);
770 case 0x0005: /* UnMap Module */
772 * Input: EDX: Flat address of module image
774 * Output: EAX: 1 if OK
777 TRACE(vxd
, "UnMapModule: %lx\n", (DWORD
)AppToWine(EDX_reg(context
)));
779 /* As we didn't map anything, there's nothing to unmap ... */
781 EAX_reg(context
) = 1;
785 case 0x0006: /* VirtualAlloc */
787 * Input: ECX: Current Process
789 * EDX: Flat address of arguments on stack
791 * DWORD *retv [out] Flat base address of allocated region
792 * LPVOID base [in] Flat address of region to reserve/commit
793 * DWORD size [in] Size of region
794 * DWORD type [in] Type of allocation
795 * DWORD prot [in] Type of access protection
797 * Output: EAX: NtStatus
800 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
801 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
802 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
803 DWORD size
= stack
[2];
804 DWORD type
= stack
[3];
805 DWORD prot
= stack
[4];
808 TRACE(vxd
, "VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
809 (DWORD
)retv
, (DWORD
)base
, size
, type
, prot
);
811 if (type
& 0x80000000)
813 WARN(vxd
, "VirtualAlloc: strange type %lx\n", type
);
817 if (!base
&& (type
& MEM_COMMIT
) && prot
== PAGE_READONLY
)
819 WARN(vxd
, "VirtualAlloc: NLS hack, allowing write access!\n");
820 prot
= PAGE_READWRITE
;
823 result
= (DWORD
)VirtualAlloc(base
, size
, type
, prot
);
825 if (WineToApp(result
))
826 *retv
= WineToApp(result
),
827 EAX_reg(context
) = STATUS_SUCCESS
;
830 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
835 case 0x0007: /* VirtualFree */
837 * Input: ECX: Current Process
839 * EDX: Flat address of arguments on stack
841 * DWORD *retv [out] TRUE if success, FALSE if failure
842 * LPVOID base [in] Flat address of region
843 * DWORD size [in] Size of region
844 * DWORD type [in] Type of operation
846 * Output: EAX: NtStatus
849 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
850 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
851 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
852 DWORD size
= stack
[2];
853 DWORD type
= stack
[3];
856 TRACE(vxd
, "VirtualFree(%lx, %lx, %lx, %lx)\n",
857 (DWORD
)retv
, (DWORD
)base
, size
, type
);
859 result
= VirtualFree(base
, size
, type
);
863 EAX_reg(context
) = STATUS_SUCCESS
;
866 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
871 case 0x0008: /* VirtualProtect */
873 * Input: ECX: Current Process
875 * EDX: Flat address of arguments on stack
877 * DWORD *retv [out] TRUE if success, FALSE if failure
878 * LPVOID base [in] Flat address of region
879 * DWORD size [in] Size of region
880 * DWORD new_prot [in] Desired access protection
881 * DWORD *old_prot [out] Previous access protection
883 * Output: EAX: NtStatus
886 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
887 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
888 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
889 DWORD size
= stack
[2];
890 DWORD new_prot
= stack
[3];
891 DWORD
*old_prot
= (DWORD
*)AppToWine(stack
[4]);
894 TRACE(vxd
, "VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
895 (DWORD
)retv
, (DWORD
)base
, size
, new_prot
, (DWORD
)old_prot
);
897 result
= VirtualProtect(base
, size
, new_prot
, old_prot
);
901 EAX_reg(context
) = STATUS_SUCCESS
;
904 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
909 case 0x0009: /* VirtualQuery */
911 * Input: ECX: Current Process
913 * EDX: Flat address of arguments on stack
915 * DWORD *retv [out] Nr. bytes returned
916 * LPVOID base [in] Flat address of region
917 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
918 * DWORD len [in] Size of buffer
920 * Output: EAX: NtStatus
923 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
924 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
925 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
926 LPMEMORY_BASIC_INFORMATION info
=
927 (LPMEMORY_BASIC_INFORMATION
)AppToWine(stack
[2]);
928 DWORD len
= stack
[3];
931 TRACE(vxd
, "VirtualQuery(%lx, %lx, %lx, %lx)\n",
932 (DWORD
)retv
, (DWORD
)base
, (DWORD
)info
, len
);
934 result
= VirtualQuery(base
, info
, len
);
937 EAX_reg(context
) = STATUS_SUCCESS
;
942 case 0x000A: /* SetVirtMemProcess */
944 * Input: ECX: Process Handle
946 * EDX: Flat address of region
948 * Output: EAX: NtStatus
951 TRACE(vxd
, "[000a] ECX=%lx EDX=%lx\n",
952 ECX_reg(context
), EDX_reg(context
));
956 EAX_reg(context
) = STATUS_SUCCESS
;
960 case 0x000B: /* ??? some kind of cleanup */
962 * Input: ECX: Process Handle
964 * Output: EAX: NtStatus
967 TRACE(vxd
, "[000b] ECX=%lx\n", ECX_reg(context
));
971 EAX_reg(context
) = STATUS_SUCCESS
;
975 case 0x000C: /* Set Debug Flags */
977 * Input: EDX: Debug Flags
979 * Output: EDX: Previous Debug Flags
982 FIXME(vxd
, "[000c] EDX=%lx\n", EDX_reg(context
));
986 EDX_reg(context
) = 0;
990 case 0x000D: /* NtCreateSection */
992 * Input: EDX: Flat address of arguments on stack
994 * HANDLE32 *retv [out] Handle of Section created
995 * DWORD flags1 [in] (?? unknown ??)
996 * DWORD atom [in] Name of Section to create
997 * LARGE_INTEGER *size [in] Size of Section
998 * DWORD protect [in] Access protection
999 * DWORD flags2 [in] (?? unknown ??)
1000 * HFILE32 hFile [in] Handle of file to map
1001 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
1003 * Output: EAX: NtStatus
1006 DWORD
*stack
= (DWORD
*) AppToWine(EDX_reg(context
));
1007 HANDLE32
*retv
= (HANDLE32
*)AppToWine(stack
[0]);
1008 DWORD flags1
= stack
[1];
1009 DWORD atom
= stack
[2];
1010 LARGE_INTEGER
*size
= (LARGE_INTEGER
*)AppToWine(stack
[3]);
1011 DWORD protect
= stack
[4];
1012 DWORD flags2
= stack
[5];
1013 HFILE32 hFile
= HFILE16_TO_HFILE32(stack
[6]);
1014 DWORD psp
= stack
[7];
1016 HANDLE32 result
= INVALID_HANDLE_VALUE32
;
1019 TRACE(vxd
, "NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1020 (DWORD
)retv
, flags1
, atom
, (DWORD
)size
, protect
, flags2
,
1023 if (!atom
|| GlobalGetAtomName32A(atom
, name
, sizeof(name
)))
1025 TRACE(vxd
, "NtCreateSection: name=%s\n", atom
? name
: NULL
);
1027 result
= CreateFileMapping32A(hFile
, NULL
, protect
,
1028 size
? size
->HighPart
: 0,
1029 size
? size
->LowPart
: 0,
1033 if (result
== INVALID_HANDLE_VALUE32
)
1034 WARN(vxd
, "NtCreateSection: failed!\n");
1036 TRACE(vxd
, "NtCreateSection: returned %lx\n", (DWORD
)result
);
1038 if (result
!= INVALID_HANDLE_VALUE32
)
1040 EAX_reg(context
) = STATUS_SUCCESS
;
1043 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1048 case 0x000E: /* NtOpenSection */
1050 * Input: EDX: Flat address of arguments on stack
1052 * HANDLE32 *retv [out] Handle of Section opened
1053 * DWORD protect [in] Access protection
1054 * DWORD atom [in] Name of Section to create
1056 * Output: EAX: NtStatus
1059 DWORD
*stack
= (DWORD
*) AppToWine(EDX_reg(context
));
1060 HANDLE32
*retv
= (HANDLE32
*)AppToWine(stack
[0]);
1061 DWORD protect
= stack
[1];
1062 DWORD atom
= stack
[2];
1064 HANDLE32 result
= INVALID_HANDLE_VALUE32
;
1067 TRACE(vxd
, "NtOpenSection(%lx, %lx, %lx)\n",
1068 (DWORD
)retv
, protect
, atom
);
1070 if (atom
&& GlobalGetAtomName32A(atom
, name
, sizeof(name
)))
1072 TRACE(vxd
, "NtOpenSection: name=%s\n", name
);
1074 result
= OpenFileMapping32A(protect
, FALSE
, name
);
1077 if (result
== INVALID_HANDLE_VALUE32
)
1078 WARN(vxd
, "NtOpenSection: failed!\n");
1080 TRACE(vxd
, "NtOpenSection: returned %lx\n", (DWORD
)result
);
1082 if (result
!= INVALID_HANDLE_VALUE32
)
1084 EAX_reg(context
) = STATUS_SUCCESS
;
1087 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1092 case 0x000F: /* NtCloseSection */
1094 * Input: EDX: Flat address of arguments on stack
1096 * HANDLE32 handle [in] Handle of Section to close
1097 * DWORD *id [out] Unique ID (?? unclear ??)
1099 * Output: EAX: NtStatus
1102 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1103 HANDLE32 handle
= stack
[0];
1104 DWORD
*id
= (DWORD
*)AppToWine(stack
[1]);
1106 TRACE(vxd
, "NtCloseSection(%lx, %lx)\n", (DWORD
)handle
, (DWORD
)id
);
1108 CloseHandle(handle
);
1109 if (id
) *id
= 0; /* FIXME */
1111 EAX_reg(context
) = STATUS_SUCCESS
;
1116 case 0x0010: /* NtDupSection */
1118 * Input: EDX: Flat address of arguments on stack
1120 * HANDLE32 handle [in] Handle of Section to duplicate
1122 * Output: EAX: NtStatus
1125 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1126 HANDLE32 handle
= stack
[0];
1128 TRACE(vxd
, "NtDupSection(%lx)\n", (DWORD
)handle
);
1130 /* Handle is 'duplicated' by incrementing RefCount */
1131 HANDLE_GetObjPtr(PROCESS_Current(), handle
, K32OBJ_MEM_MAPPED_FILE
, 0,NULL
);
1133 EAX_reg(context
) = STATUS_SUCCESS
;
1138 case 0x0011: /* NtMapViewOfSection */
1140 * Input: EDX: Flat address of arguments on stack
1142 * HANDLE32 SectionHandle [in] Section to be mapped
1143 * DWORD ProcessHandle [in] Process to be mapped into
1144 * DWORD * BaseAddress [in/out] Address to be mapped at
1145 * DWORD ZeroBits [in] (?? unclear ??)
1146 * DWORD CommitSize [in] (?? unclear ??)
1147 * LARGE_INTEGER *SectionOffset [in] Offset within section
1148 * DWORD * ViewSize [in] Size of view
1149 * DWORD InheritDisposition [in] (?? unclear ??)
1150 * DWORD AllocationType [in] (?? unclear ??)
1151 * DWORD Protect [in] Access protection
1153 * Output: EAX: NtStatus
1156 DWORD
* stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1157 HANDLE32 SectionHandle
= stack
[0];
1158 DWORD ProcessHandle
= stack
[1]; /* ignored */
1159 DWORD
* BaseAddress
= (DWORD
*)AppToWine(stack
[2]);
1160 DWORD ZeroBits
= stack
[3];
1161 DWORD CommitSize
= stack
[4];
1162 LARGE_INTEGER
*SectionOffset
= (LARGE_INTEGER
*)AppToWine(stack
[5]);
1163 DWORD
* ViewSize
= (DWORD
*)AppToWine(stack
[6]);
1164 DWORD InheritDisposition
= stack
[7];
1165 DWORD AllocationType
= stack
[8];
1166 DWORD Protect
= stack
[9];
1168 LPBYTE address
= (LPBYTE
)(BaseAddress
? AppToWine(*BaseAddress
) : 0);
1169 DWORD access
= 0, result
;
1171 switch (Protect
& ~(PAGE_GUARD
|PAGE_NOCACHE
))
1173 case PAGE_READONLY
: access
= FILE_MAP_READ
; break;
1174 case PAGE_READWRITE
: access
= FILE_MAP_WRITE
; break;
1175 case PAGE_WRITECOPY
: access
= FILE_MAP_COPY
; break;
1177 case PAGE_EXECUTE_READ
: access
= FILE_MAP_READ
; break;
1178 case PAGE_EXECUTE_READWRITE
: access
= FILE_MAP_WRITE
; break;
1179 case PAGE_EXECUTE_WRITECOPY
: access
= FILE_MAP_COPY
; break;
1182 TRACE(vxd
, "NtMapViewOfSection"
1183 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1184 (DWORD
)SectionHandle
, ProcessHandle
, (DWORD
)BaseAddress
,
1185 ZeroBits
, CommitSize
, (DWORD
)SectionOffset
, (DWORD
)ViewSize
,
1186 InheritDisposition
, AllocationType
, Protect
);
1187 TRACE(vxd
, "NtMapViewOfSection: "
1188 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1189 (DWORD
)address
, SectionOffset
? SectionOffset
->LowPart
: 0,
1190 ViewSize
? *ViewSize
: 0, access
);
1192 result
= (DWORD
)MapViewOfFileEx(SectionHandle
, access
,
1193 SectionOffset
? SectionOffset
->HighPart
: 0,
1194 SectionOffset
? SectionOffset
->LowPart
: 0,
1195 ViewSize
? *ViewSize
: 0, address
);
1197 TRACE(vxd
, "NtMapViewOfSection: result=%lx\n", result
);
1199 if (WineToApp(result
))
1201 if (BaseAddress
) *BaseAddress
= WineToApp(result
);
1202 EAX_reg(context
) = STATUS_SUCCESS
;
1205 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1210 case 0x0012: /* NtUnmapViewOfSection */
1212 * Input: EDX: Flat address of arguments on stack
1214 * DWORD ProcessHandle [in] Process (defining address space)
1215 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1217 * Output: EAX: NtStatus
1220 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1221 DWORD ProcessHandle
= stack
[0]; /* ignored */
1222 LPBYTE BaseAddress
= (LPBYTE
)AppToWine(stack
[1]);
1224 TRACE(vxd
, "NtUnmapViewOfSection(%lx, %lx)\n",
1225 ProcessHandle
, (DWORD
)BaseAddress
);
1227 UnmapViewOfFile(BaseAddress
);
1229 EAX_reg(context
) = STATUS_SUCCESS
;
1234 case 0x0013: /* NtFlushVirtualMemory */
1236 * Input: EDX: Flat address of arguments on stack
1238 * DWORD ProcessHandle [in] Process (defining address space)
1239 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1240 * DWORD *ViewSize [in?] Number of bytes to be flushed
1241 * DWORD *unknown [???] (?? unknown ??)
1243 * Output: EAX: NtStatus
1246 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1247 DWORD ProcessHandle
= stack
[0]; /* ignored */
1248 DWORD
*BaseAddress
= (DWORD
*)AppToWine(stack
[1]);
1249 DWORD
*ViewSize
= (DWORD
*)AppToWine(stack
[2]);
1250 DWORD
*unknown
= (DWORD
*)AppToWine(stack
[3]);
1252 LPBYTE address
= (LPBYTE
)(BaseAddress
? AppToWine(*BaseAddress
) : 0);
1253 DWORD size
= ViewSize
? *ViewSize
: 0;
1255 TRACE(vxd
, "NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1256 ProcessHandle
, (DWORD
)BaseAddress
, (DWORD
)ViewSize
,
1258 TRACE(vxd
, "NtFlushVirtualMemory: base=%lx, size=%lx\n",
1259 (DWORD
)address
, size
);
1261 FlushViewOfFile(address
, size
);
1263 EAX_reg(context
) = STATUS_SUCCESS
;
1268 case 0x0014: /* Get/Set Debug Registers */
1270 * Input: ECX: 0 if Get, 1 if Set
1272 * EDX: Get: Flat address of buffer to receive values of
1273 * debug registers DR0 .. DR7
1274 * Set: Flat address of buffer containing values of
1275 * debug registers DR0 .. DR7 to be set
1279 FIXME(vxd
, "[0014] ECX=%lx EDX=%lx\n",
1280 ECX_reg(context
), EDX_reg(context
));
1286 case 0x0015: /* Set Coprocessor Emulation Flag */
1288 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1293 TRACE(vxd
, "[0015] EDX=%lx\n", EDX_reg(context
));
1295 /* We don't care, as we always have a coprocessor anyway */
1299 case 0x0016: /* Init Win32S VxD PSP */
1301 * If called to query required PSP size:
1304 * Output: EDX: Required size of Win32s VxD PSP
1306 * If called to initialize allocated PSP:
1308 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1309 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1313 if (EBX_reg(context
) == 0)
1314 EDX_reg(context
) = 0x80;
1317 PDB
*psp
= PTR_SEG_OFF_TO_LIN(BX_reg(context
), 0);
1319 psp
->fileHandlesPtr
= MAKELONG(HIWORD(EBX_reg(context
)), 0x5c);
1320 memset((LPBYTE
)psp
+ 0x5c, '\xFF', 32);
1325 case 0x0017: /* Set Break Point */
1327 * Input: EBX: Offset of Break Point
1328 * CX: Selector of Break Point
1333 FIXME(vxd
, "[0017] EBX=%lx CX=%x\n",
1334 EBX_reg(context
), CX_reg(context
));
1340 case 0x0018: /* VirtualLock */
1342 * Input: ECX: Current Process
1344 * EDX: Flat address of arguments on stack
1346 * DWORD *retv [out] TRUE if success, FALSE if failure
1347 * LPVOID base [in] Flat address of range to lock
1348 * DWORD size [in] Size of range
1350 * Output: EAX: NtStatus
1353 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1354 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
1355 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
1356 DWORD size
= stack
[2];
1359 TRACE(vxd
, "VirtualLock(%lx, %lx, %lx)\n",
1360 (DWORD
)retv
, (DWORD
)base
, size
);
1362 result
= VirtualLock(base
, size
);
1366 EAX_reg(context
) = STATUS_SUCCESS
;
1369 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1374 case 0x0019: /* VirtualUnlock */
1376 * Input: ECX: Current Process
1378 * EDX: Flat address of arguments on stack
1380 * DWORD *retv [out] TRUE if success, FALSE if failure
1381 * LPVOID base [in] Flat address of range to unlock
1382 * DWORD size [in] Size of range
1384 * Output: EAX: NtStatus
1387 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1388 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
1389 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
1390 DWORD size
= stack
[2];
1393 TRACE(vxd
, "VirtualUnlock(%lx, %lx, %lx)\n",
1394 (DWORD
)retv
, (DWORD
)base
, size
);
1396 result
= VirtualUnlock(base
, size
);
1400 EAX_reg(context
) = STATUS_SUCCESS
;
1403 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1408 case 0x001A: /* KGetSystemInfo */
1412 * Output: ECX: Start of sparse memory arena
1413 * EDX: End of sparse memory arena
1416 TRACE(vxd
, "KGetSystemInfo()\n");
1419 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1420 * sparse memory arena. We do it the other way around, since
1421 * we have to reserve 3GB - 4GB for Linux, and thus use
1422 * 0GB - 3GB as sparse memory arena.
1424 * FIXME: What about other OSes ?
1427 ECX_reg(context
) = WineToApp(0x00000000);
1428 EDX_reg(context
) = WineToApp(0xbfffffff);
1432 case 0x001B: /* KGlobalMemStat */
1434 * Input: ESI: Flat address of buffer to receive memory info
1439 struct Win32sMemoryInfo
1441 DWORD DIPhys_Count
; /* Total physical pages */
1442 DWORD DIFree_Count
; /* Free physical pages */
1443 DWORD DILin_Total_Count
; /* Total virtual pages (private arena) */
1444 DWORD DILin_Total_Free
; /* Free virtual pages (private arena) */
1446 DWORD SparseTotal
; /* Total size of sparse arena (bytes ?) */
1447 DWORD SparseFree
; /* Free size of sparse arena (bytes ?) */
1450 struct Win32sMemoryInfo
*info
=
1451 (struct Win32sMemoryInfo
*)AppToWine(ESI_reg(context
));
1453 FIXME(vxd
, "KGlobalMemStat(%lx)\n", (DWORD
)info
);
1460 case 0x001C: /* Enable/Disable Exceptions */
1462 * Input: ECX: 0 to disable, 1 to enable exception handling
1467 TRACE(vxd
, "[001c] ECX=%lx\n", ECX_reg(context
));
1473 case 0x001D: /* VirtualAlloc called from 16-bit code */
1475 * Input: EDX: Segmented address of arguments on stack
1477 * LPVOID base [in] Flat address of region to reserve/commit
1478 * DWORD size [in] Size of region
1479 * DWORD type [in] Type of allocation
1480 * DWORD prot [in] Type of access protection
1482 * Output: EAX: NtStatus
1483 * EDX: Flat base address of allocated region
1486 DWORD
*stack
= PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context
)),
1487 HIWORD(EDX_reg(context
)));
1488 LPVOID base
= (LPVOID
)AppToWine(stack
[0]);
1489 DWORD size
= stack
[1];
1490 DWORD type
= stack
[2];
1491 DWORD prot
= stack
[3];
1494 TRACE(vxd
, "VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1495 (DWORD
)base
, size
, type
, prot
);
1497 if (type
& 0x80000000)
1499 WARN(vxd
, "VirtualAlloc16: strange type %lx\n", type
);
1503 result
= (DWORD
)VirtualAlloc(base
, size
, type
, prot
);
1505 if (WineToApp(result
))
1506 EDX_reg(context
) = WineToApp(result
),
1507 EAX_reg(context
) = STATUS_SUCCESS
;
1509 EDX_reg(context
) = 0,
1510 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1515 case 0x001E: /* VirtualFree called from 16-bit code */
1517 * Input: EDX: Segmented address of arguments on stack
1519 * LPVOID base [in] Flat address of region
1520 * DWORD size [in] Size of region
1521 * DWORD type [in] Type of operation
1523 * Output: EAX: NtStatus
1524 * EDX: TRUE if success, FALSE if failure
1527 DWORD
*stack
= PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context
)),
1528 HIWORD(EDX_reg(context
)));
1529 LPVOID base
= (LPVOID
)AppToWine(stack
[0]);
1530 DWORD size
= stack
[1];
1531 DWORD type
= stack
[2];
1534 TRACE(vxd
, "VirtualFree16(%lx, %lx, %lx)\n",
1535 (DWORD
)base
, size
, type
);
1537 result
= VirtualFree(base
, size
, type
);
1540 EDX_reg(context
) = TRUE
,
1541 EAX_reg(context
) = STATUS_SUCCESS
;
1543 EDX_reg(context
) = FALSE
,
1544 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1549 case 0x001F: /* FWorkingSetSize */
1551 * Input: EDX: 0 if Get, 1 if Set
1553 * ECX: Get: Buffer to receive Working Set Size
1554 * Set: Buffer containing Working Set Size
1559 DWORD
*ptr
= (DWORD
*)AppToWine(ECX_reg(context
));
1560 BOOL32 set
= EDX_reg(context
);
1562 TRACE(vxd
, "FWorkingSetSize(%lx, %lx)\n", (DWORD
)ptr
, (DWORD
)set
);
1565 /* We do it differently ... */;
1569 EAX_reg(context
) = STATUS_SUCCESS
;
1575 VXD_BARF( context
, "W32S" );