xref: /NextBSD/sys/compat/ndis/kern_windrv.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
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 
69 static struct mtx drvdb_mtx;
70 static STAILQ_HEAD(drvdb, drvdb_ent) drvdb_head;
71 
72 static driver_object	fake_pci_driver; /* serves both PCI and cardbus */
73 static driver_object	fake_pccard_driver;
74 
75 #ifdef __i386__
76 static void x86_oldldt(void *);
77 static void x86_newldt(void *);
78 
79 struct 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 
87 static struct tid	*my_tids;
88 #endif /* __i386__ */
89 
90 #define DUMMY_REGISTRY_PATH "\\\\some\\bogus\\path"
91 
92 int
windrv_libinit(void)93 windrv_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 
129 int
windrv_libfini(void)130 windrv_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 
159 driver_object *
windrv_lookup(img,name)160 windrv_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 
197 struct drvdb_ent *
windrv_match(matchfunc,ctx)198 windrv_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 
225 int
windrv_unload(mod,img,len)226 windrv_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__
315 static void
patch_user_shared_data_address(vm_offset_t img,size_t len)316 patch_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 
337 int
windrv_load(mod,img,len,bustype,devlist,regvals)338 windrv_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 
396 skipreloc:
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 
467 int
windrv_create_pdo(drv,bsddev)468 windrv_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 
491 void
windrv_destroy_pdo(drv,bsddev)492 windrv_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 
514 device_object *
windrv_find_pdo(drv,bsddev)515 windrv_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 
540 int
windrv_bus_attach(drv,name)541 windrv_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 
578 extern void	x86_64_wrap(void);
579 extern void	x86_64_wrap_call(void);
580 extern void	x86_64_wrap_end(void);
581 
582 int
windrv_wrap(func,wrap,argcnt,ftype)583 windrv_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 
621 struct 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 
630 struct gdt {
631 	uint16_t		limit;
632 	void			*base;
633 } __attribute__((__packed__));
634 
635 extern uint16_t	x86_getfs(void);
636 extern void x86_setfs(uint16_t);
637 extern void *x86_gettid(void);
638 extern void x86_critical_enter(void);
639 extern void x86_critical_exit(void);
640 extern void x86_getldt(struct gdt *, uint16_t *);
641 extern 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 
690 void
ctxsw_utow(void)691 ctxsw_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 
726 void
ctxsw_wtou(void)727 ctxsw_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 
745 static int	windrv_wrap_stdcall(funcptr, funcptr *, int);
746 static int	windrv_wrap_fastcall(funcptr, funcptr *, int);
747 static int	windrv_wrap_regparm(funcptr, funcptr *);
748 
749 extern void	x86_fastcall_wrap(void);
750 extern void	x86_fastcall_wrap_call(void);
751 extern void	x86_fastcall_wrap_arg(void);
752 extern void	x86_fastcall_wrap_end(void);
753 
754 static int
windrv_wrap_fastcall(func,wrap,argcnt)755 windrv_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 
797 extern void	x86_stdcall_wrap(void);
798 extern void	x86_stdcall_wrap_call(void);
799 extern void	x86_stdcall_wrap_arg(void);
800 extern void	x86_stdcall_wrap_end(void);
801 
802 static int
windrv_wrap_stdcall(func,wrap,argcnt)803 windrv_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 
841 extern void	x86_regparm_wrap(void);
842 extern void	x86_regparm_wrap_call(void);
843 extern void	x86_regparm_wrap_end(void);
844 
845 static int
windrv_wrap_regparm(func,wrap)846 windrv_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 
878 int
windrv_wrap(func,wrap,argcnt,ftype)879 windrv_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 
901 static void
x86_oldldt(dummy)902 x86_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 
931 static void
x86_newldt(dummy)932 x86_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 
981 int
windrv_unwrap(func)982 windrv_unwrap(func)
983 	funcptr			func;
984 {
985 	free(func, M_DEVBUF);
986 
987 	return (0);
988 }
989