Release 971116
[wine] / msdos / dpmi.c
1 /*
2  * DPMI 0.9 emulation
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <string.h>
10 #include "windows.h"
11 #include "heap.h"
12 #include "ldt.h"
13 #include "module.h"
14 #include "miscemu.h"
15 #include "drive.h"
16 #include "msdos.h"
17 #include "toolhelp.h"
18 #include "stddebug.h"
19 #include "debug.h"
20
21 #define DOS_GET_DRIVE(reg) ((reg) ? (reg) - 1 : DRIVE_GetCurrentDrive())
22
23 void CreateBPB(int drive, BYTE *data);  /* defined in int21.c */
24
25
26 /* Structure for real-mode callbacks */
27 typedef struct
28 {
29     DWORD edi;
30     DWORD esi;
31     DWORD ebp;
32     DWORD reserved;
33     DWORD ebx;
34     DWORD edx;
35     DWORD ecx;
36     DWORD eax;
37     WORD  fl;
38     WORD  es;
39     WORD  ds;
40     WORD  fs;
41     WORD  gs;
42     WORD  ip;
43     WORD  cs;
44     WORD  sp;
45     WORD  ss;
46 } REALMODECALL;
47
48
49
50 /**********************************************************************
51  *          INT_GetRealModeContext
52  */
53 static void INT_GetRealModeContext( REALMODECALL *call, CONTEXT *context )
54 {
55     EAX_reg(context) = call->eax;
56     EBX_reg(context) = call->ebx;
57     ECX_reg(context) = call->ecx;
58     EDX_reg(context) = call->edx;
59     ESI_reg(context) = call->esi;
60     EDI_reg(context) = call->edi;
61     EBP_reg(context) = call->ebp;
62     EFL_reg(context) = call->fl;
63     EIP_reg(context) = call->ip;
64     ESP_reg(context) = call->sp;
65     CS_reg(context)  = call->cs;
66     DS_reg(context)  = call->ds;
67     ES_reg(context)  = call->es;
68     FS_reg(context)  = call->fs;
69     GS_reg(context)  = call->gs;
70 }
71
72
73 /**********************************************************************
74  *          INT_SetRealModeContext
75  */
76 static void INT_SetRealModeContext( REALMODECALL *call, CONTEXT *context )
77 {
78     call->eax = EAX_reg(context);
79     call->ebx = EBX_reg(context);
80     call->ecx = ECX_reg(context);
81     call->edx = EDX_reg(context);
82     call->esi = ESI_reg(context);
83     call->edi = EDI_reg(context);
84     call->ebp = EBP_reg(context);
85     call->fl  = FL_reg(context);
86     call->ip  = IP_reg(context);
87     call->sp  = SP_reg(context);
88     call->cs  = CS_reg(context);
89     call->ds  = DS_reg(context);
90     call->es  = ES_reg(context);
91     call->fs  = FS_reg(context);
92     call->gs  = GS_reg(context);
93 }
94
95
96 /**********************************************************************
97  *          INT_DoRealModeInt
98  */
99 static void INT_DoRealModeInt( CONTEXT *context )
100 {
101     CONTEXT realmode_ctx;
102     REALMODECALL *call = (REALMODECALL *)PTR_SEG_OFF_TO_LIN( ES_reg(context),
103                                                           DI_reg(context) );
104     INT_GetRealModeContext( call, &realmode_ctx );
105
106     RESET_CFLAG(context);
107     switch (BL_reg(context))
108     {
109     case 0x2f:  /* int2f */
110         switch (AH_reg(&realmode_ctx))
111         {
112         case 0x15:
113             /* MSCDEX hook */
114             do_mscdex( &realmode_ctx );
115             break;
116         default:
117             SET_CFLAG(context);
118             break;
119         }
120         break;
121     case 0x21:  /* int21 */
122         switch (AH_reg(&realmode_ctx))
123         {
124         case 0x52:
125             ES_reg(&realmode_ctx) = 0;
126             EBX_reg(&realmode_ctx) = 0;
127             break;
128         case 0x65:
129             switch (AL_reg(&realmode_ctx))
130             {
131             case 0x06:
132                 {/* get collate table */
133                     /* ES:DI is a REALMODE pointer to 5 byte dosmem 
134                      * we fill that with 0x6, realmode pointer to collateTB
135                      */
136                     char *table = DOSMEM_MapRealToLinear(
137                        MAKELONG(EDI_reg(&realmode_ctx),ES_reg(&realmode_ctx)));
138                     *(BYTE*)table      = 0x06;
139                     *(DWORD*)(table+1) = DOSMEM_CollateTable;
140                     CX_reg(&realmode_ctx) = 258;/*FIXME: size of table?*/
141                     break;
142                 }
143             default:
144                 SET_CFLAG(context);
145                 break;
146             }
147             break;
148         case 0x44:
149             switch (AL_reg(&realmode_ctx))
150             {
151             case 0x0D:
152                 {/* generic block device request */
153                     BYTE *dataptr = DOSMEM_MapRealToLinear(
154                        MAKELONG(EDX_reg(&realmode_ctx),DS_reg(&realmode_ctx)));
155                     int drive = DOS_GET_DRIVE(BL_reg(&realmode_ctx));
156                     if (CH_reg(&realmode_ctx) != 0x08)
157                     {
158                         SET_CFLAG(context);
159                         break;
160                     }
161                     switch (CL_reg(&realmode_ctx))
162                     {
163                     case 0x66:
164                         {
165                             char    label[12],fsname[9],path[4];
166                             DWORD   serial;
167
168                             strcpy(path,"x:\\");path[0]=drive+'A';
169                             GetVolumeInformation32A(path,label,12,&serial,NULL,NULL,fsname,9);
170                             *(WORD*)dataptr         = 0;
171                             memcpy(dataptr+2,&serial,4);
172                             memcpy(dataptr+6,label  ,11);
173                             memcpy(dataptr+17,fsname,8);
174                             break;
175                         }
176                     case 0x60:  /* get device parameters */
177                                 /* used by defrag.exe of win95 */
178                         memset(dataptr, 0, 0x26);
179                         dataptr[0] = 0x04;
180                         dataptr[6] = 0; /* media type */
181                         if (drive > 1)
182                         {
183                             dataptr[1] = 0x05; /* fixed disk */
184                             setword(&dataptr[2], 0x01); /* non removable */
185                             setword(&dataptr[4], 0x300); /* # of cylinders */
186                         }
187                         else
188                         {
189                             dataptr[1] = 0x07; /* block dev, floppy */
190                             setword(&dataptr[2], 0x02); /* removable */
191                             setword(&dataptr[4], 80); /* # of cylinders */
192                         }
193                         CreateBPB(drive, &dataptr[7]);
194                         break;
195                     default:
196                         SET_CFLAG(context);
197                         break;
198                     }
199                 }
200             break;
201             default:
202                 SET_CFLAG(context);
203                 break;
204             }
205             break;
206         default:
207             SET_CFLAG(context);
208             break;
209         }
210         break;
211     default:
212         SET_CFLAG(context);
213         break;
214     }
215
216     if (EFL_reg(context)&1)
217         fprintf(stdnimp,
218                 "RealModeInt %02x: EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
219                 "                ESI=%08lx EDI=%08lx DS=%04lx ES=%04lx\n",
220                 BL_reg(context), EAX_reg(&realmode_ctx),
221                 EBX_reg(&realmode_ctx), ECX_reg(&realmode_ctx),
222                 EDX_reg(&realmode_ctx), ESI_reg(&realmode_ctx),
223                 EDI_reg(&realmode_ctx), DS_reg(&realmode_ctx),
224                 ES_reg(&realmode_ctx) );
225     INT_SetRealModeContext( call, &realmode_ctx );
226 }
227
228
229 /**********************************************************************
230  *          INT_Int31Handler
231  *
232  * Handler for int 31h (DPMI).
233  */
234 void WINAPI INT_Int31Handler( CONTEXT *context )
235 {
236     DWORD dw;
237     BYTE *ptr;
238
239     RESET_CFLAG(context);
240     switch(AX_reg(context))
241     {
242     case 0x0000:  /* Allocate LDT descriptors */
243         if (!(AX_reg(context) = AllocSelectorArray( CX_reg(context) )))
244         {
245             AX_reg(context) = 0x8011;  /* descriptor unavailable */
246             SET_CFLAG(context);
247         }
248         break;
249
250     case 0x0001:  /* Free LDT descriptor */
251         if (FreeSelector( BX_reg(context) ))
252         {
253             AX_reg(context) = 0x8022;  /* invalid selector */
254             SET_CFLAG(context);
255         }
256         else
257         {
258             /* If a segment register contains the selector being freed, */
259             /* set it to zero. */
260             if (!((DS_reg(context)^BX_reg(context)) & ~3)) DS_reg(context) = 0;
261             if (!((ES_reg(context)^BX_reg(context)) & ~3)) ES_reg(context) = 0;
262             if (!((FS_reg(context)^BX_reg(context)) & ~3)) FS_reg(context) = 0;
263             if (!((GS_reg(context)^BX_reg(context)) & ~3)) GS_reg(context) = 0;
264         }
265         break;
266
267     case 0x0002:  /* Real mode segment to descriptor */
268         {
269             WORD entryPoint = 0;  /* KERNEL entry point for descriptor */
270             switch(BX_reg(context))
271             {
272             case 0x0000: entryPoint = 183; break;  /* __0000H */
273             case 0x0040: entryPoint = 193; break;  /* __0040H */
274             case 0xa000: entryPoint = 174; break;  /* __A000H */
275             case 0xb000: entryPoint = 181; break;  /* __B000H */
276             case 0xb800: entryPoint = 182; break;  /* __B800H */
277             case 0xc000: entryPoint = 195; break;  /* __C000H */
278             case 0xd000: entryPoint = 179; break;  /* __D000H */
279             case 0xe000: entryPoint = 190; break;  /* __E000H */
280             case 0xf000: entryPoint = 194; break;  /* __F000H */
281             default:
282                 AX_reg(context) = DOSMEM_AllocSelector(BX_reg(context));
283                 break;
284             }
285             if (entryPoint) 
286                 AX_reg(context) = LOWORD(MODULE_GetEntryPoint( 
287                                                  GetModuleHandle16( "KERNEL" ),
288                                                  entryPoint ));
289         }
290         break;
291
292     case 0x0003:  /* Get next selector increment */
293         AX_reg(context) = __AHINCR;
294         break;
295
296     case 0x0004:  /* Lock selector (not supported) */
297         AX_reg(context) = 0;  /* FIXME: is this a correct return value? */
298         break;
299
300     case 0x0005:  /* Unlock selector (not supported) */
301         AX_reg(context) = 0;  /* FIXME: is this a correct return value? */
302         break;
303
304     case 0x0006:  /* Get selector base address */
305         if (!(dw = GetSelectorBase( BX_reg(context) )))
306         {
307             AX_reg(context) = 0x8022;  /* invalid selector */
308             SET_CFLAG(context);
309         }
310         else
311         {
312             CX_reg(context) = HIWORD(dw);
313             DX_reg(context) = LOWORD(dw);
314         }
315         break;
316
317     case 0x0007:  /* Set selector base address */
318         SetSelectorBase( BX_reg(context),
319                          MAKELONG( DX_reg(context), CX_reg(context) ) );
320         break;
321
322     case 0x0008:  /* Set selector limit */
323         SetSelectorLimit( BX_reg(context),
324                           MAKELONG( DX_reg(context), CX_reg(context) ) );
325         break;
326
327     case 0x0009:  /* Set selector access rights */
328         SelectorAccessRights( BX_reg(context), 1, CX_reg(context) );
329         break;
330
331     case 0x000a:  /* Allocate selector alias */
332         if (!(AX_reg(context) = AllocCStoDSAlias( BX_reg(context) )))
333         {
334             AX_reg(context) = 0x8011;  /* descriptor unavailable */
335             SET_CFLAG(context);
336         }
337         break;
338
339     case 0x000b:  /* Get descriptor */
340         {
341             ldt_entry entry;
342             LDT_GetEntry( SELECTOR_TO_ENTRY( BX_reg(context) ), &entry );
343             /* FIXME: should use ES:EDI for 32-bit clients */
344             LDT_EntryToBytes( PTR_SEG_OFF_TO_LIN( ES_reg(context),
345                                                   DI_reg(context) ), &entry );
346         }
347         break;
348
349     case 0x000c:  /* Set descriptor */
350         {
351             ldt_entry entry;
352             LDT_BytesToEntry( PTR_SEG_OFF_TO_LIN( ES_reg(context),
353                                                   DI_reg(context) ), &entry );
354             LDT_GetEntry( SELECTOR_TO_ENTRY( BX_reg(context) ), &entry );
355         }
356         break;
357
358     case 0x000d:  /* Allocate specific LDT descriptor */
359         AX_reg(context) = 0x8011; /* descriptor unavailable */
360         SET_CFLAG(context);
361         break;
362     case 0x0200: /* get real mode interrupt vector */
363         fprintf(stdnimp,
364                 "int31: get realmode interupt vector(0x%02x) unimplemented.\n",
365                 BL_reg(context)
366         );
367         SET_CFLAG(context);
368         break;
369     case 0x0201: /* set real mode interrupt vector */
370         fprintf(stdnimp,
371                 "int31: set realmode interupt vector(0x%02x,0x%04x:0x%04x) unimplemented\n",
372                 BL_reg(context),CX_reg(context),DX_reg(context)
373         );
374         SET_CFLAG(context);
375         break;
376     case 0x0204:  /* Get protected mode interrupt vector */
377         dw = (DWORD)INT_GetHandler( BL_reg(context) );
378         CX_reg(context) = HIWORD(dw);
379         DX_reg(context) = LOWORD(dw);
380         break;
381
382     case 0x0205:  /* Set protected mode interrupt vector */
383         INT_SetHandler( BL_reg(context),
384                         (FARPROC16)PTR_SEG_OFF_TO_SEGPTR( CX_reg(context),
385                                                           DX_reg(context) ));
386         break;
387
388     case 0x0300:  /* Simulate real mode interrupt */
389         INT_DoRealModeInt( context );
390         break;
391
392     case 0x0301:  /* Call real mode procedure with far return */
393         {
394             REALMODECALL *p = (REALMODECALL *)PTR_SEG_OFF_TO_LIN( ES_reg(context), DI_reg(context) );
395             fprintf(stdnimp,
396                     "RealModeCall: EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
397                     "              ESI=%08lx EDI=%08lx ES=%04x DS=%04x CS:IP=%04x:%04x\n",
398                     p->eax, p->ebx, p->ecx, p->edx,
399                     p->esi, p->edi, p->es, p->ds, p->cs, p->ip );
400             SET_CFLAG(context);
401         }
402         break;
403
404     case 0x0302:  /* Call real mode procedure with interrupt return */
405         {
406             REALMODECALL *p = (REALMODECALL *)PTR_SEG_OFF_TO_LIN( ES_reg(context), DI_reg(context) );
407             fprintf(stdnimp,
408                     "RealModeCallIret: EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
409                     "                  ESI=%08lx EDI=%08lx ES=%04x DS=%04x CS:IP=%04x:%04x\n",
410                     p->eax, p->ebx, p->ecx, p->edx,
411                     p->esi, p->edi, p->es, p->ds, p->cs, p->ip );
412             SET_CFLAG(context);
413         }
414         break;
415
416     case 0x0303:  /* Allocate Real Mode Callback Address */
417         {
418             REALMODECALL *p = (REALMODECALL *)PTR_SEG_OFF_TO_LIN( ES_reg(context), DI_reg(context) );
419             fprintf(stdnimp,
420                     "AllocRMCB: EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
421                     "           ESI=%08lx EDI=%08lx ES=%04x DS=%04x CS:IP=%04x:%04x\n"
422                     "   Function to call: %04x:%04x\n",
423                     p->eax, p->ebx, p->ecx, p->edx,
424                     p->esi, p->edi, p->es, p->ds, p->cs, p->ip,
425                     (WORD)DS_reg(context), SI_reg(context) );
426             SET_CFLAG(context);
427         }
428         break;
429
430     case 0x0400:  /* Get DPMI version */
431         {
432             SYSTEM_INFO si;
433
434             GetSystemInfo(&si);
435             AX_reg(context) = 0x005a;  /* DPMI version 0.90 */
436             BX_reg(context) = 0x0005;  /* Flags: 32-bit, virtual memory */
437             CL_reg(context) = si.wProcessorLevel;
438             DX_reg(context) = 0x0102;  /* Master/slave interrupt controller base*/
439             break;
440         }
441     case 0x0500:  /* Get free memory information */
442         {
443             MEMMANINFO mmi;
444
445             mmi.dwSize = sizeof(mmi);
446             MemManInfo(&mmi);
447             ptr = (BYTE *)PTR_SEG_OFF_TO_LIN(ES_reg(context),DI_reg(context));
448             /* the layout is just the same as MEMMANINFO, but without
449              * the dwSize entry.
450              */
451             memcpy(ptr,((char*)&mmi)+4,sizeof(mmi)-4);
452             break;
453         }
454     case 0x0501:  /* Allocate memory block */
455         if (!(ptr = (BYTE *)HeapAlloc( GetProcessHeap(), 0,
456                                        MAKELONG( CX_reg(context),
457                                                  BX_reg(context) ))))
458         {
459             AX_reg(context) = 0x8012;  /* linear memory not available */
460             SET_CFLAG(context);
461         }
462         else
463         {
464             BX_reg(context) = SI_reg(context) = HIWORD(ptr);
465             CX_reg(context) = DI_reg(context) = LOWORD(ptr);
466         }
467         break;
468
469     case 0x0502:  /* Free memory block */
470         HeapFree( GetProcessHeap(), 0,
471                   (void *)MAKELONG( DI_reg(context), SI_reg(context) ) );
472         break;
473
474     case 0x0503:  /* Resize memory block */
475         if (!(ptr = (BYTE *)HeapReAlloc( GetProcessHeap(), 0,
476                            (void *)MAKELONG(DI_reg(context),SI_reg(context)),
477                                    MAKELONG(CX_reg(context),BX_reg(context)))))
478         {
479             AX_reg(context) = 0x8012;  /* linear memory not available */
480             SET_CFLAG(context);
481         }
482         else
483         {
484             BX_reg(context) = SI_reg(context) = HIWORD(ptr);
485             CX_reg(context) = DI_reg(context) = LOWORD(ptr);
486         }
487         break;
488
489     case 0x0600:  /* Lock linear region */
490         break;  /* Just ignore it */
491
492     case 0x0601:  /* Unlock linear region */
493         break;  /* Just ignore it */
494
495     case 0x0602:  /* Unlock real-mode region */
496         break;  /* Just ignore it */
497
498     case 0x0603:  /* Lock real-mode region */
499         break;  /* Just ignore it */
500
501     case 0x0604:  /* Get page size */
502         BX_reg(context) = 0;
503         CX_reg(context) = 4096;
504         break;
505
506     case 0x0702:  /* Mark page as demand-paging candidate */
507         break;  /* Just ignore it */
508
509     case 0x0703:  /* Discard page contents */
510         break;  /* Just ignore it */
511
512     default:
513         INT_BARF( context, 0x31 );
514         AX_reg(context) = 0x8001;  /* unsupported function */
515         SET_CFLAG(context);
516         break;
517     }
518 }