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