/*- * Copyright (c) 2005 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct windrv_type { uint16_t windrv_vid; /* for PCI or USB */ uint16_t windrv_did; /* for PCI or USB */ uint32_t windrv_subsys; /* for PCI */ char *windrv_vname; /* for pccard */ char *windrv_dname; /* for pccard */ char *windrv_name; /* for pccard, PCI or USB */ }; struct drvdb_ent { driver_object *windrv_object; struct windrv_type *windrv_devlist; ndis_cfg *windrv_regvals; STAILQ_ENTRY(drvdb_ent) link; }; struct mtx drvdb_mtx; static STAILQ_HEAD(drvdb, drvdb_ent) drvdb_head; static driver_object fake_pci_driver; /* serves both PCI and cardbus */ static driver_object fake_pccard_driver; #define DUMMY_REGISTRY_PATH "\\\\some\\bogus\\path" int windrv_libinit(void) { STAILQ_INIT(&drvdb_head); mtx_init(&drvdb_mtx, "Windows driver DB lock", "Windows internal lock", MTX_DEF); /* * PCI and pccard devices don't need to use IRPs to * interact with their bus drivers (usually), so our * emulated PCI and pccard drivers are just stubs. * USB devices, on the other hand, do all their I/O * by exchanging IRPs with the USB bus driver, so * for that we need to provide emulator dispatcher * routines, which are in a separate module. */ windrv_bus_attach(&fake_pci_driver, "PCI Bus"); windrv_bus_attach(&fake_pccard_driver, "PCCARD Bus"); return(0); } int windrv_libfini(void) { struct drvdb_ent *d; mtx_lock(&drvdb_mtx); while(STAILQ_FIRST(&drvdb_head) != NULL) { d = STAILQ_FIRST(&drvdb_head); STAILQ_REMOVE_HEAD(&drvdb_head, link); free(d, M_DEVBUF); } mtx_unlock(&drvdb_mtx); free(fake_pci_driver.dro_drivername.us_buf, M_DEVBUF); free(fake_pccard_driver.dro_drivername.us_buf, M_DEVBUF); mtx_destroy(&drvdb_mtx); return(0); } /* * Given the address of a driver image, find its corresponding * driver_object. */ driver_object * windrv_lookup(img, name) vm_offset_t img; char *name; { struct drvdb_ent *d; unicode_string us; /* Damn unicode. */ if (name != NULL) { us.us_len = strlen(name) * 2; us.us_maxlen = strlen(name) * 2; us.us_buf = NULL; ndis_ascii_to_unicode(name, &us.us_buf); } mtx_lock(&drvdb_mtx); STAILQ_FOREACH(d, &drvdb_head, link) { if (d->windrv_object->dro_driverstart == (void *)img || bcmp((char *)d->windrv_object->dro_drivername.us_buf, (char *)us.us_buf, us.us_len) == 0) { mtx_unlock(&drvdb_mtx); return(d->windrv_object); } } mtx_unlock(&drvdb_mtx); if (name != NULL) ExFreePool(us.us_buf); return(NULL); } /* * Remove a driver_object from our datatabase and destroy it. Throw * away any custom driver extension info that may have been added. */ int windrv_unload(mod, img, len) module_t mod; vm_offset_t img; int len; { struct drvdb_ent *d, *r = NULL; driver_object *drv; list_entry *e, *c; mtx_lock(&drvdb_mtx); STAILQ_FOREACH(d, &drvdb_head, link) { if (d->windrv_object->dro_driverstart == (void *)img) { r = d; STAILQ_REMOVE(&drvdb_head, d, drvdb_ent, link); break; } } mtx_unlock(&drvdb_mtx); if (r == NULL) return (ENOENT); /* * Destroy any custom extensions that may have been added. */ drv = r->windrv_object; e = drv->dro_driverext->dre_usrext.nle_flink; while (e != &drv->dro_driverext->dre_usrext) { c = e->nle_flink; REMOVE_LIST_ENTRY(e); ExFreePool(e); e = c; } /* Free the driver extension */ free(drv->dro_driverext, M_DEVBUF); /* Free the driver name */ free(drv->dro_drivername.us_buf, M_DEVBUF); /* Free driver object */ free(drv, M_DEVBUF); /* Free our DB handle */ free(r, M_DEVBUF); return(0); } /* * Loader routine for actual Windows driver modules, ultimately * calls the driver's DriverEntry() routine. */ int windrv_load(mod, img, len) module_t mod; vm_offset_t img; int len; { image_import_descriptor imp_desc; image_optional_header opt_hdr; driver_entry entry; struct drvdb_ent *new; struct driver_object *dobj; int status; /* * First step: try to relocate and dynalink the executable * driver image. */ /* Perform text relocation */ if (pe_relocate(img)) return(ENOEXEC); /* Dynamically link the NDIS.SYS routines -- required. */ if (pe_patch_imports(img, "NDIS", ndis_functbl)) return(ENOEXEC); /* Dynamically link the HAL.dll routines -- also required. */ if (pe_patch_imports(img, "HAL", hal_functbl)) return(ENOEXEC); /* Dynamically link ntoskrnl.exe -- optional. */ if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) { if (pe_patch_imports(img, "ntoskrnl", ntoskrnl_functbl)) return(ENOEXEC); } /* Dynamically link USBD.SYS -- optional */ if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) { if (pe_patch_imports(img, "USBD", usbd_functbl)) return(ENOEXEC); } /* Next step: find the driver entry point. */ pe_get_optional_header(img, &opt_hdr); entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr); /* Next step: allocate and store a driver object. */ new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT); if (new == NULL) return (ENOMEM); dobj = malloc(sizeof(device_object), M_DEVBUF, M_NOWAIT|M_ZERO); if (dobj == NULL) { free (new, M_DEVBUF); return (ENOMEM); } /* Allocate a driver extension structure too. */ dobj->dro_driverext = malloc(sizeof(driver_extension), M_DEVBUF, M_NOWAIT|M_ZERO); if (dobj->dro_driverext == NULL) { free(new, M_DEVBUF); free(dobj, M_DEVBUF); return(ENOMEM); } INIT_LIST_HEAD((&dobj->dro_driverext->dre_usrext)); dobj->dro_driverstart = (void *)img; dobj->dro_driversize = len; dobj->dro_drivername.us_len = strlen(DUMMY_REGISTRY_PATH) * 2; dobj->dro_drivername.us_maxlen = strlen(DUMMY_REGISTRY_PATH) * 2; dobj->dro_drivername.us_buf = NULL; ndis_ascii_to_unicode(DUMMY_REGISTRY_PATH, &dobj->dro_drivername.us_buf); new->windrv_object = dobj; /* Now call the DriverEntry() function. */ status = MSCALL2(entry, dobj, &dobj->dro_drivername); if (status != STATUS_SUCCESS) { free(dobj->dro_drivername.us_buf, M_DEVBUF); free(dobj, M_DEVBUF); free(new, M_DEVBUF); return(ENODEV); } mtx_lock(&drvdb_mtx); STAILQ_INSERT_HEAD(&drvdb_head, new, link); mtx_unlock(&drvdb_mtx); return (0); } /* * Make a new Physical Device Object for a device that was * detected/plugged in. For us, the PDO is just a way to * get at the device_t. */ int windrv_create_pdo(drv, bsddev) driver_object *drv; device_t bsddev; { device_object *dev; /* * This is a new physical device object, which technically * is the "top of the stack." Consequently, we don't do * an IoAttachDeviceToDeviceStack() here. */ mtx_lock(&drvdb_mtx); IoCreateDevice(drv, 0, NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev); mtx_unlock(&drvdb_mtx); /* Stash pointer to our BSD device handle. */ dev->do_devext = bsddev; return(STATUS_SUCCESS); } void windrv_destroy_pdo(drv, bsddev) driver_object *drv; device_t bsddev; { device_object *pdo; pdo = windrv_find_pdo(drv, bsddev); /* Remove reference to device_t */ pdo->do_devext = NULL; mtx_lock(&drvdb_mtx); IoDeleteDevice(pdo); mtx_unlock(&drvdb_mtx); return; } /* * Given a device_t, find the corresponding PDO in a driver's * device list. */ device_object * windrv_find_pdo(drv, bsddev) driver_object *drv; device_t bsddev; { device_object *pdo; mtx_lock(&drvdb_mtx); pdo = drv->dro_devobj; if (pdo->do_devext != bsddev) { mtx_unlock(&drvdb_mtx); panic("PDO wasn't first device in list"); } mtx_unlock(&drvdb_mtx); return(pdo); } /* * Add an internally emulated driver to the database. We need this * to set up an emulated bus driver so that it can receive IRPs. */ int windrv_bus_attach(drv, name) driver_object *drv; char *name; { struct drvdb_ent *new; new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT); if (new == NULL) return (ENOMEM); drv->dro_drivername.us_len = strlen(name) * 2; drv->dro_drivername.us_maxlen = strlen(name) * 2; drv->dro_drivername.us_buf = NULL; ndis_ascii_to_unicode(name, &drv->dro_drivername.us_buf); new->windrv_object = drv; new->windrv_devlist = NULL; new->windrv_regvals = NULL; mtx_lock(&drvdb_mtx); STAILQ_INSERT_HEAD(&drvdb_head, new, link); mtx_unlock(&drvdb_mtx); return(0); } #ifdef __amd64__ extern void x86_64_wrap(void); extern void x86_64_wrap_call(void); extern void x86_64_wrap_end(void); #endif /* __amd64__ */ int windrv_wrap(func, wrap) funcptr func; funcptr *wrap; { #ifdef __amd64__ funcptr p; vm_offset_t *calladdr; vm_offset_t wrapstart, wrapend, wrapcall; wrapstart = (vm_offset_t)&x86_64_wrap; wrapend = (vm_offset_t)&x86_64_wrap_end; wrapcall = (vm_offset_t)&x86_64_wrap_call; /* Allocate a new wrapper instance. */ p = malloc((wrapend - wrapstart), M_DEVBUF, M_NOWAIT); if (p == NULL) return(ENOMEM); /* Copy over the code. */ bcopy((char *)wrapstart, p, (wrapend - wrapstart)); /* Insert the function address into the new wrapper instance. */ calladdr = (uint64_t *)((char *)p + (wrapcall - wrapstart) + 2); *calladdr = (vm_offset_t)func; *wrap = p; #else /* __amd64__ */ *wrap = func; #endif /* __amd64__ */ return(0); } int windrv_unwrap(func) funcptr func; { #ifdef __amd64__ free(func, M_DEVBUF); #endif /* __amd64__ */ return(0); }