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.5 2006/11/16 01:32:44 christos 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
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
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 *
143 windrv_lookup(img, name)
144 vm_offset_t img;
145 const char *name;
146 {
147 struct drvdb_ent *d;
148 unicode_string us;
149
150 printf("In windrv_lookup():\n");
151 printf("name = %s\n", name);
152
153 /* Damn unicode. */
154
155 if (name != NULL) {
156 us.us_len = strlen(name) * 2;
157 us.us_maxlen = strlen(name) * 2;
158 us.us_buf = NULL;
159 ndis_ascii_to_unicode(name, &us.us_buf);
160 } else {
161 us.us_len = 0;
162 us.us_maxlen = 0;
163 us.us_buf = NULL;
164 }
165
166 mtx_lock(&drvdb_mtx);
167 STAILQ_FOREACH(d, &drvdb_head, link) {
168 #ifdef NDIS_DBG
169 printf("d->windrv_object->dro_driverstart = %x\n", d->windrv_object->dro_driverstart);
170 #endif
171 if (d->windrv_object->dro_driverstart == (void *)img ||
172 (bcmp((char *)d->windrv_object->dro_drivername.us_buf,
173 (char *)us.us_buf, us.us_len) == 0 && us.us_len > 0)) {
174 mtx_unlock(&drvdb_mtx);
175 printf("found driver object!\n");
176 #ifdef NDIS_DBG
177 printf("returning %x\n", d->windrv_object);
178 #endif
179 return(d->windrv_object);
180 }
181 }
182 mtx_unlock(&drvdb_mtx);
183
184 if (name != NULL)
185 ExFreePool(us.us_buf);
186
187 printf("no driver object\n");
188 return(NULL);
189 }
190
191 /*
192 * Remove a driver_object from our datatabase and destroy it. Throw
193 * away any custom driver extension info that may have been added.
194 */
195
196 int
197 windrv_unload(module_t mod, vm_offset_t img, int len)
198 {
199 struct drvdb_ent *d, *r = NULL;
200 driver_object *drv;
201 list_entry *e, *c;
202
203 mtx_lock(&drvdb_mtx);
204 STAILQ_FOREACH(d, &drvdb_head, link) {
205 if (d->windrv_object->dro_driverstart == (void *)img) {
206 r = d;
207 STAILQ_REMOVE(&drvdb_head, d, drvdb_ent, link);
208 break;
209 }
210 }
211 mtx_unlock(&drvdb_mtx);
212
213 if (r == NULL)
214 return (ENOENT);
215
216 /*
217 * Destroy any custom extensions that may have been added.
218 */
219 drv = r->windrv_object;
220 e = drv->dro_driverext->dre_usrext.nle_flink;
221 while (e != &drv->dro_driverext->dre_usrext) {
222 c = e->nle_flink;
223 REMOVE_LIST_ENTRY(e);
224 ExFreePool(e);
225 e = c;
226 }
227
228 /* Free the driver extension */
229 free(drv->dro_driverext, M_DEVBUF);
230
231 /* Free the driver name */
232 free(drv->dro_drivername.us_buf, M_DEVBUF);
233
234 /* Free driver object */
235 free(drv, M_DEVBUF);
236
237 /* Free our DB handle */
238 free(r, M_DEVBUF);
239
240 return(0);
241 }
242
243 /*
244 * Loader routine for actual Windows driver modules, ultimately
245 * calls the driver's DriverEntry() routine.
246 */
247
248 int
249 windrv_load(module_t mod, vm_offset_t img, int len)
250 {
251 image_import_descriptor imp_desc;
252 image_optional_header opt_hdr;
253 driver_entry entry;
254 struct drvdb_ent *new;
255 struct driver_object *drv;
256 int status;
257
258 #ifdef NDIS_DBG
259 printf("in windrv_load\n");
260 printf("img = %x\n", img);
261 #endif
262
263 /*
264 * First step: try to relocate and dynalink the executable
265 * driver image.
266 */
267
268 /* Perform text relocation */
269 if (pe_relocate(img)) {
270 return(ENOEXEC);
271 }
272
273 /* Dynamically link the NDIS.SYS routines -- required. */
274 if (pe_patch_imports(img, "NDIS", ndis_functbl)) {
275 return(ENOEXEC);
276 }
277
278 /* Dynamically link the HAL.dll routines -- also required. */
279 if (pe_patch_imports(img, "HAL", hal_functbl)) {
280 return(ENOEXEC);
281 }
282
283 /* Dynamically link ntoskrnl.exe -- optional. */
284 if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) {
285 if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl))
286 return(ENOEXEC);
287 }
288
289 /* Dynamically link USBD.SYS -- optional */
290 if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) {
291 #if ubsimplemented
292 if (pe_patch_imports(img, "USBD", usbd_functbl)) {
293 return(ENOEXEC);
294 }
295 #else
296 //printf("windrv_load: pe_get_import_descriptor USBD failed");
297 return(ENOEXEC);
298 #endif
299 }
300
301 /* Next step: find the driver entry point. */
302
303 pe_get_optional_header(img, &opt_hdr);
304 entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr);
305
306 /* Next step: allocate and store a driver object. */
307
308 new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT);
309 if (new == NULL)
310 return (ENOMEM);
311
312 drv = malloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO);
313 if (drv == NULL) {
314 free (new, M_DEVBUF);
315 return (ENOMEM);
316 }
317
318 /* Allocate a driver extension structure too. */
319
320 drv->dro_driverext = malloc(sizeof(driver_extension),
321 M_DEVBUF, M_NOWAIT|M_ZERO);
322
323 if (drv->dro_driverext == NULL) {
324 free(new, M_DEVBUF);
325 free(drv, M_DEVBUF);
326 return(ENOMEM);
327 }
328
329 INIT_LIST_HEAD((&drv->dro_driverext->dre_usrext));
330
331 drv->dro_driverstart = (void *)img;
332 drv->dro_driversize = len;
333
334 drv->dro_drivername.us_len = strlen(DUMMY_REGISTRY_PATH) * 2;
335 drv->dro_drivername.us_maxlen = strlen(DUMMY_REGISTRY_PATH) * 2;
336 drv->dro_drivername.us_buf = NULL;
337 ndis_ascii_to_unicode(DUMMY_REGISTRY_PATH,
338 &drv->dro_drivername.us_buf);
339
340 new->windrv_object = drv;
341
342 /* Now call the DriverEntry() function. */
343
344 status = MSCALL2(entry, drv, &drv->dro_drivername);
345
346 if (status != STATUS_SUCCESS) {
347 free(drv->dro_drivername.us_buf, M_DEVBUF);
348 free(drv, M_DEVBUF);
349 free(new, M_DEVBUF);
350 return(ENODEV);
351 }
352
353 mtx_lock(&drvdb_mtx);
354 STAILQ_INSERT_HEAD(&drvdb_head, new, link);
355 mtx_unlock(&drvdb_mtx);
356
357 return (0);
358 }
359
360 /*
361 * Make a new Physical Device Object for a device that was
362 * detected/plugged in. For us, the PDO is just a way to
363 * get at the device_t.
364 */
365
366 int
367 windrv_create_pdo(drv, bsddev)
368 driver_object *drv;
369 device_t bsddev;
370 {
371 device_object *dev;
372
373 /*
374 * This is a new physical device object, which technically
375 * is the "top of the stack." Consequently, we don't do
376 * an IoAttachDeviceToDeviceStack() here.
377 */
378
379 mtx_lock(&drvdb_mtx);
380 IoCreateDevice(drv, 0, NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev);
381 mtx_unlock(&drvdb_mtx);
382
383 /* Stash pointer to our BSD device handle. */
384
385 dev->do_devext = bsddev;
386
387 return(STATUS_SUCCESS);
388 }
389
390 void
391 windrv_destroy_pdo(drv, bsddev)
392 driver_object *drv;
393 device_t bsddev;
394 {
395 device_object *pdo;
396
397 pdo = windrv_find_pdo(drv, bsddev);
398
399 /* Remove reference to device_t */
400
401 pdo->do_devext = NULL;
402
403 mtx_lock(&drvdb_mtx);
404 IoDeleteDevice(pdo);
405 mtx_unlock(&drvdb_mtx);
406
407 return;
408 }
409
410 /*
411 * Given a device_t, find the corresponding PDO in a driver's
412 * device list.
413 */
414
415 device_object *
416 windrv_find_pdo(drv, bsddev)
417 driver_object *drv;
418 device_t bsddev;
419 {
420 device_object *pdo;
421 #ifdef NDIS_DBG
422 printf("In windrv_find_pdo: \ndrv = %x", drv);
423 printf("\nbsddev = %x", bsddev);
424 printf("\npdo = %x", drv->dro_devobj);
425 printf("\npdo->do_devext = %x\n", drv->dro_devobj->do_devext);
426 #endif
427 mtx_lock(&drvdb_mtx);
428 pdo = drv->dro_devobj;
429 if (pdo->do_devext != bsddev) {
430 mtx_unlock(&drvdb_mtx);
431 panic("PDO wasn't first device in list");
432 }
433 mtx_unlock(&drvdb_mtx);
434
435 return(pdo);
436 }
437
438 /*
439 * Add an internally emulated driver to the database. We need this
440 * to set up an emulated bus driver so that it can receive IRPs.
441 */
442
443 int
444 windrv_bus_attach(drv, name)
445 driver_object *drv;
446 const char *name;
447 {
448 struct drvdb_ent *new;
449
450 new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT);
451 if (new == NULL)
452 return (ENOMEM);
453
454 drv->dro_drivername.us_len = strlen(name) * 2;
455 drv->dro_drivername.us_maxlen = strlen(name) * 2;
456 drv->dro_drivername.us_buf = NULL;
457 ndis_ascii_to_unicode(name, &drv->dro_drivername.us_buf);
458
459 #ifdef __NetBSD__
460 /* I added this because windrv_lookup was getting
461 * fake_pccard_driver and fake_pci_driver mixed up.
462 * I'm not sure if it will mess anything else up.
463 */
464 drv->dro_driverstart = drv;
465 #endif
466
467 new->windrv_object = drv;
468 new->windrv_devlist = NULL;
469 new->windrv_regvals = NULL;
470
471 mtx_lock(&drvdb_mtx);
472 STAILQ_INSERT_HEAD(&drvdb_head, new, link);
473 mtx_unlock(&drvdb_mtx);
474
475 return(0);
476 }
477
478 #ifdef __amd64__
479
480 extern void x86_64_wrap(void);
481 extern void x86_64_wrap_call(void);
482 extern void x86_64_wrap_end(void);
483
484 #endif /* __amd64__ */
485
486 int
487 windrv_wrap(func, wrap)
488 funcptr func;
489 funcptr *wrap;
490 {
491 #ifdef __amd64__
492 funcptr p;
493 vm_offset_t *calladdr;
494 vm_offset_t wrapstart, wrapend, wrapcall;
495
496 wrapstart = (vm_offset_t)&x86_64_wrap;
497 wrapend = (vm_offset_t)&x86_64_wrap_end;
498 wrapcall = (vm_offset_t)&x86_64_wrap_call;
499
500 /* Allocate a new wrapper instance. */
501
502 p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT);
503 if (p == NULL)
504 return(ENOMEM);
505
506 /* Copy over the code. */
507
508 bcopy((char *)wrapstart, p, (wrapend - wrapstart));
509
510 /* Insert the function address into the new wrapper instance. */
511
512 calladdr = (uint64_t *)((char *)p + (wrapcall - wrapstart) + 2);
513 *calladdr = (vm_offset_t)func;
514
515 *wrap = p;
516 #else /* __amd64__ */
517 *wrap = func;
518 #endif /* __amd64__ */
519 return(0);
520 }
521
522 int
523 windrv_unwrap(funcptr func)
524 {
525 #ifdef __amd64__
526 free(func, M_DEVBUF);
527 #endif /* __amd64__ */
528 return(0);
529 }
Cache object: 090cae3acb8795e1d1cbe4a56d458897
|