Framework for the doppler effect.
[wine] / dlls / winedos / relay.c
1 /*
2  * Routines for dynamically building calls to Wine from 
3  * protected mode applications.
4  *
5  * Copyright 2002 Jukka Heinonen
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 #include "dosexe.h"
22 #include "stackframe.h"
23 #include "wine/debug.h"
24 #include "builtin16.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(int);
27
28 /*
29  * Magic DWORD used to check stack integrity.
30  */
31 #define RELAY_MAGIC 0xabcdef00
32
33 /*
34  * Memory block for temporary 16-bit stacks used when
35  * 32-bit code calls relay.
36  */
37 typedef struct {
38     DWORD inuse;          /* non-zero if stack block is in use */
39     DWORD eip;            /* saved ip */
40     DWORD seg_cs;         /* saved cs */
41     DWORD esp;            /* saved sp */
42     DWORD seg_ss;         /* saved ss */
43     DWORD stack_bottom;   /* guard dword */
44     BYTE  stack[256-7*4]; /* 16-bit stack */
45     DWORD stack_top;      /* guard dword */
46 } RELAY_Stack16;
47
48
49 /**********************************************************************
50  *          RELAY_GetPointer
51  *
52  * Get pointer to stack block when given esp pointing to 16-bit stack
53  * inside relay data segment.
54  */
55 static RELAY_Stack16 *RELAY_GetPointer( DWORD offset )
56 {
57     offset = offset / sizeof(RELAY_Stack16) * sizeof(RELAY_Stack16);
58     return MapSL(MAKESEGPTR(DOSVM_dpmi_segments->relay_data_sel, offset));
59 }
60
61
62 /**********************************************************************
63  *          RELAY_MakeShortContext
64  *
65  * If context is using 32-bit stack or code segment, allocate
66  * 16-bit stack, make stack pointer point to this stack and
67  * make code pointer point to stub that restores everything.
68  * So, after this routine, SS and CS are guaranteed to be 16-bit.
69  *
70  * Note: This might be called from signal handler, so the stack
71  *       allocation algorithm must be signal safe.
72  */
73 static void RELAY_MakeShortContext( CONTEXT86 *context )
74 {
75     if (IS_SELECTOR_32BIT(context->SegCs) || IS_SELECTOR_32BIT(context->SegSs))
76     {
77         DWORD offset = offsetof(RELAY_Stack16, stack_top);
78         RELAY_Stack16 *stack = RELAY_GetPointer( 0 );
79
80         while (stack->inuse && offset < DOSVM_RELAY_DATA_SIZE) {
81             stack++;
82             offset += sizeof(RELAY_Stack16);
83         }
84
85         if (offset >= DOSVM_RELAY_DATA_SIZE)
86             ERR( "Too many nested interrupts!\n" );
87         
88         stack->inuse = 1;
89         stack->eip = context->Eip;
90         stack->seg_cs = context->SegCs;
91         stack->esp = context->Esp;
92         stack->seg_ss = context->SegSs;
93
94         stack->stack_bottom = RELAY_MAGIC;
95         stack->stack_top = RELAY_MAGIC;
96
97         context->SegSs = DOSVM_dpmi_segments->relay_data_sel;
98         context->Esp = offset;
99         context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
100         context->Eip = 3;
101     }
102 }
103
104
105 /**********************************************************************
106  *          RELAY_RelayStub
107  *
108  * This stub is called by __wine_call_from_16_regs in order to marshall
109  * relay parameters.
110  */
111 static void __stdcall RELAY_RelayStub( DOSRELAY proc, 
112                                        unsigned char *args, 
113                                        void *context )
114 {
115     proc( (CONTEXT86*)context, *(LPVOID *)args );
116 }
117
118
119 /**********************************************************************
120  *          DOSVM_RelayHandler
121  *
122  * Restore saved code and stack pointers and release stack block.
123  */
124 void DOSVM_RelayHandler( CONTEXT86 *context )
125 {
126     RELAY_Stack16 *stack = RELAY_GetPointer( context->Esp );
127
128     context->SegSs = stack->seg_ss;
129     context->Esp = stack->esp;
130     context->SegCs = stack->seg_cs;
131     context->Eip = stack->eip;
132
133     if (!stack->inuse || 
134         stack->stack_bottom != RELAY_MAGIC ||
135         stack->stack_top != RELAY_MAGIC)
136         ERR( "Stack corrupted!\n" );
137
138     stack->inuse = 0;
139 }
140
141
142 /**********************************************************************
143  *          DOSVM_SaveCallFrame
144  *
145  * Save current call frame. This routine must be called from DOSRELAY
146  * called using DOSVM_BuildCallFrame before the relay modifies stack 
147  * pointer. This routine makes sure that the relay can return safely
148  * to application context and that no memory is leaked.
149  *
150  * Note: If DOSVM_BuildCallFrame was called using 32-bit CS or SS,
151  *       old values of CS and SS will be lost. This does not matter
152  *       since this routine is only used by Raw Mode Switch.
153  */
154 void DOSVM_SaveCallFrame( CONTEXT86 *context, STACK16FRAME *frame )
155 {
156     *frame = *CURRENT_STACK16;
157
158     /*
159      * If context is using allocated stack, release it.
160      */
161     if (context->SegSs == DOSVM_dpmi_segments->relay_data_sel)
162     {
163         RELAY_Stack16 *stack = RELAY_GetPointer( context->Esp );
164
165         if (!stack->inuse || 
166             stack->stack_bottom != RELAY_MAGIC ||
167             stack->stack_top != RELAY_MAGIC)
168             ERR( "Stack corrupted!\n" );
169
170         stack->inuse = 0;
171     }
172 }
173
174
175 /**********************************************************************
176  *          DOSVM_RestoreCallFrame
177  *
178  * Restore saved call frame to currect stack. This routine must always
179  * be called after DOSVM_SaveCallFrame has been called and before returning
180  * from DOSRELAY.
181  */
182 void DOSVM_RestoreCallFrame( CONTEXT86 *context, STACK16FRAME *frame )
183 {
184     /*
185      * Make sure that CS and SS are 16-bit.
186      */
187     RELAY_MakeShortContext( context );
188
189     /*
190      * After this function returns to relay code, protected mode
191      * 16 bit stack will contain STACK16FRAME and single WORD
192      * (EFlags, see next comment).
193      */
194     NtCurrentTeb()->cur_stack =
195         MAKESEGPTR( context->SegSs,
196                     context->Esp - sizeof(STACK16FRAME) - sizeof(WORD) );
197     
198     /*
199      * After relay code returns to glue function, protected
200      * mode 16 bit stack will contain interrupt return record:
201      * IP, CS and EFlags. Since EFlags is ignored, it won't
202      * need to be initialized.
203      */
204     context->Esp -= 3 * sizeof(WORD);
205
206     /*
207      * Restore stack frame so that relay code won't be confused.
208      * It should be noted that relay code overwrites IP and CS
209      * in STACK16FRAME with values taken from current CONTEXT86.
210      * These values are what is returned to glue function
211      * (see previous comment).
212      */
213     *CURRENT_STACK16 = *frame;
214 }
215
216
217 /**********************************************************************
218  *          DOSVM_BuildCallFrame
219  *
220  * Modifies the context so that return to context calls DOSRELAY and 
221  * only after return from DOSRELAY the original context will be returned to.
222  */
223 void DOSVM_BuildCallFrame( CONTEXT86 *context, DOSRELAY relay, LPVOID data )
224 {
225     WORD *stack;
226     WORD  code_sel = DOSVM_dpmi_segments->relay_code_sel;
227
228     /*
229      * Make sure that CS and SS are 16-bit.
230      */
231     RELAY_MakeShortContext( context );
232
233     /*
234      * Get stack pointer after RELAY_MakeShortContext.
235      */
236     stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
237
238     /*
239      * Build call frame.
240      */
241     *(--stack) = HIWORD(data);            /* argument.hiword */ 
242     *(--stack) = LOWORD(data);            /* argument.loword */
243     *(--stack) = context->SegCs;          /* STACK16FRAME.cs */
244     *(--stack) = LOWORD(context->Eip);    /* STACK16FRAME.ip */
245     *(--stack) = LOWORD(context->Ebp);    /* STACK16FRAME.bp */
246     *(--stack) = HIWORD(relay);           /* STACK16FRAME.entry_point.hiword */
247     *(--stack) = LOWORD(relay);           /* STACK16FRAME.entry_point.loword */
248     *(--stack) = 0;                       /* STACK16FRAME.entry_ip */
249     *(--stack) = HIWORD(RELAY_RelayStub); /* STACK16FRAME.relay.hiword */
250     *(--stack) = LOWORD(RELAY_RelayStub); /* STACK16FRAME.relay.loword */
251     *(--stack) = 0;                       /* STACK16FRAME.module_cs.hiword */
252     *(--stack) = code_sel;                /* STACK16FRAME.module_cs.loword */
253     *(--stack) = 0;                       /* STACK16FRAME.callfrom_ip.hiword */
254     *(--stack) = 0;                       /* STACK16FRAME.callfrom_ip.loword */
255
256     /*
257      * Adjust stack and code pointers.
258      */
259     ADD_LOWORD( context->Esp, -28 );
260     context->SegCs = wine_get_cs();
261     context->Eip = (DWORD)__wine_call_from_16_regs;
262 }