[BACK]Return to kern_windrv.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / compat / ndis

File: [cvs.NetBSD.org] / src / sys / compat / ndis / kern_windrv.c (download)

Revision 1.8, Wed Mar 18 17:06:48 2009 UTC (5 years, 5 months ago) by cegger
Branch: MAIN
CVS Tags: yamt-pagecache-tag8, yamt-pagecache-base9, yamt-pagecache-base8, yamt-pagecache-base7, yamt-pagecache-base6, yamt-pagecache-base5, yamt-pagecache-base4, yamt-pagecache-base3, yamt-pagecache-base2, yamt-pagecache-base, yamt-pagecache, yamt-nfs-mp-base9, yamt-nfs-mp-base8, yamt-nfs-mp-base7, yamt-nfs-mp-base6, yamt-nfs-mp-base5, yamt-nfs-mp-base4, yamt-nfs-mp-base3, yamt-nfs-mp-base11, yamt-nfs-mp-base10, uebayasi-xip-base4, uebayasi-xip-base3, uebayasi-xip-base2, uebayasi-xip-base1, uebayasi-xip-base, uebayasi-xip, tls-maxphys-base, tls-maxphys, tls-earlyentropy-base, tls-earlyentropy, rmind-uvmplock-nbase, rmind-uvmplock-base, rmind-uvmplock, rmind-smpnet-nbase, rmind-smpnet-base, rmind-smpnet, riastradh-xf86-video-intel-2-7-1-pre-2-21-15, riastradh-drm2-base3, riastradh-drm2-base2, riastradh-drm2-base1, riastradh-drm2-base, riastradh-drm2, nick-hppapmap-base4, nick-hppapmap-base3, nick-hppapmap-base, netbsd-7-base, netbsd-7, netbsd-6-base, netbsd-6-1-RELEASE, netbsd-6-1-RC4, netbsd-6-1-RC3, netbsd-6-1-RC2, netbsd-6-1-RC1, netbsd-6-1-4-RELEASE, netbsd-6-1-3-RELEASE, netbsd-6-1-2-RELEASE, netbsd-6-1-1-RELEASE, netbsd-6-1, netbsd-6-0-RELEASE, netbsd-6-0-RC2, netbsd-6-0-RC1, netbsd-6-0-5-RELEASE, netbsd-6-0-4-RELEASE, netbsd-6-0-3-RELEASE, netbsd-6-0-2-RELEASE, netbsd-6-0-1-RELEASE, netbsd-6-0, netbsd-6, matt-premerge-20091211, matt-nb6-plus-nbase, matt-nb6-plus-base, matt-nb6-plus, matt-mips64-premerge-20101231, khorben-n900, jymxensuspend-base, jym-xensuspend-nbase, jym-xensuspend-base, jruoho-x86intr-base, jruoho-x86intr, jmcneill-usbmp-pre-base2, jmcneill-usbmp-base9, jmcneill-usbmp-base8, jmcneill-usbmp-base7, jmcneill-usbmp-base6, jmcneill-usbmp-base5, jmcneill-usbmp-base4, jmcneill-usbmp-base3, jmcneill-usbmp-base2, jmcneill-usbmp-base10, jmcneill-usbmp-base, jmcneill-usbmp, jmcneill-audiomp3-base, jmcneill-audiomp3, cherry-xenmp-base, cherry-xenmp, bouyer-quota2-nbase, bouyer-quota2-base, bouyer-quota2, agc-symver-base, agc-symver, HEAD
Changes since 1.7: +2 -2 lines

bcopy -> memcpy

/*-
 * 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>
#ifdef __FreeBSD__
__FBSDID("$FreeBSD: src/sys/compat/ndis/kern_windrv.c,v 1.3.2.2 2005/03/31 04:24:35 wpaul Exp $");
#endif
#ifdef __NetBSD__
__KERNEL_RCSID(0, "$NetBSD: kern_windrv.c,v 1.8 2009/03/18 17:06:48 cegger Exp $");
#endif

#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>
#ifdef __FreeBSD__
#include <sys/mutex.h>
#include <sys/module.h>
#endif /* __FreeBSD__ */
#include <sys/conf.h>
#include <sys/mbuf.h>
#ifdef __FreeBSD__
#include <sys/bus.h>
#endif

#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>
#include <compat/ndis/usbd_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(vm_offset_t img, const char *name)
{
	struct drvdb_ent	*d;
	unicode_string		us;

	printf("In windrv_lookup():\n");
	printf("name = %s\n", name);
	
	/* 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);
	} else {
		us.us_len = 0;
		us.us_maxlen = 0;
		us.us_buf = NULL;
	}

	mtx_lock(&drvdb_mtx); 
	STAILQ_FOREACH(d, &drvdb_head, link) {
#ifdef NDIS_DBG		
		printf("d->windrv_object->dro_driverstart = %x\n", d->windrv_object->dro_driverstart);
#endif		
		if (d->windrv_object->dro_driverstart == (void *)img ||	
		    (memcmp((char *)d->windrv_object->dro_drivername.us_buf,
			 (char *)us.us_buf, us.us_len) == 0 && us.us_len > 0)) {		
			mtx_unlock(&drvdb_mtx);		
			printf("found driver object!\n");
#ifdef NDIS_DBG
			printf("returning %x\n", d->windrv_object);
#endif				
			return(d->windrv_object);
		}
	}
	mtx_unlock(&drvdb_mtx);

	if (name != NULL)
		ExFreePool(us.us_buf);

	printf("no driver object\n");
	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(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(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	*drv;
	int			status;

#ifdef NDIS_DBG
	printf("in windrv_load\n");
	printf("img = %x\n", img);
#endif	

	/*
	 * 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 ubsimplemented
		if (pe_patch_imports(img, "USBD", usbd_functbl)) {	
			return(ENOEXEC);
		}
#else
	    //printf("windrv_load: pe_get_import_descriptor USBD failed");	
            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);

	drv = malloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO);
	if (drv == NULL) {
		free (new, M_DEVBUF);
		return (ENOMEM);
	}

	/* Allocate a driver extension structure too. */

	drv->dro_driverext = malloc(sizeof(driver_extension),
	    M_DEVBUF, M_NOWAIT|M_ZERO);

	if (drv->dro_driverext == NULL) {
		free(new, M_DEVBUF);
		free(drv, M_DEVBUF);
		return(ENOMEM);
	}

	INIT_LIST_HEAD((&drv->dro_driverext->dre_usrext));

	drv->dro_driverstart = (void *)img;
	drv->dro_driversize = len;

	drv->dro_drivername.us_len = strlen(DUMMY_REGISTRY_PATH) * 2;
        drv->dro_drivername.us_maxlen = strlen(DUMMY_REGISTRY_PATH) * 2;
        drv->dro_drivername.us_buf = NULL;
        ndis_ascii_to_unicode(DUMMY_REGISTRY_PATH,
	    &drv->dro_drivername.us_buf);

	new->windrv_object = drv;

	/* Now call the DriverEntry() function. */

	status = MSCALL2(entry, drv, &drv->dro_drivername);

	if (status != STATUS_SUCCESS) {
		free(drv->dro_drivername.us_buf, M_DEVBUF);
		free(drv, 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(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(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(driver_object *drv, device_t bsddev)
{
	device_object		*pdo;
#ifdef NDIS_DBG
	printf("In windrv_find_pdo: \ndrv = %x", drv);
	printf("\nbsddev = %x", bsddev);
	printf("\npdo = %x", drv->dro_devobj);
	printf("\npdo->do_devext = %x\n", drv->dro_devobj->do_devext);
#endif
	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(driver_object *drv, const 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);
		
#ifdef __NetBSD__
/* I added this because windrv_lookup was getting 
 * fake_pccard_driver and fake_pci_driver mixed up.
 * I'm not sure if it will mess anything else up.
 */
	drv->dro_driverstart = drv;
#endif		

	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(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. */

	memcpy( p, (char *)wrapstart, (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(funcptr func)
{
#ifdef __amd64__
	free(func, M_DEVBUF);
#endif /* __amd64__ */
	return(0);
}