diff options
author | cvs2svn <cvs2svn@FreeBSD.org> | 2005-02-18 03:22:38 +0000 |
---|---|---|
committer | cvs2svn <cvs2svn@FreeBSD.org> | 2005-02-18 03:22:38 +0000 |
commit | 5f6c8a8de106291f442c3b553dd0b242e75204c2 (patch) | |
tree | dca8fb79a0d82cbd6312f7ab52a523ae95b71d2a /sys/compat | |
parent | 9440f20b2e6c93fc203e8ef1d0d0550a43a17747 (diff) |
Notes
Diffstat (limited to 'sys/compat')
-rw-r--r-- | sys/compat/ndis/kern_windrv.c | 470 | ||||
-rw-r--r-- | sys/compat/ndis/winx64_wrap.S | 177 |
2 files changed, 647 insertions, 0 deletions
diff --git a/sys/compat/ndis/kern_windrv.c b/sys/compat/ndis/kern_windrv.c new file mode 100644 index 0000000000000..6fa3575fea34a --- /dev/null +++ b/sys/compat/ndis/kern_windrv.c @@ -0,0 +1,470 @@ +/*- + * 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_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 */ +#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 = 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); +} diff --git a/sys/compat/ndis/winx64_wrap.S b/sys/compat/ndis/winx64_wrap.S new file mode 100644 index 0000000000000..1f9fc20b691b8 --- /dev/null +++ b/sys/compat/ndis/winx64_wrap.S @@ -0,0 +1,177 @@ +/*- + * 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. + * + * The x86_64 callback routines were written and graciously submitted + * by Ville-Pertti Keinonen <will@exomi.com>. + * + * $FreeBSD$ + */ + +#include <machine/asmacros.h> + +/* + * Wrapper for handling up to 16 arguments. We can't really + * know how many arguments the caller will pass us. I'm taking an + * educated guess that we'll never get over 16. Handling too + * few arguments is bad. Handling too many is inefficient, but + * not fatal. If someone can think of a way to handle an arbitrary + * number of arguments with more elegant code, freel free to let + * me know. + * + * Standard amd64 calling conventions specify the following registers + * to be used for passing the first 6 arguments: + * + * %rdi, %rsi, %rdx, %rcx, %r8, %r9 + * + * Further arguments are passed on the stack (the 7th argument is + * located immediately after the return address). + * + * Windows x86_64 calling conventions only pass the first 4 + * arguments in registers: + * + * %rcx, %rdx, %r8, %r9 + * + * Even when arguments are passed in registers, the stack must have + * space reserved for those arguments. Thus the 5th argument (the + * first non-register argument) is placed 32 bytes after the return + * address. Additionally, %rdi and %rsi must be preserved. (These + * two registers are not scratch registers in the standard convention.) + * + * Note that in this template, we load a contrived 64 bit address into + * %r11 to represent our jump address. This is to guarantee that the + * assembler leaves enough room to patch in an absolute 64-bit address + * later. The idea behind this code is that we want to avoid having to + * manually create all the wrapper functions at compile time with + * a bunch of macros. This is doable, but a) messy and b) requires + * us to maintain two separate tables (one for the UNIX function + * pointers and another with the wrappers). This means I'd have to + * update two different tables each time I added a function. + * + * To avoid this, we create the wrappers at runtime instead. The + * image patch tables now contain two pointers: one two the normal + * routine, and a blank one for the wrapper. To construct a wrapper, + * we allocate some memory and copy the template function into it, + * then patch the function pointer for the routine we want to wrap + * into the newly created wrapper. The subr_pe module can then + * simply patch the wrapper routine into the jump table into the + * windows image. As a bonus, the wrapper pointer not only serves + * as the wrapper entry point address, it's also a data pointer + * that we can pass to free() later when we unload the module. + */ + + .globl x86_64_wrap_call + .globl x86_64_wrap_end + +ENTRY(x86_64_wrap) + subq $96,%rsp # allocate space on stack + mov %rsi,96-8(%rsp) # save %rsi + mov %rdi,96-16(%rsp)# save %rdi + mov %rcx,%r10 # temporarily save %rcx in scratch + mov %rsp,%rsi + add $96+56,%rsi # source == old stack top (stack+56) + mov %rsp,%rdi # destination == new stack top + mov $10,%rcx # count == 10 quadwords + rep + movsq # copy old stack contents to new location + mov %r10,%rdi # set up arg0 (%rcx -> %rdi) + mov %rdx,%rsi # set up arg1 (%rdx -> %rsi) + mov %r8,%rdx # set up arg2 (%r8 -> %rdx) + mov %r9,%rcx # set up arg3 (%r9 -> %rcx) + mov 96+40(%rsp),%r8 # set up arg4 (stack+40 -> %r8) + mov 96+48(%rsp),%r9 # set up arg5 (stack+48 -> %r9) + xor %rax,%rax # clear return value +x86_64_wrap_call: + mov $0xFF00FF00FF00FF00,%r11 + callq *%r11 # call routine + mov 96-16(%rsp),%rdi# restore %rdi + mov 96-8(%rsp),%rsi # restore %rsi + addq $96,%rsp # delete space on stack + ret +x86_64_wrap_end: + +/* + * Functions for invoking x86_64 callbacks. In each case, the first + * argument is a pointer to the function. + */ + +ENTRY(x86_64_call1) + subq $8,%rsp + mov %rsi,%rcx + call *%rdi + addq $8,%rsp + ret + +ENTRY(x86_64_call2) + subq $24,%rsp + mov %rsi,%rcx + /* %rdx is already correct */ + call *%rdi + addq $24,%rsp + ret + +ENTRY(x86_64_call3) + subq $24,%rsp + mov %rcx,%r8 + mov %rsi,%rcx + call *%rdi + addq $24,%rsp + ret + +ENTRY(x86_64_call4) + subq $40,%rsp + mov %r8,%r9 + mov %rcx,%r8 + mov %rsi,%rcx + call *%rdi + addq $40,%rsp + ret + +ENTRY(x86_64_call5) + subq $40,%rsp + mov %r9,32(%rsp) + mov %r8,%r9 + mov %rcx,%r8 + mov %rsi,%rcx + call *%rdi + addq $40,%rsp + ret + +ENTRY(x86_64_call6) + subq $56,%rsp + mov 56+8(%rsp),%rax + mov %r9,32(%rsp) + mov %rax,40(%rsp) + mov %r8,%r9 + mov %rcx,%r8 + mov %rsi,%rcx + call *%rdi + addq $56,%rsp + ret |