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 #ifdef __FreeBSD__
35 __FBSDID("$FreeBSD: src/sys/compat/ndis/kern_windrv.c,v 1.3.2.2 2005/03/31 04:24:35 wpaul Exp $");
36 #endif
37 #ifdef __NetBSD__
38 __KERNEL_RCSID(0, "$NetBSD: kern_windrv.c,v 1.8 2009/03/18 17:06:48 cegger Exp $");
39 #endif
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/unistd.h>
44 #include <sys/types.h>
45
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #include <sys/lock.h>
49 #ifdef __FreeBSD__
50 #include <sys/mutex.h>
51 #include <sys/module.h>
52 #endif /* __FreeBSD__ */
53 #include <sys/conf.h>
54 #include <sys/mbuf.h>
55 #ifdef __FreeBSD__
56 #include <sys/bus.h>
57 #endif
58
59 #include <sys/queue.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 struct windrv_type {
70 uint16_t windrv_vid; /* for PCI or USB */
71 uint16_t windrv_did; /* for PCI or USB */
72 uint32_t windrv_subsys; /* for PCI */
73 char *windrv_vname; /* for pccard */
74 char *windrv_dname; /* for pccard */
75 char *windrv_name; /* for pccard, PCI or USB */
76 };
77
78 struct drvdb_ent {
79 driver_object *windrv_object;
80 struct windrv_type *windrv_devlist;
81 ndis_cfg *windrv_regvals;
82 STAILQ_ENTRY(drvdb_ent) link;
83 };
84
85 struct mtx drvdb_mtx;
86 static STAILQ_HEAD(drvdb, drvdb_ent) drvdb_head;
87
88 static driver_object fake_pci_driver; /* serves both PCI and cardbus */
89 static driver_object fake_pccard_driver;
90
91
92 #define DUMMY_REGISTRY_PATH "\\\\some\\bogus\\path"
93
94 int
windrv_libinit(void)95 windrv_libinit(void)
96 {
97 STAILQ_INIT(&drvdb_head);
98 mtx_init(&drvdb_mtx, "Windows driver DB lock",
99 "Windows internal lock", MTX_DEF);
100
101 /*
102 * PCI and pccard devices don't need to use IRPs to
103 * interact with their bus drivers (usually), so our
104 * emulated PCI and pccard drivers are just stubs.
105 * USB devices, on the other hand, do all their I/O
106 * by exchanging IRPs with the USB bus driver, so
107 * for that we need to provide emulator dispatcher
108 * routines, which are in a separate module.
109 */
110
111 windrv_bus_attach(&fake_pci_driver, "PCI Bus");
112 windrv_bus_attach(&fake_pccard_driver, "PCCARD Bus");
113
114 return(0);
115 }
116
117 int
windrv_libfini(void)118 windrv_libfini(void)
119 {
120 struct drvdb_ent *d;
121
122 mtx_lock(&drvdb_mtx);
123 while(STAILQ_FIRST(&drvdb_head) != NULL) {
124 d = STAILQ_FIRST(&drvdb_head);
125 STAILQ_REMOVE_HEAD(&drvdb_head, link);
126 free(d, M_DEVBUF);
127 }
128 mtx_unlock(&drvdb_mtx);
129
130 free(fake_pci_driver.dro_drivername.us_buf, M_DEVBUF);
131 free(fake_pccard_driver.dro_drivername.us_buf, M_DEVBUF);
132
133 mtx_destroy(&drvdb_mtx);
134 return(0);
135 }
136
137 /*
138 * Given the address of a driver image, find its corresponding
139 * driver_object.
140 */
141
142 driver_object *
windrv_lookup(vm_offset_t img,const char * name)143 windrv_lookup(vm_offset_t img, const char *name)
144 {
145 struct drvdb_ent *d;
146 unicode_string us;
147
148 printf("In windrv_lookup():\n");
149 printf("name = %s\n", name);
150
151 /* Damn unicode. */
152
153 if (name != NULL) {
154 us.us_len = strlen(name) * 2;
155 us.us_maxlen = strlen(name) * 2;
156 us.us_buf = NULL;
157 ndis_ascii_to_unicode(name, &us.us_buf);
158 } else {
159 us.us_len = 0;
160 us.us_maxlen = 0;
161 us.us_buf = NULL;
162 }
163
164 mtx_lock(&drvdb_mtx);
165 STAILQ_FOREACH(d, &drvdb_head, link) {
166 #ifdef NDIS_DBG
167 printf("d->windrv_object->dro_driverstart = %x\n", d->windrv_object->dro_driverstart);
168 #endif
169 if (d->windrv_object->dro_driverstart == (void *)img ||
170 (memcmp((char *)d->windrv_object->dro_drivername.us_buf,
171 (char *)us.us_buf, us.us_len) == 0 && us.us_len > 0)) {
172 mtx_unlock(&drvdb_mtx);
173 printf("found driver object!\n");
174 #ifdef NDIS_DBG
175 printf("returning %x\n", d->windrv_object);
176 #endif
177 return(d->windrv_object);
178 }
179 }
180 mtx_unlock(&drvdb_mtx);
181
182 if (name != NULL)
183 ExFreePool(us.us_buf);
184
185 printf("no driver object\n");
186 return(NULL);
187 }
188
189 /*
190 * Remove a driver_object from our datatabase and destroy it. Throw
191 * away any custom driver extension info that may have been added.
192 */
193
194 int
windrv_unload(module_t mod,vm_offset_t img,int len)195 windrv_unload(module_t mod, vm_offset_t img, int len)
196 {
197 struct drvdb_ent *d, *r = NULL;
198 driver_object *drv;
199 list_entry *e, *c;
200
201 mtx_lock(&drvdb_mtx);
202 STAILQ_FOREACH(d, &drvdb_head, link) {
203 if (d->windrv_object->dro_driverstart == (void *)img) {
204 r = d;
205 STAILQ_REMOVE(&drvdb_head, d, drvdb_ent, link);
206 break;
207 }
208 }
209 mtx_unlock(&drvdb_mtx);
210
211 if (r == NULL)
212 return (ENOENT);
213
214 /*
215 * Destroy any custom extensions that may have been added.
216 */
217 drv = r->windrv_object;
218 e = drv->dro_driverext->dre_usrext.nle_flink;
219 while (e != &drv->dro_driverext->dre_usrext) {
220 c = e->nle_flink;
221 REMOVE_LIST_ENTRY(e);
222 ExFreePool(e);
223 e = c;
224 }
225
226 /* Free the driver extension */
227 free(drv->dro_driverext, M_DEVBUF);
228
229 /* Free the driver name */
230 free(drv->dro_drivername.us_buf, M_DEVBUF);
231
232 /* Free driver object */
233 free(drv, M_DEVBUF);
234
235 /* Free our DB handle */
236 free(r, M_DEVBUF);
237
238 return(0);
239 }
240
241 /*
242 * Loader routine for actual Windows driver modules, ultimately
243 * calls the driver's DriverEntry() routine.
244 */
245
246 int
windrv_load(module_t mod,vm_offset_t img,int len)247 windrv_load(module_t mod, vm_offset_t img, int len)
248 {
249 image_import_descriptor imp_desc;
250 image_optional_header opt_hdr;
251 driver_entry entry;
252 struct drvdb_ent *new;
253 struct driver_object *drv;
254 int status;
255
256 #ifdef NDIS_DBG
257 printf("in windrv_load\n");
258 printf("img = %x\n", img);
259 #endif
260
261 /*
262 * First step: try to relocate and dynalink the executable
263 * driver image.
264 */
265
266 /* Perform text relocation */
267 if (pe_relocate(img)) {
268 return(ENOEXEC);
269 }
270
271 /* Dynamically link the NDIS.SYS routines -- required. */
272 if (pe_patch_imports(img, "NDIS", ndis_functbl)) {
273 return(ENOEXEC);
274 }
275
276 /* Dynamically link the HAL.dll routines -- also required. */
277 if (pe_patch_imports(img, "HAL", hal_functbl)) {
278 return(ENOEXEC);
279 }
280
281 /* Dynamically link ntoskrnl.exe -- optional. */
282 if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) {
283 if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl))
284 return(ENOEXEC);
285 }
286
287 /* Dynamically link USBD.SYS -- optional */
288 if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) {
289 #if ubsimplemented
290 if (pe_patch_imports(img, "USBD", usbd_functbl)) {
291 return(ENOEXEC);
292 }
293 #else
294 //printf("windrv_load: pe_get_import_descriptor USBD failed");
295 return(ENOEXEC);
296 #endif
297 }
298
299 /* Next step: find the driver entry point. */
300
301 pe_get_optional_header(img, &opt_hdr);
302 entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
303
304 /* Next step: allocate and store a driver object. */
305
306 new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT);
307 if (new == NULL)
308 return (ENOMEM);
309
310 drv = malloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO);
311 if (drv == NULL) {
312 free (new, M_DEVBUF);
313 return (ENOMEM);
314 }
315
316 /* Allocate a driver extension structure too. */
317
318 drv->dro_driverext = malloc(sizeof(driver_extension),
319 M_DEVBUF, M_NOWAIT|M_ZERO);
320
321 if (drv->dro_driverext == NULL) {
322 free(new, M_DEVBUF);
323 free(drv, M_DEVBUF);
324 return(ENOMEM);
325 }
326
327 INIT_LIST_HEAD((&drv->dro_driverext->dre_usrext));
328
329 drv->dro_driverstart = (void *)img;
330 drv->dro_driversize = len;
331
332 drv->dro_drivername.us_len = strlen(DUMMY_REGISTRY_PATH) * 2;
333 drv->dro_drivername.us_maxlen = strlen(DUMMY_REGISTRY_PATH) * 2;
334 drv->dro_drivername.us_buf = NULL;
335 ndis_ascii_to_unicode(DUMMY_REGISTRY_PATH,
336 &drv->dro_drivername.us_buf);
337
338 new->windrv_object = drv;
339
340 /* Now call the DriverEntry() function. */
341
342 status = MSCALL2(entry, drv, &drv->dro_drivername);
343
344 if (status != STATUS_SUCCESS) {
345 free(drv->dro_drivername.us_buf, M_DEVBUF);
346 free(drv, M_DEVBUF);
347 free(new, M_DEVBUF);
348 return(ENODEV);
349 }
350
351 mtx_lock(&drvdb_mtx);
352 STAILQ_INSERT_HEAD(&drvdb_head, new, link);
353 mtx_unlock(&drvdb_mtx);
354
355 return (0);
356 }
357
358 /*
359 * Make a new Physical Device Object for a device that was
360 * detected/plugged in. For us, the PDO is just a way to
361 * get at the device_t.
362 */
363
364 int
windrv_create_pdo(driver_object * drv,device_t bsddev)365 windrv_create_pdo(driver_object *drv, device_t bsddev)
366 {
367 device_object *dev;
368
369 /*
370 * This is a new physical device object, which technically
371 * is the "top of the stack." Consequently, we don't do
372 * an IoAttachDeviceToDeviceStack() here.
373 */
374
375 mtx_lock(&drvdb_mtx);
376 IoCreateDevice(drv, 0, NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev);
377 mtx_unlock(&drvdb_mtx);
378
379 /* Stash pointer to our BSD device handle. */
380
381 dev->do_devext = bsddev;
382
383 return(STATUS_SUCCESS);
384 }
385
386 void
windrv_destroy_pdo(driver_object * drv,device_t bsddev)387 windrv_destroy_pdo(driver_object *drv, device_t bsddev)
388 {
389 device_object *pdo;
390
391 pdo = windrv_find_pdo(drv, bsddev);
392
393 /* Remove reference to device_t */
394
395 pdo->do_devext = NULL;
396
397 mtx_lock(&drvdb_mtx);
398 IoDeleteDevice(pdo);
399 mtx_unlock(&drvdb_mtx);
400
401 return;
402 }
403
404 /*
405 * Given a device_t, find the corresponding PDO in a driver's
406 * device list.
407 */
408
409 device_object *
windrv_find_pdo(driver_object * drv,device_t bsddev)410 windrv_find_pdo(driver_object *drv, device_t bsddev)
411 {
412 device_object *pdo;
413 #ifdef NDIS_DBG
414 printf("In windrv_find_pdo: \ndrv = %x", drv);
415 printf("\nbsddev = %x", bsddev);
416 printf("\npdo = %x", drv->dro_devobj);
417 printf("\npdo->do_devext = %x\n", drv->dro_devobj->do_devext);
418 #endif
419 mtx_lock(&drvdb_mtx);
420 pdo = drv->dro_devobj;
421 if (pdo->do_devext != bsddev) {
422 mtx_unlock(&drvdb_mtx);
423 panic("PDO wasn't first device in list");
424 }
425 mtx_unlock(&drvdb_mtx);
426
427 return(pdo);
428 }
429
430 /*
431 * Add an internally emulated driver to the database. We need this
432 * to set up an emulated bus driver so that it can receive IRPs.
433 */
434
435 int
windrv_bus_attach(driver_object * drv,const char * name)436 windrv_bus_attach(driver_object *drv, const char *name)
437 {
438 struct drvdb_ent *new;
439
440 new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT);
441 if (new == NULL)
442 return (ENOMEM);
443
444 drv->dro_drivername.us_len = strlen(name) * 2;
445 drv->dro_drivername.us_maxlen = strlen(name) * 2;
446 drv->dro_drivername.us_buf = NULL;
447 ndis_ascii_to_unicode(name, &drv->dro_drivername.us_buf);
448
449 #ifdef __NetBSD__
450 /* I added this because windrv_lookup was getting
451 * fake_pccard_driver and fake_pci_driver mixed up.
452 * I'm not sure if it will mess anything else up.
453 */
454 drv->dro_driverstart = drv;
455 #endif
456
457 new->windrv_object = drv;
458 new->windrv_devlist = NULL;
459 new->windrv_regvals = NULL;
460
461 mtx_lock(&drvdb_mtx);
462 STAILQ_INSERT_HEAD(&drvdb_head, new, link);
463 mtx_unlock(&drvdb_mtx);
464
465 return(0);
466 }
467
468 #ifdef __amd64__
469
470 extern void x86_64_wrap(void);
471 extern void x86_64_wrap_call(void);
472 extern void x86_64_wrap_end(void);
473
474 #endif /* __amd64__ */
475
476 int
windrv_wrap(funcptr func,funcptr * wrap)477 windrv_wrap(funcptr func, funcptr *wrap)
478 {
479 #ifdef __amd64__
480 funcptr p;
481 vm_offset_t *calladdr;
482 vm_offset_t wrapstart, wrapend, wrapcall;
483
484 wrapstart = (vm_offset_t)&x86_64_wrap;
485 wrapend = (vm_offset_t)&x86_64_wrap_end;
486 wrapcall = (vm_offset_t)&x86_64_wrap_call;
487
488 /* Allocate a new wrapper instance. */
489
490 p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
491 if (p == NULL)
492 return(ENOMEM);
493
494 /* Copy over the code. */
495
496 memcpy( p, (char *)wrapstart, (wrapend - wrapstart));
497
498 /* Insert the function address into the new wrapper instance. */
499
500 calladdr = (uint64_t *)((char *)p + (wrapcall - wrapstart) + 2);
501 *calladdr = (vm_offset_t)func;
502
503 *wrap = p;
504 #else /* __amd64__ */
505 *wrap = func;
506 #endif /* __amd64__ */
507 return(0);
508 }
509
510 int
windrv_unwrap(funcptr func)511 windrv_unwrap(funcptr func)
512 {
513 #ifdef __amd64__
514 free(func, M_DEVBUF);
515 #endif /* __amd64__ */
516 return(0);
517 }
518