aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/ndis/winx32_wrap.S
blob: c051504afb29064a9858d3c83a7aa14915c1ac26 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
/*-
 * Copyright (c) 2005
 *      Bill Paul <wpaul@windriver.com>.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Bill Paul.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD$
 */

/* The 'ret' macro doesn't work in this file if GPROF is enabled. */
#ifdef GPROF
#undef GPROF
#endif

#include <machine/asmacros.h>

/*
 * This file contains assembly language wrappers for the different
 * calling conventions supported by Windows on the i386 architecture.
 * In FreeBSD, the whole OS typically use same C calling convention
 * everywhere, namely _cdecl. Windows, on the other hand, uses several
 * different C calling conventions depending on the circumstances:
 *
 * _stdcall: Used for most ordinary Windows APIs. With _stdcall,
 * arguments are passed on the stack, and the callee unwinds the stack
 * before returning control to the caller. Not suitable for variadic
 * functions.
 *
 * _fastcall: Used for some APIs that may be invoked frequently and
 * where speed is a critical factor (e.g. KeAcquireSpinLock() and
 * KeReleaseSpinLock()) Similar to _stdcall, except the first 2 32-bit
 * or smaller arguments are passed in the %ecx and %edx registers
 * instead of on the stack. Not suitable for variadic functions.
 *
 * _cdecl: Used for standard C library routines and for variadic
 * functions.
 *
 * _regparm(3): Used for certain assembly routines. All arguments
 * passed in %eax, %ecx and %edx.
 *
 * Furthermore, there is an additional wrinkle that's not obvious
 * with all code: Microsoft supports the use of exceptions in C
 * (__try/__except) both in user _and_ kernel mode. Sadly, Windows
 * structured exception handling uses machine-specific features
 * that conflict rather badly with FreeBSD. (See utility routines
 * at the end of this module for more details.)
 *
 * We want to support these calling conventions in as portable a manner
 * as possible. The trick is doing it not only with different versions
 * of GNU C, but with compilers other than GNU C (e.g. the Solaris
 * SunOne C compiler). The only sure fire method is with assembly
 * language trampoline code which both fixes up the argument passing,
 * stack unwinding and exception/thread context all at once.
 *
 * You'll notice that we call the thunk/unthunk routines in the
 * *_wrap() functions in an awkward way. Rather than branching
 * directly to the address, we load the address into a register
 * first as a literal value, then we branch to it. This is done
 * to insure that the assembler doesn't translate the branch into
 * a relative branch. We use the *_wrap() routines here as templates
 * and create the actual trampolines at run time, at which point
 * we only know the absolute addresses of the thunk and unthunk
 * routines. So we need to make sure the templates have enough
 * room in them for the full address.
 *
 * Also note that when we call the a thunk/unthunk routine after
 * invoking a wrapped function, we have to make sure to preserve
 * the value returned from that function. Most functions return
 * a 32-bit value in %eax, however some routines return 64-bit
 * values, which span both %eax and %edx. Consequently, we have
 * to preserve both registers.
 */

/*
 * Handle _stdcall going from Windows to UNIX.
 * This is frustrating, because to do it right you have to
 * know how many arguments the called function takes, and there's
 * no way to figure this out on the fly: you just have to be told
 * ahead of time. We assume there will be 16 arguments. I don't
 * think there are any Windows APIs that require this many.
 */
 
	.globl x86_stdcall_wrap_call
	.globl x86_stdcall_wrap_arg
	.globl x86_stdcall_wrap_end

ENTRY(x86_stdcall_wrap)
	push	%esi
	push	%edi
	sub	$64,%esp
	mov	%esp,%esi
	add	$64+8+4,%esi
	mov	%esp,%edi
	mov	$16,%ecx	# handle up to 16 args
	rep
	movsl

	movl	$ctxsw_wtou, %eax
	call	*%eax           # unthunk

x86_stdcall_wrap_call:
        movl    $0,%eax
	call	*%eax		# jump to routine
	push	%eax		# preserve return val
	push	%edx

	movl	$ctxsw_utow, %eax
	call	*%eax		# thunk

	pop	%edx
	pop	%eax		# restore return val

	add	$64,%esp	# clean the stack
	pop	%edi
	pop	%esi
x86_stdcall_wrap_arg:
	ret	$0xFF
x86_stdcall_wrap_end:


/*
 * Handle _stdcall going from UNIX to Windows. This routine
 * expects to be passed the function to be called, number of
 * args and the arguments for the Windows function on the stack.
 */

ENTRY(x86_stdcall_call)
	push	%esi		# must preserve %esi
	push	%edi		# and %edi

	mov	16(%esp),%eax	# get arg cnt
	mov	%eax,%ecx	# save as copy count
	mov	%esp,%esi	# Set source address register to point to
	add	$20,%esi	# first agument to be forwarded.
	shl	$2,%eax		# turn arg cnt into offset
	sub	%eax,%esp	# shift stack to new location 
	mov	%esp,%edi	# store dest copy addr
	rep			# do the copy
	movsl

	call	ctxsw_utow	# thunk

	call	*12(%edi)	# branch to stdcall routine
	push	%eax		# preserve return val
	push	%edx

	call	ctxsw_wtou	# unthunk

	pop	%edx
	pop	%eax		# restore return val
	mov	%edi,%esp	# restore stack
	pop	%edi		# restore %edi
	pop	%esi		# and %esi
	ret

/*
 * Fastcall support. Similar to _stdcall, except the first
 * two arguments are passed in %ecx and %edx. It happens we
 * only support a small number of _fastcall APIs, none of them
 * take more than three arguments. So to keep the code size
 * and complexity down, we only handle 3 arguments here.
 */

/* Call _fastcall function going from Windows to UNIX. */

	.globl x86_fastcall_wrap_call
	.globl x86_fastcall_wrap_arg
	.globl x86_fastcall_wrap_end

ENTRY(x86_fastcall_wrap)
	mov	4(%esp),%eax
	push	%eax
	push	%edx
	push	%ecx

	movl	$ctxsw_wtou, %eax
	call	*%eax		# unthunk

x86_fastcall_wrap_call:
	mov	$0,%eax
	call	*%eax		# branch to fastcall routine
	push	%eax		# preserve return val
	push	%edx

	movl	$ctxsw_utow, %eax
	call	*%eax		# thunk

	pop	%edx
	pop	%eax		# restore return val
	add	$12,%esp	# clean the stack
x86_fastcall_wrap_arg:
	ret	$0xFF
x86_fastcall_wrap_end:

/*
 * Call _fastcall function going from UNIX to Windows.
 * This routine isn't normally used since NDIS miniport drivers
 * only have _stdcall entry points, but it's provided anyway
 * to round out the API, and for testing purposes.
 */

ENTRY(x86_fastcall_call)
	mov	4(%esp),%eax
	push	16(%esp)

	call	ctxsw_utow	# thunk

	mov	12(%esp),%ecx
	mov	16(%esp),%edx
	call	*8(%esp)	# branch to fastcall routine
	push	%eax		# preserve return val
	push	%edx

	call	ctxsw_wtou	# unthunk

	pop	%edx
	pop	%eax		# restore return val
	add	$4,%esp		# clean the stack
	ret

/*
 * Call regparm(3) function going from Windows to UNIX. Arguments
 * are passed in %eax, %edx and %ecx. Note that while additional
 * arguments are passed on the stack, we never bother when them,
 * since the only regparm(3) routines we need to wrap never take
 * more than 3 arguments.
 */

	.globl x86_regparm_wrap_call
	.globl x86_regparm_wrap_end

ENTRY(x86_regparm_wrap)
	push	%ecx
	push	%edx
	push	%eax

	movl	$ctxsw_wtou, %eax
	call	*%eax		# unthunk

x86_regparm_wrap_call:
	movl	$0,%eax
	call	*%eax		# jump to routine
	push	%eax		# preserve return val
	push	%edx		# preserve return val

	movl	$ctxsw_utow, %eax
	call	*%eax		# thunk

	pop	%edx		# restore return val
	pop	%eax		# restore return val
	add	$12,%esp	# restore stack
	ret
x86_regparm_wrap_end:

/*
 * Call regparm(3) function going from UNIX to Windows.
 * This routine isn't normally used since NDIS miniport drivers
 * only have _stdcall entry points, but it's provided anyway
 * to round out the API, and for testing purposes.
 */

ENTRY(x86_regparm_call)
	call	ctxsw_utow	# thunk

	mov	8(%esp),%eax
	mov	12(%esp),%edx
	mov	16(%esp),%ecx
	call	*4(%esp)	# branch to fastcall routine
	push	%eax		# preserve return val
	push	%edx		# preserve return val

	call	ctxsw_wtou	# unthunk

	pop	%edx		# restore return val
	pop	%eax		# restore return val
	ret

/*
 * Ugly hack alert:
 *
 * On Win32/i386, using __try/__except results in code that tries to
 * manipulate what's supposed to be the Windows Threada Environment 
 * Block (TEB), which one accesses via the %fs register. In particular,
 * %fs:0 (the first DWORD in the TEB) points to the exception
 * registration list. Unfortunately, FreeBSD uses %fs for the
 * per-cpu data structure (pcpu), and we can't allow Windows code
 * to muck with that. I don't even know what Solaris uses %fs for
 * (or if it even uses it at all).
 *
 * Even worse, in 32-bit protected mode, %fs is a selector that
 * refers to an entry in either the GDT or the LDT. Ideally, we would
 * like to be able to temporarily point it at another descriptor
 * while Windows code executes, but to do that we need a separate
 * descriptor entry of our own to play with.
 *
 * Therefore, we go to some trouble to learn the existing layout of
 * the GDT and update it to include an extra entry that we can use.
 * We need the following utility routines to help us do that. On
 * FreeBSD, index #7 in the GDT happens to be unused, so we turn
 * this into our own data segment descriptor. It would be better
 * if we could use a private LDT entry, but there's no easy way to
 * do that in SMP mode because of the way FreeBSD handles user LDTs.
 *
 * Once we have a custom descriptor, we have to thunk/unthunk whenever
 * we cross between FreeBSD code and Windows code. The thunking is
 * based on the premise that when executing instructions in the
 * Windows binary itself, we won't go to sleep. This is because in
 * order to yield the CPU, the code has to call back out to a FreeBSD
 * routine first, and when that happens we can unthunk in order to
 * restore FreeBSD context. What we're desperately trying to avoid is
 * being involuntarily pre-empted with the %fs register still pointing
 * to our fake TIB: if FreeBSD code runs with %fs pointing at our
 * Windows TIB instead of pcpu, we'll panic the kernel. Fortunately,
 * the only way involuntary preemption can occur is if an interrupt
 * fires, and the trap handler saves/restores %fs for us.
 *
 * The thunking routines themselves, ctxsw_utow() (Context SWitch UNIX
 * to Windows) and ctxsw_wtou() (Context SWitch Windows to UNIX), are
 * external to this module. This is done simply because it's easier 
 * to manipulate data structures in C rather than assembly.
 */

ENTRY(x86_getldt)
	movl	4(%esp),%eax
	sgdtl	(%eax)
	movl	8(%esp),%eax
	sldt	(%eax)
	xor	%eax,%eax
	ret

ENTRY(x86_setldt)
	movl    4(%esp),%eax
	lgdt	(%eax)
	jmp	1f
	nop
1:
	movl    8(%esp),%eax
	lldt	%ax
	xor	%eax,%eax
	ret

ENTRY(x86_getfs)
	mov	%fs,%ax
	ret

ENTRY(x86_setfs)
	mov	4(%esp),%fs
	ret

ENTRY(x86_gettid)
	mov	%fs:12,%eax
	ret

ENTRY(x86_critical_enter)
	cli
	ret

ENTRY(x86_critical_exit)
	sti
	ret