diff options
Diffstat (limited to 'sys/compat/ndis/kern_windrv.c')
-rw-r--r-- | sys/compat/ndis/kern_windrv.c | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/sys/compat/ndis/kern_windrv.c b/sys/compat/ndis/kern_windrv.c new file mode 100644 index 0000000..f298b99 --- /dev/null +++ b/sys/compat/ndis/kern_windrv.c @@ -0,0 +1,416 @@ +/*- + * Copyright (c) 2005 + * Bill Paul <wpaul@windriver.com>. 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/unistd.h> +#include <sys/types.h> + +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/module.h> +#include <sys/conf.h> +#include <sys/mbuf.h> +#include <sys/bus.h> + +#include <sys/queue.h> + +#include <compat/ndis/pe_var.h> +#include <compat/ndis/cfg_var.h> +#include <compat/ndis/resource_var.h> +#include <compat/ndis/ntoskrnl_var.h> +#include <compat/ndis/ndis_var.h> +#include <compat/ndis/hal_var.h> + +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) + vm_offset_t img; +{ + struct drvdb_ent *d; + + mtx_lock(&drvdb_mtx); + STAILQ_FOREACH(d, &drvdb_head, link) { + if (d->windrv_object->dro_driverstart == (void *)img) { + mtx_unlock(&drvdb_mtx); + return(d->windrv_object); + } + } + mtx_unlock(&drvdb_mtx); + + 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_HEAD((&drv->dro_driverext->dre_usrext)); + ExFreePool(c); + e = e->nle_flink; + } + + /* 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 */ +#ifdef notyet + if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) { + if (pe_patch_imports(img, "USBD", ntoskrnl_functbl)) + return(ENOEXEC); + } +#endif + + /* 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 = 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); +} |