i386 removal, part 24/x: Remove i386 specific parts from NDIS.
[dragonfly.git] / sys / emulation / 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  * $FreeBSD: src/sys/compat/ndis/kern_windrv.c,v 1.21 2010/11/22 20:46:38 bschmidt Exp $
33  */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/unistd.h>
38 #include <sys/types.h>
39
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/lock.h>
43 #include <sys/mutex2.h>
44 #include <sys/module.h>
45 #include <sys/conf.h>
46 #include <sys/mbuf.h>
47 #include <sys/bus.h>
48 #include <sys/proc.h>
49 #include <sys/sched.h>
50
51 #include <sys/queue.h>
52
53 #include <bus/u4b/usb.h>
54 #include <bus/u4b/usbdi.h>
55
56 #include <emulation/ndis/pe_var.h>
57 #include <emulation/ndis/cfg_var.h>
58 #include <emulation/ndis/resource_var.h>
59 #include <emulation/ndis/ntoskrnl_var.h>
60 #include <emulation/ndis/ndis_var.h>
61 #include <emulation/ndis/hal_var.h>
62 #include <emulation/ndis/u4bd_var.h>
63
64 static struct lock drvdb_lock;
65 static STAILQ_HEAD(drvdb, drvdb_ent) drvdb_head;
66
67 static driver_object    fake_pci_driver; /* serves both PCI and cardbus */
68 static driver_object    fake_pccard_driver;
69
70 #define DUMMY_REGISTRY_PATH "\\\\some\\bogus\\path"
71
72 int
73 windrv_libinit(void)
74 {
75         STAILQ_INIT(&drvdb_head);
76         lockinit(&drvdb_lock, "Windows driver DB lock", 0, LK_CANRECURSE);
77
78         /*
79          * PCI and pccard devices don't need to use IRPs to
80          * interact with their bus drivers (usually), so our
81          * emulated PCI and pccard drivers are just stubs.
82          * USB devices, on the other hand, do all their I/O
83          * by exchanging IRPs with the USB bus driver, so
84          * for that we need to provide emulator dispatcher
85          * routines, which are in a separate module.
86          */
87
88         windrv_bus_attach(&fake_pci_driver, "PCI Bus");
89         windrv_bus_attach(&fake_pccard_driver, "PCCARD Bus");
90
91         return (0);
92 }
93
94 int
95 windrv_libfini(void)
96 {
97         struct drvdb_ent        *d;
98
99         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
100         while(STAILQ_FIRST(&drvdb_head) != NULL) {
101                 d = STAILQ_FIRST(&drvdb_head);
102                 STAILQ_REMOVE_HEAD(&drvdb_head, link);
103                 kfree(d, M_DEVBUF);
104         }
105         lockmgr(&drvdb_lock, LK_RELEASE);
106
107         RtlFreeUnicodeString(&fake_pci_driver.dro_drivername);
108         RtlFreeUnicodeString(&fake_pccard_driver.dro_drivername);
109
110         lockuninit(&drvdb_lock);
111
112         return (0);
113 }
114
115 /*
116  * Given the address of a driver image, find its corresponding
117  * driver_object.
118  */
119
120 driver_object *
121 windrv_lookup(vm_offset_t img, char *name)
122 {
123         struct drvdb_ent        *d;
124         unicode_string          us;
125         ansi_string             as;
126
127         bzero((char *)&us, sizeof(us));
128
129         /* Damn unicode. */
130
131         if (name != NULL) {
132                 RtlInitAnsiString(&as, name);
133                 if (RtlAnsiStringToUnicodeString(&us, &as, TRUE))
134                         return (NULL);
135         }
136
137         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
138         STAILQ_FOREACH(d, &drvdb_head, link) {
139                 if (d->windrv_object->dro_driverstart == (void *)img ||
140                     (bcmp((char *)d->windrv_object->dro_drivername.us_buf,
141                     (char *)us.us_buf, us.us_len) == 0 && us.us_len)) {
142                         lockmgr(&drvdb_lock, LK_RELEASE);
143                         if (name != NULL)
144                                 ExFreePool(us.us_buf);
145                         return (d->windrv_object);
146                 }
147         }
148         lockmgr(&drvdb_lock, LK_RELEASE);
149
150         if (name != NULL)
151                 RtlFreeUnicodeString(&us);
152
153         return (NULL);
154 }
155
156 struct drvdb_ent *
157 windrv_match(matchfuncptr matchfunc, void *ctx)
158 {
159         struct drvdb_ent        *d;
160         int                     match;
161
162         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
163         STAILQ_FOREACH(d, &drvdb_head, link) {
164                 if (d->windrv_devlist == NULL)
165                         continue;
166                 match = matchfunc(d->windrv_bustype, d->windrv_devlist, ctx);
167                 if (match == TRUE) {
168                         lockmgr(&drvdb_lock, LK_RELEASE);
169                         return (d);
170                 }
171         }
172         lockmgr(&drvdb_lock, LK_RELEASE);
173
174         return (NULL);
175 }
176
177 /*
178  * Remove a driver_object from our datatabase and destroy it. Throw
179  * away any custom driver extension info that may have been added.
180  */
181
182 int
183 windrv_unload(module_t mod, vm_offset_t img, int len)
184 {
185         struct drvdb_ent        *db, *r = NULL;
186         driver_object           *drv;
187         device_object           *d, *pdo;
188         device_t                dev;
189         list_entry              *e;
190
191         drv = windrv_lookup(img, NULL);
192
193         /*
194          * When we unload a driver image, we need to force a
195          * detach of any devices that might be using it. We
196          * need the PDOs of all attached devices for this.
197          * Getting at them is a little hard. We basically
198          * have to walk the device lists of all our bus
199          * drivers.
200          */
201
202         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
203         STAILQ_FOREACH(db, &drvdb_head, link) {
204                 /*
205                  * Fake bus drivers have no devlist info.
206                  * If this driver has devlist info, it's
207                  * a loaded Windows driver and has no PDOs,
208                  * so skip it.
209                  */
210                 if (db->windrv_devlist != NULL)
211                         continue;
212                 pdo = db->windrv_object->dro_devobj;
213                 while (pdo != NULL) {
214                         d = pdo->do_attacheddev;
215                         if (d->do_drvobj != drv) {
216                                 pdo = pdo->do_nextdev;
217                                 continue;
218                         }
219                         dev = pdo->do_devext;
220                         pdo = pdo->do_nextdev;
221                         lockmgr(&drvdb_lock, LK_RELEASE);
222                         device_detach(dev);
223                         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
224                 }
225         }
226
227         STAILQ_FOREACH(db, &drvdb_head, link) {
228                 if (db->windrv_object->dro_driverstart == (void *)img) {
229                         r = db;
230                         STAILQ_REMOVE(&drvdb_head, db, drvdb_ent, link);
231                         break;
232                 }
233         }
234         lockmgr(&drvdb_lock, LK_RELEASE);
235
236         if (r == NULL)
237                 return (ENOENT);
238
239         if (drv == NULL)
240                 return (ENOENT);
241
242         /*
243          * Destroy any custom extensions that may have been added.
244          */
245         drv = r->windrv_object;
246         while (!IsListEmpty(&drv->dro_driverext->dre_usrext)) {
247                 e = RemoveHeadList(&drv->dro_driverext->dre_usrext);
248                 ExFreePool(e);
249         }
250
251         /* Free the driver extension */
252         kfree(drv->dro_driverext, M_DEVBUF);
253
254         /* Free the driver name */
255         RtlFreeUnicodeString(&drv->dro_drivername);
256
257         /* Free driver object */
258         kfree(drv, M_DEVBUF);
259
260         /* Free our DB handle */
261         kfree(r, M_DEVBUF);
262
263         return (0);
264 }
265
266 #define WINDRV_LOADED           htonl(0x42534F44)
267
268 #ifdef __x86_64__
269 static void
270 patch_user_shared_data_address(vm_offset_t img, size_t len)
271 {
272         unsigned long i, n, max_addr, *addr;
273
274         n = len - sizeof(unsigned long);
275         max_addr = KI_USER_SHARED_DATA + sizeof(kuser_shared_data);
276         for (i = 0; i < n; i++) {
277                 addr = (unsigned long *)(img + i);
278                 if (*addr >= KI_USER_SHARED_DATA && *addr < max_addr) {
279                         *addr -= KI_USER_SHARED_DATA;
280                         *addr += (unsigned long)&kuser_shared_data;
281                 }
282         }
283 }
284 #endif
285
286 /*
287  * Loader routine for actual Windows driver modules, ultimately
288  * calls the driver's DriverEntry() routine.
289  */
290
291 int
292 windrv_load(module_t mod, vm_offset_t img, int len, interface_type bustype,
293     void *devlist, ndis_cfg *regvals)
294 {
295         image_import_descriptor imp_desc;
296         image_optional_header   opt_hdr;
297         driver_entry            entry;
298         struct drvdb_ent        *new;
299         struct driver_object    *drv;
300         int                     status;
301         uint32_t                *ptr;
302         ansi_string             as;
303
304         /*
305          * First step: try to relocate and dynalink the executable
306          * driver image.
307          */
308
309         ptr = (uint32_t *)(img + 8);
310         if (*ptr == WINDRV_LOADED)
311                 goto skipreloc;
312
313         /* Perform text relocation */
314         if (pe_relocate(img))
315                 return (ENOEXEC);
316
317         /* Dynamically link the NDIS.SYS routines -- required. */
318         if (pe_patch_imports(img, "NDIS", ndis_functbl))
319                 return (ENOEXEC);
320
321         /* Dynamically link the HAL.dll routines -- optional. */
322         if (pe_get_import_descriptor(img, &imp_desc, "HAL") == 0) {
323                 if (pe_patch_imports(img, "HAL", hal_functbl))
324                         return (ENOEXEC);
325         }
326
327         /* Dynamically link ntoskrnl.exe -- optional. */
328         if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) {
329                 if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl))
330                         return (ENOEXEC);
331         }
332
333 #ifdef __x86_64__
334         patch_user_shared_data_address(img, len);
335 #endif
336
337         /* Dynamically link USBD.SYS -- optional */
338         if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) {
339                 if (pe_patch_imports(img, "USBD", usbd_functbl))
340                         return (ENOEXEC);
341         }
342
343         *ptr = WINDRV_LOADED;
344
345 skipreloc:
346
347         /* Next step: find the driver entry point. */
348
349         pe_get_optional_header(img, &opt_hdr);
350         entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
351
352         /* Next step: allocate and store a driver object. */
353
354         new = kmalloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
355         if (new == NULL)
356                 return (ENOMEM);
357
358         drv = kmalloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO);
359         if (drv == NULL) {
360                 kfree (new, M_DEVBUF);
361                 return (ENOMEM);
362         }
363
364         /* Allocate a driver extension structure too. */
365
366         drv->dro_driverext = kmalloc(sizeof(driver_extension),
367             M_DEVBUF, M_NOWAIT|M_ZERO);
368
369         if (drv->dro_driverext == NULL) {
370                 kfree(new, M_DEVBUF);
371                 kfree(drv, M_DEVBUF);
372                 return (ENOMEM);
373         }
374
375         InitializeListHead((&drv->dro_driverext->dre_usrext));
376
377         drv->dro_driverstart = (void *)img;
378         drv->dro_driversize = len;
379
380         RtlInitAnsiString(&as, DUMMY_REGISTRY_PATH);
381         if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) {
382                 kfree(new, M_DEVBUF);
383                 kfree(drv, M_DEVBUF);
384                 return (ENOMEM);
385         }
386
387         new->windrv_object = drv;
388         new->windrv_regvals = regvals;
389         new->windrv_devlist = devlist;
390         new->windrv_bustype = bustype;
391
392         /* Now call the DriverEntry() function. */
393
394         status = MSCALL2(entry, drv, &drv->dro_drivername);
395
396         if (status != STATUS_SUCCESS) {
397                 RtlFreeUnicodeString(&drv->dro_drivername);
398                 kfree(drv, M_DEVBUF);
399                 kfree(new, M_DEVBUF);
400                 return (ENODEV);
401         }
402
403         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
404         STAILQ_INSERT_HEAD(&drvdb_head, new, link);
405         lockmgr(&drvdb_lock, LK_RELEASE);
406
407         return (0);
408 }
409
410 /*
411  * Make a new Physical Device Object for a device that was
412  * detected/plugged in. For us, the PDO is just a way to
413  * get at the device_t.
414  */
415
416 int
417 windrv_create_pdo(driver_object *drv, device_t bsddev)
418 {
419         device_object           *dev;
420
421         /*
422          * This is a new physical device object, which technically
423          * is the "top of the stack." Consequently, we don't do
424          * an IoAttachDeviceToDeviceStack() here.
425          */
426
427         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
428         IoCreateDevice(drv, 0, NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev);
429         lockmgr(&drvdb_lock, LK_RELEASE);
430
431         /* Stash pointer to our BSD device handle. */
432
433         dev->do_devext = bsddev;
434
435         return (STATUS_SUCCESS);
436 }
437
438 void
439 windrv_destroy_pdo(driver_object *drv, device_t bsddev)
440 {
441         device_object           *pdo;
442
443         pdo = windrv_find_pdo(drv, bsddev);
444
445         /* Remove reference to device_t */
446
447         pdo->do_devext = NULL;
448
449         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
450         IoDeleteDevice(pdo);
451         lockmgr(&drvdb_lock, LK_RELEASE);
452 }
453
454 /*
455  * Given a device_t, find the corresponding PDO in a driver's
456  * device list.
457  */
458
459 device_object *
460 windrv_find_pdo(driver_object *drv, device_t bsddev)
461 {
462         device_object           *pdo;
463
464         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
465         pdo = drv->dro_devobj;
466         while (pdo != NULL) {
467                 if (pdo->do_devext == bsddev) {
468                         lockmgr(&drvdb_lock, LK_RELEASE);
469                         return (pdo);
470                 }
471                 pdo = pdo->do_nextdev;
472         }
473         lockmgr(&drvdb_lock, LK_RELEASE);
474
475         return (NULL);
476 }
477
478 /*
479  * Add an internally emulated driver to the database. We need this
480  * to set up an emulated bus driver so that it can receive IRPs.
481  */
482
483 int
484 windrv_bus_attach(driver_object *drv, char *name)
485 {
486         struct drvdb_ent        *new;
487         ansi_string             as;
488
489         new = kmalloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
490         if (new == NULL)
491                 return (ENOMEM);
492
493         RtlInitAnsiString(&as, name);
494         if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE))
495         {
496                 kfree(new, M_DEVBUF);
497                 return (ENOMEM);
498         }
499
500         /*
501          * Set up a fake image pointer to avoid false matches
502          * in windrv_lookup().
503          */
504         drv->dro_driverstart = (void *)0xFFFFFFFF;
505
506         new->windrv_object = drv;
507         new->windrv_devlist = NULL;
508         new->windrv_regvals = NULL;
509
510         lockmgr(&drvdb_lock, LK_EXCLUSIVE);
511         STAILQ_INSERT_HEAD(&drvdb_head, new, link);
512         lockmgr(&drvdb_lock, LK_RELEASE);
513
514         return (0);
515 }
516
517 #ifdef __x86_64__
518
519 extern void     x86_64_wrap(void);
520 extern void     x86_64_wrap_call(void);
521 extern void     x86_64_wrap_end(void);
522
523 int
524 windrv_wrap(funcptr func, funcptr *wrap, int argcnt, int ftype)
525 {
526         funcptr                 p;
527         vm_offset_t             *calladdr;
528         vm_offset_t             wrapstart, wrapend, wrapcall;
529
530         wrapstart = (vm_offset_t)&x86_64_wrap;
531         wrapend = (vm_offset_t)&x86_64_wrap_end;
532         wrapcall = (vm_offset_t)&x86_64_wrap_call;
533
534         /* Allocate a new wrapper instance. */
535
536         p = kmalloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
537         if (p == NULL)
538                 return (ENOMEM);
539
540         /* Copy over the code. */
541
542         bcopy((char *)wrapstart, p, (wrapend - wrapstart));
543
544         /* Insert the function address into the new wrapper instance. */
545
546         calladdr = (uint64_t *)((char *)p + (wrapcall - wrapstart) + 2);
547         *calladdr = (vm_offset_t)func;
548
549         *wrap = p;
550
551         return (0);
552 }
553 #endif /* __x86_64__ */
554
555
556 int
557 windrv_unwrap(funcptr func)
558 {
559         kfree(func, M_DEVBUF);
560
561         return (0);
562 }