Added Korean resources.
[wine] / msdos / dpmi.c
1 /*
2  * DPMI 0.9 emulation
3  *
4  * Copyright 1995 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <unistd.h>
25 #include <string.h>
26
27 #include "windef.h"
28 #include "wine/winbase16.h"
29 #include "miscemu.h"
30 #include "msdos.h"
31 #include "task.h"
32 #include "toolhelp.h"
33 #include "selectors.h"
34 #include "callback.h"
35 #include "wine/debug.h"
36 #include "stackframe.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(int31);
39
40 #define DOS_GET_DRIVE(reg) ((reg) ? (reg) - 1 : DRIVE_GetCurrentDrive())
41
42 void CreateBPB(int drive, BYTE *data, BOOL16 limited);  /* defined in int21.c */
43
44 static void* lastvalloced = NULL;
45
46
47 DOSVM_TABLE Dosvm = { NULL, };
48
49 static HMODULE DosModule;
50
51 /**********************************************************************
52  *          DPMI_LoadDosSystem
53  */
54 BOOL DPMI_LoadDosSystem(void)
55 {
56     if (DosModule) return TRUE;
57     DosModule = LoadLibraryA( "winedos.dll" );
58     if (!DosModule) {
59         ERR("could not load winedos.dll, DOS subsystem unavailable\n");
60         return FALSE;
61     }
62 #define GET_ADDR(func)  Dosvm.func = (void *)GetProcAddress(DosModule, #func);
63
64     GET_ADDR(LoadDosExe);
65     GET_ADDR(CallRMInt);
66     GET_ADDR(CallRMProc);
67     GET_ADDR(AllocRMCB);
68     GET_ADDR(FreeRMCB);
69     GET_ADDR(RawModeSwitch);
70     GET_ADDR(SetTimer);
71     GET_ADDR(GetTimer);
72     GET_ADDR(inport);
73     GET_ADDR(outport);
74     GET_ADDR(ASPIHandler);
75 #undef GET_ADDR
76     return TRUE;
77 }
78
79 /**********************************************************************
80  *          DPMI_xalloc
81  * special virtualalloc, allocates lineary monoton growing memory.
82  * (the usual VirtualAlloc does not satisfy that restriction)
83  */
84 static LPVOID
85 DPMI_xalloc(int len) {
86         LPVOID  ret;
87         LPVOID  oldlastv = lastvalloced;
88
89         if (lastvalloced) {
90                 int     xflag = 0;
91                 ret = NULL;
92                 while (!ret) {
93                         ret=VirtualAlloc(lastvalloced,len,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
94                         if (!ret)
95                                 lastvalloced = (char *) lastvalloced + 0x10000;
96                         /* we failed to allocate one in the first round.
97                          * try non-linear
98                          */
99                         if (!xflag && (lastvalloced<oldlastv)) { /* wrapped */
100                                 FIXME("failed to allocate lineary growing memory (%d bytes), using non-linear growing...\n",len);
101                                 xflag++;
102                         }
103                         /* if we even fail to allocate something in the next
104                          * round, return NULL
105                          */
106                         if ((xflag==1) && (lastvalloced >= oldlastv))
107                                 xflag++;
108                         if ((xflag==2) && (lastvalloced < oldlastv)) {
109                                 FIXME("failed to allocate any memory of %d bytes!\n",len);
110                                 return NULL;
111                         }
112                 }
113         } else
114                  ret=VirtualAlloc(NULL,len,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
115         lastvalloced = (LPVOID)(((DWORD)ret+len+0xffff)&~0xffff);
116         return ret;
117 }
118
119 static void
120 DPMI_xfree(LPVOID ptr) {
121         VirtualFree(ptr,0,MEM_RELEASE);
122 }
123
124 /* FIXME: perhaps we could grow this mapped area... */
125 static LPVOID
126 DPMI_xrealloc(LPVOID ptr,DWORD newsize) {
127         MEMORY_BASIC_INFORMATION        mbi;
128         LPVOID                          newptr;
129
130         newptr = DPMI_xalloc(newsize);
131         if (ptr) {
132                 if (!VirtualQuery(ptr,&mbi,sizeof(mbi))) {
133                         FIXME("realloc of DPMI_xallocd region %p?\n",ptr);
134                         return NULL;
135                 }
136                 if (mbi.State == MEM_FREE) {
137                         FIXME("realloc of DPMI_xallocd region %p?\n",ptr);
138                         return NULL;
139                 }
140                 /* We do not shrink allocated memory. most reallocs
141                  * only do grows anyway
142                  */
143                 if (newsize<=mbi.RegionSize)
144                         return ptr;
145                 memcpy(newptr,ptr,mbi.RegionSize);
146                 DPMI_xfree(ptr);
147         }
148         return newptr;
149 }
150
151 /**********************************************************************
152  *          CallRMInt
153  */
154 static void CallRMInt( CONTEXT86 *context )
155 {
156     if (!Dosvm.CallRMInt && !DPMI_LoadDosSystem())
157     {
158         ERR("could not setup real-mode calls\n");
159         return;
160     }
161     Dosvm.CallRMInt( context );
162 }
163
164
165 static void CallRMProc( CONTEXT86 *context, int iret )
166 {
167     if (!Dosvm.CallRMProc && !DPMI_LoadDosSystem())
168     {
169         ERR("could not setup real-mode calls\n");
170         return;
171     }
172     Dosvm.CallRMProc( context, iret );
173 }
174
175 static void AllocRMCB( CONTEXT86 *context )
176 {
177     if (!Dosvm.AllocRMCB && !DPMI_LoadDosSystem())
178     {
179         ERR("could not setup real-mode calls\n");
180         return;
181     }
182     Dosvm.AllocRMCB( context );
183 }
184
185 static void FreeRMCB( CONTEXT86 *context )
186 {
187     if (!Dosvm.FreeRMCB)  /* cannot have allocated one if dosvm not loaded */
188     {
189         SET_LOWORD( context->Eax, 0x8024 ); /* invalid callback address */
190         SET_CFLAG( context );
191     }
192     else Dosvm.FreeRMCB( context );
193 }
194
195 static void RawModeSwitch( CONTEXT86 *context )
196 {
197     if (!Dosvm.RawModeSwitch)
198     {
199       ERR("could not setup real-mode calls\n");
200       return;
201     }
202     else
203     {
204       /*
205        * FIXME: This routine will not work if it is called
206        *        from 32 bit DPMI program and the program returns
207        *        to protected mode while ESP or EIP is over 0xffff.
208        * FIXME: This routine will not work if it is not called
209        *        using 16-bit-to-Wine callback glue function.
210        */
211       STACK16FRAME frame = *CURRENT_STACK16;
212
213       Dosvm.RawModeSwitch( context );
214
215       /*
216        * After this function returns to relay code, protected mode
217        * 16 bit stack will contain STACK16FRAME and single WORD
218        * (EFlags, see next comment).
219        */
220       NtCurrentTeb()->cur_stack =
221         MAKESEGPTR( context->SegSs,
222                     context->Esp - sizeof(STACK16FRAME) - sizeof(WORD) );
223
224       /*
225        * After relay code returns to glue function, protected
226        * mode 16 bit stack will contain interrupt return record:
227        * IP, CS and EFlags. Since EFlags is ignored, it won't
228        * need to be initialized.
229        */
230       context->Esp -= 3 * sizeof(WORD);
231
232       /*
233        * Restore stack frame so that relay code won't be confused.
234        * It should be noted that relay code overwrites IP and CS
235        * in STACK16FRAME with values taken from current CONTEXT86.
236        * These values are what is returned to glue function
237        * (see previous comment).
238        */
239       *CURRENT_STACK16 = frame;
240     }
241 }
242
243 /**********************************************************************
244  *          INT_Int31Handler (WPROCS.149)
245  *
246  * Handler for int 31h (DPMI).
247  */
248
249 void WINAPI INT_Int31Handler( CONTEXT86 *context )
250 {
251     /*
252      * Note: For Win32s processes, the whole linear address space is
253      *       shifted by 0x10000 relative to the OS linear address space.
254      *       See the comment in msdos/vxd.c.
255      */
256     DWORD dw;
257     BYTE *ptr;
258
259     if (context->SegCs == DOSMEM_dpmi_sel) {
260         RawModeSwitch( context );
261         return;
262     }
263
264     RESET_CFLAG(context);
265     switch(AX_reg(context))
266     {
267     case 0x0000:  /* Allocate LDT descriptors */
268         TRACE("allocate LDT descriptors (%d)\n",CX_reg(context));
269         if (!(context->Eax = AllocSelectorArray16( CX_reg(context) )))
270         {
271             TRACE("failed\n");
272             context->Eax = 0x8011;  /* descriptor unavailable */
273             SET_CFLAG(context);
274         }
275         TRACE("success, array starts at 0x%04x\n",AX_reg(context));
276         break;
277
278     case 0x0001:  /* Free LDT descriptor */
279         TRACE("free LDT descriptor (0x%04x)\n",BX_reg(context));
280         if (FreeSelector16( BX_reg(context) ))
281         {
282             context->Eax = 0x8022;  /* invalid selector */
283             SET_CFLAG(context);
284         }
285         else
286         {
287             /* If a segment register contains the selector being freed, */
288             /* set it to zero. */
289             if (!((context->SegDs^BX_reg(context)) & ~3)) context->SegDs = 0;
290             if (!((context->SegEs^BX_reg(context)) & ~3)) context->SegEs = 0;
291             if (!((context->SegFs^BX_reg(context)) & ~3)) context->SegFs = 0;
292             if (!((context->SegGs^BX_reg(context)) & ~3)) context->SegGs = 0;
293         }
294         break;
295
296     case 0x0002:  /* Real mode segment to descriptor */
297         TRACE("real mode segment to descriptor (0x%04x)\n",BX_reg(context));
298         {
299             WORD entryPoint = 0;  /* KERNEL entry point for descriptor */
300             switch(BX_reg(context))
301             {
302             case 0x0000: entryPoint = 183; break;  /* __0000H */
303             case 0x0040: entryPoint = 193; break;  /* __0040H */
304             case 0xa000: entryPoint = 174; break;  /* __A000H */
305             case 0xb000: entryPoint = 181; break;  /* __B000H */
306             case 0xb800: entryPoint = 182; break;  /* __B800H */
307             case 0xc000: entryPoint = 195; break;  /* __C000H */
308             case 0xd000: entryPoint = 179; break;  /* __D000H */
309             case 0xe000: entryPoint = 190; break;  /* __E000H */
310             case 0xf000: entryPoint = 194; break;  /* __F000H */
311             default:
312                 context->Eax = DOSMEM_AllocSelector(BX_reg(context));
313                 break;
314             }
315             if (entryPoint)
316                 context->Eax = LOWORD(GetProcAddress16( GetModuleHandle16( "KERNEL" ),
317                                                         (LPCSTR)(ULONG_PTR)entryPoint ));
318         }
319         break;
320
321     case 0x0003:  /* Get next selector increment */
322         TRACE("get selector increment (__AHINCR)\n");
323         context->Eax = __AHINCR;
324         break;
325
326     case 0x0004:  /* Lock selector (not supported) */
327         FIXME("lock selector not supported\n");
328         context->Eax = 0;  /* FIXME: is this a correct return value? */
329         break;
330
331     case 0x0005:  /* Unlock selector (not supported) */
332         FIXME("unlock selector not supported\n");
333         context->Eax = 0;  /* FIXME: is this a correct return value? */
334         break;
335
336     case 0x0006:  /* Get selector base address */
337         TRACE("get selector base address (0x%04x)\n",BX_reg(context));
338         if (!(dw = GetSelectorBase( BX_reg(context) )))
339         {
340             context->Eax = 0x8022;  /* invalid selector */
341             SET_CFLAG(context);
342         }
343         else
344         {
345             CX_reg(context) = HIWORD(W32S_WINE2APP(dw));
346             DX_reg(context) = LOWORD(W32S_WINE2APP(dw));
347         }
348         break;
349
350     case 0x0007:  /* Set selector base address */
351         TRACE("set selector base address (0x%04x,0x%08lx)\n",
352                      BX_reg(context),
353                      W32S_APP2WINE(MAKELONG(DX_reg(context),CX_reg(context))));
354         dw = W32S_APP2WINE(MAKELONG(DX_reg(context), CX_reg(context)));
355         if (dw < 0x10000)
356             /* app wants to access lower 64K of DOS memory, load DOS subsystem */
357             DPMI_LoadDosSystem();
358         SetSelectorBase(BX_reg(context), dw);
359         break;
360
361     case 0x0008:  /* Set selector limit */
362         TRACE("set selector limit (0x%04x,0x%08lx)\n",BX_reg(context),MAKELONG(DX_reg(context),CX_reg(context)));
363         dw = MAKELONG( DX_reg(context), CX_reg(context) );
364         SetSelectorLimit16( BX_reg(context), dw );
365         break;
366
367     case 0x0009:  /* Set selector access rights */
368         TRACE("set selector access rights(0x%04x,0x%04x)\n",BX_reg(context),CX_reg(context));
369         SelectorAccessRights16( BX_reg(context), 1, CX_reg(context) );
370         break;
371
372     case 0x000a:  /* Allocate selector alias */
373         TRACE("allocate selector alias (0x%04x)\n",BX_reg(context));
374         if (!(AX_reg(context) = AllocCStoDSAlias16( BX_reg(context) )))
375         {
376             AX_reg(context) = 0x8011;  /* descriptor unavailable */
377             SET_CFLAG(context);
378         }
379         break;
380
381     case 0x000b:  /* Get descriptor */
382         TRACE("get descriptor (0x%04x)\n",BX_reg(context));
383         {
384             LDT_ENTRY entry;
385             wine_ldt_set_base( &entry, (void*)W32S_WINE2APP(wine_ldt_get_base(&entry)) );
386             /* FIXME: should use ES:EDI for 32-bit clients */
387             *(LDT_ENTRY *)MapSL( MAKESEGPTR( context->SegEs, LOWORD(context->Edi) )) = entry;
388         }
389         break;
390
391     case 0x000c:  /* Set descriptor */
392         TRACE("set descriptor (0x%04x)\n",BX_reg(context));
393         {
394             LDT_ENTRY entry = *(LDT_ENTRY *)MapSL(MAKESEGPTR( context->SegEs, LOWORD(context->Edi)));
395             wine_ldt_set_base( &entry, (void*)W32S_APP2WINE(wine_ldt_get_base(&entry)) );
396             wine_ldt_set_entry( LOWORD(context->Ebx), &entry );
397         }
398         break;
399
400     case 0x000d:  /* Allocate specific LDT descriptor */
401         FIXME("allocate descriptor (0x%04x), stub!\n",BX_reg(context));
402         AX_reg(context) = 0x8011; /* descriptor unavailable */
403         SET_CFLAG(context);
404         break;
405     case 0x0100:  /* Allocate DOS memory block */
406         TRACE("allocate DOS memory block (0x%x paragraphs)\n",BX_reg(context));
407         dw = GlobalDOSAlloc16((DWORD)BX_reg(context)<<4);
408         if (dw) {
409             AX_reg(context) = HIWORD(dw);
410             DX_reg(context) = LOWORD(dw);
411         } else {
412             AX_reg(context) = 0x0008; /* insufficient memory */
413             BX_reg(context) = DOSMEM_Available()>>4;
414             SET_CFLAG(context);
415         }
416         break;
417     case 0x0101:  /* Free DOS memory block */
418         TRACE("free DOS memory block (0x%04x)\n",DX_reg(context));
419         dw = GlobalDOSFree16(DX_reg(context));
420         if (!dw) {
421             AX_reg(context) = 0x0009; /* memory block address invalid */
422             SET_CFLAG(context);
423         }
424         break;
425     case 0x0200: /* get real mode interrupt vector */
426         FIXME("get realmode interupt vector(0x%02x) unimplemented.\n",
427               BL_reg(context));
428         SET_CFLAG(context);
429         break;
430     case 0x0201: /* set real mode interrupt vector */
431         FIXME("set realmode interrupt vector(0x%02x,0x%04x:0x%04x) unimplemented\n", BL_reg(context),CX_reg(context),DX_reg(context));
432         SET_CFLAG(context);
433         break;
434     case 0x0204:  /* Get protected mode interrupt vector */
435         TRACE("get protected mode interrupt handler (0x%02x), stub!\n",BL_reg(context));
436         dw = (DWORD)INT_GetPMHandler( BL_reg(context) );
437         CX_reg(context) = HIWORD(dw);
438         DX_reg(context) = LOWORD(dw);
439         break;
440
441     case 0x0205:  /* Set protected mode interrupt vector */
442         TRACE("set protected mode interrupt handler (0x%02x,%p), stub!\n",
443             BL_reg(context),MapSL(MAKESEGPTR(CX_reg(context),DX_reg(context))));
444         INT_SetPMHandler( BL_reg(context),
445                           (FARPROC16)MAKESEGPTR( CX_reg(context), DX_reg(context) ));
446         break;
447
448     case 0x0300:  /* Simulate real mode interrupt */
449         CallRMInt( context );
450         break;
451
452     case 0x0301:  /* Call real mode procedure with far return */
453         CallRMProc( context, FALSE );
454         break;
455
456     case 0x0302:  /* Call real mode procedure with interrupt return */
457         CallRMProc( context, TRUE );
458         break;
459
460     case 0x0303:  /* Allocate Real Mode Callback Address */
461         AllocRMCB( context );
462         break;
463
464     case 0x0304:  /* Free Real Mode Callback Address */
465         FreeRMCB( context );
466         break;
467
468     case 0x0305:  /* Get State Save/Restore Addresses */
469         TRACE("get state save/restore addresses\n");
470         /* we probably won't need this kind of state saving */
471         AX_reg(context) = 0;
472         /* real mode: just point to the lret */
473         BX_reg(context) = DOSMEM_wrap_seg;
474         context->Ecx = 2;
475         /* protected mode: don't have any handler yet... */
476         FIXME("no protected-mode dummy state save/restore handler yet\n");
477         SI_reg(context) = 0;
478         context->Edi = 0;
479         break;
480
481     case 0x0306:  /* Get Raw Mode Switch Addresses */
482         TRACE("get raw mode switch addresses\n");
483         /* real mode, point to standard DPMI return wrapper */
484         BX_reg(context) = DOSMEM_wrap_seg;
485         context->Ecx = 0;
486         /* protected mode, point to DPMI call wrapper */
487         SI_reg(context) = DOSMEM_dpmi_sel;
488         context->Edi = 8; /* offset of the INT 0x31 call */
489         break;
490     case 0x0400:  /* Get DPMI version */
491         TRACE("get DPMI version\n");
492         {
493             SYSTEM_INFO si;
494
495             GetSystemInfo(&si);
496             AX_reg(context) = 0x005a;  /* DPMI version 0.90 */
497             BX_reg(context) = 0x0005;  /* Flags: 32-bit, virtual memory */
498             CL_reg(context) = si.wProcessorLevel;
499             DX_reg(context) = 0x0102;  /* Master/slave interrupt controller base*/
500             break;
501         }
502     case 0x0500:  /* Get free memory information */
503         TRACE("get free memory information\n");
504         {
505             MEMMANINFO mmi;
506
507             mmi.dwSize = sizeof(mmi);
508             MemManInfo16(&mmi);
509             ptr = MapSL(MAKESEGPTR(context->SegEs,DI_reg(context)));
510             /* the layout is just the same as MEMMANINFO, but without
511              * the dwSize entry.
512              */
513             memcpy(ptr,((char*)&mmi)+4,sizeof(mmi)-4);
514             break;
515         }
516     case 0x0501:  /* Allocate memory block */
517         TRACE("allocate memory block (%ld)\n",MAKELONG(CX_reg(context),BX_reg(context)));
518         if (!(ptr = (BYTE *)DPMI_xalloc(MAKELONG(CX_reg(context), BX_reg(context)))))
519         {
520             AX_reg(context) = 0x8012;  /* linear memory not available */
521             SET_CFLAG(context);
522         } else {
523             BX_reg(context) = SI_reg(context) = HIWORD(W32S_WINE2APP(ptr));
524             CX_reg(context) = DI_reg(context) = LOWORD(W32S_WINE2APP(ptr));
525         }
526         break;
527
528     case 0x0502:  /* Free memory block */
529         TRACE("free memory block (0x%08lx)\n",
530                      W32S_APP2WINE(MAKELONG(DI_reg(context),SI_reg(context))));
531         DPMI_xfree( (void *)W32S_APP2WINE(MAKELONG(DI_reg(context), SI_reg(context))) );
532         break;
533
534     case 0x0503:  /* Resize memory block */
535         TRACE("resize memory block (0x%08lx,%ld)\n",
536                      W32S_APP2WINE(MAKELONG(DI_reg(context),SI_reg(context))),
537                      MAKELONG(CX_reg(context),BX_reg(context)));
538         if (!(ptr = (BYTE *)DPMI_xrealloc(
539                 (void *)W32S_APP2WINE(MAKELONG(DI_reg(context),SI_reg(context))),
540                 MAKELONG(CX_reg(context),BX_reg(context)))))
541         {
542             AX_reg(context) = 0x8012;  /* linear memory not available */
543             SET_CFLAG(context);
544         } else {
545             BX_reg(context) = SI_reg(context) = HIWORD(W32S_WINE2APP(ptr));
546             CX_reg(context) = DI_reg(context) = LOWORD(W32S_WINE2APP(ptr));
547         }
548         break;
549
550     case 0x0507:  /* Modify page attributes */
551         FIXME("modify page attributes unimplemented\n");
552         break;  /* Just ignore it */
553
554     case 0x0600:  /* Lock linear region */
555         FIXME("lock linear region unimplemented\n");
556         break;  /* Just ignore it */
557
558     case 0x0601:  /* Unlock linear region */
559         FIXME("unlock linear region unimplemented\n");
560         break;  /* Just ignore it */
561
562     case 0x0602:  /* Unlock real-mode region */
563         FIXME("unlock realmode region unimplemented\n");
564         break;  /* Just ignore it */
565
566     case 0x0603:  /* Lock real-mode region */
567         FIXME("lock realmode region unimplemented\n");
568         break;  /* Just ignore it */
569
570     case 0x0604:  /* Get page size */
571         TRACE("get pagesize\n");
572         BX_reg(context) = 0;
573         CX_reg(context) = getpagesize();
574         break;
575
576     case 0x0702:  /* Mark page as demand-paging candidate */
577         FIXME("mark page as demand-paging candidate\n");
578         break;  /* Just ignore it */
579
580     case 0x0703:  /* Discard page contents */
581         FIXME("discard page contents\n");
582         break;  /* Just ignore it */
583
584      case 0x0800:  /* Physical address mapping */
585         FIXME("map real to linear (0x%08lx)\n",MAKELONG(CX_reg(context),BX_reg(context)));
586          if(!(ptr=DOSMEM_MapRealToLinear(MAKELONG(CX_reg(context),BX_reg(context)))))
587          {
588              AX_reg(context) = 0x8021;
589              SET_CFLAG(context);
590          }
591          else
592          {
593              BX_reg(context) = HIWORD(W32S_WINE2APP(ptr));
594              CX_reg(context) = LOWORD(W32S_WINE2APP(ptr));
595              RESET_CFLAG(context);
596          }
597          break;
598
599     default:
600         INT_BARF( context, 0x31 );
601         AX_reg(context) = 0x8001;  /* unsupported function */
602         SET_CFLAG(context);
603         break;
604     }
605
606 }