freebsd-nq/sys/compat/ndis/kern_windrv.c

967 lines
23 KiB
C
Raw Normal View History

Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/*-
* 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>
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/smp.h>
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
#include <sys/queue.h>
#ifdef __i386__
#include <machine/segments.h>
#endif
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
#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>
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
#include <compat/ndis/usbd_var.h>
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
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;
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
#ifdef __i386__
static void x86_oldldt(void *);
static void x86_newldt(void *);
struct tid {
void *tid_except_list; /* 0x00 */
uint32_t tid_oldfs; /* 0x04 */
uint32_t tid_selector; /* 0x08 */
struct tid *tid_self; /* 0x0C */
int tid_cpu; /* 0x10 */
};
static struct tid *my_tids;
#endif /* __i386__ */
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
#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");
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
#ifdef __i386__
/*
* In order to properly support SMP machines, we have
* to modify the GDT on each CPU, since we never know
* on which one we'll end up running.
*/
my_tids = ExAllocatePoolWithTag(NonPagedPool,
sizeof(struct tid) * mp_ncpus, 0);
if (my_tids == NULL)
panic("failed to allocate thread info blocks");
smp_rendezvous(NULL, x86_newldt, NULL, NULL);
#endif
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
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);
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
RtlFreeUnicodeString(&fake_pci_driver.dro_drivername);
RtlFreeUnicodeString(&fake_pccard_driver.dro_drivername);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
mtx_destroy(&drvdb_mtx);
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
#ifdef __i386__
smp_rendezvous(NULL, x86_oldldt, NULL, NULL);
ExFreePool(my_tids);
#endif
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
return(0);
}
/*
* Given the address of a driver image, find its corresponding
* driver_object.
*/
driver_object *
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
windrv_lookup(img, name)
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
vm_offset_t img;
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
char *name;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
{
struct drvdb_ent *d;
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
unicode_string us;
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
ansi_string as;
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
bzero((char *)&us, sizeof(us));
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
/* Damn unicode. */
if (name != NULL) {
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
RtlInitAnsiString(&as, name);
if (RtlAnsiStringToUnicodeString(&us, &as, TRUE))
return(NULL);
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
}
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
mtx_lock(&drvdb_mtx);
STAILQ_FOREACH(d, &drvdb_head, link) {
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
if (d->windrv_object->dro_driverstart == (void *)img ||
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
(bcmp((char *)d->windrv_object->dro_drivername.us_buf,
(char *)us.us_buf, us.us_len) == 0 && us.us_len)) {
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
mtx_unlock(&drvdb_mtx);
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
if (name != NULL)
ExFreePool(us.us_buf);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
return(d->windrv_object);
}
}
mtx_unlock(&drvdb_mtx);
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
if (name != NULL)
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
RtlFreeUnicodeString(&us);
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
return(NULL);
}
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
struct drvdb_ent *
windrv_match(matchfunc, ctx)
matchfuncptr matchfunc;
void *ctx;
{
struct drvdb_ent *d;
int match;
mtx_lock(&drvdb_mtx);
STAILQ_FOREACH(d, &drvdb_head, link) {
if (d->windrv_devlist == NULL)
continue;
match = matchfunc(d->windrv_bustype, d->windrv_devlist, ctx);
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
if (match == TRUE) {
mtx_unlock(&drvdb_mtx);
return(d);
}
}
mtx_unlock(&drvdb_mtx);
return(NULL);
}
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/*
* 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;
{
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
struct drvdb_ent *db, *r = NULL;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
driver_object *drv;
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
device_object *d, *pdo;
device_t dev;
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
list_entry *e;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
drv = windrv_lookup(img, NULL);
/*
* When we unload a driver image, we need to force a
* detach of any devices that might be using it. We
* need the PDOs of all attached devices for this.
* Getting at them is a little hard. We basically
* have to walk the device lists of all our bus
* drivers.
*/
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
mtx_lock(&drvdb_mtx);
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
STAILQ_FOREACH(db, &drvdb_head, link) {
/*
* Fake bus drivers have no devlist info.
* If this driver has devlist info, it's
* a loaded Windows driver and has no PDOs,
* so skip it.
*/
if (db->windrv_devlist != NULL)
continue;
pdo = db->windrv_object->dro_devobj;
while (pdo != NULL) {
d = pdo->do_attacheddev;
if (d->do_drvobj != drv) {
pdo = pdo->do_nextdev;
continue;
}
dev = pdo->do_devext;
pdo = pdo->do_nextdev;
mtx_unlock(&drvdb_mtx);
device_detach(dev);
mtx_lock(&drvdb_mtx);
}
}
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
STAILQ_FOREACH(db, &drvdb_head, link) {
if (db->windrv_object->dro_driverstart == (void *)img) {
r = db;
STAILQ_REMOVE(&drvdb_head, db, drvdb_ent, link);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
break;
}
}
mtx_unlock(&drvdb_mtx);
if (r == NULL)
return (ENOENT);
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
if (drv == NULL)
return(ENOENT);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/*
* Destroy any custom extensions that may have been added.
*/
drv = r->windrv_object;
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
while (!IsListEmpty(&drv->dro_driverext->dre_usrext)) {
e = RemoveHeadList(&drv->dro_driverext->dre_usrext);
ExFreePool(e);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
}
/* Free the driver extension */
free(drv->dro_driverext, M_DEVBUF);
/* Free the driver name */
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
RtlFreeUnicodeString(&drv->dro_drivername);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/* Free driver object */
free(drv, M_DEVBUF);
/* Free our DB handle */
free(r, M_DEVBUF);
return(0);
}
#define WINDRV_LOADED htonl(0x42534F44)
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/*
* Loader routine for actual Windows driver modules, ultimately
* calls the driver's DriverEntry() routine.
*/
int
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
windrv_load(mod, img, len, bustype, devlist, regvals)
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
module_t mod;
vm_offset_t img;
int len;
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
interface_type bustype;
void *devlist;
ndis_cfg *regvals;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
{
image_import_descriptor imp_desc;
image_optional_header opt_hdr;
driver_entry entry;
struct drvdb_ent *new;
struct driver_object *drv;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
int status;
uint32_t *ptr;
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
ansi_string as;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/*
* First step: try to relocate and dynalink the executable
* driver image.
*/
ptr = (uint32_t *)(img + 8);
if (*ptr == WINDRV_LOADED)
goto skipreloc;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/* 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) {
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
if (pe_patch_imports(img, "USBD", usbd_functbl))
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
return(ENOEXEC);
}
*ptr = WINDRV_LOADED;
skipreloc:
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/* 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. */
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
if (new == NULL)
return (ENOMEM);
drv = malloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO);
if (drv == NULL) {
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
free (new, M_DEVBUF);
return (ENOMEM);
}
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/* Allocate a driver extension structure too. */
drv->dro_driverext = malloc(sizeof(driver_extension),
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
M_DEVBUF, M_NOWAIT|M_ZERO);
if (drv->dro_driverext == NULL) {
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
free(new, M_DEVBUF);
free(drv, M_DEVBUF);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
return(ENOMEM);
}
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
InitializeListHead((&drv->dro_driverext->dre_usrext));
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
drv->dro_driverstart = (void *)img;
drv->dro_driversize = len;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
RtlInitAnsiString(&as, DUMMY_REGISTRY_PATH);
if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) {
free(new, M_DEVBUF);
free(drv, M_DEVBUF);
return(ENOMEM);
}
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
new->windrv_object = drv;
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
new->windrv_regvals = regvals;
new->windrv_devlist = devlist;
new->windrv_bustype = bustype;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
/* Now call the DriverEntry() function. */
status = MSCALL2(entry, drv, &drv->dro_drivername);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
if (status != STATUS_SUCCESS) {
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
RtlFreeUnicodeString(&drv->dro_drivername);
free(drv, M_DEVBUF);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
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;
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
while (pdo != NULL) {
if (pdo->do_devext == bsddev) {
mtx_unlock(&drvdb_mtx);
return(pdo);
}
pdo = pdo->do_nextdev;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
}
mtx_unlock(&drvdb_mtx);
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
return(NULL);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
}
/*
* 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;
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
ansi_string as;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
if (new == NULL)
return (ENOMEM);
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
RtlInitAnsiString(&as, name);
if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE))
return(ENOMEM);
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
/*
* Set up a fake image pointer to avoid false matches
* in windrv_lookup().
*/
drv->dro_driverstart = (void *)0xFFFFFFFF;
Next step on the road to IRPs: create and use an imitation of the Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can simulate driver stacking. In Windows, each loaded driver image is attached to a DRIVER_OBJECT structure. Windows uses the registry to match up a given vendor/device ID combination with a corresponding DRIVER_OBJECT. When a driver image is first loaded, its DriverEntry() routine is invoked, which sets up the AddDevice() function pointer in the DRIVER_OBJECT and creates a dispatch table (based on IRP major codes). When a Windows bus driver detects a new device, it creates a Physical Device Object (PDO) for it. This is a DEVICE_OBJECT structure, with semantics analagous to that of a device_t in FreeBSD. The Windows PNP manager will invoke the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT and the PDO. The AddDevice() function then creates a new DRIVER_OBJECT structure of its own. This is known as the Functional Device Object (FDO) and corresponds roughly to a private softc instance. The driver uses IoAttachDeviceToDeviceStack() to add this device object to the driver stack for this PDO. Subsequent drivers (called filter drivers in Windows-speak) can be loaded which add themselves to the stack. When someone issues an IRP to a device, it travel along the stack passing through several possible filter drivers until it reaches the functional driver (which actually knows how to talk to the hardware) at which point it will be completed. This is how Windows achieves driver layering. Project Evil now simulates most of this. if_ndis now has a modevent handler which will use MOD_LOAD and MOD_UNLOAD events to drive the creation and destruction of DRIVER_OBJECTs. (The load event also does the relocation/dynalinking of the image.) We don't have a registry, so the DRIVER_OBJECTS are stored in a linked list for now. Eventually, the list entry will contain the vendor/device ID list extracted from the .INF file. When ndis_probe() is called and detectes a supported device, it will create a PDO for the device instance and attach it to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call our NdisAddDevice() handler to create the FDO. The NDIS miniport block is now a device extension hung off the FDO, just as it is in Windows. The miniport characteristics table is now an extension hung off the DRIVER_OBJECT as well (the characteristics are the same for all devices handled by a given driver, so they don't need to be per-instance.) We also do an IoAttachDeviceToDeviceStack() to put the FDO on the stack for the PDO. There are a couple of fake bus drivers created for the PCI and pccard buses. Eventually, there will be one for USB, which will actually accept USB IRP.s Things should still work just as before, only now we do things in the proper order and maintain the correct framework to support passing IRPs between drivers. Various changes: - corrected the comments about IRQL handling in subr_hal.c to more accurately reflect reality - update ndiscvt to make the drv_data symbol in ndis_driver_data.h a global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it. - Obtain the softc pointer from the miniport block by referencing the PDO rather than a private pointer of our own (nmb_ifp is no longer used) - implement IoAttachDeviceToDeviceStack(), IoDetachDevice(), IoGetAttachedDevice(), IoAllocateDriverObjectExtension(), IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(), IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(), IoInitializeIrp() - fix a few mistakes in the driver_object and device_object definitions - add a new module, kern_windrv.c, to handle the driver registration and relocation/dynalinkign duties (which don't really belong in kern_ndis.c). - made ndis_block and ndis_chars in the ndis_softc stucture pointers and modified all references to it - fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they work correctly with the new driver_object mechanism - changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver() (which is now deprecated) - used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines instead of kludged up alloc/free routines - added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
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);
}
Add support for Windows/x86-64 binaries to Project Evil. Ville-Pertti Keinonen (will at exomi dot comohmygodnospampleasekthx) deserves a big thanks for submitting initial patches to make it work. I have mangled his contributions appropriately. The main gotcha with Windows/x86-64 is that Microsoft uses a different calling convention than everyone else. The standard ABI requires using 6 registers for argument passing, with other arguments on the stack. Microsoft uses only 4 registers, and requires the caller to leave room on the stack for the register arguments incase the callee needs to spill them. Unlike x86, where Microsoft uses a mix of _cdecl, _stdcall and _fastcall, all routines on Windows/x86-64 uses the same convention. This unfortunately means that all the functions we export to the driver require an intermediate translation wrapper. Similarly, we have to wrap all calls back into the driver binary itself. The original patches provided macros to wrap every single routine at compile time, providing a secondary jump table with a customized wrapper for each exported routine. I decided to use a different approach: the call wrapper for each function is created from a template at runtime, and the routine to jump to is patched into the wrapper as it is created. The subr_pe module has been modified to patch in the wrapped function instead of the original. (On x86, the wrapping routine is a no-op.) There are some minor API differences that had to be accounted for: - KeAcquireSpinLock() is a real function on amd64, not a macro wrapper around KfAcquireSpinLock() - NdisFreeBuffer() is actually IoFreeMdl(). I had to change the whole NDIS_BUFFER API a bit to accomodate this. Bugs fixed along the way: - IoAllocateMdl() always returned NULL - kern_windrv.c:windrv_unload() wasn't releasing private driver object extensions correctly (found thanks to memguard) This has only been tested with the driver for the Broadcom 802.11g chipset, which was the only Windows/x86-64 driver I could find.
2005-02-16 05:41:18 +00:00
#ifdef __amd64__
extern void x86_64_wrap(void);
extern void x86_64_wrap_call(void);
extern void x86_64_wrap_end(void);
int
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
windrv_wrap(func, wrap, argcnt, ftype)
Add support for Windows/x86-64 binaries to Project Evil. Ville-Pertti Keinonen (will at exomi dot comohmygodnospampleasekthx) deserves a big thanks for submitting initial patches to make it work. I have mangled his contributions appropriately. The main gotcha with Windows/x86-64 is that Microsoft uses a different calling convention than everyone else. The standard ABI requires using 6 registers for argument passing, with other arguments on the stack. Microsoft uses only 4 registers, and requires the caller to leave room on the stack for the register arguments incase the callee needs to spill them. Unlike x86, where Microsoft uses a mix of _cdecl, _stdcall and _fastcall, all routines on Windows/x86-64 uses the same convention. This unfortunately means that all the functions we export to the driver require an intermediate translation wrapper. Similarly, we have to wrap all calls back into the driver binary itself. The original patches provided macros to wrap every single routine at compile time, providing a secondary jump table with a customized wrapper for each exported routine. I decided to use a different approach: the call wrapper for each function is created from a template at runtime, and the routine to jump to is patched into the wrapper as it is created. The subr_pe module has been modified to patch in the wrapped function instead of the original. (On x86, the wrapping routine is a no-op.) There are some minor API differences that had to be accounted for: - KeAcquireSpinLock() is a real function on amd64, not a macro wrapper around KfAcquireSpinLock() - NdisFreeBuffer() is actually IoFreeMdl(). I had to change the whole NDIS_BUFFER API a bit to accomodate this. Bugs fixed along the way: - IoAllocateMdl() always returned NULL - kern_windrv.c:windrv_unload() wasn't releasing private driver object extensions correctly (found thanks to memguard) This has only been tested with the driver for the Broadcom 802.11g chipset, which was the only Windows/x86-64 driver I could find.
2005-02-16 05:41:18 +00:00
funcptr func;
funcptr *wrap;
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
int argcnt;
int ftype;
Add support for Windows/x86-64 binaries to Project Evil. Ville-Pertti Keinonen (will at exomi dot comohmygodnospampleasekthx) deserves a big thanks for submitting initial patches to make it work. I have mangled his contributions appropriately. The main gotcha with Windows/x86-64 is that Microsoft uses a different calling convention than everyone else. The standard ABI requires using 6 registers for argument passing, with other arguments on the stack. Microsoft uses only 4 registers, and requires the caller to leave room on the stack for the register arguments incase the callee needs to spill them. Unlike x86, where Microsoft uses a mix of _cdecl, _stdcall and _fastcall, all routines on Windows/x86-64 uses the same convention. This unfortunately means that all the functions we export to the driver require an intermediate translation wrapper. Similarly, we have to wrap all calls back into the driver binary itself. The original patches provided macros to wrap every single routine at compile time, providing a secondary jump table with a customized wrapper for each exported routine. I decided to use a different approach: the call wrapper for each function is created from a template at runtime, and the routine to jump to is patched into the wrapper as it is created. The subr_pe module has been modified to patch in the wrapped function instead of the original. (On x86, the wrapping routine is a no-op.) There are some minor API differences that had to be accounted for: - KeAcquireSpinLock() is a real function on amd64, not a macro wrapper around KfAcquireSpinLock() - NdisFreeBuffer() is actually IoFreeMdl(). I had to change the whole NDIS_BUFFER API a bit to accomodate this. Bugs fixed along the way: - IoAllocateMdl() always returned NULL - kern_windrv.c:windrv_unload() wasn't releasing private driver object extensions correctly (found thanks to memguard) This has only been tested with the driver for the Broadcom 802.11g chipset, which was the only Windows/x86-64 driver I could find.
2005-02-16 05:41:18 +00:00
{
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;
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
Add support for Windows/x86-64 binaries to Project Evil. Ville-Pertti Keinonen (will at exomi dot comohmygodnospampleasekthx) deserves a big thanks for submitting initial patches to make it work. I have mangled his contributions appropriately. The main gotcha with Windows/x86-64 is that Microsoft uses a different calling convention than everyone else. The standard ABI requires using 6 registers for argument passing, with other arguments on the stack. Microsoft uses only 4 registers, and requires the caller to leave room on the stack for the register arguments incase the callee needs to spill them. Unlike x86, where Microsoft uses a mix of _cdecl, _stdcall and _fastcall, all routines on Windows/x86-64 uses the same convention. This unfortunately means that all the functions we export to the driver require an intermediate translation wrapper. Similarly, we have to wrap all calls back into the driver binary itself. The original patches provided macros to wrap every single routine at compile time, providing a secondary jump table with a customized wrapper for each exported routine. I decided to use a different approach: the call wrapper for each function is created from a template at runtime, and the routine to jump to is patched into the wrapper as it is created. The subr_pe module has been modified to patch in the wrapped function instead of the original. (On x86, the wrapping routine is a no-op.) There are some minor API differences that had to be accounted for: - KeAcquireSpinLock() is a real function on amd64, not a macro wrapper around KfAcquireSpinLock() - NdisFreeBuffer() is actually IoFreeMdl(). I had to change the whole NDIS_BUFFER API a bit to accomodate this. Bugs fixed along the way: - IoAllocateMdl() always returned NULL - kern_windrv.c:windrv_unload() wasn't releasing private driver object extensions correctly (found thanks to memguard) This has only been tested with the driver for the Broadcom 802.11g chipset, which was the only Windows/x86-64 driver I could find.
2005-02-16 05:41:18 +00:00
return(0);
}
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
#endif /* __amd64__ */
#ifdef __i386__
struct x86desc {
uint16_t x_lolimit;
uint16_t x_base0;
uint8_t x_base1;
uint8_t x_flags;
uint8_t x_hilimit;
uint8_t x_base2;
};
struct gdt {
uint16_t limit;
void *base;
} __attribute__((__packed__));
extern uint16_t x86_getfs(void);
extern void x86_setfs(uint16_t);
extern void *x86_gettid(void);
extern void x86_critical_enter(void);
extern void x86_critical_exit(void);
extern void x86_getldt(struct gdt *, uint16_t *);
extern void x86_setldt(struct gdt *, uint16_t);
#define SEL_LDT 4 /* local descriptor table */
#define SEL_TO_FS(x) (((x) << 3))
/*
* FreeBSD 6.0 and later has a special GDT segment reserved
* specifically for us, so if GNDIS_SEL is defined, use that.
* If not, use GTGATE_SEL, which is uninitialized and infrequently
* used.
*/
#ifdef GNDIS_SEL
#define FREEBSD_EMPTYSEL GNDIS_SEL
#else
#define FREEBSD_EMPTYSEL GTGATE_SEL /* slot 7 */
#endif
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
/*
* The meanings of various bits in a descriptor vary a little
* depending on whether the descriptor will be used as a
* code, data or system descriptor. (And that in turn depends
* on which segment register selects the descriptor.)
* We're only trying to create a data segment, so the definitions
* below are the ones that apply to a data descriptor.
*/
#define SEGFLAGLO_PRESENT 0x80 /* segment is present */
#define SEGFLAGLO_PRIVLVL 0x60 /* privlevel needed for this seg */
#define SEGFLAGLO_CD 0x10 /* 1 = code/data, 0 = system */
#define SEGFLAGLO_MBZ 0x08 /* must be zero */
#define SEGFLAGLO_EXPANDDOWN 0x04 /* limit expands down */
#define SEGFLAGLO_WRITEABLE 0x02 /* segment is writeable */
#define SEGGLAGLO_ACCESSED 0x01 /* segment has been accessed */
#define SEGFLAGHI_GRAN 0x80 /* granularity, 1 = byte, 0 = page */
#define SEGFLAGHI_BIG 0x40 /* 1 = 32 bit stack, 0 = 16 bit */
/*
* Context switch from UNIX to Windows. Save the existing value
* of %fs for this processor, then change it to point to our
* fake TID. Note that it is also possible to pin ourselves
* to our current CPU, though I'm not sure this is really
* necessary. It depends on whether or not an interrupt might
* preempt us while Windows code is running and we wind up
* scheduled onto another CPU as a result. So far, it doesn't
* seem like this is what happens.
*/
void
ctxsw_utow(void)
{
struct tid *t;
t = &my_tids[curthread->td_oncpu];
/*
* Ugly hack. During system bootstrap (cold == 1), only CPU 0
* is running. So if we were loaded at bootstrap, only CPU 0
* will have our special GDT entry. This is a problem for SMP
* systems, so to deal with this, we check here to make sure
* the TID for this processor has been initialized, and if it
* hasn't, we need to do it right now or else things will
* explode.
*/
This commit makes a big round of updates and fixes many, many things. First and most importantly, I threw out the thread priority-twiddling implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in favor of a new scheme that uses sleep mutexes. The old scheme was really very naughty and sought to provide the same behavior as Windows spinlocks (i.e. blocking pre-emption) but in a way that wouldn't raise the ire of WITNESS. The new scheme represents 'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If a thread on cpu0 acquires the 'dispatcher mutex,' it will block any other thread on the same processor that tries to acquire it, in effect only allowing one thread on the processor to be at 'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit and spin' routine on the spinlock variable itself. If a thread on cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher mutex' for cpu1 and then it too does an atomic sit and spin to try acquiring the spinlock. Unlike real spinlocks, this does not disable pre-emption of all threads on the CPU, but it does put any threads involved with the NDISulator to sleep, which is just as good for our purposes. This means I can now play nice with WITNESS, and I can safely do things like call malloc() when I'm at 'DISPATCH_LEVEL,' which you're allowed to do in Windows. Next, I completely re-wrote most of the event/timer/mutex handling and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects() have been re-written to use condition variables instead of msleep(). This allows us to use the Windows convention whereby thread A can tell thread B "wake up with a boosted priority." (With msleep(), you instead have thread B saying "when I get woken up, I'll use this priority here," and thread A can't tell it to do otherwise.) The new KeWaitForMultipleObjects() has been better tested and better duplicates the semantics of its Windows counterpart. I also overhauled the IoQueueWorkItem() API and underlying code. Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the same work item isn't put on the queue twice. ExQueueWorkItem(), which in my implementation is built on top of IoQueueWorkItem(), was also modified to perform a similar test. I renamed the doubly-linked list macros to give them the same names as their Windows counterparts and fixed RemoveListTail() and RemoveListHead() so they properly return the removed item. I also corrected the list handling code in ntoskrnl_dpc_thread() and ntoskrnl_workitem_thread(). I realized that the original logic did not correctly handle the case where a DPC callout tries to queue up another DPC. It works correctly now. I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to use them. I also tried to duplicate the interrupt handling scheme used in Windows. The interrupt handling is now internal to ndis.ko, and the ndis_intr() function has been removed from if_ndis.c. (In the USB case, interrupt handling isn't needed in if_ndis.c anyway.) NdisMSleep() has been rewritten to use a KeWaitForSingleObject() and a KeTimer, which is how it works in Windows. (This is mainly to insure that the NDISulator uses the KeTimer API so I can spot any problems with it that may arise.) KeCancelTimer() has been changed so that it only cancels timers, and does not attempt to cancel a DPC if the timer managed to fire and queue one up before KeCancelTimer() was called. The Windows DDK documentation seems to imply that KeCantelTimer() will also call KeRemoveQueueDpc() if necessary, but it really doesn't. The KeTimer implementation has been rewritten to use the callout API directly instead of timeout()/untimeout(). I still cheat a little in that I have to manage my own small callout timer wheel, but the timer code works more smoothly now. I discovered a race condition using timeout()/untimeout() with periodic timers where untimeout() fails to actually cancel a timer. I don't quite understand where the race is, using callout_init()/callout_reset()/callout_stop() directly seems to fix it. I also discovered and fixed a bug in winx32_wrap.S related to translating _stdcall calls. There are a couple of routines (i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that return 64-bit quantities. On the x86 arch, 64-bit values are returned in the %eax and %edx registers. However, it happens that the ctxsw_utow() routine uses %edx as a scratch register, and x86_stdcall_wrap() and x86_stdcall_call() were only preserving %eax before branching to ctxsw_utow(). This means %edx was getting clobbered in some cases. Curiously, the most noticeable effect of this bug is that the driver for the TI AXC110 chipset would constantly drop and reacquire its link for no apparent reason. Both %eax and %edx are preserved on the stack now. The _fastcall and _regparm wrappers already handled everything correctly. I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem() instead of the NdisScheduleWorkItem() API. This is to avoid possible deadlocks with any drivers that use NdisScheduleWorkItem() themselves. The unicode/ansi conversion handling code has been cleaned up. The internal routines have been moved to subr_ntoskrnl and the RtlXXX routines have been exported so that subr_ndis can call them. This removes the incestuous relationship between the two modules regarding this code and fixes the implementation so that it honors the 'maxlen' fields correctly. (Previously it was possible for NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't own, which was causing many mysterious crashes in the Marvell 8335 driver.) The registry handling code (NdisOpen/Close/ReadConfiguration()) has been fixed to allocate memory for all the parameters it hands out to callers and delete whem when NdisCloseConfiguration() is called. (Previously, it would secretly use a single static buffer.) I also substantially updated if_ndis so that the source can now be built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled. The original WPA code has been updated to fit in more cleanly with the net80211 API, and to eleminate the use of magic numbers. The ndis_80211_setstate() routine now sets a default authmode of OPEN and initializes the RTS threshold and fragmentation threshold. The WPA routines were changed so that the authentication mode is always set first, followed by the cipher. Some drivers depend on the operations being performed in this order. I also added passthrough ioctls that allow application code to directly call the MiniportSetInformation()/MiniportQueryInformation() methods via ndis_set_info() and ndis_get_info(). The ndis_linksts() routine also caches the last 4 events signalled by the driver via NdisMIndicateStatus(), and they can be queried by an application via a separate ioctl. This is done to allow wpa_supplicant to directly program the various crypto and key management options in the driver, allowing things like WPA2 support to work. Whew.
2005-10-10 16:46:39 +00:00
if (t->tid_self != t)
x86_newldt(NULL);
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
t->tid_oldfs = x86_getfs();
t->tid_cpu = curthread->td_oncpu;
x86_setfs(SEL_TO_FS(t->tid_selector));
/* Now entering Windows land, population: you. */
return;
}
/*
* Context switch from Windows back to UNIX. Restore %fs to
* its previous value. This always occurs after a call to
* ctxsw_utow().
*/
void
ctxsw_wtou(void)
{
struct tid *t;
t = x86_gettid();
x86_setfs(t->tid_oldfs);
/* Welcome back to UNIX land, we missed you. */
#ifdef EXTRA_SANITY
if (t->tid_cpu != curthread->td_oncpu)
Throw the switch on the new driver generation/loading mechanism. From here on in, if_ndis.ko will be pre-built as a module, and can be built into a static kernel (though it's not part of GENERIC). Drivers are created using the new ndisgen(8) script, which uses ndiscvt(8) under the covers, along with a few other tools. The result is a driver module that can be kldloaded into the kernel. A driver with foo.inf and foo.sys files will be converted into foo_sys.ko (and foo_sys.o, for those who want/need to make static kernels). This module contains all of the necessary info from the .INF file and the driver binary image, converted into an ELF module. You can kldload this module (or add it to /boot/loader.conf) to have it loaded automatically. Any required firmware files can be bundled into the module as well (or converted/loaded separately). Also, add a workaround for a problem in NdisMSleep(). During system bootstrap (cold == 1), msleep() always returns 0 without actually sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for the NIC's firmware to come to life, and fails to load if NdisMSleep() doesn't actually delay. As a workaround, if msleep() (and hence ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead). This is not really the right thing to do, but we can't really do much else. At the very least, this makes the Intel driver happy. There are probably other drivers that fail in this way during bootstrap. Unfortunately, the only workaround for those is to avoid pre-loading them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
panic("ctxsw GOT MOVED TO OTHER CPU!");
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
#endif
return;
}
static int windrv_wrap_stdcall(funcptr, funcptr *, int);
static int windrv_wrap_fastcall(funcptr, funcptr *, int);
static int windrv_wrap_regparm(funcptr, funcptr *);
extern void x86_fastcall_wrap(void);
extern void x86_fastcall_wrap_call(void);
extern void x86_fastcall_wrap_arg(void);
extern void x86_fastcall_wrap_end(void);
static int
windrv_wrap_fastcall(func, wrap, argcnt)
funcptr func;
funcptr *wrap;
int8_t argcnt;
{
funcptr p;
vm_offset_t *calladdr;
uint8_t *argaddr;
vm_offset_t wrapstart, wrapend, wrapcall, wraparg;
wrapstart = (vm_offset_t)&x86_fastcall_wrap;
wrapend = (vm_offset_t)&x86_fastcall_wrap_end;
wrapcall = (vm_offset_t)&x86_fastcall_wrap_call;
wraparg = (vm_offset_t)&x86_fastcall_wrap_arg;
/* 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 = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
*calladdr = (vm_offset_t)func;
argcnt -= 2;
if (argcnt < 1)
argcnt = 0;
argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1));
*argaddr = argcnt * sizeof(uint32_t);
*wrap = p;
return(0);
}
extern void x86_stdcall_wrap(void);
extern void x86_stdcall_wrap_call(void);
extern void x86_stdcall_wrap_arg(void);
extern void x86_stdcall_wrap_end(void);
static int
windrv_wrap_stdcall(func, wrap, argcnt)
funcptr func;
funcptr *wrap;
uint8_t argcnt;
{
funcptr p;
vm_offset_t *calladdr;
uint8_t *argaddr;
vm_offset_t wrapstart, wrapend, wrapcall, wraparg;
wrapstart = (vm_offset_t)&x86_stdcall_wrap;
wrapend = (vm_offset_t)&x86_stdcall_wrap_end;
wrapcall = (vm_offset_t)&x86_stdcall_wrap_call;
wraparg = (vm_offset_t)&x86_stdcall_wrap_arg;
/* 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 = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
*calladdr = (vm_offset_t)func;
argaddr = (u_int8_t *)((char *)p + ((wraparg - wrapstart) + 1));
*argaddr = argcnt * sizeof(uint32_t);
*wrap = p;
return(0);
}
extern void x86_regparm_wrap(void);
extern void x86_regparm_wrap_call(void);
extern void x86_regparm_wrap_end(void);
static int
windrv_wrap_regparm(func, wrap)
funcptr func;
funcptr *wrap;
{
funcptr p;
vm_offset_t *calladdr;
vm_offset_t wrapstart, wrapend, wrapcall;
wrapstart = (vm_offset_t)&x86_regparm_wrap;
wrapend = (vm_offset_t)&x86_regparm_wrap_end;
wrapcall = (vm_offset_t)&x86_regparm_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(x86_regparm_wrap, p, (wrapend - wrapstart));
/* Insert the function address into the new wrapper instance. */
calladdr = (vm_offset_t *)((char *)p + ((wrapcall - wrapstart) + 1));
*calladdr = (vm_offset_t)func;
*wrap = p;
return(0);
}
int
windrv_wrap(func, wrap, argcnt, ftype)
funcptr func;
funcptr *wrap;
int argcnt;
int ftype;
{
switch(ftype) {
case WINDRV_WRAP_FASTCALL:
return(windrv_wrap_fastcall(func, wrap, argcnt));
case WINDRV_WRAP_STDCALL:
return(windrv_wrap_stdcall(func, wrap, argcnt));
case WINDRV_WRAP_REGPARM:
return(windrv_wrap_regparm(func, wrap));
case WINDRV_WRAP_CDECL:
return(windrv_wrap_stdcall(func, wrap, 0));
default:
break;
}
return(EINVAL);
}
static void
x86_oldldt(dummy)
void *dummy;
{
struct thread *t;
struct x86desc *gdt;
struct gdt gtable;
uint16_t ltable;
mtx_lock_spin(&sched_lock);
t = curthread;
/* Grab location of existing GDT. */
x86_getldt(&gtable, &ltable);
/* Find the slot we updated. */
gdt = gtable.base;
gdt += FREEBSD_EMPTYSEL;
/* Empty it out. */
bzero((char *)gdt, sizeof(struct x86desc));
/* Restore GDT. */
x86_setldt(&gtable, ltable);
mtx_unlock_spin(&sched_lock);
return;
}
static void
x86_newldt(dummy)
void *dummy;
{
struct gdt gtable;
uint16_t ltable;
struct x86desc *l;
struct thread *t;
mtx_lock_spin(&sched_lock);
t = curthread;
/* Grab location of existing GDT. */
x86_getldt(&gtable, &ltable);
/* Get pointer to the GDT table. */
l = gtable.base;
/* Get pointer to empty slot */
l += FREEBSD_EMPTYSEL;
/* Initialize TID for this CPU. */
my_tids[t->td_oncpu].tid_selector = FREEBSD_EMPTYSEL;
my_tids[t->td_oncpu].tid_self = &my_tids[t->td_oncpu];
/* Set up new GDT entry. */
l->x_lolimit = sizeof(struct tid);
l->x_hilimit = SEGFLAGHI_GRAN|SEGFLAGHI_BIG;
l->x_base0 = (vm_offset_t)(&my_tids[t->td_oncpu]) & 0xFFFF;
l->x_base1 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 16) & 0xFF;
l->x_base2 = ((vm_offset_t)(&my_tids[t->td_oncpu]) >> 24) & 0xFF;
l->x_flags = SEGFLAGLO_PRESENT|SEGFLAGLO_CD|SEGFLAGLO_WRITEABLE;
/* Update the GDT. */
x86_setldt(&gtable, ltable);
mtx_unlock_spin(&sched_lock);
/* Whew. */
return;
}
#endif /* __i386__ */
Add support for Windows/x86-64 binaries to Project Evil. Ville-Pertti Keinonen (will at exomi dot comohmygodnospampleasekthx) deserves a big thanks for submitting initial patches to make it work. I have mangled his contributions appropriately. The main gotcha with Windows/x86-64 is that Microsoft uses a different calling convention than everyone else. The standard ABI requires using 6 registers for argument passing, with other arguments on the stack. Microsoft uses only 4 registers, and requires the caller to leave room on the stack for the register arguments incase the callee needs to spill them. Unlike x86, where Microsoft uses a mix of _cdecl, _stdcall and _fastcall, all routines on Windows/x86-64 uses the same convention. This unfortunately means that all the functions we export to the driver require an intermediate translation wrapper. Similarly, we have to wrap all calls back into the driver binary itself. The original patches provided macros to wrap every single routine at compile time, providing a secondary jump table with a customized wrapper for each exported routine. I decided to use a different approach: the call wrapper for each function is created from a template at runtime, and the routine to jump to is patched into the wrapper as it is created. The subr_pe module has been modified to patch in the wrapped function instead of the original. (On x86, the wrapping routine is a no-op.) There are some minor API differences that had to be accounted for: - KeAcquireSpinLock() is a real function on amd64, not a macro wrapper around KfAcquireSpinLock() - NdisFreeBuffer() is actually IoFreeMdl(). I had to change the whole NDIS_BUFFER API a bit to accomodate this. Bugs fixed along the way: - IoAllocateMdl() always returned NULL - kern_windrv.c:windrv_unload() wasn't releasing private driver object extensions correctly (found thanks to memguard) This has only been tested with the driver for the Broadcom 802.11g chipset, which was the only Windows/x86-64 driver I could find.
2005-02-16 05:41:18 +00:00
int
windrv_unwrap(func)
funcptr func;
{
free(func, M_DEVBUF);
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
Add support for Windows/x86-64 binaries to Project Evil. Ville-Pertti Keinonen (will at exomi dot comohmygodnospampleasekthx) deserves a big thanks for submitting initial patches to make it work. I have mangled his contributions appropriately. The main gotcha with Windows/x86-64 is that Microsoft uses a different calling convention than everyone else. The standard ABI requires using 6 registers for argument passing, with other arguments on the stack. Microsoft uses only 4 registers, and requires the caller to leave room on the stack for the register arguments incase the callee needs to spill them. Unlike x86, where Microsoft uses a mix of _cdecl, _stdcall and _fastcall, all routines on Windows/x86-64 uses the same convention. This unfortunately means that all the functions we export to the driver require an intermediate translation wrapper. Similarly, we have to wrap all calls back into the driver binary itself. The original patches provided macros to wrap every single routine at compile time, providing a secondary jump table with a customized wrapper for each exported routine. I decided to use a different approach: the call wrapper for each function is created from a template at runtime, and the routine to jump to is patched into the wrapper as it is created. The subr_pe module has been modified to patch in the wrapped function instead of the original. (On x86, the wrapping routine is a no-op.) There are some minor API differences that had to be accounted for: - KeAcquireSpinLock() is a real function on amd64, not a macro wrapper around KfAcquireSpinLock() - NdisFreeBuffer() is actually IoFreeMdl(). I had to change the whole NDIS_BUFFER API a bit to accomodate this. Bugs fixed along the way: - IoAllocateMdl() always returned NULL - kern_windrv.c:windrv_unload() wasn't releasing private driver object extensions correctly (found thanks to memguard) This has only been tested with the driver for the Broadcom 802.11g chipset, which was the only Windows/x86-64 driver I could find.
2005-02-16 05:41:18 +00:00
return(0);
}