initial commit
[freebsd-arm:freebsd-arm.git] / compat / ndis / kern_windrv.c
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
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
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 *
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 *
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
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 /*
315  * Loader routine for actual Windows driver modules, ultimately
316  * calls the driver's DriverEntry() routine.
317  */
318
319 int
320 windrv_load(mod, img, len, bustype, devlist, regvals)
321         module_t                mod;
322         vm_offset_t             img;
323         int                     len;
324         interface_type          bustype;
325         void                    *devlist;
326         ndis_cfg                *regvals;
327 {
328         image_import_descriptor imp_desc;
329         image_optional_header   opt_hdr;
330         driver_entry            entry;
331         struct drvdb_ent        *new;
332         struct driver_object    *drv;
333         int                     status;
334         uint32_t                *ptr;
335         ansi_string             as;
336
337         /*
338          * First step: try to relocate and dynalink the executable
339          * driver image.
340          */
341
342         ptr = (uint32_t *)(img + 8);
343         if (*ptr == WINDRV_LOADED)
344                 goto skipreloc;
345
346         /* Perform text relocation */
347         if (pe_relocate(img))
348                 return (ENOEXEC);
349
350         /* Dynamically link the NDIS.SYS routines -- required. */
351         if (pe_patch_imports(img, "NDIS", ndis_functbl))
352                 return (ENOEXEC);
353
354         /* Dynamically link the HAL.dll routines -- optional. */
355         if (pe_get_import_descriptor(img, &imp_desc, "HAL") == 0) {
356                 if (pe_patch_imports(img, "HAL", hal_functbl))
357                         return (ENOEXEC);
358         }
359
360         /* Dynamically link ntoskrnl.exe -- optional. */
361         if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) {
362                 if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl))
363                         return (ENOEXEC);
364         }
365
366         /* Dynamically link USBD.SYS -- optional */
367         if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) {
368                 if (pe_patch_imports(img, "USBD", usbd_functbl))
369                         return (ENOEXEC);
370         }
371
372         *ptr = WINDRV_LOADED;
373
374 skipreloc:
375
376         /* Next step: find the driver entry point. */
377
378         pe_get_optional_header(img, &opt_hdr);
379         entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
380
381         /* Next step: allocate and store a driver object. */
382
383         new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
384         if (new == NULL)
385                 return (ENOMEM);
386
387         drv = malloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO);
388         if (drv == NULL) {
389                 free (new, M_DEVBUF);
390                 return (ENOMEM);
391         }
392
393         /* Allocate a driver extension structure too. */
394
395         drv->dro_driverext = malloc(sizeof(driver_extension),
396             M_DEVBUF, M_NOWAIT|M_ZERO);
397
398         if (drv->dro_driverext == NULL) {
399                 free(new, M_DEVBUF);
400                 free(drv, M_DEVBUF);
401                 return (ENOMEM);
402         }
403
404         InitializeListHead((&drv->dro_driverext->dre_usrext));
405
406         drv->dro_driverstart = (void *)img;
407         drv->dro_driversize = len;
408
409         RtlInitAnsiString(&as, DUMMY_REGISTRY_PATH);
410         if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) {
411                 free(new, M_DEVBUF);
412                 free(drv, M_DEVBUF);
413                 return (ENOMEM);
414         }
415
416         new->windrv_object = drv;
417         new->windrv_regvals = regvals;
418         new->windrv_devlist = devlist;
419         new->windrv_bustype = bustype;
420
421         /* Now call the DriverEntry() function. */
422
423         status = MSCALL2(entry, drv, &drv->dro_drivername);
424
425         if (status != STATUS_SUCCESS) {
426                 RtlFreeUnicodeString(&drv->dro_drivername);
427                 free(drv, M_DEVBUF);
428                 free(new, M_DEVBUF);
429                 return (ENODEV);
430         }
431
432         mtx_lock(&drvdb_mtx); 
433         STAILQ_INSERT_HEAD(&drvdb_head, new, link);
434         mtx_unlock(&drvdb_mtx); 
435
436         return (0);
437 }
438
439 /*
440  * Make a new Physical Device Object for a device that was
441  * detected/plugged in. For us, the PDO is just a way to
442  * get at the device_t.
443  */
444
445 int
446 windrv_create_pdo(drv, bsddev)
447         driver_object           *drv;
448         device_t                bsddev;
449 {
450         device_object           *dev;
451
452         /*
453          * This is a new physical device object, which technically
454          * is the "top of the stack." Consequently, we don't do
455          * an IoAttachDeviceToDeviceStack() here.
456          */
457
458         mtx_lock(&drvdb_mtx);
459         IoCreateDevice(drv, 0, NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev);
460         mtx_unlock(&drvdb_mtx);
461
462         /* Stash pointer to our BSD device handle. */
463
464         dev->do_devext = bsddev;
465
466         return (STATUS_SUCCESS);
467 }
468
469 void
470 windrv_destroy_pdo(drv, bsddev)
471         driver_object           *drv;
472         device_t                bsddev;
473 {
474         device_object           *pdo;
475
476         pdo = windrv_find_pdo(drv, bsddev);
477
478         /* Remove reference to device_t */
479
480         pdo->do_devext = NULL;
481
482         mtx_lock(&drvdb_mtx);
483         IoDeleteDevice(pdo);
484         mtx_unlock(&drvdb_mtx);
485 }
486
487 /*
488  * Given a device_t, find the corresponding PDO in a driver's
489  * device list.
490  */
491
492 device_object *
493 windrv_find_pdo(drv, bsddev)
494         driver_object           *drv;
495         device_t                bsddev;
496 {
497         device_object           *pdo;
498
499         mtx_lock(&drvdb_mtx);
500         pdo = drv->dro_devobj;
501         while (pdo != NULL) {
502                 if (pdo->do_devext == bsddev) {
503                         mtx_unlock(&drvdb_mtx);
504                         return (pdo);
505                 }
506                 pdo = pdo->do_nextdev;
507         }
508         mtx_unlock(&drvdb_mtx);
509
510         return (NULL);
511 }
512
513 /*
514  * Add an internally emulated driver to the database. We need this
515  * to set up an emulated bus driver so that it can receive IRPs.
516  */
517
518 int
519 windrv_bus_attach(drv, name)
520         driver_object           *drv;
521         char                    *name;
522 {
523         struct drvdb_ent        *new;
524         ansi_string             as;
525
526         new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
527         if (new == NULL)
528                 return (ENOMEM);
529
530         RtlInitAnsiString(&as, name);
531         if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE))
532         {
533                 free(new, M_DEVBUF);
534                 return (ENOMEM);
535         }
536
537         /*
538          * Set up a fake image pointer to avoid false matches
539          * in windrv_lookup().
540          */
541         drv->dro_driverstart = (void *)0xFFFFFFFF;
542
543         new->windrv_object = drv;
544         new->windrv_devlist = NULL;
545         new->windrv_regvals = NULL;
546
547         mtx_lock(&drvdb_mtx);
548         STAILQ_INSERT_HEAD(&drvdb_head, new, link);
549         mtx_unlock(&drvdb_mtx);
550
551         return (0);
552 }
553
554 #ifdef __amd64__
555
556 extern void     x86_64_wrap(void);
557 extern void     x86_64_wrap_call(void);
558 extern void     x86_64_wrap_end(void);
559
560 int
561 windrv_wrap(func, wrap, argcnt, ftype)
562         funcptr                 func;
563         funcptr                 *wrap;
564         int                     argcnt;
565         int                     ftype;
566 {
567         funcptr                 p;
568         vm_offset_t             *calladdr;
569         vm_offset_t             wrapstart, wrapend, wrapcall;
570
571         wrapstart = (vm_offset_t)&x86_64_wrap;
572         wrapend = (vm_offset_t)&x86_64_wrap_end;
573         wrapcall = (vm_offset_t)&x86_64_wrap_call;
574
575         /* Allocate a new wrapper instance. */
576
577         p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
578         if (p == NULL)
579                 return (ENOMEM);
580
581         /* Copy over the code. */
582
583         bcopy((char *)wrapstart, p, (wrapend - wrapstart));
584
585         /* Insert the function address into the new wrapper instance. */
586
587         calladdr = (uint64_t *)((char *)p + (wrapcall - wrapstart) + 2);
588         *calladdr = (vm_offset_t)func;
589
590         *wrap = p;
591
592         return (0);
593 }
594 #endif /* __amd64__ */
595
596
597 #ifdef __i386__
598
599 struct x86desc {
600         uint16_t                x_lolimit;
601         uint16_t                x_base0;
602         uint8_t                 x_base1;
603         uint8_t                 x_flags;
604         uint8_t                 x_hilimit;
605         uint8_t                 x_base2;
606 };
607
608 struct gdt {
609         uint16_t                limit;
610         void                    *base;
611 } __attribute__((__packed__));
612
613 extern uint16_t x86_getfs(void);
614 extern void x86_setfs(uint16_t);
615 extern void *x86_gettid(void);
616 extern void x86_critical_enter(void);
617 extern void x86_critical_exit(void);
618 extern void x86_getldt(struct gdt *, uint16_t *);
619 extern void x86_setldt(struct gdt *, uint16_t);
620
621 #define SEL_LDT 4               /* local descriptor table */
622 #define SEL_TO_FS(x)            (((x) << 3))
623
624 /*
625  * FreeBSD 6.0 and later has a special GDT segment reserved
626  * specifically for us, so if GNDIS_SEL is defined, use that.
627  * If not, use GTGATE_SEL, which is uninitialized and infrequently
628  * used.
629  */
630
631 #ifdef GNDIS_SEL
632 #define FREEBSD_EMPTYSEL        GNDIS_SEL
633 #else
634 #define FREEBSD_EMPTYSEL        GTGATE_SEL      /* slot 7 */
635 #endif
636
637 /*
638  * The meanings of various bits in a descriptor vary a little
639  * depending on whether the descriptor will be used as a
640  * code, data or system descriptor. (And that in turn depends
641  * on which segment register selects the descriptor.)
642  * We're only trying to create a data segment, so the definitions
643  * below are the ones that apply to a data descriptor.
644  */
645
646 #define SEGFLAGLO_PRESENT       0x80    /* segment is present */
647 #define SEGFLAGLO_PRIVLVL       0x60    /* privlevel needed for this seg */
648 #define SEGFLAGLO_CD            0x10    /* 1 = code/data, 0 = system */
649 #define SEGFLAGLO_MBZ           0x08    /* must be zero */
650 #define SEGFLAGLO_EXPANDDOWN    0x04    /* limit expands down */
651 #define SEGFLAGLO_WRITEABLE     0x02    /* segment is writeable */
652 #define SEGGLAGLO_ACCESSED      0x01    /* segment has been accessed */
653
654 #define SEGFLAGHI_GRAN          0x80    /* granularity, 1 = byte, 0 = page */
655 #define SEGFLAGHI_BIG           0x40    /* 1 = 32 bit stack, 0 = 16 bit */
656
657 /*
658  * Context switch from UNIX to Windows. Save the existing value
659  * of %fs for this processor, then change it to point to our
660  * fake TID. Note that it is also possible to pin ourselves
661  * to our current CPU, though I'm not sure this is really
662  * necessary. It depends on whether or not an interrupt might
663  * preempt us while Windows code is running and we wind up
664  * scheduled onto another CPU as a result. So far, it doesn't
665  * seem like this is what happens.
666  */
667
668 void
669 ctxsw_utow(void)
670 {
671         struct tid              *t;
672
673         t = &my_tids[curthread->td_oncpu];
674
675         /*
676          * Ugly hack. During system bootstrap (cold == 1), only CPU 0
677          * is running. So if we were loaded at bootstrap, only CPU 0
678          * will have our special GDT entry. This is a problem for SMP
679          * systems, so to deal with this, we check here to make sure
680          * the TID for this processor has been initialized, and if it
681          * hasn't, we need to do it right now or else things will
682          * explode.
683          */
684
685         if (t->tid_self != t)
686                 x86_newldt(NULL);
687
688         x86_critical_enter();
689         t->tid_oldfs = x86_getfs();
690         t->tid_cpu = curthread->td_oncpu;
691         sched_pin();
692         x86_setfs(SEL_TO_FS(t->tid_selector));
693         x86_critical_exit();
694
695         /* Now entering Windows land, population: you. */
696 }
697
698 /*
699  * Context switch from Windows back to UNIX. Restore %fs to
700  * its previous value. This always occurs after a call to
701  * ctxsw_utow().
702  */
703
704 void
705 ctxsw_wtou(void)
706 {
707         struct tid              *t;
708
709         x86_critical_enter();
710         t = x86_gettid();
711         x86_setfs(t->tid_oldfs);
712         sched_unpin();
713         x86_critical_exit();
714
715         /* Welcome back to UNIX land, we missed you. */
716
717 #ifdef EXTRA_SANITY
718         if (t->tid_cpu != curthread->td_oncpu)
719                 panic("ctxsw GOT MOVED TO OTHER CPU!");
720 #endif
721 }
722
723 static int      windrv_wrap_stdcall(funcptr, funcptr *, int);
724 static int      windrv_wrap_fastcall(funcptr, funcptr *, int);
725 static int      windrv_wrap_regparm(funcptr, funcptr *);
726
727 extern void     x86_fastcall_wrap(void);
728 extern void     x86_fastcall_wrap_call(void);
729 extern void     x86_fastcall_wrap_arg(void);
730 extern void     x86_fastcall_wrap_end(void);
731
732 static int
733 windrv_wrap_fastcall(func, wrap, argcnt)
734         funcptr                 func;
735         funcptr                 *wrap;
736         int8_t                  argcnt;
737 {
738         funcptr                 p;
739         vm_offset_t             *calladdr;
740         uint8_t                 *argaddr;
741         vm_offset_t             wrapstart, wrapend, wrapcall, wraparg;
742
743         wrapstart = (vm_offset_t)&x86_fastcall_wrap;
744         wrapend = (vm_offset_t)&x86_fastcall_wrap_end;
745         wrapcall = (vm_offset_t)&x86_fastcall_wrap_call;
746         wraparg = (vm_offset_t)&x86_fastcall_wrap_arg;
747
748         /* Allocate a new wrapper instance. */
749
750         p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
751         if (p == NULL)
752                 return (ENOMEM);
753
754         /* Copy over the code. */
755
756         bcopy((char *)wrapstart, p, (wrapend - wrapstart));
757
758         /* Insert the function address into the new wrapper instance. */
759
760         calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
761         *calladdr = (vm_offset_t)func;
762
763         argcnt -= 2;
764         if (argcnt < 1)
765                 argcnt = 0;
766
767         argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1));
768         *argaddr = argcnt * sizeof(uint32_t);
769
770         *wrap = p;
771
772         return (0);
773 }
774
775 extern void     x86_stdcall_wrap(void);
776 extern void     x86_stdcall_wrap_call(void);
777 extern void     x86_stdcall_wrap_arg(void);
778 extern void     x86_stdcall_wrap_end(void);
779
780 static int
781 windrv_wrap_stdcall(func, wrap, argcnt)
782         funcptr                 func;
783         funcptr                 *wrap;
784         uint8_t                 argcnt;
785 {
786         funcptr                 p;
787         vm_offset_t             *calladdr;
788         uint8_t                 *argaddr;
789         vm_offset_t             wrapstart, wrapend, wrapcall, wraparg;
790
791         wrapstart = (vm_offset_t)&x86_stdcall_wrap;
792         wrapend = (vm_offset_t)&x86_stdcall_wrap_end;
793         wrapcall = (vm_offset_t)&x86_stdcall_wrap_call;
794         wraparg = (vm_offset_t)&x86_stdcall_wrap_arg;
795
796         /* Allocate a new wrapper instance. */
797
798         p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
799         if (p == NULL)
800                 return (ENOMEM);
801
802         /* Copy over the code. */
803
804         bcopy((char *)wrapstart, p, (wrapend - wrapstart));
805
806         /* Insert the function address into the new wrapper instance. */
807
808         calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
809         *calladdr = (vm_offset_t)func;
810
811         argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1));
812         *argaddr = argcnt * sizeof(uint32_t);
813
814         *wrap = p;
815
816         return (0);
817 }
818
819 extern void     x86_regparm_wrap(void);
820 extern void     x86_regparm_wrap_call(void);
821 extern void     x86_regparm_wrap_end(void);
822
823 static int
824 windrv_wrap_regparm(func, wrap)
825         funcptr                 func;
826         funcptr                 *wrap;
827 {
828         funcptr                 p;
829         vm_offset_t             *calladdr;
830         vm_offset_t             wrapstart, wrapend, wrapcall;
831
832         wrapstart = (vm_offset_t)&x86_regparm_wrap;
833         wrapend = (vm_offset_t)&x86_regparm_wrap_end;
834         wrapcall = (vm_offset_t)&x86_regparm_wrap_call;
835
836         /* Allocate a new wrapper instance. */
837
838         p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
839         if (p == NULL)
840                 return (ENOMEM);
841
842         /* Copy over the code. */
843
844         bcopy(x86_regparm_wrap, p, (wrapend - wrapstart));
845
846         /* Insert the function address into the new wrapper instance. */
847
848         calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
849         *calladdr = (vm_offset_t)func;
850
851         *wrap = p;
852
853         return (0);
854 }
855
856 int
857 windrv_wrap(func, wrap, argcnt, ftype)
858         funcptr                 func;
859         funcptr                 *wrap;
860         int                     argcnt;
861         int                     ftype;
862 {
863         switch(ftype) {
864         case WINDRV_WRAP_FASTCALL:
865                 return (windrv_wrap_fastcall(func, wrap, argcnt));
866         case WINDRV_WRAP_STDCALL:
867                 return (windrv_wrap_stdcall(func, wrap, argcnt));
868         case WINDRV_WRAP_REGPARM:
869                 return (windrv_wrap_regparm(func, wrap));
870         case WINDRV_WRAP_CDECL:
871                 return (windrv_wrap_stdcall(func, wrap, 0));
872         default:
873                 break;
874         }
875
876         return (EINVAL);
877 }
878
879 static void
880 x86_oldldt(dummy)
881         void                    *dummy;
882 {
883         struct x86desc          *gdt;
884         struct gdt              gtable;
885         uint16_t                ltable;
886
887         mtx_lock_spin(&dt_lock);
888
889         /* Grab location of existing GDT. */
890
891         x86_getldt(&gtable, &ltable);
892
893         /* Find the slot we updated. */
894
895         gdt = gtable.base;
896         gdt += FREEBSD_EMPTYSEL;
897
898         /* Empty it out. */
899
900         bzero((char *)gdt, sizeof(struct x86desc));
901
902         /* Restore GDT. */
903
904         x86_setldt(&gtable, ltable);
905
906         mtx_unlock_spin(&dt_lock);
907 }
908
909 static void
910 x86_newldt(dummy)
911         void                    *dummy;
912 {
913         struct gdt              gtable;
914         uint16_t                ltable;
915         struct x86desc          *l;
916         struct thread           *t;
917
918         t = curthread;
919
920         mtx_lock_spin(&dt_lock);
921
922         /* Grab location of existing GDT. */
923
924         x86_getldt(&gtable, &ltable);
925
926         /* Get pointer to the GDT table. */
927
928         l = gtable.base;
929
930         /* Get pointer to empty slot */
931
932         l += FREEBSD_EMPTYSEL;
933
934         /* Initialize TID for this CPU. */
935
936         my_tids[t->td_oncpu].tid_selector = FREEBSD_EMPTYSEL;
937         my_tids[t->td_oncpu].tid_self = &my_tids[t->td_oncpu];
938
939         /* Set up new GDT entry. */
940
941         l->x_lolimit = sizeof(struct tid);
942         l->x_hilimit = SEGFLAGHI_GRAN|SEGFLAGHI_BIG;
943         l->x_base0 = (vm_offset_t)(&my_tids[t->td_oncpu]) & 0xFFFF;
944         l->x_base1 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 16) & 0xFF;
945         l->x_base2 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 24) & 0xFF;
946         l->x_flags = SEGFLAGLO_PRESENT|SEGFLAGLO_CD|SEGFLAGLO_WRITEABLE;
947
948         /* Update the GDT. */
949
950         x86_setldt(&gtable, ltable);
951
952         mtx_unlock_spin(&dt_lock);
953
954         /* Whew. */
955 }
956
957 #endif /* __i386__ */
958
959 int
960 windrv_unwrap(func)
961         funcptr                 func;
962 {
963         free(func, M_DEVBUF);
964
965         return (0);
966 }