Release 980927
[wine.git] / msdos / vxd.c
blob011c1177bbbc8b956e6eb6d2915083cfa1f8e329
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 default:
53 VXD_BARF( context, "VMM" );
57 /***********************************************************************
58 * VXD_PageFile
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 );
68 switch(service)
70 case 0x00: /* get version, is this windows version? */
71 TRACE(vxd,"returning version\n");
72 AX_reg(context) = VXD_WinVersion();
73 RESET_CFLAG(context);
74 break;
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? */
81 RESET_CFLAG(context);
82 break;
84 case 0x02: /* delete permanent swap on exit */
85 TRACE(vxd,"VxD PageFile: supposed to delete swap\n");
86 RESET_CFLAG(context);
87 break;
89 case 0x03: /* current temporary swap file size */
90 TRACE(vxd,"VxD PageFile: what is current temp. swap size\n");
91 RESET_CFLAG(context);
92 break;
94 case 0x04: /* read or write?? INTERRUP.D */
95 case 0x05: /* cancel?? INTERRUP.D */
96 case 0x06: /* test I/O valid INTERRUP.D */
97 default:
98 VXD_BARF( context, "pagefile" );
99 break;
104 /***********************************************************************
105 * VXD_Shell
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 */
115 case 0x0000:
116 TRACE(vxd,"returning version\n");
117 AX_reg(context) = VXD_WinVersion();
118 EBX_reg(context) = 1; /* system VM Handle */
119 break;
121 case 0x0001:
122 case 0x0002:
123 case 0x0003:
124 case 0x0004:
125 case 0x0005:
126 TRACE(vxd,"VxD Shell: EDX = %08lx\n",EDX_reg(context));
127 VXD_BARF( context, "shell" );
128 break;
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); */
138 break;
140 case 0x0007:
141 case 0x0008:
142 case 0x0009:
143 case 0x000A:
144 case 0x000B:
145 case 0x000C:
146 case 0x000D:
147 case 0x000E:
148 case 0x000F:
149 case 0x0010:
150 case 0x0011:
151 case 0x0012:
152 case 0x0013:
153 case 0x0014:
154 case 0x0015:
155 case 0x0016:
156 default:
157 TRACE(vxd,"VxD Shell: EDX = %08lx\n",EDX_reg(context));
158 VXD_BARF( context, "shell");
159 break;
164 /***********************************************************************
165 * VXD_Comm
167 void WINAPI VXD_Comm( CONTEXT *context )
169 unsigned service = AX_reg(context);
171 TRACE(vxd,"[%04x] Comm\n", (UINT16)service);
173 switch (service)
175 case 0x0000: /* get version */
176 TRACE(vxd,"returning version\n");
177 AX_reg(context) = VXD_WinVersion();
178 RESET_CFLAG(context);
179 break;
181 case 0x0001: /* set port global */
182 case 0x0002: /* get focus */
183 case 0x0003: /* virtualise port */
184 default:
185 VXD_BARF( context, "comm" );
189 /***********************************************************************
190 * VXD_Timer
192 void VXD_Timer( CONTEXT *context )
194 unsigned service = AX_reg(context);
196 TRACE(vxd,"[%04x] Virtual Timer\n", (UINT16)service);
198 switch(service)
200 case 0x0000: /* version */
201 AX_reg(context) = VXD_WinVersion();
202 RESET_CFLAG(context);
203 break;
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 */
210 break;
212 case 0x0101: /* current Windows time, msecs */
213 case 0x0102: /* current VM time, msecs */
214 EAX_reg(context) = GetTickCount();
215 break;
217 default:
218 VXD_BARF( context, "VTD" );
222 /***********************************************************************
223 * VXD_TimerAPI
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);
234 switch(service)
236 case 0x0000: /* version */
237 AX_reg(context) = VXD_WinVersion();
238 RESET_CFLAG(context);
239 break;
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);
249 break;
251 default:
252 VXD_BARF( context, "VTDAPI" );
256 /***********************************************************************
257 * VXD_ConfigMG
259 void VXD_ConfigMG ( CONTEXT *context )
261 unsigned service = AX_reg(context);
263 TRACE(vxd,"[%04x] ConfigMG \n", (UINT16)service);
265 switch(service)
267 case 0x0000: /* version */
268 AX_reg(context) = VXD_WinVersion();
269 RESET_CFLAG(context);
270 break;
272 default:
273 VXD_BARF( context, "CONFIGMG" );
277 /***********************************************************************
278 * VXD_Win32s
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 */
341 * Input: None
343 * Output: EAX: LoWord: Win32s Version (1.30)
344 * HiWord: VxD Version (200)
346 * EBX: Build (172)
348 * ECX: ??? (1)
350 * EDX: Debugging Flags
352 * EDI: Error Flag
353 * 0 if OK,
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;
422 int found = 0;
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; }
429 else found = 0;
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;
449 break;
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));
473 /* FIXME */
475 EAX_reg(context) = 0;
476 break;
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));
495 /* FIXME */
497 EAX_reg(context) = ECX_reg(context);
498 break;
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));
512 /* FIXME */
514 EAX_reg(context) = 6;
515 break;
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;
532 else
535 * Structure of a Win32s Module Table Entry:
537 struct Win32sModule
539 DWORD flags;
540 DWORD flatBaseAddr;
541 LPCSTR moduleName;
542 LPCSTR pathName;
543 LPCSTR unknown;
544 LPBYTE baseAddr;
545 DWORD hModule;
546 DWORD relocDelta;
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
553 * into memory.
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);
565 UINT32 i;
567 TRACE(vxd, "MapModule: Loading %s\n", module->pathName);
569 for (i = 0;
570 !error && i < nt_header->FileHeader.NumberOfSections;
571 i++, pe_seg++)
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)
584 error = TRUE;
587 _lclose32(image);
589 if (error)
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",
607 count, (DWORD)page);
609 for(i = 0; i < count; i++)
611 int offset = r->TypeOffset[i] & 0xFFF;
612 int type = r->TypeOffset[i] >> 12;
613 switch(type)
615 case IMAGE_REL_BASED_ABSOLUTE:
616 break;
617 case IMAGE_REL_BASED_HIGH:
618 *(WORD *)(page+offset) += HIWORD(module->relocDelta);
619 break;
620 case IMAGE_REL_BASED_LOW:
621 *(WORD *)(page+offset) += LOWORD(module->relocDelta);
622 break;
623 case IMAGE_REL_BASED_HIGHLOW:
624 *(DWORD*)(page+offset) += module->relocDelta;
625 break;
626 default:
627 WARN(vxd, "MapModule: Unsupported fixup type\n");
628 break;
632 r = (IMAGE_BASE_RELOCATION *)((LPBYTE)r + r->SizeOfBlock);
636 EAX_reg(context) = 0;
637 RESET_CFLAG(context);
639 break;
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;
654 break;
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];
678 DWORD result;
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);
686 type &= 0x7fffffff;
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;
700 else
701 *retv = 0,
702 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
704 break;
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];
726 DWORD result;
728 TRACE(vxd, "VirtualFree(%lx, %lx, %lx, %lx)\n",
729 (DWORD)retv, (DWORD)base, size, type);
731 result = VirtualFree(base, size, type);
733 if (result)
734 *retv = TRUE,
735 EAX_reg(context) = STATUS_SUCCESS;
736 else
737 *retv = FALSE,
738 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
740 break;
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]);
764 DWORD result;
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);
771 if (result)
772 *retv = TRUE,
773 EAX_reg(context) = STATUS_SUCCESS;
774 else
775 *retv = FALSE,
776 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
778 break;
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];
801 DWORD result;
803 TRACE(vxd, "VirtualQuery(%lx, %lx, %lx, %lx)\n",
804 (DWORD)retv, (DWORD)base, (DWORD)info, len);
806 result = VirtualQuery(base, info, len);
808 *retv = result;
809 EAX_reg(context) = STATUS_SUCCESS;
811 break;
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));
826 /* FIXME */
828 EAX_reg(context) = STATUS_SUCCESS;
829 break;
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));
841 /* FIXME */
843 EAX_reg(context) = STATUS_SUCCESS;
844 break;
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));
856 /* FIXME */
858 EDX_reg(context) = 0;
859 break;
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;
889 char name[128];
891 TRACE(vxd, "NtCreateSection(%lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx)\n",
892 (DWORD)retv, flags1, atom, (DWORD)size, protect, flags2,
893 (DWORD)hFile, psp);
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,
902 atom? name : NULL);
905 if (result == INVALID_HANDLE_VALUE32)
906 WARN(vxd, "NtCreateSection: failed!\n");
907 else
908 TRACE(vxd, "NtCreateSection: returned %lx\n", (DWORD)result);
910 if (result != INVALID_HANDLE_VALUE32)
911 *retv = result,
912 EAX_reg(context) = STATUS_SUCCESS;
913 else
914 *retv = result,
915 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
917 break;
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;
937 char name[128];
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");
951 else
952 TRACE(vxd, "NtOpenSection: returned %lx\n", (DWORD)result);
954 if (result != INVALID_HANDLE_VALUE32)
955 *retv = result,
956 EAX_reg(context) = STATUS_SUCCESS;
957 else
958 *retv = result,
959 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
961 break;
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);
980 CloseHandle(handle);
981 if (id) *id = 0; /* FIXME */
983 EAX_reg(context) = STATUS_SUCCESS;
985 break;
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;
1007 break;
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;
1076 else
1077 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1079 break;
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;
1103 break;
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,
1129 (DWORD)unknown);
1130 TRACE(vxd, "NtFlushVirtualMemory: base=%lx, size=%lx\n",
1131 (DWORD)address, size);
1133 FlushViewOfFile(address, size);
1135 EAX_reg(context) = STATUS_SUCCESS;
1137 break;
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
1148 * Output: None
1151 FIXME(vxd, "[0014] ECX=%lx EDX=%lx\n",
1152 ECX_reg(context), EDX_reg(context));
1154 /* FIXME */
1155 break;
1158 case 0x0015: /* Set Coprocessor Emulation Flag */
1160 * Input: EDX: 0 to deactivate, 1 to activate coprocessor emulation
1162 * Output: None
1165 TRACE(vxd, "[0015] EDX=%lx\n", EDX_reg(context));
1167 /* We don't care, as we always have a coprocessor anyway */
1168 break;
1171 case 0x0016: /* Init Win32S VxD PSP */
1173 * If called to query required PSP size:
1175 * Input: EBX: 0
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)
1182 * Output: None
1185 if (EBX_reg(context) == 0)
1186 EDX_reg(context) = 0x80;
1187 else
1189 PDB *psp = PTR_SEG_OFF_TO_LIN(BX_reg(context), 0);
1190 psp->nbFiles = 32;
1191 psp->fileHandlesPtr = MAKELONG(HIWORD(EBX_reg(context)), 0x5c);
1192 memset((LPBYTE)psp + 0x5c, '\xFF', 32);
1194 break;
1197 case 0x0017: /* Set Break Point */
1199 * Input: EBX: Offset of Break Point
1200 * CX: Selector of Break Point
1202 * Output: None
1205 FIXME(vxd, "[0017] EBX=%lx CX=%x\n",
1206 EBX_reg(context), CX_reg(context));
1208 /* FIXME */
1209 break;
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];
1229 DWORD result;
1231 TRACE(vxd, "VirtualLock(%lx, %lx, %lx)\n",
1232 (DWORD)retv, (DWORD)base, size);
1234 result = VirtualLock(base, size);
1236 if (result)
1237 *retv = TRUE,
1238 EAX_reg(context) = STATUS_SUCCESS;
1239 else
1240 *retv = FALSE,
1241 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1243 break;
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];
1263 DWORD result;
1265 TRACE(vxd, "VirtualUnlock(%lx, %lx, %lx)\n",
1266 (DWORD)retv, (DWORD)base, size);
1268 result = VirtualUnlock(base, size);
1270 if (result)
1271 *retv = TRUE,
1272 EAX_reg(context) = STATUS_SUCCESS;
1273 else
1274 *retv = FALSE,
1275 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1277 break;
1280 case 0x001A: /* KGetSystemInfo */
1282 * Input: None
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);
1301 break;
1304 case 0x001B: /* KGlobalMemStat */
1306 * Input: ESI: Flat address of buffer to receive memory info
1308 * Output: None
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);
1327 /* FIXME */
1329 break;
1332 case 0x001C: /* Enable/Disable Exceptions */
1334 * Input: ECX: 0 to disable, 1 to enable exception handling
1336 * Output: None
1339 TRACE(vxd, "[001c] ECX=%lx\n", ECX_reg(context));
1341 /* FIXME */
1342 break;
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];
1364 DWORD result;
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);
1372 type &= 0x7fffffff;
1375 result = (DWORD)VirtualAlloc(base, size, type, prot);
1377 if (WineToApp(result))
1378 EDX_reg(context) = WineToApp(result),
1379 EAX_reg(context) = STATUS_SUCCESS;
1380 else
1381 EDX_reg(context) = 0,
1382 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1384 break;
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];
1404 DWORD result;
1406 TRACE(vxd, "VirtualFree16(%lx, %lx, %lx)\n",
1407 (DWORD)base, size, type);
1409 result = VirtualFree(base, size, type);
1411 if (result)
1412 EDX_reg(context) = TRUE,
1413 EAX_reg(context) = STATUS_SUCCESS;
1414 else
1415 EDX_reg(context) = FALSE,
1416 EAX_reg(context) = STATUS_NO_MEMORY; /* FIXME */
1418 break;
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
1428 * Output: NtStatus
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);
1436 if (set)
1437 /* We do it differently ... */;
1438 else
1439 *ptr = 0x100;
1441 EAX_reg(context) = STATUS_SUCCESS;
1443 break;
1446 default:
1447 VXD_BARF( context, "W32S" );
1450 #undef AppToWine
1451 #undef WineToApp