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