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();
53 VXD_BARF( context
, "VMM" );
57 /***********************************************************************
60 void WINAPI
VXD_PageFile( CONTEXT
*context
)
62 unsigned service
= AX_reg(context
);
64 /* taken from Ralf Brown's Interrupt List */
66 TRACE(vxd
,"[%04x] PageFile\n", (UINT16
)service
);
70 case 0x00: /* get version, is this windows version? */
71 TRACE(vxd
,"returning version\n");
72 AX_reg(context
) = VXD_WinVersion();
76 case 0x01: /* get swap file info */
77 TRACE(vxd
,"VxD PageFile: returning swap file info\n");
78 AX_reg(context
) = 0x00; /* paging disabled */
79 ECX_reg(context
) = 0; /* maximum size of paging file */
80 /* FIXME: do I touch DS:SI or DS:DI? */
84 case 0x02: /* delete permanent swap on exit */
85 TRACE(vxd
,"VxD PageFile: supposed to delete swap\n");
89 case 0x03: /* current temporary swap file size */
90 TRACE(vxd
,"VxD PageFile: what is current temp. swap size\n");
94 case 0x04: /* read or write?? INTERRUP.D */
95 case 0x05: /* cancel?? INTERRUP.D */
96 case 0x06: /* test I/O valid INTERRUP.D */
98 VXD_BARF( context
, "pagefile" );
104 /***********************************************************************
107 void WINAPI
VXD_Shell( CONTEXT
*context
)
109 unsigned service
= DX_reg(context
);
111 TRACE(vxd
,"[%04x] Shell\n", (UINT16
)service
);
113 switch (service
) /* Ralf Brown says EDX, but I use DX instead */
116 TRACE(vxd
,"returning version\n");
117 AX_reg(context
) = VXD_WinVersion();
118 EBX_reg(context
) = 1; /* system VM Handle */
126 TRACE(vxd
,"VxD Shell: EDX = %08lx\n",EDX_reg(context
));
127 VXD_BARF( context
, "shell" );
130 case 0x0006: /* SHELL_Get_VM_State */
131 TRACE(vxd
,"VxD Shell: returning VM state\n");
132 /* Actually we don't, not yet. We have to return a structure
133 * and I am not to sure how to set it up and return it yet,
134 * so for now let's do nothing. I can (hopefully) get this
135 * by the next release
137 /* RESET_CFLAG(context); */
157 TRACE(vxd
,"VxD Shell: EDX = %08lx\n",EDX_reg(context
));
158 VXD_BARF( context
, "shell");
164 /***********************************************************************
167 void WINAPI
VXD_Comm( CONTEXT
*context
)
169 unsigned service
= AX_reg(context
);
171 TRACE(vxd
,"[%04x] Comm\n", (UINT16
)service
);
175 case 0x0000: /* get version */
176 TRACE(vxd
,"returning version\n");
177 AX_reg(context
) = VXD_WinVersion();
178 RESET_CFLAG(context
);
181 case 0x0001: /* set port global */
182 case 0x0002: /* get focus */
183 case 0x0003: /* virtualise port */
185 VXD_BARF( context
, "comm" );
189 /***********************************************************************
192 void VXD_Timer( CONTEXT
*context
)
194 unsigned service
= AX_reg(context
);
196 TRACE(vxd
,"[%04x] Virtual Timer\n", (UINT16
)service
);
200 case 0x0000: /* version */
201 AX_reg(context
) = VXD_WinVersion();
202 RESET_CFLAG(context
);
205 case 0x0100: /* clock tick time, in 840nsecs */
206 EAX_reg(context
) = GetTickCount();
208 EDX_reg(context
) = EAX_reg(context
) >> 22;
209 EAX_reg(context
) <<= 10; /* not very precise */
212 case 0x0101: /* current Windows time, msecs */
213 case 0x0102: /* current VM time, msecs */
214 EAX_reg(context
) = GetTickCount();
218 VXD_BARF( context
, "VTD" );
222 /***********************************************************************
225 void VXD_TimerAPI ( CONTEXT
*context
)
227 static DWORD clockTicks
= 0;
228 static WORD clockTickSelector
= 0;
230 unsigned service
= AX_reg(context
);
232 TRACE(vxd
,"[%04x] TimerAPI \n", (UINT16
)service
);
236 case 0x0000: /* version */
237 AX_reg(context
) = VXD_WinVersion();
238 RESET_CFLAG(context
);
241 case 0x0009: /* get system time selector */
242 FIXME(vxd
, "Get_System_Time_Selector: this clock doesn't tick!\n");
244 if ( !clockTickSelector
)
245 clockTickSelector
= SELECTOR_AllocBlock( &clockTicks
, sizeof(DWORD
),
246 SEGMENT_DATA
, FALSE
, TRUE
);
247 AX_reg(context
) = clockTickSelector
;
248 RESET_CFLAG(context
);
252 VXD_BARF( context
, "VTDAPI" );
256 /***********************************************************************
259 void VXD_ConfigMG ( CONTEXT
*context
)
261 unsigned service
= AX_reg(context
);
263 TRACE(vxd
,"[%04x] ConfigMG \n", (UINT16
)service
);
267 case 0x0000: /* version */
268 AX_reg(context
) = VXD_WinVersion();
269 RESET_CFLAG(context
);
273 VXD_BARF( context
, "CONFIGMG" );
277 /***********************************************************************
280 * This is an implementation of the services of the Win32s VxD.
281 * Since official documentation of these does not seem to be available,
282 * certain arguments of some of the services remain unclear.
284 * FIXME: The following services are currently unimplemented:
285 * Exception handling (0x01, 0x1C)
286 * Debugger support (0x0C, 0x14, 0x17)
287 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
288 * Memory Statistics (0x1B)
291 * We have a specific problem running Win32s on Linux (and probably also
292 * the other x86 unixes), since Win32s tries to allocate its main 'flat
293 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
294 * The rationale for this seems to be that they want one the one hand to
295 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
296 * at linear address 0, but want at other hand to have offset 0 of the
297 * flat data/code segment point to an unmapped page (to catch NULL pointer
298 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
299 * so that the Win 3.1 memory area at linear address zero shows up in the
300 * flat segments at offset 0x10000 (since linear addresses wrap around at
301 * 4GB). To compensate for that discrepancy between flat segment offsets
302 * and plain linear addresses, all flat pointers passed between the 32-bit
303 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
304 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
306 * The problem for us is now that Linux does not allow a LDT selector with
307 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
308 * address space. To address this problem we introduce *another* offset:
309 * We add 0x10000 to every linear address we get as an argument from Win32s.
310 * This means especially that the flat code/data selectors get actually
311 * allocated with base 0x0, so that flat offsets and (real) linear addresses
312 * do again agree! In fact, every call e.g. of a Win32s VxD service now
313 * has all pointer arguments (which are offsets in the flat data segement)
314 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
315 * increased by 0x10000 by *our* code.
317 * Note that to keep everything consistent, this offset has to be applied by
318 * every Wine function that operates on 'linear addresses' passed to it by
319 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
320 * API routines, this affects only two locations: this VxD and the DPMI
321 * handler. (NOTE: Should any Win32s application pass a linear address to
322 * any routine apart from those, e.g. some other VxD handler, that code
323 * would have to take the offset into account as well!)
325 * The application of the offset is triggered by marking the current process
326 * as a Win32s process by setting the PDB32_WIN32S_PROC flag in the process
327 * database. This is done the first time any application calls the GetVersion()
328 * service of the Win32s VxD. (Note that the flag is never removed.)
332 void VXD_Win32s( CONTEXT
*context
)
334 #define AppToWine(addr) ((addr)? ((LPBYTE)(addr)) + 0x10000 : NULL)
335 #define WineToApp(addr) ((addr)? ((DWORD) (addr)) - 0x10000 : 0)
337 switch (AX_reg(context
))
339 case 0x0000: /* Get Version */
343 * Output: EAX: LoWord: Win32s Version (1.30)
344 * HiWord: VxD Version (200)
350 * EDX: Debugging Flags
354 * 1 if VMCPD VxD not found
357 TRACE(vxd
, "GetVersion()\n");
359 EAX_reg(context
) = VXD_WinVersion() | (200 << 16);
360 EBX_reg(context
) = 0;
361 ECX_reg(context
) = 0;
362 EDX_reg(context
) = 0;
363 EDI_reg(context
) = 0;
366 * If this is the first time we are called for this process,
367 * hack the memory image of WIN32S16 so that it doesn't try
368 * to access the GDT directly ...
370 * The first code segment of WIN32S16 (version 1.30) contains
371 * an unexported function somewhere between the exported functions
372 * SetFS and StackLinearToSegmented that tries to find a selector
373 * in the LDT that maps to the memory image of the LDT itself.
374 * If it succeeds, it stores this selector into a global variable
375 * which will be used to speed up execution by using this selector
376 * to modify the LDT directly instead of using the DPMI calls.
378 * To perform this search of the LDT, this function uses the
379 * sgdt and sldt instructions to find the linear address of
380 * the (GDT and then) LDT. While those instructions themselves
381 * execute without problem, the linear address that sgdt returns
382 * points (at least under Linux) to the kernel address space, so
383 * that any subsequent access leads to a segfault.
385 * Fortunately, WIN32S16 still contains as a fallback option the
386 * mechanism of using DPMI calls to modify LDT selectors instead
387 * of direct writes to the LDT. Thus we can circumvent the problem
388 * by simply replacing the first byte of the offending function
389 * with an 'retf' instruction. This means that the global variable
390 * supposed to contain the LDT alias selector will remain zero,
391 * and hence WIN32S16 will fall back to using DPMI calls.
393 * The heuristic we employ to _find_ that function is as follows:
394 * We search between the addresses of the exported symbols SetFS
395 * and StackLinearToSegmented for the byte sequence '0F 01 04'
396 * (this is the opcode of 'sgdt [si]'). We then search backwards
397 * from this address for the last occurrance of 'CB' (retf) that marks
398 * the end of the preceeding function. The following byte (which
399 * should now be the first byte of the function we are looking for)
400 * will be replaced by 'CB' (retf).
402 * This heuristic works for the retail as well as the debug version
403 * of Win32s version 1.30. For versions earlier than that this
404 * hack should not be necessary at all, since the whole mechanism
405 * ('PERF130') was introduced only in 1.30 to improve the overall
406 * performance of Win32s.
409 if (!(PROCESS_Current()->flags
& PDB32_WIN32S_PROC
))
411 HMODULE16 hModule
= GetModuleHandle16("win32s16");
412 SEGPTR func1
= (SEGPTR
)WIN32_GetProcAddress16(hModule
, "SetFS");
413 SEGPTR func2
= (SEGPTR
)WIN32_GetProcAddress16(hModule
,
414 "StackLinearToSegmented");
416 if ( hModule
&& func1
&& func2
417 && SELECTOROF(func1
) == SELECTOROF(func2
))
419 BYTE
*start
= PTR_SEG_TO_LIN(func1
);
420 BYTE
*end
= PTR_SEG_TO_LIN(func2
);
421 BYTE
*p
, *retv
= NULL
;
424 for (p
= start
; p
< end
; p
++)
425 if (*p
== 0xCB) found
= 0, retv
= p
;
426 else if (*p
== 0x0F) found
= 1;
427 else if (*p
== 0x01 && found
== 1) found
= 2;
428 else if (*p
== 0x04 && found
== 2) { found
= 3; break; }
431 if (found
== 3 && retv
)
433 TRACE(vxd
, "PERF130 hack: "
434 "Replacing byte %02X at offset %04X:%04X\n",
435 *(retv
+1), SELECTOROF(func1
),
436 OFFSETOF(func1
) + retv
+1-start
);
438 *(retv
+1) = (BYTE
)0xCB;
444 * Mark process as Win32s, so that subsequent DPMI calls
445 * will perform the AppToWine/WineToApp address shift.
448 PROCESS_Current()->flags
|= PDB32_WIN32S_PROC
;
452 case 0x0001: /* Install Exception Handling */
454 * Input: EBX: Flat address of W32SKRNL Exception Data
456 * ECX: LoWord: Flat Code Selector
457 * HiWord: Flat Data Selector
459 * EDX: Flat address of W32SKRNL Exception Handler
460 * (this is equal to W32S_BackTo32 + 0x40)
462 * ESI: SEGPTR KERNEL.HASGPHANDLER
464 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
466 * Output: EAX: 0 if OK
469 TRACE(vxd
, "[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
470 EBX_reg(context
), ECX_reg(context
), EDX_reg(context
),
471 ESI_reg(context
), EDI_reg(context
));
475 EAX_reg(context
) = 0;
479 case 0x0002: /* Set Page Access Flags */
481 * Input: EBX: New access flags
482 * Bit 2: User Page if set, Supervisor Page if clear
483 * Bit 1: Read-Write if set, Read-Only if clear
485 * ECX: Size of memory area to change
487 * EDX: Flat start address of memory area
489 * Output: EAX: Size of area changed
492 TRACE(vxd
, "[0002] EBX=%lx ECX=%lx EDX=%lx\n",
493 EBX_reg(context
), ECX_reg(context
), EDX_reg(context
));
497 EAX_reg(context
) = ECX_reg(context
);
501 case 0x0003: /* Get Page Access Flags */
503 * Input: EDX: Flat address of page to query
505 * Output: EAX: Page access flags
506 * Bit 2: User Page if set, Supervisor Page if clear
507 * Bit 1: Read-Write if set, Read-Only if clear
510 TRACE(vxd
, "[0003] EDX=%lx\n", EDX_reg(context
));
514 EAX_reg(context
) = 6;
518 case 0x0004: /* Map Module */
520 * Input: ECX: IMTE (offset in Module Table) of new module
522 * EDX: Flat address of Win32s Module Table
524 * Output: EAX: 0 if OK
527 if (!EDX_reg(context
) || CX_reg(context
) == 0xFFFF)
529 TRACE(vxd
, "MapModule: Initialization call\n");
530 EAX_reg(context
) = 0;
535 * Structure of a Win32s Module Table Entry:
550 * Note: This function should set up a demand-paged memory image
551 * of the given module. Since mmap does not allow file offsets
552 * not aligned at 1024 bytes, we simply load the image fully
556 struct Win32sModule
*moduleTable
=
557 (struct Win32sModule
*)AppToWine(EDX_reg(context
));
558 struct Win32sModule
*module
= moduleTable
+ ECX_reg(context
);
560 IMAGE_NT_HEADERS
*nt_header
= PE_HEADER(module
->baseAddr
);
561 IMAGE_SECTION_HEADER
*pe_seg
= PE_SECTIONS(module
->baseAddr
);
563 HFILE32 image
= FILE_Open(module
->pathName
, O_RDONLY
);
564 BOOL32 error
= (image
== INVALID_HANDLE_VALUE32
);
567 TRACE(vxd
, "MapModule: Loading %s\n", module
->pathName
);
570 !error
&& i
< nt_header
->FileHeader
.NumberOfSections
;
572 if(!(pe_seg
->Characteristics
& IMAGE_SCN_CNT_UNINITIALIZED_DATA
))
574 DWORD off
= pe_seg
->PointerToRawData
;
575 DWORD len
= pe_seg
->SizeOfRawData
;
576 LPBYTE addr
= module
->baseAddr
+ pe_seg
->VirtualAddress
;
578 TRACE(vxd
, "MapModule: "
579 "Section %d at %08lx from %08lx len %08lx\n",
580 i
, (DWORD
)addr
, off
, len
);
582 if ( _llseek32(image
, off
, SEEK_SET
) != off
583 || _lread32(image
, addr
, len
) != len
)
590 ERR(vxd
, "MapModule: Unable to load %s\n", module
->pathName
);
592 else if (module
->relocDelta
!= 0)
594 IMAGE_DATA_DIRECTORY
*dir
= nt_header
->OptionalHeader
.DataDirectory
595 + IMAGE_DIRECTORY_ENTRY_BASERELOC
;
596 IMAGE_BASE_RELOCATION
*r
= (IMAGE_BASE_RELOCATION
*)
597 (dir
->Size
? module
->baseAddr
+ dir
->VirtualAddress
: 0);
599 TRACE(vxd
, "MapModule: Reloc delta %08lx\n", module
->relocDelta
);
601 while (r
&& r
->VirtualAddress
)
603 LPBYTE page
= module
->baseAddr
+ r
->VirtualAddress
;
604 int count
= (r
->SizeOfBlock
- 8) / 2;
606 TRACE(vxd
, "MapModule: %d relocations for page %08lx\n",
609 for(i
= 0; i
< count
; i
++)
611 int offset
= r
->TypeOffset
[i
] & 0xFFF;
612 int type
= r
->TypeOffset
[i
] >> 12;
615 case IMAGE_REL_BASED_ABSOLUTE
:
617 case IMAGE_REL_BASED_HIGH
:
618 *(WORD
*)(page
+offset
) += HIWORD(module
->relocDelta
);
620 case IMAGE_REL_BASED_LOW
:
621 *(WORD
*)(page
+offset
) += LOWORD(module
->relocDelta
);
623 case IMAGE_REL_BASED_HIGHLOW
:
624 *(DWORD
*)(page
+offset
) += module
->relocDelta
;
627 WARN(vxd
, "MapModule: Unsupported fixup type\n");
632 r
= (IMAGE_BASE_RELOCATION
*)((LPBYTE
)r
+ r
->SizeOfBlock
);
636 EAX_reg(context
) = 0;
637 RESET_CFLAG(context
);
642 case 0x0005: /* UnMap Module */
644 * Input: EDX: Flat address of module image
646 * Output: EAX: 1 if OK
649 TRACE(vxd
, "UnMapModule: %lx\n", (DWORD
)AppToWine(EDX_reg(context
)));
651 /* As we didn't map anything, there's nothing to unmap ... */
653 EAX_reg(context
) = 1;
657 case 0x0006: /* VirtualAlloc */
659 * Input: ECX: Current Process
661 * EDX: Flat address of arguments on stack
663 * DWORD *retv [out] Flat base address of allocated region
664 * LPVOID base [in] Flat address of region to reserve/commit
665 * DWORD size [in] Size of region
666 * DWORD type [in] Type of allocation
667 * DWORD prot [in] Type of access protection
669 * Output: EAX: NtStatus
672 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
673 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
674 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
675 DWORD size
= stack
[2];
676 DWORD type
= stack
[3];
677 DWORD prot
= stack
[4];
680 TRACE(vxd
, "VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
681 (DWORD
)retv
, (DWORD
)base
, size
, type
, prot
);
683 if (type
& 0x80000000)
685 WARN(vxd
, "VirtualAlloc: strange type %lx\n", type
);
689 if (!base
&& (type
& MEM_COMMIT
) && prot
== PAGE_READONLY
)
691 WARN(vxd
, "VirtualAlloc: NLS hack, allowing write access!\n");
692 prot
= PAGE_READWRITE
;
695 result
= (DWORD
)VirtualAlloc(base
, size
, type
, prot
);
697 if (WineToApp(result
))
698 *retv
= WineToApp(result
),
699 EAX_reg(context
) = STATUS_SUCCESS
;
702 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
707 case 0x0007: /* VirtualFree */
709 * Input: ECX: Current Process
711 * EDX: Flat address of arguments on stack
713 * DWORD *retv [out] TRUE if success, FALSE if failure
714 * LPVOID base [in] Flat address of region
715 * DWORD size [in] Size of region
716 * DWORD type [in] Type of operation
718 * Output: EAX: NtStatus
721 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
722 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
723 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
724 DWORD size
= stack
[2];
725 DWORD type
= stack
[3];
728 TRACE(vxd
, "VirtualFree(%lx, %lx, %lx, %lx)\n",
729 (DWORD
)retv
, (DWORD
)base
, size
, type
);
731 result
= VirtualFree(base
, size
, type
);
735 EAX_reg(context
) = STATUS_SUCCESS
;
738 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
743 case 0x0008: /* VirtualProtect */
745 * Input: ECX: Current Process
747 * EDX: Flat address of arguments on stack
749 * DWORD *retv [out] TRUE if success, FALSE if failure
750 * LPVOID base [in] Flat address of region
751 * DWORD size [in] Size of region
752 * DWORD new_prot [in] Desired access protection
753 * DWORD *old_prot [out] Previous access protection
755 * Output: EAX: NtStatus
758 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
759 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
760 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
761 DWORD size
= stack
[2];
762 DWORD new_prot
= stack
[3];
763 DWORD
*old_prot
= (DWORD
*)AppToWine(stack
[4]);
766 TRACE(vxd
, "VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
767 (DWORD
)retv
, (DWORD
)base
, size
, new_prot
, (DWORD
)old_prot
);
769 result
= VirtualProtect(base
, size
, new_prot
, old_prot
);
773 EAX_reg(context
) = STATUS_SUCCESS
;
776 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
781 case 0x0009: /* VirtualQuery */
783 * Input: ECX: Current Process
785 * EDX: Flat address of arguments on stack
787 * DWORD *retv [out] Nr. bytes returned
788 * LPVOID base [in] Flat address of region
789 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
790 * DWORD len [in] Size of buffer
792 * Output: EAX: NtStatus
795 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
796 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
797 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
798 LPMEMORY_BASIC_INFORMATION info
=
799 (LPMEMORY_BASIC_INFORMATION
)AppToWine(stack
[2]);
800 DWORD len
= stack
[3];
803 TRACE(vxd
, "VirtualQuery(%lx, %lx, %lx, %lx)\n",
804 (DWORD
)retv
, (DWORD
)base
, (DWORD
)info
, len
);
806 result
= VirtualQuery(base
, info
, len
);
809 EAX_reg(context
) = STATUS_SUCCESS
;
814 case 0x000A: /* SetVirtMemProcess */
816 * Input: ECX: Process Handle
818 * EDX: Flat address of region
820 * Output: EAX: NtStatus
823 TRACE(vxd
, "[000a] ECX=%lx EDX=%lx\n",
824 ECX_reg(context
), EDX_reg(context
));
828 EAX_reg(context
) = STATUS_SUCCESS
;
832 case 0x000B: /* ??? some kind of cleanup */
834 * Input: ECX: Process Handle
836 * Output: EAX: NtStatus
839 TRACE(vxd
, "[000b] ECX=%lx\n", ECX_reg(context
));
843 EAX_reg(context
) = STATUS_SUCCESS
;
847 case 0x000C: /* Set Debug Flags */
849 * Input: EDX: Debug Flags
851 * Output: EDX: Previous Debug Flags
854 FIXME(vxd
, "[000c] EDX=%lx\n", EDX_reg(context
));
858 EDX_reg(context
) = 0;
862 case 0x000D: /* NtCreateSection */
864 * Input: EDX: Flat address of arguments on stack
866 * HANDLE32 *retv [out] Handle of Section created
867 * DWORD flags1 [in] (?? unknown ??)
868 * DWORD atom [in] Name of Section to create
869 * LARGE_INTEGER *size [in] Size of Section
870 * DWORD protect [in] Access protection
871 * DWORD flags2 [in] (?? unknown ??)
872 * HFILE32 hFile [in] Handle of file to map
873 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
875 * Output: EAX: NtStatus
878 DWORD
*stack
= (DWORD
*) AppToWine(EDX_reg(context
));
879 HANDLE32
*retv
= (HANDLE32
*)AppToWine(stack
[0]);
880 DWORD flags1
= stack
[1];
881 DWORD atom
= stack
[2];
882 LARGE_INTEGER
*size
= (LARGE_INTEGER
*)AppToWine(stack
[3]);
883 DWORD protect
= stack
[4];
884 DWORD flags2
= stack
[5];
885 HFILE32 hFile
= HFILE16_TO_HFILE32(stack
[6]);
886 DWORD psp
= stack
[7];
888 HANDLE32 result
= INVALID_HANDLE_VALUE32
;
891 TRACE(vxd
, "NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
892 (DWORD
)retv
, flags1
, atom
, (DWORD
)size
, protect
, flags2
,
895 if (!atom
|| GlobalGetAtomName32A(atom
, name
, sizeof(name
)))
897 TRACE(vxd
, "NtCreateSection: name=%s\n", atom
? name
: NULL
);
899 result
= CreateFileMapping32A(hFile
, NULL
, protect
,
900 size
? size
->HighPart
: 0,
901 size
? size
->LowPart
: 0,
905 if (result
== INVALID_HANDLE_VALUE32
)
906 WARN(vxd
, "NtCreateSection: failed!\n");
908 TRACE(vxd
, "NtCreateSection: returned %lx\n", (DWORD
)result
);
910 if (result
!= INVALID_HANDLE_VALUE32
)
912 EAX_reg(context
) = STATUS_SUCCESS
;
915 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
920 case 0x000E: /* NtOpenSection */
922 * Input: EDX: Flat address of arguments on stack
924 * HANDLE32 *retv [out] Handle of Section opened
925 * DWORD protect [in] Access protection
926 * DWORD atom [in] Name of Section to create
928 * Output: EAX: NtStatus
931 DWORD
*stack
= (DWORD
*) AppToWine(EDX_reg(context
));
932 HANDLE32
*retv
= (HANDLE32
*)AppToWine(stack
[0]);
933 DWORD protect
= stack
[1];
934 DWORD atom
= stack
[2];
936 HANDLE32 result
= INVALID_HANDLE_VALUE32
;
939 TRACE(vxd
, "NtOpenSection(%lx, %lx, %lx)\n",
940 (DWORD
)retv
, protect
, atom
);
942 if (atom
&& GlobalGetAtomName32A(atom
, name
, sizeof(name
)))
944 TRACE(vxd
, "NtOpenSection: name=%s\n", name
);
946 result
= OpenFileMapping32A(protect
, FALSE
, name
);
949 if (result
== INVALID_HANDLE_VALUE32
)
950 WARN(vxd
, "NtOpenSection: failed!\n");
952 TRACE(vxd
, "NtOpenSection: returned %lx\n", (DWORD
)result
);
954 if (result
!= INVALID_HANDLE_VALUE32
)
956 EAX_reg(context
) = STATUS_SUCCESS
;
959 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
964 case 0x000F: /* NtCloseSection */
966 * Input: EDX: Flat address of arguments on stack
968 * HANDLE32 handle [in] Handle of Section to close
969 * DWORD *id [out] Unique ID (?? unclear ??)
971 * Output: EAX: NtStatus
974 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
975 HANDLE32 handle
= stack
[0];
976 DWORD
*id
= (DWORD
*)AppToWine(stack
[1]);
978 TRACE(vxd
, "NtCloseSection(%lx, %lx)\n", (DWORD
)handle
, (DWORD
)id
);
981 if (id
) *id
= 0; /* FIXME */
983 EAX_reg(context
) = STATUS_SUCCESS
;
988 case 0x0010: /* NtDupSection */
990 * Input: EDX: Flat address of arguments on stack
992 * HANDLE32 handle [in] Handle of Section to duplicate
994 * Output: EAX: NtStatus
997 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
998 HANDLE32 handle
= stack
[0];
1000 TRACE(vxd
, "NtDupSection(%lx)\n", (DWORD
)handle
);
1002 /* Handle is 'duplicated' by incrementing RefCount */
1003 HANDLE_GetObjPtr(PROCESS_Current(), handle
, K32OBJ_MEM_MAPPED_FILE
, 0,NULL
);
1005 EAX_reg(context
) = STATUS_SUCCESS
;
1010 case 0x0011: /* NtMapViewOfSection */
1012 * Input: EDX: Flat address of arguments on stack
1014 * HANDLE32 SectionHandle [in] Section to be mapped
1015 * DWORD ProcessHandle [in] Process to be mapped into
1016 * DWORD * BaseAddress [in/out] Address to be mapped at
1017 * DWORD ZeroBits [in] (?? unclear ??)
1018 * DWORD CommitSize [in] (?? unclear ??)
1019 * LARGE_INTEGER *SectionOffset [in] Offset within section
1020 * DWORD * ViewSize [in] Size of view
1021 * DWORD InheritDisposition [in] (?? unclear ??)
1022 * DWORD AllocationType [in] (?? unclear ??)
1023 * DWORD Protect [in] Access protection
1025 * Output: EAX: NtStatus
1028 DWORD
* stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1029 HANDLE32 SectionHandle
= stack
[0];
1030 DWORD ProcessHandle
= stack
[1]; /* ignored */
1031 DWORD
* BaseAddress
= (DWORD
*)AppToWine(stack
[2]);
1032 DWORD ZeroBits
= stack
[3];
1033 DWORD CommitSize
= stack
[4];
1034 LARGE_INTEGER
*SectionOffset
= (LARGE_INTEGER
*)AppToWine(stack
[5]);
1035 DWORD
* ViewSize
= (DWORD
*)AppToWine(stack
[6]);
1036 DWORD InheritDisposition
= stack
[7];
1037 DWORD AllocationType
= stack
[8];
1038 DWORD Protect
= stack
[9];
1040 LPBYTE address
= (LPBYTE
)(BaseAddress
? AppToWine(*BaseAddress
) : 0);
1041 DWORD access
= 0, result
;
1043 switch (Protect
& ~(PAGE_GUARD
|PAGE_NOCACHE
))
1045 case PAGE_READONLY
: access
= FILE_MAP_READ
; break;
1046 case PAGE_READWRITE
: access
= FILE_MAP_WRITE
; break;
1047 case PAGE_WRITECOPY
: access
= FILE_MAP_COPY
; break;
1049 case PAGE_EXECUTE_READ
: access
= FILE_MAP_READ
; break;
1050 case PAGE_EXECUTE_READWRITE
: access
= FILE_MAP_WRITE
; break;
1051 case PAGE_EXECUTE_WRITECOPY
: access
= FILE_MAP_COPY
; break;
1054 TRACE(vxd
, "NtMapViewOfSection"
1055 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1056 (DWORD
)SectionHandle
, ProcessHandle
, (DWORD
)BaseAddress
,
1057 ZeroBits
, CommitSize
, (DWORD
)SectionOffset
, (DWORD
)ViewSize
,
1058 InheritDisposition
, AllocationType
, Protect
);
1059 TRACE(vxd
, "NtMapViewOfSection: "
1060 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1061 (DWORD
)address
, SectionOffset
? SectionOffset
->LowPart
: 0,
1062 ViewSize
? *ViewSize
: 0, access
);
1064 result
= (DWORD
)MapViewOfFileEx(SectionHandle
, access
,
1065 SectionOffset
? SectionOffset
->HighPart
: 0,
1066 SectionOffset
? SectionOffset
->LowPart
: 0,
1067 ViewSize
? *ViewSize
: 0, address
);
1069 TRACE(vxd
, "NtMapViewOfSection: result=%lx\n", result
);
1071 if (WineToApp(result
))
1073 if (BaseAddress
) *BaseAddress
= WineToApp(result
);
1074 EAX_reg(context
) = STATUS_SUCCESS
;
1077 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1082 case 0x0012: /* NtUnmapViewOfSection */
1084 * Input: EDX: Flat address of arguments on stack
1086 * DWORD ProcessHandle [in] Process (defining address space)
1087 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1089 * Output: EAX: NtStatus
1092 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1093 DWORD ProcessHandle
= stack
[0]; /* ignored */
1094 LPBYTE BaseAddress
= (LPBYTE
)AppToWine(stack
[1]);
1096 TRACE(vxd
, "NtUnmapViewOfSection(%lx, %lx)\n",
1097 ProcessHandle
, (DWORD
)BaseAddress
);
1099 UnmapViewOfFile(BaseAddress
);
1101 EAX_reg(context
) = STATUS_SUCCESS
;
1106 case 0x0013: /* NtFlushVirtualMemory */
1108 * Input: EDX: Flat address of arguments on stack
1110 * DWORD ProcessHandle [in] Process (defining address space)
1111 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1112 * DWORD *ViewSize [in?] Number of bytes to be flushed
1113 * DWORD *unknown [???] (?? unknown ??)
1115 * Output: EAX: NtStatus
1118 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1119 DWORD ProcessHandle
= stack
[0]; /* ignored */
1120 DWORD
*BaseAddress
= (DWORD
*)AppToWine(stack
[1]);
1121 DWORD
*ViewSize
= (DWORD
*)AppToWine(stack
[2]);
1122 DWORD
*unknown
= (DWORD
*)AppToWine(stack
[3]);
1124 LPBYTE address
= (LPBYTE
)(BaseAddress
? AppToWine(*BaseAddress
) : 0);
1125 DWORD size
= ViewSize
? *ViewSize
: 0;
1127 TRACE(vxd
, "NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1128 ProcessHandle
, (DWORD
)BaseAddress
, (DWORD
)ViewSize
,
1130 TRACE(vxd
, "NtFlushVirtualMemory: base=%lx, size=%lx\n",
1131 (DWORD
)address
, size
);
1133 FlushViewOfFile(address
, size
);
1135 EAX_reg(context
) = STATUS_SUCCESS
;
1140 case 0x0014: /* Get/Set Debug Registers */
1142 * Input: ECX: 0 if Get, 1 if Set
1144 * EDX: Get: Flat address of buffer to receive values of
1145 * debug registers DR0 .. DR7
1146 * Set: Flat address of buffer containing values of
1147 * debug registers DR0 .. DR7 to be set
1151 FIXME(vxd
, "[0014] ECX=%lx EDX=%lx\n",
1152 ECX_reg(context
), EDX_reg(context
));
1158 case 0x0015: /* Set Coprocessor Emulation Flag */
1160 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1165 TRACE(vxd
, "[0015] EDX=%lx\n", EDX_reg(context
));
1167 /* We don't care, as we always have a coprocessor anyway */
1171 case 0x0016: /* Init Win32S VxD PSP */
1173 * If called to query required PSP size:
1176 * Output: EDX: Required size of Win32s VxD PSP
1178 * If called to initialize allocated PSP:
1180 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1181 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1185 if (EBX_reg(context
) == 0)
1186 EDX_reg(context
) = 0x80;
1189 PDB
*psp
= PTR_SEG_OFF_TO_LIN(BX_reg(context
), 0);
1191 psp
->fileHandlesPtr
= MAKELONG(HIWORD(EBX_reg(context
)), 0x5c);
1192 memset((LPBYTE
)psp
+ 0x5c, '\xFF', 32);
1197 case 0x0017: /* Set Break Point */
1199 * Input: EBX: Offset of Break Point
1200 * CX: Selector of Break Point
1205 FIXME(vxd
, "[0017] EBX=%lx CX=%x\n",
1206 EBX_reg(context
), CX_reg(context
));
1212 case 0x0018: /* VirtualLock */
1214 * Input: ECX: Current Process
1216 * EDX: Flat address of arguments on stack
1218 * DWORD *retv [out] TRUE if success, FALSE if failure
1219 * LPVOID base [in] Flat address of range to lock
1220 * DWORD size [in] Size of range
1222 * Output: EAX: NtStatus
1225 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1226 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
1227 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
1228 DWORD size
= stack
[2];
1231 TRACE(vxd
, "VirtualLock(%lx, %lx, %lx)\n",
1232 (DWORD
)retv
, (DWORD
)base
, size
);
1234 result
= VirtualLock(base
, size
);
1238 EAX_reg(context
) = STATUS_SUCCESS
;
1241 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1246 case 0x0019: /* VirtualUnlock */
1248 * Input: ECX: Current Process
1250 * EDX: Flat address of arguments on stack
1252 * DWORD *retv [out] TRUE if success, FALSE if failure
1253 * LPVOID base [in] Flat address of range to unlock
1254 * DWORD size [in] Size of range
1256 * Output: EAX: NtStatus
1259 DWORD
*stack
= (DWORD
*)AppToWine(EDX_reg(context
));
1260 DWORD
*retv
= (DWORD
*)AppToWine(stack
[0]);
1261 LPVOID base
= (LPVOID
) AppToWine(stack
[1]);
1262 DWORD size
= stack
[2];
1265 TRACE(vxd
, "VirtualUnlock(%lx, %lx, %lx)\n",
1266 (DWORD
)retv
, (DWORD
)base
, size
);
1268 result
= VirtualUnlock(base
, size
);
1272 EAX_reg(context
) = STATUS_SUCCESS
;
1275 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1280 case 0x001A: /* KGetSystemInfo */
1284 * Output: ECX: Start of sparse memory arena
1285 * EDX: End of sparse memory arena
1288 TRACE(vxd
, "KGetSystemInfo()\n");
1291 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1292 * sparse memory arena. We do it the other way around, since
1293 * we have to reserve 3GB - 4GB for Linux, and thus use
1294 * 0GB - 3GB as sparse memory arena.
1296 * FIXME: What about other OSes ?
1299 ECX_reg(context
) = WineToApp(0x00000000);
1300 EDX_reg(context
) = WineToApp(0xbfffffff);
1304 case 0x001B: /* KGlobalMemStat */
1306 * Input: ESI: Flat address of buffer to receive memory info
1311 struct Win32sMemoryInfo
1313 DWORD DIPhys_Count
; /* Total physical pages */
1314 DWORD DIFree_Count
; /* Free physical pages */
1315 DWORD DILin_Total_Count
; /* Total virtual pages (private arena) */
1316 DWORD DILin_Total_Free
; /* Free virtual pages (private arena) */
1318 DWORD SparseTotal
; /* Total size of sparse arena (bytes ?) */
1319 DWORD SparseFree
; /* Free size of sparse arena (bytes ?) */
1322 struct Win32sMemoryInfo
*info
=
1323 (struct Win32sMemoryInfo
*)AppToWine(ESI_reg(context
));
1325 FIXME(vxd
, "KGlobalMemStat(%lx)\n", (DWORD
)info
);
1332 case 0x001C: /* Enable/Disable Exceptions */
1334 * Input: ECX: 0 to disable, 1 to enable exception handling
1339 TRACE(vxd
, "[001c] ECX=%lx\n", ECX_reg(context
));
1345 case 0x001D: /* VirtualAlloc called from 16-bit code */
1347 * Input: EDX: Segmented address of arguments on stack
1349 * LPVOID base [in] Flat address of region to reserve/commit
1350 * DWORD size [in] Size of region
1351 * DWORD type [in] Type of allocation
1352 * DWORD prot [in] Type of access protection
1354 * Output: EAX: NtStatus
1355 * EDX: Flat base address of allocated region
1358 DWORD
*stack
= PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context
)),
1359 HIWORD(EDX_reg(context
)));
1360 LPVOID base
= (LPVOID
)AppToWine(stack
[0]);
1361 DWORD size
= stack
[1];
1362 DWORD type
= stack
[2];
1363 DWORD prot
= stack
[3];
1366 TRACE(vxd
, "VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1367 (DWORD
)base
, size
, type
, prot
);
1369 if (type
& 0x80000000)
1371 WARN(vxd
, "VirtualAlloc16: strange type %lx\n", type
);
1375 result
= (DWORD
)VirtualAlloc(base
, size
, type
, prot
);
1377 if (WineToApp(result
))
1378 EDX_reg(context
) = WineToApp(result
),
1379 EAX_reg(context
) = STATUS_SUCCESS
;
1381 EDX_reg(context
) = 0,
1382 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1387 case 0x001E: /* VirtualFree called from 16-bit code */
1389 * Input: EDX: Segmented address of arguments on stack
1391 * LPVOID base [in] Flat address of region
1392 * DWORD size [in] Size of region
1393 * DWORD type [in] Type of operation
1395 * Output: EAX: NtStatus
1396 * EDX: TRUE if success, FALSE if failure
1399 DWORD
*stack
= PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context
)),
1400 HIWORD(EDX_reg(context
)));
1401 LPVOID base
= (LPVOID
)AppToWine(stack
[0]);
1402 DWORD size
= stack
[1];
1403 DWORD type
= stack
[2];
1406 TRACE(vxd
, "VirtualFree16(%lx, %lx, %lx)\n",
1407 (DWORD
)base
, size
, type
);
1409 result
= VirtualFree(base
, size
, type
);
1412 EDX_reg(context
) = TRUE
,
1413 EAX_reg(context
) = STATUS_SUCCESS
;
1415 EDX_reg(context
) = FALSE
,
1416 EAX_reg(context
) = STATUS_NO_MEMORY
; /* FIXME */
1421 case 0x001F: /* FWorkingSetSize */
1423 * Input: EDX: 0 if Get, 1 if Set
1425 * ECX: Get: Buffer to receive Working Set Size
1426 * Set: Buffer containing Working Set Size
1431 DWORD
*ptr
= (DWORD
*)AppToWine(ECX_reg(context
));
1432 BOOL32 set
= EDX_reg(context
);
1434 TRACE(vxd
, "FWorkingSetSize(%lx, %lx)\n", (DWORD
)ptr
, (DWORD
)set
);
1437 /* We do it differently ... */;
1441 EAX_reg(context
) = STATUS_SUCCESS
;
1447 VXD_BARF( context
, "W32S" );