kern_windrv.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 2005
5 *      Bill Paul <wpaul@windriver.com>.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *      This product includes software developed by Bill Paul.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: stable/11/sys/compat/ndis/kern_windrv.c 330897 2018-03-14 03:19:51Z eadler $");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/unistd.h>
41#include <sys/types.h>
42
43#include <sys/kernel.h>
44#include <sys/malloc.h>
45#include <sys/lock.h>
46#include <sys/mutex.h>
47#include <sys/module.h>
48#include <sys/conf.h>
49#include <sys/mbuf.h>
50#include <sys/bus.h>
51#include <sys/proc.h>
52#include <sys/sched.h>
53#include <sys/smp.h>
54
55#include <sys/queue.h>
56
57#ifdef __i386__
58#include <machine/segments.h>
59#endif
60
61#include <dev/usb/usb.h>
62
63#include <compat/ndis/pe_var.h>
64#include <compat/ndis/cfg_var.h>
65#include <compat/ndis/resource_var.h>
66#include <compat/ndis/ntoskrnl_var.h>
67#include <compat/ndis/ndis_var.h>
68#include <compat/ndis/hal_var.h>
69#include <compat/ndis/usbd_var.h>
70
71static struct mtx drvdb_mtx;
72static STAILQ_HEAD(drvdb, drvdb_ent) drvdb_head;
73
74static driver_object	fake_pci_driver; /* serves both PCI and cardbus */
75static driver_object	fake_pccard_driver;
76
77#ifdef __i386__
78static void x86_oldldt(void *);
79static void x86_newldt(void *);
80
81struct tid {
82	void			*tid_except_list;	/* 0x00 */
83	uint32_t		tid_oldfs;		/* 0x04 */
84	uint32_t		tid_selector;		/* 0x08 */
85	struct tid		*tid_self;		/* 0x0C */
86	int			tid_cpu;		/* 0x10 */
87};
88
89static struct tid	*my_tids;
90#endif /* __i386__ */
91
92#define DUMMY_REGISTRY_PATH "\\\\some\\bogus\\path"
93
94int
95windrv_libinit(void)
96{
97	STAILQ_INIT(&drvdb_head);
98	mtx_init(&drvdb_mtx, "Windows driver DB lock",
99	    "Windows internal lock", MTX_DEF);
100
101	/*
102	 * PCI and pccard devices don't need to use IRPs to
103	 * interact with their bus drivers (usually), so our
104	 * emulated PCI and pccard drivers are just stubs.
105	 * USB devices, on the other hand, do all their I/O
106	 * by exchanging IRPs with the USB bus driver, so
107	 * for that we need to provide emulator dispatcher
108	 * routines, which are in a separate module.
109	 */
110
111	windrv_bus_attach(&fake_pci_driver, "PCI Bus");
112	windrv_bus_attach(&fake_pccard_driver, "PCCARD Bus");
113
114#ifdef __i386__
115
116	/*
117	 * In order to properly support SMP machines, we have
118	 * to modify the GDT on each CPU, since we never know
119	 * on which one we'll end up running.
120	 */
121
122	my_tids = ExAllocatePoolWithTag(NonPagedPool,
123	    sizeof(struct tid) * mp_ncpus, 0);
124	if (my_tids == NULL)
125		panic("failed to allocate thread info blocks");
126	smp_rendezvous(NULL, x86_newldt, NULL, NULL);
127#endif
128	return (0);
129}
130
131int
132windrv_libfini(void)
133{
134	struct drvdb_ent	*d;
135
136	mtx_lock(&drvdb_mtx);
137	while(STAILQ_FIRST(&drvdb_head) != NULL) {
138		d = STAILQ_FIRST(&drvdb_head);
139		STAILQ_REMOVE_HEAD(&drvdb_head, link);
140		free(d, M_DEVBUF);
141	}
142	mtx_unlock(&drvdb_mtx);
143
144	RtlFreeUnicodeString(&fake_pci_driver.dro_drivername);
145	RtlFreeUnicodeString(&fake_pccard_driver.dro_drivername);
146
147	mtx_destroy(&drvdb_mtx);
148
149#ifdef __i386__
150	smp_rendezvous(NULL, x86_oldldt, NULL, NULL);
151	ExFreePool(my_tids);
152#endif
153	return (0);
154}
155
156/*
157 * Given the address of a driver image, find its corresponding
158 * driver_object.
159 */
160
161driver_object *
162windrv_lookup(img, name)
163	vm_offset_t		img;
164	char			*name;
165{
166	struct drvdb_ent	*d;
167	unicode_string		us;
168	ansi_string		as;
169
170	bzero((char *)&us, sizeof(us));
171
172	/* Damn unicode. */
173
174	if (name != NULL) {
175		RtlInitAnsiString(&as, name);
176		if (RtlAnsiStringToUnicodeString(&us, &as, TRUE))
177			return (NULL);
178	}
179
180	mtx_lock(&drvdb_mtx);
181	STAILQ_FOREACH(d, &drvdb_head, link) {
182		if (d->windrv_object->dro_driverstart == (void *)img ||
183		    (bcmp((char *)d->windrv_object->dro_drivername.us_buf,
184		    (char *)us.us_buf, us.us_len) == 0 && us.us_len)) {
185			mtx_unlock(&drvdb_mtx);
186			if (name != NULL)
187				ExFreePool(us.us_buf);
188			return (d->windrv_object);
189		}
190	}
191	mtx_unlock(&drvdb_mtx);
192
193	if (name != NULL)
194		RtlFreeUnicodeString(&us);
195
196	return (NULL);
197}
198
199struct drvdb_ent *
200windrv_match(matchfunc, ctx)
201	matchfuncptr		matchfunc;
202	void			*ctx;
203{
204	struct drvdb_ent	*d;
205	int			match;
206
207	mtx_lock(&drvdb_mtx);
208	STAILQ_FOREACH(d, &drvdb_head, link) {
209		if (d->windrv_devlist == NULL)
210			continue;
211		match = matchfunc(d->windrv_bustype, d->windrv_devlist, ctx);
212		if (match == TRUE) {
213			mtx_unlock(&drvdb_mtx);
214			return (d);
215		}
216	}
217	mtx_unlock(&drvdb_mtx);
218
219	return (NULL);
220}
221
222/*
223 * Remove a driver_object from our datatabase and destroy it. Throw
224 * away any custom driver extension info that may have been added.
225 */
226
227int
228windrv_unload(mod, img, len)
229	module_t		mod;
230	vm_offset_t		img;
231	int			len;
232{
233	struct drvdb_ent	*db, *r = NULL;
234	driver_object		*drv;
235	device_object		*d, *pdo;
236	device_t		dev;
237	list_entry		*e;
238
239	drv = windrv_lookup(img, NULL);
240
241	/*
242	 * When we unload a driver image, we need to force a
243	 * detach of any devices that might be using it. We
244	 * need the PDOs of all attached devices for this.
245	 * Getting at them is a little hard. We basically
246	 * have to walk the device lists of all our bus
247	 * drivers.
248	 */
249
250	mtx_lock(&drvdb_mtx);
251	STAILQ_FOREACH(db, &drvdb_head, link) {
252		/*
253		 * Fake bus drivers have no devlist info.
254		 * If this driver has devlist info, it's
255		 * a loaded Windows driver and has no PDOs,
256		 * so skip it.
257		 */
258		if (db->windrv_devlist != NULL)
259			continue;
260		pdo = db->windrv_object->dro_devobj;
261		while (pdo != NULL) {
262			d = pdo->do_attacheddev;
263			if (d->do_drvobj != drv) {
264				pdo = pdo->do_nextdev;
265				continue;
266			}
267			dev = pdo->do_devext;
268			pdo = pdo->do_nextdev;
269			mtx_unlock(&drvdb_mtx);
270			device_detach(dev);
271			mtx_lock(&drvdb_mtx);
272		}
273	}
274
275	STAILQ_FOREACH(db, &drvdb_head, link) {
276		if (db->windrv_object->dro_driverstart == (void *)img) {
277			r = db;
278			STAILQ_REMOVE(&drvdb_head, db, drvdb_ent, link);
279			break;
280		}
281	}
282	mtx_unlock(&drvdb_mtx);
283
284	if (r == NULL)
285		return (ENOENT);
286
287	if (drv == NULL)
288		return (ENOENT);
289
290	/*
291	 * Destroy any custom extensions that may have been added.
292	 */
293	drv = r->windrv_object;
294	while (!IsListEmpty(&drv->dro_driverext->dre_usrext)) {
295		e = RemoveHeadList(&drv->dro_driverext->dre_usrext);
296		ExFreePool(e);
297	}
298
299	/* Free the driver extension */
300	free(drv->dro_driverext, M_DEVBUF);
301
302	/* Free the driver name */
303	RtlFreeUnicodeString(&drv->dro_drivername);
304
305	/* Free driver object */
306	free(drv, M_DEVBUF);
307
308	/* Free our DB handle */
309	free(r, M_DEVBUF);
310
311	return (0);
312}
313
314#define WINDRV_LOADED		htonl(0x42534F44)
315
316#ifdef __amd64__
317static void
318patch_user_shared_data_address(vm_offset_t img, size_t len)
319{
320	unsigned long i, n, max_addr, *addr;
321
322	n = len - sizeof(unsigned long);
323	max_addr = KI_USER_SHARED_DATA + sizeof(kuser_shared_data);
324	for (i = 0; i < n; i++) {
325		addr = (unsigned long *)(img + i);
326		if (*addr >= KI_USER_SHARED_DATA && *addr < max_addr) {
327			*addr -= KI_USER_SHARED_DATA;
328			*addr += (unsigned long)&kuser_shared_data;
329		}
330	}
331}
332#endif
333
334/*
335 * Loader routine for actual Windows driver modules, ultimately
336 * calls the driver's DriverEntry() routine.
337 */
338
339int
340windrv_load(mod, img, len, bustype, devlist, regvals)
341	module_t		mod;
342	vm_offset_t		img;
343	int			len;
344	interface_type		bustype;
345	void			*devlist;
346	ndis_cfg		*regvals;
347{
348	image_import_descriptor	imp_desc;
349	image_optional_header	opt_hdr;
350	driver_entry		entry;
351	struct drvdb_ent	*new;
352	struct driver_object	*drv;
353	int			status;
354	uint32_t		*ptr;
355	ansi_string		as;
356
357	/*
358	 * First step: try to relocate and dynalink the executable
359	 * driver image.
360	 */
361
362	ptr = (uint32_t *)(img + 8);
363	if (*ptr == WINDRV_LOADED)
364		goto skipreloc;
365
366	/* Perform text relocation */
367	if (pe_relocate(img))
368		return (ENOEXEC);
369
370	/* Dynamically link the NDIS.SYS routines -- required. */
371	if (pe_patch_imports(img, "NDIS", ndis_functbl))
372		return (ENOEXEC);
373
374	/* Dynamically link the HAL.dll routines -- optional. */
375	if (pe_get_import_descriptor(img, &imp_desc, "HAL") == 0) {
376		if (pe_patch_imports(img, "HAL", hal_functbl))
377			return (ENOEXEC);
378	}
379
380	/* Dynamically link ntoskrnl.exe -- optional. */
381	if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) {
382		if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl))
383			return (ENOEXEC);
384	}
385
386#ifdef __amd64__
387	patch_user_shared_data_address(img, len);
388#endif
389
390	/* Dynamically link USBD.SYS -- optional */
391	if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) {
392		if (pe_patch_imports(img, "USBD", usbd_functbl))
393			return (ENOEXEC);
394	}
395
396	*ptr = WINDRV_LOADED;
397
398skipreloc:
399
400	/* Next step: find the driver entry point. */
401
402	pe_get_optional_header(img, &opt_hdr);
403	entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
404
405	/* Next step: allocate and store a driver object. */
406
407	new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
408	if (new == NULL)
409		return (ENOMEM);
410
411	drv = malloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO);
412	if (drv == NULL) {
413		free (new, M_DEVBUF);
414		return (ENOMEM);
415	}
416
417	/* Allocate a driver extension structure too. */
418
419	drv->dro_driverext = malloc(sizeof(driver_extension),
420	    M_DEVBUF, M_NOWAIT|M_ZERO);
421
422	if (drv->dro_driverext == NULL) {
423		free(new, M_DEVBUF);
424		free(drv, M_DEVBUF);
425		return (ENOMEM);
426	}
427
428	InitializeListHead((&drv->dro_driverext->dre_usrext));
429
430	drv->dro_driverstart = (void *)img;
431	drv->dro_driversize = len;
432
433	RtlInitAnsiString(&as, DUMMY_REGISTRY_PATH);
434	if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) {
435		free(new, M_DEVBUF);
436		free(drv, M_DEVBUF);
437		return (ENOMEM);
438	}
439
440	new->windrv_object = drv;
441	new->windrv_regvals = regvals;
442	new->windrv_devlist = devlist;
443	new->windrv_bustype = bustype;
444
445	/* Now call the DriverEntry() function. */
446
447	status = MSCALL2(entry, drv, &drv->dro_drivername);
448
449	if (status != STATUS_SUCCESS) {
450		RtlFreeUnicodeString(&drv->dro_drivername);
451		free(drv, M_DEVBUF);
452		free(new, M_DEVBUF);
453		return (ENODEV);
454	}
455
456	mtx_lock(&drvdb_mtx);
457	STAILQ_INSERT_HEAD(&drvdb_head, new, link);
458	mtx_unlock(&drvdb_mtx);
459
460	return (0);
461}
462
463/*
464 * Make a new Physical Device Object for a device that was
465 * detected/plugged in. For us, the PDO is just a way to
466 * get at the device_t.
467 */
468
469int
470windrv_create_pdo(drv, bsddev)
471	driver_object		*drv;
472	device_t		bsddev;
473{
474	device_object		*dev;
475
476	/*
477	 * This is a new physical device object, which technically
478	 * is the "top of the stack." Consequently, we don't do
479	 * an IoAttachDeviceToDeviceStack() here.
480	 */
481
482	mtx_lock(&drvdb_mtx);
483	IoCreateDevice(drv, 0, NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev);
484	mtx_unlock(&drvdb_mtx);
485
486	/* Stash pointer to our BSD device handle. */
487
488	dev->do_devext = bsddev;
489
490	return (STATUS_SUCCESS);
491}
492
493void
494windrv_destroy_pdo(drv, bsddev)
495	driver_object		*drv;
496	device_t		bsddev;
497{
498	device_object		*pdo;
499
500	pdo = windrv_find_pdo(drv, bsddev);
501
502	/* Remove reference to device_t */
503
504	pdo->do_devext = NULL;
505
506	mtx_lock(&drvdb_mtx);
507	IoDeleteDevice(pdo);
508	mtx_unlock(&drvdb_mtx);
509}
510
511/*
512 * Given a device_t, find the corresponding PDO in a driver's
513 * device list.
514 */
515
516device_object *
517windrv_find_pdo(drv, bsddev)
518	driver_object		*drv;
519	device_t		bsddev;
520{
521	device_object		*pdo;
522
523	mtx_lock(&drvdb_mtx);
524	pdo = drv->dro_devobj;
525	while (pdo != NULL) {
526		if (pdo->do_devext == bsddev) {
527			mtx_unlock(&drvdb_mtx);
528			return (pdo);
529		}
530		pdo = pdo->do_nextdev;
531	}
532	mtx_unlock(&drvdb_mtx);
533
534	return (NULL);
535}
536
537/*
538 * Add an internally emulated driver to the database. We need this
539 * to set up an emulated bus driver so that it can receive IRPs.
540 */
541
542int
543windrv_bus_attach(drv, name)
544	driver_object		*drv;
545	char			*name;
546{
547	struct drvdb_ent	*new;
548	ansi_string		as;
549
550	new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
551	if (new == NULL)
552		return (ENOMEM);
553
554	RtlInitAnsiString(&as, name);
555	if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE))
556	{
557		free(new, M_DEVBUF);
558		return (ENOMEM);
559	}
560
561	/*
562	 * Set up a fake image pointer to avoid false matches
563	 * in windrv_lookup().
564	 */
565	drv->dro_driverstart = (void *)0xFFFFFFFF;
566
567	new->windrv_object = drv;
568	new->windrv_devlist = NULL;
569	new->windrv_regvals = NULL;
570
571	mtx_lock(&drvdb_mtx);
572	STAILQ_INSERT_HEAD(&drvdb_head, new, link);
573	mtx_unlock(&drvdb_mtx);
574
575	return (0);
576}
577
578#ifdef __amd64__
579
580extern void	x86_64_wrap(void);
581extern void	x86_64_wrap_call(void);
582extern void	x86_64_wrap_end(void);
583
584int
585windrv_wrap(func, wrap, argcnt, ftype)
586	funcptr			func;
587	funcptr			*wrap;
588	int			argcnt;
589	int			ftype;
590{
591	funcptr			p;
592	vm_offset_t		*calladdr;
593	vm_offset_t		wrapstart, wrapend, wrapcall;
594
595	wrapstart = (vm_offset_t)&x86_64_wrap;
596	wrapend = (vm_offset_t)&x86_64_wrap_end;
597	wrapcall = (vm_offset_t)&x86_64_wrap_call;
598
599	/* Allocate a new wrapper instance. */
600
601	p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
602	if (p == NULL)
603		return (ENOMEM);
604
605	/* Copy over the code. */
606
607	bcopy((char *)wrapstart, p, (wrapend - wrapstart));
608
609	/* Insert the function address into the new wrapper instance. */
610
611	calladdr = (uint64_t *)((char *)p + (wrapcall - wrapstart) + 2);
612	*calladdr = (vm_offset_t)func;
613
614	*wrap = p;
615
616	return (0);
617}
618#endif /* __amd64__ */
619
620
621#ifdef __i386__
622
623struct x86desc {
624	uint16_t		x_lolimit;
625	uint16_t		x_base0;
626	uint8_t			x_base1;
627	uint8_t			x_flags;
628	uint8_t			x_hilimit;
629	uint8_t			x_base2;
630};
631
632struct gdt {
633	uint16_t		limit;
634	void			*base;
635} __attribute__((__packed__));
636
637extern uint16_t	x86_getfs(void);
638extern void x86_setfs(uint16_t);
639extern void *x86_gettid(void);
640extern void x86_critical_enter(void);
641extern void x86_critical_exit(void);
642extern void x86_getldt(struct gdt *, uint16_t *);
643extern void x86_setldt(struct gdt *, uint16_t);
644
645#define SEL_LDT	4		/* local descriptor table */
646#define SEL_TO_FS(x)		(((x) << 3))
647
648/*
649 * FreeBSD 6.0 and later has a special GDT segment reserved
650 * specifically for us, so if GNDIS_SEL is defined, use that.
651 * If not, use GTGATE_SEL, which is uninitialized and infrequently
652 * used.
653 */
654
655#ifdef GNDIS_SEL
656#define FREEBSD_EMPTYSEL	GNDIS_SEL
657#else
658#define FREEBSD_EMPTYSEL	GTGATE_SEL	/* slot 7 */
659#endif
660
661/*
662 * The meanings of various bits in a descriptor vary a little
663 * depending on whether the descriptor will be used as a
664 * code, data or system descriptor. (And that in turn depends
665 * on which segment register selects the descriptor.)
666 * We're only trying to create a data segment, so the definitions
667 * below are the ones that apply to a data descriptor.
668 */
669
670#define SEGFLAGLO_PRESENT	0x80	/* segment is present */
671#define SEGFLAGLO_PRIVLVL	0x60	/* privlevel needed for this seg */
672#define SEGFLAGLO_CD		0x10	/* 1 = code/data, 0 = system */
673#define SEGFLAGLO_MBZ		0x08	/* must be zero */
674#define SEGFLAGLO_EXPANDDOWN	0x04	/* limit expands down */
675#define SEGFLAGLO_WRITEABLE	0x02	/* segment is writeable */
676#define SEGGLAGLO_ACCESSED	0x01	/* segment has been accessed */
677
678#define SEGFLAGHI_GRAN		0x80	/* granularity, 1 = byte, 0 = page */
679#define SEGFLAGHI_BIG		0x40	/* 1 = 32 bit stack, 0 = 16 bit */
680
681/*
682 * Context switch from UNIX to Windows. Save the existing value
683 * of %fs for this processor, then change it to point to our
684 * fake TID. Note that it is also possible to pin ourselves
685 * to our current CPU, though I'm not sure this is really
686 * necessary. It depends on whether or not an interrupt might
687 * preempt us while Windows code is running and we wind up
688 * scheduled onto another CPU as a result. So far, it doesn't
689 * seem like this is what happens.
690 */
691
692void
693ctxsw_utow(void)
694{
695	struct tid		*t;
696
697	t = &my_tids[curthread->td_oncpu];
698
699	/*
700	 * Ugly hack. During system bootstrap (cold == 1), only CPU 0
701	 * is running. So if we were loaded at bootstrap, only CPU 0
702	 * will have our special GDT entry. This is a problem for SMP
703	 * systems, so to deal with this, we check here to make sure
704	 * the TID for this processor has been initialized, and if it
705	 * hasn't, we need to do it right now or else things will
706	 * explode.
707	 */
708
709	if (t->tid_self != t)
710		x86_newldt(NULL);
711
712	x86_critical_enter();
713	t->tid_oldfs = x86_getfs();
714	t->tid_cpu = curthread->td_oncpu;
715	sched_pin();
716	x86_setfs(SEL_TO_FS(t->tid_selector));
717	x86_critical_exit();
718
719	/* Now entering Windows land, population: you. */
720}
721
722/*
723 * Context switch from Windows back to UNIX. Restore %fs to
724 * its previous value. This always occurs after a call to
725 * ctxsw_utow().
726 */
727
728void
729ctxsw_wtou(void)
730{
731	struct tid		*t;
732
733	x86_critical_enter();
734	t = x86_gettid();
735	x86_setfs(t->tid_oldfs);
736	sched_unpin();
737	x86_critical_exit();
738
739	/* Welcome back to UNIX land, we missed you. */
740
741#ifdef EXTRA_SANITY
742	if (t->tid_cpu != curthread->td_oncpu)
743		panic("ctxsw GOT MOVED TO OTHER CPU!");
744#endif
745}
746
747static int	windrv_wrap_stdcall(funcptr, funcptr *, int);
748static int	windrv_wrap_fastcall(funcptr, funcptr *, int);
749static int	windrv_wrap_regparm(funcptr, funcptr *);
750
751extern void	x86_fastcall_wrap(void);
752extern void	x86_fastcall_wrap_call(void);
753extern void	x86_fastcall_wrap_arg(void);
754extern void	x86_fastcall_wrap_end(void);
755
756static int
757windrv_wrap_fastcall(func, wrap, argcnt)
758	funcptr			func;
759	funcptr			*wrap;
760	int8_t			argcnt;
761{
762	funcptr			p;
763	vm_offset_t		*calladdr;
764	uint8_t			*argaddr;
765	vm_offset_t		wrapstart, wrapend, wrapcall, wraparg;
766
767	wrapstart = (vm_offset_t)&x86_fastcall_wrap;
768	wrapend = (vm_offset_t)&x86_fastcall_wrap_end;
769	wrapcall = (vm_offset_t)&x86_fastcall_wrap_call;
770	wraparg = (vm_offset_t)&x86_fastcall_wrap_arg;
771
772	/* Allocate a new wrapper instance. */
773
774	p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
775	if (p == NULL)
776		return (ENOMEM);
777
778	/* Copy over the code. */
779
780	bcopy((char *)wrapstart, p, (wrapend - wrapstart));
781
782	/* Insert the function address into the new wrapper instance. */
783
784	calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
785	*calladdr = (vm_offset_t)func;
786
787	argcnt -= 2;
788	if (argcnt < 1)
789		argcnt = 0;
790
791	argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1));
792	*argaddr = argcnt * sizeof(uint32_t);
793
794	*wrap = p;
795
796	return (0);
797}
798
799extern void	x86_stdcall_wrap(void);
800extern void	x86_stdcall_wrap_call(void);
801extern void	x86_stdcall_wrap_arg(void);
802extern void	x86_stdcall_wrap_end(void);
803
804static int
805windrv_wrap_stdcall(func, wrap, argcnt)
806	funcptr			func;
807	funcptr			*wrap;
808	uint8_t			argcnt;
809{
810	funcptr			p;
811	vm_offset_t		*calladdr;
812	uint8_t			*argaddr;
813	vm_offset_t		wrapstart, wrapend, wrapcall, wraparg;
814
815	wrapstart = (vm_offset_t)&x86_stdcall_wrap;
816	wrapend = (vm_offset_t)&x86_stdcall_wrap_end;
817	wrapcall = (vm_offset_t)&x86_stdcall_wrap_call;
818	wraparg = (vm_offset_t)&x86_stdcall_wrap_arg;
819
820	/* Allocate a new wrapper instance. */
821
822	p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
823	if (p == NULL)
824		return (ENOMEM);
825
826	/* Copy over the code. */
827
828	bcopy((char *)wrapstart, p, (wrapend - wrapstart));
829
830	/* Insert the function address into the new wrapper instance. */
831
832	calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
833	*calladdr = (vm_offset_t)func;
834
835	argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1));
836	*argaddr = argcnt * sizeof(uint32_t);
837
838	*wrap = p;
839
840	return (0);
841}
842
843extern void	x86_regparm_wrap(void);
844extern void	x86_regparm_wrap_call(void);
845extern void	x86_regparm_wrap_end(void);
846
847static int
848windrv_wrap_regparm(func, wrap)
849	funcptr			func;
850	funcptr			*wrap;
851{
852	funcptr			p;
853	vm_offset_t		*calladdr;
854	vm_offset_t		wrapstart, wrapend, wrapcall;
855
856	wrapstart = (vm_offset_t)&x86_regparm_wrap;
857	wrapend = (vm_offset_t)&x86_regparm_wrap_end;
858	wrapcall = (vm_offset_t)&x86_regparm_wrap_call;
859
860	/* Allocate a new wrapper instance. */
861
862	p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
863	if (p == NULL)
864		return (ENOMEM);
865
866	/* Copy over the code. */
867
868	bcopy(x86_regparm_wrap, p, (wrapend - wrapstart));
869
870	/* Insert the function address into the new wrapper instance. */
871
872	calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
873	*calladdr = (vm_offset_t)func;
874
875	*wrap = p;
876
877	return (0);
878}
879
880int
881windrv_wrap(func, wrap, argcnt, ftype)
882	funcptr			func;
883	funcptr			*wrap;
884	int			argcnt;
885	int			ftype;
886{
887	switch(ftype) {
888	case WINDRV_WRAP_FASTCALL:
889		return (windrv_wrap_fastcall(func, wrap, argcnt));
890	case WINDRV_WRAP_STDCALL:
891		return (windrv_wrap_stdcall(func, wrap, argcnt));
892	case WINDRV_WRAP_REGPARM:
893		return (windrv_wrap_regparm(func, wrap));
894	case WINDRV_WRAP_CDECL:
895		return (windrv_wrap_stdcall(func, wrap, 0));
896	default:
897		break;
898	}
899
900	return (EINVAL);
901}
902
903static void
904x86_oldldt(dummy)
905	void			*dummy;
906{
907	struct x86desc		*gdt;
908	struct gdt		gtable;
909	uint16_t		ltable;
910
911	mtx_lock_spin(&dt_lock);
912
913	/* Grab location of existing GDT. */
914
915	x86_getldt(&gtable, &ltable);
916
917	/* Find the slot we updated. */
918
919	gdt = gtable.base;
920	gdt += FREEBSD_EMPTYSEL;
921
922	/* Empty it out. */
923
924	bzero((char *)gdt, sizeof(struct x86desc));
925
926	/* Restore GDT. */
927
928	x86_setldt(&gtable, ltable);
929
930	mtx_unlock_spin(&dt_lock);
931}
932
933static void
934x86_newldt(dummy)
935	void			*dummy;
936{
937	struct gdt		gtable;
938	uint16_t		ltable;
939	struct x86desc		*l;
940	struct thread		*t;
941
942	t = curthread;
943
944	mtx_lock_spin(&dt_lock);
945
946	/* Grab location of existing GDT. */
947
948	x86_getldt(&gtable, &ltable);
949
950	/* Get pointer to the GDT table. */
951
952	l = gtable.base;
953
954	/* Get pointer to empty slot */
955
956	l += FREEBSD_EMPTYSEL;
957
958	/* Initialize TID for this CPU. */
959
960	my_tids[t->td_oncpu].tid_selector = FREEBSD_EMPTYSEL;
961	my_tids[t->td_oncpu].tid_self = &my_tids[t->td_oncpu];
962
963	/* Set up new GDT entry. */
964
965	l->x_lolimit = sizeof(struct tid);
966	l->x_hilimit = SEGFLAGHI_GRAN|SEGFLAGHI_BIG;
967	l->x_base0 = (vm_offset_t)(&my_tids[t->td_oncpu]) & 0xFFFF;
968	l->x_base1 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 16) & 0xFF;
969	l->x_base2 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 24) & 0xFF;
970	l->x_flags = SEGFLAGLO_PRESENT|SEGFLAGLO_CD|SEGFLAGLO_WRITEABLE;
971
972	/* Update the GDT. */
973
974	x86_setldt(&gtable, ltable);
975
976	mtx_unlock_spin(&dt_lock);
977
978	/* Whew. */
979}
980
981#endif /* __i386__ */
982
983int
984windrv_unwrap(func)
985	funcptr			func;
986{
987	free(func, M_DEVBUF);
988
989	return (0);
990}
991