Fixed bug in SwitchStackTo.
[wine/multimedia.git] / msdos / vxd.c
blob35eaeeb7ab54555a49c97b6e461764473655a4e4
1 /*
2 * VxD emulation
4 * Copyright 1995 Anand Kumria
5 */
7 #include <fcntl.h>
8 #include <memory.h>
9 #include "windows.h"
10 #include "winbase.h"
11 #include "msdos.h"
12 #include "miscemu.h"
13 #include "selectors.h"
14 #include "module.h"
15 #include "task.h"
16 #include "process.h"
17 #include "file.h"
18 #include "debug.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 /***********************************************************************
37 * VXD_VMM
39 void VXD_VMM ( CONTEXT *context )
41 unsigned service = AX_reg(context);
43 TRACE(vxd,"[%04x] VMM \n", (UINT16)service);
45 switch(service)
47 case 0x0000: /* version */
48 AX_reg(context) = VXD_WinVersion();
49 RESET_CFLAG(context);
50 break;
52 case 0x026d: /* Get_Debug_Flag '/m' */
53 case 0x026e: /* Get_Debug_Flag '/n' */
54 AL_reg(context) = 0;
55 RESET_CFLAG(context);
56 break;
58 default:
59 VXD_BARF( context, "VMM" );
63 /***********************************************************************
64 * VXD_PageFile
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 );
74 switch(service)
76 case 0x00: /* get version, is this windows version? */
77 TRACE(vxd,"returning version\n");
78 AX_reg(context) = VXD_WinVersion();
79 RESET_CFLAG(context);
80 break;
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? */
87 RESET_CFLAG(context);
88 break;
90 case 0x02: /* delete permanent swap on exit */
91 TRACE(vxd,"VxD PageFile: supposed to delete swap\n");
92 RESET_CFLAG(context);
93 break;
95 case 0x03: /* current temporary swap file size */
96 TRACE(vxd,"VxD PageFile: what is current temp. swap size\n");
97 RESET_CFLAG(context);
98 break;
100 case 0x04: /* read or write?? INTERRUP.D */
101 case 0x05: /* cancel?? INTERRUP.D */
102 case 0x06: /* test I/O valid INTERRUP.D */
103 default:
104 VXD_BARF( context, "pagefile" );
105 break;
109 /***********************************************************************
110 * VXD_Reboot
112 void VXD_Reboot ( CONTEXT *context )
114 unsigned service = AX_reg(context);
116 TRACE(vxd,"[%04x] VMM \n", (UINT16)service);
118 switch(service)
120 case 0x0000: /* version */
121 AX_reg(context) = VXD_WinVersion();
122 RESET_CFLAG(context);
123 break;
125 default:
126 VXD_BARF( context, "REBOOT" );
130 /***********************************************************************
131 * VXD_VDD
133 void VXD_VDD ( CONTEXT *context )
135 unsigned service = AX_reg(context);
137 TRACE(vxd,"[%04x] VMM \n", (UINT16)service);
139 switch(service)
141 case 0x0000: /* version */
142 AX_reg(context) = VXD_WinVersion();
143 RESET_CFLAG(context);
144 break;
146 default:
147 VXD_BARF( context, "VDD" );
151 /***********************************************************************
152 * VXD_Shell
154 void WINAPI VXD_Shell( CONTEXT *context )
156 unsigned service = DX_reg(context);
158 TRACE(vxd,"[%04x] Shell\n", (UINT16)service);
160 switch (service) /* Ralf Brown says EDX, but I use DX instead */
162 case 0x0000:
163 TRACE(vxd,"returning version\n");
164 AX_reg(context) = VXD_WinVersion();
165 EBX_reg(context) = 1; /* system VM Handle */
166 break;
168 case 0x0001:
169 case 0x0002:
170 case 0x0003:
171 case 0x0004:
172 case 0x0005:
173 TRACE(vxd,"VxD Shell: EDX = %08lx\n",EDX_reg(context));
174 VXD_BARF( context, "shell" );
175 break;
177 case 0x0006: /* SHELL_Get_VM_State */
178 TRACE(vxd,"VxD Shell: returning VM state\n");
179 /* Actually we don't, not yet. We have to return a structure
180 * and I am not to sure how to set it up and return it yet,
181 * so for now let's do nothing. I can (hopefully) get this
182 * by the next release
184 /* RESET_CFLAG(context); */
185 break;
187 case 0x0007:
188 case 0x0008:
189 case 0x0009:
190 case 0x000A:
191 case 0x000B:
192 case 0x000C:
193 case 0x000D:
194 case 0x000E:
195 case 0x000F:
196 case 0x0010:
197 case 0x0011:
198 case 0x0012:
199 case 0x0013:
200 case 0x0014:
201 case 0x0015:
202 case 0x0016:
203 default:
204 TRACE(vxd,"VxD Shell: EDX = %08lx\n",EDX_reg(context));
205 VXD_BARF( context, "shell");
206 break;
211 /***********************************************************************
212 * VXD_Comm
214 void WINAPI VXD_Comm( CONTEXT *context )
216 unsigned service = AX_reg(context);
218 TRACE(vxd,"[%04x] Comm\n", (UINT16)service);
220 switch (service)
222 case 0x0000: /* get version */
223 TRACE(vxd,"returning version\n");
224 AX_reg(context) = VXD_WinVersion();
225 RESET_CFLAG(context);
226 break;
228 case 0x0001: /* set port global */
229 case 0x0002: /* get focus */
230 case 0x0003: /* virtualise port */
231 default:
232 VXD_BARF( context, "comm" );
236 /***********************************************************************
237 * VXD_Timer
239 void VXD_Timer( CONTEXT *context )
241 unsigned service = AX_reg(context);
243 TRACE(vxd,"[%04x] Virtual Timer\n", (UINT16)service);
245 switch(service)
247 case 0x0000: /* version */
248 AX_reg(context) = VXD_WinVersion();
249 RESET_CFLAG(context);
250 break;
252 case 0x0100: /* clock tick time, in 840nsecs */
253 EAX_reg(context) = GetTickCount();
255 EDX_reg(context) = EAX_reg(context) >> 22;
256 EAX_reg(context) <<= 10; /* not very precise */
257 break;
259 case 0x0101: /* current Windows time, msecs */
260 case 0x0102: /* current VM time, msecs */
261 EAX_reg(context) = GetTickCount();
262 break;
264 default:
265 VXD_BARF( context, "VTD" );
269 /***********************************************************************
270 * VXD_TimerAPI
272 void VXD_TimerAPI ( CONTEXT *context )
274 static DWORD clockTicks = 0;
275 static WORD clockTickSelector = 0;
277 unsigned service = AX_reg(context);
279 TRACE(vxd,"[%04x] TimerAPI \n", (UINT16)service);
281 switch(service)
283 case 0x0000: /* version */
284 AX_reg(context) = VXD_WinVersion();
285 RESET_CFLAG(context);
286 break;
288 case 0x0009: /* get system time selector */
289 FIXME(vxd, "Get_System_Time_Selector: this clock doesn't tick!\n");
291 if ( !clockTickSelector )
292 clockTickSelector = SELECTOR_AllocBlock( &clockTicks, sizeof(DWORD),
293 SEGMENT_DATA, FALSE, TRUE );
294 AX_reg(context) = clockTickSelector;
295 RESET_CFLAG(context);
296 break;
298 default:
299 VXD_BARF( context, "VTDAPI" );
303 /***********************************************************************
304 * VXD_ConfigMG
306 void VXD_ConfigMG ( CONTEXT *context )
308 unsigned service = AX_reg(context);
310 TRACE(vxd,"[%04x] ConfigMG \n", (UINT16)service);
312 switch(service)
314 case 0x0000: /* version */
315 AX_reg(context) = VXD_WinVersion();
316 RESET_CFLAG(context);
317 break;
319 default:
320 VXD_BARF( context, "CONFIGMG" );
324 /***********************************************************************
325 * VXD_Win32s
327 * This is an implementation of the services of the Win32s VxD.
328 * Since official documentation of these does not seem to be available,
329 * certain arguments of some of the services remain unclear.
331 * FIXME: The following services are currently unimplemented:
332 * Exception handling (0x01, 0x1C)
333 * Debugger support (0x0C, 0x14, 0x17)
334 * Low-level memory access (0x02, 0x03, 0x0A, 0x0B)
335 * Memory Statistics (0x1B)
338 * We have a specific problem running Win32s on Linux (and probably also
339 * the other x86 unixes), since Win32s tries to allocate its main 'flat
340 * code/data segment' selectors with a base of 0xffff0000 (and limit 4GB).
341 * The rationale for this seems to be that they want one the one hand to
342 * be able to leave the Win 3.1 memory (starting with the main DOS memory)
343 * at linear address 0, but want at other hand to have offset 0 of the
344 * flat data/code segment point to an unmapped page (to catch NULL pointer
345 * accesses). Hence they allocate the flat segments with a base of 0xffff0000
346 * so that the Win 3.1 memory area at linear address zero shows up in the
347 * flat segments at offset 0x10000 (since linear addresses wrap around at
348 * 4GB). To compensate for that discrepancy between flat segment offsets
349 * and plain linear addresses, all flat pointers passed between the 32-bit
350 * and the 16-bit parts of Win32s are shifted by 0x10000 in the appropriate
351 * direction by the glue code (mainly) in W32SKRNL and WIN32S16.
353 * The problem for us is now that Linux does not allow a LDT selector with
354 * base 0xffff0000 to be created, since it would 'see' a part of the kernel
355 * address space. To address this problem we introduce *another* offset:
356 * We add 0x10000 to every linear address we get as an argument from Win32s.
357 * This means especially that the flat code/data selectors get actually
358 * allocated with base 0x0, so that flat offsets and (real) linear addresses
359 * do again agree! In fact, every call e.g. of a Win32s VxD service now
360 * has all pointer arguments (which are offsets in the flat data segement)
361 * first reduced by 0x10000 by the W32SKRNL glue code, and then again
362 * increased by 0x10000 by *our* code.
364 * Note that to keep everything consistent, this offset has to be applied by
365 * every Wine function that operates on 'linear addresses' passed to it by
366 * Win32s. Fortunately, since Win32s does not directly call any Wine 32-bit
367 * API routines, this affects only two locations: this VxD and the DPMI
368 * handler. (NOTE: Should any Win32s application pass a linear address to
369 * any routine apart from those, e.g. some other VxD handler, that code
370 * would have to take the offset into account as well!)
372 * The application of the offset is triggered by marking the current process
373 * as a Win32s process by setting the PDB32_WIN32S_PROC flag in the process
374 * database. This is done the first time any application calls the GetVersion()
375 * service of the Win32s VxD. (Note that the flag is never removed.)
379 void VXD_Win32s( CONTEXT *context )
381 #define AppToWine(addr) ((addr)? ((LPBYTE)(addr)) + 0x10000 : NULL)
382 #define WineToApp(addr) ((addr)? ((DWORD) (addr)) - 0x10000 : 0)
384 switch (AX_reg(context))
386 case 0x0000: /* Get Version */
388 * Input: None
390 * Output: EAX: LoWord: Win32s Version (1.30)
391 * HiWord: VxD Version (200)
393 * EBX: Build (172)
395 * ECX: ??? (1)
397 * EDX: Debugging Flags
399 * EDI: Error Flag
400 * 0 if OK,
401 * 1 if VMCPD VxD not found
404 TRACE(vxd, "GetVersion()\n");
406 EAX_reg(context) = VXD_WinVersion() | (200 << 16);
407 EBX_reg(context) = 0;
408 ECX_reg(context) = 0;
409 EDX_reg(context) = 0;
410 EDI_reg(context) = 0;
413 * If this is the first time we are called for this process,
414 * hack the memory image of WIN32S16 so that it doesn't try
415 * to access the GDT directly ...
417 * The first code segment of WIN32S16 (version 1.30) contains
418 * an unexported function somewhere between the exported functions
419 * SetFS and StackLinearToSegmented that tries to find a selector
420 * in the LDT that maps to the memory image of the LDT itself.
421 * If it succeeds, it stores this selector into a global variable
422 * which will be used to speed up execution by using this selector
423 * to modify the LDT directly instead of using the DPMI calls.
425 * To perform this search of the LDT, this function uses the
426 * sgdt and sldt instructions to find the linear address of
427 * the (GDT and then) LDT. While those instructions themselves
428 * execute without problem, the linear address that sgdt returns
429 * points (at least under Linux) to the kernel address space, so
430 * that any subsequent access leads to a segfault.
432 * Fortunately, WIN32S16 still contains as a fallback option the
433 * mechanism of using DPMI calls to modify LDT selectors instead
434 * of direct writes to the LDT. Thus we can circumvent the problem
435 * by simply replacing the first byte of the offending function
436 * with an 'retf' instruction. This means that the global variable
437 * supposed to contain the LDT alias selector will remain zero,
438 * and hence WIN32S16 will fall back to using DPMI calls.
440 * The heuristic we employ to _find_ that function is as follows:
441 * We search between the addresses of the exported symbols SetFS
442 * and StackLinearToSegmented for the byte sequence '0F 01 04'
443 * (this is the opcode of 'sgdt [si]'). We then search backwards
444 * from this address for the last occurrance of 'CB' (retf) that marks
445 * the end of the preceeding function. The following byte (which
446 * should now be the first byte of the function we are looking for)
447 * will be replaced by 'CB' (retf).
449 * This heuristic works for the retail as well as the debug version
450 * of Win32s version 1.30. For versions earlier than that this
451 * hack should not be necessary at all, since the whole mechanism
452 * ('PERF130') was introduced only in 1.30 to improve the overall
453 * performance of Win32s.
456 if (!(PROCESS_Current()->flags & PDB32_WIN32S_PROC))
458 HMODULE16 hModule = GetModuleHandle16("win32s16");
459 SEGPTR func1 = (SEGPTR)WIN32_GetProcAddress16(hModule, "SetFS");
460 SEGPTR func2 = (SEGPTR)WIN32_GetProcAddress16(hModule,
461 "StackLinearToSegmented");
463 if ( hModule && func1 && func2
464 && SELECTOROF(func1) == SELECTOROF(func2))
466 BYTE *start = PTR_SEG_TO_LIN(func1);
467 BYTE *end = PTR_SEG_TO_LIN(func2);
468 BYTE *p, *retv = NULL;
469 int found = 0;
471 for (p = start; p < end; p++)
472 if (*p == 0xCB) found = 0, retv = p;
473 else if (*p == 0x0F) found = 1;
474 else if (*p == 0x01 && found == 1) found = 2;
475 else if (*p == 0x04 && found == 2) { found = 3; break; }
476 else found = 0;
478 if (found == 3 && retv)
480 TRACE(vxd, "PERF130 hack: "
481 "Replacing byte %02X at offset %04X:%04X\n",
482 *(retv+1), SELECTOROF(func1),
483 OFFSETOF(func1) + retv+1-start);
485 *(retv+1) = (BYTE)0xCB;
491 * Mark process as Win32s, so that subsequent DPMI calls
492 * will perform the AppToWine/WineToApp address shift.
495 PROCESS_Current()->flags |= PDB32_WIN32S_PROC;
496 break;
499 case 0x0001: /* Install Exception Handling */
501 * Input: EBX: Flat address of W32SKRNL Exception Data
503 * ECX: LoWord: Flat Code Selector
504 * HiWord: Flat Data Selector
506 * EDX: Flat address of W32SKRNL Exception Handler
507 * (this is equal to W32S_BackTo32 + 0x40)
509 * ESI: SEGPTR KERNEL.HASGPHANDLER
511 * EDI: SEGPTR phCurrentTask (KERNEL.THHOOK + 0x10)
513 * Output: EAX: 0 if OK
516 TRACE(vxd, "[0001] EBX=%lx ECX=%lx EDX=%lx ESI=%lx EDI=%lx\n",
517 EBX_reg(context), ECX_reg(context), EDX_reg(context),
518 ESI_reg(context), EDI_reg(context));
520 /* FIXME */
522 EAX_reg(context) = 0;
523 break;
526 case 0x0002: /* Set Page Access Flags */
528 * Input: EBX: New access flags
529 * Bit 2: User Page if set, Supervisor Page if clear
530 * Bit 1: Read-Write if set, Read-Only if clear
532 * ECX: Size of memory area to change
534 * EDX: Flat start address of memory area
536 * Output: EAX: Size of area changed
539 TRACE(vxd, "[0002] EBX=%lx ECX=%lx EDX=%lx\n",
540 EBX_reg(context), ECX_reg(context), EDX_reg(context));
542 /* FIXME */
544 EAX_reg(context) = ECX_reg(context);
545 break;
548 case 0x0003: /* Get Page Access Flags */
550 * Input: EDX: Flat address of page to query
552 * Output: EAX: Page access flags
553 * Bit 2: User Page if set, Supervisor Page if clear
554 * Bit 1: Read-Write if set, Read-Only if clear
557 TRACE(vxd, "[0003] EDX=%lx\n", EDX_reg(context));
559 /* FIXME */
561 EAX_reg(context) = 6;
562 break;
565 case 0x0004: /* Map Module */
567 * Input: ECX: IMTE (offset in Module Table) of new module
569 * EDX: Flat address of Win32s Module Table
571 * Output: EAX: 0 if OK
574 if (!EDX_reg(context) || CX_reg(context) == 0xFFFF)
576 TRACE(vxd, "MapModule: Initialization call\n");
577 EAX_reg(context) = 0;
579 else
582 * Structure of a Win32s Module Table Entry:
584 struct Win32sModule
586 DWORD flags;
587 DWORD flatBaseAddr;
588 LPCSTR moduleName;
589 LPCSTR pathName;
590 LPCSTR unknown;
591 LPBYTE baseAddr;
592 DWORD hModule;
593 DWORD relocDelta;
597 * Note: This function should set up a demand-paged memory image
598 * of the given module. Since mmap does not allow file offsets
599 * not aligned at 1024 bytes, we simply load the image fully
600 * into memory.
603 struct Win32sModule *moduleTable =
604 (struct Win32sModule *)AppToWine(EDX_reg(context));
605 struct Win32sModule *module = moduleTable + ECX_reg(context);
607 IMAGE_NT_HEADERS *nt_header = PE_HEADER(module->baseAddr);
608 IMAGE_SECTION_HEADER *pe_seg = PE_SECTIONS(module->baseAddr);
610 HFILE32 image = FILE_Open(module->pathName, O_RDONLY,0);
611 BOOL32 error = (image == INVALID_HANDLE_VALUE32);
612 UINT32 i;
614 TRACE(vxd, "MapModule: Loading %s\n", module->pathName);
616 for (i = 0;
617 !error && i < nt_header->FileHeader.NumberOfSections;
618 i++, pe_seg++)
619 if(!(pe_seg->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
621 DWORD off = pe_seg->PointerToRawData;
622 DWORD len = pe_seg->SizeOfRawData;
623 LPBYTE addr = module->baseAddr + pe_seg->VirtualAddress;
625 TRACE(vxd, "MapModule: "
626 "Section %d at %08lx from %08lx len %08lx\n",
627 i, (DWORD)addr, off, len);
629 if ( _llseek32(image, off, SEEK_SET) != off
630 || _lread32(image, addr, len) != len)
631 error = TRUE;
634 _lclose32(image);
636 if (error)
637 ERR(vxd, "MapModule: Unable to load %s\n", module->pathName);
639 else if (module->relocDelta != 0)
641 IMAGE_DATA_DIRECTORY *dir = nt_header->OptionalHeader.DataDirectory
642 + IMAGE_DIRECTORY_ENTRY_BASERELOC;
643 IMAGE_BASE_RELOCATION *r = (IMAGE_BASE_RELOCATION *)
644 (dir->Size? module->baseAddr + dir->VirtualAddress : 0);
646 TRACE(vxd, "MapModule: Reloc delta %08lx\n", module->relocDelta);
648 while (r && r->VirtualAddress)
650 LPBYTE page = module->baseAddr + r->VirtualAddress;
651 int count = (r->SizeOfBlock - 8) / 2;
653 TRACE(vxd, "MapModule: %d relocations for page %08lx\n",
654 count, (DWORD)page);
656 for(i = 0; i < count; i++)
658 int offset = r->TypeOffset[i] & 0xFFF;
659 int type = r->TypeOffset[i] >> 12;
660 switch(type)
662 case IMAGE_REL_BASED_ABSOLUTE:
663 break;
664 case IMAGE_REL_BASED_HIGH:
665 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
666 break;
667 case IMAGE_REL_BASED_LOW:
668 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
669 break;
670 case IMAGE_REL_BASED_HIGHLOW:
671 *(DWORD*)(page+offset) += module->relocDelta;
672 break;
673 default:
674 WARN(vxd, "MapModule: Unsupported fixup type\n");
675 break;
679 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
683 EAX_reg(context) = 0;
684 RESET_CFLAG(context);
686 break;
689 case 0x0005: /* UnMap Module */
691 * Input: EDX: Flat address of module image
693 * Output: EAX: 1 if OK
696 TRACE(vxd, "UnMapModule: %lx\n", (DWORD)AppToWine(EDX_reg(context)));
698 /* As we didn't map anything, there's nothing to unmap ... */
700 EAX_reg(context) = 1;
701 break;
704 case 0x0006: /* VirtualAlloc */
706 * Input: ECX: Current Process
708 * EDX: Flat address of arguments on stack
710 * DWORD *retv [out] Flat base address of allocated region
711 * LPVOID base [in] Flat address of region to reserve/commit
712 * DWORD size [in] Size of region
713 * DWORD type [in] Type of allocation
714 * DWORD prot [in] Type of access protection
716 * Output: EAX: NtStatus
719 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
720 DWORD *retv = (DWORD *)AppToWine(stack[0]);
721 LPVOID base = (LPVOID) AppToWine(stack[1]);
722 DWORD size = stack[2];
723 DWORD type = stack[3];
724 DWORD prot = stack[4];
725 DWORD result;
727 TRACE(vxd, "VirtualAlloc(%lx, %lx, %lx, %lx, %lx)\n",
728 (DWORD)retv, (DWORD)base, size, type, prot);
730 if (type & 0x80000000)
732 WARN(vxd, "VirtualAlloc: strange type %lx\n", type);
733 type &= 0x7fffffff;
736 if (!base && (type & MEM_COMMIT) && prot == PAGE_READONLY)
738 WARN(vxd, "VirtualAlloc: NLS hack, allowing write access!\n");
739 prot = PAGE_READWRITE;
742 result = (DWORD)VirtualAlloc(base, size, type, prot);
744 if (WineToApp(result))
745 *retv = WineToApp(result),
746 EAX_reg(context) = STATUS_SUCCESS;
747 else
748 *retv = 0,
749 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
751 break;
754 case 0x0007: /* VirtualFree */
756 * Input: ECX: Current Process
758 * EDX: Flat address of arguments on stack
760 * DWORD *retv [out] TRUE if success, FALSE if failure
761 * LPVOID base [in] Flat address of region
762 * DWORD size [in] Size of region
763 * DWORD type [in] Type of operation
765 * Output: EAX: NtStatus
768 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
769 DWORD *retv = (DWORD *)AppToWine(stack[0]);
770 LPVOID base = (LPVOID) AppToWine(stack[1]);
771 DWORD size = stack[2];
772 DWORD type = stack[3];
773 DWORD result;
775 TRACE(vxd, "VirtualFree(%lx, %lx, %lx, %lx)\n",
776 (DWORD)retv, (DWORD)base, size, type);
778 result = VirtualFree(base, size, type);
780 if (result)
781 *retv = TRUE,
782 EAX_reg(context) = STATUS_SUCCESS;
783 else
784 *retv = FALSE,
785 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
787 break;
790 case 0x0008: /* VirtualProtect */
792 * Input: ECX: Current Process
794 * EDX: Flat address of arguments on stack
796 * DWORD *retv [out] TRUE if success, FALSE if failure
797 * LPVOID base [in] Flat address of region
798 * DWORD size [in] Size of region
799 * DWORD new_prot [in] Desired access protection
800 * DWORD *old_prot [out] Previous access protection
802 * Output: EAX: NtStatus
805 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
806 DWORD *retv = (DWORD *)AppToWine(stack[0]);
807 LPVOID base = (LPVOID) AppToWine(stack[1]);
808 DWORD size = stack[2];
809 DWORD new_prot = stack[3];
810 DWORD *old_prot = (DWORD *)AppToWine(stack[4]);
811 DWORD result;
813 TRACE(vxd, "VirtualProtect(%lx, %lx, %lx, %lx, %lx)\n",
814 (DWORD)retv, (DWORD)base, size, new_prot, (DWORD)old_prot);
816 result = VirtualProtect(base, size, new_prot, old_prot);
818 if (result)
819 *retv = TRUE,
820 EAX_reg(context) = STATUS_SUCCESS;
821 else
822 *retv = FALSE,
823 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
825 break;
828 case 0x0009: /* VirtualQuery */
830 * Input: ECX: Current Process
832 * EDX: Flat address of arguments on stack
834 * DWORD *retv [out] Nr. bytes returned
835 * LPVOID base [in] Flat address of region
836 * LPMEMORY_BASIC_INFORMATION info [out] Info buffer
837 * DWORD len [in] Size of buffer
839 * Output: EAX: NtStatus
842 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
843 DWORD *retv = (DWORD *)AppToWine(stack[0]);
844 LPVOID base = (LPVOID) AppToWine(stack[1]);
845 LPMEMORY_BASIC_INFORMATION info =
846 (LPMEMORY_BASIC_INFORMATION)AppToWine(stack[2]);
847 DWORD len = stack[3];
848 DWORD result;
850 TRACE(vxd, "VirtualQuery(%lx, %lx, %lx, %lx)\n",
851 (DWORD)retv, (DWORD)base, (DWORD)info, len);
853 result = VirtualQuery(base, info, len);
855 *retv = result;
856 EAX_reg(context) = STATUS_SUCCESS;
858 break;
861 case 0x000A: /* SetVirtMemProcess */
863 * Input: ECX: Process Handle
865 * EDX: Flat address of region
867 * Output: EAX: NtStatus
870 TRACE(vxd, "[000a] ECX=%lx EDX=%lx\n",
871 ECX_reg(context), EDX_reg(context));
873 /* FIXME */
875 EAX_reg(context) = STATUS_SUCCESS;
876 break;
879 case 0x000B: /* ??? some kind of cleanup */
881 * Input: ECX: Process Handle
883 * Output: EAX: NtStatus
886 TRACE(vxd, "[000b] ECX=%lx\n", ECX_reg(context));
888 /* FIXME */
890 EAX_reg(context) = STATUS_SUCCESS;
891 break;
894 case 0x000C: /* Set Debug Flags */
896 * Input: EDX: Debug Flags
898 * Output: EDX: Previous Debug Flags
901 FIXME(vxd, "[000c] EDX=%lx\n", EDX_reg(context));
903 /* FIXME */
905 EDX_reg(context) = 0;
906 break;
909 case 0x000D: /* NtCreateSection */
911 * Input: EDX: Flat address of arguments on stack
913 * HANDLE32 *retv [out] Handle of Section created
914 * DWORD flags1 [in] (?? unknown ??)
915 * DWORD atom [in] Name of Section to create
916 * LARGE_INTEGER *size [in] Size of Section
917 * DWORD protect [in] Access protection
918 * DWORD flags2 [in] (?? unknown ??)
919 * HFILE32 hFile [in] Handle of file to map
920 * DWORD psp [in] (Win32s: PSP that hFile belongs to)
922 * Output: EAX: NtStatus
925 DWORD *stack = (DWORD *) AppToWine(EDX_reg(context));
926 HANDLE32 *retv = (HANDLE32 *)AppToWine(stack[0]);
927 DWORD flags1 = stack[1];
928 DWORD atom = stack[2];
929 LARGE_INTEGER *size = (LARGE_INTEGER *)AppToWine(stack[3]);
930 DWORD protect = stack[4];
931 DWORD flags2 = stack[5];
932 HFILE32 hFile = HFILE16_TO_HFILE32(stack[6]);
933 DWORD psp = stack[7];
935 HANDLE32 result = INVALID_HANDLE_VALUE32;
936 char name[128];
938 TRACE(vxd, "NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
939 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
940 (DWORD)hFile, psp);
942 if (!atom || GlobalGetAtomName32A(atom, name, sizeof(name)))
944 TRACE(vxd, "NtCreateSection: name=%s\n", atom? name : NULL);
946 result = CreateFileMapping32A(hFile, NULL, protect,
947 size? size->HighPart : 0,
948 size? size->LowPart : 0,
949 atom? name : NULL);
952 if (result == INVALID_HANDLE_VALUE32)
953 WARN(vxd, "NtCreateSection: failed!\n");
954 else
955 TRACE(vxd, "NtCreateSection: returned %lx\n", (DWORD)result);
957 if (result != INVALID_HANDLE_VALUE32)
958 *retv = result,
959 EAX_reg(context) = STATUS_SUCCESS;
960 else
961 *retv = result,
962 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
964 break;
967 case 0x000E: /* NtOpenSection */
969 * Input: EDX: Flat address of arguments on stack
971 * HANDLE32 *retv [out] Handle of Section opened
972 * DWORD protect [in] Access protection
973 * DWORD atom [in] Name of Section to create
975 * Output: EAX: NtStatus
978 DWORD *stack = (DWORD *) AppToWine(EDX_reg(context));
979 HANDLE32 *retv = (HANDLE32 *)AppToWine(stack[0]);
980 DWORD protect = stack[1];
981 DWORD atom = stack[2];
983 HANDLE32 result = INVALID_HANDLE_VALUE32;
984 char name[128];
986 TRACE(vxd, "NtOpenSection(%lx, %lx, %lx)\n",
987 (DWORD)retv, protect, atom);
989 if (atom && GlobalGetAtomName32A(atom, name, sizeof(name)))
991 TRACE(vxd, "NtOpenSection: name=%s\n", name);
993 result = OpenFileMapping32A(protect, FALSE, name);
996 if (result == INVALID_HANDLE_VALUE32)
997 WARN(vxd, "NtOpenSection: failed!\n");
998 else
999 TRACE(vxd, "NtOpenSection: returned %lx\n", (DWORD)result);
1001 if (result != INVALID_HANDLE_VALUE32)
1002 *retv = result,
1003 EAX_reg(context) = STATUS_SUCCESS;
1004 else
1005 *retv = result,
1006 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1008 break;
1011 case 0x000F: /* NtCloseSection */
1013 * Input: EDX: Flat address of arguments on stack
1015 * HANDLE32 handle [in] Handle of Section to close
1016 * DWORD *id [out] Unique ID (?? unclear ??)
1018 * Output: EAX: NtStatus
1021 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
1022 HANDLE32 handle = stack[0];
1023 DWORD *id = (DWORD *)AppToWine(stack[1]);
1025 TRACE(vxd, "NtCloseSection(%lx, %lx)\n", (DWORD)handle, (DWORD)id);
1027 CloseHandle(handle);
1028 if (id) *id = 0; /* FIXME */
1030 EAX_reg(context) = STATUS_SUCCESS;
1032 break;
1035 case 0x0010: /* NtDupSection */
1037 * Input: EDX: Flat address of arguments on stack
1039 * HANDLE32 handle [in] Handle of Section to duplicate
1041 * Output: EAX: NtStatus
1044 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
1045 HANDLE32 handle = stack[0];
1047 TRACE(vxd, "NtDupSection(%lx)\n", (DWORD)handle);
1049 /* Handle is 'duplicated' by incrementing RefCount */
1050 HANDLE_GetObjPtr(PROCESS_Current(), handle, K32OBJ_MEM_MAPPED_FILE, 0,NULL);
1052 EAX_reg(context) = STATUS_SUCCESS;
1054 break;
1057 case 0x0011: /* NtMapViewOfSection */
1059 * Input: EDX: Flat address of arguments on stack
1061 * HANDLE32 SectionHandle [in] Section to be mapped
1062 * DWORD ProcessHandle [in] Process to be mapped into
1063 * DWORD * BaseAddress [in/out] Address to be mapped at
1064 * DWORD ZeroBits [in] (?? unclear ??)
1065 * DWORD CommitSize [in] (?? unclear ??)
1066 * LARGE_INTEGER *SectionOffset [in] Offset within section
1067 * DWORD * ViewSize [in] Size of view
1068 * DWORD InheritDisposition [in] (?? unclear ??)
1069 * DWORD AllocationType [in] (?? unclear ??)
1070 * DWORD Protect [in] Access protection
1072 * Output: EAX: NtStatus
1075 DWORD * stack = (DWORD *)AppToWine(EDX_reg(context));
1076 HANDLE32 SectionHandle = stack[0];
1077 DWORD ProcessHandle = stack[1]; /* ignored */
1078 DWORD * BaseAddress = (DWORD *)AppToWine(stack[2]);
1079 DWORD ZeroBits = stack[3];
1080 DWORD CommitSize = stack[4];
1081 LARGE_INTEGER *SectionOffset = (LARGE_INTEGER *)AppToWine(stack[5]);
1082 DWORD * ViewSize = (DWORD *)AppToWine(stack[6]);
1083 DWORD InheritDisposition = stack[7];
1084 DWORD AllocationType = stack[8];
1085 DWORD Protect = stack[9];
1087 LPBYTE address = (LPBYTE)(BaseAddress? AppToWine(*BaseAddress) : 0);
1088 DWORD access = 0, result;
1090 switch (Protect & ~(PAGE_GUARD|PAGE_NOCACHE))
1092 case PAGE_READONLY: access = FILE_MAP_READ; break;
1093 case PAGE_READWRITE: access = FILE_MAP_WRITE; break;
1094 case PAGE_WRITECOPY: access = FILE_MAP_COPY; break;
1096 case PAGE_EXECUTE_READ: access = FILE_MAP_READ; break;
1097 case PAGE_EXECUTE_READWRITE: access = FILE_MAP_WRITE; break;
1098 case PAGE_EXECUTE_WRITECOPY: access = FILE_MAP_COPY; break;
1101 TRACE(vxd, "NtMapViewOfSection"
1102 "(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
1103 (DWORD)SectionHandle, ProcessHandle, (DWORD)BaseAddress,
1104 ZeroBits, CommitSize, (DWORD)SectionOffset, (DWORD)ViewSize,
1105 InheritDisposition, AllocationType, Protect);
1106 TRACE(vxd, "NtMapViewOfSection: "
1107 "base=%lx, offset=%lx, size=%lx, access=%lx\n",
1108 (DWORD)address, SectionOffset? SectionOffset->LowPart : 0,
1109 ViewSize? *ViewSize : 0, access);
1111 result = (DWORD)MapViewOfFileEx(SectionHandle, access,
1112 SectionOffset? SectionOffset->HighPart : 0,
1113 SectionOffset? SectionOffset->LowPart : 0,
1114 ViewSize? *ViewSize : 0, address);
1116 TRACE(vxd, "NtMapViewOfSection: result=%lx\n", result);
1118 if (WineToApp(result))
1120 if (BaseAddress) *BaseAddress = WineToApp(result);
1121 EAX_reg(context) = STATUS_SUCCESS;
1123 else
1124 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1126 break;
1129 case 0x0012: /* NtUnmapViewOfSection */
1131 * Input: EDX: Flat address of arguments on stack
1133 * DWORD ProcessHandle [in] Process (defining address space)
1134 * LPBYTE BaseAddress [in] Base address of view to be unmapped
1136 * Output: EAX: NtStatus
1139 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
1140 DWORD ProcessHandle = stack[0]; /* ignored */
1141 LPBYTE BaseAddress = (LPBYTE)AppToWine(stack[1]);
1143 TRACE(vxd, "NtUnmapViewOfSection(%lx, %lx)\n",
1144 ProcessHandle, (DWORD)BaseAddress);
1146 UnmapViewOfFile(BaseAddress);
1148 EAX_reg(context) = STATUS_SUCCESS;
1150 break;
1153 case 0x0013: /* NtFlushVirtualMemory */
1155 * Input: EDX: Flat address of arguments on stack
1157 * DWORD ProcessHandle [in] Process (defining address space)
1158 * LPBYTE *BaseAddress [in?] Base address of range to be flushed
1159 * DWORD *ViewSize [in?] Number of bytes to be flushed
1160 * DWORD *unknown [???] (?? unknown ??)
1162 * Output: EAX: NtStatus
1165 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
1166 DWORD ProcessHandle = stack[0]; /* ignored */
1167 DWORD *BaseAddress = (DWORD *)AppToWine(stack[1]);
1168 DWORD *ViewSize = (DWORD *)AppToWine(stack[2]);
1169 DWORD *unknown = (DWORD *)AppToWine(stack[3]);
1171 LPBYTE address = (LPBYTE)(BaseAddress? AppToWine(*BaseAddress) : 0);
1172 DWORD size = ViewSize? *ViewSize : 0;
1174 TRACE(vxd, "NtFlushVirtualMemory(%lx, %lx, %lx, %lx)\n",
1175 ProcessHandle, (DWORD)BaseAddress, (DWORD)ViewSize,
1176 (DWORD)unknown);
1177 TRACE(vxd, "NtFlushVirtualMemory: base=%lx, size=%lx\n",
1178 (DWORD)address, size);
1180 FlushViewOfFile(address, size);
1182 EAX_reg(context) = STATUS_SUCCESS;
1184 break;
1187 case 0x0014: /* Get/Set Debug Registers */
1189 * Input: ECX: 0 if Get, 1 if Set
1191 * EDX: Get: Flat address of buffer to receive values of
1192 * debug registers DR0 .. DR7
1193 * Set: Flat address of buffer containing values of
1194 * debug registers DR0 .. DR7 to be set
1195 * Output: None
1198 FIXME(vxd, "[0014] ECX=%lx EDX=%lx\n",
1199 ECX_reg(context), EDX_reg(context));
1201 /* FIXME */
1202 break;
1205 case 0x0015: /* Set Coprocessor Emulation Flag */
1207 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1209 * Output: None
1212 TRACE(vxd, "[0015] EDX=%lx\n", EDX_reg(context));
1214 /* We don't care, as we always have a coprocessor anyway */
1215 break;
1218 case 0x0016: /* Init Win32S VxD PSP */
1220 * If called to query required PSP size:
1222 * Input: EBX: 0
1223 * Output: EDX: Required size of Win32s VxD PSP
1225 * If called to initialize allocated PSP:
1227 * Input: EBX: LoWord: Selector of Win32s VxD PSP
1228 * HiWord: Paragraph of Win32s VxD PSP (DOSMEM)
1229 * Output: None
1232 if (EBX_reg(context) == 0)
1233 EDX_reg(context) = 0x80;
1234 else
1236 PDB *psp = PTR_SEG_OFF_TO_LIN(BX_reg(context), 0);
1237 psp->nbFiles = 32;
1238 psp->fileHandlesPtr = MAKELONG(HIWORD(EBX_reg(context)), 0x5c);
1239 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1241 break;
1244 case 0x0017: /* Set Break Point */
1246 * Input: EBX: Offset of Break Point
1247 * CX: Selector of Break Point
1249 * Output: None
1252 FIXME(vxd, "[0017] EBX=%lx CX=%x\n",
1253 EBX_reg(context), CX_reg(context));
1255 /* FIXME */
1256 break;
1259 case 0x0018: /* VirtualLock */
1261 * Input: ECX: Current Process
1263 * EDX: Flat address of arguments on stack
1265 * DWORD *retv [out] TRUE if success, FALSE if failure
1266 * LPVOID base [in] Flat address of range to lock
1267 * DWORD size [in] Size of range
1269 * Output: EAX: NtStatus
1272 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
1273 DWORD *retv = (DWORD *)AppToWine(stack[0]);
1274 LPVOID base = (LPVOID) AppToWine(stack[1]);
1275 DWORD size = stack[2];
1276 DWORD result;
1278 TRACE(vxd, "VirtualLock(%lx, %lx, %lx)\n",
1279 (DWORD)retv, (DWORD)base, size);
1281 result = VirtualLock(base, size);
1283 if (result)
1284 *retv = TRUE,
1285 EAX_reg(context) = STATUS_SUCCESS;
1286 else
1287 *retv = FALSE,
1288 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1290 break;
1293 case 0x0019: /* VirtualUnlock */
1295 * Input: ECX: Current Process
1297 * EDX: Flat address of arguments on stack
1299 * DWORD *retv [out] TRUE if success, FALSE if failure
1300 * LPVOID base [in] Flat address of range to unlock
1301 * DWORD size [in] Size of range
1303 * Output: EAX: NtStatus
1306 DWORD *stack = (DWORD *)AppToWine(EDX_reg(context));
1307 DWORD *retv = (DWORD *)AppToWine(stack[0]);
1308 LPVOID base = (LPVOID) AppToWine(stack[1]);
1309 DWORD size = stack[2];
1310 DWORD result;
1312 TRACE(vxd, "VirtualUnlock(%lx, %lx, %lx)\n",
1313 (DWORD)retv, (DWORD)base, size);
1315 result = VirtualUnlock(base, size);
1317 if (result)
1318 *retv = TRUE,
1319 EAX_reg(context) = STATUS_SUCCESS;
1320 else
1321 *retv = FALSE,
1322 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1324 break;
1327 case 0x001A: /* KGetSystemInfo */
1329 * Input: None
1331 * Output: ECX: Start of sparse memory arena
1332 * EDX: End of sparse memory arena
1335 TRACE(vxd, "KGetSystemInfo()\n");
1338 * Note: Win32s reserves 0GB - 2GB for Win 3.1 and uses 2GB - 4GB as
1339 * sparse memory arena. We do it the other way around, since
1340 * we have to reserve 3GB - 4GB for Linux, and thus use
1341 * 0GB - 3GB as sparse memory arena.
1343 * FIXME: What about other OSes ?
1346 ECX_reg(context) = WineToApp(0x00000000);
1347 EDX_reg(context) = WineToApp(0xbfffffff);
1348 break;
1351 case 0x001B: /* KGlobalMemStat */
1353 * Input: ESI: Flat address of buffer to receive memory info
1355 * Output: None
1358 struct Win32sMemoryInfo
1360 DWORD DIPhys_Count; /* Total physical pages */
1361 DWORD DIFree_Count; /* Free physical pages */
1362 DWORD DILin_Total_Count; /* Total virtual pages (private arena) */
1363 DWORD DILin_Total_Free; /* Free virtual pages (private arena) */
1365 DWORD SparseTotal; /* Total size of sparse arena (bytes ?) */
1366 DWORD SparseFree; /* Free size of sparse arena (bytes ?) */
1369 struct Win32sMemoryInfo *info =
1370 (struct Win32sMemoryInfo *)AppToWine(ESI_reg(context));
1372 FIXME(vxd, "KGlobalMemStat(%lx)\n", (DWORD)info);
1374 /* FIXME */
1376 break;
1379 case 0x001C: /* Enable/Disable Exceptions */
1381 * Input: ECX: 0 to disable, 1 to enable exception handling
1383 * Output: None
1386 TRACE(vxd, "[001c] ECX=%lx\n", ECX_reg(context));
1388 /* FIXME */
1389 break;
1392 case 0x001D: /* VirtualAlloc called from 16-bit code */
1394 * Input: EDX: Segmented address of arguments on stack
1396 * LPVOID base [in] Flat address of region to reserve/commit
1397 * DWORD size [in] Size of region
1398 * DWORD type [in] Type of allocation
1399 * DWORD prot [in] Type of access protection
1401 * Output: EAX: NtStatus
1402 * EDX: Flat base address of allocated region
1405 DWORD *stack = PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context)),
1406 HIWORD(EDX_reg(context)));
1407 LPVOID base = (LPVOID)AppToWine(stack[0]);
1408 DWORD size = stack[1];
1409 DWORD type = stack[2];
1410 DWORD prot = stack[3];
1411 DWORD result;
1413 TRACE(vxd, "VirtualAlloc16(%lx, %lx, %lx, %lx)\n",
1414 (DWORD)base, size, type, prot);
1416 if (type & 0x80000000)
1418 WARN(vxd, "VirtualAlloc16: strange type %lx\n", type);
1419 type &= 0x7fffffff;
1422 result = (DWORD)VirtualAlloc(base, size, type, prot);
1424 if (WineToApp(result))
1425 EDX_reg(context) = WineToApp(result),
1426 EAX_reg(context) = STATUS_SUCCESS;
1427 else
1428 EDX_reg(context) = 0,
1429 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1431 break;
1434 case 0x001E: /* VirtualFree called from 16-bit code */
1436 * Input: EDX: Segmented address of arguments on stack
1438 * LPVOID base [in] Flat address of region
1439 * DWORD size [in] Size of region
1440 * DWORD type [in] Type of operation
1442 * Output: EAX: NtStatus
1443 * EDX: TRUE if success, FALSE if failure
1446 DWORD *stack = PTR_SEG_OFF_TO_LIN(LOWORD(EDX_reg(context)),
1447 HIWORD(EDX_reg(context)));
1448 LPVOID base = (LPVOID)AppToWine(stack[0]);
1449 DWORD size = stack[1];
1450 DWORD type = stack[2];
1451 DWORD result;
1453 TRACE(vxd, "VirtualFree16(%lx, %lx, %lx)\n",
1454 (DWORD)base, size, type);
1456 result = VirtualFree(base, size, type);
1458 if (result)
1459 EDX_reg(context) = TRUE,
1460 EAX_reg(context) = STATUS_SUCCESS;
1461 else
1462 EDX_reg(context) = FALSE,
1463 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1465 break;
1468 case 0x001F: /* FWorkingSetSize */
1470 * Input: EDX: 0 if Get, 1 if Set
1472 * ECX: Get: Buffer to receive Working Set Size
1473 * Set: Buffer containing Working Set Size
1475 * Output: NtStatus
1478 DWORD *ptr = (DWORD *)AppToWine(ECX_reg(context));
1479 BOOL32 set = EDX_reg(context);
1481 TRACE(vxd, "FWorkingSetSize(%lx, %lx)\n", (DWORD)ptr, (DWORD)set);
1483 if (set)
1484 /* We do it differently ... */;
1485 else
1486 *ptr = 0x100;
1488 EAX_reg(context) = STATUS_SUCCESS;
1490 break;
1493 default:
1494 VXD_BARF( context, "W32S" );
1497 #undef AppToWine
1498 #undef WineToApp