From: Sascha Wildner Date: Wed, 7 Sep 2011 20:28:06 +0000 (+0200) Subject: Sync ndis(4) and tools with FreeBSD and hook it all back into the build. X-Git-Tag: v2.12.0~64 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/efba76b4e799bdf82bd255a0dd06d36c3f5a82c5 Sync ndis(4) and tools with FreeBSD and hook it all back into the build. It supports NDIS 5.x drivers, that means Windows XP and Server 2003. The latter is an educated guess by me. No testing has actually been done using Windows Server 2003 drivers. It is tested on i386 with the following PCI adapters: * D-Link DWL-G520+ (Texas Instruments ACX111 chip), aka acx(4). * Linksys WMP600N (Ralink RT2860 chip). * Planex GW-DS54GR (Realtek RTL8185 chip) which was kindly donated by sephe. Thanks to Max Herrgard for testing it on x86_64 with a CNet CWP-854 (Ralink RT2561T chip), aka ral(4). Note that this port has the following caveats: * PCI adapters need hw.ioapic_enable=0 in /boot/loader.conf but worked great then. Without it, the box will completely freeze after a while. This has been observed on both my own testing box as well as on a different box by Max Herrgard. The cause is yet unknown. :-( * PC Card adapters are untested (they might just work). * USB adapters are not yet stable at all. Panics might ensue. Taken-from: FreeBSD --- diff --git a/Makefile_upgrade.inc b/Makefile_upgrade.inc index 6deb8c895f..d43f64f41c 100644 --- a/Makefile_upgrade.inc +++ b/Makefile_upgrade.inc @@ -1733,6 +1733,8 @@ TO_REMOVE+=/usr/share/man/man8/xten.8.gz TO_REMOVE+=/usr/bin/gprof4 TO_REMOVE+=/usr/share/misc/gprof.callg TO_REMOVE+=/usr/share/misc/gprof.flat +TO_REMOVE+=/usr/share/man/cat4/i386/ndis.4.gz +TO_REMOVE+=/usr/share/man/man4/i386/ndis.4.gz .if ${MACHINE_ARCH} == "x86_64" TO_REMOVE+=/usr/libdata/stallion/2681.sys @@ -1775,9 +1777,3 @@ TO_REMOVE+=/usr/share/man/man4/rum.4.gz TO_REMOVE+=/boot/modules/if_ural.ko TO_REMOVE+=/usr/share/man/cat4/ural.4.gz TO_REMOVE+=/usr/share/man/man4/ural.4.gz -TO_REMOVE+=/boot/modules/ndis.ko -TO_REMOVE+=/usr/share/man/cat4/i386/ndis.4.gz -TO_REMOVE+=/usr/share/man/man4/i386/ndis.4.gz -TO_REMOVE+=/usr/sbin/ndiscvt -TO_REMOVE+=/usr/share/man/cat8/ndiscvt.8.gz -TO_REMOVE+=/usr/share/man/man8/ndiscvt.8.gz diff --git a/etc/mtree/BSD.root.dist b/etc/mtree/BSD.root.dist index 6f9aabe058..d5fd23f59d 100644 --- a/etc/mtree/BSD.root.dist +++ b/etc/mtree/BSD.root.dist @@ -1,5 +1,4 @@ # $FreeBSD: src/etc/mtree/BSD.root.dist,v 1.45.2.4 2002/06/10 15:33:27 obrien Exp $ -# $DragonFly: src/etc/mtree/BSD.root.dist,v 1.12 2008/09/01 19:39:49 dillon Exp $ # # Please see the file src/etc/mtree/README before making changes to this file. # @@ -12,6 +11,10 @@ defaults .. .. + compat + ndis + .. + .. dev .. etc diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 38e35516c7..d52b3171ac 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -171,6 +171,7 @@ MAN= aac.4 \ natm.4 \ ncr.4 \ ncv.4 \ + ndis.4 \ netgraph.4 \ netintro.4 \ nfe.4 \ diff --git a/share/man/man4/man4.i386/Makefile b/share/man/man4/man4.i386/Makefile index b107620882..a69c2ca3a2 100644 --- a/share/man/man4/man4.i386/Makefile +++ b/share/man/man4/man4.i386/Makefile @@ -10,9 +10,6 @@ MAN= aic.4 apm.4 ar.4 asr.4 \ spkr.4 sr.4 stl.4 \ tx.4 viapm.4 vx.4 -# XXX re-add when adjusted to the new 802.11 framework -#MAN+= ndis.4 \ - MLINKS= spkr.4 speaker.4 \ stl.4 stli.4 diff --git a/share/man/man4/man4.i386/ndis.4 b/share/man/man4/ndis.4 similarity index 88% rename from share/man/man4/man4.i386/ndis.4 rename to share/man/man4/ndis.4 index 316ca46ce7..d1874e577b 100644 --- a/share/man/man4/man4.i386/ndis.4 +++ b/share/man/man4/ndis.4 @@ -28,17 +28,16 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF .\" THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $FreeBSD: src/share/man/man4/man4.i386/ndis.4,v 1.9 2004/12/21 01:09:34 brueffer Exp $ -.\" $DragonFly: src/share/man/man4/man4.i386/ndis.4,v 1.6 2008/07/26 16:25:41 swildner Exp $ +.\" $FreeBSD: src/share/man/man4/ndis.4,v 1.5 2010/03/14 15:49:04 gavin Exp $ .\" -.Dd December 10, 2003 -.Dt NDIS 4 i386 +.Dd May 10, 2011 +.Dt NDIS 4 .Os .Sh NAME .Nm ndis .Nd NDIS miniport driver wrapper .Sh SYNOPSIS -.\".Cd "options NDISAPI" +.Cd "options NDISAPI" .Cd "device ndis" .Cd "device wlan" .Sh DESCRIPTION @@ -90,10 +89,8 @@ file, which contains the definitions for driver-specific registry keys and other installation data such as device identifiers. These two files can be converted -into a -.Pa ndis_driver_data.h -file using the -.Xr ndiscvt 8 +into a kernel module file using the +.Xr ndisgen 8 utility. This file contains a binary image of the driver plus registry key data. @@ -108,7 +105,7 @@ file. The .Nm driver is designed to support mainly Ethernet and wireless -network devices with PCI and PCMCIA bus attachments. +network devices with PCI, PCMCIA and USB bus attachments. (Cardbus devices are also supported as a subset of PCI.) It can @@ -123,11 +120,11 @@ driver-specific registry keys to control the media setting which can be configured via the .Xr sysctl 8 command. -.Sh EXAMPLES -Refer to the -.Sx EXAMPLES -section of -.Xr wlan 4 . +.\".Sh EXAMPLES +.\"Refer to the +.\".Sx EXAMPLES +.\"section of +.\".Xr wlan 4 . .Sh DIAGNOSTICS .Bl -diag .It "ndis%d: watchdog timeout" @@ -136,13 +133,17 @@ issued, however the device failed to acknowledge the transmission before a timeout expired. .El .Sh SEE ALSO +.Xr altq 4 , .Xr arp 4 , .Xr ifmedia 4 , .Xr netintro 4 , .Xr ng_ether 4 , .Xr wlan 4 , .Xr ifconfig 8 , -.Xr ndiscvt 8 +.Xr ndis_events 8 , +.Xr ndiscvt 8 , +.Xr ndisgen 8 , +.Xr wpa_supplicant 8 .Rs .%T "NDIS 5.1 specification" .%O http://www.microsoft.com @@ -151,16 +152,9 @@ before a timeout expired. The .Nm device driver first appeared in -.Fx 5.3 -and was imported into -.Dx 1.0 . +.Fx 5.3 . .Sh AUTHORS -.An -nosplit The .Nm driver was written by .An Bill Paul Aq wpaul@windriver.com . -Porting to -.Dx -was done by -.An Matthew Dillon . diff --git a/share/man/man4/usb.4 b/share/man/man4/usb.4 index efa6fbe834..98ae90f2f5 100644 --- a/share/man/man4/usb.4 +++ b/share/man/man4/usb.4 @@ -88,6 +88,8 @@ RealTek RTL8150 Ethernet driver .El .Ss Wireless network interfaces .Bl -tag -width ".Xr snd_uaudio 4" -offset indent -compact +.It Xr ndis 4 +NDIS miniport driver wrapper .\".It Xr rum 4 .\"Ralink Technology RT2501USB/RT2601USB IEEE 802.11 driver .It Xr ubt 4 diff --git a/share/man/man4/wlan.4 b/share/man/man4/wlan.4 index b1d5a481ec..3e1a0adc3e 100644 --- a/share/man/man4/wlan.4 +++ b/share/man/man4/wlan.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD: src/share/man/man4/wlan.4,v 1.23 2010/01/14 09:38:23 roam Exp $ .\" -.Dd April 14, 2010 +.Dd May 10, 2011 .Dt WLAN 4 .Os .Sh NAME @@ -41,10 +41,9 @@ Where a device does not directly support 802.11 functionality this layer fills in. The .Nm -module is required by all native 802.11 drivers. -.\" as well as the -.\".Xr ndis 4 -.\"support. +module is required by all native 802.11 drivers as well as the +.Xr ndis 4 +support. .Pp .Nm supports multi-mode devices capable of diff --git a/share/man/man9/ieee80211.9 b/share/man/man9/ieee80211.9 index beec0bb933..2fddb7401d 100644 --- a/share/man/man9/ieee80211.9 +++ b/share/man/man9/ieee80211.9 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD: src/share/man/man9/ieee80211.9,v 1.7 2010/03/29 17:39:38 trasz Exp $ .\" -.Dd April 28, 2010 +.Dd May 10, 2011 .Dt IEEE80211 9 .Os .Sh NAME @@ -550,7 +550,7 @@ Device supports Reduced Inter Frame Spacing (RIFS). .El .Sh SEE ALSO .Xr ioctl 2 , -.\".Xr ndis 4 , +.Xr ndis 4 , .\".Xr ieee80211_amrr 9 , .Xr ieee80211_beacon 9 , .Xr ieee80211_bmiss 9 , diff --git a/sys/config/LINT b/sys/config/LINT index f1e4adf8c3..6c550223a6 100644 --- a/sys/config/LINT +++ b/sys/config/LINT @@ -296,6 +296,10 @@ options COMPAT_43 # options COMPAT_DF12 #Compatible with DragonFly 1.2 and earlier +# Enable NDIS binary driver support +options NDISAPI +device ndis + # # These three options provide support for System V Interface # Definition-style interprocess communication, in the form of shared diff --git a/sys/config/LINT64 b/sys/config/LINT64 index e47227abe3..7575450f73 100644 --- a/sys/config/LINT64 +++ b/sys/config/LINT64 @@ -160,6 +160,10 @@ options CPU_ENABLE_EST # options COMPAT_43 +# Enable NDIS binary driver support +options NDISAPI +device ndis + # # These three options provide support for System V Interface # Definition-style interprocess communication, in the form of shared diff --git a/sys/cpu/i386/include/segments.h b/sys/cpu/i386/include/segments.h index adfe64832e..3d1427ffad 100644 --- a/sys/cpu/i386/include/segments.h +++ b/sys/cpu/i386/include/segments.h @@ -228,12 +228,13 @@ struct region_descriptor { #define GBIOSARGS_SEL 14 /* BIOS interface (Arguments) */ #define GTLS_START 15 /* Thread TLS Descriptor */ #define GTLS_END 17 /* Thread TLS Descriptor */ +#define GNDIS_SEL 18 /* For the NDIS layer */ #define NGTLS (GTLS_END - GTLS_START + 1) #ifdef BDE_DEBUGGER #define NGDT 21 /* some of 11-17 are reserved for debugger */ #else -#define NGDT 18 +#define NGDT 19 #endif /* diff --git a/sys/dev/netif/Makefile b/sys/dev/netif/Makefile index 2774de2c1c..13a8064664 100644 --- a/sys/dev/netif/Makefile +++ b/sys/dev/netif/Makefile @@ -1,6 +1,7 @@ SUBDIR= an age alc ale ar ath aue axe bce bfe bge \ - cue dc ed em ep et e1000 fwe \ - fxp ic iwi iwn jme kue lge lgue lnc mii_layer my msk mxge nfe nge pcn \ + cue dc ed em ep et e1000 \ + fwe fxp ic iwi iwn jme kue lge lgue lnc \ + mii_layer my msk mxge ndis nfe nge pcn \ ral re rl rue sbni sbsh sf sis sk sln sr ste stge ti tl tx txp \ vge vr vx wb wi wpi xe xl ig_hal emx ae diff --git a/sys/dev/netif/ndis/Makefile b/sys/dev/netif/ndis/Makefile index 0891d1888b..3493815a82 100644 --- a/sys/dev/netif/ndis/Makefile +++ b/sys/dev/netif/ndis/Makefile @@ -1,8 +1,8 @@ -# -# $DragonFly: src/sys/dev/netif/ndis/Makefile,v 1.1 2004/07/29 20:51:36 dillon Exp $ +# $FreeBSD: src/sys/modules/if_ndis/Makefile,v 1.6 2005/09/27 18:10:35 mlaier Exp $ KMOD= if_ndis -SRCS= if_ndis.c if_ndis_pccard.c if_ndis_pci.c \ - card_if.h device_if.h bus_if.h pci_if.h +SRCS= if_ndis.c if_ndis_pci.c if_ndis_pccard.c if_ndis_usb.c +SRCS+= device_if.h bus_if.h pci_if.h card_if.h +SRCS+= opt_usb.h .include diff --git a/sys/dev/netif/ndis/README b/sys/dev/netif/ndis/README deleted file mode 100644 index d230bc4fa4..0000000000 --- a/sys/dev/netif/ndis/README +++ /dev/null @@ -1,60 +0,0 @@ -$DragonFly: src/sys/dev/netif/ndis/README,v 1.2 2008/02/01 08:39:58 hasso Exp $ - USING IF_NDIS WITH WINDOWS DEVICE DRIVERS - -(1) Locate the .INF and .SYS file for the windows driver. I have no idea - where the official location for these things are but I found the one - for the Centrino 2200 chipset (for my Sony TR3A) here: - - http://news.gw.com/freebsd.hardware/4894 - http://www.powernotebooks.com/Support/intel_2200_wlan.zip - - My MD5 (intel_2200_wlan.zip) = 281812933642f3f2fd54710ee7bba2d4 - This is unofficial. - -(2) Unpack the files into a temporary directory, then copy the appropriate - .INF and .SYS file to /usr/local/modules/if_ndis. - - mkdir -p /usr/local/modules/if_ndis - cp .... - -(3) Generate a driver module header file using ndiscvt then build and - install the module. The object directory in which you store the - header file should match what make obj creates. If your DFly sources - are in /usr/src then the example below will work. - - # cd /usr/src/sys/dev/netif/ndis - # make obj - # make clean - # ndiscvt -i /usr/local/modules/if_ndis/w22n51.INF -s /usr/local/modules/if_ndis/w22n51.sys -o /usr/obj/usr/src/sys/dev/netif/ndis/ndis_driver_data.h - # make - # make install - -(4) Load all required modules into the running kernel: - - # kldload /modules/wlan.ko - # kldload /modules/ndis.ko - # kldload /modules/if_ndis.ko - - You should get something similar to the following system console - messages: - - ndis0: NDIS API version: 5.1 - ndis0: MAC address: 00:0e:35:15:ee:72 - ndis0: 11b rates: 1Mbps 2Mbps 5.5Mbps 11Mbps 5.5Mbps 11Mbps - ndis0: 11g rates: 6Mbps 9Mbps 12Mbps 18Mbps 24Mbps 36Mbps 48Mbps 54Mbps - -(5) Bring the interface up and associate the SSID. If you aren't sure - what your basestation id is you can use 'ifconfig -v ndis0 list scan' to - list available ssid's. - - # ifconfig ndis0 up - # ifconfig -v ndis0 list scan - # ifconfig ssid "your_basestation_id" - -(6) Ifconfig should show the interface up and associated. Run dhclient - or ifconfig your IP address. - - # ifconfig ndis0 - [should show the interface up and associated] - # dhclient ndis0 - diff --git a/sys/dev/netif/ndis/if_ndis.c b/sys/dev/netif/ndis/if_ndis.c index a367764bf9..0d2f22adce 100644 --- a/sys/dev/netif/ndis/if_ndis.c +++ b/sys/dev/netif/ndis/if_ndis.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,8 +29,10 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/if_ndis/if_ndis.c,v 1.65 2004/07/07 17:46:30 wpaul Exp $ - * $DragonFly: src/sys/dev/netif/ndis/if_ndis.c,v 1.23 2008/08/17 04:32:34 sephe Exp $ + * $FreeBSD: src/sys/dev/if_ndis/if_ndis.c,v 1.178 2011/01/07 18:41:59 bschmidt Exp $ + * + * WPA support originally contributed by Arvind Srinivasan + * then hacked upon mercilessly by me. */ #include @@ -38,48 +40,80 @@ #include #include #include +#include +#include #include #include #include +#include #include -#include #include -#include -#include -#include -#include +#include #include -#include #include #include #include #include +#include #include #include +#include + +#include +#include +#include #include #include - -#include +#include #include #include +#include +#include -#include #include +#include #include #include #include #include -#include -#include "if_ndisvar.h" +#include +#include + +#define NDIS_DEBUG +#ifdef NDIS_DEBUG +#define DPRINTF(x) do { if (ndis_debug > 0) kprintf x; } while (0) +int ndis_debug = 0; +SYSCTL_INT(_debug, OID_AUTO, ndis, CTLFLAG_RW, &ndis_debug, 0, + "if_ndis debug level"); +#else +#define DPRINTF(x) +#endif -#define NDIS_IMAGE -#define NDIS_REGVALS +SYSCTL_DECL(_hw_ndisusb); +int ndisusb_halt = 1; +SYSCTL_INT(_hw_ndisusb, OID_AUTO, halt, CTLFLAG_RW, &ndisusb_halt, 0, + "Halt NDIS USB driver when it's attached"); -#include "ndis_driver_data.h" +/* 0 - 30 dBm to mW conversion table */ +static const uint16_t dBm2mW[] = { + 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, + 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, + 10, 11, 13, 14, 16, 18, 20, 22, 25, 28, + 32, 35, 40, 45, 50, 56, 63, 71, 79, 89, + 100, 112, 126, 141, 158, 178, 200, 224, 251, 282, + 316, 355, 398, 447, 501, 562, 631, 708, 794, 891, + 1000 +}; + +MODULE_DEPEND(if_ndis, ether, 1, 1, 1); +MODULE_DEPEND(if_ndis, wlan, 1, 1, 1); +MODULE_DEPEND(if_ndis, ndis, 1, 1, 1); + +MODULE_VERSION(if_ndis, 1); int ndis_attach (device_t); int ndis_detach (device_t); @@ -87,39 +121,155 @@ int ndis_suspend (device_t); int ndis_resume (device_t); void ndis_shutdown (device_t); -static __stdcall void ndis_txeof (ndis_handle, - ndis_packet *, ndis_status); -static __stdcall void ndis_rxeof (ndis_handle, - ndis_packet **, uint32_t); -static __stdcall void ndis_linksts (ndis_handle, - ndis_status, void *, uint32_t); -static __stdcall void ndis_linksts_done (ndis_handle); - -static void ndis_intr (void *); -static void ndis_intrtask (void *); +int ndisdrv_modevent (module_t, int, void *); + +static void ndis_txeof (ndis_handle, ndis_packet *, ndis_status); +static void ndis_rxeof (ndis_handle, ndis_packet **, uint32_t); +static void ndis_rxeof_eth (ndis_handle, ndis_handle, char *, void *, + uint32_t, void *, uint32_t, uint32_t); +static void ndis_rxeof_done (ndis_handle); +static void ndis_rxeof_xfr (kdpc *, ndis_handle, void *, void *); +static void ndis_rxeof_xfr_done (ndis_handle, ndis_packet *, + uint32_t, uint32_t); +static void ndis_linksts (ndis_handle, ndis_status, void *, uint32_t); +static void ndis_linksts_done (ndis_handle); + +/* We need to wrap these functions for amd64. */ +static funcptr ndis_txeof_wrap; +static funcptr ndis_rxeof_wrap; +static funcptr ndis_rxeof_eth_wrap; +static funcptr ndis_rxeof_done_wrap; +static funcptr ndis_rxeof_xfr_wrap; +static funcptr ndis_rxeof_xfr_done_wrap; +static funcptr ndis_linksts_wrap; +static funcptr ndis_linksts_done_wrap; +static funcptr ndis_ticktask_wrap; +static funcptr ndis_starttask_wrap; +static funcptr ndis_resettask_wrap; +static funcptr ndis_inputtask_wrap; + +static struct ieee80211vap *ndis_vap_create(struct ieee80211com *, + const char name[IFNAMSIZ], int unit, int opmode, + int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void ndis_vap_delete (struct ieee80211vap *); static void ndis_tick (void *); -static void ndis_ticktask (void *); +static void ndis_ticktask (device_object *, void *); +static int ndis_raw_xmit (struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void ndis_update_mcast (struct ifnet *ifp); +static void ndis_update_promisc (struct ifnet *ifp); static void ndis_start (struct ifnet *); -static void ndis_starttask (void *); +static void ndis_starttask (device_object *, void *); +static void ndis_resettask (device_object *, void *); +static void ndis_inputtask (device_object *, void *); static int ndis_ioctl (struct ifnet *, u_long, caddr_t, struct ucred *); -static int ndis_wi_ioctl_get (struct ifnet *, u_long, caddr_t); -static int ndis_wi_ioctl_set (struct ifnet *, u_long, caddr_t); +static int ndis_ioctl_80211 (struct ifnet *, u_long, caddr_t, struct ucred *); +static int ndis_newstate (struct ieee80211vap *, enum ieee80211_state, + int); +static int ndis_nettype_chan (uint32_t); +static int ndis_nettype_mode (uint32_t); +static void ndis_scan (void *); +static void ndis_scan_results (struct ndis_softc *); +static void ndis_scan_start (struct ieee80211com *); +static void ndis_scan_end (struct ieee80211com *); +static void ndis_set_channel (struct ieee80211com *); +static void ndis_scan_curchan (struct ieee80211_scan_state *, unsigned long); +static void ndis_scan_mindwell (struct ieee80211_scan_state *); static void ndis_init (void *); static void ndis_stop (struct ndis_softc *); -static void ndis_watchdog (struct ifnet *); static int ndis_ifmedia_upd (struct ifnet *); static void ndis_ifmedia_sts (struct ifnet *, struct ifmediareq *); +static int ndis_get_bssid_list (struct ndis_softc *, + ndis_80211_bssid_list_ex **); static int ndis_get_assoc (struct ndis_softc *, ndis_wlan_bssid_ex **); static int ndis_probe_offload (struct ndis_softc *); static int ndis_set_offload (struct ndis_softc *); static void ndis_getstate_80211 (struct ndis_softc *); static void ndis_setstate_80211 (struct ndis_softc *); +static void ndis_auth_and_assoc (struct ndis_softc *, struct ieee80211vap *); static void ndis_media_status (struct ifnet *, struct ifmediareq *); +static int ndis_set_cipher (struct ndis_softc *, int); +static int ndis_set_wpa (struct ndis_softc *, void *, int); +static int ndis_add_key (struct ieee80211vap *, + const struct ieee80211_key *, const u_int8_t []); +static int ndis_del_key (struct ieee80211vap *, + const struct ieee80211_key *); static void ndis_setmulti (struct ndis_softc *); static void ndis_map_sclist (void *, bus_dma_segment_t *, int, bus_size_t, int); +static int ndisdrv_loaded = 0; + +/* + * This routine should call windrv_load() once for each driver + * image. This will do the relocation and dynalinking for the + * image, and create a Windows driver object which will be + * saved in our driver database. + */ +int +ndisdrv_modevent(module_t mod, int cmd, void *arg) +{ + int error = 0; + + switch (cmd) { + case MOD_LOAD: + ndisdrv_loaded++; + if (ndisdrv_loaded > 1) + break; + windrv_wrap((funcptr)ndis_rxeof, &ndis_rxeof_wrap, + 3, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_rxeof_eth, &ndis_rxeof_eth_wrap, + 8, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_rxeof_done, &ndis_rxeof_done_wrap, + 1, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_rxeof_xfr, &ndis_rxeof_xfr_wrap, + 4, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_rxeof_xfr_done, + &ndis_rxeof_xfr_done_wrap, 4, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_txeof, &ndis_txeof_wrap, + 3, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_linksts, &ndis_linksts_wrap, + 4, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_linksts_done, + &ndis_linksts_done_wrap, 1, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_ticktask, &ndis_ticktask_wrap, + 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_starttask, &ndis_starttask_wrap, + 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_resettask, &ndis_resettask_wrap, + 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)ndis_inputtask, &ndis_inputtask_wrap, + 2, WINDRV_WRAP_STDCALL); + break; + case MOD_UNLOAD: + ndisdrv_loaded--; + if (ndisdrv_loaded > 0) + break; + /* fallthrough */ + case MOD_SHUTDOWN: + windrv_unwrap(ndis_rxeof_wrap); + windrv_unwrap(ndis_rxeof_eth_wrap); + windrv_unwrap(ndis_rxeof_done_wrap); + windrv_unwrap(ndis_rxeof_xfr_wrap); + windrv_unwrap(ndis_rxeof_xfr_done_wrap); + windrv_unwrap(ndis_txeof_wrap); + windrv_unwrap(ndis_linksts_wrap); + windrv_unwrap(ndis_linksts_done_wrap); + windrv_unwrap(ndis_ticktask_wrap); + windrv_unwrap(ndis_starttask_wrap); + windrv_unwrap(ndis_resettask_wrap); + windrv_unwrap(ndis_inputtask_wrap); + break; + default: + error = EINVAL; + break; + } + + return (error); +} + /* * Program the 64-bit multicast hash filter. */ @@ -131,7 +281,7 @@ ndis_setmulti(struct ndis_softc *sc) int len, mclistsz, error; uint8_t *mclist; - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) return; @@ -142,12 +292,12 @@ ndis_setmulti(struct ndis_softc *sc) error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &len); if (error) - device_printf (sc->ndis_dev, - "set filter failed: %d\n", error); + device_printf(sc->ndis_dev, + "set allmulti failed: %d\n", error); return; } - if (LIST_EMPTY(&ifp->if_multiaddrs)) + if (TAILQ_EMPTY(&ifp->if_multiaddrs)) return; len = sizeof(mclistsz); @@ -163,23 +313,32 @@ ndis_setmulti(struct ndis_softc *sc) sc->ndis_filter |= NDIS_PACKET_TYPE_MULTICAST; len = 0; - LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { +#if 0 /* XXX swildner */ + if_maddr_rlock(ifp); +#endif + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), mclist + (ETHER_ADDR_LEN * len), ETHER_ADDR_LEN); len++; if (len > mclistsz) { +#if 0 /* XXX swildner */ + if_maddr_runlock(ifp); +#endif sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST; goto out; } } +#if 0 /* XXX swildner */ + if_maddr_runlock(ifp); +#endif len = len * ETHER_ADDR_LEN; error = ndis_set_info(sc, OID_802_3_MULTICAST_LIST, mclist, &len); if (error) { - device_printf (sc->ndis_dev, "set mclist failed: %d\n", error); + device_printf(sc->ndis_dev, "set mclist failed: %d\n", error); sc->ndis_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; sc->ndis_filter &= ~NDIS_PACKET_TYPE_MULTICAST; } @@ -191,7 +350,7 @@ out: error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &len); if (error) - device_printf (sc->ndis_dev, "set filter failed: %d\n", error); + device_printf(sc->ndis_dev, "set multi failed: %d\n", error); } static int @@ -203,19 +362,19 @@ ndis_set_offload(struct ndis_softc *sc) struct ifnet *ifp; int len, error; - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) - return(EINVAL); + return (EINVAL); /* See if there's anything to set. */ error = ndis_probe_offload(sc); if (error) - return(error); - + return (error); + if (sc->ndis_hwassist == 0 && ifp->if_capabilities == 0) - return(0); + return (0); len = sizeof(ndis_task_offload_hdr) + sizeof(ndis_task_offload) + sizeof(ndis_task_tcpip_csum); @@ -223,7 +382,7 @@ ndis_set_offload(struct ndis_softc *sc) ntoh = kmalloc(len, M_TEMP, M_NOWAIT|M_ZERO); if (ntoh == NULL) - return(ENOMEM); + return (ENOMEM); ntoh->ntoh_vers = NDIS_TASK_OFFLOAD_VERSION; ntoh->ntoh_len = sizeof(ndis_task_offload_hdr); @@ -252,7 +411,7 @@ ndis_set_offload(struct ndis_softc *sc) error = ndis_set_info(sc, OID_TCP_TASK_OFFLOAD, ntoh, &len); kfree(ntoh, M_TEMP); - return(error); + return (error); } static int @@ -264,18 +423,18 @@ ndis_probe_offload(struct ndis_softc *sc) struct ifnet *ifp; int len, error, dummy; - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; len = sizeof(dummy); error = ndis_get_info(sc, OID_TCP_TASK_OFFLOAD, &dummy, &len); if (error != ENOSPC) - return(error); + return (error); ntoh = kmalloc(len, M_TEMP, M_NOWAIT|M_ZERO); if (ntoh == NULL) - return(ENOMEM); + return (ENOMEM); ntoh->ntoh_vers = NDIS_TASK_OFFLOAD_VERSION; ntoh->ntoh_len = sizeof(ndis_task_offload_hdr); @@ -287,12 +446,12 @@ ndis_probe_offload(struct ndis_softc *sc) if (error) { kfree(ntoh, M_TEMP); - return(error); + return (error); } if (ntoh->ntoh_vers != NDIS_TASK_OFFLOAD_VERSION) { kfree(ntoh, M_TEMP); - return(EINVAL); + return (EINVAL); } nto = (ndis_task_offload *)((char *)ntoh + @@ -317,7 +476,7 @@ ndis_probe_offload(struct ndis_softc *sc) if (nttc == NULL) { kfree(ntoh, M_TEMP); - return(ENOENT); + return (ENOENT); } sc->ndis_v4tx = nttc->nttc_v4tx; @@ -341,7 +500,33 @@ ndis_probe_offload(struct ndis_softc *sc) ifp->if_capabilities |= IFCAP_RXCSUM; kfree(ntoh, M_TEMP); - return(0); + return (0); +} + +static int +ndis_nettype_chan(uint32_t type) +{ + switch (type) { + case NDIS_80211_NETTYPE_11FH: return (IEEE80211_CHAN_FHSS); + case NDIS_80211_NETTYPE_11DS: return (IEEE80211_CHAN_B); + case NDIS_80211_NETTYPE_11OFDM5: return (IEEE80211_CHAN_A); + case NDIS_80211_NETTYPE_11OFDM24: return (IEEE80211_CHAN_G); + } + DPRINTF(("unknown channel nettype %d\n", type)); + return (IEEE80211_CHAN_B); /* Default to 11B chan */ +} + +static int +ndis_nettype_mode(uint32_t type) +{ + switch (type) { + case NDIS_80211_NETTYPE_11FH: return (IEEE80211_MODE_FH); + case NDIS_80211_NETTYPE_11DS: return (IEEE80211_MODE_11B); + case NDIS_80211_NETTYPE_11OFDM5: return (IEEE80211_MODE_11A); + case NDIS_80211_NETTYPE_11OFDM24: return (IEEE80211_MODE_11G); + } + DPRINTF(("unknown mode nettype %d\n", type)); + return (IEEE80211_MODE_AUTO); } /* @@ -353,43 +538,89 @@ ndis_attach(device_t dev) { u_char eaddr[ETHER_ADDR_LEN]; struct ndis_softc *sc; - struct ifnet *ifp; - void *img; - int error = 0, len; + driver_object *pdrv; + device_object *pdo; + struct ifnet *ifp = NULL; + int error = 0, len, mode; + uint8_t bands = 0; int i; sc = device_get_softc(dev); - callout_init(&sc->ndis_stat_timer); - - sc->ndis_regvals = ndis_regvals; + lockinit(&sc->ndis_lock, "network driver", 0, LK_CANRECURSE); + KeInitializeSpinLock(&sc->ndis_rxlock); + KeInitializeSpinLock(&sc->ndisusb_xferlock); + InitializeListHead(&sc->ndis_shlist); + InitializeListHead(&sc->ndisusb_xferlist); + callout_init(&sc->ndis_stat_callout); -#if __FreeBSD_version < 502113 - sysctl_ctx_init(&sc->ndis_ctx); + if (sc->ndis_iftype == PCMCIABus) { + error = ndis_alloc_amem(sc); + if (error) { + device_printf(dev, "failed to allocate " + "attribute memory\n"); + goto fail; + } + } -#endif /* Create sysctl registry nodes */ ndis_create_sysctls(sc); - /* Set up driver image in memory. */ - img = drv_data; - ndis_load_driver((vm_offset_t)img, sc); + /* Find the PDO for this device instance. */ + + if (sc->ndis_iftype == PCIBus) + pdrv = windrv_lookup(0, "PCI Bus"); + else if (sc->ndis_iftype == PCMCIABus) + pdrv = windrv_lookup(0, "PCCARD Bus"); + else + pdrv = windrv_lookup(0, "USB Bus"); + pdo = windrv_find_pdo(pdrv, dev); + + /* + * Create a new functional device object for this + * device. This is what creates the miniport block + * for this device instance. + */ + + if (NdisAddDevice(sc->ndis_dobj, pdo) != STATUS_SUCCESS) { + device_printf(dev, "failed to create FDO!\n"); + error = ENXIO; + goto fail; + } /* Tell the user what version of the API the driver is using. */ device_printf(dev, "NDIS API version: %d.%d\n", - sc->ndis_chars.nmc_version_major, - sc->ndis_chars.nmc_version_minor); + sc->ndis_chars->nmc_version_major, + sc->ndis_chars->nmc_version_minor); /* Do resource conversion. */ - ndis_convert_res(sc); + if (sc->ndis_iftype == PCMCIABus || sc->ndis_iftype == PCIBus) + ndis_convert_res(sc); + else + sc->ndis_block->nmb_rlist = NULL; /* Install our RX and TX interrupt handlers. */ - sc->ndis_block.nmb_senddone_func = ndis_txeof; - sc->ndis_block.nmb_pktind_func = ndis_rxeof; + sc->ndis_block->nmb_senddone_func = ndis_txeof_wrap; + sc->ndis_block->nmb_pktind_func = ndis_rxeof_wrap; + sc->ndis_block->nmb_ethrxindicate_func = ndis_rxeof_eth_wrap; + sc->ndis_block->nmb_ethrxdone_func = ndis_rxeof_done_wrap; + sc->ndis_block->nmb_tdcond_func = ndis_rxeof_xfr_done_wrap; + + /* Override the status handler so we can detect link changes. */ + sc->ndis_block->nmb_status_func = ndis_linksts_wrap; + sc->ndis_block->nmb_statusdone_func = ndis_linksts_done_wrap; + + /* Set up work item handlers. */ + sc->ndis_tickitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndis_startitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndis_resetitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndis_inputitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndisusb_xferitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block); /* Call driver's init routine. */ if (ndis_init_nic(sc)) { - device_printf (dev, "init handler failed\n"); + device_printf(dev, "init handler failed\n"); error = ENXIO; goto fail; } @@ -400,25 +631,45 @@ ndis_attach(device_t dev) len = sizeof(eaddr); ndis_get_info(sc, OID_802_3_CURRENT_ADDRESS, &eaddr, &len); - bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - /* - * Figure out of we're allowed to use multipacket sends - * with this driver, and if so, how many. + * Figure out how big to make the TX buffer pool. */ - if (sc->ndis_chars.nmc_sendsingle_func && - sc->ndis_chars.nmc_sendmulti_func == NULL) { - sc->ndis_maxpkts = 1; - } else { - len = sizeof(sc->ndis_maxpkts); - ndis_get_info(sc, OID_GEN_MAXIMUM_SEND_PACKETS, - &sc->ndis_maxpkts, &len); + len = sizeof(sc->ndis_maxpkts); + if (ndis_get_info(sc, OID_GEN_MAXIMUM_SEND_PACKETS, + &sc->ndis_maxpkts, &len)) { + device_printf(dev, "failed to get max TX packets\n"); + error = ENXIO; + goto fail; } + /* + * If this is a deserialized miniport, we don't have + * to honor the OID_GEN_MAXIMUM_SEND_PACKETS result. + */ + if (!NDIS_SERIALIZED(sc->ndis_block)) + sc->ndis_maxpkts = NDIS_TXPKTS; + + /* Enforce some sanity, just in case. */ + + if (sc->ndis_maxpkts == 0) + sc->ndis_maxpkts = 10; + sc->ndis_txarray = kmalloc(sizeof(ndis_packet *) * sc->ndis_maxpkts, M_DEVBUF, M_WAITOK|M_ZERO); + /* Allocate a pool of ndis_packets for TX encapsulation. */ + + NdisAllocatePacketPool(&i, &sc->ndis_txpool, + sc->ndis_maxpkts, PROTOCOL_RESERVED_SIZE_IN_PACKET); + + if (i != NDIS_STATUS_SUCCESS) { + sc->ndis_txpool = NULL; + device_printf(dev, "failed to allocate TX packet pool"); + error = ENOMEM; + goto fail; + } + sc->ndis_txpending = sc->ndis_maxpkts; sc->ndis_oidcnt = 0; @@ -434,50 +685,68 @@ ndis_attach(device_t dev) * supported by this driver. If it is, then this an 802.11 * wireless driver, and we should set up media for wireless. */ - for (i = 0; i < sc->ndis_oidcnt; i++) { + for (i = 0; i < sc->ndis_oidcnt; i++) if (sc->ndis_oids[i] == OID_802_11_CONFIGURATION) { sc->ndis_80211++; break; } + + if (sc->ndis_80211) + ifp = if_alloc(IFT_IEEE80211); + else + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + error = ENOSPC; + goto fail; } + sc->ifp = ifp; + ifp->if_softc = sc; /* Check for task offload support. */ ndis_probe_offload(sc); - ifp = &sc->arpcom.ac_if; - ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; +#if 0 + if (sc->ndis_iftype == PNPBus) + ifp->if_flags |= IFF_NEEDSGIANT; +#endif ifp->if_ioctl = ndis_ioctl; ifp->if_start = ndis_start; - ifp->if_watchdog = ndis_watchdog; ifp->if_init = ndis_init; ifp->if_baudrate = 10000000; ifq_set_maxlen(&ifp->if_snd, 50); +#if 0 /* XXX swildner */ + ifp->if_snd.ifq_drv_maxlen = 25; +#endif ifq_set_ready(&ifp->if_snd); ifp->if_capenable = ifp->if_capabilities; ifp->if_hwassist = sc->ndis_hwassist; /* Do media setup */ if (sc->ndis_80211) { - struct ieee80211com *ic = (void *)ifp; + struct ieee80211com *ic = ifp->if_l2com; ndis_80211_rates_ex rates; struct ndis_80211_nettype_list *ntl; uint32_t arg; int r; - ic->ic_phytype = IEEE80211_T_DS; + callout_init(&sc->ndis_scan_callout); + + ifp->if_ioctl = ndis_ioctl_80211; + ic->ic_ifp = ifp; ic->ic_opmode = IEEE80211_M_STA; - ic->ic_caps = IEEE80211_C_IBSS; - ic->ic_state = IEEE80211_S_ASSOC; - ic->ic_modecaps = (1<ic_phytype = IEEE80211_T_DS; + ic->ic_caps = IEEE80211_C_8023ENCAP | + IEEE80211_C_STA | IEEE80211_C_IBSS; + setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); len = 0; r = ndis_get_info(sc, OID_802_11_NETWORK_TYPES_SUPPORTED, NULL, &len); if (r != ENOSPC) goto nonettypes; - ntl = kmalloc(len, M_DEVBUF, M_WAITOK|M_ZERO); + ntl = kmalloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); r = ndis_get_info(sc, OID_802_11_NETWORK_TYPES_SUPPORTED, ntl, &len); if (r != 0) { @@ -486,29 +755,27 @@ ndis_attach(device_t dev) } for (i = 0; i < ntl->ntl_items; i++) { - switch (ntl->ntl_type[i]) { - case NDIS_80211_NETTYPE_11FH: - case NDIS_80211_NETTYPE_11DS: - ic->ic_modecaps |= (1<ic_modecaps |= (1<ic_modecaps |= (1<ntl_type[i]); + if (mode) { + setbit(ic->ic_modecaps, mode); + setbit(&bands, mode); + } else + device_printf(dev, "Unknown nettype %d\n", + ntl->ntl_type[i]); } kfree(ntl, M_DEVBUF); nonettypes: + /* Default to 11b channels if the card did not supply any */ + if (bands == 0) { + setbit(ic->ic_modecaps, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11B); + } len = sizeof(rates); bzero((char *)&rates, len); r = ndis_get_info(sc, OID_802_11_SUPPORTED_RATES, (void *)rates, &len); if (r) - device_printf (dev, "get rates failed: 0x%x\n", r); + device_printf(dev, "get rates failed: 0x%x\n", r); /* * Since the supported rates only up to 8 can be supported, * if this is not 802.11b we're just going to be faking it @@ -534,11 +801,11 @@ nonettypes: ic->ic_sup_rates[x].rs_nrates++ ic->ic_curmode = IEEE80211_MODE_AUTO; - if (ic->ic_modecaps & (1<ic_modecaps, IEEE80211_MODE_11A)) ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates = 0; - if (ic->ic_modecaps & (1<ic_modecaps, IEEE80211_MODE_11B)) ic->ic_sup_rates[IEEE80211_MODE_11B].rs_nrates = 0; - if (ic->ic_modecaps & (1<ic_modecaps, IEEE80211_MODE_11G)) ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates = 0; for (i = 0; i < len; i++) { switch (rates[i] & IEEE80211_RATE_VAL) { @@ -547,11 +814,10 @@ nonettypes: case 11: case 10: case 22: - if (!(ic->ic_modecaps & - (1<ic_modecaps, IEEE80211_MODE_11B)) { /* Lazy-init 802.11b. */ - ic->ic_modecaps |= - (1<ic_modecaps, + IEEE80211_MODE_11B); ic->ic_sup_rates[IEEE80211_MODE_11B]. rs_nrates = 0; } @@ -559,11 +825,11 @@ nonettypes: INCRATE(IEEE80211_MODE_11B); break; default: - if (ic->ic_modecaps & (1<ic_modecaps, IEEE80211_MODE_11A)) { SETRATE(IEEE80211_MODE_11A, rates[i]); INCRATE(IEEE80211_MODE_11A); } - if (ic->ic_modecaps & (1<ic_modecaps, IEEE80211_MODE_11G)) { SETRATE(IEEE80211_MODE_11G, rates[i]); INCRATE(IEEE80211_MODE_11G); } @@ -578,7 +844,7 @@ nonettypes: * just cheat here. Just how in the heck do * we detect turbo modes, though? */ - if (ic->ic_modecaps & (1<ic_modecaps, IEEE80211_MODE_11B)) { TESTSETRATE(IEEE80211_MODE_11B, IEEE80211_RATE_BASIC|2); TESTSETRATE(IEEE80211_MODE_11B, @@ -588,54 +854,92 @@ nonettypes: TESTSETRATE(IEEE80211_MODE_11B, IEEE80211_RATE_BASIC|22); } - if (ic->ic_modecaps & (1<ic_modecaps, IEEE80211_MODE_11G)) { + TESTSETRATE(IEEE80211_MODE_11G, 48); TESTSETRATE(IEEE80211_MODE_11G, 72); TESTSETRATE(IEEE80211_MODE_11G, 96); TESTSETRATE(IEEE80211_MODE_11G, 108); } - if (ic->ic_modecaps & (1<ic_modecaps, IEEE80211_MODE_11A)) { + TESTSETRATE(IEEE80211_MODE_11A, 48); TESTSETRATE(IEEE80211_MODE_11A, 72); TESTSETRATE(IEEE80211_MODE_11A, 96); TESTSETRATE(IEEE80211_MODE_11A, 108); } #undef SETRATE #undef INCRATE + ieee80211_init_channels(ic, NULL, &bands); + /* - * Taking yet more guesses here. + * To test for WPA support, we need to see if we can + * set AUTHENTICATION_MODE to WPA and read it back + * successfully. */ - for (i = 1; i < IEEE80211_CHAN_MAX; i++) { - int chanflag = 0; - - if (ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates) - chanflag |= IEEE80211_CHAN_G; - if (i <= 14) - chanflag |= IEEE80211_CHAN_B; - if (ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates && - i > 14) - chanflag = IEEE80211_CHAN_A; - if (chanflag == 0) - break; - ic->ic_channels[i].ic_freq = - ieee80211_ieee2mhz(i, chanflag); - ic->ic_channels[i].ic_flags = chanflag; + i = sizeof(arg); + arg = NDIS_80211_AUTHMODE_WPA; + r = ndis_set_info(sc, + OID_802_11_AUTHENTICATION_MODE, &arg, &i); + if (r == 0) { + r = ndis_get_info(sc, + OID_802_11_AUTHENTICATION_MODE, &arg, &i); + if (r == 0 && arg == NDIS_80211_AUTHMODE_WPA) + ic->ic_caps |= IEEE80211_C_WPA; } + /* + * To test for supported ciphers, we set each + * available encryption type in descending order. + * If ENC3 works, then we have WEP, TKIP and AES. + * If only ENC2 works, then we have WEP and TKIP. + * If only ENC1 works, then we have just WEP. + */ i = sizeof(arg); - r = ndis_get_info(sc, OID_802_11_WEP_STATUS, &arg, &i); - if (arg != NDIS_80211_WEPSTAT_NOTSUPPORTED) - ic->ic_caps |= IEEE80211_C_WEP; + arg = NDIS_80211_WEPSTAT_ENC3ENABLED; + r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); + if (r == 0) { + ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP + | IEEE80211_CRYPTO_TKIP + | IEEE80211_CRYPTO_AES_CCM; + goto got_crypto; + } + arg = NDIS_80211_WEPSTAT_ENC2ENABLED; + r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); + if (r == 0) { + ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP + | IEEE80211_CRYPTO_TKIP; + goto got_crypto; + } + arg = NDIS_80211_WEPSTAT_ENC1ENABLED; + r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); + if (r == 0) + ic->ic_cryptocaps |= IEEE80211_CRYPTO_WEP; +got_crypto: i = sizeof(arg); r = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &i); if (r == 0) ic->ic_caps |= IEEE80211_C_PMGT; - bcopy(eaddr, &ic->ic_myaddr, sizeof(eaddr)); - ieee80211_ifattach(ic); - ieee80211_media_init(ic, ieee80211_media_change, - ndis_media_status); - ic->ic_ibss_chan = IEEE80211_CHAN_ANYC; - ic->ic_bss->ni_chan = ic->ic_ibss_chan; + + r = ndis_get_info(sc, OID_802_11_TX_POWER_LEVEL, &arg, &i); + if (r == 0) + ic->ic_caps |= IEEE80211_C_TXPMGT; + + ieee80211_ifattach(ic, eaddr); + ic->ic_raw_xmit = ndis_raw_xmit; + ic->ic_scan_start = ndis_scan_start; + ic->ic_scan_end = ndis_scan_end; + ic->ic_set_channel = ndis_set_channel; + ic->ic_scan_curchan = ndis_scan_curchan; + ic->ic_scan_mindwell = ndis_scan_mindwell; + ic->ic_bsschan = IEEE80211_CHAN_ANYC; + //ic->ic_bss->ni_chan = ic->ic_bsschan; + ic->ic_vap_create = ndis_vap_create; + ic->ic_vap_delete = ndis_vap_delete; + ic->ic_update_mcast = ndis_update_mcast; + ic->ic_update_promisc = ndis_update_promisc; + + if (bootverbose) + ieee80211_announce(ic); + } else { ifmedia_init(&sc->ifmedia, IFM_IMASK, ndis_ifmedia_upd, ndis_ifmedia_sts); @@ -649,29 +953,65 @@ nonettypes: ether_ifattach(ifp, eaddr, NULL); } - if (error == 0) { - error = bus_setup_intr(dev, sc->ndis_irq, INTR_MPSAFE, - ndis_intr, sc, - &sc->ndis_intrhand, - ifp->if_serializer); - if (error) { - device_printf(dev, "couldn't set up irq\n"); - goto fail; - } +fail: + if (error) { + ndis_detach(dev); + return (error); } + if (sc->ndis_iftype == PNPBus && ndisusb_halt == 0) + return (error); - /* Override the status handler so we can detect link changes. */ - sc->ndis_block.nmb_status_func = ndis_linksts; - sc->ndis_block.nmb_statusdone_func = ndis_linksts_done; -fail: - if (error) - ndis_detach(dev); - else - /* We're done talking to the NIC for now; halt it. */ - ndis_halt_nic(sc); + DPRINTF(("attach done.\n")); + /* We're done talking to the NIC for now; halt it. */ + ndis_halt_nic(sc); + DPRINTF(("halting done.\n")); - return(error); + return (error); +} + +static struct ieee80211vap * +ndis_vap_create(struct ieee80211com *ic, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ndis_vap *nvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + nvp = (struct ndis_vap *) kmalloc(sizeof(struct ndis_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (nvp == NULL) + return NULL; + vap = &nvp->vap; + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); + /* override with driver methods */ + nvp->newstate = vap->iv_newstate; + vap->iv_newstate = ndis_newstate; + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, ndis_media_status); + ic->ic_opmode = opmode; + /* install key handing routines */ + vap->iv_key_set = ndis_add_key; + vap->iv_key_delete = ndis_del_key; + return vap; +} + +static void +ndis_vap_delete(struct ieee80211vap *vap) +{ + struct ndis_vap *nvp = NDIS_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = ic->ic_ifp; + struct ndis_softc *sc = ifp->if_softc; + + ndis_stop(sc); + callout_stop(&sc->ndis_scan_callout); /* XXX swildner callout_drain() */ + ieee80211_vap_detach(vap); + kfree(nvp, M_80211_VAP); } /* @@ -686,24 +1026,36 @@ ndis_detach(device_t dev) { struct ndis_softc *sc; struct ifnet *ifp; + driver_object *drv; sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; - - ifp->if_flags &= ~IFF_UP; + ifp = sc->ifp; + if (ifp != NULL) + ifp->if_flags &= ~IFF_UP; if (device_is_attached(dev)) { - lwkt_serialize_enter(ifp->if_serializer); ndis_stop(sc); - bus_teardown_intr(dev, sc->ndis_irq, sc->ndis_intrhand); - lwkt_serialize_exit(ifp->if_serializer); - - if (sc->ndis_80211) - ieee80211_ifdetach(&sc->ic); - else - ether_ifdetach(ifp); + if (ifp != NULL) { + if (sc->ndis_80211) + ieee80211_ifdetach(ifp->if_l2com); + else + ether_ifdetach(ifp); + } } + + if (sc->ndis_tickitem != NULL) + IoFreeWorkItem(sc->ndis_tickitem); + if (sc->ndis_startitem != NULL) + IoFreeWorkItem(sc->ndis_startitem); + if (sc->ndis_resetitem != NULL) + IoFreeWorkItem(sc->ndis_resetitem); + if (sc->ndis_inputitem != NULL) + IoFreeWorkItem(sc->ndis_inputitem); + if (sc->ndisusb_xferitem != NULL) + IoFreeWorkItem(sc->ndisusb_xferitem); + bus_generic_detach(dev); + ndis_unload_driver(sc); if (sc->ndis_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ndis_irq); @@ -717,18 +1069,40 @@ ndis_detach(device_t dev) bus_release_resource(dev, SYS_RES_MEMORY, sc->ndis_altmem_rid, sc->ndis_res_altmem); + if (ifp != NULL) + if_free(ifp); + + if (sc->ndis_iftype == PCMCIABus) + ndis_free_amem(sc); + if (sc->ndis_sc) ndis_destroy_dma(sc); - ndis_unload_driver((void *)ifp); + if (sc->ndis_txarray) + kfree(sc->ndis_txarray, M_DEVBUF); + + if (!sc->ndis_80211) + ifmedia_removeall(&sc->ifmedia); + + if (sc->ndis_txpool != NULL) + NdisFreePacketPool(sc->ndis_txpool); + + /* Destroy the PDO for this device. */ + + if (sc->ndis_iftype == PCIBus) + drv = windrv_lookup(0, "PCI Bus"); + else if (sc->ndis_iftype == PCMCIABus) + drv = windrv_lookup(0, "PCCARD Bus"); + else + drv = windrv_lookup(0, "USB Bus"); + if (drv == NULL) + panic("couldn't find driver object"); + windrv_destroy_pdo(drv, dev); if (sc->ndis_iftype == PCIBus) bus_dma_tag_destroy(sc->ndis_parent_tag); -#if __FreeBSD_version < 502113 - sysctl_ctx_free(&sc->ndis_ctx); -#endif - return(0); + return (0); } int @@ -737,16 +1111,17 @@ ndis_suspend(device_t dev) struct ndis_softc *sc; struct ifnet *ifp; + wlan_serialize_enter(); sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; - lwkt_serialize_enter(ifp->if_serializer); + ifp = sc->ifp; #ifdef notdef if (NDIS_INITIALIZED(sc)) - ndis_stop(sc); + ndis_stop(sc); #endif - lwkt_serialize_exit(ifp->if_serializer); - return(0); + + wlan_serialize_exit(); + return (0); } int @@ -755,114 +1130,376 @@ ndis_resume(device_t dev) struct ndis_softc *sc; struct ifnet *ifp; + wlan_serialize_enter(); sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; - lwkt_serialize_enter(ifp->if_serializer); if (NDIS_INITIALIZED(sc)) - ndis_init(sc); - lwkt_serialize_exit(ifp->if_serializer); + ndis_init(sc); - return(0); + wlan_serialize_exit(); + return (0); } /* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - * - * When handling received NDIS packets, the 'status' field in the - * out-of-band portion of the ndis_packet has special meaning. In the - * most common case, the underlying NDIS driver will set this field - * to NDIS_STATUS_SUCCESS, which indicates that it's ok for us to - * take posession of it. We then change the status field to - * NDIS_STATUS_PENDING to tell the driver that we now own the packet, - * and that we will return it at some point in the future via the - * return packet handler. - * - * If the driver hands us a packet with a status of NDIS_STATUS_RESOURCES, - * this means the driver is running out of packet/buffer resources and - * wants to maintain ownership of the packet. In this case, we have to - * copy the packet data into local storage and let the driver keep the - * packet. + * The following bunch of routines are here to support drivers that + * use the NdisMEthIndicateReceive()/MiniportTransferData() mechanism. + * The NdisMEthIndicateReceive() handler runs at DISPATCH_LEVEL for + * serialized miniports, or IRQL <= DISPATCH_LEVEL for deserialized + * miniports. */ -__stdcall static void -ndis_rxeof(ndis_handle adapter, ndis_packet **packets, uint32_t pktcnt) +static void +ndis_rxeof_eth(ndis_handle adapter, ndis_handle ctx, char *addr, void *hdr, + uint32_t hdrlen, void *lookahead, uint32_t lookaheadlen, uint32_t pktlen) { - struct ndis_softc *sc; ndis_miniport_block *block; + uint8_t irql = 0; + uint32_t status; + ndis_buffer *b; ndis_packet *p; - uint32_t s; - ndis_tcpip_csum *csum; - struct ifnet *ifp; - struct mbuf *m0, *m; - int i; + struct mbuf *m; + ndis_ethpriv *priv; - block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)(block->nmb_ifp); - ifp = block->nmb_ifp; + block = adapter; - for (i = 0; i < pktcnt; i++) { - p = packets[i]; - /* Stash the softc here so ptom can use it. */ - p->np_softc = sc; - if (ndis_ptom(&m0, p)) { - device_printf (sc->ndis_dev, "ptom failed\n"); - if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) - ndis_return_packet(sc, p); - } else { - if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) { - m = m_dup(m0, MB_DONTWAIT); - /* - * NOTE: we want to destroy the mbuf here, but - * we don't actually want to return it to the - * driver via the return packet handler. By - * bumping np_refcnt, we can prevent the - * ndis_return_packet() routine from actually - * doing anything. - */ - p->np_refcnt++; - m_freem(m0); - if (m == NULL) - ifp->if_ierrors++; - else - m0 = m; - } else - p->np_oob.npo_status = NDIS_STATUS_PENDING; - m0->m_pkthdr.rcvif = ifp; - ifp->if_ipackets++; + m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return; - /* Deal with checksum offload. */ + /* Save the data provided to us so far. */ - if (ifp->if_capenable & IFCAP_RXCSUM && - p->np_ext.npe_info[ndis_tcpipcsum_info] != NULL) { - s = (uintptr_t) - p->np_ext.npe_info[ndis_tcpipcsum_info]; - csum = (ndis_tcpip_csum *)&s; - if (csum->u.ntc_rxflags & - NDIS_RXCSUM_IP_PASSED) - m0->m_pkthdr.csum_flags |= - CSUM_IP_CHECKED|CSUM_IP_VALID; - if (csum->u.ntc_rxflags & - (NDIS_RXCSUM_TCP_PASSED | - NDIS_RXCSUM_UDP_PASSED)) { - m0->m_pkthdr.csum_flags |= - CSUM_DATA_VALID|CSUM_PSEUDO_HDR| - CSUM_FRAG_NOT_CHECKED; - m0->m_pkthdr.csum_data = 0xFFFF; - } - } + m->m_len = lookaheadlen + hdrlen; + m->m_pkthdr.len = pktlen + hdrlen; + m->m_next = NULL; + m_copyback(m, 0, hdrlen, hdr); + m_copyback(m, hdrlen, lookaheadlen, lookahead); - ifp->if_input(ifp, m0); - } + /* Now create a fake NDIS_PACKET to hold the data */ + + NdisAllocatePacket(&status, &p, block->nmb_rxpool); + + if (status != NDIS_STATUS_SUCCESS) { + m_freem(m); + return; + } + + p->np_m0 = m; + + b = IoAllocateMdl(m->m_data, m->m_pkthdr.len, FALSE, FALSE, NULL); + + if (b == NULL) { + NdisFreePacket(p); + m_freem(m); + return; } + + p->np_private.npp_head = p->np_private.npp_tail = b; + p->np_private.npp_totlen = m->m_pkthdr.len; + + /* Save the packet RX context somewhere. */ + priv = (ndis_ethpriv *)&p->np_protocolreserved; + priv->nep_ctx = ctx; + + if (!NDIS_SERIALIZED(block)) + KeAcquireSpinLock(&block->nmb_lock, &irql); + + InsertTailList((&block->nmb_packetlist), (&p->np_list)); + + if (!NDIS_SERIALIZED(block)) + KeReleaseSpinLock(&block->nmb_lock, irql); } /* - * A frame was downloaded to the chip. It's safe for us to clean up - * the list buffers. + * NdisMEthIndicateReceiveComplete() handler, runs at DISPATCH_LEVEL + * for serialized miniports, or IRQL <= DISPATCH_LEVEL for deserialized + * miniports. */ -__stdcall static void -ndis_txeof(ndis_handle adapter, ndis_packet *packet, ndis_status status) +static void +ndis_rxeof_done(ndis_handle adapter) +{ + struct ndis_softc *sc; + ndis_miniport_block *block; + + block = adapter; + + /* Schedule transfer/RX of queued packets. */ + + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + + KeInsertQueueDpc(&sc->ndis_rxdpc, NULL, NULL); +} + +/* + * MiniportTransferData() handler, runs at DISPATCH_LEVEL. + */ +static void +ndis_rxeof_xfr(kdpc *dpc, ndis_handle adapter, void *sysarg1, void *sysarg2) +{ + ndis_miniport_block *block; + struct ndis_softc *sc; + ndis_packet *p; + list_entry *l; + uint32_t status; + ndis_ethpriv *priv; + struct ifnet *ifp; + struct mbuf *m; + + block = adapter; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ifp = sc->ifp; + + KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); + + l = block->nmb_packetlist.nle_flink; + while(!IsListEmpty(&block->nmb_packetlist)) { + l = RemoveHeadList((&block->nmb_packetlist)); + p = CONTAINING_RECORD(l, ndis_packet, np_list); + InitializeListHead((&p->np_list)); + + priv = (ndis_ethpriv *)&p->np_protocolreserved; + m = p->np_m0; + p->np_softc = sc; + p->np_m0 = NULL; + + KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); + + status = MSCALL6(sc->ndis_chars->nmc_transferdata_func, + p, &p->np_private.npp_totlen, block, priv->nep_ctx, + m->m_len, m->m_pkthdr.len - m->m_len); + + KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); + + /* + * If status is NDIS_STATUS_PENDING, do nothing and + * wait for a callback to the ndis_rxeof_xfr_done() + * handler. + */ + + m->m_len = m->m_pkthdr.len; + m->m_pkthdr.rcvif = ifp; + + if (status == NDIS_STATUS_SUCCESS) { + IoFreeMdl(p->np_private.npp_head); + NdisFreePacket(p); + KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); + IF_ENQUEUE(&sc->ndis_rxqueue, m); + KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); + IoQueueWorkItem(sc->ndis_inputitem, + (io_workitem_func)ndis_inputtask_wrap, + WORKQUEUE_CRITICAL, ifp); + } + + if (status == NDIS_STATUS_FAILURE) + m_freem(m); + + /* Advance to next packet */ + l = block->nmb_packetlist.nle_flink; + } + + KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); +} + +/* + * NdisMTransferDataComplete() handler, runs at DISPATCH_LEVEL. + */ +static void +ndis_rxeof_xfr_done(ndis_handle adapter, ndis_packet *packet, uint32_t status, + uint32_t len) +{ + ndis_miniport_block *block; + struct ndis_softc *sc; + struct ifnet *ifp; + struct mbuf *m; + + block = adapter; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ifp = sc->ifp; + + m = packet->np_m0; + IoFreeMdl(packet->np_private.npp_head); + NdisFreePacket(packet); + + if (status != NDIS_STATUS_SUCCESS) { + m_freem(m); + return; + } + + m->m_len = m->m_pkthdr.len; + m->m_pkthdr.rcvif = ifp; + KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); + IF_ENQUEUE(&sc->ndis_rxqueue, m); + KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); + IoQueueWorkItem(sc->ndis_inputitem, + (io_workitem_func)ndis_inputtask_wrap, + WORKQUEUE_CRITICAL, ifp); +} +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + * + * When handling received NDIS packets, the 'status' field in the + * out-of-band portion of the ndis_packet has special meaning. In the + * most common case, the underlying NDIS driver will set this field + * to NDIS_STATUS_SUCCESS, which indicates that it's ok for us to + * take posession of it. We then change the status field to + * NDIS_STATUS_PENDING to tell the driver that we now own the packet, + * and that we will return it at some point in the future via the + * return packet handler. + * + * If the driver hands us a packet with a status of NDIS_STATUS_RESOURCES, + * this means the driver is running out of packet/buffer resources and + * wants to maintain ownership of the packet. In this case, we have to + * copy the packet data into local storage and let the driver keep the + * packet. + */ +static void +ndis_rxeof(ndis_handle adapter, ndis_packet **packets, uint32_t pktcnt) +{ + struct ndis_softc *sc; + ndis_miniport_block *block; + ndis_packet *p; + uint32_t s; + ndis_tcpip_csum *csum; + struct ifnet *ifp; + struct mbuf *m0, *m; + int i; + + block = (ndis_miniport_block *)adapter; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ifp = sc->ifp; + + /* + * There's a slim chance the driver may indicate some packets + * before we're completely ready to handle them. If we detect this, + * we need to return them to the miniport and ignore them. + */ + if (!(ifp->if_flags & IFF_RUNNING)) { + for (i = 0; i < pktcnt; i++) { + p = packets[i]; + if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) { + p->np_refcnt++; + ndis_return_packet(p); + } + } + return; + } + + for (i = 0; i < pktcnt; i++) { + p = packets[i]; + /* Stash the softc here so ptom can use it. */ + p->np_softc = sc; + if (ndis_ptom(&m0, p)) { + device_printf(sc->ndis_dev, "ptom failed\n"); + if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) + ndis_return_packet(p); + } else { +#ifdef notdef + if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) { + m = m_dup(m0, MB_DONTWAIT); + /* + * NOTE: we want to destroy the mbuf here, but + * we don't actually want to return it to the + * driver via the return packet handler. By + * bumping np_refcnt, we can prevent the + * ndis_return_packet() routine from actually + * doing anything. + */ + p->np_refcnt++; + m_freem(m0); + if (m == NULL) + ifp->if_ierrors++; + else + m0 = m; + } else + p->np_oob.npo_status = NDIS_STATUS_PENDING; +#endif + m = m_dup(m0, MB_DONTWAIT); + if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) + p->np_refcnt++; + else + p->np_oob.npo_status = NDIS_STATUS_PENDING; + m_freem(m0); + if (m == NULL) { + ifp->if_ierrors++; + continue; + } + m0 = m; + m0->m_pkthdr.rcvif = ifp; + + /* Deal with checksum offload. */ + + if (ifp->if_capenable & IFCAP_RXCSUM && + p->np_ext.npe_info[ndis_tcpipcsum_info] != NULL) { + s = (uintptr_t) + p->np_ext.npe_info[ndis_tcpipcsum_info]; + csum = (ndis_tcpip_csum *)&s; + if (csum->u.ntc_rxflags & + NDIS_RXCSUM_IP_PASSED) + m0->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED|CSUM_IP_VALID; + if (csum->u.ntc_rxflags & + (NDIS_RXCSUM_TCP_PASSED | + NDIS_RXCSUM_UDP_PASSED)) { + m0->m_pkthdr.csum_flags |= + CSUM_DATA_VALID|CSUM_PSEUDO_HDR; + m0->m_pkthdr.csum_data = 0xFFFF; + } + } + + KeAcquireSpinLockAtDpcLevel(&sc->ndis_rxlock); + IF_ENQUEUE(&sc->ndis_rxqueue, m0); + KeReleaseSpinLockFromDpcLevel(&sc->ndis_rxlock); + IoQueueWorkItem(sc->ndis_inputitem, + (io_workitem_func)ndis_inputtask_wrap, + WORKQUEUE_CRITICAL, ifp); + } + } +} + +/* + * This routine is run at PASSIVE_LEVEL. We use this routine to pass + * packets into the stack in order to avoid calling (*ifp->if_input)() + * with any locks held (at DISPATCH_LEVEL, we'll be holding the + * 'dispatch level' per-cpu sleep lock). + */ +static void +ndis_inputtask(device_object *dobj, void *arg) +{ + ndis_miniport_block *block; + struct ifnet *ifp; + struct ndis_softc *sc; + struct mbuf *m; + struct ieee80211com *ic; + struct ieee80211vap *vap; + uint8_t irql; + + ifp = arg; + sc = ifp->if_softc; + ic = ifp->if_l2com; + vap = TAILQ_FIRST(&ic->ic_vaps); + block = dobj->do_devext; + + KeAcquireSpinLock(&sc->ndis_rxlock, &irql); + while(1) { + IF_DEQUEUE(&sc->ndis_rxqueue, m); + if (m == NULL) + break; + KeReleaseSpinLock(&sc->ndis_rxlock, irql); + if ((sc->ndis_80211 != 0) && (vap != NULL)) + vap->iv_deliver_data(vap, vap->iv_bss, m); + else + (*ifp->if_input)(ifp, m); + KeAcquireSpinLock(&sc->ndis_rxlock, &irql); + } + KeReleaseSpinLock(&sc->ndis_rxlock, irql); +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ +static void +ndis_txeof(ndis_handle adapter, ndis_packet *packet, ndis_status status) { struct ndis_softc *sc; ndis_miniport_block *block; @@ -871,8 +1508,8 @@ ndis_txeof(ndis_handle adapter, ndis_packet *packet, ndis_status status) struct mbuf *m; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)block->nmb_ifp; - ifp = block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ifp = sc->ifp; m = packet->np_m0; idx = packet->np_txidx; @@ -882,6 +1519,7 @@ ndis_txeof(ndis_handle adapter, ndis_packet *packet, ndis_status status) ndis_free_packet(packet); m_freem(m); + NDISMTX_LOCK(sc); sc->ndis_txarray[idx] = NULL; sc->ndis_txpending++; @@ -889,22 +1527,55 @@ ndis_txeof(ndis_handle adapter, ndis_packet *packet, ndis_status status) ifp->if_opackets++; else ifp->if_oerrors++; - ifp->if_timer = 0; + + sc->ndis_tx_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; - ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE); + NDISMTX_UNLOCK(sc); + + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_starttask_wrap, + WORKQUEUE_CRITICAL, ifp); } -__stdcall static void -ndis_linksts(ndis_handle adapter, ndis_status status, void *sbuf, uint32_t slen) +static void +ndis_linksts(ndis_handle adapter, ndis_status status, void *sbuf, + uint32_t slen) { ndis_miniport_block *block; + struct ndis_softc *sc; block = adapter; - block->nmb_getstat = status; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + sc->ndis_sts = status; + + /* Event list is all full up, drop this one. */ + + NDISMTX_LOCK(sc); + if (sc->ndis_evt[sc->ndis_evtpidx].ne_sts) { + NDISMTX_UNLOCK(sc); + return; + } + + /* Cache the event. */ + + if (slen) { + sc->ndis_evt[sc->ndis_evtpidx].ne_buf = kmalloc(slen, + M_TEMP, M_NOWAIT); + if (sc->ndis_evt[sc->ndis_evtpidx].ne_buf == NULL) { + NDISMTX_UNLOCK(sc); + return; + } + bcopy((char *)sbuf, + sc->ndis_evt[sc->ndis_evtpidx].ne_buf, slen); + } + sc->ndis_evt[sc->ndis_evtpidx].ne_sts = status; + sc->ndis_evt[sc->ndis_evtpidx].ne_len = slen; + NDIS_EVTINC(sc->ndis_evtpidx); + NDISMTX_UNLOCK(sc); } -__stdcall static void +static void ndis_linksts_done(ndis_handle adapter) { ndis_miniport_block *block; @@ -912,20 +1583,26 @@ ndis_linksts_done(ndis_handle adapter) struct ifnet *ifp; block = adapter; - ifp = block->nmb_ifp; - sc = ifp->if_softc; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ifp = sc->ifp; if (!NDIS_INITIALIZED(sc)) return; - switch (block->nmb_getstat) { + switch (sc->ndis_sts) { case NDIS_STATUS_MEDIA_CONNECT: - ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE); - ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE); + IoQueueWorkItem(sc->ndis_tickitem, + (io_workitem_func)ndis_ticktask_wrap, + WORKQUEUE_CRITICAL, sc); + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_starttask_wrap, + WORKQUEUE_CRITICAL, ifp); break; case NDIS_STATUS_MEDIA_DISCONNECT: if (sc->ndis_link) - ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE); + IoQueueWorkItem(sc->ndis_tickitem, + (io_workitem_func)ndis_ticktask_wrap, + WORKQUEUE_CRITICAL, sc); break; default: break; @@ -933,115 +1610,96 @@ ndis_linksts_done(ndis_handle adapter) } static void -ndis_intrtask(void *arg) -{ - struct ndis_softc *sc; - struct ifnet *ifp; - uint8_t irql; - - sc = arg; - ifp = &sc->arpcom.ac_if; - - lwkt_serialize_enter(ifp->if_serializer); - irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL); - ndis_intrhand(sc); - FASTCALL1(hal_lower_irql, irql); - ndis_enable_intr(sc); - lwkt_serialize_exit(ifp->if_serializer); -} - -static void -ndis_intr(void *arg) +ndis_tick(void *xsc) { struct ndis_softc *sc; - struct ifnet *ifp; - int is_our_intr = 0; - int call_isr = 0; - - sc = arg; - ifp = &sc->arpcom.ac_if; - if (sc->ndis_block.nmb_miniportadapterctx == NULL) - return; + sc = xsc; - if (sc->ndis_block.nmb_interrupt->ni_isrreq == TRUE) - ndis_isr(sc, &is_our_intr, &call_isr); - else { - ndis_disable_intr(sc); - call_isr = 1; + if (sc->ndis_hang_timer && --sc->ndis_hang_timer == 0) { + IoQueueWorkItem(sc->ndis_tickitem, + (io_workitem_func)ndis_ticktask_wrap, + WORKQUEUE_CRITICAL, sc); + sc->ndis_hang_timer = sc->ndis_block->nmb_checkforhangsecs; } - if ((is_our_intr || call_isr)) - ndis_sched(ndis_intrtask, ifp, NDIS_SWI); -} - -static void -ndis_tick(void *xsc) -{ - struct ndis_softc *sc; + if (sc->ndis_tx_timer && --sc->ndis_tx_timer == 0) { + sc->ifp->if_oerrors++; + device_printf(sc->ndis_dev, "watchdog timeout\n"); - sc = xsc; + IoQueueWorkItem(sc->ndis_resetitem, + (io_workitem_func)ndis_resettask_wrap, + WORKQUEUE_CRITICAL, sc); + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_starttask_wrap, + WORKQUEUE_CRITICAL, sc->ifp); + } - ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE); - callout_reset(&sc->ndis_stat_timer, - hz *sc->ndis_block.nmb_checkforhangsecs, ndis_tick, sc); + callout_reset(&sc->ndis_stat_callout, hz, ndis_tick, sc); } static void -ndis_ticktask(void *xsc) +ndis_ticktask(device_object *d, void *xsc) { struct ndis_softc *sc; - struct ifnet *ifp; - __stdcall ndis_checkforhang_handler hangfunc; + struct ieee80211com *ic; + struct ieee80211vap *vap; + ndis_checkforhang_handler hangfunc; uint8_t rval; - ndis_media_state linkstate; - int error, len; sc = xsc; - ifp = &sc->arpcom.ac_if; + ic = sc->ifp->if_l2com; + vap = TAILQ_FIRST(&ic->ic_vaps); + + NDIS_LOCK(sc); + if (!NDIS_INITIALIZED(sc)) { + NDIS_UNLOCK(sc); + return; + } + NDIS_UNLOCK(sc); - lwkt_serialize_enter(ifp->if_serializer); - hangfunc = sc->ndis_chars.nmc_checkhang_func; + hangfunc = sc->ndis_chars->nmc_checkhang_func; if (hangfunc != NULL) { - rval = hangfunc(sc->ndis_block.nmb_miniportadapterctx); + rval = MSCALL1(hangfunc, + sc->ndis_block->nmb_miniportadapterctx); if (rval == TRUE) { ndis_reset_nic(sc); - lwkt_serialize_exit(ifp->if_serializer); return; } } - len = sizeof(linkstate); - error = ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS, - (void *)&linkstate, &len); - - if (sc->ndis_link == 0 && linkstate == nmc_connected) { - device_printf(sc->ndis_dev, "link up\n"); + NDIS_LOCK(sc); + if (sc->ndis_link == 0 && + sc->ndis_sts == NDIS_STATUS_MEDIA_CONNECT) { sc->ndis_link = 1; - if (sc->ndis_80211) + NDIS_UNLOCK(sc); + if ((sc->ndis_80211 != 0) && (vap != NULL)) { ndis_getstate_80211(sc); -#ifdef LINK_STATE_UP - sc->arpcom.ac_if.if_link_state = LINK_STATE_UP; - rt_ifmsg(&(sc->arpcom.ac_if)); -#endif /* LINK_STATE_UP */ + ieee80211_new_state(vap, IEEE80211_S_RUN, -1); + } + NDIS_LOCK(sc); + sc->ifp->if_link_state = LINK_STATE_UP; + if_link_state_change(sc->ifp); } - if (sc->ndis_link == 1 && linkstate == nmc_disconnected) { - device_printf(sc->ndis_dev, "link down\n"); + if (sc->ndis_link == 1 && + sc->ndis_sts == NDIS_STATUS_MEDIA_DISCONNECT) { sc->ndis_link = 0; -#ifdef LINK_STATE_DOWN - sc->arpcom.ac_if.if_link_state = LINK_STATE_DOWN; - rt_ifmsg(&(sc->arpcom.ac_if)); -#endif /* LINK_STATE_DOWN */ + NDIS_UNLOCK(sc); + if ((sc->ndis_80211 != 0) && (vap != NULL)) + ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); + NDIS_LOCK(sc); + sc->ifp->if_link_state = LINK_STATE_DOWN; + if_link_state_change(sc->ifp); } - lwkt_serialize_exit(ifp->if_serializer); + NDIS_UNLOCK(sc); } static void ndis_map_sclist(void *arg, bus_dma_segment_t *segs, int nseg, - bus_size_t mapsize, int error) + bus_size_t mapsize, int error) { struct ndis_sc_list *sclist; int i; @@ -1059,14 +1717,39 @@ ndis_map_sclist(void *arg, bus_dma_segment_t *segs, int nseg, } } +static int +ndis_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + /* no support; just discard */ + m_freem(m); + ieee80211_free_node(ni); + return (0); +} + +static void +ndis_update_mcast(struct ifnet *ifp) +{ + struct ndis_softc *sc = ifp->if_softc; + + ndis_setmulti(sc); +} + +static void +ndis_update_promisc(struct ifnet *ifp) +{ + /* not supported */ +} + static void -ndis_starttask(void *arg) +ndis_starttask(device_object *d, void *arg) { struct ifnet *ifp; ifp = arg; + if (!ifq_is_empty(&ifp->if_snd)) - if_devstart(ifp); + ndis_start(ifp); } /* @@ -1081,7 +1764,6 @@ ndis_starttask(void *arg) * we need to perform busdma work here. Those that use map registers * will do the mapping themselves on a buffer by buffer basis. */ - static void ndis_start(struct ifnet *ifp) { @@ -1089,11 +1771,13 @@ ndis_start(struct ifnet *ifp) struct mbuf *m = NULL; ndis_packet **p0 = NULL, *p = NULL; ndis_tcpip_csum *csum; - int pcnt = 0; + int pcnt = 0, status; sc = ifp->if_softc; + NDIS_LOCK(sc); if (!sc->ndis_link || ifp->if_flags & IFF_OACTIVE) { + NDIS_UNLOCK(sc); return; } @@ -1104,10 +1788,15 @@ ndis_start(struct ifnet *ifp) if (m == NULL) break; - sc->ndis_txarray[sc->ndis_txidx] = NULL; + NdisAllocatePacket(&status, + &sc->ndis_txarray[sc->ndis_txidx], sc->ndis_txpool); + + if (status != NDIS_STATUS_SUCCESS) + break; if (ndis_mtop(m, &sc->ndis_txarray[sc->ndis_txidx])) { - m_freem(m); + ifq_prepend(&ifp->if_snd, m); + NDIS_UNLOCK(sc); return; } @@ -1159,8 +1848,8 @@ ndis_start(struct ifnet *ifp) * If there's a BPF listener, bounce a copy of this frame * to him. */ - - BPF_MTAP(ifp, m); + if (!sc->ndis_80211) /* XXX handle 80211 */ + BPF_MTAP(ifp, m); /* * The array that p0 points to must appear contiguous, @@ -1169,12 +1858,12 @@ ndis_start(struct ifnet *ifp) * so the this batch of packets can be transmitted, then * wait for txeof to ask us to send the rest. */ - if (sc->ndis_txidx == 0) break; } if (pcnt == 0) { + NDIS_UNLOCK(sc); return; } @@ -1184,20 +1873,31 @@ ndis_start(struct ifnet *ifp) /* * Set a timeout in case the chip goes out to lunch. */ - ifp->if_timer = 5; + sc->ndis_tx_timer = 5; - if (sc->ndis_maxpkts == 1) - ndis_send_packet(sc, p); - else - ndis_send_packets(sc, p0, pcnt); -} + NDIS_UNLOCK(sc); -static void -ndis_init(void *xsc) + /* + * According to NDIS documentation, if a driver exports + * a MiniportSendPackets() routine, we prefer that over + * a MiniportSend() routine (which sends just a single + * packet). + */ + if (sc->ndis_chars->nmc_sendmulti_func != NULL) + ndis_send_packets(sc, p0, pcnt); + else + ndis_send_packet(sc, p); + + return; +} + +static void +ndis_init(void *xsc) { struct ndis_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - int i, error; + struct ifnet *ifp = sc->ifp; + struct ieee80211com *ic = ifp->if_l2com; + int i, len, error; /* * Avoid reintializing the link unnecessarily. @@ -1205,15 +1905,22 @@ ndis_init(void *xsc) * fixing the upper layer modules so they don't * call ifp->if_init() quite as often. */ - if (sc->ndis_link && sc->ndis_skip) + if (sc->ndis_link) return; /* * Cancel pending I/O and free all RX/TX buffers. */ ndis_stop(sc); - if (ndis_init_nic(sc)) - return; + + if (!(sc->ndis_iftype == PNPBus && ndisusb_halt == 0)) { + error = ndis_init_nic(sc); + if (error != 0) { + device_printf(sc->ndis_dev, + "failed to initialize the device: %d\n", error); + return; + } + } /* Init our MAC address */ @@ -1227,13 +1934,20 @@ ndis_init(void *xsc) if (ifp->if_flags & IFF_PROMISC) sc->ndis_filter |= NDIS_PACKET_TYPE_PROMISCUOUS; - i = sizeof(sc->ndis_filter); + len = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, - &sc->ndis_filter, &i); + &sc->ndis_filter, &len); if (error) - device_printf (sc->ndis_dev, "set filter failed: %d\n", error); + device_printf(sc->ndis_dev, "set filter failed: %d\n", error); + + /* + * Set lookahead. + */ + i = ifp->if_mtu; + len = sizeof(i); + ndis_set_info(sc, OID_GEN_CURRENT_LOOKAHEAD, &i, &len); /* * Program the multicast filter, if necessary. @@ -1243,18 +1957,18 @@ ndis_init(void *xsc) /* Setup task offload. */ ndis_set_offload(sc); - /* Enable interrupts. */ - ndis_enable_intr(sc); - - if (sc->ndis_80211) - ndis_setstate_80211(sc); + NDIS_LOCK(sc); sc->ndis_txidx = 0; sc->ndis_txpending = sc->ndis_maxpkts; sc->ndis_link = 0; + sc->ifp->if_link_state = LINK_STATE_UNKNOWN; + if_link_state_change(sc->ifp); + ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; + sc->ndis_tx_timer = 0; /* * Some drivers don't set this value. The NDIS spec says @@ -1262,12 +1976,16 @@ ndis_init(void *xsc) * seconds." We use 3 seconds, because it seems for some * drivers, exactly 2 seconds is too fast. */ + if (sc->ndis_block->nmb_checkforhangsecs == 0) + sc->ndis_block->nmb_checkforhangsecs = 3; - if (sc->ndis_block.nmb_checkforhangsecs == 0) - sc->ndis_block.nmb_checkforhangsecs = 3; + sc->ndis_hang_timer = sc->ndis_block->nmb_checkforhangsecs; + callout_reset(&sc->ndis_stat_callout, hz, ndis_tick, sc); + NDIS_UNLOCK(sc); - callout_reset(&sc->ndis_stat_timer, - hz * sc->ndis_block.nmb_checkforhangsecs, ndis_tick, sc); + /* XXX force handling */ + if (sc->ndis_80211) + ieee80211_start_all(ic); /* start all vap's */ } /* @@ -1283,7 +2001,7 @@ ndis_ifmedia_upd(struct ifnet *ifp) if (NDIS_INITIALIZED(sc)) ndis_init(sc); - return(0); + return (0); } /* @@ -1295,7 +2013,7 @@ ndis_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) struct ndis_softc *sc; uint32_t media_info; ndis_media_state linkstate; - int error, len; + int len; ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; @@ -1305,17 +2023,17 @@ ndis_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) return; len = sizeof(linkstate); - error = ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS, + ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS, (void *)&linkstate, &len); len = sizeof(media_info); - error = ndis_get_info(sc, OID_GEN_LINK_SPEED, + ndis_get_info(sc, OID_GEN_LINK_SPEED, (void *)&media_info, &len); if (linkstate == nmc_connected) ifmr->ifm_status |= IFM_ACTIVE; - switch(media_info) { + switch (media_info) { case 100000: ifmr->ifm_active |= IFM_10_T; break; @@ -1331,22 +2049,177 @@ ndis_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) } } +static int +ndis_set_cipher(struct ndis_softc *sc, int cipher) +{ + struct ieee80211com *ic; + int rval = 0, len; + uint32_t arg, save; + + ic = sc->ifp->if_l2com; + + len = sizeof(arg); + + if (cipher == WPA_CSE_WEP40 || cipher == WPA_CSE_WEP104) { + if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_WEP)) + return (ENOTSUP); + arg = NDIS_80211_WEPSTAT_ENC1ENABLED; + } + + if (cipher == WPA_CSE_TKIP) { + if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIP)) + return (ENOTSUP); + arg = NDIS_80211_WEPSTAT_ENC2ENABLED; + } + + if (cipher == WPA_CSE_CCMP) { + if (!(ic->ic_cryptocaps & IEEE80211_CRYPTO_AES_CCM)) + return (ENOTSUP); + arg = NDIS_80211_WEPSTAT_ENC3ENABLED; + } + + DPRINTF(("Setting cipher to %d\n", arg)); + save = arg; + rval = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); + + if (rval) + return (rval); + + /* Check that the cipher was set correctly. */ + + len = sizeof(save); + rval = ndis_get_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); + + if (rval != 0 || arg != save) + return (ENODEV); + + return (0); +} + +/* + * WPA is hairy to set up. Do the work in a separate routine + * so we don't clutter the setstate function too much. + * Important yet undocumented fact: first we have to set the + * authentication mode, _then_ we enable the ciphers. If one + * of the WPA authentication modes isn't enabled, the driver + * might not permit the TKIP or AES ciphers to be selected. + */ +static int +ndis_set_wpa(struct ndis_softc *sc, void *ie, int ielen) +{ + struct ieee80211_ie_wpa *w; + struct ndis_ie *n; + char *pos; + uint32_t arg; + int i; + + /* + * Apparently, the only way for us to know what ciphers + * and key management/authentication mode to use is for + * us to inspect the optional information element (IE) + * stored in the 802.11 state machine. This IE should be + * supplied by the WPA supplicant. + */ + + w = (struct ieee80211_ie_wpa *)ie; + + /* Check for the right kind of IE. */ + if (w->wpa_id != IEEE80211_ELEMID_VENDOR) { + DPRINTF(("Incorrect IE type %d\n", w->wpa_id)); + return (EINVAL); + } + + /* Skip over the ucast cipher OIDs. */ + pos = (char *)&w->wpa_uciphers[0]; + pos += w->wpa_uciphercnt * sizeof(struct ndis_ie); + + /* Skip over the authmode count. */ + pos += sizeof(u_int16_t); + + /* + * Check for the authentication modes. I'm + * pretty sure there's only supposed to be one. + */ + + n = (struct ndis_ie *)pos; + if (n->ni_val == WPA_ASE_NONE) + arg = NDIS_80211_AUTHMODE_WPANONE; + + if (n->ni_val == WPA_ASE_8021X_UNSPEC) + arg = NDIS_80211_AUTHMODE_WPA; + + if (n->ni_val == WPA_ASE_8021X_PSK) + arg = NDIS_80211_AUTHMODE_WPAPSK; + + DPRINTF(("Setting WPA auth mode to %d\n", arg)); + i = sizeof(arg); + if (ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i)) + return (ENOTSUP); + i = sizeof(arg); + ndis_get_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i); + + /* Now configure the desired ciphers. */ + + /* First, set up the multicast group cipher. */ + n = (struct ndis_ie *)&w->wpa_mcipher[0]; + + if (ndis_set_cipher(sc, n->ni_val)) + return (ENOTSUP); + + /* Now start looking around for the unicast ciphers. */ + pos = (char *)&w->wpa_uciphers[0]; + n = (struct ndis_ie *)pos; + + for (i = 0; i < w->wpa_uciphercnt; i++) { + if (ndis_set_cipher(sc, n->ni_val)) + return (ENOTSUP); + n++; + } + + return (0); +} + +static void +ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ndis_softc *sc = vap->iv_ic->ic_ifp->if_softc; + uint32_t txrate; + int len; + + if (!NDIS_INITIALIZED(sc)) + return; + + len = sizeof(txrate); + if (ndis_get_info(sc, OID_GEN_LINK_SPEED, &txrate, &len) == 0) + vap->iv_bss->ni_txrate = txrate / 5000; + ieee80211_media_status(ifp, imr); +} + static void ndis_setstate_80211(struct ndis_softc *sc) { struct ieee80211com *ic; - ndis_80211_ssid ssid; + struct ieee80211vap *vap; + ndis_80211_macaddr bssid; ndis_80211_config config; - ndis_80211_wep wep; - int i, rval = 0, len; + int rval = 0, len; uint32_t arg; struct ifnet *ifp; - ic = &sc->ic; - ifp = &sc->ic.ic_ac.ac_if; + ifp = sc->ifp; + ic = ifp->if_l2com; + vap = TAILQ_FIRST(&ic->ic_vaps); - if (!NDIS_INITIALIZED(sc)) + if (!NDIS_INITIALIZED(sc)) { + DPRINTF(("%s: NDIS not initialized\n", __func__)); return; + } + + /* Disassociate and turn off radio. */ + len = sizeof(arg); + arg = 1; + ndis_set_info(sc, OID_802_11_DISASSOCIATE, &arg, &len); /* Set network infrastructure mode. */ @@ -1361,86 +2234,231 @@ ndis_setstate_80211(struct ndis_softc *sc) if (rval) device_printf (sc->ndis_dev, "set infra failed: %d\n", rval); + /* Set power management */ + len = sizeof(arg); + if (vap->iv_flags & IEEE80211_F_PMGTON) + arg = NDIS_80211_POWERMODE_FAST_PSP; + else + arg = NDIS_80211_POWERMODE_CAM; + ndis_set_info(sc, OID_802_11_POWER_MODE, &arg, &len); + + /* Set TX power */ + if ((ic->ic_caps & IEEE80211_C_TXPMGT) && + ic->ic_txpowlimit < (sizeof(dBm2mW) / sizeof(dBm2mW[0]))) { + arg = dBm2mW[ic->ic_txpowlimit]; + len = sizeof(arg); + ndis_set_info(sc, OID_802_11_TX_POWER_LEVEL, &arg, &len); + } + + /* + * Default encryption mode to off, authentication + * to open and privacy to 'accept everything.' + */ + len = sizeof(arg); + arg = NDIS_80211_WEPSTAT_DISABLED; + ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); + + len = sizeof(arg); + arg = NDIS_80211_AUTHMODE_OPEN; + ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); + + /* + * Note that OID_802_11_PRIVACY_FILTER is optional: + * not all drivers implement it. + */ + len = sizeof(arg); + arg = NDIS_80211_PRIVFILT_8021XWEP; + ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len); + + len = sizeof(config); + bzero((char *)&config, len); + config.nc_length = len; + config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh); + rval = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &len); + + /* + * Some drivers expect us to initialize these values, so + * provide some defaults. + */ + + if (config.nc_beaconperiod == 0) + config.nc_beaconperiod = 100; + if (config.nc_atimwin == 0) + config.nc_atimwin = 100; + if (config.nc_fhconfig.ncf_dwelltime == 0) + config.nc_fhconfig.ncf_dwelltime = 200; + if (rval == 0 && ic->ic_bsschan != IEEE80211_CHAN_ANYC) { + int chan, chanflag; + + chan = ieee80211_chan2ieee(ic, ic->ic_bsschan); + chanflag = config.nc_dsconfig > 2500000 ? IEEE80211_CHAN_2GHZ : + IEEE80211_CHAN_5GHZ; + if (chan != ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0)) { + config.nc_dsconfig = + ic->ic_bsschan->ic_freq * 1000; + len = sizeof(config); + config.nc_length = len; + config.nc_fhconfig.ncf_length = + sizeof(ndis_80211_config_fh); + DPRINTF(("Setting channel to %ukHz\n", config.nc_dsconfig)); + rval = ndis_set_info(sc, OID_802_11_CONFIGURATION, + &config, &len); + if (rval) + device_printf(sc->ndis_dev, "couldn't change " + "DS config to %ukHz: %d\n", + config.nc_dsconfig, rval); + } + } else if (rval) + device_printf(sc->ndis_dev, "couldn't retrieve " + "channel info: %d\n", rval); + + /* Set the BSSID to our value so the driver doesn't associate */ + len = IEEE80211_ADDR_LEN; + bcopy(IF_LLADDR(ifp), bssid, len); + DPRINTF(("Setting BSSID to %6D\n", (uint8_t *)&bssid, ":")); + rval = ndis_set_info(sc, OID_802_11_BSSID, &bssid, &len); + if (rval) + device_printf(sc->ndis_dev, + "setting BSSID failed: %d\n", rval); +} + +static void +ndis_auth_and_assoc(struct ndis_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211com *ic; + struct ieee80211_node *ni; + ndis_80211_ssid ssid; + ndis_80211_macaddr bssid; + ndis_80211_wep wep; + int i, rval = 0, len, error; + uint32_t arg; + struct ifnet *ifp; + + ifp = sc->ifp; + ic = ifp->if_l2com; + ni = vap->iv_bss; + + if (!NDIS_INITIALIZED(sc)) { + DPRINTF(("%s: NDIS not initialized\n", __func__)); + return; + } + + /* Initial setup */ + ndis_setstate_80211(sc); + + /* Set network infrastructure mode. */ + + len = sizeof(arg); + if (vap->iv_opmode == IEEE80211_M_IBSS) + arg = NDIS_80211_NET_INFRA_IBSS; + else + arg = NDIS_80211_NET_INFRA_BSS; + + rval = ndis_set_info(sc, OID_802_11_INFRASTRUCTURE_MODE, &arg, &len); + + if (rval) + device_printf (sc->ndis_dev, "set infra failed: %d\n", rval); + + /* Set RTS threshold */ + + len = sizeof(arg); + arg = vap->iv_rtsthreshold; + ndis_set_info(sc, OID_802_11_RTS_THRESHOLD, &arg, &len); + + /* Set fragmentation threshold */ + + len = sizeof(arg); + arg = vap->iv_fragthreshold; + ndis_set_info(sc, OID_802_11_FRAGMENTATION_THRESHOLD, &arg, &len); + /* Set WEP */ -#ifdef IEEE80211_F_PRIVACY - if (ic->ic_flags & IEEE80211_F_PRIVACY) { -#else - if (ic->ic_wep_mode >= IEEE80211_WEP_ON) { -#endif + if (vap->iv_flags & IEEE80211_F_PRIVACY && + !(vap->iv_flags & IEEE80211_F_WPA)) { + int keys_set = 0; + + if (ni->ni_authmode == IEEE80211_AUTH_SHARED) { + len = sizeof(arg); + arg = NDIS_80211_AUTHMODE_SHARED; + DPRINTF(("Setting shared auth\n")); + ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, + &arg, &len); + } for (i = 0; i < IEEE80211_WEP_NKID; i++) { - if (ic->ic_nw_keys[i].wk_keylen) { + if (vap->iv_nw_keys[i].wk_keylen) { + if (vap->iv_nw_keys[i].wk_cipher->ic_cipher != + IEEE80211_CIPHER_WEP) + continue; bzero((char *)&wep, sizeof(wep)); - wep.nw_keylen = ic->ic_nw_keys[i].wk_keylen; -#ifdef notdef - /* 5 and 13 are the only valid key lengths */ - if (ic->ic_nw_keys[i].wk_len < 5) + wep.nw_keylen = vap->iv_nw_keys[i].wk_keylen; + + /* + * 5, 13 and 16 are the only valid + * key lengths. Anything in between + * will be zero padded out to the + * next highest boundary. + */ + if (vap->iv_nw_keys[i].wk_keylen < 5) wep.nw_keylen = 5; - else if (ic->ic_nw_keys[i].wk_len > 5 && - ic->ic_nw_keys[i].wk_len < 13) + else if (vap->iv_nw_keys[i].wk_keylen > 5 && + vap->iv_nw_keys[i].wk_keylen < 13) wep.nw_keylen = 13; -#endif + else if (vap->iv_nw_keys[i].wk_keylen > 13 && + vap->iv_nw_keys[i].wk_keylen < 16) + wep.nw_keylen = 16; + wep.nw_keyidx = i; wep.nw_length = (sizeof(uint32_t) * 3) + wep.nw_keylen; - if (i == ic->ic_def_txkey) + if (i == vap->iv_def_txkey) wep.nw_keyidx |= NDIS_80211_WEPKEY_TX; - bcopy(ic->ic_nw_keys[i].wk_key, + bcopy(vap->iv_nw_keys[i].wk_key, wep.nw_keydata, wep.nw_length); len = sizeof(wep); + DPRINTF(("Setting WEP key %d\n", i)); rval = ndis_set_info(sc, OID_802_11_ADD_WEP, &wep, &len); if (rval) device_printf(sc->ndis_dev, "set wepkey failed: %d\n", rval); + keys_set++; } } - arg = NDIS_80211_WEPSTAT_ENABLED; - len = sizeof(arg); - rval = ndis_set_info(sc, OID_802_11_WEP_STATUS, &arg, &len); - if (rval) - device_printf(sc->ndis_dev, - "enable WEP failed: %d\n", rval); -#ifndef IEEE80211_F_PRIVACY - if (ic->ic_wep_mode != IEEE80211_WEP_8021X && - ic->ic_wep_mode != IEEE80211_WEP_ON) - arg = NDIS_80211_PRIVFILT_ACCEPTALL; - else -#endif - arg = NDIS_80211_PRIVFILT_8021XWEP; - len = sizeof(arg); - rval = ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len); -#ifdef IEEE80211_WEP_8021X /*IEEE80211_F_WEPON*/ - /* Accept that we only have "shared" and 802.1x modes. */ - if (rval == 0) { - if (arg == NDIS_80211_PRIVFILT_ACCEPTALL) - ic->ic_wep_mode = IEEE80211_WEP_MIXED; + if (keys_set) { + DPRINTF(("Setting WEP on\n")); + arg = NDIS_80211_WEPSTAT_ENABLED; + len = sizeof(arg); + rval = ndis_set_info(sc, + OID_802_11_WEP_STATUS, &arg, &len); + if (rval) + device_printf(sc->ndis_dev, + "enable WEP failed: %d\n", rval); + if (vap->iv_flags & IEEE80211_F_DROPUNENC) + arg = NDIS_80211_PRIVFILT_8021XWEP; else - ic->ic_wep_mode = IEEE80211_WEP_8021X; + arg = NDIS_80211_PRIVFILT_ACCEPTALL; + + len = sizeof(arg); + ndis_set_info(sc, + OID_802_11_PRIVACY_FILTER, &arg, &len); } -#endif - arg = NDIS_80211_AUTHMODE_OPEN; - } else { - arg = NDIS_80211_WEPSTAT_DISABLED; - len = sizeof(arg); - ndis_set_info(sc, OID_802_11_WEP_STATUS, &arg, &len); - arg = NDIS_80211_AUTHMODE_OPEN; } - len = sizeof(arg); - rval = ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); - -#ifdef notyet - if (rval) - device_printf (sc->ndis_dev, "set auth failed: %d\n", rval); -#endif + /* Set up WPA. */ + if ((vap->iv_flags & IEEE80211_F_WPA) && + vap->iv_appie_assocreq != NULL) { + struct ieee80211_appie *ie = vap->iv_appie_assocreq; + error = ndis_set_wpa(sc, ie->ie_data, ie->ie_len); + if (error != 0) + device_printf(sc->ndis_dev, "WPA setup failed\n"); + } #ifdef notyet /* Set network type. */ arg = 0; - switch (ic->ic_curmode) { + switch (vap->iv_curmode) { case IEEE80211_MODE_11A: arg = NDIS_80211_NETTYPE_11OFDM5; break; @@ -1452,161 +2470,129 @@ ndis_setstate_80211(struct ndis_softc *sc) break; default: device_printf(sc->ndis_dev, "unknown mode: %d\n", - ic->ic_curmode); + vap->iv_curmode); } if (arg) { + DPRINTF(("Setting network type to %d\n", arg)); len = sizeof(arg); rval = ndis_set_info(sc, OID_802_11_NETWORK_TYPE_IN_USE, &arg, &len); if (rval) - device_printf (sc->ndis_dev, + device_printf(sc->ndis_dev, "set nettype failed: %d\n", rval); } #endif - len = sizeof(config); - bzero((char *)&config, len); - config.nc_length = len; - config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh); - rval = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &len); - /* - * Some drivers expect us to initialize these values, so - * provide some defaults. + * If the user selected a specific BSSID, try + * to use that one. This is useful in the case where + * there are several APs in range with the same network + * name. To delete the BSSID, we use the broadcast + * address as the BSSID. + * Note that some drivers seem to allow setting a BSSID + * in ad-hoc mode, which has the effect of forcing the + * NIC to create an ad-hoc cell with a specific BSSID, + * instead of a randomly chosen one. However, the net80211 + * code makes the assumtion that the BSSID setting is invalid + * when you're in ad-hoc mode, so we don't allow that here. */ - if (config.nc_beaconperiod == 0) - config.nc_beaconperiod = 100; - if (config.nc_atimwin == 0) - config.nc_atimwin = 100; - if (config.nc_fhconfig.ncf_dwelltime == 0) - config.nc_fhconfig.ncf_dwelltime = 200; - if (rval == 0 && ic->ic_ibss_chan != IEEE80211_CHAN_ANYC) { - int chan, chanflag; + len = IEEE80211_ADDR_LEN; + if (vap->iv_flags & IEEE80211_F_DESBSSID && + vap->iv_opmode != IEEE80211_M_IBSS) + bcopy(ni->ni_bssid, bssid, len); + else + bcopy(ifp->if_broadcastaddr, bssid, len); - chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); - chanflag = config.nc_dsconfig > 2500000 ? IEEE80211_CHAN_2GHZ : - IEEE80211_CHAN_5GHZ; - if (chan != ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0)) { - config.nc_dsconfig = - ic->ic_ibss_chan->ic_freq * 1000; - ic->ic_bss->ni_chan = ic->ic_ibss_chan; - len = sizeof(config); - config.nc_length = len; - config.nc_fhconfig.ncf_length = - sizeof(ndis_80211_config_fh); - rval = ndis_set_info(sc, OID_802_11_CONFIGURATION, - &config, &len); - if (rval) - device_printf(sc->ndis_dev, "couldn't change " - "DS config to %ukHz: %d\n", - config.nc_dsconfig, rval); - } - } else if (rval) - device_printf(sc->ndis_dev, "couldn't retrieve " - "channel info: %d\n", rval); + DPRINTF(("Setting BSSID to %6D\n", (uint8_t *)&bssid, ":")); + rval = ndis_set_info(sc, OID_802_11_BSSID, &bssid, &len); + if (rval) + device_printf(sc->ndis_dev, + "setting BSSID failed: %d\n", rval); /* Set SSID -- always do this last. */ +#ifdef NDIS_DEBUG + if (ndis_debug > 0) { + kprintf("Setting ESSID to "); + ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); + kprintf("\n"); + } +#endif + len = sizeof(ssid); bzero((char *)&ssid, len); - ssid.ns_ssidlen = ic->ic_des_esslen; + ssid.ns_ssidlen = ni->ni_esslen; if (ssid.ns_ssidlen == 0) { ssid.ns_ssidlen = 1; } else - bcopy(ic->ic_des_essid, ssid.ns_ssid, ssid.ns_ssidlen); + bcopy(ni->ni_essid, ssid.ns_ssid, ssid.ns_ssidlen); + rval = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len); if (rval) device_printf (sc->ndis_dev, "set ssid failed: %d\n", rval); -} -static void -ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr) -{ - struct ieee80211com *ic = (void *)ifp; /* XXX */ - struct ieee80211_node *ni = NULL; - - imr->ifm_status = IFM_AVALID; - imr->ifm_active = IFM_IEEE80211; - if (ic->ic_state == IEEE80211_S_RUN) - imr->ifm_status |= IFM_ACTIVE; - imr->ifm_active |= IFM_AUTO; - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - ni = ic->ic_bss; - /* calculate rate subtype */ - imr->ifm_active |= ieee80211_rate2media(ic, - ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); - break; - case IEEE80211_M_IBSS: - ni = ic->ic_bss; - /* calculate rate subtype */ - imr->ifm_active |= ieee80211_rate2media(ic, - ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); - imr->ifm_active |= IFM_IEEE80211_ADHOC; - break; - case IEEE80211_M_AHDEMO: - /* should not come here */ - break; - case IEEE80211_M_HOSTAP: - imr->ifm_active |= IFM_IEEE80211_HOSTAP; - break; - case IEEE80211_M_MONITOR: - imr->ifm_active |= IFM_IEEE80211_MONITOR; - break; - } - switch (ic->ic_curmode) { - case IEEE80211_MODE_11A: - imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A); - break; - case IEEE80211_MODE_11B: - imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11B); - break; - case IEEE80211_MODE_11G: - imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G); - break; - case IEEE80211_MODE_TURBO_A: - imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A) - | IFM_IEEE80211_TURBO; - break; - } + return; } static int -ndis_get_assoc(struct ndis_softc *sc, ndis_wlan_bssid_ex **assoc) +ndis_get_bssid_list(struct ndis_softc *sc, ndis_80211_bssid_list_ex **bl) { - ndis_80211_bssid_list_ex *bl; + int len, error; + + len = sizeof(uint32_t) + (sizeof(ndis_wlan_bssid_ex) * 16); + *bl = kmalloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); + if (*bl == NULL) + return (ENOMEM); + + error = ndis_get_info(sc, OID_802_11_BSSID_LIST, *bl, &len); + if (error == ENOSPC) { + kfree(*bl, M_DEVBUF); + *bl = kmalloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); + if (*bl == NULL) + return (ENOMEM); + + error = ndis_get_info(sc, OID_802_11_BSSID_LIST, *bl, &len); + } + if (error) { + DPRINTF(("%s: failed to read\n", __func__)); + kfree(*bl, M_DEVBUF); + return (error); + } + + return (0); +} + +static int +ndis_get_assoc(struct ndis_softc *sc, ndis_wlan_bssid_ex **assoc) +{ + struct ifnet *ifp = sc->ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap; + struct ieee80211_node *ni; + ndis_80211_bssid_list_ex *bl; ndis_wlan_bssid_ex *bs; ndis_80211_macaddr bssid; int i, len, error; if (!sc->ndis_link) - return(ENOENT); + return (ENOENT); len = sizeof(bssid); error = ndis_get_info(sc, OID_802_11_BSSID, &bssid, &len); if (error) { device_printf(sc->ndis_dev, "failed to get bssid\n"); - return(ENOENT); - } - len = 0; - error = ndis_get_info(sc, OID_802_11_BSSID_LIST, NULL, &len); - if (error != ENOSPC) { - device_printf(sc->ndis_dev, "bssid_list failed\n"); - return (error); + return (ENOENT); } - bl = kmalloc(len, M_TEMP, M_NOWAIT|M_ZERO); - if (bl == NULL) - return (ENOMEM); - error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); - if (error) { - kfree(bl, M_TEMP); - device_printf(sc->ndis_dev, "bssid_list failed\n"); + vap = TAILQ_FIRST(&ic->ic_vaps); + ni = vap->iv_bss; + + error = ndis_get_bssid_list(sc, &bl); + if (error) return (error); - } bs = (ndis_wlan_bssid_ex *)&bl->nblx_bssid[0]; for (i = 0; i < bl->nblx_items; i++) { @@ -1614,106 +2600,53 @@ ndis_get_assoc(struct ndis_softc *sc, ndis_wlan_bssid_ex **assoc) *assoc = kmalloc(bs->nwbx_len, M_TEMP, M_NOWAIT); if (*assoc == NULL) { kfree(bl, M_TEMP); - return(ENOMEM); + return (ENOMEM); } bcopy((char *)bs, (char *)*assoc, bs->nwbx_len); kfree(bl, M_TEMP); - return(0); - } + if (ic->ic_opmode == IEEE80211_M_STA) + ni->ni_associd = 1 | 0xc000; /* fake associd */ + return (0); + } bs = (ndis_wlan_bssid_ex *)((char *)bs + bs->nwbx_len); } kfree(bl, M_TEMP); - return(ENOENT); + return (ENOENT); } static void ndis_getstate_80211(struct ndis_softc *sc) { struct ieee80211com *ic; - ndis_80211_ssid ssid; - ndis_80211_config config; + struct ieee80211vap *vap; + struct ieee80211_node *ni; ndis_wlan_bssid_ex *bs; int rval, len, i = 0; + int chanflag; uint32_t arg; struct ifnet *ifp; - ic = &sc->ic; - ifp = &sc->ic.ic_ac.ac_if; + ifp = sc->ifp; + ic = ifp->if_l2com; + vap = TAILQ_FIRST(&ic->ic_vaps); + ni = vap->iv_bss; if (!NDIS_INITIALIZED(sc)) return; - if (sc->ndis_link) - ic->ic_state = IEEE80211_S_RUN; - else - ic->ic_state = IEEE80211_S_ASSOC; - - - /* - * If we're associated, retrieve info on the current bssid. - */ - if ((rval = ndis_get_assoc(sc, &bs)) == 0) { - switch(bs->nwbx_nettype) { - case NDIS_80211_NETTYPE_11FH: - case NDIS_80211_NETTYPE_11DS: - ic->ic_curmode = IEEE80211_MODE_11B; - break; - case NDIS_80211_NETTYPE_11OFDM5: - ic->ic_curmode = IEEE80211_MODE_11A; - break; - case NDIS_80211_NETTYPE_11OFDM24: - ic->ic_curmode = IEEE80211_MODE_11G; - break; - default: - device_printf(sc->ndis_dev, - "unknown nettype %d\n", arg); - break; - } - kfree(bs, M_TEMP); - } else { + if ((rval = ndis_get_assoc(sc, &bs)) != 0) return; - } - - len = sizeof(ssid); - bzero((char *)&ssid, len); - rval = ndis_get_info(sc, OID_802_11_SSID, &ssid, &len); - - if (rval) - device_printf (sc->ndis_dev, "get ssid failed: %d\n", rval); - bcopy(ssid.ns_ssid, ic->ic_bss->ni_essid, ssid.ns_ssidlen); - ic->ic_bss->ni_esslen = ssid.ns_ssidlen; - len = sizeof(arg); - rval = ndis_get_info(sc, OID_GEN_LINK_SPEED, &arg, &len); - if (rval) - device_printf (sc->ndis_dev, "get link speed failed: %d\n", - rval); - - if (ic->ic_modecaps & (1<ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B]; - for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) { - if ((ic->ic_bss->ni_rates.rs_rates[i] & - IEEE80211_RATE_VAL) == arg / 5000) - break; - } - } - - if (i == ic->ic_bss->ni_rates.rs_nrates && - ic->ic_modecaps & (1<ic_bss->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11G]; - for (i = 0; i < ic->ic_bss->ni_rates.rs_nrates; i++) { - if ((ic->ic_bss->ni_rates.rs_rates[i] & - IEEE80211_RATE_VAL) == arg / 5000) - break; - } - } + /* We're associated, retrieve info on the current bssid. */ + ic->ic_curmode = ndis_nettype_mode(bs->nwbx_nettype); + chanflag = ndis_nettype_chan(bs->nwbx_nettype); + IEEE80211_ADDR_COPY(ni->ni_bssid, bs->nwbx_macaddr); - if (i == ic->ic_bss->ni_rates.rs_nrates) - device_printf(sc->ndis_dev, "no matching rate for: %d\n", - arg / 5000); - else - ic->ic_bss->ni_txrate = i; + /* Get SSID from current association info. */ + bcopy(bs->nwbx_ssid.ns_ssid, ni->ni_essid, + bs->nwbx_ssid.ns_ssidlen); + ni->ni_esslen = bs->nwbx_ssid.ns_ssidlen; if (ic->ic_caps & IEEE80211_C_PMGT) { len = sizeof(arg); @@ -1723,45 +2656,82 @@ ndis_getstate_80211(struct ndis_softc *sc) device_printf(sc->ndis_dev, "get power mode failed: %d\n", rval); if (arg == NDIS_80211_POWERMODE_CAM) - ic->ic_flags &= ~IEEE80211_F_PMGTON; + vap->iv_flags &= ~IEEE80211_F_PMGTON; else - ic->ic_flags |= IEEE80211_F_PMGTON; + vap->iv_flags |= IEEE80211_F_PMGTON; } - len = sizeof(config); - bzero((char *)&config, len); - config.nc_length = len; - config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh); - rval = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &len); - if (rval == 0) { - int chan; - - chan = ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0); - if (chan < 0 || chan >= IEEE80211_CHAN_MAX) { - if (ifp->if_flags & IFF_DEBUG) - device_printf(sc->ndis_dev, "current channel " - "(%uMHz) out of bounds\n", - config.nc_dsconfig / 1000); - ic->ic_bss->ni_chan = &ic->ic_channels[1]; - } else - ic->ic_bss->ni_chan = &ic->ic_channels[chan]; - } else - device_printf(sc->ndis_dev, "couldn't retrieve " - "channel info: %d\n", rval); + /* Get TX power */ + if (ic->ic_caps & IEEE80211_C_TXPMGT) { + len = sizeof(arg); + ndis_get_info(sc, OID_802_11_TX_POWER_LEVEL, &arg, &len); + for (i = 0; i < (sizeof(dBm2mW) / sizeof(dBm2mW[0])); i++) + if (dBm2mW[i] >= arg) + break; + ic->ic_txpowlimit = i; + } + + /* + * Use the current association information to reflect + * what channel we're on. + */ + ic->ic_curchan = ieee80211_find_channel(ic, + bs->nwbx_config.nc_dsconfig / 1000, chanflag); + if (ic->ic_curchan == NULL) + ic->ic_curchan = &ic->ic_channels[0]; + ni->ni_chan = ic->ic_curchan; + ic->ic_bsschan = ic->ic_curchan; + + kfree(bs, M_TEMP); + + /* + * Determine current authentication mode. + */ + len = sizeof(arg); + rval = ndis_get_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); + if (rval) + device_printf(sc->ndis_dev, + "get authmode status failed: %d\n", rval); + else { + vap->iv_flags &= ~IEEE80211_F_WPA; + switch (arg) { + case NDIS_80211_AUTHMODE_OPEN: + ni->ni_authmode = IEEE80211_AUTH_OPEN; + break; + case NDIS_80211_AUTHMODE_SHARED: + ni->ni_authmode = IEEE80211_AUTH_SHARED; + break; + case NDIS_80211_AUTHMODE_AUTO: + ni->ni_authmode = IEEE80211_AUTH_AUTO; + break; + case NDIS_80211_AUTHMODE_WPA: + case NDIS_80211_AUTHMODE_WPAPSK: + case NDIS_80211_AUTHMODE_WPANONE: + ni->ni_authmode = IEEE80211_AUTH_WPA; + vap->iv_flags |= IEEE80211_F_WPA1; + break; + case NDIS_80211_AUTHMODE_WPA2: + case NDIS_80211_AUTHMODE_WPA2PSK: + ni->ni_authmode = IEEE80211_AUTH_WPA; + vap->iv_flags |= IEEE80211_F_WPA2; + break; + default: + ni->ni_authmode = IEEE80211_AUTH_NONE; + break; + } + } -/* len = sizeof(arg); rval = ndis_get_info(sc, OID_802_11_WEP_STATUS, &arg, &len); if (rval) - device_printf (sc->ndis_dev, + device_printf(sc->ndis_dev, "get wep status failed: %d\n", rval); if (arg == NDIS_80211_WEPSTAT_ENABLED) - ic->ic_flags |= IEEE80211_F_WEPON; + vap->iv_flags |= IEEE80211_F_PRIVACY|IEEE80211_F_DROPUNENC; else - ic->ic_flags &= ~IEEE80211_F_WEPON; -*/ + vap->iv_flags &= ~(IEEE80211_F_PRIVACY|IEEE80211_F_DROPUNENC); } static int @@ -1771,7 +2741,9 @@ ndis_ioctl(struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr) struct ifreq *ifr = (struct ifreq *) data; int i, error = 0; - switch(command) { + /*NDIS_LOCK(sc);*/ + + switch (command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING && @@ -1808,15 +2780,7 @@ ndis_ioctl(struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr) break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: - if (sc->ndis_80211) { - error = ieee80211_ioctl(&sc->ic, command, data, cr); - if (error == ENETRESET) { - ndis_setstate_80211(sc); - /*ndis_init(sc);*/ - error = 0; - } - } else - error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); + error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); break; case SIOCSIFCAP: ifp->if_capenable = ifr->ifr_reqcap; @@ -1826,157 +2790,272 @@ ndis_ioctl(struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr) ifp->if_hwassist = 0; ndis_set_offload(sc); break; - case SIOCGIFGENERIC: - case SIOCSIFGENERIC: - if (sc->ndis_80211 && NDIS_INITIALIZED(sc)) { - if (command == SIOCGIFGENERIC) - error = ndis_wi_ioctl_get(ifp, command, data); - else - error = ndis_wi_ioctl_set(ifp, command, data); - } else - error = ENOTTY; - if (error != ENOTTY) - break; default: - sc->ndis_skip = 1; - if (sc->ndis_80211) { - error = ieee80211_ioctl(&sc->ic, command, data, cr); - if (error == ENETRESET) { - ndis_setstate_80211(sc); - error = 0; - } - } else - error = ether_ioctl(ifp, command, data); - sc->ndis_skip = 0; + error = ether_ioctl(ifp, command, data); break; } + + /*NDIS_UNLOCK(sc);*/ + return(error); } static int -ndis_wi_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data) +ndis_ioctl_80211(struct ifnet *ifp, u_long command, caddr_t data, + struct ucred *cr) { - struct wi_req wreq; - struct ifreq *ifr; - struct ndis_softc *sc; - ndis_80211_bssid_list_ex *bl; - ndis_wlan_bssid_ex *wb; - struct wi_apinfo *api; - int error, i, j, len, maxaps; - - sc = ifp->if_softc; - ifr = (struct ifreq *)data; - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - return (error); + struct ndis_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + struct ifreq *ifr = (struct ifreq *) data; + struct ndis_oid_data oid; + struct ndis_evt evt; + void *oidbuf; + int error = 0; - switch (wreq.wi_type) { - case WI_RID_READ_APS: - len = 0; - error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, - NULL, &len); - if (error == 0) - tsleep(&error, PCATCH, "ssidscan", hz * 2); - len = 0; - error = ndis_get_info(sc, OID_802_11_BSSID_LIST, NULL, &len); - if (error != ENOSPC) + switch (command) { + case SIOCSIFFLAGS: + /*NDIS_LOCK(sc);*/ + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) + ndis_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + ndis_stop(sc); + } + sc->ndis_if_flags = ifp->if_flags; + error = 0; + /*NDIS_UNLOCK(sc);*/ + break; + case SIOCGDRVSPEC: + if ((error = priv_check(curthread, PRIV_DRIVER))) + break; + error = copyin(ifr->ifr_data, &oid, sizeof(oid)); + if (error) break; - bl = kmalloc(len, M_DEVBUF, M_WAITOK|M_ZERO); - error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); + oidbuf = kmalloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); + if (oidbuf == NULL) { + error = ENOMEM; + break; + } + error = copyin((caddr_t)ifr->ifr_data + sizeof(oid), oidbuf, oid.len); if (error) { - kfree(bl, M_DEVBUF); + kfree(oidbuf, M_TEMP); break; } - maxaps = (2 * wreq.wi_len - sizeof(int)) / sizeof(*api); - maxaps = MIN(maxaps, bl->nblx_items); - wreq.wi_len = (maxaps * sizeof(*api) + sizeof(int)) / 2; - *(int *)&wreq.wi_val = maxaps; - api = (struct wi_apinfo *)&((int *)&wreq.wi_val)[1]; - wb = bl->nblx_bssid; - while (maxaps--) { - bzero(api, sizeof(*api)); - bcopy(&wb->nwbx_macaddr, &api->bssid, - sizeof(api->bssid)); - api->namelen = wb->nwbx_ssid.ns_ssidlen; - bcopy(&wb->nwbx_ssid.ns_ssid, &api->name, api->namelen); - if (wb->nwbx_privacy) - api->capinfo |= IEEE80211_CAPINFO_PRIVACY; - /* XXX Where can we get noise information? */ - api->signal = wb->nwbx_rssi + 149; /* XXX */ - api->quality = api->signal; - api->channel = - ieee80211_mhz2ieee(wb->nwbx_config.nc_dsconfig / - 1000, 0); - /* In "auto" infrastructure mode, this is useless. */ - if (wb->nwbx_netinfra == NDIS_80211_NET_INFRA_IBSS) - api->capinfo |= IEEE80211_CAPINFO_IBSS; - if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) { - j = sizeof(ndis_80211_rates_ex); - /* handle other extended things */ - } else - j = sizeof(ndis_80211_rates); - for (i = api->rate = 0; i < j; i++) - api->rate = MAX(api->rate, 5 * - (wb->nwbx_supportedrates[i] & 0x7f)); - api++; - wb = (ndis_wlan_bssid_ex *)((char *)wb + wb->nwbx_len); + error = ndis_get_info(sc, oid.oid, oidbuf, &oid.len); + if (error) { + kfree(oidbuf, M_TEMP); + break; + } + error = copyout(&oid, ifr->ifr_data, sizeof(oid)); + if (error) { + kfree(oidbuf, M_TEMP); + break; + } + error = copyout(oidbuf, (caddr_t)ifr->ifr_data + sizeof(oid), oid.len); + kfree(oidbuf, M_TEMP); + break; + case SIOCSDRVSPEC: + if ((error = priv_check(curthread, PRIV_DRIVER))) + break; + error = copyin(ifr->ifr_data, &oid, sizeof(oid)); + if (error) + break; + oidbuf = kmalloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); + if (oidbuf == NULL) { + error = ENOMEM; + break; + } + error = copyin((caddr_t)ifr->ifr_data + sizeof(oid), oidbuf, oid.len); + if (error) { + kfree(oidbuf, M_TEMP); + break; + } + error = ndis_set_info(sc, oid.oid, oidbuf, &oid.len); + if (error) { + kfree(oidbuf, M_TEMP); + break; } - kfree(bl, M_DEVBUF); - error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); + error = copyout(&oid, ifr->ifr_data, sizeof(oid)); + if (error) { + kfree(oidbuf, M_TEMP); + break; + } + error = copyout(oidbuf, (caddr_t)ifr->ifr_data + sizeof(oid), oid.len); + kfree(oidbuf, M_TEMP); + break; + case SIOCGPRIVATE_0: + if ((error = priv_check(curthread, PRIV_DRIVER))) + break; + NDIS_LOCK(sc); + if (sc->ndis_evt[sc->ndis_evtcidx].ne_sts == 0) { + error = ENOENT; + NDIS_UNLOCK(sc); + break; + } + error = copyin(ifr->ifr_data, &evt, sizeof(evt)); + if (error) { + NDIS_UNLOCK(sc); + break; + } + if (evt.ne_len < sc->ndis_evt[sc->ndis_evtcidx].ne_len) { + error = ENOSPC; + NDIS_UNLOCK(sc); + break; + } + error = copyout(&sc->ndis_evt[sc->ndis_evtcidx], + ifr->ifr_data, sizeof(uint32_t) * 2); + if (error) { + NDIS_UNLOCK(sc); + break; + } + if (sc->ndis_evt[sc->ndis_evtcidx].ne_len) { + error = copyout(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, + (caddr_t)ifr->ifr_data + (sizeof(uint32_t) * 2), + sc->ndis_evt[sc->ndis_evtcidx].ne_len); + if (error) { + NDIS_UNLOCK(sc); + break; + } + kfree(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, M_TEMP); + sc->ndis_evt[sc->ndis_evtcidx].ne_buf = NULL; + } + sc->ndis_evt[sc->ndis_evtcidx].ne_len = 0; + sc->ndis_evt[sc->ndis_evtcidx].ne_sts = 0; + NDIS_EVTINC(sc->ndis_evtcidx); + NDIS_UNLOCK(sc); + break; + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, command); + break; + case SIOCGIFADDR: + error = ether_ioctl(ifp, command, data); break; default: - error = ENOTTY; + error = EINVAL; break; } return (error); } -static int -ndis_wi_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data) +int +ndis_del_key(struct ieee80211vap *vap, const struct ieee80211_key *key) { - struct wi_req wreq; - struct ifreq *ifr; struct ndis_softc *sc; - uint32_t foo; - int error, len; + ndis_80211_key rkey; + int len, error = 0; + + sc = vap->iv_ic->ic_ifp->if_softc; + + bzero((char *)&rkey, sizeof(rkey)); + len = sizeof(rkey); + + rkey.nk_len = len; + rkey.nk_keyidx = key->wk_keyix; + + bcopy(vap->iv_ifp->if_broadcastaddr, + rkey.nk_bssid, IEEE80211_ADDR_LEN); + + error = ndis_set_info(sc, OID_802_11_REMOVE_KEY, &rkey, &len); - error = priv_check(curthread, PRIV_ROOT); if (error) - return (error); + return (0); + + return (1); +} +/* + * In theory this could be called for any key, but we'll + * only use it for WPA TKIP or AES keys. These need to be + * set after initial authentication with the AP. + */ +static int +ndis_add_key(struct ieee80211vap *vap, const struct ieee80211_key *key, + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ndis_softc *sc; + struct ifnet *ifp; + ndis_80211_key rkey; + int len, error = 0; + + ifp = vap->iv_ic->ic_ifp; sc = ifp->if_softc; - ifr = (struct ifreq *)data; - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - return (error); - switch (wreq.wi_type) { - case WI_RID_SCAN_APS: - case WI_RID_SCAN_REQ: /* arguments ignored */ - len = sizeof(foo); - foo = 0; - error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, &foo, - &len); + switch (key->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_TKIP: + + len = sizeof(ndis_80211_key); + bzero((char *)&rkey, sizeof(rkey)); + + rkey.nk_len = len; + rkey.nk_keylen = key->wk_keylen; + + if (key->wk_flags & IEEE80211_KEY_SWMIC) + rkey.nk_keylen += 16; + + /* key index - gets weird in NDIS */ + + if (key->wk_keyix != IEEE80211_KEYIX_NONE) + rkey.nk_keyidx = key->wk_keyix; + else + rkey.nk_keyidx = 0; + + if (key->wk_flags & IEEE80211_KEY_XMIT) + rkey.nk_keyidx |= 1 << 31; + + if (key->wk_flags & IEEE80211_KEY_GROUP) { + bcopy(ifp->if_broadcastaddr, + rkey.nk_bssid, IEEE80211_ADDR_LEN); + } else { + bcopy(vap->iv_bss->ni_bssid, + rkey.nk_bssid, IEEE80211_ADDR_LEN); + /* pairwise key */ + rkey.nk_keyidx |= 1 << 30; + } + + /* need to set bit 29 based on keyrsc */ + rkey.nk_keyrsc = key->wk_keyrsc[0]; /* XXX need tid */ + + if (rkey.nk_keyrsc) + rkey.nk_keyidx |= 1 << 29; + + if (key->wk_flags & IEEE80211_KEY_SWMIC) { + bcopy(key->wk_key, rkey.nk_keydata, 16); + bcopy(key->wk_key + 24, rkey.nk_keydata + 16, 8); + bcopy(key->wk_key + 16, rkey.nk_keydata + 24, 8); + } else + bcopy(key->wk_key, rkey.nk_keydata, key->wk_keylen); + + error = ndis_set_info(sc, OID_802_11_ADD_KEY, &rkey, &len); + break; + case IEEE80211_CIPHER_WEP: + error = 0; break; + /* + * I don't know how to set up keys for the AES + * cipher yet. Is it the same as TKIP? + */ + case IEEE80211_CIPHER_AES_CCM: default: error = ENOTTY; break; } - return (error); + + /* We need to return 1 for success, 0 for failure. */ + + if (error) + return (0); + + return (1); } static void -ndis_watchdog(struct ifnet *ifp) +ndis_resettask(device_object *d, void *arg) { struct ndis_softc *sc; - sc = ifp->if_softc; - - ifp->if_oerrors++; - device_printf(sc->ndis_dev, "watchdog timeout\n"); - + sc = arg; ndis_reset_nic(sc); - ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE); } /* @@ -1987,15 +3066,35 @@ static void ndis_stop(struct ndis_softc *sc) { struct ifnet *ifp; + int i; - ifp = &sc->arpcom.ac_if; - callout_stop(&sc->ndis_stat_timer); - - ndis_halt_nic(sc); + ifp = sc->ifp; + callout_stop(&sc->ndis_stat_callout); /* XXX swildner callout_drain() */ - ifp->if_timer = 0; + NDIS_LOCK(sc); + sc->ndis_tx_timer = 0; sc->ndis_link = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + NDIS_UNLOCK(sc); + + if (sc->ndis_iftype != PNPBus || + (sc->ndis_iftype == PNPBus && + !(sc->ndisusb_status & NDISUSB_STATUS_DETACH) && + ndisusb_halt != 0)) + ndis_halt_nic(sc); + + NDIS_LOCK(sc); + for (i = 0; i < NDIS_EVENTS; i++) { + if (sc->ndis_evt[i].ne_sts && sc->ndis_evt[i].ne_buf != NULL) { + kfree(sc->ndis_evt[i].ne_buf, M_TEMP); + sc->ndis_evt[i].ne_buf = NULL; + } + sc->ndis_evt[i].ne_sts = 0; + sc->ndis_evt[i].ne_len = 0; + } + sc->ndis_evtcidx = 0; + sc->ndis_evtpidx = 0; + NDIS_UNLOCK(sc); } /* @@ -2005,12 +3104,219 @@ ndis_stop(struct ndis_softc *sc) void ndis_shutdown(device_t dev) { - struct ndis_softc *sc; - struct ifnet *ifp; + struct ndis_softc *sc; + wlan_serialize_enter(); sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; - lwkt_serialize_enter(ifp->if_serializer); - ndis_shutdown_nic(sc); - lwkt_serialize_exit(ifp->if_serializer); + ndis_stop(sc); + wlan_serialize_exit(); +} + +static int +ndis_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ndis_vap *nvp = NDIS_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = ic->ic_ifp; + struct ndis_softc *sc = ifp->if_softc; + enum ieee80211_state ostate; + + DPRINTF(("%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate])); + + ostate = vap->iv_state; + vap->iv_state = nstate; + + switch (nstate) { + /* pass on to net80211 */ + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + return nvp->newstate(vap, nstate, arg); + case IEEE80211_S_ASSOC: + if (ostate != IEEE80211_S_AUTH) { + wlan_serialize_exit(); + ndis_auth_and_assoc(sc, vap); + wlan_serialize_enter(); + } + break; + case IEEE80211_S_AUTH: + wlan_serialize_exit(); + ndis_auth_and_assoc(sc, vap); + if (vap->iv_state == IEEE80211_S_AUTH) /* XXX */ + ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); + wlan_serialize_enter(); + break; + default: + break; + } + return (0); +} + +static void +ndis_scan(void *arg) +{ + struct ieee80211vap *vap = arg; + + ieee80211_scan_done(vap); +} + +static void +ndis_scan_results(struct ndis_softc *sc) +{ + struct ieee80211com *ic; + struct ieee80211vap *vap; + ndis_80211_bssid_list_ex *bl; + ndis_wlan_bssid_ex *wb; + struct ieee80211_scanparams sp; + struct ieee80211_frame wh; + struct ieee80211_channel *saved_chan; + int i, j; + int rssi, noise, freq, chanflag; + uint8_t ssid[2+IEEE80211_NWID_LEN]; + uint8_t rates[2+IEEE80211_RATE_MAXSIZE]; + uint8_t *frm, *efrm; + + ic = sc->ifp->if_l2com; + vap = TAILQ_FIRST(&ic->ic_vaps); + saved_chan = ic->ic_curchan; + noise = -96; + + if (ndis_get_bssid_list(sc, &bl)) + return; + + DPRINTF(("%s: %d results\n", __func__, bl->nblx_items)); + wb = &bl->nblx_bssid[0]; + for (i = 0; i < bl->nblx_items; i++) { + memset(&sp, 0, sizeof(sp)); + + memcpy(wh.i_addr2, wb->nwbx_macaddr, sizeof(wh.i_addr2)); + memcpy(wh.i_addr3, wb->nwbx_macaddr, sizeof(wh.i_addr3)); + rssi = 100 * (wb->nwbx_rssi - noise) / (-32 - noise); + rssi = max(0, min(rssi, 100)); /* limit 0 <= rssi <= 100 */ + if (wb->nwbx_privacy) + sp.capinfo |= IEEE80211_CAPINFO_PRIVACY; + sp.bintval = wb->nwbx_config.nc_beaconperiod; + switch (wb->nwbx_netinfra) { + case NDIS_80211_NET_INFRA_IBSS: + sp.capinfo |= IEEE80211_CAPINFO_IBSS; + break; + case NDIS_80211_NET_INFRA_BSS: + sp.capinfo |= IEEE80211_CAPINFO_ESS; + break; + } + sp.rates = &rates[0]; + for (j = 0; j < IEEE80211_RATE_MAXSIZE; j++) { + /* XXX - check units */ + if (wb->nwbx_supportedrates[j] == 0) + break; + rates[2 + j] = + wb->nwbx_supportedrates[j] & 0x7f; + } + rates[1] = j; + sp.ssid = (uint8_t *)&ssid[0]; + memcpy(sp.ssid + 2, &wb->nwbx_ssid.ns_ssid, + wb->nwbx_ssid.ns_ssidlen); + sp.ssid[1] = wb->nwbx_ssid.ns_ssidlen; + + chanflag = ndis_nettype_chan(wb->nwbx_nettype); + freq = wb->nwbx_config.nc_dsconfig / 1000; + sp.chan = sp.bchan = ieee80211_mhz2ieee(freq, chanflag); + /* Hack ic->ic_curchan to be in sync with the scan result */ + ic->ic_curchan = ieee80211_find_channel(ic, freq, chanflag); + if (ic->ic_curchan == NULL) + ic->ic_curchan = &ic->ic_channels[0]; + + /* Process extended info from AP */ + if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) { + frm = (uint8_t *)&wb->nwbx_ies; + efrm = frm + wb->nwbx_ielen; + if (efrm - frm < 12) + goto done; + sp.tstamp = frm; frm += 8; + sp.bintval = le16toh(*(uint16_t *)frm); frm += 2; + sp.capinfo = le16toh(*(uint16_t *)frm); frm += 2; + sp.ies = frm; + sp.ies_len = efrm - frm; + } +done: + DPRINTF(("scan: bssid %6D chan %dMHz (%d/%d) rssi %d\n", + wb->nwbx_macaddr, ":", freq, sp.bchan, chanflag, + rssi)); + ieee80211_add_scan(vap, &sp, &wh, 0, rssi, noise); + wb = (ndis_wlan_bssid_ex *)((char *)wb + wb->nwbx_len); + } + kfree(bl, M_DEVBUF); + /* Restore the channel after messing with it */ + ic->ic_curchan = saved_chan; +} + +static void +ndis_scan_start(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ndis_softc *sc = ifp->if_softc; + struct ieee80211vap *vap; + struct ieee80211_scan_state *ss; + ndis_80211_ssid ssid; + int error, len; + + ss = ic->ic_scan; + vap = TAILQ_FIRST(&ic->ic_vaps); + + if (!NDIS_INITIALIZED(sc)) { + DPRINTF(("%s: scan aborted\n", __func__)); + ieee80211_cancel_scan(vap); + return; + } + + len = sizeof(ssid); + bzero((char *)&ssid, len); + if (ss->ss_nssid == 0) + ssid.ns_ssidlen = 1; + else { + /* Perform a directed scan */ + ssid.ns_ssidlen = ss->ss_ssid[0].len; + bcopy(ss->ss_ssid[0].ssid, ssid.ns_ssid, ssid.ns_ssidlen); + } + + error = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len); + if (error) + DPRINTF(("%s: set ESSID failed\n", __func__)); + + len = 0; + error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, NULL, &len); + if (error) { + DPRINTF(("%s: scan command failed\n", __func__)); + ieee80211_cancel_scan(vap); + return; + } + /* Set a timer to collect the results */ + callout_reset(&sc->ndis_scan_callout, hz * 3, ndis_scan, vap); +} + +static void +ndis_set_channel(struct ieee80211com *ic) +{ + /* ignore */ +} + +static void +ndis_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) +{ + /* ignore */ +} + +static void +ndis_scan_mindwell(struct ieee80211_scan_state *ss) +{ + /* NB: don't try to abort scan; wait for firmware to finish */ +} + +static void +ndis_scan_end(struct ieee80211com *ic) +{ + struct ndis_softc *sc = ic->ic_ifp->if_softc; + + ndis_scan_results(sc); } diff --git a/sys/dev/netif/ndis/if_ndis_pccard.c b/sys/dev/netif/ndis/if_ndis_pccard.c index 89c76d26fb..4e7725c1fd 100644 --- a/sys/dev/netif/ndis/if_ndis_pccard.c +++ b/sys/dev/netif/ndis/if_ndis_pccard.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,72 +29,64 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/if_ndis/if_ndis_pccard.c,v 1.6 2004/07/11 00:19:30 wpaul Exp $ + * $FreeBSD: src/sys/dev/if_ndis/if_ndis_pccard.c,v 1.19 2010/12/16 15:19:32 jhb Exp $ */ #include #include #include #include -#include #include #include #include #include -#include -#include +#include #include #include #include +#include +#include + #include -#include +#include +#include + #include +#include #include #include #include -#include -#include "if_ndisvar.h" +#include #include #include "card_if.h" -#include "ndis_driver_data.h" - -#ifdef NDIS_PCMCIA_DEV_TABLE - -MODULE_DEPEND(ndis, pccard, 1, 1, 1); -MODULE_DEPEND(ndis, wlan, 1, 1, 1); -MODULE_DEPEND(ndis, ndisapi, 1, 1, 1); - -/* - * Various supported device vendors/types and their names. - * These are defined in the ndis_driver_data.h file. - */ -static struct ndis_pccard_type ndis_devs[] = { -#ifdef NDIS_PCMCIA_DEV_TABLE - NDIS_PCMCIA_DEV_TABLE -#endif - { NULL, NULL, NULL } -}; +MODULE_DEPEND(if_ndis, pccard, 1, 1, 1); static int ndis_probe_pccard (device_t); static int ndis_attach_pccard (device_t); +static int ndis_detach_pccard (device_t); static struct resource_list *ndis_get_resource_list (device_t, device_t); +static int ndis_devcompare (interface_type, + struct ndis_pccard_type *, device_t); +extern int ndisdrv_modevent (module_t, int, void *); extern int ndis_attach (device_t); extern int ndis_shutdown (device_t); extern int ndis_detach (device_t); extern int ndis_suspend (device_t); extern int ndis_resume (device_t); +extern unsigned char drv_data[]; + static device_method_t ndis_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ndis_probe_pccard), DEVMETHOD(device_attach, ndis_attach_pccard), - DEVMETHOD(device_detach, ndis_detach), + DEVMETHOD(device_detach, ndis_detach_pccard), DEVMETHOD(device_shutdown, ndis_shutdown), DEVMETHOD(device_suspend, ndis_suspend), DEVMETHOD(device_resume, ndis_resume), @@ -112,50 +104,61 @@ static device_method_t ndis_methods[] = { }; static driver_t ndis_driver = { -#ifdef NDIS_DEVNAME - NDIS_DEVNAME, -#else "ndis", -#endif ndis_methods, sizeof(struct ndis_softc) }; static devclass_t ndis_devclass; -#ifdef NDIS_MODNAME -#define NDIS_MODNAME_OVERRIDE_PCMCIA(x) \ - DRIVER_MODULE(x, pccard, ndis_driver, ndis_devclass, NULL, NULL) -NDIS_MODNAME_OVERRIDE_PCMCIA(NDIS_MODNAME); -#else -DRIVER_MODULE(ndis, pccard, ndis_driver, ndis_devclass, NULL, NULL); -#endif +DRIVER_MODULE(if_ndis, pccard, ndis_driver, ndis_devclass, ndisdrv_modevent, NULL); -/* - * Probe for an NDIS device. Check the PCI vendor and device - * IDs against our list and return a device name if we find a match. - */ static int -ndis_probe_pccard(device_t dev) +ndis_devcompare(interface_type bustype, struct ndis_pccard_type *t, + device_t dev) { - struct ndis_pccard_type *t; const char *prodstr, *vendstr; - int error; - t = ndis_devs; + if (bustype != PCMCIABus) + return(FALSE); prodstr = pccard_get_product_str(dev); vendstr = pccard_get_vendor_str(dev); while(t->ndis_name != NULL) { - if (ndis_strcasecmp(vendstr, t->ndis_vid) == 0 && - ndis_strcasecmp(prodstr, t->ndis_did) == 0) { + if (strcasecmp(vendstr, t->ndis_vid) == 0 && + strcasecmp(prodstr, t->ndis_did) == 0) { device_set_desc(dev, t->ndis_name); - return(0); + return(TRUE); } t++; } + return(FALSE); +} + +/* + * Probe for an NDIS device. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + */ +static int +ndis_probe_pccard(device_t dev) +{ + driver_object *drv; + struct drvdb_ent *db; + + drv = windrv_lookup(0, "PCCARD Bus"); + if (drv == NULL) + return(ENXIO); + + db = windrv_match((matchfuncptr)ndis_devcompare, dev); + + if (db != NULL) { + /* Create PDO for this device instance */ + windrv_create_pdo(drv, dev); + return(0); + } + return(ENXIO); } @@ -171,10 +174,20 @@ ndis_attach_pccard(device_t dev) struct ndis_pccard_type *t; int devidx = 0; const char *prodstr, *vendstr; + struct drvdb_ent *db; + wlan_serialize_enter(); sc = device_get_softc(dev); unit = device_get_unit(dev); sc->ndis_dev = dev; + + db = windrv_match((matchfuncptr)ndis_devcompare, dev); + if (db == NULL) { + wlan_serialize_exit(); + return (ENXIO); + } + sc->ndis_dobj = db->windrv_object; + sc->ndis_regvals = db->windrv_regvals; resource_list_init(&sc->ndis_rl); sc->ndis_io_rid = 0; @@ -208,18 +221,14 @@ ndis_attach_pccard(device_t dev) /* Figure out exactly which device we matched. */ - t = ndis_devs; + t = db->windrv_devlist; - error = pccard_get_product_str(dev, &prodstr); - if (error) - return(error); - error = pccard_get_vendor_str(dev, &vendstr); - if (error) - return(error); + prodstr = pccard_get_product_str(dev); + vendstr = pccard_get_vendor_str(dev); while(t->ndis_name != NULL) { - if (ndis_strcasecmp(vendstr, t->ndis_vid) == 0 && - ndis_strcasecmp(prodstr, t->ndis_did) == 0) + if (strcasecmp(vendstr, t->ndis_vid) == 0 && + strcasecmp(prodstr, t->ndis_did) == 0) break; t++; devidx++; @@ -230,6 +239,18 @@ ndis_attach_pccard(device_t dev) error = ndis_attach(dev); fail: + wlan_serialize_exit(); + return(error); +} + +static int +ndis_detach_pccard(device_t dev) +{ + int error = 0; + + wlan_serialize_enter(); + error = ndis_detach(dev); + wlan_serialize_exit(); return(error); } @@ -242,8 +263,6 @@ ndis_get_resource_list(device_t dev, device_t child) return (&sc->ndis_rl); } -#endif /* NDIS_PCI_DEV_TABLE */ - #define NDIS_AM_RID 3 int diff --git a/sys/dev/netif/ndis/if_ndis_pci.c b/sys/dev/netif/ndis/if_ndis_pci.c index 9bc27cd46e..ea6f84608e 100644 --- a/sys/dev/netif/ndis/if_ndis_pci.c +++ b/sys/dev/netif/ndis/if_ndis_pci.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,60 +29,49 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/if_ndis/if_ndis_pci.c,v 1.7 2004/07/11 00:19:30 wpaul Exp $ + * $FreeBSD: src/sys/dev/if_ndis/if_ndis_pci.c,v 1.26 2010/12/19 11:14:34 tijl Exp $ */ #include #include #include -#include #include #include #include #include -#include -#include +#include #include #include #include +#include +#include + #include #include #include +#include +#include -#include #include +#include #include #include #include -#include -#include "if_ndisvar.h" - -#include "ndis_driver_data.h" +#include -#ifdef NDIS_PCI_DEV_TABLE - -MODULE_DEPEND(ndis, pci, 1, 1, 1); -MODULE_DEPEND(ndis, wlan, 1, 1, 1); -MODULE_DEPEND(ndis, ndisapi, 1, 1, 1); - -/* - * Various supported device vendors/types and their names. - * These are defined in the ndis_driver_data.h file. - */ -static struct ndis_pci_type ndis_devs[] = { -#ifdef NDIS_PCI_DEV_TABLE - NDIS_PCI_DEV_TABLE -#endif - { 0, 0, 0, NULL } -}; +MODULE_DEPEND(if_ndis, pci, 1, 1, 1); static int ndis_probe_pci (device_t); static int ndis_attach_pci (device_t); +static int ndis_detach_pci (device_t); static struct resource_list *ndis_get_resource_list (device_t, device_t); +static int ndis_devcompare (interface_type, + struct ndis_pci_type *, device_t); +extern int ndisdrv_modevent (module_t, int, void *); extern int ndis_attach (device_t); extern int ndis_shutdown (device_t); extern int ndis_detach (device_t); @@ -93,7 +82,7 @@ static device_method_t ndis_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ndis_probe_pci), DEVMETHOD(device_attach, ndis_attach_pci), - DEVMETHOD(device_detach, ndis_detach), + DEVMETHOD(device_detach, ndis_detach_pci), DEVMETHOD(device_shutdown, ndis_shutdown), DEVMETHOD(device_suspend, ndis_suspend), DEVMETHOD(device_resume, ndis_resume), @@ -105,28 +94,41 @@ static device_method_t ndis_methods[] = { }; static driver_t ndis_driver = { -#ifdef NDIS_DEVNAME - NDIS_DEVNAME, -#else "ndis", -#endif ndis_methods, sizeof(struct ndis_softc) }; static devclass_t ndis_devclass; -#ifdef NDIS_MODNAME -#define NDIS_MODNAME_OVERRIDE_PCI(x) \ - DRIVER_MODULE(x, pci, ndis_driver, ndis_devclass, NULL, NULL) -#define NDIS_MODNAME_OVERRIDE_CARDBUS(x) \ - DRIVER_MODULE(x, cardbus, ndis_driver, ndis_devclass, NULL, NULL) -NDIS_MODNAME_OVERRIDE_PCI(NDIS_MODNAME); -NDIS_MODNAME_OVERRIDE_CARDBUS(NDIS_MODNAME); -#else -DRIVER_MODULE(ndis, pci, ndis_driver, ndis_devclass, NULL, NULL); -DRIVER_MODULE(ndis, cardbus, ndis_driver, ndis_devclass, NULL, NULL); -#endif +DRIVER_MODULE(if_ndis, pci, ndis_driver, ndis_devclass, ndisdrv_modevent, NULL); +DRIVER_MODULE(if_ndis, cardbus, ndis_driver, ndis_devclass, ndisdrv_modevent, NULL); + +static int +ndis_devcompare(interface_type bustype, struct ndis_pci_type *t, device_t dev) +{ + uint16_t vid, did; + uint32_t subsys; + + if (bustype != PCIBus) + return(FALSE); + + vid = pci_get_vendor(dev); + did = pci_get_device(dev); + subsys = pci_get_subdevice(dev); + subsys = (subsys << 16) | pci_get_subvendor(dev); + + while(t->ndis_name != NULL) { + if ((t->ndis_vid == vid) && (t->ndis_did == did) && + (t->ndis_subsys == subsys || t->ndis_subsys == 0)) { + device_set_desc(dev, t->ndis_name); + return(TRUE); + } + t++; + } + + return(FALSE); +} /* * Probe for an NDIS device. Check the PCI vendor and device @@ -135,19 +137,20 @@ DRIVER_MODULE(ndis, cardbus, ndis_driver, ndis_devclass, NULL, NULL); static int ndis_probe_pci(device_t dev) { - struct ndis_pci_type *t; + driver_object *drv; + struct drvdb_ent *db; - t = ndis_devs; + drv = windrv_lookup(0, "PCI Bus"); - while(t->ndis_name != NULL) { - if ((pci_get_vendor(dev) == t->ndis_vid) && - (pci_get_device(dev) == t->ndis_did) && - ((pci_read_config(dev, PCIR_SUBVEND_0, 4) == - t->ndis_subsys) || t->ndis_subsys == 0)) { - device_set_desc(dev, t->ndis_name); - return(0); - } - t++; + if (drv == NULL) + return(ENXIO); + + db = windrv_match((matchfuncptr)ndis_devcompare, dev); + + if (db != NULL) { + /* Create PDO for this device instance */ + windrv_create_pdo(drv, dev); + return(0); } return(ENXIO); @@ -166,11 +169,24 @@ ndis_attach_pci(device_t dev) int devidx = 0, defidx = 0; struct resource_list *rl; struct resource_list_entry *rle; + struct drvdb_ent *db; + uint16_t vid, did; + uint32_t subsys; + + wlan_serialize_enter(); sc = device_get_softc(dev); unit = device_get_unit(dev); sc->ndis_dev = dev; + db = windrv_match((matchfuncptr)ndis_devcompare, dev); + if (db == NULL) { + wlan_serialize_exit(); + return (ENXIO); + } + sc->ndis_dobj = db->windrv_object; + sc->ndis_regvals = db->windrv_regvals; + /* * Map control/status registers. */ @@ -201,7 +217,7 @@ ndis_attach_pci(device_t dev) error = ENXIO; goto fail; } - if (rle->rid == PCIR_BAR(2)) { + if (sc->ndis_res_mem) { sc->ndis_altmem_rid = rle->rid; sc->ndis_res_altmem = bus_alloc_resource_any(dev, @@ -290,24 +306,25 @@ ndis_attach_pci(device_t dev) /* Figure out exactly which device we matched. */ - t = ndis_devs; + vid = pci_get_vendor(dev); + did = pci_get_device(dev); + subsys = pci_get_subdevice(dev); + subsys = (subsys << 16) | pci_get_subvendor(dev); + + t = db->windrv_devlist; while(t->ndis_name != NULL) { - if ((pci_get_vendor(dev) == t->ndis_vid) && - (pci_get_device(dev) == t->ndis_did)) { + if (t->ndis_vid == vid && t->ndis_did == did) { if (t->ndis_subsys == 0) defidx = devidx; - else { - if (t->ndis_subsys == - pci_read_config(dev, PCIR_SUBVEND_0, 4)) - break; - } + else if (t->ndis_subsys == subsys) + break; } t++; devidx++; } - if (ndis_devs[devidx].ndis_name == NULL) + if (t->ndis_name == NULL) sc->ndis_devidx = defidx; else sc->ndis_devidx = devidx; @@ -315,6 +332,18 @@ ndis_attach_pci(device_t dev) error = ndis_attach(dev); fail: + wlan_serialize_exit(); + return(error); +} + +static int +ndis_detach_pci(device_t dev) +{ + int error = 0; + + wlan_serialize_enter(); + error = ndis_detach(dev); + wlan_serialize_exit(); return(error); } @@ -326,5 +355,3 @@ ndis_get_resource_list(device_t dev, device_t child) sc = device_get_softc(dev); return (BUS_GET_RESOURCE_LIST(device_get_parent(sc->ndis_dev), dev)); } - -#endif /* NDIS_PCI_DEV_TABLE */ diff --git a/sys/dev/netif/ndis/if_ndis_usb.c b/sys/dev/netif/ndis/if_ndis_usb.c new file mode 100644 index 0000000000..ed3f4e9330 --- /dev/null +++ b/sys/dev/netif/ndis/if_ndis_usb.c @@ -0,0 +1,262 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/if_ndis/if_ndis_usb.c,v 1.10 2008/12/27 08:03:32 weongyo Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +SYSCTL_NODE(_hw, OID_AUTO, ndisusb, CTLFLAG_RD, 0, "NDIS USB driver parameters"); + +MODULE_DEPEND(if_ndis, usb, 1, 1, 1); + +static device_probe_t ndisusb_match; +static device_attach_t ndisusb_attach; +static device_detach_t ndisusb_detach; +static bus_get_resource_list_t ndis_get_resource_list; + +extern int ndisdrv_modevent (module_t, int, void *); +extern int ndis_attach (device_t); +extern int ndis_shutdown (device_t); +extern int ndis_detach (device_t); +extern int ndis_suspend (device_t); +extern int ndis_resume (device_t); + +extern unsigned char drv_data[]; + +static device_method_t ndis_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ndisusb_match), + DEVMETHOD(device_attach, ndisusb_attach), + DEVMETHOD(device_detach, ndisusb_detach), + DEVMETHOD(device_shutdown, ndis_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_get_resource_list, ndis_get_resource_list), + + { 0, 0 } +}; + +static driver_t ndis_driver = { + "ndis", + ndis_methods, + sizeof(struct ndis_softc) +}; + +static devclass_t ndis_devclass; + +DRIVER_MODULE(if_ndis, uhub, ndis_driver, ndis_devclass, ndisdrv_modevent, 0); + +static int +ndisusb_devcompare(interface_type bustype, struct ndis_usb_type *t, device_t dev) +{ + struct usb_attach_arg *uaa; + + if (bustype != PNPBus) + return (FALSE); + + uaa = device_get_ivars(dev); + + while (t->ndis_name != NULL) { + if ((uaa->vendor == t->ndis_vid) && + (uaa->product == t->ndis_did)) { + device_set_desc(dev, t->ndis_name); + return (TRUE); + } + t++; + } + + return (FALSE); +} + +static int +ndisusb_match(device_t self) +{ + struct drvdb_ent *db; + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (windrv_lookup(0, "USB Bus") == NULL) + return (UMATCH_NONE); + + if (uaa->iface != NULL) + return (UMATCH_NONE); + + db = windrv_match((matchfuncptr)ndisusb_devcompare, self); + if (db == NULL) + return (UMATCH_NONE); + + return (UMATCH_VENDOR_PRODUCT); +} + +static int +ndisusb_attach(device_t self) +{ + struct drvdb_ent *db; + struct ndisusb_softc *dummy = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); + struct ndis_softc *sc; + struct ndis_usb_type *t; + driver_object *drv; + int devidx = 0; + usbd_status status; + + wlan_serialize_enter(); + sc = (struct ndis_softc *)dummy; + + if (uaa->device == NULL) { + wlan_serialize_exit(); + return ENXIO; + } + + db = windrv_match((matchfuncptr)ndisusb_devcompare, self); + if (db == NULL) { + wlan_serialize_exit(); + return (ENXIO); + } + + sc->ndis_dev = self; + sc->ndis_dobj = db->windrv_object; + sc->ndis_regvals = db->windrv_regvals; + sc->ndis_iftype = PNPBus; + + /* Create PDO for this device instance */ + + drv = windrv_lookup(0, "USB Bus"); + windrv_create_pdo(drv, self); + + status = usbd_set_config_no(uaa->device, NDISUSB_CONFIG_NO, 0); + if (status != USBD_NORMAL_COMPLETION) { + device_printf(self, "setting config no failed\n"); + wlan_serialize_exit(); + return (ENXIO); + } + + /* Figure out exactly which device we matched. */ + + t = db->windrv_devlist; + + while (t->ndis_name != NULL) { + if ((uaa->vendor == t->ndis_vid) && + (uaa->product == t->ndis_did)) { + sc->ndis_devidx = devidx; + break; + } + t++; + devidx++; + } + + if (ndis_attach(self) != 0) { + wlan_serialize_exit(); + return ENXIO; + } + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, uaa->device, self); + + wlan_serialize_exit(); + return 0; +} + +static int +ndisusb_detach(device_t self) +{ + int i, error; + struct ndis_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); + + wlan_serialize_enter(); + sc->ndisusb_status |= NDISUSB_STATUS_DETACH; + + for (i = 0; i < NDISUSB_ENDPT_MAX; i++) { + if (sc->ndisusb_ep[i] == NULL) + continue; + + usbd_abort_pipe(sc->ndisusb_ep[i]); + usbd_close_pipe(sc->ndisusb_ep[i]); + sc->ndisusb_ep[i] = NULL; + } + + if (sc->ndisusb_iin_buf != NULL) { + kfree(sc->ndisusb_iin_buf, M_USBDEV); + sc->ndisusb_iin_buf = NULL; + } + + ndis_pnpevent_nic(self, NDIS_PNP_EVENT_SURPRISE_REMOVED); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, uaa->device, self); + + error = ndis_detach(self); + + wlan_serialize_exit(); + return error; +} + +static struct resource_list * +ndis_get_resource_list(device_t dev, device_t child) +{ + struct ndis_softc *sc; + + sc = device_get_softc(dev); + return (BUS_GET_RESOURCE_LIST(device_get_parent(sc->ndis_dev), dev)); +} diff --git a/sys/dev/netif/ndis/if_ndisvar.h b/sys/dev/netif/ndis/if_ndisvar.h index 61746e51cc..e9f26e73bc 100644 --- a/sys/dev/netif/ndis/if_ndisvar.h +++ b/sys/dev/netif/ndis/if_ndisvar.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,13 +29,22 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/if_ndis/if_ndisvar.h,v 1.15 2004/07/11 00:19:30 wpaul Exp $ - * $DragonFly: src/sys/dev/netif/ndis/if_ndisvar.h,v 1.3 2005/12/31 19:39:14 dillon Exp $ + * $FreeBSD: src/sys/dev/if_ndis/if_ndisvar.h,v 1.39 2009/05/02 15:14:18 thompsa Exp $ */ #define NDIS_DEFAULT_NODENAME "FreeBSD NDIS node" #define NDIS_NODENAME_LEN 32 +/* For setting/getting OIDs from userspace. */ + +struct ndis_oid_data { + uint32_t oid; + uint32_t len; +#ifdef notdef + uint8_t data[1]; +#endif +}; + struct ndis_pci_type { uint16_t ndis_vid; uint16_t ndis_did; @@ -49,32 +58,74 @@ struct ndis_pccard_type { char *ndis_name; }; +struct ndis_usb_type { + uint16_t ndis_vid; + uint16_t ndis_did; + char *ndis_name; +}; + struct ndis_shmem { + list_entry ndis_list; bus_dma_tag_t ndis_stag; bus_dmamap_t ndis_smap; void *ndis_saddr; - struct ndis_shmem *ndis_next; + ndis_physaddr ndis_paddr; }; struct ndis_cfglist { ndis_cfg ndis_cfg; + struct sysctl_oid *ndis_oid; TAILQ_ENTRY(ndis_cfglist) link; }; +/* + * Helper struct to make parsing information + * elements easier. + */ +struct ndis_ie { + uint8_t ni_oui[3]; + uint8_t ni_val; +}; + TAILQ_HEAD(nch, ndis_cfglist); -#define NDIS_INITIALIZED(sc) (sc->ndis_block.nmb_miniportadapterctx != NULL) +#define NDIS_INITIALIZED(sc) (sc->ndis_block->nmb_devicectx != NULL) +#define NDIS_TXPKTS 64 #define NDIS_INC(x) \ - (x)->ndis_txidx = ((x)->ndis_txidx + 1) % (x)->ndis_maxpkts + (x)->ndis_txidx = ((x)->ndis_txidx + 1) % (x)->ndis_maxpkts + + +#define NDIS_EVENTS 4 +#define NDIS_EVTINC(x) (x) = ((x) + 1) % NDIS_EVENTS + +struct ndis_evt { + uint32_t ne_sts; + uint32_t ne_len; + char *ne_buf; +}; + +struct ndis_vap { + struct ieee80211vap vap; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define NDIS_VAP(vap) ((struct ndis_vap *)(vap)) -#define arpcom ic.ic_ac +#define NDISUSB_CONFIG_NO 1 +#define NDISUSB_IFACE_INDEX 0 +#define NDISUSB_INTR_TIMEOUT 1000 +#define NDISUSB_TX_TIMEOUT 10000 +struct ndisusb_xfer { + usbd_xfer_handle nx_xfer; + usbd_private_handle nx_priv; + usbd_status nx_status; + list_entry nx_xferlist; +}; struct ndis_softc { - struct ieee80211com ic; /* interface info */ -#ifdef notdef - struct ieee80211com arpcom; /* interface info */ -#endif + struct ifnet *ifp; struct ifmedia ifmedia; /* media info */ u_long ndis_hwassist; uint32_t ndis_v4tx; @@ -95,23 +146,28 @@ struct ndis_softc { struct resource *ndis_res_cm; /* common mem (pccard) */ struct resource_list ndis_rl; int ndis_rescnt; + struct lock ndis_lock; + uint8_t ndis_irql; device_t ndis_dev; int ndis_unit; - ndis_miniport_block ndis_block; - ndis_miniport_characteristics ndis_chars; + ndis_miniport_block *ndis_block; + ndis_miniport_characteristics *ndis_chars; interface_type ndis_type; - struct callout ndis_stat_timer; + struct callout ndis_scan_callout; + struct callout ndis_stat_callout; int ndis_maxpkts; ndis_oid *ndis_oids; int ndis_oidcnt; int ndis_txidx; int ndis_txpending; ndis_packet **ndis_txarray; + ndis_handle ndis_txpool; int ndis_sc; ndis_cfg *ndis_regvals; struct nch ndis_cfglist_head; int ndis_80211; int ndis_link; + uint32_t ndis_sts; uint32_t ndis_filter; int ndis_if_flags; int ndis_skip; @@ -122,13 +178,55 @@ struct ndis_softc { #endif int ndis_devidx; interface_type ndis_iftype; - + driver_object *ndis_dobj; + io_workitem *ndis_tickitem; + io_workitem *ndis_startitem; + io_workitem *ndis_resetitem; + io_workitem *ndis_inputitem; + kdpc ndis_rxdpc; bus_dma_tag_t ndis_parent_tag; - struct ndis_shmem *ndis_shlist; + list_entry ndis_shlist; bus_dma_tag_t ndis_mtag; bus_dma_tag_t ndis_ttag; bus_dmamap_t *ndis_mmaps; bus_dmamap_t *ndis_tmaps; int ndis_mmapcnt; + struct ndis_evt ndis_evt[NDIS_EVENTS]; + int ndis_evtpidx; + int ndis_evtcidx; + struct ifqueue ndis_rxqueue; + kspin_lock ndis_rxlock; + + int (*ndis_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + int ndis_tx_timer; + int ndis_hang_timer; + + io_workitem *ndisusb_xferitem; + list_entry ndisusb_xferlist; + kspin_lock ndisusb_xferlock; +#define NDISUSB_ENDPT_BOUT 0 +#define NDISUSB_ENDPT_BIN 1 +#define NDISUSB_ENDPT_IIN 2 +#define NDISUSB_ENDPT_IOUT 3 +#define NDISUSB_ENDPT_MAX 4 + usbd_pipe_handle ndisusb_ep[NDISUSB_ENDPT_MAX]; + char *ndisusb_iin_buf; + int ndisusb_status; +#define NDISUSB_STATUS_DETACH 0x1 }; +#define NDISMTX_LOCK(_sc) lockmgr(&(_sc)->ndis_lock, LK_EXCLUSIVE) +#define NDISMTX_UNLOCK(_sc) lockmgr(&(_sc)->ndis_lock, LK_RELEASE) +#define NDISUSB_LOCK(_sc) get_mplock() +#define NDISUSB_UNLOCK(_sc) rel_mplock() +#define NDIS_LOCK(_sc) do { \ + if ((_sc)->ndis_iftype == PNPBus) \ + NDISUSB_LOCK(_sc); \ + NDISMTX_LOCK(_sc); \ +} while (0) +#define NDIS_UNLOCK(_sc) do { \ + if ((_sc)->ndis_iftype == PNPBus) \ + NDISUSB_UNLOCK(_sc); \ + NDISMTX_UNLOCK(_sc); \ +} while (0) diff --git a/sys/emulation/Makefile b/sys/emulation/Makefile index 1f83f33afd..e69e177950 100644 --- a/sys/emulation/Makefile +++ b/sys/emulation/Makefile @@ -1,15 +1,7 @@ -# $DragonFly: src/sys/emulation/Makefile,v 1.4 2008/07/23 16:39:34 dillon Exp $ -# +SUBDIR= ndis .if ${MACHINE_ARCH} == i386 -SUBDIR=linux -.elif ${MACHINE_ARCH} == x86_64 -SUBDIR= -.else -.error Unknown MACHINE_ARCH. +SUBDIR+=linux .endif -# needs to be ported to new net80211 stack -# SUBDIR= ndis - .include diff --git a/sys/emulation/ndis/Makefile b/sys/emulation/ndis/Makefile index e0497bb66d..5b70ade497 100644 --- a/sys/emulation/ndis/Makefile +++ b/sys/emulation/ndis/Makefile @@ -1,9 +1,17 @@ -# $DragonFly: src/sys/emulation/ndis/Makefile,v 1.3 2006/06/25 11:02:39 corecode Exp $ -# +# $FreeBSD: src/sys/modules/ndis/Makefile,v 1.14 2010/08/23 06:13:29 imp Exp $ KMOD= ndis SRCS= subr_pe.c subr_ndis.c subr_hal.c subr_ntoskrnl.c kern_ndis.c +SRCS+= kern_windrv.c subr_usbd.c SRCS+= device_if.h bus_if.h pci_if.h +SRCS+= opt_usb.h -.include +.if ${MACHINE_ARCH} == "x86_64" +SRCS+= winx64_wrap.S +.endif + +.if ${MACHINE_ARCH} == "i386" +SRCS+= winx32_wrap.S +.endif +.include diff --git a/sys/emulation/ndis/cfg_var.h b/sys/emulation/ndis/cfg_var.h index 1da6d61da4..56a02a3735 100644 --- a/sys/emulation/ndis/cfg_var.h +++ b/sys/emulation/ndis/cfg_var.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,8 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/compat/ndis/cfg_var.h,v 1.2 2003/12/18 03:51:21 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/cfg_var.h,v 1.1 2004/07/29 20:51:33 dillon Exp $ + * $FreeBSD: src/sys/compat/ndis/cfg_var.h,v 1.3 2005/01/05 22:34:36 imp Exp $ */ #ifndef _CFG_VAR_H_ diff --git a/sys/emulation/ndis/hal_var.h b/sys/emulation/ndis/hal_var.h index a2ed11cbfe..a3580a19e7 100644 --- a/sys/emulation/ndis/hal_var.h +++ b/sys/emulation/ndis/hal_var.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,8 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/compat/ndis/hal_var.h,v 1.3 2004/04/14 07:48:02 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/hal_var.h,v 1.1 2004/07/29 20:51:33 dillon Exp $ + * $FreeBSD: src/sys/compat/ndis/hal_var.h,v 1.10 2009/03/12 02:51:55 weongyo Exp $ */ #ifndef _HAL_VAR_H_ @@ -47,13 +46,13 @@ extern image_patch_table hal_functbl[]; __BEGIN_DECLS -__stdcall __regcall uint8_t hal_lock(REGARGS1(kspin_lock *lock)); -__stdcall __regcall void hal_unlock(REGARGS2(kspin_lock *lock, uint8_t newirql)); -__stdcall uint8_t hal_irql(void); -__stdcall __regcall uint8_t hal_raise_irql(REGARGS1(uint8_t irql)); -__stdcall __regcall void hal_lower_irql(REGARGS1(uint8_t oldirql)); +extern int hal_libinit(void); +extern int hal_libfini(void); +extern uint8_t KfAcquireSpinLock(kspin_lock *); +extern void KfReleaseSpinLock(kspin_lock *, uint8_t); +extern uint8_t KfRaiseIrql(uint8_t); +extern void KfLowerIrql(uint8_t); +extern uint8_t KeGetCurrentIrql(void); __END_DECLS #endif /* _HAL_VAR_H_ */ - - diff --git a/sys/emulation/ndis/kern_ndis.c b/sys/emulation/ndis/kern_ndis.c index 84b77d7b41..2cae57d9dd 100644 --- a/sys/emulation/ndis/kern_ndis.c +++ b/sys/emulation/ndis/kern_ndis.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,8 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/compat/ndis/kern_ndis.c,v 1.57 2004/07/11 00:19:30 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/kern_ndis.c,v 1.14 2006/12/20 18:14:41 dillon Exp $ + * $FreeBSD: src/sys/compat/ndis/kern_ndis.c,v 1.111 2011/02/23 21:45:28 brucec Exp $ */ #include @@ -45,6 +44,7 @@ #include #include #include +#include #include #include @@ -52,7 +52,6 @@ #include #include #include - #include #include @@ -64,495 +63,199 @@ #include #include -#include "regcall.h" -#include "pe_var.h" -#include "resource_var.h" -#include "ntoskrnl_var.h" -#include "ndis_var.h" -#include "hal_var.h" -#include "cfg_var.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include #include #define NDIS_DUMMY_PATH "\\\\some\\bogus\\path" -__stdcall static void ndis_status_func(ndis_handle, ndis_status, - void *, uint32_t); -__stdcall static void ndis_statusdone_func(ndis_handle); -__stdcall static void ndis_setdone_func(ndis_handle, ndis_status); -__stdcall static void ndis_getdone_func(ndis_handle, ndis_status); -__stdcall static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t); -__stdcall static void ndis_sendrsrcavail_func(ndis_handle); - -struct nd_head ndis_devhead; - -struct ndis_req { - void (*nr_func)(void *); - void *nr_arg; - int nr_exit; - STAILQ_ENTRY(ndis_req) link; +static void ndis_status_func(ndis_handle, ndis_status, void *, uint32_t); +static void ndis_statusdone_func(ndis_handle); +static void ndis_setdone_func(ndis_handle, ndis_status); +static void ndis_getdone_func(ndis_handle, ndis_status); +static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t); +static void ndis_sendrsrcavail_func(ndis_handle); +static void ndis_intrsetup(kdpc *, device_object *, + irp *, struct ndis_softc *); +static void ndis_return(device_object *, void *); + +static image_patch_table kernndis_functbl[] = { + IMPORT_SFUNC(ndis_status_func, 4), + IMPORT_SFUNC(ndis_statusdone_func, 1), + IMPORT_SFUNC(ndis_setdone_func, 2), + IMPORT_SFUNC(ndis_getdone_func, 2), + IMPORT_SFUNC(ndis_resetdone_func, 3), + IMPORT_SFUNC(ndis_sendrsrcavail_func, 1), + IMPORT_SFUNC(ndis_intrsetup, 4), + IMPORT_SFUNC(ndis_return, 1), + + { NULL, NULL, NULL } }; -struct ndisproc { - struct ndisqhead *np_q; - struct thread *np_td; - int np_state; -}; - -static void ndis_return(void *); -static int ndis_create_kthreads(void); -static void ndis_destroy_kthreads(void); -static void ndis_stop_thread(int); -static int ndis_enlarge_thrqueue(int); -static int ndis_shrink_thrqueue(int); -static void ndis_runq(void *); - -static MALLOC_DEFINE(M_NDIS_PACKET, "ndis_packet", "ndis packet slosh"); -static MALLOC_DEFINE(M_NDIS_BUFFER, "ndis_buffer", "ndis buffer slosh"); -struct lwkt_token ndis_thr_token; -static STAILQ_HEAD(ndisqhead, ndis_req) ndis_ttodo; -struct ndisqhead ndis_itodo; -struct ndisqhead ndis_free; -static int ndis_jobs = 32; - -static struct ndisproc ndis_tproc; -static struct ndisproc ndis_iproc; +static struct nd_head ndis_devhead; /* * This allows us to export our symbols to other modules. * Note that we call ourselves 'ndisapi' to avoid a namespace * collision with if_ndis.ko, which internally calls itself * 'ndis.' + * + * Note: some of the subsystems depend on each other, so the + * order in which they're started is important. The order of + * importance is: + * + * HAL - spinlocks and IRQL manipulation + * ntoskrnl - DPC and workitem threads, object waiting + * windrv - driver/device registration + * + * The HAL should also be the last thing shut down, since + * the ntoskrnl subsystem will use spinlocks right up until + * the DPC and workitem threads are terminated. */ + static int ndis_modevent(module_t mod, int cmd, void *arg) { int error = 0; + image_patch_table *patch; switch (cmd) { case MOD_LOAD: /* Initialize subsystems */ - ndis_libinit(); + hal_libinit(); ntoskrnl_libinit(); - - /* Initialize TX buffer UMA zone. */ - ndis_create_kthreads(); + windrv_libinit(); + ndis_libinit(); + usbd_libinit(); + + patch = kernndis_functbl; + while (patch->ipt_func != NULL) { + windrv_wrap((funcptr)patch->ipt_func, + (funcptr *)&patch->ipt_wrap, + patch->ipt_argcnt, patch->ipt_ftype); + patch++; + } TAILQ_INIT(&ndis_devhead); - break; case MOD_SHUTDOWN: - /* stop kthreads */ - ndis_destroy_kthreads(); if (TAILQ_FIRST(&ndis_devhead) == NULL) { /* Shut down subsystems */ ndis_libfini(); + usbd_libfini(); + windrv_libfini(); ntoskrnl_libfini(); + hal_libfini(); - /* Remove zones */ -#if 0 /* YYY */ - malloc_uninit(M_NDIS_PACKET); - malloc_uninit(M_NDIS_BUFFER); -#endif + patch = kernndis_functbl; + while (patch->ipt_func != NULL) { + windrv_unwrap(patch->ipt_wrap); + patch++; + } } break; case MOD_UNLOAD: - /* stop kthreads */ - ndis_destroy_kthreads(); - /* Shut down subsystems */ ndis_libfini(); + usbd_libfini(); + windrv_libfini(); ntoskrnl_libfini(); + hal_libfini(); + + patch = kernndis_functbl; + while (patch->ipt_func != NULL) { + windrv_unwrap(patch->ipt_wrap); + patch++; + } - /* Remove zones */ -#if 0 /* YYY */ - malloc_uninit(M_NDIS_PACKET); - malloc_uninit(M_NDIS_BUFFER); -#endif break; default: error = EINVAL; break; } - return(error); -} -DEV_MODULE(ndisapi, ndis_modevent, NULL); -MODULE_VERSION(ndisapi, 1); - -/* - * We create two kthreads for the NDIS subsystem. One of them is a task - * queue for performing various odd jobs. The other is an swi thread - * reserved exclusively for running interrupt handlers. The reason we - * have our own task queue is that there are some cases where we may - * need to sleep for a significant amount of time, and if we were to - * use one of the taskqueue threads, we might delay the processing - * of other pending tasks which might need to run right away. We have - * a separate swi thread because we don't want our interrupt handling - * to be delayed either. - * - * By default there are 32 jobs available to start, and another 8 - * are added to the free list each time a new device is created. - */ - -static void -ndis_runq(void *arg) -{ - struct ndis_req *r = NULL, *die = NULL; - struct ndisproc *p; - - p = arg; - get_mplock(); - - while (1) { - - /* Sleep, but preserve our original priority. */ - ndis_thsuspend(p->np_td, 0); - - /* Look for any jobs on the work queue. */ - - lwkt_gettoken(&ndis_thr_token); - p->np_state = NDIS_PSTATE_RUNNING; - while(STAILQ_FIRST(p->np_q) != NULL) { - r = STAILQ_FIRST(p->np_q); - STAILQ_REMOVE_HEAD(p->np_q, link); - lwkt_reltoken(&ndis_thr_token); - - /* Do the work. */ - - if (r->nr_func != NULL) - (*r->nr_func)(r->nr_arg); - - lwkt_gettoken(&ndis_thr_token); - STAILQ_INSERT_HEAD(&ndis_free, r, link); - - /* Check for a shutdown request */ - - if (r->nr_exit == TRUE) - die = r; - } - p->np_state = NDIS_PSTATE_SLEEPING; - lwkt_reltoken(&ndis_thr_token); - - /* Bail if we were told to shut down. */ - - if (die != NULL) - break; - } - - wakeup(die); - rel_mplock(); -} - -static int -ndis_create_kthreads(void) -{ - struct ndis_req *r; - int i, error = 0; - - lwkt_token_init(&ndis_thr_token, "ndis"); - - STAILQ_INIT(&ndis_ttodo); - STAILQ_INIT(&ndis_itodo); - STAILQ_INIT(&ndis_free); - - for (i = 0; i < ndis_jobs; i++) { - r = kmalloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK); - STAILQ_INSERT_HEAD(&ndis_free, r, link); - } - - if (error == 0) { - ndis_tproc.np_q = &ndis_ttodo; - ndis_tproc.np_state = NDIS_PSTATE_SLEEPING; - error = kthread_create_stk(ndis_runq, &ndis_tproc, - &ndis_tproc.np_td, NDIS_KSTACK_PAGES * PAGE_SIZE, - "ndis taskqueue"); - } - - if (error == 0) { - ndis_iproc.np_q = &ndis_itodo; - ndis_iproc.np_state = NDIS_PSTATE_SLEEPING; - error = kthread_create_stk(ndis_runq, &ndis_iproc, - &ndis_iproc.np_td, NDIS_KSTACK_PAGES * PAGE_SIZE, - "ndis swi"); - } - - if (error) { - while ((r = STAILQ_FIRST(&ndis_free)) != NULL) { - STAILQ_REMOVE_HEAD(&ndis_free, link); - kfree(r, M_DEVBUF); - } - return(error); - } - - return(0); -} - -static void -ndis_destroy_kthreads(void) -{ - struct ndis_req *r; - - /* Stop the threads. */ - - ndis_stop_thread(NDIS_TASKQUEUE); - ndis_stop_thread(NDIS_SWI); - - /* Destroy request structures. */ - - while ((r = STAILQ_FIRST(&ndis_free)) != NULL) { - STAILQ_REMOVE_HEAD(&ndis_free, link); - kfree(r, M_DEVBUF); - } - - lwkt_token_uninit(&ndis_thr_token); - - return; + return (error); } +DEV_MODULE(ndis, ndis_modevent, NULL); +MODULE_VERSION(ndis, 1); static void -ndis_stop_thread(int t) -{ - struct ndis_req *r; - struct ndisqhead *q; - thread_t td; - - if (t == NDIS_TASKQUEUE) { - q = &ndis_ttodo; - td = ndis_tproc.np_td; - } else { - q = &ndis_itodo; - td = ndis_iproc.np_td; - } - - /* Create and post a special 'exit' job. */ - - lwkt_gettoken(&ndis_thr_token); - r = STAILQ_FIRST(&ndis_free); - STAILQ_REMOVE_HEAD(&ndis_free, link); - r->nr_func = NULL; - r->nr_arg = NULL; - r->nr_exit = TRUE; - STAILQ_INSERT_TAIL(q, r, link); - lwkt_reltoken(&ndis_thr_token); - - ndis_thresume(td); - - /* wait for thread exit */ - - tsleep(r, PCATCH, "ndisthexit", hz * 60); - - /* Now empty the job list. */ - - lwkt_gettoken(&ndis_thr_token); - while ((r = STAILQ_FIRST(q)) != NULL) { - STAILQ_REMOVE_HEAD(q, link); - STAILQ_INSERT_HEAD(&ndis_free, r, link); - } - lwkt_reltoken(&ndis_thr_token); -} - -static int -ndis_enlarge_thrqueue(int cnt) -{ - struct ndis_req *r; - int i; - - for (i = 0; i < cnt; i++) { - r = kmalloc(sizeof(struct ndis_req), M_DEVBUF, M_WAITOK); - lwkt_gettoken(&ndis_thr_token); - STAILQ_INSERT_HEAD(&ndis_free, r, link); - ndis_jobs++; - lwkt_reltoken(&ndis_thr_token); - } - - return(0); -} - -static int -ndis_shrink_thrqueue(int cnt) -{ - struct ndis_req *r; - int i; - - for (i = 0; i < cnt; i++) { - lwkt_gettoken(&ndis_thr_token); - r = STAILQ_FIRST(&ndis_free); - if (r == NULL) { - lwkt_reltoken(&ndis_thr_token); - return(ENOMEM); - } - STAILQ_REMOVE_HEAD(&ndis_free, link); - ndis_jobs--; - lwkt_reltoken(&ndis_thr_token); - kfree(r, M_DEVBUF); - } - - return(0); -} - -int -ndis_unsched(void (*func)(void *), void *arg, int t) -{ - struct ndis_req *r; - struct ndisqhead *q; - thread_t td; - - if (t == NDIS_TASKQUEUE) { - q = &ndis_ttodo; - td = ndis_tproc.np_td; - } else { - q = &ndis_itodo; - td = ndis_iproc.np_td; - } - - lwkt_gettoken(&ndis_thr_token); - STAILQ_FOREACH(r, q, link) { - if (r->nr_func == func && r->nr_arg == arg) { - STAILQ_REMOVE(q, r, ndis_req, link); - STAILQ_INSERT_HEAD(&ndis_free, r, link); - lwkt_reltoken(&ndis_thr_token); - return(0); - } - } - - lwkt_reltoken(&ndis_thr_token); - - return(ENOENT); -} - -int -ndis_sched(void (*func)(void *), void *arg, int t) -{ - struct ndis_req *r; - struct ndisqhead *q; - thread_t td; - int s; - - if (t == NDIS_TASKQUEUE) { - q = &ndis_ttodo; - td = ndis_tproc.np_td; - } else { - q = &ndis_itodo; - td = ndis_iproc.np_td; - } - - lwkt_gettoken(&ndis_thr_token); - /* - * Check to see if an instance of this job is already - * pending. If so, don't bother queuing it again. - */ - STAILQ_FOREACH(r, q, link) { - if (r->nr_func == func && r->nr_arg == arg) { - lwkt_reltoken(&ndis_thr_token); - return(0); - } - } - r = STAILQ_FIRST(&ndis_free); - if (r == NULL) { - lwkt_reltoken(&ndis_thr_token); - return(EAGAIN); - } - STAILQ_REMOVE_HEAD(&ndis_free, link); - r->nr_func = func; - r->nr_arg = arg; - r->nr_exit = FALSE; - STAILQ_INSERT_TAIL(q, r, link); - if (t == NDIS_TASKQUEUE) - s = ndis_tproc.np_state; - else - s = ndis_iproc.np_state; - lwkt_reltoken(&ndis_thr_token); - - /* - * Post the job, but only if the thread is actually blocked - * on its own suspend call. If a driver queues up a job with - * NdisScheduleWorkItem() which happens to do a KeWaitForObject(), - * it may suspend there, and in that case we don't want to wake - * it up until KeWaitForObject() gets woken up on its own. - */ - if (s == NDIS_PSTATE_SLEEPING) - ndis_thresume(td); - - return(0); -} - -int -ndis_thsuspend(thread_t td, int timo) -{ - int error; - - error = tsleep(td, 0, "ndissp", timo); - return(error); -} - -void -ndis_thresume(struct thread *td) -{ - wakeup(td); -} - -__stdcall static void ndis_sendrsrcavail_func(ndis_handle adapter) { - return; } -__stdcall static void +static void ndis_status_func(ndis_handle adapter, ndis_status status, void *sbuf, - uint32_t slen) + uint32_t slen) { ndis_miniport_block *block; - block = adapter; + struct ndis_softc *sc; + struct ifnet *ifp; - if (block->nmb_ifp->if_flags & IFF_DEBUG) - device_printf (block->nmb_dev, "status: %x\n", status); - return; + block = adapter; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ifp = sc->ifp; + if (ifp->if_flags & IFF_DEBUG) + device_printf(sc->ndis_dev, "status: %x\n", status); } -__stdcall static void +static void ndis_statusdone_func(ndis_handle adapter) { ndis_miniport_block *block; + struct ndis_softc *sc; + struct ifnet *ifp; + block = adapter; - - if (block->nmb_ifp->if_flags & IFF_DEBUG) - device_printf (block->nmb_dev, "status complete\n"); - return; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ifp = sc->ifp; + if (ifp->if_flags & IFF_DEBUG) + device_printf(sc->ndis_dev, "status complete\n"); } -__stdcall static void +static void ndis_setdone_func(ndis_handle adapter, ndis_status status) { ndis_miniport_block *block; block = adapter; block->nmb_setstat = status; - wakeup(&block->nmb_wkupdpctimer); - return; + KeSetEvent(&block->nmb_setevent, IO_NO_INCREMENT, FALSE); } -__stdcall static void +static void ndis_getdone_func(ndis_handle adapter, ndis_status status) { ndis_miniport_block *block; block = adapter; block->nmb_getstat = status; - wakeup(&block->nmb_wkupdpctimer); - return; + KeSetEvent(&block->nmb_getevent, IO_NO_INCREMENT, FALSE); } -__stdcall static void +static void ndis_resetdone_func(ndis_handle adapter, ndis_status status, - uint8_t addressingreset) + uint8_t addressingreset) { ndis_miniport_block *block; + struct ndis_softc *sc; + struct ifnet *ifp; + block = adapter; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ifp = sc->ifp; - if (block->nmb_ifp->if_flags & IFF_DEBUG) - device_printf (block->nmb_dev, "reset done...\n"); - wakeup(block->nmb_ifp); - return; + if (ifp->if_flags & IFF_DEBUG) + device_printf(sc->ndis_dev, "reset done...\n"); + KeSetEvent(&block->nmb_resetevent, IO_NO_INCREMENT, FALSE); } int @@ -561,16 +264,17 @@ ndis_create_sysctls(void *arg) struct ndis_softc *sc; ndis_cfg *vals; char buf[256]; + struct sysctl_oid *oidp; + struct sysctl_ctx_entry *e; if (arg == NULL) - return(EINVAL); + return (EINVAL); sc = arg; vals = sc->ndis_regvals; TAILQ_INIT(&sc->ndis_cfglist_head); -#if __FreeBSD_version < 502113 /* Create the sysctl tree. */ sc->ndis_tree = SYSCTL_ADD_NODE(&sc->ndis_ctx, @@ -578,32 +282,34 @@ ndis_create_sysctls(void *arg) device_get_nameunit(sc->ndis_dev), CTLFLAG_RD, 0, device_get_desc(sc->ndis_dev)); -#endif /* Add the driver-specific registry keys. */ - vals = sc->ndis_regvals; while(1) { if (vals->nc_cfgkey == NULL) break; + if (vals->nc_idx != sc->ndis_devidx) { vals++; continue; } -#if 1 - SYSCTL_ADD_STRING(&sc->ndis_ctx, - SYSCTL_CHILDREN(sc->ndis_tree), - OID_AUTO, vals->nc_cfgkey, - CTLFLAG_RW, vals->nc_val, - sizeof(vals->nc_val), - vals->nc_cfgdesc); -#else - SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)), - OID_AUTO, vals->nc_cfgkey, - CTLFLAG_RW, vals->nc_val, - sizeof(vals->nc_val), - vals->nc_cfgdesc); -#endif + + /* See if we already have a sysctl with this name */ + + oidp = NULL; + TAILQ_FOREACH(e, &sc->ndis_ctx, link) { + oidp = e->entry; + if (strcasecmp(oidp->oid_name, vals->nc_cfgkey) == 0) + break; + oidp = NULL; + } + + if (oidp != NULL) { + vals++; + continue; + } + + ndis_add_sysctl(sc, vals->nc_cfgkey, vals->nc_cfgdesc, + vals->nc_val, CTLFLAG_RW); vals++; } @@ -620,6 +326,16 @@ ndis_create_sysctls(void *arg) ndis_add_sysctl(sc, "NdisVersion", "NDIS API Version", "0x00050001", CTLFLAG_RD); + /* + * Some miniport drivers rely on the existence of the SlotNumber, + * NetCfgInstanceId and DriverDesc keys. + */ + ndis_add_sysctl(sc, "SlotNumber", "Slot Numer", "01", CTLFLAG_RD); + ndis_add_sysctl(sc, "NetCfgInstanceId", "NetCfgInstanceId", + "{12345678-1234-5678-CAFE0-123456789ABC}", CTLFLAG_RD); + ndis_add_sysctl(sc, "DriverDesc", "Driver Description", + "NDIS Network Adapter", CTLFLAG_RD); + /* Bus type (PCI, PCMCIA, etc...) */ ksprintf(buf, "%d", (int)sc->ndis_iftype); ndis_add_sysctl(sc, "BusType", "Bus Type", buf, CTLFLAG_RD); @@ -636,7 +352,7 @@ ndis_create_sysctls(void *arg) "Interrupt Number", buf, CTLFLAG_RD); } - return(0); + return (0); } int @@ -659,97 +375,130 @@ ndis_add_sysctl(void *arg, char *key, char *desc, char *val, int flag) TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link); -#if 1 + cfg->ndis_oid = SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree), OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag, cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val), cfg->ndis_cfg.nc_cfgdesc); -#else - SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)), - OID_AUTO, cfg->ndis_cfg.nc_cfgkey, flag, - cfg->ndis_cfg.nc_val, sizeof(cfg->ndis_cfg.nc_val), - cfg->ndis_cfg.nc_cfgdesc); -#endif - return(0); + return (0); } +/* + * Somewhere, somebody decided "hey, let's automatically create + * a sysctl tree for each device instance as it's created -- it'll + * make life so much easier!" Lies. Why must they turn the kernel + * into a house of lies? + */ + int ndis_flush_sysctls(void *arg) { struct ndis_softc *sc; struct ndis_cfglist *cfg; + struct sysctl_ctx_list *clist; sc = arg; + clist = &sc->ndis_ctx; + while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) { cfg = TAILQ_FIRST(&sc->ndis_cfglist_head); TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link); + sysctl_ctx_entry_del(clist, cfg->ndis_oid); + sysctl_remove_oid(cfg->ndis_oid, 1, 0); kfree(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF); kfree(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF); kfree(cfg, M_DEVBUF); } - return(0); + return (0); +} + +void * +ndis_get_routine_address(struct image_patch_table *functbl, char *name) +{ + int i; + + for (i = 0; functbl[i].ipt_name != NULL; i++) + if (strcmp(name, functbl[i].ipt_name) == 0) + return (functbl[i].ipt_wrap); + return (NULL); } static void -ndis_return(void *arg) +ndis_return(device_object *dobj, void *arg) { - struct ndis_softc *sc; + ndis_miniport_block *block; + ndis_miniport_characteristics *ch; ndis_return_handler returnfunc; ndis_handle adapter; ndis_packet *p; uint8_t irql; + list_entry *l; + + block = arg; + ch = IoGetDriverObjectExtension(dobj->do_drvobj, (void *)1); p = arg; - sc = p->np_softc; - adapter = sc->ndis_block.nmb_miniportadapterctx; + adapter = block->nmb_miniportadapterctx; if (adapter == NULL) return; - returnfunc = sc->ndis_chars.nmc_return_packet_func; - irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL); - returnfunc(adapter, p); - FASTCALL1(hal_lower_irql, irql); + returnfunc = ch->nmc_return_packet_func; - return; + KeAcquireSpinLock(&block->nmb_returnlock, &irql); + while (!IsListEmpty(&block->nmb_returnlist)) { + l = RemoveHeadList((&block->nmb_returnlist)); + p = CONTAINING_RECORD(l, ndis_packet, np_list); + InitializeListHead((&p->np_list)); + KeReleaseSpinLock(&block->nmb_returnlock, irql); + MSCALL2(returnfunc, adapter, p); + KeAcquireSpinLock(&block->nmb_returnlock, &irql); + } + KeReleaseSpinLock(&block->nmb_returnlock, irql); } static void -ndis_extref_packet(void *arg) +ndis_reference_packet(void *arg) { - ndis_packet *p = arg; + ndis_packet *p; + + if (arg == NULL) + return; + + p = arg; - ++p->np_refcnt; + /* Increment refcount. */ + atomic_add_int(&p->np_refcnt, 1); } -static void -ndis_extfree_packet(void *arg) +void +ndis_return_packet(void *arg) { - ndis_packet *p = arg; + ndis_packet *p; + ndis_miniport_block *block; - if (p == NULL) + if (arg == NULL) return; - /* Decrement refcount. */ - p->np_refcnt--; + p = arg; /* Release packet when refcount hits zero, otherwise return. */ - if (p->np_refcnt) + if (atomic_fetchadd_int(&p->np_refcnt, -1) > 1) return; - ndis_sched(ndis_return, p, NDIS_SWI); + block = ((struct ndis_softc *)p->np_softc)->ndis_block; - return; -} + KeAcquireSpinLockAtDpcLevel(&block->nmb_returnlock); + InitializeListHead((&p->np_list)); + InsertHeadList((&block->nmb_returnlist), (&p->np_list)); + KeReleaseSpinLockFromDpcLevel(&block->nmb_returnlock); -void -ndis_return_packet(struct ndis_softc *sc, ndis_packet *p) -{ - ndis_extfree_packet(p); + IoQueueWorkItem(block->nmb_returnitem, + (io_workitem_func)kernndis_functbl[7].ipt_wrap, + WORKQUEUE_CRITICAL, block); } void @@ -761,12 +510,10 @@ ndis_free_bufs(ndis_buffer *b0) return; while(b0 != NULL) { - next = b0->nb_next; - kfree(b0, M_NDIS_BUFFER); + next = b0->mdl_next; + IoFreeMdl(b0); b0 = next; } - - return; } void @@ -776,9 +523,7 @@ ndis_free_packet(ndis_packet *p) return; ndis_free_bufs(p->np_private.npp_head); - kfree(p, M_NDIS_PACKET); - - return; + NdisFreePacket(p); } int @@ -790,12 +535,13 @@ ndis_convert_res(void *arg) ndis_miniport_block *block; device_t dev; struct resource_list *brl; + struct resource_list_entry *brle; struct resource_list brl_rev; - struct resource_list_entry *brle, *n; - int error = 0; + struct resource_list_entry *n; + int error = 0; sc = arg; - block = &sc->ndis_block; + block = sc->ndis_block; dev = sc->ndis_dev; SLIST_INIT(&brl_rev); @@ -805,7 +551,7 @@ ndis_convert_res(void *arg) M_DEVBUF, M_WAITOK|M_NULLOK|M_ZERO); if (rl == NULL) - return(ENOMEM); + return (ENOMEM); rl->cprl_version = 5; rl->cprl_version = 1; @@ -829,6 +575,7 @@ ndis_convert_res(void *arg) * in order to fix this, we have to create our own * temporary list with the entries in reverse order. */ + SLIST_FOREACH(brle, brl, link) { n = kmalloc(sizeof(struct resource_list_entry), M_TEMP, M_WAITOK|M_NULLOK); @@ -858,15 +605,20 @@ ndis_convert_res(void *arg) CM_RESOURCE_MEMORY_READ_WRITE; prd->cprd_sharedisp = CmResourceShareDeviceExclusive; - prd->u.cprd_port.cprd_start.np_quad = + prd->u.cprd_mem.cprd_start.np_quad = brle->start; - prd->u.cprd_port.cprd_len = brle->count; + prd->u.cprd_mem.cprd_len = brle->count; break; case SYS_RES_IRQ: prd->cprd_type = CmResourceTypeInterrupt; prd->cprd_flags = 0; + /* + * Always mark interrupt resources as + * shared, since in our implementation, + * they will be. + */ prd->cprd_sharedisp = - CmResourceShareDeviceExclusive; + CmResourceShareShared; prd->u.cprd_intr.cprd_level = brle->start; prd->u.cprd_intr.cprd_vector = brle->start; prd->u.cprd_intr.cprd_affinity = 0; @@ -888,7 +640,7 @@ bad: kfree (n, M_TEMP); } - return(error); + return (error); } /* @@ -900,48 +652,46 @@ bad: * the ndis_packet as external storage. In most cases, this will * point to a memory region allocated by the driver (either by * ndis_malloc_withtag() or ndis_alloc_sharedmem()). We expect - * the driver to handle free()ing this region for is, so we set up + * the driver to handle kfree()ing this region for is, so we set up * a dummy no-op free handler for it. - */ + */ int ndis_ptom(struct mbuf **m0, ndis_packet *p) { - struct mbuf *m, *prev = NULL; + struct mbuf *m = NULL, *prev = NULL; ndis_buffer *buf; ndis_packet_private *priv; uint32_t totlen = 0; + struct ifnet *ifp; + struct ether_header *eh; + int diff; if (p == NULL || m0 == NULL) - return(EINVAL); + return (EINVAL); priv = &p->np_private; buf = priv->npp_head; p->np_refcnt = 0; - for (buf = priv->npp_head; buf != NULL; buf = buf->nb_next) { - if (buf == priv->npp_head) - MGETHDR(m, MB_DONTWAIT, MT_HEADER); - else + for (buf = priv->npp_head; buf != NULL; buf = buf->mdl_next) { + if (buf == priv->npp_head) { + /* XXX swildner: why not MT_HEADER? (see FreeBSD) */ + MGETHDR(m, MB_DONTWAIT, MT_DATA); + } else { MGET(m, MB_DONTWAIT, MT_DATA); + } if (m == NULL) { m_freem(*m0); *m0 = NULL; - return(ENOBUFS); + return (ENOBUFS); } - m->m_len = buf->nb_bytecount; - m->m_data = MDL_VA(buf); - m->m_ext.ext_free = ndis_extfree_packet; - m->m_ext.ext_ref = ndis_extref_packet; - m->m_ext.ext_arg = p; - m->m_ext.ext_buf = m->m_data; - m->m_ext.ext_size = m->m_len; - m->m_flags |= M_EXT; -#if 0 - MEXTADD(m, m->m_data, m->m_len, ndis_free_packet, - p, 0, EXT_NDIS); -#endif - p->np_refcnt++; + m->m_len = MmGetMdlByteCount(buf); + m->m_data = MmGetMdlVirtualAddress(buf); + m_extadd(m, m->m_data, m->m_len, ndis_reference_packet, + ndis_return_packet, p); +// p->np_refcnt++; + totlen += m->m_len; if (m->m_flags & M_PKTHDR) *m0 = m; @@ -950,13 +700,31 @@ ndis_ptom(struct mbuf **m0, ndis_packet *p) prev = m; } + /* + * This is a hack to deal with the Marvell 8335 driver + * which, when associated with an AP in WPA-PSK mode, + * seems to overpad its frames by 8 bytes. I don't know + * that the extra 8 bytes are for, and they're not there + * in open mode, so for now clamp the frame size at 1514 + * until I can figure out how to deal with this properly, + * otherwise if_ethersubr() will spank us by discarding + * the 'oversize' frames. + */ + + eh = mtod((*m0), struct ether_header *); + ifp = ((struct ndis_softc *)p->np_softc)->ifp; + if (totlen > ETHER_MAX_FRAME(ifp, eh->ether_type, FALSE)) { + diff = totlen - ETHER_MAX_FRAME(ifp, eh->ether_type, FALSE); + totlen -= diff; + m->m_len -= diff; + } (*m0)->m_pkthdr.len = totlen; - return(0); + return (0); } /* - * Create an mbuf chain from an NDIS packet chain. + * Create an NDIS packet from an mbuf chain. * This is used mainly when transmitting packets, where we need * to turn an mbuf off an interface's send queue and transform it * into an NDIS packet which will be fed into the NDIS driver's @@ -977,43 +745,33 @@ ndis_mtop(struct mbuf *m0, ndis_packet **p) ndis_buffer *buf = NULL, *prev = NULL; ndis_packet_private *priv; - if (p == NULL || m0 == NULL) - return(EINVAL); + if (p == NULL || *p == NULL || m0 == NULL) + return (EINVAL); - /* If caller didn't supply a packet, make one. */ - if (*p == NULL) { - *p = kmalloc(sizeof(ndis_packet), M_NDIS_PACKET, M_NOWAIT|M_ZERO); - if (*p == NULL) - return(ENOMEM); - } - priv = &(*p)->np_private; priv->npp_totlen = m0->m_pkthdr.len; - priv->npp_packetooboffset = offsetof(ndis_packet, np_oob); - priv->npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS; for (m = m0; m != NULL; m = m->m_next) { if (m->m_len == 0) continue; - buf = kmalloc(sizeof(ndis_buffer), M_NDIS_BUFFER, M_NOWAIT|M_ZERO); + buf = IoAllocateMdl(m->m_data, m->m_len, FALSE, FALSE, NULL); if (buf == NULL) { ndis_free_packet(*p); *p = NULL; - return(ENOMEM); + return (ENOMEM); } + MmBuildMdlForNonPagedPool(buf); - MDL_INIT(buf, m->m_data, m->m_len); if (priv->npp_head == NULL) priv->npp_head = buf; else - prev->nb_next = buf; + prev->mdl_next = buf; prev = buf; } priv->npp_tail = buf; - priv->npp_totlen = m0->m_pkthdr.len; - return(0); + return (0); } int @@ -1023,7 +781,7 @@ ndis_get_supported_oids(void *arg, ndis_oid **oids, int *oidcnt) ndis_oid *o; if (arg == NULL || oids == NULL || oidcnt == NULL) - return(EINVAL); + return (EINVAL); len = 0; ndis_get_info(arg, OID_GEN_SUPPORTED_LIST, NULL, &len); @@ -1033,13 +791,13 @@ ndis_get_supported_oids(void *arg, ndis_oid **oids, int *oidcnt) if (rval) { kfree(o, M_DEVBUF); - return(rval); + return (rval); } *oids = o; *oidcnt = len / 4; - return(0); + return (0); } int @@ -1050,25 +808,51 @@ ndis_set_info(void *arg, ndis_oid oid, void *buf, int *buflen) ndis_handle adapter; ndis_setinfo_handler setfunc; uint32_t byteswritten = 0, bytesneeded = 0; - int error; uint8_t irql; + uint64_t duetime; + + /* + * According to the NDIS spec, MiniportQueryInformation() + * and MiniportSetInformation() requests are handled serially: + * once one request has been issued, we must wait for it to + * finish before allowing another request to proceed. + */ sc = arg; - setfunc = sc->ndis_chars.nmc_setinfo_func; - adapter = sc->ndis_block.nmb_miniportadapterctx; - if (adapter == NULL || setfunc == NULL) - return(ENXIO); + KeResetEvent(&sc->ndis_block->nmb_setevent); + + KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); + + if (sc->ndis_block->nmb_pendingreq != NULL) { + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + panic("ndis_set_info() called while other request pending"); + } else + sc->ndis_block->nmb_pendingreq = (ndis_request *)sc; + + setfunc = sc->ndis_chars->nmc_setinfo_func; + adapter = sc->ndis_block->nmb_miniportadapterctx; + + if (adapter == NULL || setfunc == NULL || + sc->ndis_block->nmb_devicectx == NULL) { + sc->ndis_block->nmb_pendingreq = NULL; + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + return (ENXIO); + } - irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL); - rval = setfunc(adapter, oid, buf, *buflen, + rval = MSCALL6(setfunc, adapter, oid, buf, *buflen, &byteswritten, &bytesneeded); - FASTCALL1(hal_lower_irql, irql); + + sc->ndis_block->nmb_pendingreq = NULL; + + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); if (rval == NDIS_STATUS_PENDING) { - error = tsleep(&sc->ndis_block.nmb_wkupdpctimer, - 0, "ndisset", 5 * hz); - rval = sc->ndis_block.nmb_setstat; + /* Wait up to 5 seconds. */ + duetime = (5 * 1000000) * -10; + KeWaitForSingleObject(&sc->ndis_block->nmb_setevent, + 0, 0, FALSE, &duetime); + rval = sc->ndis_block->nmb_setstat; } if (byteswritten) @@ -1077,22 +861,22 @@ ndis_set_info(void *arg, ndis_oid oid, void *buf, int *buflen) *buflen = bytesneeded; if (rval == NDIS_STATUS_INVALID_LENGTH) - return(ENOSPC); + return (ENOSPC); if (rval == NDIS_STATUS_INVALID_OID) - return(EINVAL); + return (EINVAL); if (rval == NDIS_STATUS_NOT_SUPPORTED || rval == NDIS_STATUS_NOT_ACCEPTED) - return(ENOTSUP); + return (ENOTSUP); if (rval != NDIS_STATUS_SUCCESS) - return(ENODEV); + return (ENODEV); - return(0); + return (0); } -typedef __stdcall void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status); +typedef void (*ndis_senddone_func)(ndis_handle, ndis_packet *, ndis_status); int ndis_send_packets(void *arg, ndis_packet **packets, int cnt) @@ -1100,20 +884,22 @@ ndis_send_packets(void *arg, ndis_packet **packets, int cnt) struct ndis_softc *sc; ndis_handle adapter; ndis_sendmulti_handler sendfunc; - ndis_senddone_func senddonefunc; + ndis_senddone_func senddonefunc; int i; ndis_packet *p; - uint8_t irql; + uint8_t irql = 0; sc = arg; - adapter = sc->ndis_block.nmb_miniportadapterctx; + adapter = sc->ndis_block->nmb_miniportadapterctx; if (adapter == NULL) - return(ENXIO); - sendfunc = sc->ndis_chars.nmc_sendmulti_func; - senddonefunc = sc->ndis_block.nmb_senddone_func; - irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL); - sendfunc(adapter, packets, cnt); - FASTCALL1(hal_lower_irql, irql); + return (ENXIO); + sendfunc = sc->ndis_chars->nmc_sendmulti_func; + senddonefunc = sc->ndis_block->nmb_senddone_func; + + if (NDIS_SERIALIZED(sc->ndis_block)) + KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); + + MSCALL3(sendfunc, adapter, packets, cnt); for (i = 0; i < cnt; i++) { p = packets[i]; @@ -1125,9 +911,12 @@ ndis_send_packets(void *arg, ndis_packet **packets, int cnt) */ if (p == NULL || p->np_oob.npo_status == NDIS_STATUS_PENDING) continue; - senddonefunc(&sc->ndis_block, p, p->np_oob.npo_status); + MSCALL3(senddonefunc, sc->ndis_block, p, p->np_oob.npo_status); } + if (NDIS_SERIALIZED(sc->ndis_block)) + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + return(0); } @@ -1138,26 +927,33 @@ ndis_send_packet(void *arg, ndis_packet *packet) ndis_handle adapter; ndis_status status; ndis_sendsingle_handler sendfunc; - ndis_senddone_func senddonefunc; - uint8_t irql; + ndis_senddone_func senddonefunc; + uint8_t irql = 0; sc = arg; - adapter = sc->ndis_block.nmb_miniportadapterctx; + adapter = sc->ndis_block->nmb_miniportadapterctx; if (adapter == NULL) - return(ENXIO); - sendfunc = sc->ndis_chars.nmc_sendsingle_func; - senddonefunc = sc->ndis_block.nmb_senddone_func; - - irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL); - status = sendfunc(adapter, packet, packet->np_private.npp_flags); - FASTCALL1(hal_lower_irql, irql); + return (ENXIO); + sendfunc = sc->ndis_chars->nmc_sendsingle_func; + senddonefunc = sc->ndis_block->nmb_senddone_func; + + if (NDIS_SERIALIZED(sc->ndis_block)) + KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); + status = MSCALL3(sendfunc, adapter, packet, + packet->np_private.npp_flags); + + if (status == NDIS_STATUS_PENDING) { + if (NDIS_SERIALIZED(sc->ndis_block)) + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + return (0); + } - if (status == NDIS_STATUS_PENDING) - return(0); + MSCALL3(senddonefunc, sc->ndis_block, packet, status); - senddonefunc(&sc->ndis_block, packet, status); + if (NDIS_SERIALIZED(sc->ndis_block)) + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); - return(0); + return (0); } int @@ -1176,11 +972,11 @@ ndis_init_dma(void *arg) &sc->ndis_tmaps[i]); if (error) { kfree(sc->ndis_tmaps, M_DEVBUF); - return(ENODEV); + return (ENODEV); } } - return(0); + return (0); } int @@ -1203,11 +999,12 @@ ndis_destroy_dma(void *arg) } bus_dmamap_destroy(sc->ndis_ttag, sc->ndis_tmaps[i]); } - if (sc->ndis_tmaps) - kfree(sc->ndis_tmaps, M_DEVBUF); + + kfree(sc->ndis_tmaps, M_DEVBUF); + bus_dma_tag_destroy(sc->ndis_ttag); - return(0); + return (0); } int @@ -1218,23 +1015,37 @@ ndis_reset_nic(void *arg) ndis_reset_handler resetfunc; uint8_t addressing_reset; int rval; - uint8_t irql; + uint8_t irql = 0; sc = arg; - adapter = sc->ndis_block.nmb_miniportadapterctx; - resetfunc = sc->ndis_chars.nmc_reset_func; - if (adapter == NULL || resetfunc == NULL) - return(EIO); - irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL); - rval = resetfunc(&addressing_reset, adapter); - FASTCALL1(hal_lower_irql, irql); + NDIS_LOCK(sc); + adapter = sc->ndis_block->nmb_miniportadapterctx; + resetfunc = sc->ndis_chars->nmc_reset_func; - if (rval == NDIS_STATUS_PENDING) { - tsleep(sc, 0, "ndisrst", 0); + if (adapter == NULL || resetfunc == NULL || + sc->ndis_block->nmb_devicectx == NULL) { + NDIS_UNLOCK(sc); + return(EIO); } - return(0); + NDIS_UNLOCK(sc); + + KeResetEvent(&sc->ndis_block->nmb_resetevent); + + if (NDIS_SERIALIZED(sc->ndis_block)) + KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); + + rval = MSCALL2(resetfunc, &addressing_reset, adapter); + + if (NDIS_SERIALIZED(sc->ndis_block)) + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + + if (rval == NDIS_STATUS_PENDING) + KeWaitForSingleObject(&sc->ndis_block->nmb_resetevent, + 0, 0, FALSE, NULL); + + return (0); } int @@ -1243,27 +1054,54 @@ ndis_halt_nic(void *arg) struct ndis_softc *sc; ndis_handle adapter; ndis_halt_handler haltfunc; + ndis_miniport_block *block; + int empty = 0; + uint8_t irql; sc = arg; + block = sc->ndis_block; - adapter = sc->ndis_block.nmb_miniportadapterctx; + if (!cold) + KeFlushQueuedDpcs(); + + /* + * Wait for all packets to be returned. + */ + + while (1) { + KeAcquireSpinLock(&block->nmb_returnlock, &irql); + empty = IsListEmpty(&block->nmb_returnlist); + KeReleaseSpinLock(&block->nmb_returnlock, irql); + if (empty) + break; + NdisMSleep(1000); + } + + NDIS_LOCK(sc); + adapter = sc->ndis_block->nmb_miniportadapterctx; if (adapter == NULL) { - return(EIO); + NDIS_UNLOCK(sc); + return (EIO); } + sc->ndis_block->nmb_devicectx = NULL; + /* * The adapter context is only valid after the init * handler has been called, and is invalid once the * halt handler has been called. */ - haltfunc = sc->ndis_chars.nmc_halt_func; + haltfunc = sc->ndis_chars->nmc_halt_func; + NDIS_UNLOCK(sc); - haltfunc(adapter); + MSCALL1(haltfunc, adapter); - sc->ndis_block.nmb_miniportadapterctx = NULL; + NDIS_LOCK(sc); + sc->ndis_block->nmb_miniportadapterctx = NULL; + NDIS_UNLOCK(sc); - return(0); + return (0); } int @@ -1274,22 +1112,48 @@ ndis_shutdown_nic(void *arg) ndis_shutdown_handler shutdownfunc; sc = arg; - adapter = sc->ndis_block.nmb_miniportadapterctx; - shutdownfunc = sc->ndis_chars.nmc_shutdown_handler; + NDIS_LOCK(sc); + adapter = sc->ndis_block->nmb_miniportadapterctx; + shutdownfunc = sc->ndis_chars->nmc_shutdown_handler; + NDIS_UNLOCK(sc); if (adapter == NULL || shutdownfunc == NULL) - return(EIO); + return (EIO); - if (sc->ndis_chars.nmc_rsvd0 == NULL) - shutdownfunc(adapter); + if (sc->ndis_chars->nmc_rsvd0 == NULL) + MSCALL1(shutdownfunc, adapter); else - shutdownfunc(sc->ndis_chars.nmc_rsvd0); + MSCALL1(shutdownfunc, sc->ndis_chars->nmc_rsvd0); - ndis_shrink_thrqueue(8); - TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link); + TAILQ_REMOVE(&ndis_devhead, sc->ndis_block, link); return(0); } +int +ndis_pnpevent_nic(void *arg, int type) +{ + device_t dev; + struct ndis_softc *sc; + ndis_handle adapter; + ndis_pnpevent_handler pnpeventfunc; + + dev = arg; + sc = device_get_softc(arg); + NDIS_LOCK(sc); + adapter = sc->ndis_block->nmb_miniportadapterctx; + pnpeventfunc = sc->ndis_chars->nmc_pnpevent_handler; + NDIS_UNLOCK(sc); + if (adapter == NULL || pnpeventfunc == NULL) + return (EIO); + + if (sc->ndis_chars->nmc_rsvd0 == NULL) + MSCALL4(pnpeventfunc, adapter, type, NULL, 0); + else + MSCALL4(pnpeventfunc, sc->ndis_chars->nmc_rsvd0, type, NULL, 0); + + return (0); +} + int ndis_init_nic(void *arg) { @@ -1301,18 +1165,20 @@ ndis_init_nic(void *arg) uint32_t chosenmedium, i; if (arg == NULL) - return(EINVAL); + return (EINVAL); sc = arg; - block = &sc->ndis_block; - initfunc = sc->ndis_chars.nmc_init_func; + NDIS_LOCK(sc); + block = sc->ndis_block; + initfunc = sc->ndis_chars->nmc_init_func; + NDIS_UNLOCK(sc); - TAILQ_INIT(&block->nmb_timerlist); + sc->ndis_block->nmb_timerlist = NULL; for (i = 0; i < NdisMediumMax; i++) mediumarray[i] = i; - status = initfunc(&openstatus, &chosenmedium, + status = MSCALL6(initfunc, &openstatus, &chosenmedium, mediumarray, NdisMediumMax, block, block); /* @@ -1321,90 +1187,48 @@ ndis_init_nic(void *arg) * If the init failed, none of these will work. */ if (status != NDIS_STATUS_SUCCESS) { - sc->ndis_block.nmb_miniportadapterctx = NULL; - return(ENXIO); + NDIS_LOCK(sc); + sc->ndis_block->nmb_miniportadapterctx = NULL; + NDIS_UNLOCK(sc); + return (ENXIO); } - return(0); -} - -void -ndis_enable_intr(void *arg) -{ - struct ndis_softc *sc; - ndis_handle adapter; - ndis_enable_interrupts_handler intrenbfunc; - - sc = arg; - adapter = sc->ndis_block.nmb_miniportadapterctx; - intrenbfunc = sc->ndis_chars.nmc_enable_interrupts_func; - if (adapter == NULL || intrenbfunc == NULL) - return; - intrenbfunc(adapter); - - return; -} - -void -ndis_disable_intr(void *arg) -{ - struct ndis_softc *sc; - ndis_handle adapter; - ndis_disable_interrupts_handler intrdisfunc; - - sc = arg; - adapter = sc->ndis_block.nmb_miniportadapterctx; - intrdisfunc = sc->ndis_chars.nmc_disable_interrupts_func; - if (adapter == NULL || intrdisfunc == NULL) - return; - intrdisfunc(adapter); - - return; -} - -int -ndis_isr(void *arg, int *ourintr, int *callhandler) -{ - struct ndis_softc *sc; - ndis_handle adapter; - ndis_isr_handler isrfunc; - uint8_t accepted, queue; - - if (arg == NULL || ourintr == NULL || callhandler == NULL) - return(EINVAL); + /* + * This may look really goofy, but apparently it is possible + * to halt a miniport too soon after it's been initialized. + * After MiniportInitialize() finishes, pause for 1 second + * to give the chip a chance to handle any short-lived timers + * that were set in motion. If we call MiniportHalt() too soon, + * some of the timers may not be cancelled, because the driver + * expects them to fire before the halt is called. + */ - sc = arg; - adapter = sc->ndis_block.nmb_miniportadapterctx; - isrfunc = sc->ndis_chars.nmc_isr_func; - if (adapter == NULL || isrfunc == NULL) - return(ENXIO); + tsleep(arg, 0, "ndwait", hz); - isrfunc(&accepted, &queue, adapter); - *ourintr = accepted; - *callhandler = queue; + NDIS_LOCK(sc); + sc->ndis_block->nmb_devicectx = sc; + NDIS_UNLOCK(sc); - return(0); + return (0); } -int -ndis_intrhand(void *arg) +static void +ndis_intrsetup(kdpc *dpc, device_object *dobj, irp *ip, struct ndis_softc *sc) { - struct ndis_softc *sc; - ndis_handle adapter; - ndis_interrupt_handler intrfunc; + ndis_miniport_interrupt *intr; - if (arg == NULL) - return(EINVAL); + intr = sc->ndis_block->nmb_interrupt; - sc = arg; - adapter = sc->ndis_block.nmb_miniportadapterctx; - intrfunc = sc->ndis_chars.nmc_interrupt_func; - if (adapter == NULL || intrfunc == NULL) - return(EINVAL); + /* Sanity check. */ - intrfunc(adapter); + if (intr == NULL) + return; - return(0); + KeAcquireSpinLockAtDpcLevel(&intr->ni_dpccountlock); + KeResetEvent(&intr->ni_dpcevt); + if (KeInsertQueueDpc(&intr->ni_dpc, NULL, NULL) == TRUE) + intr->ni_dpccnt++; + KeReleaseSpinLockFromDpcLevel(&intr->ni_dpccountlock); } int @@ -1415,27 +1239,46 @@ ndis_get_info(void *arg, ndis_oid oid, void *buf, int *buflen) ndis_handle adapter; ndis_queryinfo_handler queryfunc; uint32_t byteswritten = 0, bytesneeded = 0; - int error; uint8_t irql; + uint64_t duetime; sc = arg; - queryfunc = sc->ndis_chars.nmc_queryinfo_func; - adapter = sc->ndis_block.nmb_miniportadapterctx; - if (adapter == NULL || queryfunc == NULL) - return(ENXIO); + KeResetEvent(&sc->ndis_block->nmb_getevent); + + KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); + + if (sc->ndis_block->nmb_pendingreq != NULL) { + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + panic("ndis_get_info() called while other request pending"); + } else + sc->ndis_block->nmb_pendingreq = (ndis_request *)sc; + + queryfunc = sc->ndis_chars->nmc_queryinfo_func; + adapter = sc->ndis_block->nmb_miniportadapterctx; + + if (adapter == NULL || queryfunc == NULL || + sc->ndis_block->nmb_devicectx == NULL) { + sc->ndis_block->nmb_pendingreq = NULL; + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + return (ENXIO); + } - irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL); - rval = queryfunc(adapter, oid, buf, *buflen, + rval = MSCALL6(queryfunc, adapter, oid, buf, *buflen, &byteswritten, &bytesneeded); - FASTCALL1(hal_lower_irql, irql); + + sc->ndis_block->nmb_pendingreq = NULL; + + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); /* Wait for requests that block. */ if (rval == NDIS_STATUS_PENDING) { - error = tsleep(&sc->ndis_block.nmb_wkupdpctimer, - 0, "ndisget", 5 * hz); - rval = sc->ndis_block.nmb_getstat; + /* Wait up to 5 seconds. */ + duetime = (5 * 1000000) * -10; + KeWaitForSingleObject(&sc->ndis_block->nmb_getevent, + 0, 0, FALSE, &duetime); + rval = sc->ndis_block->nmb_getstat; } if (byteswritten) @@ -1445,138 +1288,130 @@ ndis_get_info(void *arg, ndis_oid oid, void *buf, int *buflen) if (rval == NDIS_STATUS_INVALID_LENGTH || rval == NDIS_STATUS_BUFFER_TOO_SHORT) - return(ENOSPC); + return (ENOSPC); if (rval == NDIS_STATUS_INVALID_OID) - return(EINVAL); + return (EINVAL); if (rval == NDIS_STATUS_NOT_SUPPORTED || rval == NDIS_STATUS_NOT_ACCEPTED) - return(ENOTSUP); + return (ENOTSUP); if (rval != NDIS_STATUS_SUCCESS) - return(ENODEV); + return (ENODEV); - return(0); + return (0); } -int -ndis_unload_driver(void *arg) +uint32_t +NdisAddDevice(driver_object *drv, device_object *pdo) { + device_object *fdo; + ndis_miniport_block *block; struct ndis_softc *sc; + uint32_t status; + int error; - sc = arg; + sc = device_get_softc(pdo->do_devext); - kfree(sc->ndis_block.nmb_rlist, M_DEVBUF); + if (sc->ndis_iftype == PCMCIABus || sc->ndis_iftype == PCIBus) { + error = bus_setup_intr(sc->ndis_dev, sc->ndis_irq, + INTR_MPSAFE, + ntoskrnl_intr, sc, &sc->ndis_intrhand, NULL); + if (error) + return (NDIS_STATUS_FAILURE); + } - ndis_flush_sysctls(sc); + status = IoCreateDevice(drv, sizeof(ndis_miniport_block), NULL, + FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo); - ndis_shrink_thrqueue(8); - TAILQ_REMOVE(&ndis_devhead, &sc->ndis_block, link); + if (status != STATUS_SUCCESS) + return (status); - return(0); -} + block = fdo->do_devext; -#define NDIS_LOADED htonl(0x42534F44) + block->nmb_filterdbs.nf_ethdb = block; + block->nmb_deviceobj = fdo; + block->nmb_physdeviceobj = pdo; + block->nmb_nextdeviceobj = IoAttachDeviceToDeviceStack(fdo, pdo); + KeInitializeSpinLock(&block->nmb_lock); + KeInitializeSpinLock(&block->nmb_returnlock); + KeInitializeEvent(&block->nmb_getevent, EVENT_TYPE_NOTIFY, TRUE); + KeInitializeEvent(&block->nmb_setevent, EVENT_TYPE_NOTIFY, TRUE); + KeInitializeEvent(&block->nmb_resetevent, EVENT_TYPE_NOTIFY, TRUE); + InitializeListHead(&block->nmb_parmlist); + InitializeListHead(&block->nmb_returnlist); + block->nmb_returnitem = IoAllocateWorkItem(fdo); -int -ndis_load_driver(vm_offset_t img, void *arg) -{ - driver_entry entry; - image_optional_header opt_hdr; - image_import_descriptor imp_desc; - ndis_unicode_string dummystr; - ndis_miniport_block *block; - ndis_status status; - int idx; - uint32_t *ptr; - struct ndis_softc *sc; - - sc = arg; + /* + * Stash pointers to the miniport block and miniport + * characteristics info in the if_ndis softc so the + * UNIX wrapper driver can get to them later. + */ + sc->ndis_block = block; + sc->ndis_chars = IoGetDriverObjectExtension(drv, (void *)1); /* - * Only perform the relocation/linking phase once - * since the binary image may be shared among multiple - * device instances. + * If the driver has a MiniportTransferData() function, + * we should allocate a private RX packet pool. */ - ptr = (uint32_t *)(img + 8); - if (*ptr != NDIS_LOADED) { - /* 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); + if (sc->ndis_chars->nmc_transferdata_func != NULL) { + NdisAllocatePacketPool(&status, &block->nmb_rxpool, + 32, PROTOCOL_RESERVED_SIZE_IN_PACKET); + if (status != NDIS_STATUS_SUCCESS) { + IoDetachDevice(block->nmb_nextdeviceobj); + IoDeleteDevice(fdo); + return (status); } - *ptr = NDIS_LOADED; + InitializeListHead((&block->nmb_packetlist)); } - /* Locate the driver entry point */ - pe_get_optional_header(img, &opt_hdr); - entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr); + /* Give interrupt handling priority over timers. */ + IoInitializeDpcRequest(fdo, kernndis_functbl[6].ipt_wrap); + KeSetImportanceDpc(&fdo->do_dpc, KDPC_IMPORTANCE_HIGH); - dummystr.nus_len = strlen(NDIS_DUMMY_PATH) * 2; - dummystr.nus_maxlen = strlen(NDIS_DUMMY_PATH) * 2; - dummystr.nus_buf = NULL; - ndis_ascii_to_unicode(NDIS_DUMMY_PATH, &dummystr.nus_buf); + /* Finish up BSD-specific setup. */ - /* - * Now that we have the miniport driver characteristics, - * create an NDIS block and call the init handler. - * This will cause the driver to try to probe for - * a device. - */ + block->nmb_signature = (void *)0xcafebabe; + block->nmb_status_func = kernndis_functbl[0].ipt_wrap; + block->nmb_statusdone_func = kernndis_functbl[1].ipt_wrap; + block->nmb_setdone_func = kernndis_functbl[2].ipt_wrap; + block->nmb_querydone_func = kernndis_functbl[3].ipt_wrap; + block->nmb_resetdone_func = kernndis_functbl[4].ipt_wrap; + block->nmb_sendrsrc_func = kernndis_functbl[5].ipt_wrap; + block->nmb_pendingreq = NULL; - block = &sc->ndis_block; + TAILQ_INSERT_TAIL(&ndis_devhead, block, link); - ptr = (uint32_t *)block; - for (idx = 0; idx < sizeof(ndis_miniport_block) / 4; idx++) { - *ptr = idx | 0xdead0000; - ptr++; - } + return (STATUS_SUCCESS); +} - block->nmb_signature = (void *)0xcafebabe; - block->nmb_setdone_func = ndis_setdone_func; - block->nmb_querydone_func = ndis_getdone_func; - block->nmb_status_func = ndis_status_func; - block->nmb_statusdone_func = ndis_statusdone_func; - block->nmb_resetdone_func = ndis_resetdone_func; - block->nmb_sendrsrc_func = ndis_sendrsrcavail_func; - - block->nmb_ifp = &sc->arpcom.ac_if; - block->nmb_dev = sc->ndis_dev; - block->nmb_img = img; - block->nmb_devobj.do_rsvd = block; +int +ndis_unload_driver(void *arg) +{ + struct ndis_softc *sc; + device_object *fdo; - /* - * Now call the DriverEntry() routine. This will cause - * a callout to the NdisInitializeWrapper() and - * NdisMRegisterMiniport() routines. - */ - status = entry(&block->nmb_devobj, &dummystr); + sc = arg; - kfree (dummystr.nus_buf, M_DEVBUF); + if (sc->ndis_intrhand) + bus_teardown_intr(sc->ndis_dev, + sc->ndis_irq, sc->ndis_intrhand); - if (status != NDIS_STATUS_SUCCESS) - return(ENODEV); + if (sc->ndis_block->nmb_rlist != NULL) + kfree(sc->ndis_block->nmb_rlist, M_DEVBUF); - ndis_enlarge_thrqueue(8); + ndis_flush_sysctls(sc); - TAILQ_INSERT_TAIL(&ndis_devhead, block, link); + TAILQ_REMOVE(&ndis_devhead, sc->ndis_block, link); - return(0); -} + if (sc->ndis_chars->nmc_transferdata_func != NULL) + NdisFreePacketPool(sc->ndis_block->nmb_rxpool); + fdo = sc->ndis_block->nmb_deviceobj; + IoFreeWorkItem(sc->ndis_block->nmb_returnitem); + IoDetachDevice(sc->ndis_block->nmb_nextdeviceobj); + IoDeleteDevice(fdo); + return (0); +} diff --git a/sys/emulation/ndis/kern_windrv.c b/sys/emulation/ndis/kern_windrv.c new file mode 100644 index 0000000000..ba2e5e57b5 --- /dev/null +++ b/sys/emulation/ndis/kern_windrv.c @@ -0,0 +1,937 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/compat/ndis/kern_windrv.c,v 1.21 2010/11/22 20:46:38 bschmidt Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __i386__ +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static struct lock drvdb_lock; +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; + +#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__ */ + +#define DUMMY_REGISTRY_PATH "\\\\some\\bogus\\path" + +int +windrv_libinit(void) +{ + STAILQ_INIT(&drvdb_head); + lockinit(&drvdb_lock, "Windows driver DB lock", 0, LK_CANRECURSE); + + /* + * 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"); + +#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) * ncpus, 0); + if (my_tids == NULL) + panic("failed to allocate thread info blocks"); + lwkt_cpusync_simple(-1, x86_newldt, NULL); +#endif + return (0); +} + +int +windrv_libfini(void) +{ + struct drvdb_ent *d; + + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + while(STAILQ_FIRST(&drvdb_head) != NULL) { + d = STAILQ_FIRST(&drvdb_head); + STAILQ_REMOVE_HEAD(&drvdb_head, link); + kfree(d, M_DEVBUF); + } + lockmgr(&drvdb_lock, LK_RELEASE); + + RtlFreeUnicodeString(&fake_pci_driver.dro_drivername); + RtlFreeUnicodeString(&fake_pccard_driver.dro_drivername); + + lockuninit(&drvdb_lock); + +#ifdef __i386__ + lwkt_cpusync_simple(-1, x86_oldldt, NULL); + ExFreePool(my_tids); +#endif + return (0); +} + +/* + * Given the address of a driver image, find its corresponding + * driver_object. + */ + +driver_object * +windrv_lookup(vm_offset_t img, char *name) +{ + struct drvdb_ent *d; + unicode_string us; + ansi_string as; + + bzero((char *)&us, sizeof(us)); + + /* Damn unicode. */ + + if (name != NULL) { + RtlInitAnsiString(&as, name); + if (RtlAnsiStringToUnicodeString(&us, &as, TRUE)) + return (NULL); + } + + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + STAILQ_FOREACH(d, &drvdb_head, link) { + if (d->windrv_object->dro_driverstart == (void *)img || + (bcmp((char *)d->windrv_object->dro_drivername.us_buf, + (char *)us.us_buf, us.us_len) == 0 && us.us_len)) { + lockmgr(&drvdb_lock, LK_RELEASE); + if (name != NULL) + ExFreePool(us.us_buf); + return (d->windrv_object); + } + } + lockmgr(&drvdb_lock, LK_RELEASE); + + if (name != NULL) + RtlFreeUnicodeString(&us); + + return (NULL); +} + +struct drvdb_ent * +windrv_match(matchfuncptr matchfunc, void *ctx) +{ + struct drvdb_ent *d; + int match; + + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + STAILQ_FOREACH(d, &drvdb_head, link) { + if (d->windrv_devlist == NULL) + continue; + match = matchfunc(d->windrv_bustype, d->windrv_devlist, ctx); + if (match == TRUE) { + lockmgr(&drvdb_lock, LK_RELEASE); + return (d); + } + } + lockmgr(&drvdb_lock, LK_RELEASE); + + return (NULL); +} + +/* + * Remove a driver_object from our datatabase and destroy it. Throw + * away any custom driver extension info that may have been added. + */ + +int +windrv_unload(module_t mod, vm_offset_t img, int len) +{ + struct drvdb_ent *db, *r = NULL; + driver_object *drv; + device_object *d, *pdo; + device_t dev; + list_entry *e; + + 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. + */ + + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + 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; + lockmgr(&drvdb_lock, LK_RELEASE); + device_detach(dev); + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + } + } + + STAILQ_FOREACH(db, &drvdb_head, link) { + if (db->windrv_object->dro_driverstart == (void *)img) { + r = db; + STAILQ_REMOVE(&drvdb_head, db, drvdb_ent, link); + break; + } + } + lockmgr(&drvdb_lock, LK_RELEASE); + + if (r == NULL) + return (ENOENT); + + if (drv == NULL) + return (ENOENT); + + /* + * Destroy any custom extensions that may have been added. + */ + drv = r->windrv_object; + while (!IsListEmpty(&drv->dro_driverext->dre_usrext)) { + e = RemoveHeadList(&drv->dro_driverext->dre_usrext); + ExFreePool(e); + } + + /* Free the driver extension */ + kfree(drv->dro_driverext, M_DEVBUF); + + /* Free the driver name */ + RtlFreeUnicodeString(&drv->dro_drivername); + + /* Free driver object */ + kfree(drv, M_DEVBUF); + + /* Free our DB handle */ + kfree(r, M_DEVBUF); + + return (0); +} + +#define WINDRV_LOADED htonl(0x42534F44) + +#ifdef __amd64__ +static void +patch_user_shared_data_address(vm_offset_t img, size_t len) +{ + unsigned long i, n, max_addr, *addr; + + n = len - sizeof(unsigned long); + max_addr = KI_USER_SHARED_DATA + sizeof(kuser_shared_data); + for (i = 0; i < n; i++) { + addr = (unsigned long *)(img + i); + if (*addr >= KI_USER_SHARED_DATA && *addr < max_addr) { + *addr -= KI_USER_SHARED_DATA; + *addr += (unsigned long)&kuser_shared_data; + } + } +} +#endif + +/* + * Loader routine for actual Windows driver modules, ultimately + * calls the driver's DriverEntry() routine. + */ + +int +windrv_load(module_t mod, vm_offset_t img, int len, interface_type bustype, + void *devlist, ndis_cfg *regvals) +{ + image_import_descriptor imp_desc; + image_optional_header opt_hdr; + driver_entry entry; + struct drvdb_ent *new; + struct driver_object *drv; + int status; + uint32_t *ptr; + ansi_string as; + + /* + * First step: try to relocate and dynalink the executable + * driver image. + */ + + ptr = (uint32_t *)(img + 8); + if (*ptr == WINDRV_LOADED) + goto skipreloc; + + /* 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 -- optional. */ + if (pe_get_import_descriptor(img, &imp_desc, "HAL") == 0) { + 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); + } + +#ifdef __amd64__ + patch_user_shared_data_address(img, len); +#endif + + /* Dynamically link USBD.SYS -- optional */ + if (pe_get_import_descriptor(img, &imp_desc, "USBD") == 0) { + if (pe_patch_imports(img, "USBD", usbd_functbl)) + return (ENOEXEC); + } + + *ptr = WINDRV_LOADED; + +skipreloc: + + /* Next step: find the driver entry point. */ + + pe_get_optional_header(img, &opt_hdr); + entry = (driver_entry)pe_translate_addr(img, opt_hdr.ioh_entryaddr); + + /* Next step: allocate and store a driver object. */ + + new = kmalloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO); + if (new == NULL) + return (ENOMEM); + + drv = kmalloc(sizeof(driver_object), M_DEVBUF, M_NOWAIT|M_ZERO); + if (drv == NULL) { + kfree (new, M_DEVBUF); + return (ENOMEM); + } + + /* Allocate a driver extension structure too. */ + + drv->dro_driverext = kmalloc(sizeof(driver_extension), + M_DEVBUF, M_NOWAIT|M_ZERO); + + if (drv->dro_driverext == NULL) { + kfree(new, M_DEVBUF); + kfree(drv, M_DEVBUF); + return (ENOMEM); + } + + InitializeListHead((&drv->dro_driverext->dre_usrext)); + + drv->dro_driverstart = (void *)img; + drv->dro_driversize = len; + + RtlInitAnsiString(&as, DUMMY_REGISTRY_PATH); + if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) { + kfree(new, M_DEVBUF); + kfree(drv, M_DEVBUF); + return (ENOMEM); + } + + new->windrv_object = drv; + new->windrv_regvals = regvals; + new->windrv_devlist = devlist; + new->windrv_bustype = bustype; + + /* Now call the DriverEntry() function. */ + + status = MSCALL2(entry, drv, &drv->dro_drivername); + + if (status != STATUS_SUCCESS) { + RtlFreeUnicodeString(&drv->dro_drivername); + kfree(drv, M_DEVBUF); + kfree(new, M_DEVBUF); + return (ENODEV); + } + + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + STAILQ_INSERT_HEAD(&drvdb_head, new, link); + lockmgr(&drvdb_lock, LK_RELEASE); + + return (0); +} + +/* + * Make a new Physical Device Object for a device that was + * detected/plugged in. For us, the PDO is just a way to + * get at the device_t. + */ + +int +windrv_create_pdo(driver_object *drv, device_t bsddev) +{ + device_object *dev; + + /* + * This is a new physical device object, which technically + * is the "top of the stack." Consequently, we don't do + * an IoAttachDeviceToDeviceStack() here. + */ + + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + IoCreateDevice(drv, 0, NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &dev); + lockmgr(&drvdb_lock, LK_RELEASE); + + /* Stash pointer to our BSD device handle. */ + + dev->do_devext = bsddev; + + return (STATUS_SUCCESS); +} + +void +windrv_destroy_pdo(driver_object *drv, device_t bsddev) +{ + device_object *pdo; + + pdo = windrv_find_pdo(drv, bsddev); + + /* Remove reference to device_t */ + + pdo->do_devext = NULL; + + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + IoDeleteDevice(pdo); + lockmgr(&drvdb_lock, LK_RELEASE); +} + +/* + * Given a device_t, find the corresponding PDO in a driver's + * device list. + */ + +device_object * +windrv_find_pdo(driver_object *drv, device_t bsddev) +{ + device_object *pdo; + + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + pdo = drv->dro_devobj; + while (pdo != NULL) { + if (pdo->do_devext == bsddev) { + lockmgr(&drvdb_lock, LK_RELEASE); + return (pdo); + } + pdo = pdo->do_nextdev; + } + lockmgr(&drvdb_lock, LK_RELEASE); + + return (NULL); +} + +/* + * Add an internally emulated driver to the database. We need this + * to set up an emulated bus driver so that it can receive IRPs. + */ + +int +windrv_bus_attach(driver_object *drv, char *name) +{ + struct drvdb_ent *new; + ansi_string as; + + new = kmalloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO); + if (new == NULL) + return (ENOMEM); + + RtlInitAnsiString(&as, name); + if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) + { + kfree(new, M_DEVBUF); + return (ENOMEM); + } + + /* + * Set up a fake image pointer to avoid false matches + * in windrv_lookup(). + */ + drv->dro_driverstart = (void *)0xFFFFFFFF; + + new->windrv_object = drv; + new->windrv_devlist = NULL; + new->windrv_regvals = NULL; + + lockmgr(&drvdb_lock, LK_EXCLUSIVE); + STAILQ_INSERT_HEAD(&drvdb_head, new, link); + lockmgr(&drvdb_lock, LK_RELEASE); + + return (0); +} + +#ifdef __x86_64__ + +extern void x86_64_wrap(void); +extern void x86_64_wrap_call(void); +extern void x86_64_wrap_end(void); + +int +windrv_wrap(funcptr func, funcptr *wrap, int argcnt, int ftype) +{ + 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 = kmalloc((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; + + return (0); +} +#endif /* __x86_64__ */ + + +#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_TO_FS(x) (((x) << 3)) + +/* + * 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_gd->gd_cpuid]; + + /* + * 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. + */ + + if (t->tid_self != t) + x86_newldt(NULL); + + x86_critical_enter(); + t->tid_oldfs = x86_getfs(); + t->tid_cpu = curthread->td_gd->gd_cpuid; +#if 0 /* XXX swildner */ + sched_pin(); +#endif + x86_setfs(SEL_TO_FS(t->tid_selector)); + x86_critical_exit(); + + /* Now entering Windows land, population: you. */ +} + +/* + * 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; + + x86_critical_enter(); + t = x86_gettid(); + x86_setfs(t->tid_oldfs); +#if 0 /* XXX swildner */ + sched_unpin(); +#endif + x86_critical_exit(); + + /* Welcome back to UNIX land, we missed you. */ + +#ifdef EXTRA_SANITY + if (t->tid_cpu != curthread->td_gd->gd_cpuid) + panic("ctxsw GOT MOVED TO OTHER CPU!"); +#endif +} + +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(funcptr func, funcptr *wrap, int 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 = kmalloc((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(funcptr func, funcptr *wrap, int 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 = kmalloc((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(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 = kmalloc((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(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(void *dummy) +{ + struct x86desc *gdt; + struct gdt gtable; + uint16_t ltable; + + mtx_spinlock(&dt_lock); + + /* Grab location of existing GDT. */ + + x86_getldt(>able, <able); + + /* Find the slot we updated. */ + + gdt = gtable.base; + gdt += GNDIS_SEL; + + /* Empty it out. */ + + bzero((char *)gdt, sizeof(struct x86desc)); + + /* Restore GDT. */ + + x86_setldt(>able, ltable); + + mtx_spinunlock(&dt_lock); +} + +static void +x86_newldt(void *dummy) +{ + struct gdt gtable; + uint16_t ltable; + struct x86desc *l; + struct thread *t; + + t = curthread; + + mtx_spinlock(&dt_lock); + + /* Grab location of existing GDT. */ + + x86_getldt(>able, <able); + + /* Get pointer to the GDT table. */ + + l = gtable.base; + + /* Get pointer to empty slot */ + + l += GNDIS_SEL; + + /* Initialize TID for this CPU. */ + + my_tids[t->td_gd->gd_cpuid].tid_selector = GNDIS_SEL; + my_tids[t->td_gd->gd_cpuid].tid_self = &my_tids[t->td_gd->gd_cpuid]; + + /* 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_gd->gd_cpuid]) & 0xFFFF; + l->x_base1 = ((vm_offset_t)(&my_tids[t->td_gd->gd_cpuid]) >> 16) & 0xFF; + l->x_base2 = ((vm_offset_t)(&my_tids[t->td_gd->gd_cpuid]) >> 24) & 0xFF; + l->x_flags = SEGFLAGLO_PRESENT|SEGFLAGLO_CD|SEGFLAGLO_WRITEABLE; + + /* Update the GDT. */ + + x86_setldt(>able, ltable); + + mtx_spinunlock(&dt_lock); + + /* Whew. */ +} + +#endif /* __i386__ */ + +int +windrv_unwrap(funcptr func) +{ + kfree(func, M_DEVBUF); + + return (0); +} diff --git a/sys/emulation/ndis/ndis_var.h b/sys/emulation/ndis/ndis_var.h index 496fe19c67..c708df80cb 100644 --- a/sys/emulation/ndis/ndis_var.h +++ b/sys/emulation/ndis/ndis_var.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,8 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/compat/ndis/ndis_var.h,v 1.27 2004/07/11 00:19:30 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/ndis_var.h,v 1.3 2005/12/31 23:35:40 dillon Exp $ + * $FreeBSD: src/sys/compat/ndis/ndis_var.h,v 1.51 2010/12/06 20:54:53 bschmidt Exp $ */ #ifndef _NDIS_VAR_H_ @@ -269,6 +268,31 @@ typedef uint8_t ndis_kirql; #define OID_PNP_WAKE_UP_PATTERN_LIST 0xFD010105 #define OID_PNP_ENABLE_WAKE_UP 0xFD010106 +/* + * These are the possible power states for + * OID_PNP_SET_POWER and OID_PNP_QUERY_POWER. + */ +#define NDIS_POWERSTATE_UNSPEC 0 +#define NDIS_POWERSTATE_D0 1 +#define NDIS_POWERSTATE_D1 2 +#define NDIS_POWERSTATE_D2 3 +#define NDIS_POWERSTATE_D3 4 + +/* + * These are used with the MiniportPnpEventNotify() method. + */ + +#define NDIS_POWERPROFILE_BATTERY 0 +#define NDIS_POWERPROFILE_ACONLINE 1 + +#define NDIS_PNP_EVENT_QUERY_REMOVED 0 +#define NDIS_PNP_EVENT_REMOVED 1 +#define NDIS_PNP_EVENT_SURPRISE_REMOVED 2 +#define NDIS_PNP_EVENT_QUERY_STOPPED 3 +#define NDIS_PNP_EVENT_STOPPED 4 +#define NDIS_PNP_EVENT_PROFILECHANGED 5 + + /* PnP/PM Statistics (Optional). */ #define OID_PNP_WAKE_UP_OK 0xFD020200 #define OID_PNP_WAKE_UP_ERROR 0xFD020201 @@ -311,12 +335,15 @@ typedef uint8_t ndis_kirql; #define OID_802_11_REMOVE_KEY 0x0D01011E #define OID_802_11_ASSOCIATION_INFORMATION 0x0D01011F #define OID_802_11_TEST 0x0D010120 +#define OID_802_11_CAPABILITY 0x0D010122 +#define OID_802_11_PMKID 0x0D010123 /* structures/definitions for 802.11 */ #define NDIS_80211_NETTYPE_11FH 0x00000000 #define NDIS_80211_NETTYPE_11DS 0x00000001 #define NDIS_80211_NETTYPE_11OFDM5 0x00000002 #define NDIS_80211_NETTYPE_11OFDM24 0x00000003 +#define NDIS_80211_NETTYPE_AUTO 0x00000004 struct ndis_80211_nettype_list { uint32_t ntl_items; @@ -391,6 +418,8 @@ typedef struct ndis_80211_wep ndis_80211_wep; #define NDIS_80211_AUTHMODE_WPA 0x00000003 #define NDIS_80211_AUTHMODE_WPAPSK 0x00000004 #define NDIS_80211_AUTHMODE_WPANONE 0x00000005 +#define NDIS_80211_AUTHMODE_WPA2 0x00000006 +#define NDIS_80211_AUTHMODE_WPA2PSK 0x00000007 typedef uint8_t ndis_80211_rates[8]; typedef uint8_t ndis_80211_rates_ex[16]; @@ -437,7 +466,7 @@ struct ndis_wlan_bssid_ex { uint32_t nwbx_netinfra; ndis_80211_rates_ex nwbx_supportedrates; uint32_t nwbx_ielen; - uint32_t nwbx_ies[1]; + uint8_t nwbx_ies[1]; }; typedef struct ndis_wlan_bssid_ex ndis_wlan_bssid_ex; @@ -484,6 +513,7 @@ typedef uint32_t ndis_80211_antenna; #define NDIS_80211_RELOADDEFAULT_WEP 0x00000000 #define NDIS_80211_STATUSTYPE_AUTH 0x00000000 +#define NDIS_80211_STATUSTYPE_PMKIDLIST 0x00000001 struct ndis_80211_status_indication { uint32_t nsi_type; @@ -491,6 +521,11 @@ struct ndis_80211_status_indication { typedef struct ndis_80211_status_indication ndis_80211_status_indication; +#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E + struct ndis_80211_auth_request { uint32_t nar_len; ndis_80211_macaddr nar_bssid; @@ -504,8 +539,9 @@ struct ndis_80211_key { uint32_t nk_keyidx; uint32_t nk_keylen; ndis_80211_macaddr nk_bssid; + uint8_t nk_pad[6]; uint64_t nk_keyrsc; - uint8_t nk_keydata[256]; + uint8_t nk_keydata[32]; }; typedef struct ndis_80211_key ndis_80211_key; @@ -574,6 +610,61 @@ struct ndis_80211_test { typedef struct ndis_80211_test ndis_80211_test; +struct ndis_80211_auth_encrypt { + uint32_t ne_authmode; + uint32_t ne_cryptstat; +}; + +typedef struct ndis_80211_auth_encrypt ndis_80211_auth_encrypt; + +struct ndis_80211_caps { + uint32_t nc_len; + uint32_t nc_ver; + uint32_t nc_numpmkids; + ndis_80211_auth_encrypt nc_authencs[1]; +}; + +typedef struct ndis_80211_caps ndis_80211_caps; + +struct ndis_80211_bssidinfo { + ndis_80211_macaddr nb_bssid; + uint8_t nb_pmkid[16]; +}; + +typedef struct ndis_80211_bssidinfo ndis_80211_bssidinfo; + +struct ndis_80211_pmkid { + uint32_t np_len; + uint32_t np_bssidcnt; + ndis_80211_bssidinfo np_bssidinfo[1]; +}; + +typedef struct ndis_80211_pmkid ndis_80211_pmkid; + +struct ndis_80211_pmkid_cand { + ndis_80211_macaddr npc_bssid; + uint32_t npc_flags; +}; + +typedef struct ndis_80211_pmkid_cand ndis_80211_pmkid_cand; + +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED (0x01) + +struct ndis_80211_pmkid_candidate_list { + uint32_t npcl_version; + uint32_t npcl_numcandidates; + ndis_80211_pmkid_cand npcl_candidatelist[1]; +}; + +typedef struct ndis_80211_pmkid_candidate_list ndis_80211_pmkid_candidate_list; + +struct ndis_80211_enc_indication { + uint32_t nei_statustype; + ndis_80211_pmkid_candidate_list nei_pmkidlist; +}; + +typedef struct ndis_80211_enc_indication ndis_80211_enc_indication; + /* TCP OIDs. */ #define OID_TCP_TASK_OFFLOAD 0xFC010201 @@ -683,6 +774,7 @@ typedef struct ndis_task_ipsec ndis_task_ipsec; * all attributes. */ +#define NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT 0x00000001 #define NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT 0x00000002 #define NDIS_ATTRIBUTE_IGNORE_TOKEN_RING_ERRORS 0x00000004 #define NDIS_ATTRIBUTE_BUS_MASTER 0x00000008 @@ -693,6 +785,9 @@ typedef struct ndis_task_ipsec ndis_task_ipsec; #define NDIS_ATTRIBUTE_NOT_CO_NDIS 0x00000100 #define NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS 0x00000200 +#define NDIS_SERIALIZED(block) \ + (((block)->nmb_flags & NDIS_ATTRIBUTE_DESERIALIZE) == 0) + enum ndis_media_state { nmc_connected, nmc_disconnected @@ -734,15 +829,24 @@ typedef enum ndis_media_state ndis_media_state; #define NDIS_DMA_32BITS 0x01 #define NDIS_DMA_64BITS 0x02 +/* struct ndis_physaddr { +#ifdef __i386__ uint64_t np_quad; +#endif +#ifdef __x86_64__ + uint32_t np_low; + uint32_t np_high; +#define np_quad np_low +#endif #ifdef notdef uint32_t np_low; uint32_t np_high; #endif }; +*/ -typedef struct ndis_physaddr ndis_physaddr; +typedef struct physaddr ndis_physaddr; struct ndis_ansi_string { uint16_t nas_len; @@ -752,6 +856,7 @@ struct ndis_ansi_string { typedef struct ndis_ansi_string ndis_ansi_string; +#ifdef notdef /* * nus_buf is really a wchar_t *, but it's inconvenient to include * all the necessary header goop needed to define it, and it's a @@ -762,9 +867,10 @@ struct ndis_unicode_string { uint16_t nus_maxlen; uint16_t *nus_buf; }; - typedef struct ndis_unicode_string ndis_unicode_string; +#endif +typedef unicode_string ndis_unicode_string; enum ndis_parm_type { ndis_parm_int, @@ -792,8 +898,20 @@ struct ndis_config_parm { } ncp_parmdata; }; +/* + * Not part of Windows NDIS spec; we uses this to keep a + * list of ndis_config_parm structures that we've allocated. + */ + typedef struct ndis_config_parm ndis_config_parm; +struct ndis_parmlist_entry { + list_entry np_list; + ndis_config_parm np_parm; +}; + +typedef struct ndis_parmlist_entry ndis_parmlist_entry; + #ifdef notdef struct ndis_list_entry { struct ndis_list_entry *nle_flink; @@ -885,6 +1003,26 @@ struct ndis_spin_lock { typedef struct ndis_spin_lock ndis_spin_lock; +struct ndis_rw_lock { + union { + kspin_lock nrl_spinlock; + void *nrl_ctx; + } u; + uint8_t nrl_rsvd[16]; +}; + +#define nrl_spinlock u.nrl_spinlock +#define nrl_ctx u.nrl_ctx; + +typedef struct ndis_rw_lock ndis_rw_lock; + +struct ndis_lock_state { + uint16_t nls_lockstate; + ndis_kirql nls_oldirql; +}; + +typedef struct ndis_lock_state ndis_lock_state; + struct ndis_request { uint8_t nr_macreserved[4*sizeof(void *)]; uint32_t nr_requesttype; @@ -919,16 +1057,16 @@ typedef struct ndis_request ndis_request; * Filler, not used. */ struct ndis_miniport_interrupt { - void *ni_introbj; + kinterrupt *ni_introbj; ndis_kspin_lock ni_dpccountlock; void *ni_rsvd; void *ni_isrfunc; void *ni_dpcfunc; - struct ndis_kdpc ni_dpc; + kdpc ni_dpc; ndis_miniport_block *ni_block; uint8_t ni_dpccnt; uint8_t ni_filler1; - struct ndis_kevent ni_dpcsdoneevent; + struct nt_kevent ni_dpcevt; uint8_t ni_shared; uint8_t ni_isrreq; }; @@ -942,9 +1080,11 @@ enum ndis_interrupt_mode { typedef enum ndis_interrupt_mode ndis_interrupt_mode; +#define NUMBER_OF_SINGLE_WORK_ITEMS 6 + struct ndis_work_item; -typedef __stdcall void (*ndis_proc)(struct ndis_work_item *, void *); +typedef void (*ndis_proc)(struct ndis_work_item *, void *); struct ndis_work_item { void *nwi_ctx; @@ -954,6 +1094,13 @@ struct ndis_work_item { typedef struct ndis_work_item ndis_work_item; +#define NdisInitializeWorkItem(w, f, c) \ + do { \ + (w)->nwi_ctx = c; \ + (w)->nwi_func = f; \ + } while (0) + +#ifdef notdef struct ndis_buffer { struct ndis_buffer *nb_next; uint16_t nb_size; @@ -966,6 +1113,7 @@ struct ndis_buffer { }; typedef struct ndis_buffer ndis_buffer; +#endif struct ndis_sc_element { ndis_physaddr nse_addr; @@ -1115,6 +1263,22 @@ struct ndis_packet_oob { typedef struct ndis_packet_oob ndis_packet_oob; +/* + * Our protocol private region for handling ethernet. + * We need this to stash some of the things returned + * by NdisMEthIndicateReceive(). + */ + +struct ndis_ethpriv { + void *nep_ctx; /* packet context */ + long nep_offset; /* residual data to transfer */ + void *nep_pad[2]; +}; + +typedef struct ndis_ethpriv ndis_ethpriv; + +#define PROTOCOL_RESERVED_SIZE_IN_PACKET (4 * sizeof(void *)) + struct ndis_packet { ndis_packet_private np_private; union { @@ -1133,6 +1297,7 @@ struct ndis_packet { } np_macrsvd; } u; uint32_t *np_rsvd[2]; + uint8_t np_protocolreserved[PROTOCOL_RESERVED_SIZE_IN_PACKET]; /* * This next part is probably wrong, but we need some place @@ -1148,12 +1313,29 @@ struct ndis_packet { void *np_softc; void *np_m0; int np_txidx; + list_entry np_list; }; typedef struct ndis_packet ndis_packet; +struct ndis_packet_pool { + slist_header np_head; + int np_dead; + nt_kevent np_event; + kspin_lock np_lock; + int np_cnt; + int np_len; + int np_protrsvd; + void *np_pktmem; +}; + +typedef struct ndis_packet_pool ndis_packet_pool; + /* mbuf ext type for NDIS */ -#define EXT_NDIS 0x999 +#define EXT_NDIS EXT_NET_DRV + +/* mtx type for NDIS */ +#define MTX_NDIS_LOCK "NDIS lock" struct ndis_filterdbs { union { @@ -1167,6 +1349,8 @@ struct ndis_filterdbs { typedef struct ndis_filterdbs ndis_filterdbs; +#define nf_ethdb u.nf_ethdb + enum ndis_medium { NdisMedium802_3, NdisMedium802_5, @@ -1312,10 +1496,15 @@ struct ndis_timer_entry { TAILQ_HEAD(nte_head, ndis_timer_entry); +#define NDIS_FH_TYPE_VFS 0 +#define NDIS_FH_TYPE_MODULE 1 + struct ndis_fh { + int nf_type; + char *nf_name; void *nf_vp; void *nf_map; - uint32_t nf_maplen; + uint32_t nf_maplen; }; typedef struct ndis_fh ndis_fh; @@ -1346,7 +1535,7 @@ struct ndis_miniport_block { /* * Windows-specific portion -- DO NOT MODIFY OR NDIS * DRIVERS WILL NOT WORK. - */ + */ void *nmb_signature; /* magic number */ ndis_miniport_block *nmb_nextminiport; ndis_mdriver_block *nmb_driverhandle; @@ -1390,11 +1579,11 @@ struct ndis_miniport_block { void *nmb_resetdone_func; ndis_medium nmb_medium; uint32_t nmb_busnum; - uint32_t nmb_bustye; + uint32_t nmb_bustype; uint32_t nmb_adaptertype; - void *nmb_deviceobj; - void *nmb_physdeviceobj; - void *nmb_nextdeviceobj; + device_object *nmb_deviceobj; /* Functional device */ + device_object *nmb_physdeviceobj; /* Physical device */ + device_object *nmb_nextdeviceobj; /* Next dev in stack */ void *nmb_mapreg; void *nmb_callmgraflist; void *nmb_miniportthread; @@ -1434,45 +1623,47 @@ struct ndis_miniport_block { * End of windows-specific portion of miniport block. Everything * below is BSD-specific. */ - struct ifnet *nmb_ifp; - uint8_t nmb_dummybuf[128]; - device_object nmb_devobj; - ndis_config_parm nmb_replyparm; - int nmb_pciidx; - device_t nmb_dev; + list_entry nmb_parmlist; ndis_resource_list *nmb_rlist; ndis_status nmb_getstat; + nt_kevent nmb_getevent; ndis_status nmb_setstat; - struct nte_head nmb_timerlist; - vm_offset_t nmb_img; + nt_kevent nmb_setevent; + nt_kevent nmb_resetevent; + io_workitem *nmb_returnitem; + ndis_miniport_timer *nmb_timerlist; + ndis_handle nmb_rxpool; + list_entry nmb_returnlist; + kspin_lock nmb_returnlock; TAILQ_ENTRY(ndis_miniport_block) link; }; TAILQ_HEAD(nd_head, ndis_miniport_block); -typedef __stdcall ndis_status (*ndis_init_handler)(ndis_status *, uint32_t *, +typedef ndis_status (*ndis_init_handler)(ndis_status *, uint32_t *, ndis_medium *, uint32_t, ndis_handle, ndis_handle); -typedef __stdcall ndis_status (*ndis_queryinfo_handler)(ndis_handle, ndis_oid, +typedef ndis_status (*ndis_queryinfo_handler)(ndis_handle, ndis_oid, void *, uint32_t, uint32_t *, uint32_t *); -typedef __stdcall ndis_status (*ndis_setinfo_handler)(ndis_handle, ndis_oid, +typedef ndis_status (*ndis_setinfo_handler)(ndis_handle, ndis_oid, void *, uint32_t, uint32_t *, uint32_t *); -typedef __stdcall ndis_status (*ndis_sendsingle_handler)(ndis_handle, +typedef ndis_status (*ndis_sendsingle_handler)(ndis_handle, ndis_packet *, uint32_t); -typedef __stdcall ndis_status (*ndis_sendmulti_handler)(ndis_handle, +typedef ndis_status (*ndis_sendmulti_handler)(ndis_handle, ndis_packet **, uint32_t); -typedef __stdcall void (*ndis_isr_handler)(uint8_t *, uint8_t *, ndis_handle); -typedef __stdcall void (*ndis_interrupt_handler)(ndis_handle); -typedef __stdcall int (*ndis_reset_handler)(uint8_t *, ndis_handle); -typedef __stdcall void (*ndis_halt_handler)(ndis_handle); -typedef __stdcall void (*ndis_return_handler)(ndis_handle, ndis_packet *); -typedef __stdcall void (*ndis_enable_interrupts_handler)(ndis_handle); -typedef __stdcall void (*ndis_disable_interrupts_handler)(ndis_handle); -typedef __stdcall void (*ndis_shutdown_handler)(void *); -typedef __stdcall void (*ndis_allocdone_handler)(ndis_handle, void *, +typedef void (*ndis_isr_handler)(uint8_t *, uint8_t *, ndis_handle); +typedef void (*ndis_interrupt_handler)(ndis_handle); +typedef int (*ndis_reset_handler)(uint8_t *, ndis_handle); +typedef void (*ndis_halt_handler)(ndis_handle); +typedef void (*ndis_return_handler)(ndis_handle, ndis_packet *); +typedef void (*ndis_enable_interrupts_handler)(ndis_handle); +typedef void (*ndis_disable_interrupts_handler)(ndis_handle); +typedef void (*ndis_shutdown_handler)(void *); +typedef void (*ndis_pnpevent_handler)(void *, int, void *, uint32_t); +typedef void (*ndis_allocdone_handler)(ndis_handle, void *, ndis_physaddr *, uint32_t, void *); -typedef __stdcall uint8_t (*ndis_checkforhang_handler)(ndis_handle); +typedef uint8_t (*ndis_checkforhang_handler)(ndis_handle); -typedef __stdcall ndis_status (*driver_entry)(void *, ndis_unicode_string *); +typedef ndis_status (*driver_entry)(void *, unicode_string *); extern image_patch_table ndis_functbl[]; @@ -1482,19 +1673,63 @@ extern image_patch_table ndis_functbl[]; #define NDIS_PSTATE_RUNNING 1 #define NDIS_PSTATE_SLEEPING 2 -__BEGIN_DECLS -struct ndis_softc; +#define NdisQueryPacket(p, pbufcnt, bufcnt, firstbuf, plen) \ + do { \ + if ((firstbuf) != NULL) { \ + ndis_buffer **_first; \ + _first = firstbuf; \ + *(_first) = (p)->np_private.npp_head; \ + } \ + if ((plen) || (bufcnt) || (pbufcnt)) { \ + if ((p)->np_private.npp_validcounts == FALSE) { \ + ndis_buffer *tmp; \ + unsigned int tlen = 0, pcnt = 0; \ + unsigned int add = 0; \ + unsigned int pktlen, off; \ + \ + tmp = (p)->np_private.npp_head; \ + while (tmp != NULL) { \ + off = MmGetMdlByteOffset(tmp); \ + pktlen = MmGetMdlByteCount(tmp);\ + tlen += pktlen; \ + pcnt += \ + NDIS_BUFFER_TO_SPAN_PAGES(tmp); \ + add++; \ + tmp = tmp->mdl_next; \ + } \ + (p)->np_private.npp_count = add; \ + (p)->np_private.npp_totlen = tlen; \ + (p)->np_private.npp_physcnt = pcnt; \ + (p)->np_private.npp_validcounts = TRUE; \ + } \ + if (pbufcnt) { \ + unsigned int *_pbufcnt; \ + _pbufcnt = (pbufcnt); \ + *(_pbufcnt) = (p)->np_private.npp_physcnt; \ + } \ + if (bufcnt) { \ + unsigned int *_bufcnt; \ + _bufcnt = (bufcnt); \ + *(_bufcnt) = (p)->np_private.npp_count; \ + } \ + if (plen) { \ + unsigned int *_plen; \ + _plen = (plen); \ + *(_plen) = (p)->np_private.npp_totlen; \ + } \ + } \ + } while (0) +__BEGIN_DECLS extern int ndis_libinit(void); extern int ndis_libfini(void); -extern int ndis_ascii_to_unicode(char *, uint16_t **); -extern int ndis_unicode_to_ascii(uint16_t *, int, char **); extern int ndis_load_driver(vm_offset_t, void *); extern int ndis_unload_driver(void *); extern int ndis_mtop(struct mbuf *, ndis_packet **); extern int ndis_ptom(struct mbuf **, ndis_packet *); extern int ndis_get_info(void *, ndis_oid, void *, int *); extern int ndis_set_info(void *, ndis_oid, void *, int *); +extern void *ndis_get_routine_address(struct image_patch_table *, char *); extern int ndis_get_supported_oids(void *, ndis_oid **, int *); extern int ndis_send_packets(void *, ndis_packet **, int); extern int ndis_send_packet(void *, ndis_packet *); @@ -1503,25 +1738,30 @@ extern int ndis_alloc_amem(void *); extern void ndis_free_amem(void *); extern void ndis_free_packet(ndis_packet *); extern void ndis_free_bufs(ndis_buffer *); -extern void ndis_return_packet(struct ndis_softc *sc, ndis_packet *p); extern int ndis_reset_nic(void *); extern int ndis_halt_nic(void *); extern int ndis_shutdown_nic(void *); +extern int ndis_pnpevent_nic(void *, int); extern int ndis_init_nic(void *); -extern int ndis_isr(void *, int *, int *); -extern int ndis_intrhand(void *); -extern void ndis_enable_intr(void *); -extern void ndis_disable_intr(void *); +extern void ndis_return_packet(void *); extern int ndis_init_dma(void *); extern int ndis_destroy_dma(void *); extern int ndis_create_sysctls(void *); extern int ndis_add_sysctl(void *, char *, char *, char *, int); extern int ndis_flush_sysctls(void *); -extern int ndis_sched(void (*)(void *), void *, int); -extern int ndis_unsched(void (*)(void *), void *, int); -extern int ndis_thsuspend(struct thread *, int); -extern void ndis_thresume(struct thread *); -extern int ndis_strcasecmp(const char *, const char *); + +extern uint32_t NdisAddDevice(driver_object *, device_object *); +extern void NdisAllocatePacketPool(ndis_status *, + ndis_handle *, uint32_t, uint32_t); +extern void NdisAllocatePacketPoolEx(ndis_status *, + ndis_handle *, uint32_t, uint32_t, uint32_t); +extern uint32_t NdisPacketPoolUsage(ndis_handle); +extern void NdisFreePacketPool(ndis_handle); +extern void NdisAllocatePacket(ndis_status *, + ndis_packet **, ndis_handle); +extern void NdisFreePacket(ndis_packet *); +extern ndis_status NdisScheduleWorkItem(ndis_work_item *); +extern void NdisMSleep(uint32_t); __END_DECLS #endif /* _NDIS_VAR_H_ */ diff --git a/sys/emulation/ndis/ntoskrnl_var.h b/sys/emulation/ndis/ntoskrnl_var.h index 98cb6d989e..d2c78c3ef8 100644 --- a/sys/emulation/ndis/ntoskrnl_var.h +++ b/sys/emulation/ndis/ntoskrnl_var.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,32 +29,131 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/compat/ndis/ntoskrnl_var.h,v 1.15 2004/04/20 02:27:38 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/ntoskrnl_var.h,v 1.4 2007/11/14 18:27:52 swildner Exp $ + * $FreeBSD: src/sys/compat/ndis/ntoskrnl_var.h,v 1.51 2010/12/06 20:54:53 bschmidt Exp $ */ #ifndef _NTOSKRNL_VAR_H_ #define _NTOSKRNL_VAR_H_ +#define MTX_NTOSKRNL_SPIN_LOCK "NDIS thread lock" + +/* + * us_buf is really a wchar_t *, but it's inconvenient to include + * all the necessary header goop needed to define it, and it's a + * pointer anyway, so for now, just make it a uint16_t *. + */ +struct unicode_string { + uint16_t us_len; + uint16_t us_maxlen; + uint16_t *us_buf; +}; + +typedef struct unicode_string unicode_string; + +struct ansi_string { + uint16_t as_len; + uint16_t as_maxlen; + char *as_buf; +}; + +typedef struct ansi_string ansi_string; + +/* + * Windows memory descriptor list. In Windows, it's possible for + * buffers to be passed between user and kernel contexts without + * copying. Buffers may also be allocated in either paged or + * non-paged memory regions. An MDL describes the pages of memory + * used to contain a particular buffer. Note that a single MDL + * may describe a buffer that spans multiple pages. An array of + * page addresses appears immediately after the MDL structure itself. + * MDLs are therefore implicitly variably sized, even though they + * don't look it. + * + * Note that in FreeBSD, we can take many shortcuts in the way + * we handle MDLs because: + * + * - We are only concerned with pages in kernel context. This means + * we will only ever use the kernel's memory map, and remapping + * of buffers is never needed. + * + * - Kernel pages can never be paged out, so we don't have to worry + * about whether or not a page is actually mapped before going to + * touch it. + */ + +struct mdl { + struct mdl *mdl_next; + uint16_t mdl_size; + uint16_t mdl_flags; + void *mdl_process; + void *mdl_mappedsystemva; + void *mdl_startva; + uint32_t mdl_bytecount; + uint32_t mdl_byteoffset; +}; + +typedef struct mdl mdl, ndis_buffer; + +/* MDL flags */ + +#define MDL_MAPPED_TO_SYSTEM_VA 0x0001 +#define MDL_PAGES_LOCKED 0x0002 +#define MDL_SOURCE_IS_NONPAGED_POOL 0x0004 +#define MDL_ALLOCATED_FIXED_SIZE 0x0008 +#define MDL_PARTIAL 0x0010 +#define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020 +#define MDL_IO_PAGE_READ 0x0040 +#define MDL_WRITE_OPERATION 0x0080 +#define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100 +#define MDL_FREE_EXTRA_PTES 0x0200 +#define MDL_IO_SPACE 0x0800 +#define MDL_NETWORK_HEADER 0x1000 +#define MDL_MAPPING_CAN_FAIL 0x2000 +#define MDL_ALLOCATED_MUST_SUCCEED 0x4000 +#define MDL_ZONE_ALLOCED 0x8000 /* BSD private */ + +#define MDL_ZONE_PAGES 16 +#define MDL_ZONE_SIZE (sizeof(mdl) + (sizeof(vm_offset_t) * MDL_ZONE_PAGES)) + /* Note: assumes x86 page size of 4K. */ + +#ifndef PAGE_SHIFT +#if PAGE_SIZE == 4096 #define PAGE_SHIFT 12 +#elif PAGE_SIZE == 8192 +#define PAGE_SHIFT 13 +#else +#error PAGE_SHIFT undefined! +#endif +#endif + #define SPAN_PAGES(ptr, len) \ - ((uint32_t)((((uintptr_t)(ptr) & (PAGE_SIZE -1)) + \ + ((uint32_t)((((uintptr_t)(ptr) & (PAGE_SIZE - 1)) + \ (len) + (PAGE_SIZE - 1)) >> PAGE_SHIFT)) + #define PAGE_ALIGN(ptr) \ ((void *)((uintptr_t)(ptr) & ~(PAGE_SIZE - 1))) + #define BYTE_OFFSET(ptr) \ ((uint32_t)((uintptr_t)(ptr) & (PAGE_SIZE - 1))) -#define MDL_INIT(b, baseva, len) \ - (b)->nb_next = NULL; \ - (b)->nb_size = (uint16_t)(sizeof(struct ndis_buffer) + \ - (sizeof(uint32_t) * SPAN_PAGES((baseva), (len)))); \ - (b)->nb_flags = 0; \ - (b)->nb_startva = (void *)PAGE_ALIGN((baseva)); \ - (b)->nb_byteoffset = BYTE_OFFSET((baseva)); \ - (b)->nb_bytecount = (uint32_t)(len); -#define MDL_VA(b) \ - ((void *)((char *)((b)->nb_startva) + (b)->nb_byteoffset)) + +#define MDL_PAGES(m) (vm_offset_t *)(m + 1) + +#define MmInitializeMdl(b, baseva, len) \ + (b)->mdl_next = NULL; \ + (b)->mdl_size = (uint16_t)(sizeof(mdl) + \ + (sizeof(vm_offset_t) * SPAN_PAGES((baseva), (len)))); \ + (b)->mdl_flags = 0; \ + (b)->mdl_startva = (void *)PAGE_ALIGN((baseva)); \ + (b)->mdl_byteoffset = BYTE_OFFSET((baseva)); \ + (b)->mdl_bytecount = (uint32_t)(len); + +#define MmGetMdlByteOffset(mdl) ((mdl)->mdl_byteoffset) +#define MmGetMdlByteCount(mdl) ((mdl)->mdl_bytecount) +#define MmGetMdlVirtualAddress(mdl) \ + ((void *)((char *)((mdl)->mdl_startva) + (mdl)->mdl_byteoffset)) +#define MmGetMdlStartVa(mdl) ((mdl)->mdl_startva) +#define MmGetMdlPfnArray(mdl) MDL_PAGES(mdl) #define WDM_MAJOR 1 #define WDM_MINOR_WIN98 0x00 @@ -63,6 +162,16 @@ #define WDM_MINOR_WINXP 0x20 #define WDM_MINOR_WIN2003 0x30 +enum nt_caching_type { + MmNonCached = 0, + MmCached = 1, + MmWriteCombined = 2, + MmHardwareCoherentCached = 3, + MmNonCachedUnordered = 4, + MmUSWCCached = 5, + MmMaximumCacheType = 6 +}; + /*- * The ndis_kspin_lock type is called KSPIN_LOCK in MS-Windows. * According to the Windows DDK header files, KSPIN_LOCK is defined like this: @@ -72,7 +181,7 @@ * typedef [public] unsigned __int3264 ULONG_PTR, *PULONG_PTR; * typedef unsigned __int64 ULONG_PTR, *PULONG_PTR; * typedef _W64 unsigned long ULONG_PTR, *PULONG_PTR; - * + * * The keyword __int3264 specifies an integral type that has the following * properties: * + It is 32-bit on 32-bit platforms @@ -110,54 +219,65 @@ struct list_entry { typedef struct list_entry list_entry; -#define INIT_LIST_HEAD(l) \ - l->nle_flink = l->nle_blink = l +#define InitializeListHead(l) \ + (l)->nle_flink = (l)->nle_blink = (l) + +#define IsListEmpty(h) \ + ((h)->nle_flink == (h)) -#define REMOVE_LIST_ENTRY(e) \ +#define RemoveEntryList(e) \ do { \ list_entry *b; \ list_entry *f; \ \ - f = e->nle_flink; \ - b = e->nle_blink; \ + f = (e)->nle_flink; \ + b = (e)->nle_blink; \ b->nle_flink = f; \ f->nle_blink = b; \ } while (0) -#define REMOVE_LIST_HEAD(l) \ - do { \ - list_entry *f; \ - list_entry *e; \ - \ - e = l->nle_flink; \ - f = e->nle_flink; \ - l->nle_flink = f; \ - f->nle_blink = l; \ - } while (0) +/* These two have to be inlined since they return things. */ -#define REMOVE_LIST_TAIL(l) \ - do { \ - list_entry *b; \ - list_entry *e; \ - \ - e = l->nle_blink; \ - b = e->nle_blink; \ - l->nle_blink = b; \ - b->nle_flink = l; \ - } while (0) +static __inline__ list_entry * +RemoveHeadList(list_entry *l) +{ + list_entry *f; + list_entry *e; + + e = l->nle_flink; + f = e->nle_flink; + l->nle_flink = f; + f->nle_blink = l; + + return (e); +} + +static __inline__ list_entry * +RemoveTailList(list_entry *l) +{ + list_entry *b; + list_entry *e; -#define INSERT_LIST_TAIL(l, e) \ + e = l->nle_blink; + b = e->nle_blink; + l->nle_blink = b; + b->nle_flink = l; + + return (e); +} + +#define InsertTailList(l, e) \ do { \ list_entry *b; \ \ b = l->nle_blink; \ e->nle_flink = l; \ e->nle_blink = b; \ - b->nle_flink = e; \ - l->nle_blink = e; \ + b->nle_flink = (e); \ + l->nle_blink = (e); \ } while (0) -#define INSERT_LIST_HEAD(l, e) \ +#define InsertHeadList(l, e) \ do { \ list_entry *f; \ \ @@ -168,17 +288,32 @@ typedef struct list_entry list_entry; l->nle_flink = e; \ } while (0) +#define CONTAINING_RECORD(addr, type, field) \ + ((type *)((vm_offset_t)(addr) - (vm_offset_t)(&((type *)0)->field))) + struct nt_dispatch_header { uint8_t dh_type; uint8_t dh_abs; uint8_t dh_size; uint8_t dh_inserted; - uint32_t dh_sigstate; + int32_t dh_sigstate; list_entry dh_waitlisthead; }; typedef struct nt_dispatch_header nt_dispatch_header; +/* Dispatcher object types */ + +#define DISP_TYPE_NOTIFICATION_EVENT 0 /* KEVENT */ +#define DISP_TYPE_SYNCHRONIZATION_EVENT 1 /* KEVENT */ +#define DISP_TYPE_MUTANT 2 /* KMUTANT/KMUTEX */ +#define DISP_TYPE_PROCESS 3 /* KPROCESS */ +#define DISP_TYPE_QUEUE 4 /* KQUEUE */ +#define DISP_TYPE_SEMAPHORE 5 /* KSEMAPHORE */ +#define DISP_TYPE_THREAD 6 /* KTHREAD */ +#define DISP_TYPE_NOTIFICATION_TIMER 8 /* KTIMER */ +#define DISP_TYPE_SYNCHRONIZATION_TIMER 9 /* KTIMER */ + #define OTYPE_EVENT 0 #define OTYPE_MUTEX 1 #define OTYPE_THREAD 2 @@ -207,6 +342,12 @@ typedef struct nt_dispatch_header nt_dispatch_header; #define AT_DISPATCH_LEVEL(td) \ (lwkt_getpri(td) == TDPRI_INT_HIGH) +#define AT_DIRQL_LEVEL(td) \ + ((td)->td_priority <= PI_NET) + +#define AT_HIGH_LEVEL(td) \ + ((td)->td_critnest != 0) + struct nt_objref { nt_dispatch_header no_dh; void *no_obj; @@ -221,10 +362,15 @@ typedef struct nt_objref nt_objref; #define EVENT_TYPE_SYNC 1 /* - * For ktimers, we use a struct callout pointer in a union - * to overlay the callout handle over the k_timerlistentry. + * We need to use the timeout()/untimeout() API for ktimers + * since timers can be initialized, but not destroyed (so + * malloc()ing our own callout structures would mean a leak, + * since there'd be no way to free() them). This means we + * need to use struct callout_handle, which is really just a + * pointer. To make it easier to deal with, we use a union + * to overlay the callout_handle over the k_timerlistentry. * The latter is a list_entry, which is two pointers, so - * there's enough space available to hide a callout handle + * there's enough space available to hide a callout_handle * there. */ @@ -233,14 +379,14 @@ struct ktimer { uint64_t k_duetime; union { list_entry k_timerlistentry; - struct callout *k_handle; + struct callout *k_callout; } u; void *k_dpc; uint32_t k_period; }; #define k_timerlistentry u.k_timerlistentry -#define k_handle u.k_handle +#define k_callout u.k_callout typedef struct ktimer ktimer; @@ -253,20 +399,26 @@ typedef struct nt_kevent nt_kevent; /* Kernel defered procedure call (i.e. timer callback) */ struct kdpc; -typedef __stdcall void (*kdpc_func)(struct kdpc *, void *, void *, void *); +typedef void (*kdpc_func)(struct kdpc *, void *, void *, void *); struct kdpc { uint16_t k_type; - uint8_t k_num; - uint8_t k_importance; + uint8_t k_num; /* CPU number */ + uint8_t k_importance; /* priority */ list_entry k_dpclistentry; - kdpc_func k_deferedfunc; + void *k_deferedfunc; void *k_deferredctx; void *k_sysarg1; void *k_sysarg2; - register_t k_lock; + void *k_lock; }; +#define KDPC_IMPORTANCE_LOW 0 +#define KDPC_IMPORTANCE_MEDIUM 1 +#define KDPC_IMPORTANCE_HIGH 2 + +#define KDPC_CPU_DEFAULT 255 + typedef struct kdpc kdpc; /* @@ -282,18 +434,12 @@ typedef struct kdpc kdpc; */ struct kmutant { nt_dispatch_header km_header; - union { - list_entry km_listentry; - uint32_t km_acquirecnt; - } u; + list_entry km_listentry; void *km_ownerthread; uint8_t km_abandoned; uint8_t km_apcdisable; }; -#define km_listentry u.km_listentry -#define km_acquirecnt u.km_acquirecnt - typedef struct kmutant kmutant; #define LOOKASIDE_DEPTH 256 @@ -330,7 +476,9 @@ typedef struct general_lookaside general_lookaside; struct npaged_lookaside_list { general_lookaside nll_l; +#ifdef __i386__ kspin_lock nll_obsoletelock; +#endif }; typedef struct npaged_lookaside_list npaged_lookaside_list; @@ -376,18 +524,33 @@ struct wait_block { void *wb_kthread; nt_dispatch_header *wb_object; struct wait_block *wb_next; +#ifdef notdef uint16_t wb_waitkey; uint16_t wb_waittype; +#endif + uint8_t wb_waitkey; + uint8_t wb_waittype; + uint8_t wb_awakened; + uint8_t wb_oldpri; }; typedef struct wait_block wait_block; +#define wb_ext wb_kthread + #define THREAD_WAIT_OBJECTS 3 #define MAX_WAIT_OBJECTS 64 #define WAITTYPE_ALL 0 #define WAITTYPE_ANY 1 +#define WAITKEY_VALID 0x8000 + +/* kthread priority */ +#define LOW_PRIORITY 0 +#define LOW_REALTIME_PRIORITY 16 +#define HIGH_PRIORITY 31 + struct thread_context { void *tc_thrctx; void *tc_thrfunc; @@ -395,11 +558,133 @@ struct thread_context { typedef struct thread_context thread_context; +/* Forward declaration */ +struct driver_object; +struct devobj_extension; + +struct driver_extension { + struct driver_object *dre_driverobj; + void *dre_adddevicefunc; + uint32_t dre_reinitcnt; + unicode_string dre_srvname; + + /* + * Drivers are allowed to add one or more custom extensions + * to the driver object, but there's no special pointer + * for them. Hang them off here for now. + */ + + list_entry dre_usrext; +}; + +typedef struct driver_extension driver_extension; + +struct custom_extension { + list_entry ce_list; + void *ce_clid; +}; + +typedef struct custom_extension custom_extension; + +/* + * The KINTERRUPT structure in Windows is opaque to drivers. + * We define our own custom version with things we need. + */ + +struct kinterrupt { + list_entry ki_list; + device_t ki_dev; + int ki_rid; + void *ki_cookie; + struct resource *ki_irq; + kspin_lock ki_lock_priv; + kspin_lock *ki_lock; + void *ki_svcfunc; + void *ki_svcctx; +}; + +typedef struct kinterrupt kinterrupt; + +struct ksystem_time { + uint32_t low_part; + int32_t high1_time; + int32_t high2_time; +}; + +enum nt_product_type { + NT_PRODUCT_WIN_NT = 1, + NT_PRODUCT_LAN_MAN_NT, + NT_PRODUCT_SERVER +}; + +enum alt_arch_type { + STANDARD_DESIGN, + NEC98x86, + END_ALTERNATIVES +}; + +struct kuser_shared_data { + uint32_t tick_count; + uint32_t tick_count_multiplier; + volatile struct ksystem_time interrupt_time; + volatile struct ksystem_time system_time; + volatile struct ksystem_time time_zone_bias; + uint16_t image_number_low; + uint16_t image_number_high; + int16_t nt_system_root[260]; + uint32_t max_stack_trace_depth; + uint32_t crypto_exponent; + uint32_t time_zone_id; + uint32_t large_page_min; + uint32_t reserved2[7]; + enum nt_product_type nt_product_type; + uint8_t product_type_is_valid; + uint32_t nt_major_version; + uint32_t nt_minor_version; + uint8_t processor_features[64]; + uint32_t reserved1; + uint32_t reserved3; + volatile uint32_t time_slip; + enum alt_arch_type alt_arch_type; + int64_t system_expiration_date; + uint32_t suite_mask; + uint8_t kdbg_enabled; + volatile uint32_t active_console; + volatile uint32_t dismount_count; + uint32_t com_plus_package; + uint32_t last_system_rit_event_tick_count; + uint32_t num_phys_pages; + uint8_t safe_boot_mode; + uint32_t trace_log; + uint64_t fill0; + uint64_t sys_call[4]; + union { + volatile struct ksystem_time tick_count; + volatile uint64_t tick_count_quad; + } tick; +}; + +/* + * In Windows, there are Physical Device Objects (PDOs) and + * Functional Device Objects (FDOs). Physical Device Objects are + * created and maintained by bus drivers. For example, the PCI + * bus driver might detect two PCI ethernet cards on a given + * bus. The PCI bus driver will then allocate two device_objects + * for its own internal bookeeping purposes. This is analagous + * to the device_t that the FreeBSD PCI code allocates and passes + * into each PCI driver's probe and attach routines. + * + * When an ethernet driver claims one of the ethernet cards + * on the bus, it will create its own device_object. This is + * the Functional Device Object. This object is analagous to the + * device-specific softc structure. + */ + struct device_object { uint16_t do_type; uint16_t do_size; uint32_t do_refcnt; - struct device_object *do_drvobj; + struct driver_object *do_drvobj; struct device_object *do_nextdev; struct device_object *do_attacheddev; struct irp *do_currirp; @@ -408,6 +693,7 @@ struct device_object { uint32_t do_characteristics; void *do_vpb; void *do_devext; + uint32_t do_devtype; uint8_t do_stacksize; union { list_entry do_listent; @@ -421,20 +707,477 @@ struct device_object { struct nt_kevent do_devlock; uint16_t do_sectorsz; uint16_t do_spare1; - void *do_devobj_ext; + struct devobj_extension *do_devobj_ext; void *do_rsvd; }; typedef struct device_object device_object; +struct devobj_extension { + uint16_t dve_type; + uint16_t dve_size; + device_object *dve_devobj; +}; + +typedef struct devobj_extension devobj_extension; + +/* Device object flags */ + +#define DO_VERIFY_VOLUME 0x00000002 +#define DO_BUFFERED_IO 0x00000004 +#define DO_EXCLUSIVE 0x00000008 +#define DO_DIRECT_IO 0x00000010 +#define DO_MAP_IO_BUFFER 0x00000020 +#define DO_DEVICE_HAS_NAME 0x00000040 +#define DO_DEVICE_INITIALIZING 0x00000080 +#define DO_SYSTEM_BOOT_PARTITION 0x00000100 +#define DO_LONG_TERM_REQUESTS 0x00000200 +#define DO_NEVER_LAST_DEVICE 0x00000400 +#define DO_SHUTDOWN_REGISTERED 0x00000800 +#define DO_BUS_ENUMERATED_DEVICE 0x00001000 +#define DO_POWER_PAGABLE 0x00002000 +#define DO_POWER_INRUSH 0x00004000 +#define DO_LOW_PRIORITY_FILESYSTEM 0x00010000 + +/* Priority boosts */ + +#define IO_NO_INCREMENT 0 +#define IO_CD_ROM_INCREMENT 1 +#define IO_DISK_INCREMENT 1 +#define IO_KEYBOARD_INCREMENT 6 +#define IO_MAILSLOT_INCREMENT 2 +#define IO_MOUSE_INCREMENT 6 +#define IO_NAMED_PIPE_INCREMENT 2 +#define IO_NETWORK_INCREMENT 2 +#define IO_PARALLEL_INCREMENT 1 +#define IO_SERIAL_INCREMENT 2 +#define IO_SOUND_INCREMENT 8 +#define IO_VIDEO_INCREMENT 1 + +/* IRP major codes */ + +#define IRP_MJ_CREATE 0x00 +#define IRP_MJ_CREATE_NAMED_PIPE 0x01 +#define IRP_MJ_CLOSE 0x02 +#define IRP_MJ_READ 0x03 +#define IRP_MJ_WRITE 0x04 +#define IRP_MJ_QUERY_INFORMATION 0x05 +#define IRP_MJ_SET_INFORMATION 0x06 +#define IRP_MJ_QUERY_EA 0x07 +#define IRP_MJ_SET_EA 0x08 +#define IRP_MJ_FLUSH_BUFFERS 0x09 +#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a +#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b +#define IRP_MJ_DIRECTORY_CONTROL 0x0c +#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d +#define IRP_MJ_DEVICE_CONTROL 0x0e +#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f +#define IRP_MJ_SHUTDOWN 0x10 +#define IRP_MJ_LOCK_CONTROL 0x11 +#define IRP_MJ_CLEANUP 0x12 +#define IRP_MJ_CREATE_MAILSLOT 0x13 +#define IRP_MJ_QUERY_SECURITY 0x14 +#define IRP_MJ_SET_SECURITY 0x15 +#define IRP_MJ_POWER 0x16 +#define IRP_MJ_SYSTEM_CONTROL 0x17 +#define IRP_MJ_DEVICE_CHANGE 0x18 +#define IRP_MJ_QUERY_QUOTA 0x19 +#define IRP_MJ_SET_QUOTA 0x1a +#define IRP_MJ_PNP 0x1b +#define IRP_MJ_PNP_POWER IRP_MJ_PNP // Obsolete.... +#define IRP_MJ_MAXIMUM_FUNCTION 0x1b +#define IRP_MJ_SCSI IRP_MJ_INTERNAL_DEVICE_CONTROL + +/* IRP minor codes */ + +#define IRP_MN_QUERY_DIRECTORY 0x01 +#define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x02 +#define IRP_MN_USER_FS_REQUEST 0x00 + +#define IRP_MN_MOUNT_VOLUME 0x01 +#define IRP_MN_VERIFY_VOLUME 0x02 +#define IRP_MN_LOAD_FILE_SYSTEM 0x03 +#define IRP_MN_TRACK_LINK 0x04 +#define IRP_MN_KERNEL_CALL 0x04 + +#define IRP_MN_LOCK 0x01 +#define IRP_MN_UNLOCK_SINGLE 0x02 +#define IRP_MN_UNLOCK_ALL 0x03 +#define IRP_MN_UNLOCK_ALL_BY_KEY 0x04 + +#define IRP_MN_NORMAL 0x00 +#define IRP_MN_DPC 0x01 +#define IRP_MN_MDL 0x02 +#define IRP_MN_COMPLETE 0x04 +#define IRP_MN_COMPRESSED 0x08 + +#define IRP_MN_MDL_DPC (IRP_MN_MDL | IRP_MN_DPC) +#define IRP_MN_COMPLETE_MDL (IRP_MN_COMPLETE | IRP_MN_MDL) +#define IRP_MN_COMPLETE_MDL_DPC (IRP_MN_COMPLETE_MDL | IRP_MN_DPC) + +#define IRP_MN_SCSI_CLASS 0x01 + +#define IRP_MN_START_DEVICE 0x00 +#define IRP_MN_QUERY_REMOVE_DEVICE 0x01 +#define IRP_MN_REMOVE_DEVICE 0x02 +#define IRP_MN_CANCEL_REMOVE_DEVICE 0x03 +#define IRP_MN_STOP_DEVICE 0x04 +#define IRP_MN_QUERY_STOP_DEVICE 0x05 +#define IRP_MN_CANCEL_STOP_DEVICE 0x06 + +#define IRP_MN_QUERY_DEVICE_RELATIONS 0x07 +#define IRP_MN_QUERY_INTERFACE 0x08 +#define IRP_MN_QUERY_CAPABILITIES 0x09 +#define IRP_MN_QUERY_RESOURCES 0x0A +#define IRP_MN_QUERY_RESOURCE_REQUIREMENTS 0x0B +#define IRP_MN_QUERY_DEVICE_TEXT 0x0C +#define IRP_MN_FILTER_RESOURCE_REQUIREMENTS 0x0D + +#define IRP_MN_READ_CONFIG 0x0F +#define IRP_MN_WRITE_CONFIG 0x10 +#define IRP_MN_EJECT 0x11 +#define IRP_MN_SET_LOCK 0x12 +#define IRP_MN_QUERY_ID 0x13 +#define IRP_MN_QUERY_PNP_DEVICE_STATE 0x14 +#define IRP_MN_QUERY_BUS_INFORMATION 0x15 +#define IRP_MN_DEVICE_USAGE_NOTIFICATION 0x16 +#define IRP_MN_SURPRISE_REMOVAL 0x17 +#define IRP_MN_QUERY_LEGACY_BUS_INFORMATION 0x18 + +#define IRP_MN_WAIT_WAKE 0x00 +#define IRP_MN_POWER_SEQUENCE 0x01 +#define IRP_MN_SET_POWER 0x02 +#define IRP_MN_QUERY_POWER 0x03 + +#define IRP_MN_QUERY_ALL_DATA 0x00 +#define IRP_MN_QUERY_SINGLE_INSTANCE 0x01 +#define IRP_MN_CHANGE_SINGLE_INSTANCE 0x02 +#define IRP_MN_CHANGE_SINGLE_ITEM 0x03 +#define IRP_MN_ENABLE_EVENTS 0x04 +#define IRP_MN_DISABLE_EVENTS 0x05 +#define IRP_MN_ENABLE_COLLECTION 0x06 +#define IRP_MN_DISABLE_COLLECTION 0x07 +#define IRP_MN_REGINFO 0x08 +#define IRP_MN_EXECUTE_METHOD 0x09 +#define IRP_MN_REGINFO_EX 0x0b + +/* IRP flags */ + +#define IRP_NOCACHE 0x00000001 +#define IRP_PAGING_IO 0x00000002 +#define IRP_MOUNT_COMPLETION 0x00000002 +#define IRP_SYNCHRONOUS_API 0x00000004 +#define IRP_ASSOCIATED_IRP 0x00000008 +#define IRP_BUFFERED_IO 0x00000010 +#define IRP_DEALLOCATE_BUFFER 0x00000020 +#define IRP_INPUT_OPERATION 0x00000040 +#define IRP_SYNCHRONOUS_PAGING_IO 0x00000040 +#define IRP_CREATE_OPERATION 0x00000080 +#define IRP_READ_OPERATION 0x00000100 +#define IRP_WRITE_OPERATION 0x00000200 +#define IRP_CLOSE_OPERATION 0x00000400 +#define IRP_DEFER_IO_COMPLETION 0x00000800 +#define IRP_OB_QUERY_NAME 0x00001000 +#define IRP_HOLD_DEVICE_QUEUE 0x00002000 +#define IRP_RETRY_IO_COMPLETION 0x00004000 +#define IRP_CLASS_CACHE_OPERATION 0x00008000 +#define IRP_SET_USER_EVENT IRP_CLOSE_OPERATION + +/* IRP I/O control flags */ + +#define IRP_QUOTA_CHARGED 0x01 +#define IRP_ALLOCATED_MUST_SUCCEED 0x02 +#define IRP_ALLOCATED_FIXED_SIZE 0x04 +#define IRP_LOOKASIDE_ALLOCATION 0x08 + +/* I/O method types */ + +#define METHOD_BUFFERED 0 +#define METHOD_IN_DIRECT 1 +#define METHOD_OUT_DIRECT 2 +#define METHOD_NEITHER 3 + +/* File access types */ + +#define FILE_ANY_ACCESS 0x0000 +#define FILE_SPECIAL_ACCESS FILE_ANY_ACCESS +#define FILE_READ_ACCESS 0x0001 +#define FILE_WRITE_ACCESS 0x0002 + +/* Recover I/O access method from IOCTL code. */ + +#define IO_METHOD(x) ((x) & 0xFFFFFFFC) + +/* Recover function code from IOCTL code */ + +#define IO_FUNC(x) (((x) & 0x7FFC) >> 2) + +/* Macro to construct an IOCTL code. */ + +#define IOCTL_CODE(dev, func, iomethod, acc) \ + ((dev) << 16) | (acc << 14) | (func << 2) | (iomethod)) + + +struct io_status_block { + union { + uint32_t isb_status; + void *isb_ptr; + } u; + register_t isb_info; +}; +#define isb_status u.isb_status +#define isb_ptr u.isb_ptr + +typedef struct io_status_block io_status_block; + +struct kapc { + uint16_t apc_type; + uint16_t apc_size; + uint32_t apc_spare0; + void *apc_thread; + list_entry apc_list; + void *apc_kernfunc; + void *apc_rundownfunc; + void *apc_normalfunc; + void *apc_normctx; + void *apc_sysarg1; + void *apc_sysarg2; + uint8_t apc_stateidx; + uint8_t apc_cpumode; + uint8_t apc_inserted; +}; + +typedef struct kapc kapc; + +typedef uint32_t (*completion_func)(device_object *, + struct irp *, void *); +typedef uint32_t (*cancel_func)(device_object *, + struct irp *); + +struct io_stack_location { + uint8_t isl_major; + uint8_t isl_minor; + uint8_t isl_flags; + uint8_t isl_ctl; + + /* + * There's a big-ass union here in the actual Windows + * definition of the stucture, but it contains stuff + * that doesn't really apply to BSD, and defining it + * all properly would require duplicating over a dozen + * other structures that we'll never use. Since the + * io_stack_location structure is opaque to drivers + * anyway, I'm not going to bother with the extra crap. + */ + + union { + struct { + uint32_t isl_len; + uint32_t *isl_key; + uint64_t isl_byteoff; + } isl_read; + struct { + uint32_t isl_len; + uint32_t *isl_key; + uint64_t isl_byteoff; + } isl_write; + struct { + uint32_t isl_obuflen; + uint32_t isl_ibuflen; + uint32_t isl_iocode; + void *isl_type3ibuf; + } isl_ioctl; + struct { + void *isl_arg1; + void *isl_arg2; + void *isl_arg3; + void *isl_arg4; + } isl_others; + } isl_parameters __attribute__((packed)); + + void *isl_devobj; + void *isl_fileobj; + completion_func isl_completionfunc; + void *isl_completionctx; +}; + +typedef struct io_stack_location io_stack_location; + +/* Stack location control flags */ + +#define SL_PENDING_RETURNED 0x01 +#define SL_INVOKE_ON_CANCEL 0x20 +#define SL_INVOKE_ON_SUCCESS 0x40 +#define SL_INVOKE_ON_ERROR 0x80 + struct irp { - uint32_t i_dummy; + uint16_t irp_type; + uint16_t irp_size; + mdl *irp_mdl; + uint32_t irp_flags; + union { + struct irp *irp_master; + uint32_t irp_irpcnt; + void *irp_sysbuf; + } irp_assoc; + list_entry irp_thlist; + io_status_block irp_iostat; + uint8_t irp_reqmode; + uint8_t irp_pendingreturned; + uint8_t irp_stackcnt; + uint8_t irp_currentstackloc; + uint8_t irp_cancel; + uint8_t irp_cancelirql; + uint8_t irp_apcenv; + uint8_t irp_allocflags; + io_status_block *irp_usriostat; + nt_kevent *irp_usrevent; + union { + struct { + void *irp_apcfunc; + void *irp_apcctx; + } irp_asyncparms; + uint64_t irp_allocsz; + } irp_overlay; + cancel_func irp_cancelfunc; + void *irp_userbuf; + + /* Windows kernel info */ + + union { + struct { + union { + kdevice_qentry irp_dqe; + struct { + void *irp_drvctx[4]; + } s1; + } u1; + void *irp_thread; + char *irp_auxbuf; + struct { + list_entry irp_list; + union { + io_stack_location *irp_csl; + uint32_t irp_pkttype; + } u2; + } s2; + void *irp_fileobj; + } irp_overlay; + union { + kapc irp_apc; + struct { + void *irp_xfer; + void *irp_dev; + } irp_usb; + } irp_misc; + void *irp_compkey; + } irp_tail; }; +#define irp_csl s2.u2.irp_csl +#define irp_pkttype s2.u2.irp_pkttype + +#define IRP_NDIS_DEV(irp) (irp)->irp_tail.irp_misc.irp_usb.irp_dev +#define IRP_NDISUSB_XFER(irp) (irp)->irp_tail.irp_misc.irp_usb.irp_xfer + typedef struct irp irp; +#define InterlockedExchangePointer(dst, val) \ + (void *)InterlockedExchange((uint32_t *)(dst), (uintptr_t)(val)) + +#define IoSizeOfIrp(ssize) \ + ((uint16_t) (sizeof(irp) + ((ssize) * (sizeof(io_stack_location))))) + +#define IoSetCancelRoutine(irp, func) \ + (cancel_func)InterlockedExchangePointer( \ + (void *)&(ip)->irp_cancelfunc, (void *)(func)) + +#define IoSetCancelValue(irp, val) \ + (u_long)InterlockedExchangePointer( \ + (void *)&(ip)->irp_cancel, (void *)(val)) + +#define IoGetCurrentIrpStackLocation(irp) \ + (irp)->irp_tail.irp_overlay.irp_csl + +#define IoGetNextIrpStackLocation(irp) \ + ((irp)->irp_tail.irp_overlay.irp_csl - 1) + +#define IoSetNextIrpStackLocation(irp) \ + do { \ + irp->irp_currentstackloc--; \ + irp->irp_tail.irp_overlay.irp_csl--; \ + } while(0) + +#define IoSetCompletionRoutine(irp, func, ctx, ok, err, cancel) \ + do { \ + io_stack_location *s; \ + s = IoGetNextIrpStackLocation((irp)); \ + s->isl_completionfunc = (func); \ + s->isl_completionctx = (ctx); \ + s->isl_ctl = 0; \ + if (ok) s->isl_ctl = SL_INVOKE_ON_SUCCESS; \ + if (err) s->isl_ctl |= SL_INVOKE_ON_ERROR; \ + if (cancel) s->isl_ctl |= SL_INVOKE_ON_CANCEL; \ + } while(0) + +#define IoMarkIrpPending(irp) \ + IoGetCurrentIrpStackLocation(irp)->isl_ctl |= SL_PENDING_RETURNED +#define IoUnmarkIrpPending(irp) \ + IoGetCurrentIrpStackLocation(irp)->isl_ctl &= ~SL_PENDING_RETURNED + +#define IoCopyCurrentIrpStackLocationToNext(irp) \ + do { \ + io_stack_location *src, *dst; \ + src = IoGetCurrentIrpStackLocation(irp); \ + dst = IoGetNextIrpStackLocation(irp); \ + bcopy((char *)src, (char *)dst, \ + offsetof(io_stack_location, isl_completionfunc)); \ + } while(0) + +#define IoSkipCurrentIrpStackLocation(irp) \ + do { \ + (irp)->irp_currentstackloc++; \ + (irp)->irp_tail.irp_overlay.irp_csl++; \ + } while(0) + +#define IoInitializeDpcRequest(dobj, dpcfunc) \ + KeInitializeDpc(&(dobj)->do_dpc, dpcfunc, dobj) + +#define IoRequestDpc(dobj, irp, ctx) \ + KeInsertQueueDpc(&(dobj)->do_dpc, irp, ctx) + typedef uint32_t (*driver_dispatch)(device_object *, irp *); +/* + * The driver_object is allocated once for each driver that's loaded + * into the system. A new one is allocated for each driver and + * populated a bit via the driver's DriverEntry function. + * In general, a Windows DriverEntry() function will provide a pointer + * to its AddDevice() method and set up the dispatch table. + * For NDIS drivers, this is all done behind the scenes in the + * NdisInitializeWrapper() and/or NdisMRegisterMiniport() routines. + */ + +struct driver_object { + uint16_t dro_type; + uint16_t dro_size; + device_object *dro_devobj; + uint32_t dro_flags; + void *dro_driverstart; + uint32_t dro_driversize; + void *dro_driversection; + driver_extension *dro_driverext; + unicode_string dro_drivername; + unicode_string *dro_hwdb; + void *dro_pfastiodispatch; + void *dro_driverinitfunc; + void *dro_driverstartiofunc; + void *dro_driverunloadfunc; + driver_dispatch dro_dispatch[IRP_MJ_MAXIMUM_FUNCTION + 1]; +}; + +typedef struct driver_object driver_object; + #define DEVPROP_DEVICE_DESCRIPTION 0x00000000 #define DEVPROP_HARDWARE_ID 0x00000001 #define DEVPROP_COMPATIBLE_IDS 0x00000002 @@ -456,19 +1199,165 @@ typedef uint32_t (*driver_dispatch)(device_object *, irp *); #define DEVPROP_INSTALL_STATE 0x00000012 #define DEVPROP_REMOVAL_POLICY 0x00000013 +/* Various supported device types (used with IoCreateDevice()) */ + +#define FILE_DEVICE_BEEP 0x00000001 +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_CONTROLLER 0x00000004 +#define FILE_DEVICE_DATALINK 0x00000005 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_INPORT_PORT 0x0000000A +#define FILE_DEVICE_KEYBOARD 0x0000000B +#define FILE_DEVICE_MAILSLOT 0x0000000C +#define FILE_DEVICE_MIDI_IN 0x0000000D +#define FILE_DEVICE_MIDI_OUT 0x0000000E +#define FILE_DEVICE_MOUSE 0x0000000F +#define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_BROWSER 0x00000013 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SCANNER 0x00000019 +#define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001A +#define FILE_DEVICE_SERIAL_PORT 0x0000001B +#define FILE_DEVICE_SCREEN 0x0000001C +#define FILE_DEVICE_SOUND 0x0000001D +#define FILE_DEVICE_STREAMS 0x0000001E +#define FILE_DEVICE_TAPE 0x0000001F +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_TRANSPORT 0x00000021 +#define FILE_DEVICE_UNKNOWN 0x00000022 +#define FILE_DEVICE_VIDEO 0x00000023 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_WAVE_IN 0x00000025 +#define FILE_DEVICE_WAVE_OUT 0x00000026 +#define FILE_DEVICE_8042_PORT 0x00000027 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 +#define FILE_DEVICE_BATTERY 0x00000029 +#define FILE_DEVICE_BUS_EXTENDER 0x0000002A +#define FILE_DEVICE_MODEM 0x0000002B +#define FILE_DEVICE_VDM 0x0000002C +#define FILE_DEVICE_MASS_STORAGE 0x0000002D +#define FILE_DEVICE_SMB 0x0000002E +#define FILE_DEVICE_KS 0x0000002F +#define FILE_DEVICE_CHANGER 0x00000030 +#define FILE_DEVICE_SMARTCARD 0x00000031 +#define FILE_DEVICE_ACPI 0x00000032 +#define FILE_DEVICE_DVD 0x00000033 +#define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 +#define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 +#define FILE_DEVICE_DFS_VOLUME 0x00000036 +#define FILE_DEVICE_SERENUM 0x00000037 +#define FILE_DEVICE_TERMSRV 0x00000038 +#define FILE_DEVICE_KSEC 0x00000039 +#define FILE_DEVICE_FIPS 0x0000003A + +/* Device characteristics */ + +#define FILE_REMOVABLE_MEDIA 0x00000001 +#define FILE_READ_ONLY_DEVICE 0x00000002 +#define FILE_FLOPPY_DISKETTE 0x00000004 +#define FILE_WRITE_ONCE_MEDIA 0x00000008 +#define FILE_REMOTE_DEVICE 0x00000010 +#define FILE_DEVICE_IS_MOUNTED 0x00000020 +#define FILE_VIRTUAL_VOLUME 0x00000040 +#define FILE_AUTOGENERATED_DEVICE_NAME 0x00000080 +#define FILE_DEVICE_SECURE_OPEN 0x00000100 + +/* Status codes */ + #define STATUS_SUCCESS 0x00000000 #define STATUS_USER_APC 0x000000C0 #define STATUS_KERNEL_APC 0x00000100 #define STATUS_ALERTED 0x00000101 #define STATUS_TIMEOUT 0x00000102 +#define STATUS_PENDING 0x00000103 +#define STATUS_FAILURE 0xC0000001 +#define STATUS_NOT_IMPLEMENTED 0xC0000002 +#define STATUS_ACCESS_VIOLATION 0xC0000005 #define STATUS_INVALID_PARAMETER 0xC000000D #define STATUS_INVALID_DEVICE_REQUEST 0xC0000010 +#define STATUS_MORE_PROCESSING_REQUIRED 0xC0000016 +#define STATUS_NO_MEMORY 0xC0000017 #define STATUS_BUFFER_TOO_SMALL 0xC0000023 #define STATUS_MUTANT_NOT_OWNED 0xC0000046 +#define STATUS_NOT_SUPPORTED 0xC00000BB #define STATUS_INVALID_PARAMETER_2 0xC00000F0 +#define STATUS_INSUFFICIENT_RESOURCES 0xC000009A +#define STATUS_DEVICE_NOT_CONNECTED 0xC000009D +#define STATUS_CANCELLED 0xC0000120 +#define STATUS_NOT_FOUND 0xC0000225 +#define STATUS_DEVICE_REMOVED 0xC00002B6 #define STATUS_WAIT_0 0x00000000 +/* Memory pool types, for ExAllocatePoolWithTag() */ + +#define NonPagedPool 0x00000000 +#define PagedPool 0x00000001 +#define NonPagedPoolMustSucceed 0x00000002 +#define DontUseThisType 0x00000003 +#define NonPagedPoolCacheAligned 0x00000004 +#define PagedPoolCacheAligned 0x00000005 +#define NonPagedPoolCacheAlignedMustS 0x00000006 +#define MaxPoolType 0x00000007 + +/* + * IO_WORKITEM is an opaque structures that must be allocated + * via IoAllocateWorkItem() and released via IoFreeWorkItem(). + * Consequently, we can define it any way we want. + */ +typedef void (*io_workitem_func)(device_object *, void *); + +struct io_workitem { + io_workitem_func iw_func; + void *iw_ctx; + list_entry iw_listentry; + device_object *iw_dobj; + int iw_idx; +}; + +typedef struct io_workitem io_workitem; + +#define WORKQUEUE_CRITICAL 0 +#define WORKQUEUE_DELAYED 1 +#define WORKQUEUE_HYPERCRITICAL 2 + +#define WORKITEM_THREADS 4 +#define WORKITEM_LEGACY_THREAD 3 +#define WORKIDX_INC(x) (x) = (x + 1) % WORKITEM_LEGACY_THREAD + +/* + * Older, deprecated work item API, needed to support NdisQueueWorkItem(). + */ + +struct work_queue_item; + +typedef void (*work_item_func)(struct work_queue_item *, void *); + +struct work_queue_item { + list_entry wqi_entry; + work_item_func wqi_func; + void *wqi_ctx; +}; + +typedef struct work_queue_item work_queue_item; + +#define ExInitializeWorkItem(w, func, ctx) \ + do { \ + (w)->wqi_func = (func); \ + (w)->wqi_ctx = (ctx); \ + InitializeListHead(&((w)->wqi_entry)); \ + } while (0) + /* * FreeBSD's kernel stack is 2 pages in size by default. The * Windows stack is larger, so we need to give our threads more @@ -476,41 +1365,165 @@ typedef uint32_t (*driver_dispatch)(device_object *, irp *); */ #define NDIS_KSTACK_PAGES 8 +/* + * Different kinds of function wrapping we can do. + */ + +#define WINDRV_WRAP_STDCALL 1 +#define WINDRV_WRAP_FASTCALL 2 +#define WINDRV_WRAP_REGPARM 3 +#define WINDRV_WRAP_CDECL 4 +#define WINDRV_WRAP_AMD64 5 + +struct drvdb_ent { + driver_object *windrv_object; + void *windrv_devlist; + ndis_cfg *windrv_regvals; + interface_type windrv_bustype; + STAILQ_ENTRY(drvdb_ent) link; +}; + extern image_patch_table ntoskrnl_functbl[]; +#ifdef __amd64__ +extern struct kuser_shared_data kuser_shared_data; +#endif +typedef void (*funcptr)(void); +typedef int (*matchfuncptr)(interface_type, void *, void *); __BEGIN_DECLS +extern int windrv_libinit(void); +extern int windrv_libfini(void); +extern driver_object *windrv_lookup(vm_offset_t, char *); +extern struct drvdb_ent *windrv_match(matchfuncptr, void *); +extern int windrv_load(module_t, vm_offset_t, int, interface_type, + void *, ndis_cfg *); +extern int windrv_unload(module_t, vm_offset_t, int); +extern int windrv_create_pdo(driver_object *, device_t); +extern void windrv_destroy_pdo(driver_object *, device_t); +extern device_object *windrv_find_pdo(driver_object *, device_t); +extern int windrv_bus_attach(driver_object *, char *); +extern int windrv_wrap(funcptr, funcptr *, int, int); +extern int windrv_unwrap(funcptr); +extern void ctxsw_utow(void); +extern void ctxsw_wtou(void); + extern int ntoskrnl_libinit(void); extern int ntoskrnl_libfini(void); -__stdcall extern void ntoskrnl_init_dpc(kdpc *, void *, void *); -__stdcall extern uint8_t ntoskrnl_queue_dpc(kdpc *, void *, void *); -__stdcall extern uint8_t ntoskrnl_dequeue_dpc(kdpc *); -__stdcall extern void ntoskrnl_init_timer(ktimer *); -__stdcall extern void ntoskrnl_init_timer_ex(ktimer *, uint32_t); -__stdcall extern uint8_t ntoskrnl_set_timer(ktimer *, int64_t, kdpc *); -__stdcall extern uint8_t ntoskrnl_set_timer_ex(ktimer *, int64_t, - uint32_t, kdpc *); -__stdcall extern uint8_t ntoskrnl_cancel_timer(ktimer *); -__stdcall extern uint8_t ntoskrnl_read_timer(ktimer *); -__stdcall extern uint32_t ntoskrnl_waitforobj(nt_dispatch_header *, uint32_t, + +extern void ntoskrnl_intr(void *); +extern void ntoskrnl_time(uint64_t *); + +extern uint16_t ExQueryDepthSList(slist_header *); +extern slist_entry + *InterlockedPushEntrySList(slist_header *, slist_entry *); +extern slist_entry *InterlockedPopEntrySList(slist_header *); +extern uint32_t RtlUnicodeStringToAnsiString(ansi_string *, + unicode_string *, uint8_t); +extern uint32_t RtlAnsiStringToUnicodeString(unicode_string *, + ansi_string *, uint8_t); +extern void RtlInitAnsiString(ansi_string *, char *); +extern void RtlInitUnicodeString(unicode_string *, + uint16_t *); +extern void RtlFreeUnicodeString(unicode_string *); +extern void RtlFreeAnsiString(ansi_string *); +extern void KeInitializeDpc(kdpc *, void *, void *); +extern uint8_t KeInsertQueueDpc(kdpc *, void *, void *); +extern uint8_t KeRemoveQueueDpc(kdpc *); +extern void KeSetImportanceDpc(kdpc *, uint32_t); +extern void KeSetTargetProcessorDpc(kdpc *, uint8_t); +extern void KeFlushQueuedDpcs(void); +extern uint32_t KeGetCurrentProcessorNumber(void); +extern void KeInitializeTimer(ktimer *); +extern void KeInitializeTimerEx(ktimer *, uint32_t); +extern uint8_t KeSetTimer(ktimer *, int64_t, kdpc *); +extern uint8_t KeSetTimerEx(ktimer *, int64_t, uint32_t, kdpc *); +extern uint8_t KeCancelTimer(ktimer *); +extern uint8_t KeReadStateTimer(ktimer *); +extern uint32_t KeWaitForSingleObject(void *, uint32_t, uint32_t, uint8_t, int64_t *); -__stdcall extern void ntoskrnl_init_event(nt_kevent *, uint32_t, uint8_t); -__stdcall extern void ntoskrnl_clear_event(nt_kevent *); -__stdcall extern uint32_t ntoskrnl_read_event(nt_kevent *); -__stdcall extern uint32_t ntoskrnl_set_event(nt_kevent *, uint32_t, uint8_t); -__stdcall extern uint32_t ntoskrnl_reset_event(nt_kevent *); -__stdcall __regcall void ntoskrnl_lock_dpc(REGARGS1(kspin_lock *lock)); -__stdcall __regcall void ntoskrnl_unlock_dpc(REGARGS1(kspin_lock *lock)); +extern void KeInitializeEvent(nt_kevent *, uint32_t, uint8_t); +extern void KeClearEvent(nt_kevent *); +extern uint32_t KeReadStateEvent(nt_kevent *); +extern uint32_t KeSetEvent(nt_kevent *, uint32_t, uint8_t); +extern uint32_t KeResetEvent(nt_kevent *); +#ifdef __i386__ +extern void KefAcquireSpinLockAtDpcLevel(kspin_lock *); +extern void KefReleaseSpinLockFromDpcLevel(kspin_lock *); +extern uint8_t KeAcquireSpinLockRaiseToDpc(kspin_lock *); +#else +extern void KeAcquireSpinLockAtDpcLevel(kspin_lock *); +extern void KeReleaseSpinLockFromDpcLevel(kspin_lock *); +#endif +extern void KeInitializeSpinLock(kspin_lock *); +extern uint8_t KeAcquireInterruptSpinLock(kinterrupt *); +extern void KeReleaseInterruptSpinLock(kinterrupt *, uint8_t); +extern uint8_t KeSynchronizeExecution(kinterrupt *, void *, void *); +extern uintptr_t InterlockedExchange(volatile uint32_t *, + uintptr_t); +extern void *ExAllocatePoolWithTag(uint32_t, size_t, uint32_t); +extern void ExFreePool(void *); +extern uint32_t IoConnectInterrupt(kinterrupt **, void *, void *, + kspin_lock *, uint32_t, uint8_t, uint8_t, uint8_t, uint8_t, + uint32_t, uint8_t); +extern uint8_t MmIsAddressValid(void *); +extern void *MmGetSystemRoutineAddress(unicode_string *); +extern void *MmMapIoSpace(uint64_t, uint32_t, uint32_t); +extern void MmUnmapIoSpace(void *, size_t); +extern void MmBuildMdlForNonPagedPool(mdl *); +extern void IoDisconnectInterrupt(kinterrupt *); +extern uint32_t IoAllocateDriverObjectExtension(driver_object *, + void *, uint32_t, void **); +extern void *IoGetDriverObjectExtension(driver_object *, void *); +extern uint32_t IoCreateDevice(driver_object *, uint32_t, + unicode_string *, uint32_t, uint32_t, uint8_t, device_object **); +extern void IoDeleteDevice(device_object *); +extern device_object *IoGetAttachedDevice(device_object *); +extern uint32_t IofCallDriver(device_object *, irp *); +extern void IofCompleteRequest(irp *, uint8_t); +extern void IoAcquireCancelSpinLock(uint8_t *); +extern void IoReleaseCancelSpinLock(uint8_t); +extern uint8_t IoCancelIrp(irp *); +extern void IoDetachDevice(device_object *); +extern device_object *IoAttachDeviceToDeviceStack(device_object *, + device_object *); +extern mdl *IoAllocateMdl(void *, uint32_t, uint8_t, uint8_t, irp *); +extern void IoFreeMdl(mdl *); +extern io_workitem *IoAllocateWorkItem(device_object *); +extern void ExQueueWorkItem(work_queue_item *, u_int32_t); +extern void IoFreeWorkItem(io_workitem *); +extern void IoQueueWorkItem(io_workitem *, io_workitem_func, + uint32_t, void *); + +#define IoCallDriver(a, b) IofCallDriver(a, b) +#define IoCompleteRequest(a, b) IofCompleteRequest(a, b) /* * On the Windows x86 arch, KeAcquireSpinLock() and KeReleaseSpinLock() * routines live in the HAL. We try to imitate this behavior. */ #ifdef __i386__ -#define ntoskrnl_acquire_spinlock(a, b) \ - *(b) = FASTCALL(hal_lock, a, 0) -#define ntoskrnl_release_spinlock(a, b) \ - FASTCALL(hal_unlock, a, b) +#define KI_USER_SHARED_DATA 0xffdf0000 +#define KeAcquireSpinLock(a, b) *(b) = KfAcquireSpinLock(a) +#define KeReleaseSpinLock(a, b) KfReleaseSpinLock(a, b) +#define KeRaiseIrql(a, b) *(b) = KfRaiseIrql(a) +#define KeLowerIrql(a) KfLowerIrql(a) +#define KeAcquireSpinLockAtDpcLevel(a) KefAcquireSpinLockAtDpcLevel(a) +#define KeReleaseSpinLockFromDpcLevel(a) KefReleaseSpinLockFromDpcLevel(a) #endif /* __i386__ */ + +#ifdef __x86_64__ +#define KI_USER_SHARED_DATA 0xfffff78000000000UL +#define KeAcquireSpinLock(a, b) *(b) = KfAcquireSpinLock(a) +#define KeReleaseSpinLock(a, b) KfReleaseSpinLock(a, b) + +/* + * These may need to be redefined later; + * not sure where they live on amd64 yet. + */ +#define KeRaiseIrql(a, b) *(b) = KfRaiseIrql(a) +#define KeLowerIrql(a) KfLowerIrql(a) +#endif /* __x86_64__ */ + __END_DECLS #endif /* _NTOSKRNL_VAR_H_ */ diff --git a/sys/emulation/ndis/pe_var.h b/sys/emulation/ndis/pe_var.h index 42fbb5e74c..ec25b2ee8a 100644 --- a/sys/emulation/ndis/pe_var.h +++ b/sys/emulation/ndis/pe_var.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,22 +29,21 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/compat/ndis/pe_var.h,v 1.7 2004/04/14 07:48:02 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/pe_var.h,v 1.1 2004/07/29 20:51:34 dillon Exp $ + * $FreeBSD: src/sys/compat/ndis/pe_var.h,v 1.15 2009/03/07 07:26:22 weongyo Exp $ */ #ifndef _PE_VAR_H_ -#define _PE_VAR_H_ +#define _PE_VAR_H_ /* * Image Format */ -#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ -#define IMAGE_OS2_SIGNATURE 0x454E /* NE */ -#define IMAGE_OS2_SIGNATURE_LE 0x454C /* LE */ -#define IMAGE_VXD_SIGNATURE 0x454C /* LE */ -#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ +#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */ +#define IMAGE_OS2_SIGNATURE 0x454E /* NE */ +#define IMAGE_OS2_SIGNATURE_LE 0x454C /* LE */ +#define IMAGE_VXD_SIGNATURE 0x454C /* LE */ +#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ /* * All PE files have one of these, just so if you attempt to @@ -85,7 +84,7 @@ struct image_file_header { uint16_t ifh_numsections; /* # of sections */ uint32_t ifh_timestamp; /* Date/time stamp */ uint32_t ifh_symtblptr; /* Offset to symbol table */ - uint32_t ifh_numsyms; /* # of symbols */ + uint32_t ifh_numsyms; /* # of symbols */ uint16_t ifh_optionalhdrlen; /* Size of optional header */ uint16_t ifh_characteristics; /* Characteristics */ }; @@ -94,57 +93,57 @@ typedef struct image_file_header image_file_header; /* Machine types */ -#define IMAGE_FILE_MACHINE_UNKNOWN 0 -#define IMAGE_FILE_MACHINE_I860 0x014d -#define IMAGE_FILE_MACHINE_I386 0x014c -#define IMAGE_FILE_MACHINE_R3000 0x0162 -#define IMAGE_FILE_MACHINE_R4000 0x0166 -#define IMAGE_FILE_MACHINE_R10000 0x0168 -#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 -#define IMAGE_FILE_MACHINE_ALPHA 0x0184 -#define IMAGE_FILE_MACHINE_SH3 0x01a2 -#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 -#define IMAGE_FILE_MACHINE_SH3E 0x01a4 -#define IMAGE_FILE_MACHINE_SH4 0x01a6 -#define IMAGE_FILE_MACHINE_SH5 0x01a8 -#define IMAGE_FILE_MACHINE_ARM 0x01c0 -#define IMAGE_FILE_MACHINE_THUMB 0x01c2 -#define IMAGE_FILE_MACHINE_AM33 0x01d3 -#define IMAGE_FILE_MACHINE_POWERPC 0x01f0 -#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 -#define IMAGE_FILE_MACHINE_IA64 0x0200 -#define IMAGE_FILE_MACHINE_MIPS16 0x0266 -#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 -#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 -#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 -#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64 -#define IMAGE_FILE_MACHINE_TRICORE 0x0520 -#define IMAGE_FILE_MACHINE_CEF 0x0cef -#define IMAGE_FILE_MACHINE_EBC 0x0ebc -#define IMAGE_FILE_MACHINE_AMD64 0x8664 -#define IMAGE_FILE_MACHINE_M32R 0x9041 -#define IMAGE_FILE_MACHINE_CEE 0xc0ee +#define IMAGE_FILE_MACHINE_UNKNOWN 0 +#define IMAGE_FILE_MACHINE_I860 0x014d +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_R3000 0x0162 +#define IMAGE_FILE_MACHINE_R4000 0x0166 +#define IMAGE_FILE_MACHINE_R10000 0x0168 +#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 +#define IMAGE_FILE_MACHINE_ALPHA 0x0184 +#define IMAGE_FILE_MACHINE_SH3 0x01a2 +#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 +#define IMAGE_FILE_MACHINE_SH3E 0x01a4 +#define IMAGE_FILE_MACHINE_SH4 0x01a6 +#define IMAGE_FILE_MACHINE_SH5 0x01a8 +#define IMAGE_FILE_MACHINE_ARM 0x01c0 +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 +#define IMAGE_FILE_MACHINE_AM33 0x01d3 +#define IMAGE_FILE_MACHINE_POWERPC 0x01f0 +#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 +#define IMAGE_FILE_MACHINE_IA64 0x0200 +#define IMAGE_FILE_MACHINE_MIPS16 0x0266 +#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 +#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 +#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 +#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64 +#define IMAGE_FILE_MACHINE_TRICORE 0x0520 +#define IMAGE_FILE_MACHINE_CEF 0x0cef +#define IMAGE_FILE_MACHINE_EBC 0x0ebc +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_M32R 0x9041 +#define IMAGE_FILE_MACHINE_CEE 0xc0ee /* Characteristics */ -#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 /* No relocation info */ -#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 -#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 -#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 -#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 -#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 -#define IMAGE_FILE_16BIT_MACHINE 0x0040 -#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 -#define IMAGE_FILE_32BIT_MACHINE 0x0100 -#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 -#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 -#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 -#define IMAGE_FILE_SYSTEM 0x1000 -#define IMAGE_FILE_DLL 0x2000 -#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 -#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 - -#define IMAGE_SIZEOF_FILE_HEADER 20 +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 /* No relocation info */ +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 +#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 +#define IMAGE_FILE_16BIT_MACHINE 0x0040 +#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 +#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 +#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 +#define IMAGE_FILE_SYSTEM 0x1000 +#define IMAGE_FILE_DLL 0x2000 +#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 +#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 + +#define IMAGE_SIZEOF_FILE_HEADER 20 /* * Directory format. @@ -157,7 +156,7 @@ struct image_data_directory { typedef struct image_data_directory image_data_directory; -#define IMAGE_DIRECTORY_ENTRIES_MAX 16 +#define IMAGE_DIRECTORY_ENTRIES_MAX 16 /* * Optional header format. @@ -175,11 +174,13 @@ struct image_optional_header { uint32_t ioh_bsssize; uint32_t ioh_entryaddr; uint32_t ioh_codebaseaddr; +#ifndef __x86_64__ uint32_t ioh_databaseaddr; +#endif /* NT-specific fields */ - uint32_t ioh_imagebase; + uintptr_t ioh_imagebase; uint32_t ioh_sectalign; uint32_t ioh_filealign; uint16_t ioh_osver_major; @@ -194,10 +195,10 @@ struct image_optional_header { uint32_t ioh_csum; uint16_t ioh_subsys; uint16_t ioh_dll_characteristics; - uint32_t ioh_stackreservesize; - uint32_t ioh_stackcommitsize; - uint32_t ioh_heapreservesize; - uint32_t ioh_heapcommitsize; + uintptr_t ioh_stackreservesize; + uintptr_t ioh_stackcommitsize; + uintptr_t ioh_heapreservesize; + uintptr_t ioh_heapcommitsize; uint16_t ioh_loaderflags; uint32_t ioh_rva_size_cnt; image_data_directory ioh_datadir[IMAGE_DIRECTORY_ENTRIES_MAX]; @@ -213,52 +214,56 @@ struct image_nt_header { typedef struct image_nt_header image_nt_header; +#define IMAGE_SIZEOF_NT_HEADER(nthdr) \ + (offsetof(image_nt_header, inh_optionalhdr) + \ + ((image_nt_header *)(nthdr))->inh_filehdr.ifh_optionalhdrlen) + /* Directory Entries */ -#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 /* Export Directory */ -#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 /* Import Directory */ -#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 /* Resource Directory */ -#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 /* Exception Directory */ -#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 /* Security Directory */ -#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 /* Base Relocation Table */ -#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 /* Debug Directory */ -#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 /* Description String */ -#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* Machine Value (MIPS GP) */ -#define IMAGE_DIRECTORY_ENTRY_TLS 9 /* TLS Directory */ -#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 /* Load Configuration Directory */ -#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 /* Bound Import Directory in headers */ -#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */ -#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 -#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 /* Export Directory */ +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 /* Import Directory */ +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 /* Resource Directory */ +#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 /* Exception Directory */ +#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 /* Security Directory */ +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 /* Base Relocation Table */ +#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 /* Debug Directory */ +#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 /* Description String */ +#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* Machine Value (MIPS GP) */ +#define IMAGE_DIRECTORY_ENTRY_TLS 9 /* TLS Directory */ +#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 /* Load Configuration Directory */ +#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 /* Bound Import Directory in headers */ +#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */ +#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 +#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 /* Resource types */ -#define RT_CURSOR 1 -#define RT_BITMAP 2 -#define RT_ICON 3 -#define RT_MENU 4 -#define RT_DIALOG 5 -#define RT_STRING 6 -#define RT_FONTDIR 7 -#define RT_FONT 8 -#define RT_ACCELERATOR 9 -#define RT_RCDATA 10 -#define RT_MESSAGETABLE 11 -#define RT_GROUP_CURSOR 12 -#define RT_GROUP_ICON 14 -#define RT_VERSION 16 -#define RT_DLGINCLUDE 17 -#define RT_PLUGPLAY 19 -#define RT_VXD 20 -#define RT_ANICURSOR 21 -#define RT_ANIICON 22 -#define RT_HTML 23 +#define RT_CURSOR 1 +#define RT_BITMAP 2 +#define RT_ICON 3 +#define RT_MENU 4 +#define RT_DIALOG 5 +#define RT_STRING 6 +#define RT_FONTDIR 7 +#define RT_FONT 8 +#define RT_ACCELERATOR 9 +#define RT_RCDATA 10 +#define RT_MESSAGETABLE 11 +#define RT_GROUP_CURSOR 12 +#define RT_GROUP_ICON 14 +#define RT_VERSION 16 +#define RT_DLGINCLUDE 17 +#define RT_PLUGPLAY 19 +#define RT_VXD 20 +#define RT_ANICURSOR 21 +#define RT_ANIICON 22 +#define RT_HTML 23 /* * Section header format. */ -#define IMAGE_SHORT_NAME_LEN 8 +#define IMAGE_SHORT_NAME_LEN 8 struct image_section_header { uint8_t ish_name[IMAGE_SHORT_NAME_LEN]; @@ -278,7 +283,12 @@ struct image_section_header { typedef struct image_section_header image_section_header; -#define IMAGE_SIZEOF_SECTION_HEADER 40 +#define IMAGE_SIZEOF_SECTION_HEADER 40 + +#define IMAGE_FIRST_SECTION(nthdr) \ + ((image_section_header *)((vm_offset_t)(nthdr) + \ + offsetof(image_nt_header, inh_optionalhdr) + \ + ((image_nt_header *)(nthdr))->inh_filehdr.ifh_optionalhdrlen)) /* * Import format @@ -286,11 +296,11 @@ typedef struct image_section_header image_section_header; struct image_import_by_name { uint16_t iibn_hint; - u_int8_t iibn_name[1]; + uint8_t iibn_name[1]; }; -#define IMAGE_ORDINAL_FLAG 0x80000000 -#define IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff) +#define IMAGE_ORDINAL_FLAG 0x80000000 +#define IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff) struct image_import_descriptor { uint32_t iid_import_name_table_addr; @@ -310,22 +320,22 @@ struct image_base_reloc { typedef struct image_base_reloc image_base_reloc; -#define IMR_RELTYPE(x) ((x >> 12) & 0xF) -#define IMR_RELOFFSET(x) (x & 0xFFF) +#define IMR_RELTYPE(x) ((x >> 12) & 0xF) +#define IMR_RELOFFSET(x) (x & 0xFFF) /* generic relocation types */ -#define IMAGE_REL_BASED_ABSOLUTE 0 -#define IMAGE_REL_BASED_HIGH 1 -#define IMAGE_REL_BASED_LOW 2 -#define IMAGE_REL_BASED_HIGHLOW 3 -#define IMAGE_REL_BASED_HIGHADJ 4 -#define IMAGE_REL_BASED_MIPS_JMPADDR 5 -#define IMAGE_REL_BASED_SECTION 6 -#define IMAGE_REL_BASED_REL 7 -#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 -#define IMAGE_REL_BASED_IA64_IMM64 9 /* yes, 9 too */ -#define IMAGE_REL_BASED_DIR64 10 -#define IMAGE_REL_BASED_HIGH3ADJ 11 +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGH 1 +#define IMAGE_REL_BASED_LOW 2 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_HIGHADJ 4 +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define IMAGE_REL_BASED_SECTION 6 +#define IMAGE_REL_BASED_REL 7 +#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 +#define IMAGE_REL_BASED_IA64_IMM64 9 /* yes, 9 too */ +#define IMAGE_REL_BASED_DIR64 10 +#define IMAGE_REL_BASED_HIGH3ADJ 11 struct image_resource_directory_entry { uint32_t irde_name; @@ -334,8 +344,8 @@ struct image_resource_directory_entry { typedef struct image_resource_directory_entry image_resource_directory_entry; -#define RESOURCE_NAME_STR 0x80000000 -#define RESOURCE_DIR_FLAG 0x80000000 +#define RESOURCE_NAME_STR 0x80000000 +#define RESOURCE_DIR_FLAG 0x80000000 struct image_resource_directory { uint32_t ird_characteristics; @@ -395,20 +405,129 @@ typedef struct message_resource_block message_resource_block; struct message_resource_entry { uint16_t mre_len; uint16_t mre_flags; - char mre_text[0]; + char mre_text[]; }; typedef struct message_resource_entry message_resource_entry; -#define MESSAGE_RESOURCE_UNICODE 0x0001 +#define MESSAGE_RESOURCE_UNICODE 0x0001 struct image_patch_table { char *ipt_name; void (*ipt_func)(void); + void (*ipt_wrap)(void); + int ipt_argcnt; + int ipt_ftype; }; typedef struct image_patch_table image_patch_table; +/* + * AMD64 support. Microsoft uses a different calling convention + * than everyone else on the amd64 platform. Sadly, gcc has no + * built-in support for it (yet). + * + * The three major differences we're concerned with are: + * + * - The first 4 register-sized arguments are passed in the + * %rcx, %rdx, %r8 and %r9 registers, and the rest are pushed + * onto the stack. (The ELF ABI uses 6 registers, not 4). + * + * - The caller must reserve space on the stack for the 4 + * register arguments in case the callee has to spill them. + * + * - The stack myst be 16-byte aligned by the time the callee + * executes. A call instruction implicitly pushes an 8 byte + * return address onto the stack. We have to make sure that + * the amount of space we consume, plus the return address, + * is a multiple of 16 bytes in size. This means that in + * some cases, we may need to chew up an extra 8 bytes on + * the stack that will be unused. + * + * On the bright side, Microsoft seems to be using just the one + * calling convention for all functions on amd64, unlike x86 where + * they use a mix of _stdcall, _fastcall and _cdecl. + */ + +#ifdef __x86_64__ + +extern uint64_t x86_64_call1(void *, uint64_t); +extern uint64_t x86_64_call2(void *, uint64_t, uint64_t); +extern uint64_t x86_64_call3(void *, uint64_t, uint64_t, uint64_t); +extern uint64_t x86_64_call4(void *, uint64_t, uint64_t, uint64_t, uint64_t); +extern uint64_t x86_64_call5(void *, uint64_t, uint64_t, uint64_t, uint64_t, + uint64_t); +extern uint64_t x86_64_call6(void *, uint64_t, uint64_t, uint64_t, uint64_t, + uint64_t, uint64_t); + + +#define MSCALL1(fn, a) \ + x86_64_call1((fn), (uint64_t)(a)) +#define MSCALL2(fn, a, b) \ + x86_64_call2((fn), (uint64_t)(a), (uint64_t)(b)) +#define MSCALL3(fn, a, b, c) \ + x86_64_call3((fn), (uint64_t)(a), (uint64_t)(b), \ + (uint64_t)(c)) +#define MSCALL4(fn, a, b, c, d) \ + x86_64_call4((fn), (uint64_t)(a), (uint64_t)(b), \ + (uint64_t)(c), (uint64_t)(d)) +#define MSCALL5(fn, a, b, c, d, e) \ + x86_64_call5((fn), (uint64_t)(a), (uint64_t)(b), \ + (uint64_t)(c), (uint64_t)(d), (uint64_t)(e)) +#define MSCALL6(fn, a, b, c, d, e, f) \ + x86_64_call6((fn), (uint64_t)(a), (uint64_t)(b), \ + (uint64_t)(c), (uint64_t)(d), (uint64_t)(e), (uint64_t)(f)) + +#endif /* __x86_64__ */ + +#ifdef __i386__ + +extern uint32_t x86_stdcall_call(void *, int, ...); + +#define MSCALL1(fn, a) x86_stdcall_call(fn, 1, (a)) +#define MSCALL2(fn, a, b) x86_stdcall_call(fn, 2, (a), (b)) +#define MSCALL3(fn, a, b, c) x86_stdcall_call(fn, 3, (a), (b), (c)) +#define MSCALL4(fn, a, b, c, d) x86_stdcall_call(fn, 4, (a), (b), (c), (d)) +#define MSCALL5(fn, a, b, c, d, e) \ + x86_stdcall_call(fn, 5, (a), (b), (c), (d), (e)) +#define MSCALL6(fn, a, b, c, d, e, f) \ + x86_stdcall_call(fn, 6, (a), (b), (c), (d), (e), (f)) + +#endif /* __i386__ */ + + +#define FUNC void(*)(void) + +#ifdef __i386__ +#define IMPORT_SFUNC(x, y) { #x, (FUNC)x, NULL, y, WINDRV_WRAP_STDCALL } +#define IMPORT_SFUNC_MAP(x, y, z) \ + { #x, (FUNC)y, NULL, z, WINDRV_WRAP_STDCALL } +#define IMPORT_FFUNC(x, y) { #x, (FUNC)x, NULL, y, WINDRV_WRAP_FASTCALL } +#define IMPORT_FFUNC_MAP(x, y, z) \ + { #x, (FUNC)y, NULL, z, WINDRV_WRAP_FASTCALL } +#define IMPORT_RFUNC(x, y) { #x, (FUNC)x, NULL, y, WINDRV_WRAP_REGPARM } +#define IMPORT_RFUNC_MAP(x, y, z) \ + { #x, (FUNC)y, NULL, z, WINDRV_WRAP_REGPARM } +#define IMPORT_CFUNC(x, y) { #x, (FUNC)x, NULL, y, WINDRV_WRAP_CDECL } +#define IMPORT_CFUNC_MAP(x, y, z) \ + { #x, (FUNC)y, NULL, z, WINDRV_WRAP_CDECL } +#endif /* __i386__ */ + +#ifdef __x86_64__ +#define IMPORT_SFUNC(x, y) { #x, (FUNC)x, NULL, y, WINDRV_WRAP_AMD64 } +#define IMPORT_SFUNC_MAP(x, y, z) \ + { #x, (FUNC)y, NULL, z, WINDRV_WRAP_AMD64 } +#define IMPORT_FFUNC(x, y) { #x, (FUNC)x, NULL, y, WINDRV_WRAP_AMD64 } +#define IMPORT_FFUNC_MAP(x, y, z) \ + { #x, (FUNC)y, NULL, z, WINDRV_WRAP_AMD64 } +#define IMPORT_RFUNC(x, y) { #x, (FUNC)x, NULL, y, WINDRV_WRAP_AMD64 } +#define IMPORT_RFUNC_MAP(x, y, z) \ + { #x, (FUNC)y, NULL, z, WINDRV_WRAP_AMD64 } +#define IMPORT_CFUNC(x, y) { #x, (FUNC)x, NULL, y, WINDRV_WRAP_AMD64 } +#define IMPORT_CFUNC_MAP(x, y, z) \ + { #x, (FUNC)y, NULL, z, WINDRV_WRAP_AMD64 } +#endif /* __x86_64__ */ + __BEGIN_DECLS extern int pe_get_dos_header(vm_offset_t, image_dos_header *); extern int pe_is_nt_image(vm_offset_t); @@ -418,7 +537,7 @@ extern int pe_get_section_header(vm_offset_t, image_section_header *); extern int pe_numsections(vm_offset_t); extern vm_offset_t pe_imagebase(vm_offset_t); extern vm_offset_t pe_directory_offset(vm_offset_t, uint32_t); -extern vm_offset_t pe_translate_addr (vm_offset_t, uint32_t); +extern vm_offset_t pe_translate_addr (vm_offset_t, vm_offset_t); extern int pe_get_section(vm_offset_t, image_section_header *, const char *); extern int pe_relocate(vm_offset_t); extern int pe_get_import_descriptor(vm_offset_t, image_import_descriptor *, char *); diff --git a/sys/emulation/ndis/regcall.h b/sys/emulation/ndis/regcall.h deleted file mode 100644 index a9e56cfbf5..0000000000 --- a/sys/emulation/ndis/regcall.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * - * Copyright (c) 2004 The DragonFly Project. All rights reserved. - * - * This code is derived from software contributed to The DragonFly Project - * by Matthew Dillon - * - * 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. Neither the name of The DragonFly Project nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific, prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS 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. - * - * Copyright (c) 2003 - * Bill Paul . All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD: src/sys/compat/ndis/pe_var.h,v 1.7 2004/04/14 07:48:02 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/regcall.h,v 1.1 2004/07/29 20:51:34 dillon Exp $ - */ - -#ifndef _REGCALL_H_ -#define _REGCALL_H_ - -/* - * Note: Windows uses the _stdcall calling convention. This means - * that the callback functions provided in the function table must - * be declared using __attribute__((__stdcall__)), otherwise the - * Windows code will likely screw up the %esp register and cause - * us to jump to an invalid address when it returns. With the - * stdcall calling convention the target procedure is responsible for - * popping the call arguments. - * - * Note: Windows often passes arguments in registers. In windows - * the order is: %ecx, %edx. The regparm(3) attribute in GNU C will - * pass arguments in the order: %eax, %edx, %ecx, with any remaining - * arguments passed on the stack. - */ - -#ifdef __x86_64__ -#define __stdcall -#define __regcall -#define REGARGS1(decl1) decl1 -#define REGARGS2(decl1, decl2) decl1, decl2 -#define REGCALL1(arg1) arg1 -#define REGCALL2(arg1, arg2) arg1, arg2 -#else -#define __stdcall __attribute__((__stdcall__)) -#define __regcall __attribute__((__regparm__(3))) -#define REGARGS1(decl1) int dummy1, int dummy2, decl1 -#define REGARGS2(decl1, decl2) int dummy1, decl2, decl1 -#define REGCALL1(arg1) 0, 0, arg1 -#define REGCALL2(arg1, arg2) 0, arg2, arg1 -#endif - - -/* - * This mess allows us to call a _fastcall style routine with our - * version of gcc, which lacks __attribute__((__fastcall__)). Only - * has meaning on x86; everywhere else, it's a no-op. - */ - -#ifdef __i386__ -typedef __stdcall __regcall int (*fcall1)(REGARGS1(uint32_t)); -typedef __stdcall __regcall int (*fcall2)(REGARGS2(uint32_t, uint32_t)); -typedef __stdcall __regcall int (*fcall3)(REGARGS2(uint32_t, uint32_t), uint32_t); -static __inline uint32_t -fastcall1(fcall1 f, uint32_t a) -{ - return(f(REGCALL1(a))); -} - -static __inline uint32_t -fastcall2(fcall2 f, uint32_t a, uint32_t b) -{ - return(f(REGCALL2(a, b))); -} - -static __inline uint32_t -fastcall3(fcall3 f, uint32_t a, uint32_t b, uint32_t c) -{ - return(f(REGCALL2(a, b), c)); -} - -#define FASTCALL1(f, a) \ - fastcall1((fcall1)(f), (uint32_t)(a)) -#define FASTCALL2(f, a, b) \ - fastcall2((fcall2)(f), (uint32_t)(a), (uint32_t)(b)) -#define FASTCALL3(f, a, b, c) \ - fastcall3((fcall3)(f), (uint32_t)(a), (uint32_t)(b), (uint32_t)(c)) -#else -#define FASTCALL1(f, a) (f)((a)) -#define FASTCALL2(f, a, b) (f)((a), (b)) -#define FASTCALL3(f, a, b, c) (f)((a), (b), (c)) -#endif /* __i386__ */ - -#endif diff --git a/sys/emulation/ndis/resource_var.h b/sys/emulation/ndis/resource_var.h index b4f1ff8435..05a97f6fe6 100644 --- a/sys/emulation/ndis/resource_var.h +++ b/sys/emulation/ndis/resource_var.h @@ -1,51 +1,87 @@ -/* - * $FreeBSD: src/sys/compat/ndis/resource_var.h,v 1.2 2004/03/29 02:15:29 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/resource_var.h,v 1.1 2004/07/29 20:51:34 dillon Exp $ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/compat/ndis/resource_var.h,v 1.4 2009/03/07 07:26:22 weongyo Exp $ */ +#ifndef _RESOURCE_VAR_H_ +#define _RESOURCE_VAR_H_ + typedef int cm_resource_type; struct physaddr { uint64_t np_quad; +#ifdef notdef + uint32_t np_low; + uint32_t np_high; +#endif }; typedef struct physaddr physaddr; enum interface_type { - InterfaceTypeUndefined = -1, - Internal, - Isa, - Eisa, - MicroChannel, - TurboChannel, - PCIBus, - VMEBus, - NuBus, - PCMCIABus, - CBus, - MPIBus, - MPSABus, - ProcessorInternal, - InternalPowerBus, - PNPISABus, - PNPBus, - MaximumInterfaceType + InterfaceTypeUndefined = -1, + Internal, + Isa, + Eisa, + MicroChannel, + TurboChannel, + PCIBus, + VMEBus, + NuBus, + PCMCIABus, + CBus, + MPIBus, + MPSABus, + ProcessorInternal, + InternalPowerBus, + PNPISABus, + PNPBus, + MaximumInterfaceType }; typedef enum interface_type interface_type; -#define CmResourceTypeNull 0 /* ResType_All or ResType_None (0x0000) */ -#define CmResourceTypePort 1 /* ResType_IO (0x0002) */ -#define CmResourceTypeInterrupt 2 /* ResType_IRQ (0x0004) */ -#define CmResourceTypeMemory 3 /* ResType_Mem (0x0001) */ -#define CmResourceTypeDma 4 /* ResType_DMA (0x0003) */ -#define CmResourceTypeDeviceSpecific 5 /* ResType_ClassSpecific (0xFFFF) */ -#define CmResourceTypeBusNumber 6 /* ResType_BusNumber (0x0006) */ -#define CmResourceTypeMaximum 7 -#define CmResourceTypeNonArbitrated 128 /* Not arbitrated if 0x80 bit set */ -#define CmResourceTypeConfigData 128 /* ResType_Reserved (0x8000) */ -#define CmResourceTypeDevicePrivate 129 /* ResType_DevicePrivate (0x8001) */ -#define CmResourceTypePcCardConfig 130 /* ResType_PcCardConfig (0x8002) */ +#define CmResourceTypeNull 0 /* ResType_All or ResType_None (0x0000) */ +#define CmResourceTypePort 1 /* ResType_IO (0x0002) */ +#define CmResourceTypeInterrupt 2 /* ResType_IRQ (0x0004) */ +#define CmResourceTypeMemory 3 /* ResType_Mem (0x0001) */ +#define CmResourceTypeDma 4 /* ResType_DMA (0x0003) */ +#define CmResourceTypeDeviceSpecific 5 /* ResType_ClassSpecific (0xFFFF) */ +#define CmResourceTypeBusNumber 6 /* ResType_BusNumber (0x0006) */ +#define CmResourceTypeMaximum 7 +#define CmResourceTypeNonArbitrated 128 /* Not arbitrated if 0x80 bit set */ +#define CmResourceTypeConfigData 128 /* ResType_Reserved (0x8000) */ +#define CmResourceTypeDevicePrivate 129 /* ResType_DevicePrivate (0x8001) */ +#define CmResourceTypePcCardConfig 130 /* ResType_PcCardConfig (0x8002) */ enum cm_share_disposition { CmResourceShareUndetermined = 0, /* Reserved */ @@ -58,41 +94,41 @@ typedef enum cm_share_disposition cm_share_disposition; /* Define the bit masks for Flags when type is CmResourceTypeInterrupt */ -#define CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE 0 -#define CM_RESOURCE_INTERRUPT_LATCHED 1 +#define CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE 0 +#define CM_RESOURCE_INTERRUPT_LATCHED 1 /* Define the bit masks for Flags when type is CmResourceTypeMemory */ -#define CM_RESOURCE_MEMORY_READ_WRITE 0x0000 -#define CM_RESOURCE_MEMORY_READ_ONLY 0x0001 -#define CM_RESOURCE_MEMORY_WRITE_ONLY 0x0002 -#define CM_RESOURCE_MEMORY_PREFETCHABLE 0x0004 +#define CM_RESOURCE_MEMORY_READ_WRITE 0x0000 +#define CM_RESOURCE_MEMORY_READ_ONLY 0x0001 +#define CM_RESOURCE_MEMORY_WRITE_ONLY 0x0002 +#define CM_RESOURCE_MEMORY_PREFETCHABLE 0x0004 -#define CM_RESOURCE_MEMORY_COMBINEDWRITE 0x0008 -#define CM_RESOURCE_MEMORY_24 0x0010 -#define CM_RESOURCE_MEMORY_CACHEABLE 0x0020 +#define CM_RESOURCE_MEMORY_COMBINEDWRITE 0x0008 +#define CM_RESOURCE_MEMORY_24 0x0010 +#define CM_RESOURCE_MEMORY_CACHEABLE 0x0020 /* Define the bit masks for Flags when type is CmResourceTypePort */ -#define CM_RESOURCE_PORT_MEMORY 0x0000 -#define CM_RESOURCE_PORT_IO 0x0001 -#define CM_RESOURCE_PORT_10_BIT_DECODE 0x0004 -#define CM_RESOURCE_PORT_12_BIT_DECODE 0x0008 -#define CM_RESOURCE_PORT_16_BIT_DECODE 0x0010 -#define CM_RESOURCE_PORT_POSITIVE_DECODE 0x0020 -#define CM_RESOURCE_PORT_PASSIVE_DECODE 0x0040 -#define CM_RESOURCE_PORT_WINDOW_DECODE 0x0080 +#define CM_RESOURCE_PORT_MEMORY 0x0000 +#define CM_RESOURCE_PORT_IO 0x0001 +#define CM_RESOURCE_PORT_10_BIT_DECODE 0x0004 +#define CM_RESOURCE_PORT_12_BIT_DECODE 0x0008 +#define CM_RESOURCE_PORT_16_BIT_DECODE 0x0010 +#define CM_RESOURCE_PORT_POSITIVE_DECODE 0x0020 +#define CM_RESOURCE_PORT_PASSIVE_DECODE 0x0040 +#define CM_RESOURCE_PORT_WINDOW_DECODE 0x0080 /* Define the bit masks for Flags when type is CmResourceTypeDma */ -#define CM_RESOURCE_DMA_8 0x0000 -#define CM_RESOURCE_DMA_16 0x0001 -#define CM_RESOURCE_DMA_32 0x0002 -#define CM_RESOURCE_DMA_8_AND_16 0x0004 -#define CM_RESOURCE_DMA_BUS_MASTER 0x0008 -#define CM_RESOURCE_DMA_TYPE_A 0x0010 -#define CM_RESOURCE_DMA_TYPE_B 0x0020 -#define CM_RESOURCE_DMA_TYPE_F 0x0040 +#define CM_RESOURCE_DMA_8 0x0000 +#define CM_RESOURCE_DMA_16 0x0001 +#define CM_RESOURCE_DMA_32 0x0002 +#define CM_RESOURCE_DMA_8_AND_16 0x0004 +#define CM_RESOURCE_DMA_BUS_MASTER 0x0008 +#define CM_RESOURCE_DMA_TYPE_A 0x0010 +#define CM_RESOURCE_DMA_TYPE_B 0x0020 +#define CM_RESOURCE_DMA_TYPE_F 0x0040 struct cm_partial_resource_desc { uint8_t cprd_type; @@ -129,7 +165,7 @@ struct cm_partial_resource_desc { uint32_t cprd_rsvd1; uint32_t cprd_rsvd2; } cprd_devspec; - } u; + } u __attribute__((packed)); }; typedef struct cm_partial_resource_desc cm_partial_resource_desc; @@ -159,3 +195,5 @@ struct cm_resource_list { typedef struct cm_resource_list cm_resource_list; typedef cm_partial_resource_list ndis_resource_list; + +#endif /* _RESOURCE_VAR_H_ */ diff --git a/sys/emulation/ndis/subr_hal.c b/sys/emulation/ndis/subr_hal.c index 5c482668e7..98a9e60428 100644 --- a/sys/emulation/ndis/subr_hal.c +++ b/sys/emulation/ndis/subr_hal.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,8 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/compat/ndis/subr_hal.c,v 1.12 2004/04/19 22:39:04 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/subr_hal.c,v 1.4 2006/12/23 00:27:02 swildner Exp $ + * $FreeBSD: src/sys/compat/ndis/subr_hal.c,v 1.32 2009/11/02 11:07:42 rpaulo Exp $ */ #include @@ -40,140 +39,171 @@ #include #include #include +#include #include +#include +#include #include + #include #include #include -#include - -#include "regcall.h" -#include "pe_var.h" -#include "resource_var.h" -#include "ntoskrnl_var.h" -#include "ndis_var.h" -#include "hal_var.h" +#include +#include +#include +#include +#include -#define FUNC void(*)(void) - -__stdcall static void hal_stall_exec_cpu(uint32_t); -__stdcall static void hal_writeport_buf_ulong(uint32_t *, +static void KeStallExecutionProcessor(uint32_t); +static void WRITE_PORT_BUFFER_ULONG(uint32_t *, uint32_t *, uint32_t); -__stdcall static void hal_writeport_buf_ushort(uint16_t *, +static void WRITE_PORT_BUFFER_USHORT(uint16_t *, uint16_t *, uint32_t); -__stdcall static void hal_writeport_buf_uchar(uint8_t *, +static void WRITE_PORT_BUFFER_UCHAR(uint8_t *, uint8_t *, uint32_t); -__stdcall static void hal_writeport_ulong(uint32_t *, uint32_t); -__stdcall static void hal_writeport_ushort(uint16_t *, uint16_t); -__stdcall static void hal_writeport_uchar(uint8_t *, uint8_t); -__stdcall static uint32_t hal_readport_ulong(uint32_t *); -__stdcall static uint16_t hal_readport_ushort(uint16_t *); -__stdcall static uint8_t hal_readport_uchar(uint8_t *); -__stdcall static void hal_readport_buf_ulong(uint32_t *, +static void WRITE_PORT_ULONG(uint32_t *, uint32_t); +static void WRITE_PORT_USHORT(uint16_t *, uint16_t); +static void WRITE_PORT_UCHAR(uint8_t *, uint8_t); +static uint32_t READ_PORT_ULONG(uint32_t *); +static uint16_t READ_PORT_USHORT(uint16_t *); +static uint8_t READ_PORT_UCHAR(uint8_t *); +static void READ_PORT_BUFFER_ULONG(uint32_t *, uint32_t *, uint32_t); -__stdcall static void hal_readport_buf_ushort(uint16_t *, +static void READ_PORT_BUFFER_USHORT(uint16_t *, uint16_t *, uint32_t); -__stdcall static void hal_readport_buf_uchar(uint8_t *, +static void READ_PORT_BUFFER_UCHAR(uint8_t *, uint8_t *, uint32_t); -__stdcall static uint64_t hal_perfcount(uint64_t *); -__stdcall static void dummy (void); +static uint64_t KeQueryPerformanceCounter(uint64_t *); +static void _KeLowerIrql(uint8_t); +static uint8_t KeRaiseIrqlToDpcLevel(void); +static void dummy (void); + +#define NDIS_MAXCPUS 64 +static struct lock disp_lock[NDIS_MAXCPUS]; + +int +hal_libinit(void) +{ + image_patch_table *patch; + int i; + + for (i = 0; i < NDIS_MAXCPUS; i++) + lockinit(&disp_lock[i], "HAL preemption lock", 0, + LK_CANRECURSE); + + patch = hal_functbl; + while (patch->ipt_func != NULL) { + windrv_wrap((funcptr)patch->ipt_func, + (funcptr *)&patch->ipt_wrap, + patch->ipt_argcnt, patch->ipt_ftype); + patch++; + } + + return (0); +} + +int +hal_libfini(void) +{ + image_patch_table *patch; + int i; + + for (i = 0; i < NDIS_MAXCPUS; i++) + lockuninit(&disp_lock[i]); -extern struct mtx_pool *ndis_mtxpool; + patch = hal_functbl; + while (patch->ipt_func != NULL) { + windrv_unwrap(patch->ipt_wrap); + patch++; + } -__stdcall static void -hal_stall_exec_cpu(uint32_t usecs) + return (0); +} + +static void +KeStallExecutionProcessor(uint32_t usecs) { DELAY(usecs); - return; } -__stdcall static void -hal_writeport_ulong(uint32_t *port, uint32_t val) +static void +WRITE_PORT_ULONG(uint32_t *port, uint32_t val) { bus_space_write_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val); - return; } -__stdcall static void -hal_writeport_ushort(uint16_t *port, uint16_t val) +static void +WRITE_PORT_USHORT(uint16_t *port, uint16_t val) { bus_space_write_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val); - return; } -__stdcall static void -hal_writeport_uchar(uint8_t *port, uint8_t val) +static void +WRITE_PORT_UCHAR(uint8_t *port, uint8_t val) { bus_space_write_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val); - return; } -__stdcall static void -hal_writeport_buf_ulong(uint32_t *port, uint32_t *val, uint32_t cnt) +static void +WRITE_PORT_BUFFER_ULONG(uint32_t *port, uint32_t *val, uint32_t cnt) { bus_space_write_multi_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); - return; } -__stdcall static void -hal_writeport_buf_ushort(uint16_t *port, uint16_t *val, uint32_t cnt) +static void +WRITE_PORT_BUFFER_USHORT(uint16_t *port, uint16_t *val, uint32_t cnt) { bus_space_write_multi_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); - return; } -__stdcall static void -hal_writeport_buf_uchar(uint8_t *port, uint8_t *val, uint32_t cnt) +static void +WRITE_PORT_BUFFER_UCHAR(uint8_t *port, uint8_t *val, uint32_t cnt) { bus_space_write_multi_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); - return; } -__stdcall static uint16_t -hal_readport_ushort(uint16_t *port) +static uint16_t +READ_PORT_USHORT(uint16_t *port) { - return(bus_space_read_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port)); + return (bus_space_read_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port)); } -__stdcall static uint32_t -hal_readport_ulong(uint32_t *port) +static uint32_t +READ_PORT_ULONG(uint32_t *port) { - return(bus_space_read_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port)); + return (bus_space_read_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port)); } -__stdcall static uint8_t -hal_readport_uchar(uint8_t *port) +static uint8_t +READ_PORT_UCHAR(uint8_t *port) { - return(bus_space_read_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port)); + return (bus_space_read_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port)); } -__stdcall static void -hal_readport_buf_ulong(uint32_t *port, uint32_t *val, uint32_t cnt) +static void +READ_PORT_BUFFER_ULONG(uint32_t *port, uint32_t *val, uint32_t cnt) { bus_space_read_multi_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); - return; } -__stdcall static void -hal_readport_buf_ushort(uint16_t *port, uint16_t *val, uint32_t cnt) +static void +READ_PORT_BUFFER_USHORT(uint16_t *port, uint16_t *val, uint32_t cnt) { bus_space_read_multi_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); - return; } -__stdcall static void -hal_readport_buf_uchar(uint8_t *port, uint8_t *val, uint32_t cnt) +static void +READ_PORT_BUFFER_UCHAR(uint8_t *port, uint8_t *val, uint32_t cnt) { bus_space_read_multi_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val, cnt); - return; } /* @@ -196,13 +226,19 @@ hal_readport_buf_uchar(uint8_t *port, uint8_t *val, uint32_t cnt) * until the current thread lowers the IRQL to something less than * DISPATCH_LEVEL. * - * In FreeBSD, ISRs run in interrupt threads, so to duplicate the - * Windows notion of IRQLs, we use the following rules: + * There's another commonly used IRQL in Windows, which is APC_LEVEL. + * An APC is an Asynchronous Procedure Call, which differs from a DPC + * (Defered Procedure Call) in that a DPC is queued up to run in + * another thread, while an APC runs in the thread that scheduled + * it (similar to a signal handler in a UNIX process). We don't + * actually support the notion of APCs in FreeBSD, so for now, the + * only IRQLs we're interested in are DISPATCH_LEVEL and PASSIVE_LEVEL. * - * PASSIVE_LEVEL == normal kernel thread priority - * DISPATCH_LEVEL == lowest interrupt thread priotity (PI_SOFT) - * DEVICE_LEVEL == highest interrupt thread priority (PI_REALTIME) - * HIGH_LEVEL == interrupts disabled (critical_enter()) + * To simulate DISPATCH_LEVEL, we raise the current thread's priority + * to TDPRI_INT_HIGH, which is the highest we can give it. This should, + * if I understand things correctly, prevent anything except for an + * interrupt thread from preempting us. PASSIVE_LEVEL is basically + * everything else. * * Be aware that, at least on the x86 arch, the Windows spinlock * functions are divided up in peculiar ways. The actual spinlock @@ -221,105 +257,189 @@ hal_readport_buf_uchar(uint8_t *port, uint8_t *val, uint32_t cnt) * KeAcquireSpinLock() must be running at IRQL <= DISPATCH_LEVEL. If * we detect someone trying to acquire a spinlock from DEVICE_LEVEL * or HIGH_LEVEL, we panic. + * + * Alternate sleep-lock-based spinlock implementation + * -------------------------------------------------- + * + * The earlier spinlock implementation was arguably a bit of a hack + * and presented several problems. It was basically designed to provide + * the functionality of spinlocks without incurring the wrath of + * WITNESS. We could get away with using both our spinlock implementation + * and FreeBSD sleep locks at the same time, but if WITNESS knew what + * we were really up to, it would have spanked us rather severely. + * + * There's another method we can use based entirely on sleep locks. + * First, it's important to realize that everything we're locking + * resides inside Project Evil itself: any critical data being locked + * by drivers belongs to the drivers, and should not be referenced + * by any other OS code outside of the NDISulator. The priority-based + * locking scheme has system-wide effects, just like real spinlocks + * (blocking preemption affects the whole CPU), but since we keep all + * our critical data private, we can use a simpler mechanism that + * affects only code/threads directly related to Project Evil. + * + * The idea is to create a sleep lock mutex for each CPU in the system. + * When a CPU running in the NDISulator wants to acquire a spinlock, it + * does the following: + * - Pin ourselves to the current CPU + * - Acquire the mutex for the current CPU + * - Spin on the spinlock variable using atomic test and set, just like + * a real spinlock. + * - Once we have the lock, we execute our critical code + * + * To give up the lock, we do: + * - Clear the spinlock variable with an atomic op + * - Release the per-CPU mutex + * - Unpin ourselves from the current CPU. + * + * On a uniprocessor system, this means all threads that access protected + * data are serialized through the per-CPU mutex. After one thread + * acquires the 'spinlock,' any other thread that uses a spinlock on the + * current CPU will block on the per-CPU mutex, which has the same general + * effect of blocking pre-emption, but _only_ for those threads that are + * running NDISulator code. + * + * On a multiprocessor system, threads on different CPUs all block on + * their respective per-CPU mutex, and the atomic test/set operation + * on the spinlock variable provides inter-CPU synchronization, though + * only for threads running NDISulator code. + * + * This method solves an important problem. In Windows, you're allowed + * to do an ExAllocatePoolWithTag() with a spinlock held, provided you + * allocate from NonPagedPool. This implies an atomic heap allocation + * that will not cause the current thread to sleep. (You can't sleep + * while holding real spinlock: clowns will eat you.) But in FreeBSD, + * malloc(9) _always_ triggers the acquisition of a sleep lock, even + * when you use M_NOWAIT. This is not a problem for FreeBSD native + * code: you're allowed to sleep in things like interrupt threads. But + * it is a problem with the old priority-based spinlock implementation: + * even though we get away with it most of the time, we really can't + * do a malloc(9) after doing a KeAcquireSpinLock() or KeRaiseIrql(). + * With the new implementation, it's not a problem: you're allowed to + * acquire more than one sleep lock (as long as you avoid lock order + * reversals). + * + * The one drawback to this approach is that now we have a lot of + * contention on one per-CPU mutex within the NDISulator code. Whether + * or not this is preferable to the expected Windows spinlock behavior + * of blocking pre-emption is debatable. */ -__stdcall __regcall uint8_t -hal_lock(REGARGS1(kspin_lock *lock)) +uint8_t +KfAcquireSpinLock(kspin_lock *lock) { uint8_t oldirql; - /* I am so going to hell for this. */ - if (hal_irql() > DISPATCH_LEVEL) - panic("IRQL_NOT_LESS_THAN_OR_EQUAL"); + KeRaiseIrql(DISPATCH_LEVEL, &oldirql); + KeAcquireSpinLockAtDpcLevel(lock); - oldirql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL); - FASTCALL1(ntoskrnl_lock_dpc, lock); - - return(oldirql); + return (oldirql); } -__stdcall __regcall void -hal_unlock(REGARGS2(kspin_lock *lock, uint8_t newirql)) +void +KfReleaseSpinLock(kspin_lock *lock, uint8_t newirql) { - FASTCALL1(ntoskrnl_unlock_dpc, lock); - FASTCALL1(hal_lower_irql, newirql); - - return; + KeReleaseSpinLockFromDpcLevel(lock); + KeLowerIrql(newirql); } -__stdcall uint8_t -hal_irql(void) +uint8_t +KeGetCurrentIrql(void) { - if (AT_DISPATCH_LEVEL(curthread)) - return(DISPATCH_LEVEL); - return(PASSIVE_LEVEL); + if (lockstatus(&disp_lock[curthread->td_gd->gd_cpuid], curthread)) + return (DISPATCH_LEVEL); + return (PASSIVE_LEVEL); } -__stdcall static uint64_t -hal_perfcount(uint64_t *freq) +static uint64_t +KeQueryPerformanceCounter(uint64_t *freq) { if (freq != NULL) *freq = hz; - return((uint64_t)ticks); + return ((uint64_t)ticks); } -__stdcall __regcall uint8_t -hal_raise_irql(REGARGS1(uint8_t irql)) +uint8_t +KfRaiseIrql(uint8_t irql) { uint8_t oldirql; - if (irql < hal_irql()) - panic("IRQL_NOT_LESS_THAN"); + oldirql = KeGetCurrentIrql(); - if (hal_irql() == DISPATCH_LEVEL) - return(DISPATCH_LEVEL); + /* I am so going to hell for this. */ + if (oldirql > irql) + panic("IRQL_NOT_LESS_THAN"); - oldirql = lwkt_getpri_self(); - lwkt_setpri_self(TDPRI_INT_HIGH); + if (oldirql != DISPATCH_LEVEL) { +#if 0 /* XXX swildner */ + sched_pin(); +#endif + lockmgr(&disp_lock[curthread->td_gd->gd_cpuid], LK_EXCLUSIVE); + } +/*kprintf("RAISE IRQL: %d %d\n", irql, oldirql);*/ - return(oldirql); + return (oldirql); } -__stdcall __regcall void -hal_lower_irql(REGARGS1(uint8_t oldirql)) +void +KfLowerIrql(uint8_t oldirql) { if (oldirql == DISPATCH_LEVEL) return; - if (hal_irql() != DISPATCH_LEVEL) + if (KeGetCurrentIrql() != DISPATCH_LEVEL) panic("IRQL_NOT_GREATER_THAN"); - lwkt_setpri_self(oldirql); + lockmgr(&disp_lock[curthread->td_gd->gd_cpuid], LK_RELEASE); +#if 0 /* XXX swildner */ + sched_unpin(); +#endif +} + +static uint8_t +KeRaiseIrqlToDpcLevel(void) +{ + uint8_t irql; + + KeRaiseIrql(DISPATCH_LEVEL, &irql); + return (irql); +} + +static void +_KeLowerIrql(uint8_t oldirql) +{ + KeLowerIrql(oldirql); } -__stdcall static void dummy(void) { - kprintf ("hal dummy called...\n"); - return; + kprintf("hal dummy called...\n"); } image_patch_table hal_functbl[] = { - { "KeStallExecutionProcessor", (FUNC)hal_stall_exec_cpu }, - { "WRITE_PORT_ULONG", (FUNC)hal_writeport_ulong }, - { "WRITE_PORT_USHORT", (FUNC)hal_writeport_ushort }, - { "WRITE_PORT_UCHAR", (FUNC)hal_writeport_uchar }, - { "WRITE_PORT_BUFFER_ULONG", (FUNC)hal_writeport_buf_ulong }, - { "WRITE_PORT_BUFFER_USHORT", (FUNC)hal_writeport_buf_ushort }, - { "WRITE_PORT_BUFFER_UCHAR", (FUNC)hal_writeport_buf_uchar }, - { "READ_PORT_ULONG", (FUNC)hal_readport_ulong }, - { "READ_PORT_USHORT", (FUNC)hal_readport_ushort }, - { "READ_PORT_UCHAR", (FUNC)hal_readport_uchar }, - { "READ_PORT_BUFFER_ULONG", (FUNC)hal_readport_buf_ulong }, - { "READ_PORT_BUFFER_USHORT", (FUNC)hal_readport_buf_ushort }, - { "READ_PORT_BUFFER_UCHAR", (FUNC)hal_readport_buf_uchar }, - { "KfAcquireSpinLock", (FUNC)hal_lock }, - { "KfReleaseSpinLock", (FUNC)hal_unlock }, - { "KeGetCurrentIrql", (FUNC)hal_irql }, - { "KeQueryPerformanceCounter", (FUNC)hal_perfcount }, - { "KfLowerIrql", (FUNC)hal_lower_irql }, - { "KfRaiseIrql", (FUNC)hal_raise_irql }, + IMPORT_SFUNC(KeStallExecutionProcessor, 1), + IMPORT_SFUNC(WRITE_PORT_ULONG, 2), + IMPORT_SFUNC(WRITE_PORT_USHORT, 2), + IMPORT_SFUNC(WRITE_PORT_UCHAR, 2), + IMPORT_SFUNC(WRITE_PORT_BUFFER_ULONG, 3), + IMPORT_SFUNC(WRITE_PORT_BUFFER_USHORT, 3), + IMPORT_SFUNC(WRITE_PORT_BUFFER_UCHAR, 3), + IMPORT_SFUNC(READ_PORT_ULONG, 1), + IMPORT_SFUNC(READ_PORT_USHORT, 1), + IMPORT_SFUNC(READ_PORT_UCHAR, 1), + IMPORT_SFUNC(READ_PORT_BUFFER_ULONG, 3), + IMPORT_SFUNC(READ_PORT_BUFFER_USHORT, 3), + IMPORT_SFUNC(READ_PORT_BUFFER_UCHAR, 3), + IMPORT_FFUNC(KfAcquireSpinLock, 1), + IMPORT_FFUNC(KfReleaseSpinLock, 1), + IMPORT_SFUNC(KeGetCurrentIrql, 0), + IMPORT_SFUNC(KeQueryPerformanceCounter, 1), + IMPORT_FFUNC(KfLowerIrql, 1), + IMPORT_FFUNC(KfRaiseIrql, 1), + IMPORT_SFUNC(KeRaiseIrqlToDpcLevel, 0), +#undef KeLowerIrql + IMPORT_SFUNC_MAP(KeLowerIrql, _KeLowerIrql, 1), /* * This last entry is a catch-all for any function we haven't @@ -328,9 +448,9 @@ image_patch_table hal_functbl[] = { * in this table. */ - { NULL, (FUNC)dummy }, + { NULL, (FUNC)dummy, NULL, 0, WINDRV_WRAP_STDCALL }, /* End of list. */ - { NULL, NULL }, + { NULL, NULL, NULL } }; diff --git a/sys/emulation/ndis/subr_ndis.c b/sys/emulation/ndis/subr_ndis.c index 0d8ac36944..d9d8cee04a 100644 --- a/sys/emulation/ndis/subr_ndis.c +++ b/sys/emulation/ndis/subr_ndis.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,8 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/compat/ndis/subr_ndis.c,v 1.62 2004/07/11 00:19:30 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/subr_ndis.c,v 1.19 2006/12/23 00:27:02 swildner Exp $ + * $FreeBSD: src/sys/compat/ndis/subr_ndis.c,v 1.125 2010/12/06 20:54:53 bschmidt Exp $ */ /* @@ -48,18 +47,21 @@ * expects. */ + #include #include -#include -#include #include #include #include +#include +#include #include #include +#include #include #include +#include #include #include #include @@ -67,8 +69,9 @@ #include #include #include -#include -#include +#include +#include +#include #include #include @@ -77,203 +80,218 @@ #include #include -#include + +#include +#include +#include #include #include #include #include - -#include "regcall.h" -#include "pe_var.h" -#include "resource_var.h" -#include "ntoskrnl_var.h" -#include "hal_var.h" -#include "ndis_var.h" -#include "cfg_var.h" +#include +#include + +#include +#include +#include +#include +#include +#include #include -#define FUNC void(*)(void) +#include +#include +#include +#include +#include + +#include static char ndis_filepath[MAXPATHLEN]; -extern struct nd_head ndis_devhead; SYSCTL_STRING(_hw, OID_AUTO, ndis_filepath, CTLFLAG_RW, ndis_filepath, MAXPATHLEN, "Path used by NdisOpenFile() to search for files"); -__stdcall static void ndis_initwrap(ndis_handle *, - device_object *, void *, void *); -__stdcall static ndis_status ndis_register_miniport(ndis_handle, +static void NdisInitializeWrapper(ndis_handle *, + driver_object *, void *, void *); +static ndis_status NdisMRegisterMiniport(ndis_handle, ndis_miniport_characteristics *, int); -__stdcall static ndis_status ndis_malloc_withtag(void **, uint32_t, uint32_t); -__stdcall static ndis_status ndis_malloc(void **, +static ndis_status NdisAllocateMemoryWithTag(void **, + uint32_t, uint32_t); +static ndis_status NdisAllocateMemory(void **, uint32_t, uint32_t, ndis_physaddr); -__stdcall static void ndis_free(void *, uint32_t, uint32_t); -__stdcall static ndis_status ndis_setattr_ex(ndis_handle, ndis_handle, +static void NdisFreeMemory(void *, uint32_t, uint32_t); +static ndis_status NdisMSetAttributesEx(ndis_handle, ndis_handle, uint32_t, uint32_t, ndis_interface_type); -__stdcall static void ndis_open_cfg(ndis_status *, ndis_handle *, ndis_handle); -__stdcall static void ndis_open_cfgbyidx(ndis_status *, ndis_handle, - uint32_t, ndis_unicode_string *, ndis_handle *); -__stdcall static void ndis_open_cfgbyname(ndis_status *, ndis_handle, - ndis_unicode_string *, ndis_handle *); +static void NdisOpenConfiguration(ndis_status *, + ndis_handle *, ndis_handle); +static void NdisOpenConfigurationKeyByIndex(ndis_status *, + ndis_handle, uint32_t, unicode_string *, ndis_handle *); +static void NdisOpenConfigurationKeyByName(ndis_status *, + ndis_handle, unicode_string *, ndis_handle *); static ndis_status ndis_encode_parm(ndis_miniport_block *, struct sysctl_oid *, ndis_parm_type, ndis_config_parm **); static ndis_status ndis_decode_parm(ndis_miniport_block *, ndis_config_parm *, char *); -__stdcall static void ndis_read_cfg(ndis_status *, ndis_config_parm **, - ndis_handle, ndis_unicode_string *, ndis_parm_type); -__stdcall static void ndis_write_cfg(ndis_status *, ndis_handle, - ndis_unicode_string *, ndis_config_parm *); -__stdcall static void ndis_close_cfg(ndis_handle); -__stdcall static void ndis_create_lock(ndis_spin_lock *); -__stdcall static void ndis_destroy_lock(ndis_spin_lock *); -__stdcall static void ndis_lock(ndis_spin_lock *); -__stdcall static void ndis_unlock(ndis_spin_lock *); -__stdcall static void ndis_lock_dpr(ndis_spin_lock *); -__stdcall static void ndis_unlock_dpr(ndis_spin_lock *); -__stdcall static uint32_t ndis_read_pci(ndis_handle, uint32_t, +static void NdisReadConfiguration(ndis_status *, ndis_config_parm **, + ndis_handle, unicode_string *, ndis_parm_type); +static void NdisWriteConfiguration(ndis_status *, ndis_handle, + unicode_string *, ndis_config_parm *); +static void NdisCloseConfiguration(ndis_handle); +static void NdisAllocateSpinLock(ndis_spin_lock *); +static void NdisFreeSpinLock(ndis_spin_lock *); +static void NdisAcquireSpinLock(ndis_spin_lock *); +static void NdisReleaseSpinLock(ndis_spin_lock *); +static void NdisDprAcquireSpinLock(ndis_spin_lock *); +static void NdisDprReleaseSpinLock(ndis_spin_lock *); +static void NdisInitializeReadWriteLock(ndis_rw_lock *); +static void NdisAcquireReadWriteLock(ndis_rw_lock *, + uint8_t, ndis_lock_state *); +static void NdisReleaseReadWriteLock(ndis_rw_lock *, ndis_lock_state *); +static uint32_t NdisReadPciSlotInformation(ndis_handle, uint32_t, uint32_t, void *, uint32_t); -__stdcall static uint32_t ndis_write_pci(ndis_handle, uint32_t, +static uint32_t NdisWritePciSlotInformation(ndis_handle, uint32_t, uint32_t, void *, uint32_t); -static void ndis_syslog(ndis_handle, ndis_error_code, uint32_t, ...); +static void NdisWriteErrorLogEntry(ndis_handle, ndis_error_code, uint32_t, ...); static void ndis_map_cb(void *, bus_dma_segment_t *, int, int); -__stdcall static void ndis_vtophys_load(ndis_handle, ndis_buffer *, - uint32_t, uint8_t, ndis_paddr_unit *, uint32_t *); -__stdcall static void ndis_vtophys_unload(ndis_handle, ndis_buffer *, uint32_t); -__stdcall static void ndis_create_timer(ndis_miniport_timer *, ndis_handle, +static void NdisMStartBufferPhysicalMapping(ndis_handle, + ndis_buffer *, uint32_t, uint8_t, ndis_paddr_unit *, uint32_t *); +static void NdisMCompleteBufferPhysicalMapping(ndis_handle, + ndis_buffer *, uint32_t); +static void NdisMInitializeTimer(ndis_miniport_timer *, ndis_handle, ndis_timer_function, void *); -__stdcall static void ndis_init_timer(ndis_timer *, +static void NdisInitializeTimer(ndis_timer *, ndis_timer_function, void *); -__stdcall static void ndis_set_timer(ndis_timer *, uint32_t); -__stdcall static void ndis_set_periodic_timer(ndis_miniport_timer *, uint32_t); -__stdcall static void ndis_cancel_timer(ndis_timer *, uint8_t *); -__stdcall static void ndis_query_resources(ndis_status *, ndis_handle, +static void NdisSetTimer(ndis_timer *, uint32_t); +static void NdisMSetPeriodicTimer(ndis_miniport_timer *, uint32_t); +static void NdisMCancelTimer(ndis_timer *, uint8_t *); +static void ndis_timercall(kdpc *, ndis_miniport_timer *, + void *, void *); +static void NdisMQueryAdapterResources(ndis_status *, ndis_handle, ndis_resource_list *, uint32_t *); -__stdcall static ndis_status ndis_register_ioport(void **, +static ndis_status NdisMRegisterIoPortRange(void **, ndis_handle, uint32_t, uint32_t); -__stdcall static void ndis_deregister_ioport(ndis_handle, +static void NdisMDeregisterIoPortRange(ndis_handle, uint32_t, uint32_t, void *); -__stdcall static void ndis_read_netaddr(ndis_status *, void **, +static void NdisReadNetworkAddress(ndis_status *, void **, uint32_t *, ndis_handle); -__stdcall static ndis_status ndis_mapreg_cnt(uint32_t, uint32_t *); -__stdcall static ndis_status ndis_alloc_mapreg(ndis_handle, +static ndis_status NdisQueryMapRegisterCount(uint32_t, uint32_t *); +static ndis_status NdisMAllocateMapRegisters(ndis_handle, uint32_t, uint8_t, uint32_t, uint32_t); -__stdcall static void ndis_free_mapreg(ndis_handle); +static void NdisMFreeMapRegisters(ndis_handle); static void ndis_mapshared_cb(void *, bus_dma_segment_t *, int, int); -__stdcall static void ndis_alloc_sharedmem(ndis_handle, uint32_t, +static void NdisMAllocateSharedMemory(ndis_handle, uint32_t, uint8_t, void **, ndis_physaddr *); -static void ndis_asyncmem_complete(void *); -__stdcall static ndis_status ndis_alloc_sharedmem_async(ndis_handle, +static void ndis_asyncmem_complete(device_object *, void *); +static ndis_status NdisMAllocateSharedMemoryAsync(ndis_handle, uint32_t, uint8_t, void *); -__stdcall static void ndis_free_sharedmem(ndis_handle, uint32_t, +static void NdisMFreeSharedMemory(ndis_handle, uint32_t, uint8_t, void *, ndis_physaddr); -__stdcall static ndis_status ndis_map_iospace(void **, ndis_handle, +static ndis_status NdisMMapIoSpace(void **, ndis_handle, ndis_physaddr, uint32_t); -__stdcall static void ndis_unmap_iospace(ndis_handle, void *, uint32_t); -__stdcall static uint32_t ndis_cachefill(void); -__stdcall static uint32_t ndis_dma_align(ndis_handle); -__stdcall static ndis_status ndis_init_sc_dma(ndis_handle, +static void NdisMUnmapIoSpace(ndis_handle, void *, uint32_t); +static uint32_t NdisGetCacheFillSize(void); +static void *NdisGetRoutineAddress(unicode_string *); +static uint32_t NdisMGetDmaAlignment(ndis_handle); +static ndis_status NdisMInitializeScatterGatherDma(ndis_handle, uint8_t, uint32_t); -__stdcall static void ndis_alloc_packetpool(ndis_status *, - ndis_handle *, uint32_t, uint32_t); -__stdcall static void ndis_ex_alloc_packetpool(ndis_status *, - ndis_handle *, uint32_t, uint32_t, uint32_t); -__stdcall static uint32_t ndis_packetpool_use(ndis_handle); -__stdcall static void ndis_free_packetpool(ndis_handle); -__stdcall static void ndis_alloc_packet(ndis_status *, - ndis_packet **, ndis_handle); -__stdcall static void ndis_release_packet(ndis_packet *); -__stdcall static void ndis_unchain_headbuf(ndis_packet *, ndis_buffer **); -__stdcall static void ndis_unchain_tailbuf(ndis_packet *, ndis_buffer **); -__stdcall static void ndis_alloc_bufpool(ndis_status *, +static void NdisUnchainBufferAtFront(ndis_packet *, ndis_buffer **); +static void NdisUnchainBufferAtBack(ndis_packet *, ndis_buffer **); +static void NdisAllocateBufferPool(ndis_status *, ndis_handle *, uint32_t); -__stdcall static void ndis_free_bufpool(ndis_handle); -__stdcall static void ndis_alloc_buf(ndis_status *, ndis_buffer **, +static void NdisFreeBufferPool(ndis_handle); +static void NdisAllocateBuffer(ndis_status *, ndis_buffer **, ndis_handle, void *, uint32_t); -__stdcall static void ndis_release_buf(ndis_buffer *); -__stdcall static uint32_t ndis_buflen(ndis_buffer *); -__stdcall static void ndis_query_buf(ndis_buffer *, void **, uint32_t *); -__stdcall static void ndis_query_buf_safe(ndis_buffer *, void **, +static void NdisFreeBuffer(ndis_buffer *); +static uint32_t NdisBufferLength(ndis_buffer *); +static void NdisQueryBuffer(ndis_buffer *, void **, uint32_t *); +static void NdisQueryBufferSafe(ndis_buffer *, void **, uint32_t *, uint32_t); -__stdcall static void *ndis_buf_vaddr(ndis_buffer *); -__stdcall static void *ndis_buf_vaddr_safe(ndis_buffer *, uint32_t); -__stdcall static void ndis_adjust_buflen(ndis_buffer *, int); -__stdcall static uint32_t ndis_interlock_inc(uint32_t *); -__stdcall static uint32_t ndis_interlock_dec(uint32_t *); -__stdcall static void ndis_init_event(ndis_event *); -__stdcall static void ndis_set_event(ndis_event *); -__stdcall static void ndis_reset_event(ndis_event *); -__stdcall static uint8_t ndis_wait_event(ndis_event *, uint32_t); -__stdcall static ndis_status ndis_unicode2ansi(ndis_ansi_string *, - ndis_unicode_string *); -__stdcall static ndis_status ndis_ansi2unicode(ndis_unicode_string *, - ndis_ansi_string *); -__stdcall static ndis_status ndis_assign_pcirsrc(ndis_handle, +static void *NdisBufferVirtualAddress(ndis_buffer *); +static void *NdisBufferVirtualAddressSafe(ndis_buffer *, uint32_t); +static void NdisAdjustBufferLength(ndis_buffer *, int); +static uint32_t NdisInterlockedIncrement(uint32_t *); +static uint32_t NdisInterlockedDecrement(uint32_t *); +static void NdisInitializeEvent(ndis_event *); +static void NdisSetEvent(ndis_event *); +static void NdisResetEvent(ndis_event *); +static uint8_t NdisWaitEvent(ndis_event *, uint32_t); +static ndis_status NdisUnicodeStringToAnsiString(ansi_string *, + unicode_string *); +static ndis_status + NdisAnsiStringToUnicodeString(unicode_string *, ansi_string *); +static ndis_status NdisMPciAssignResources(ndis_handle, uint32_t, ndis_resource_list **); -__stdcall static ndis_status ndis_register_intr(ndis_miniport_interrupt *, +static ndis_status NdisMRegisterInterrupt(ndis_miniport_interrupt *, ndis_handle, uint32_t, uint32_t, uint8_t, uint8_t, ndis_interrupt_mode); -__stdcall static void ndis_deregister_intr(ndis_miniport_interrupt *); -__stdcall static void ndis_register_shutdown(ndis_handle, void *, +static void NdisMDeregisterInterrupt(ndis_miniport_interrupt *); +static void NdisMRegisterAdapterShutdownHandler(ndis_handle, void *, ndis_shutdown_handler); -__stdcall static void ndis_deregister_shutdown(ndis_handle); -__stdcall static uint32_t ndis_numpages(ndis_buffer *); -__stdcall static void ndis_buf_physpages(ndis_buffer *, uint32_t *); -__stdcall static void ndis_query_bufoffset(ndis_buffer *, +static void NdisMDeregisterAdapterShutdownHandler(ndis_handle); +static uint32_t NDIS_BUFFER_TO_SPAN_PAGES(ndis_buffer *); +static void NdisGetBufferPhysicalArraySize(ndis_buffer *, + uint32_t *); +static void NdisQueryBufferOffset(ndis_buffer *, uint32_t *, uint32_t *); -__stdcall static void ndis_sleep(uint32_t); -__stdcall static uint32_t ndis_read_pccard_amem(ndis_handle, +static uint32_t NdisReadPcmciaAttributeMemory(ndis_handle, uint32_t, void *, uint32_t); -__stdcall static uint32_t ndis_write_pccard_amem(ndis_handle, +static uint32_t NdisWritePcmciaAttributeMemory(ndis_handle, uint32_t, void *, uint32_t); -__stdcall static list_entry *ndis_insert_head(list_entry *, +static list_entry *NdisInterlockedInsertHeadList(list_entry *, list_entry *, ndis_spin_lock *); -__stdcall static list_entry *ndis_remove_head(list_entry *, +static list_entry *NdisInterlockedRemoveHeadList(list_entry *, ndis_spin_lock *); -__stdcall static list_entry *ndis_insert_tail(list_entry *, +static list_entry *NdisInterlockedInsertTailList(list_entry *, list_entry *, ndis_spin_lock *); -__stdcall static uint8_t ndis_sync_with_intr(ndis_miniport_interrupt *, +static uint8_t + NdisMSynchronizeWithInterrupt(ndis_miniport_interrupt *, void *, void *); -__stdcall static void ndis_time(uint64_t *); -__stdcall static void ndis_uptime(uint32_t *); -__stdcall static void ndis_init_string(ndis_unicode_string *, char *); -__stdcall static void ndis_init_ansi_string(ndis_ansi_string *, char *); -__stdcall static void ndis_init_unicode_string(ndis_unicode_string *, - uint16_t *); -__stdcall static void ndis_free_string(ndis_unicode_string *); -__stdcall static ndis_status ndis_remove_miniport(ndis_handle *); -__stdcall static void ndis_termwrap(ndis_handle, void *); -__stdcall static void ndis_get_devprop(ndis_handle, device_object **, +static void NdisGetCurrentSystemTime(uint64_t *); +static void NdisGetSystemUpTime(uint32_t *); +static uint32_t NdisGetVersion(void); +static void NdisInitializeString(unicode_string *, char *); +static void NdisInitAnsiString(ansi_string *, char *); +static void NdisInitUnicodeString(unicode_string *, uint16_t *); +static void NdisFreeString(unicode_string *); +static ndis_status NdisMRemoveMiniport(ndis_handle *); +static void NdisTerminateWrapper(ndis_handle, void *); +static void NdisMGetDeviceProperty(ndis_handle, device_object **, device_object **, device_object **, cm_resource_list *, cm_resource_list *); -__stdcall static void ndis_firstbuf(ndis_packet *, ndis_buffer **, - void **, uint32_t *, uint32_t *); -__stdcall static void ndis_firstbuf_safe(ndis_packet *, ndis_buffer **, - void **, uint32_t *, uint32_t *, uint32_t); -__stdcall static void ndis_open_file(ndis_status *, ndis_handle *, uint32_t *, - ndis_unicode_string *, ndis_physaddr); -__stdcall static void ndis_map_file(ndis_status *, void **, ndis_handle); -__stdcall static void ndis_unmap_file(ndis_handle); -__stdcall static void ndis_close_file(ndis_handle); -__stdcall static u_int8_t ndis_cpu_cnt(void); -__stdcall static void ndis_ind_statusdone(ndis_handle); -__stdcall static void ndis_ind_status(ndis_handle, ndis_status, +static void NdisGetFirstBufferFromPacket(ndis_packet *, + ndis_buffer **, void **, uint32_t *, uint32_t *); +static void NdisGetFirstBufferFromPacketSafe(ndis_packet *, + ndis_buffer **, void **, uint32_t *, uint32_t *, uint32_t); +static int ndis_find_sym(linker_file_t, char *, char *, caddr_t *); +static void NdisOpenFile(ndis_status *, ndis_handle *, uint32_t *, + unicode_string *, ndis_physaddr); +static void NdisMapFile(ndis_status *, void **, ndis_handle); +static void NdisUnmapFile(ndis_handle); +static void NdisCloseFile(ndis_handle); +static uint8_t NdisSystemProcessorCount(void); +static void NdisGetCurrentProcessorCounts(uint32_t *, uint32_t *, uint32_t *); +static void NdisMIndicateStatusComplete(ndis_handle); +static void NdisMIndicateStatus(ndis_handle, ndis_status, void *, uint32_t); -static void ndis_workfunc(void *); -__stdcall static ndis_status ndis_sched_workitem(ndis_work_item *); -__stdcall static void ndis_pkt_to_pkt(ndis_packet *, uint32_t, uint32_t, - ndis_packet *, uint32_t, uint32_t *); -__stdcall static void ndis_pkt_to_pkt_safe(ndis_packet *, uint32_t, uint32_t, - ndis_packet *, uint32_t, uint32_t *, uint32_t); -__stdcall static ndis_status ndis_register_dev(ndis_handle, - ndis_unicode_string *, ndis_unicode_string *, driver_dispatch **, +static uint8_t ndis_intr(kinterrupt *, void *); +static void ndis_intrhand(kdpc *, ndis_miniport_interrupt *, void *, void *); +static funcptr ndis_findwrap(funcptr); +static void NdisCopyFromPacketToPacket(ndis_packet *, + uint32_t, uint32_t, ndis_packet *, uint32_t, uint32_t *); +static void NdisCopyFromPacketToPacketSafe(ndis_packet *, + uint32_t, uint32_t, ndis_packet *, uint32_t, uint32_t *, uint32_t); +static void NdisIMCopySendPerPacketInfo(ndis_packet *, ndis_packet *); +static ndis_status NdisMRegisterDevice(ndis_handle, + unicode_string *, unicode_string *, driver_dispatch **, void **, ndis_handle *); -__stdcall static ndis_status ndis_deregister_dev(ndis_handle); -__stdcall static ndis_status ndis_query_name(ndis_unicode_string *, - ndis_handle); -__stdcall static void ndis_register_unload(ndis_handle, void *); -__stdcall static void dummy(void); +static ndis_status NdisMDeregisterDevice(ndis_handle); +static ndis_status + NdisMQueryAdapterInstanceName(unicode_string *, ndis_handle); +static void NdisMRegisterUnloadHandler(ndis_handle, void *); +static void dummy(void); /* * Some really old drivers do not properly check the return value @@ -288,138 +306,166 @@ __stdcall static void dummy(void); int ndis_libinit(void) { + image_patch_table *patch; + strcpy(ndis_filepath, "/compat/ndis"); - return(0); -} -int -ndis_libfini(void) -{ - return(0); -} + patch = ndis_functbl; + while (patch->ipt_func != NULL) { + windrv_wrap((funcptr)patch->ipt_func, + (funcptr *)&patch->ipt_wrap, + patch->ipt_argcnt, patch->ipt_ftype); + patch++; + } -/* - * NDIS deals with strings in unicode format, so we have - * do deal with them that way too. For now, we only handle - * conversion between unicode and ASCII since that's all - * that device drivers care about. - */ + return (0); +} int -ndis_ascii_to_unicode(char *ascii, uint16_t **unicode) +ndis_libfini(void) { - uint16_t *ustr; - int i; + image_patch_table *patch; - if (*unicode == NULL) - *unicode = kmalloc(strlen(ascii) * 2, M_DEVBUF, M_WAITOK); - ustr = *unicode; - for (i = 0; i < strlen(ascii); i++) { - *ustr = (uint16_t)ascii[i]; - ustr++; + patch = ndis_functbl; + while (patch->ipt_func != NULL) { + windrv_unwrap(patch->ipt_wrap); + patch++; } - return(0); + return (0); } -int -ndis_unicode_to_ascii(uint16_t *unicode, int ulen, char **ascii) +static funcptr +ndis_findwrap(funcptr func) { - uint8_t *astr; - int i; + image_patch_table *patch; - if (*ascii == NULL) - *ascii = kmalloc((ulen / 2) + 1, M_DEVBUF, M_WAITOK|M_ZERO); - astr = *ascii; - for (i = 0; i < ulen / 2; i++) { - *astr = (uint8_t)unicode[i]; - astr++; + patch = ndis_functbl; + while (patch->ipt_func != NULL) { + if ((funcptr)patch->ipt_func == func) + return((funcptr)patch->ipt_wrap); + patch++; } - return(0); + return (NULL); } -__stdcall static void -ndis_initwrap(ndis_handle *wrapper, device_object *drv_obj, void *path, - void *unused) +/* + * This routine does the messy Windows Driver Model device attachment + * stuff on behalf of NDIS drivers. We register our own AddDevice + * routine here + */ +static void +NdisInitializeWrapper(ndis_handle *wrapper, driver_object *drv, void *path, + void *unused) { - ndis_miniport_block *block; + /* + * As of yet, I haven't come up with a compelling + * reason to define a private NDIS wrapper structure, + * so we use a pointer to the driver object as the + * wrapper handle. The driver object has the miniport + * characteristics struct for this driver hung off it + * via IoAllocateDriverObjectExtension(), and that's + * really all the private data we need. + */ - block = drv_obj->do_rsvd; - *wrapper = block; + *wrapper = drv; - return; + /* + * If this was really Windows, we'd be registering dispatch + * routines for the NDIS miniport module here, but we're + * not Windows so all we really need to do is set up an + * AddDevice function that'll be invoked when a new device + * instance appears. + */ + + drv->dro_driverext->dre_adddevicefunc = NdisAddDevice; } -__stdcall static void -ndis_termwrap(ndis_handle handle, void *syspec) +static void +NdisTerminateWrapper(ndis_handle handle, void *syspec) { - return; + /* Nothing to see here, move along. */ } -__stdcall static ndis_status -ndis_register_miniport(ndis_handle handle, - ndis_miniport_characteristics *characteristics, - int len) +static ndis_status +NdisMRegisterMiniport(ndis_handle handle, + ndis_miniport_characteristics *characteristics, int len) { - ndis_miniport_block *block; - struct ndis_softc *sc; + ndis_miniport_characteristics *ch = NULL; + driver_object *drv; - block = (ndis_miniport_block *)handle; - sc = (struct ndis_softc *)block->nmb_ifp; - bcopy((char *)characteristics, (char *)&sc->ndis_chars, - sizeof(ndis_miniport_characteristics)); - if (sc->ndis_chars.nmc_version_major < 5 || - sc->ndis_chars.nmc_version_minor < 1) { - sc->ndis_chars.nmc_shutdown_handler = NULL; - sc->ndis_chars.nmc_canceltxpkts_handler = NULL; - sc->ndis_chars.nmc_pnpevent_handler = NULL; + drv = (driver_object *)handle; + + /* + * We need to save the NDIS miniport characteristics + * somewhere. This data is per-driver, not per-device + * (all devices handled by the same driver have the + * same characteristics) so we hook it onto the driver + * object using IoAllocateDriverObjectExtension(). + * The extra extension info is automagically deleted when + * the driver is unloaded (see windrv_unload()). + */ + + if (IoAllocateDriverObjectExtension(drv, (void *)1, + sizeof(ndis_miniport_characteristics), (void **)&ch) != + STATUS_SUCCESS) { + return (NDIS_STATUS_RESOURCES); + } + + bzero((char *)ch, sizeof(ndis_miniport_characteristics)); + + bcopy((char *)characteristics, (char *)ch, len); + + if (ch->nmc_version_major < 5 || ch->nmc_version_minor < 1) { + ch->nmc_shutdown_handler = NULL; + ch->nmc_canceltxpkts_handler = NULL; + ch->nmc_pnpevent_handler = NULL; } - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static ndis_status -ndis_malloc_withtag(void **vaddr, uint32_t len, uint32_t tag) +static ndis_status +NdisAllocateMemoryWithTag(void **vaddr, uint32_t len, uint32_t tag) { void *mem; - mem = kmalloc(len, M_DEVBUF, M_INTWAIT|M_NULLOK); - if (mem == NULL) - return(NDIS_STATUS_RESOURCES); + mem = ExAllocatePoolWithTag(NonPagedPool, len, tag); + if (mem == NULL) { + return (NDIS_STATUS_RESOURCES); + } *vaddr = mem; - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static ndis_status -ndis_malloc(void **vaddr, uint32_t len, uint32_t flags, - ndis_physaddr highaddr) +static ndis_status +NdisAllocateMemory(void **vaddr, uint32_t len, uint32_t flags, + ndis_physaddr highaddr) { void *mem; - mem = kmalloc(len, M_DEVBUF, M_INTWAIT|M_NULLOK); + mem = ExAllocatePoolWithTag(NonPagedPool, len, 0); if (mem == NULL) - return(NDIS_STATUS_RESOURCES); + return (NDIS_STATUS_RESOURCES); *vaddr = mem; - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_free(void *vaddr, uint32_t len, uint32_t flags) +static void +NdisFreeMemory(void *vaddr, uint32_t len, uint32_t flags) { if (len == 0) return; - kfree(vaddr, M_DEVBUF); - return; + ExFreePool(vaddr); } -__stdcall static ndis_status -ndis_setattr_ex(ndis_handle adapter_handle, ndis_handle adapter_ctx, - uint32_t hangsecs, uint32_t flags, - ndis_interface_type iftype) +static ndis_status +NdisMSetAttributesEx(ndis_handle adapter_handle, ndis_handle adapter_ctx, + uint32_t hangsecs, uint32_t flags, ndis_interface_type iftype) { ndis_miniport_block *block; @@ -432,143 +478,139 @@ ndis_setattr_ex(ndis_handle adapter_handle, ndis_handle adapter_ctx, block->nmb_checkforhangsecs = hangsecs; block->nmb_flags = flags; - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_open_cfg(ndis_status *status, ndis_handle *cfg, ndis_handle wrapctx) +static void +NdisOpenConfiguration(ndis_status *status, ndis_handle *cfg, + ndis_handle wrapctx) { *cfg = wrapctx; *status = NDIS_STATUS_SUCCESS; - return; } -__stdcall static void -ndis_open_cfgbyname(ndis_status *status, ndis_handle cfg, - ndis_unicode_string *subkey, ndis_handle *subhandle) +static void +NdisOpenConfigurationKeyByName(ndis_status *status, ndis_handle cfg, + unicode_string *subkey, ndis_handle *subhandle) { *subhandle = cfg; *status = NDIS_STATUS_SUCCESS; - return; } -__stdcall static void -ndis_open_cfgbyidx(ndis_status *status, ndis_handle cfg, uint32_t idx, - ndis_unicode_string *subkey, ndis_handle *subhandle) +static void +NdisOpenConfigurationKeyByIndex(ndis_status *status, ndis_handle cfg, + uint32_t idx, unicode_string *subkey, ndis_handle *subhandle) { *status = NDIS_STATUS_FAILURE; - return; } static ndis_status ndis_encode_parm(ndis_miniport_block *block, struct sysctl_oid *oid, - ndis_parm_type type, ndis_config_parm **parm) + ndis_parm_type type, ndis_config_parm **parm) { - uint16_t *unicode; - ndis_unicode_string *ustr; + ndis_config_parm *p; + ndis_parmlist_entry *np; + unicode_string *us; + ansi_string as; int base = 0; + uint32_t val; + char tmp[32]; - unicode = (uint16_t *)&block->nmb_dummybuf; + np = ExAllocatePoolWithTag(NonPagedPool, + sizeof(ndis_parmlist_entry), 0); + if (np == NULL) + return (NDIS_STATUS_RESOURCES); + InsertHeadList((&block->nmb_parmlist), (&np->np_list)); + *parm = p = &np->np_parm; switch(type) { case ndis_parm_string: - ndis_ascii_to_unicode((char *)oid->oid_arg1, &unicode); - (*parm)->ncp_type = ndis_parm_string; - ustr = &(*parm)->ncp_parmdata.ncp_stringdata; - ustr->nus_len = strlen((char *)oid->oid_arg1) * 2; - ustr->nus_buf = unicode; + /* See if this might be a number. */ + val = strtoul((char *)oid->oid_arg1, NULL, 10); + us = &p->ncp_parmdata.ncp_stringdata; + p->ncp_type = ndis_parm_string; + if (val) { + ksnprintf(tmp, 32, "%x", val); + RtlInitAnsiString(&as, tmp); + } else { + RtlInitAnsiString(&as, (char *)oid->oid_arg1); + } + + if (RtlAnsiStringToUnicodeString(us, &as, TRUE)) { + ExFreePool(np); + return (NDIS_STATUS_RESOURCES); + } break; case ndis_parm_int: if (strncmp((char *)oid->oid_arg1, "0x", 2) == 0) base = 16; else base = 10; - (*parm)->ncp_type = ndis_parm_int; - (*parm)->ncp_parmdata.ncp_intdata = + p->ncp_type = ndis_parm_int; + p->ncp_parmdata.ncp_intdata = strtol((char *)oid->oid_arg1, NULL, base); break; case ndis_parm_hexint: +#ifdef notdef if (strncmp((char *)oid->oid_arg1, "0x", 2) == 0) base = 16; else base = 10; - (*parm)->ncp_type = ndis_parm_hexint; - (*parm)->ncp_parmdata.ncp_intdata = +#endif + base = 16; + p->ncp_type = ndis_parm_hexint; + p->ncp_parmdata.ncp_intdata = strtoul((char *)oid->oid_arg1, NULL, base); break; default: - return(NDIS_STATUS_FAILURE); + return (NDIS_STATUS_FAILURE); break; } - return(NDIS_STATUS_SUCCESS); -} - -int -ndis_strcasecmp(const char *s1, const char *s2) -{ - char a, b; - - /* - * In the kernel, toupper() is a macro. Have to be careful - * not to use pointer arithmetic when passing it arguments. - */ - - while(1) { - a = *s1; - b = *s2++; - if (toupper(a) != toupper(b)) - break; - if (*s1++ == 0) - return(0); - } - - return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1)); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_read_cfg(ndis_status *status, ndis_config_parm **parm, ndis_handle cfg, - ndis_unicode_string *key, ndis_parm_type type) +static void +NdisReadConfiguration(ndis_status *status, ndis_config_parm **parm, + ndis_handle cfg, unicode_string *key, ndis_parm_type type) { char *keystr = NULL; - uint16_t *unicode; ndis_miniport_block *block; struct ndis_softc *sc; struct sysctl_oid *oidp; struct sysctl_ctx_entry *e; + ansi_string as; block = (ndis_miniport_block *)cfg; - sc = (struct ndis_softc *)block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); - if (key->nus_len == 0 || key->nus_buf == NULL) { + if (key->us_len == 0 || key->us_buf == NULL) { *status = NDIS_STATUS_FAILURE; return; } - ndis_unicode_to_ascii(key->nus_buf, key->nus_len, &keystr); + if (RtlUnicodeStringToAnsiString(&as, key, TRUE)) { + *status = NDIS_STATUS_RESOURCES; + return; + } - *parm = &block->nmb_replyparm; - bzero((char *)&block->nmb_replyparm, sizeof(ndis_config_parm)); - unicode = (uint16_t *)&block->nmb_dummybuf; + keystr = as.as_buf; /* * See if registry key is already in a list of known keys * included with the driver. */ -#if __FreeBSD_version < 502113 TAILQ_FOREACH(e, &sc->ndis_ctx, link) { -#else - TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) { -#endif oidp = e->entry; - if (ndis_strcasecmp(oidp->oid_name, keystr) == 0) { + if (strcasecmp(oidp->oid_name, keystr) == 0) { if (strcmp((char *)oidp->oid_arg1, "UNSET") == 0) { - kfree(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); *status = NDIS_STATUS_FAILURE; return; } + *status = ndis_encode_parm(block, oidp, type, parm); - kfree(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); return; } } @@ -592,24 +634,23 @@ ndis_read_cfg(ndis_status *status, ndis_config_parm **parm, ndis_handle cfg, ndis_add_sysctl(sc, keystr, "(dynamic string key)", "UNSET", CTLFLAG_RW); - kfree(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); *status = NDIS_STATUS_FAILURE; - return; } static ndis_status -ndis_decode_parm(ndis_miniport_block *block, ndis_config_parm *parm, - char *val) +ndis_decode_parm(ndis_miniport_block *block, ndis_config_parm *parm, char *val) { - ndis_unicode_string *ustr; - char *astr = NULL; + unicode_string *ustr; + ansi_string as; switch(parm->ncp_type) { case ndis_parm_string: ustr = &parm->ncp_parmdata.ncp_stringdata; - ndis_unicode_to_ascii(ustr->nus_buf, ustr->nus_len, &astr); - bcopy(astr, val, 254); - kfree(astr, M_DEVBUF); + if (RtlUnicodeStringToAnsiString(&as, ustr, TRUE)) + return (NDIS_STATUS_RESOURCES); + bcopy(as.as_buf, val, as.as_len); + RtlFreeAnsiString(&as); break; case ndis_parm_int: ksprintf(val, "%d", parm->ncp_parmdata.ncp_intdata); @@ -618,16 +659,17 @@ ndis_decode_parm(ndis_miniport_block *block, ndis_config_parm *parm, ksprintf(val, "%xu", parm->ncp_parmdata.ncp_intdata); break; default: - return(NDIS_STATUS_FAILURE); + return (NDIS_STATUS_FAILURE); break; } - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_write_cfg(ndis_status *status, ndis_handle cfg, ndis_unicode_string *key, - ndis_config_parm *parm) +static void +NdisWriteConfiguration(ndis_status *status, ndis_handle cfg, + unicode_string *key, ndis_config_parm *parm) { + ansi_string as; char *keystr = NULL; ndis_miniport_block *block; struct ndis_softc *sc; @@ -636,30 +678,31 @@ ndis_write_cfg(ndis_status *status, ndis_handle cfg, ndis_unicode_string *key, char val[256]; block = (ndis_miniport_block *)cfg; - sc = (struct ndis_softc *)block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + + if (RtlUnicodeStringToAnsiString(&as, key, TRUE)) { + *status = NDIS_STATUS_RESOURCES; + return; + } - ndis_unicode_to_ascii(key->nus_buf, key->nus_len, &keystr); + keystr = as.as_buf; /* Decode the parameter into a string. */ bzero(val, sizeof(val)); *status = ndis_decode_parm(block, parm, val); if (*status != NDIS_STATUS_SUCCESS) { - kfree(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); return; } /* See if the key already exists. */ -#if __FreeBSD_version < 502113 TAILQ_FOREACH(e, &sc->ndis_ctx, link) { -#else - TAILQ_FOREACH(e, device_get_sysctl_ctx(sc->ndis_dev), link) { -#endif oidp = e->entry; - if (ndis_strcasecmp(oidp->oid_name, keystr) == 0) { + if (strcasecmp(oidp->oid_name, keystr) == 0) { /* Found it, set the value. */ strcpy((char *)oidp->oid_arg1, val); - kfree(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); return; } } @@ -668,27 +711,38 @@ ndis_write_cfg(ndis_status *status, ndis_handle cfg, ndis_unicode_string *key, ndis_add_sysctl(sc, keystr, "(dynamically set key)", val, CTLFLAG_RW); - kfree(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); *status = NDIS_STATUS_SUCCESS; - return; } -__stdcall static void -ndis_close_cfg(ndis_handle cfg) +static void +NdisCloseConfiguration(ndis_handle cfg) { - return; + list_entry *e; + ndis_parmlist_entry *pe; + ndis_miniport_block *block; + ndis_config_parm *p; + + block = (ndis_miniport_block *)cfg; + + while (!IsListEmpty(&block->nmb_parmlist)) { + e = RemoveHeadList(&block->nmb_parmlist); + pe = CONTAINING_RECORD(e, ndis_parmlist_entry, np_list); + p = &pe->np_parm; + if (p->ncp_type == ndis_parm_string) + RtlFreeUnicodeString(&p->ncp_parmdata.ncp_stringdata); + ExFreePool(e); + } } /* * Initialize a Windows spinlock. */ -__stdcall static void -ndis_create_lock(ndis_spin_lock *lock) +static void +NdisAllocateSpinLock(ndis_spin_lock *lock) { - lock->nsl_spinlock = 0; + KeInitializeSpinLock(&lock->nsl_spinlock); lock->nsl_kirql = 0; - - return; } /* @@ -699,135 +753,201 @@ ndis_create_lock(ndis_spin_lock *lock) * the block of memory in which the spinlock resides. (Yes, ADMtek, I'm * talking to you.) */ -__stdcall static void -ndis_destroy_lock(ndis_spin_lock *lock) +static void +NdisFreeSpinLock(ndis_spin_lock *lock) { #ifdef notdef - lock->nsl_spinlock = 0; + KeInitializeSpinLock(&lock->nsl_spinlock); lock->nsl_kirql = 0; #endif - return; } /* * Acquire a spinlock from IRQL <= DISPATCH_LEVEL. */ -__stdcall static void -ndis_lock(ndis_spin_lock *lock) +static void +NdisAcquireSpinLock(ndis_spin_lock *lock) { - lock->nsl_kirql = FASTCALL2(hal_lock, - &lock->nsl_spinlock, DISPATCH_LEVEL); - return; + KeAcquireSpinLock(&lock->nsl_spinlock, &lock->nsl_kirql); } /* * Release a spinlock from IRQL == DISPATCH_LEVEL. */ -__stdcall static void -ndis_unlock(ndis_spin_lock *lock) +static void +NdisReleaseSpinLock(ndis_spin_lock *lock) { - FASTCALL2(hal_unlock, &lock->nsl_spinlock, lock->nsl_kirql); - return; + KeReleaseSpinLock(&lock->nsl_spinlock, lock->nsl_kirql); } /* * Acquire a spinlock when already running at IRQL == DISPATCH_LEVEL. */ -__stdcall static void -ndis_lock_dpr(ndis_spin_lock *lock) +static void +NdisDprAcquireSpinLock(ndis_spin_lock *lock) { - FASTCALL1(ntoskrnl_lock_dpc, &lock->nsl_spinlock); - return; + KeAcquireSpinLockAtDpcLevel(&lock->nsl_spinlock); } /* * Release a spinlock without leaving IRQL == DISPATCH_LEVEL. */ -__stdcall static void -ndis_unlock_dpr(ndis_spin_lock *lock) +static void +NdisDprReleaseSpinLock(ndis_spin_lock *lock) { - FASTCALL1(ntoskrnl_unlock_dpc, &lock->nsl_spinlock); - return; + KeReleaseSpinLockFromDpcLevel(&lock->nsl_spinlock); } -__stdcall static uint32_t -ndis_read_pci(ndis_handle adapter, uint32_t slot, uint32_t offset, - void *buf, uint32_t len) +static void +NdisInitializeReadWriteLock(ndis_rw_lock *lock) +{ + KeInitializeSpinLock(&lock->nrl_spinlock); + bzero((char *)&lock->nrl_rsvd, sizeof(lock->nrl_rsvd)); +} + +static void +NdisAcquireReadWriteLock(ndis_rw_lock *lock, uint8_t writeacc, + ndis_lock_state *state) +{ + if (writeacc == TRUE) { + KeAcquireSpinLock(&lock->nrl_spinlock, &state->nls_oldirql); + lock->nrl_rsvd[0]++; + } else + lock->nrl_rsvd[1]++; +} + +static void +NdisReleaseReadWriteLock(ndis_rw_lock *lock, ndis_lock_state *state) +{ + if (lock->nrl_rsvd[0]) { + lock->nrl_rsvd[0]--; + KeReleaseSpinLock(&lock->nrl_spinlock, state->nls_oldirql); + } else + lock->nrl_rsvd[1]--; +} + +static uint32_t +NdisReadPciSlotInformation(ndis_handle adapter, uint32_t slot, uint32_t offset, + void *buf, uint32_t len) { ndis_miniport_block *block; int i; char *dest; + device_t dev; block = (ndis_miniport_block *)adapter; dest = buf; - if (block == NULL || block->nmb_dev == NULL) - return(0); + if (block == NULL) + return (0); - for (i = 0; i < len; i++) - dest[i] = pci_read_config(block->nmb_dev, i + offset, 1); + dev = block->nmb_physdeviceobj->do_devext; + + /* + * I have a test system consisting of a Sun w2100z + * dual 2.4Ghz Opteron machine and an Atheros 802.11a/b/g + * "Aries" miniPCI NIC. (The NIC is installed in the + * machine using a miniPCI to PCI bus adapter card.) + * When running in SMP mode, I found that + * performing a large number of consecutive calls to + * NdisReadPciSlotInformation() would result in a + * sudden system reset (or in some cases a freeze). + * My suspicion is that the multiple reads are somehow + * triggering a fatal PCI bus error that leads to a + * machine check. The 1us delay in the loop below + * seems to prevent this problem. + */ - return(len); + for (i = 0; i < len; i++) { + DELAY(1); + dest[i] = pci_read_config(dev, i + offset, 1); + } + + return (len); } -__stdcall static uint32_t -ndis_write_pci(ndis_handle adapter, uint32_t slot, uint32_t offset, - void *buf, uint32_t len) +static uint32_t +NdisWritePciSlotInformation(ndis_handle adapter, uint32_t slot, + uint32_t offset, void *buf, uint32_t len) { ndis_miniport_block *block; int i; char *dest; + device_t dev; block = (ndis_miniport_block *)adapter; dest = buf; - if (block == NULL || block->nmb_dev == NULL) - return(0); + if (block == NULL) + return (0); - for (i = 0; i < len; i++) - pci_write_config(block->nmb_dev, i + offset, dest[i], 1); + dev = block->nmb_physdeviceobj->do_devext; + for (i = 0; i < len; i++) { + DELAY(1); + pci_write_config(dev, i + offset, dest[i], 1); + } - return(len); + return (len); } /* * The errorlog routine uses a variable argument list, so we * have to declare it this way. */ + #define ERRMSGLEN 512 static void -ndis_syslog(ndis_handle adapter, ndis_error_code code, +NdisWriteErrorLogEntry(ndis_handle adapter, ndis_error_code code, uint32_t numerrors, ...) { ndis_miniport_block *block; - __va_list ap; + va_list ap; int i, error; - char *str = NULL, *ustr = NULL; + char *str = NULL; uint16_t flags; - char msgbuf[ERRMSGLEN]; - + device_t dev; + driver_object *drv; + struct ndis_softc *sc; + struct ifnet *ifp; + unicode_string us; + ansi_string as = { 0, 0, NULL }; block = (ndis_miniport_block *)adapter; - - error = pe_get_message(block->nmb_img, code, &str, &i, &flags); - if (error == 0 && flags & MESSAGE_RESOURCE_UNICODE) { - ustr = msgbuf; - ndis_unicode_to_ascii((uint16_t *)str, - ((i / 2)) > (ERRMSGLEN - 1) ? ERRMSGLEN : i, &ustr); - str = ustr; + dev = block->nmb_physdeviceobj->do_devext; + drv = block->nmb_deviceobj->do_drvobj; + sc = device_get_softc(dev); + ifp = sc->ifp; + + if (ifp != NULL && ifp->if_flags & IFF_DEBUG) { + error = pe_get_message((vm_offset_t)drv->dro_driverstart, + code, &str, &i, &flags); + if (error == 0) { + if (flags & MESSAGE_RESOURCE_UNICODE) { + RtlInitUnicodeString(&us, (uint16_t *)str); + if (RtlUnicodeStringToAnsiString(&as, + &us, TRUE) == STATUS_SUCCESS) + str = as.as_buf; + else + str = NULL; + } + } } - device_printf (block->nmb_dev, "NDIS ERROR: %x (%s)\n", code, + + device_printf(dev, "NDIS ERROR: %x (%s)\n", code, str == NULL ? "unknown error" : str); - device_printf (block->nmb_dev, "NDIS NUMERRORS: %x\n", numerrors); - __va_start(ap, numerrors); - for (i = 0; i < numerrors; i++) - device_printf (block->nmb_dev, "argptr: %p\n", - __va_arg(ap, void *)); - __va_end(ap); + if (ifp != NULL && ifp->if_flags & IFF_DEBUG) { + device_printf(dev, "NDIS NUMERRORS: %x\n", numerrors); + va_start(ap, numerrors); + for (i = 0; i < numerrors; i++) + device_printf(dev, "argptr: %p\n", + va_arg(ap, void *)); + va_end(ap); + } - return; + if (as.as_len) + RtlFreeAnsiString(&as); } static void @@ -847,14 +967,12 @@ ndis_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) } ctx->nma_cnt = nseg; - - return; } -__stdcall static void -ndis_vtophys_load(ndis_handle adapter, ndis_buffer *buf, uint32_t mapreg, - uint8_t writedev, ndis_paddr_unit *addrarray, - uint32_t *arraysize) +static void +NdisMStartBufferPhysicalMapping(ndis_handle adapter, ndis_buffer *buf, + uint32_t mapreg, uint8_t writedev, ndis_paddr_unit *addrarray, + uint32_t *arraysize) { ndis_miniport_block *block; struct ndis_softc *sc; @@ -866,7 +984,7 @@ ndis_vtophys_load(ndis_handle adapter, ndis_buffer *buf, uint32_t mapreg, return; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)(block->nmb_ifp); + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); if (mapreg > sc->ndis_mmapcnt) return; @@ -875,7 +993,7 @@ ndis_vtophys_load(ndis_handle adapter, ndis_buffer *buf, uint32_t mapreg, nma.nma_fraglist = addrarray; error = bus_dmamap_load(sc->ndis_mtag, map, - MDL_VA(buf), buf->nb_bytecount, ndis_map_cb, + MmGetMdlVirtualAddress(buf), MmGetMdlByteCount(buf), ndis_map_cb, (void *)&nma, BUS_DMA_NOWAIT); if (error) @@ -885,13 +1003,11 @@ ndis_vtophys_load(ndis_handle adapter, ndis_buffer *buf, uint32_t mapreg, writedev ? BUS_DMASYNC_PREWRITE : BUS_DMASYNC_PREREAD); *arraysize = nma.nma_cnt; - - return; } -__stdcall static void -ndis_vtophys_unload(ndis_handle adapter, ndis_buffer *buf, - uint32_t mapreg) +static void +NdisMCompleteBufferPhysicalMapping(ndis_handle adapter, ndis_buffer *buf, + uint32_t mapreg) { ndis_miniport_block *block; struct ndis_softc *sc; @@ -901,7 +1017,7 @@ ndis_vtophys_unload(ndis_handle adapter, ndis_buffer *buf, return; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)(block->nmb_ifp); + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); if (mapreg > sc->ndis_mmapcnt) return; @@ -912,98 +1028,133 @@ ndis_vtophys_unload(ndis_handle adapter, ndis_buffer *buf, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->ndis_mtag, map); - - return; } /* - * This is an older pre-miniport timer init routine which doesn't - * accept a miniport context handle. The function context (ctx) - * is supposed to be a pointer to the adapter handle, which should - * have been handed to us via NdisSetAttributesEx(). We use this - * function context to track down the corresponding ndis_miniport_block - * structure. It's vital that we track down the miniport block structure, - * so if we can't do it, we panic. Note that we also play some games - * here by treating ndis_timer and ndis_miniport_timer as the same - * thing. + * This is an older (?) timer init routine which doesn't + * accept a miniport context handle. Serialized miniports should + * never call this function. */ -__stdcall static void -ndis_init_timer(ndis_timer *timer, ndis_timer_function func, void *ctx) +static void +NdisInitializeTimer(ndis_timer *timer, ndis_timer_function func, void *ctx) +{ + KeInitializeTimer(&timer->nt_ktimer); + KeInitializeDpc(&timer->nt_kdpc, func, ctx); + KeSetImportanceDpc(&timer->nt_kdpc, KDPC_IMPORTANCE_LOW); +} + +static void +ndis_timercall(kdpc *dpc, ndis_miniport_timer *timer, void *sysarg1, + void *sysarg2) { - ntoskrnl_init_timer(&timer->nt_ktimer); - ntoskrnl_init_dpc(&timer->nt_kdpc, func, ctx); + /* + * Since we're called as a DPC, we should be running + * at DISPATCH_LEVEL here. This means to acquire the + * spinlock, we can use KeAcquireSpinLockAtDpcLevel() + * rather than KeAcquireSpinLock(). + */ + if (NDIS_SERIALIZED(timer->nmt_block)) + KeAcquireSpinLockAtDpcLevel(&timer->nmt_block->nmb_lock); - return; + MSCALL4(timer->nmt_timerfunc, dpc, timer->nmt_timerctx, + sysarg1, sysarg2); + + if (NDIS_SERIALIZED(timer->nmt_block)) + KeReleaseSpinLockFromDpcLevel(&timer->nmt_block->nmb_lock); } -__stdcall static void -ndis_create_timer(ndis_miniport_timer *timer, ndis_handle handle, - ndis_timer_function func, void *ctx) +/* + * For a long time I wondered why there were two NDIS timer initialization + * routines, and why this one needed an NDIS_MINIPORT_TIMER and the + * MiniportAdapterHandle. The NDIS_MINIPORT_TIMER has its own callout + * function and context pointers separate from those in the DPC, which + * allows for another level of indirection: when the timer fires, we + * can have our own timer function invoked, and from there we can call + * the driver's function. But why go to all that trouble? Then it hit + * me: for serialized miniports, the timer callouts are not re-entrant. + * By trapping the callouts and having access to the MiniportAdapterHandle, + * we can protect the driver callouts by acquiring the NDIS serialization + * lock. This is essential for allowing serialized miniports to work + * correctly on SMP systems. On UP hosts, setting IRQL to DISPATCH_LEVEL + * is enough to prevent other threads from pre-empting you, but with + * SMP, you must acquire a lock as well, otherwise the other CPU is + * free to clobber you. + */ +static void +NdisMInitializeTimer(ndis_miniport_timer *timer, ndis_handle handle, + ndis_timer_function func, void *ctx) { - /* Save the funcptr and context */ + ndis_miniport_block *block; + struct ndis_softc *sc; + + block = (ndis_miniport_block *)handle; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + + /* Save the driver's funcptr and context */ timer->nmt_timerfunc = func; timer->nmt_timerctx = ctx; timer->nmt_block = handle; - ntoskrnl_init_timer(&timer->nmt_ktimer); - ntoskrnl_init_dpc(&timer->nmt_kdpc, func, ctx); - - return; + /* + * Set up the timer so it will call our intermediate DPC. + * Be sure to use the wrapped entry point, since + * ntoskrnl_run_dpc() expects to invoke a function with + * Microsoft calling conventions. + */ + KeInitializeTimer(&timer->nmt_ktimer); + KeInitializeDpc(&timer->nmt_kdpc, + ndis_findwrap((funcptr)ndis_timercall), timer); + timer->nmt_ktimer.k_dpc = &timer->nmt_kdpc; } /* * In Windows, there's both an NdisMSetTimer() and an NdisSetTimer(), * but the former is just a macro wrapper around the latter. */ -__stdcall static void -ndis_set_timer(ndis_timer *timer, uint32_t msecs) +static void +NdisSetTimer(ndis_timer *timer, uint32_t msecs) { /* * KeSetTimer() wants the period in * hundred nanosecond intervals. */ - ntoskrnl_set_timer(&timer->nt_ktimer, + KeSetTimer(&timer->nt_ktimer, ((int64_t)msecs * -10000), &timer->nt_kdpc); - - return; } -__stdcall static void -ndis_set_periodic_timer(ndis_miniport_timer *timer, uint32_t msecs) +static void +NdisMSetPeriodicTimer(ndis_miniport_timer *timer, uint32_t msecs) { - ntoskrnl_set_timer_ex(&timer->nmt_ktimer, + KeSetTimerEx(&timer->nmt_ktimer, ((int64_t)msecs * -10000), msecs, &timer->nmt_kdpc); - - return; } /* * Technically, this is really NdisCancelTimer(), but we also * (ab)use it for NdisMCancelTimer(), since in our implementation * we don't need the extra info in the ndis_miniport_timer - * structure. + * structure just to cancel a timer. */ -__stdcall static void -ndis_cancel_timer(ndis_timer *timer, uint8_t *cancelled) +static void +NdisMCancelTimer(ndis_timer *timer, uint8_t *cancelled) { - *cancelled = ntoskrnl_cancel_timer(&timer->nt_ktimer); - return; + *cancelled = KeCancelTimer(&timer->nt_ktimer); } -__stdcall static void -ndis_query_resources(ndis_status *status, ndis_handle adapter, - ndis_resource_list *list, uint32_t *buflen) +static void +NdisMQueryAdapterResources(ndis_status *status, ndis_handle adapter, + ndis_resource_list *list, uint32_t *buflen) { ndis_miniport_block *block; struct ndis_softc *sc; int rsclen; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); rsclen = sizeof(ndis_resource_list) + (sizeof(cm_partial_resource_desc) * (sc->ndis_rescnt - 1)); @@ -1015,86 +1166,87 @@ ndis_query_resources(ndis_status *status, ndis_handle adapter, bcopy((char *)block->nmb_rlist, (char *)list, rsclen); *status = NDIS_STATUS_SUCCESS; - return; } -__stdcall static ndis_status -ndis_register_ioport(void **offset, ndis_handle adapter, - uint32_t port, uint32_t numports) +static ndis_status +NdisMRegisterIoPortRange(void **offset, ndis_handle adapter, uint32_t port, + uint32_t numports) { struct ndis_miniport_block *block; struct ndis_softc *sc; if (adapter == NULL) - return(NDIS_STATUS_FAILURE); + return (NDIS_STATUS_FAILURE); block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)(block->nmb_ifp); + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); if (sc->ndis_res_io == NULL) - return(NDIS_STATUS_FAILURE); + return (NDIS_STATUS_FAILURE); /* Don't let the device map more ports than we have. */ if (rman_get_size(sc->ndis_res_io) < numports) - return(NDIS_STATUS_INVALID_LENGTH); + return (NDIS_STATUS_INVALID_LENGTH); *offset = (void *)rman_get_start(sc->ndis_res_io); - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_deregister_ioport(ndis_handle adapter, uint32_t port, - uint32_t numports, void *offset) +static void +NdisMDeregisterIoPortRange(ndis_handle adapter, uint32_t port, + uint32_t numports, void *offset) { - return; } -__stdcall static void -ndis_read_netaddr(ndis_status *status, void **addr, - uint32_t *addrlen, ndis_handle adapter) +static void +NdisReadNetworkAddress(ndis_status *status, void **addr, uint32_t *addrlen, + ndis_handle adapter) { struct ndis_softc *sc; ndis_miniport_block *block; uint8_t empty[] = { 0, 0, 0, 0, 0, 0 }; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + if (sc->ifp == NULL) { + *status = NDIS_STATUS_FAILURE; + return; + } - if (bcmp(sc->arpcom.ac_enaddr, empty, ETHER_ADDR_LEN) == 0) + if (sc->ifp->if_lladdr == NULL || + bcmp(IF_LLADDR(sc->ifp), empty, ETHER_ADDR_LEN) == 0) *status = NDIS_STATUS_FAILURE; else { - *addr = sc->arpcom.ac_enaddr; + *addr = IF_LLADDR(sc->ifp); *addrlen = ETHER_ADDR_LEN; *status = NDIS_STATUS_SUCCESS; } - - return; } -__stdcall static ndis_status -ndis_mapreg_cnt(uint32_t bustype, uint32_t *cnt) +static ndis_status +NdisQueryMapRegisterCount(uint32_t bustype, uint32_t *cnt) { *cnt = 8192; - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static ndis_status -ndis_alloc_mapreg(ndis_handle adapter, uint32_t dmachannel, uint8_t dmasize, - uint32_t physmapneeded, uint32_t maxmap) +static ndis_status +NdisMAllocateMapRegisters(ndis_handle adapter, uint32_t dmachannel, + uint8_t dmasize, uint32_t physmapneeded, uint32_t maxmap) { struct ndis_softc *sc; ndis_miniport_block *block; int error, i, nseg = NDIS_MAXSEG; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); sc->ndis_mmaps = kmalloc(sizeof(bus_dmamap_t) * physmapneeded, - M_DEVBUF, M_INTWAIT|M_ZERO); + M_DEVBUF, M_NOWAIT|M_ZERO); if (sc->ndis_mmaps == NULL) - return(NDIS_STATUS_RESOURCES); + return (NDIS_STATUS_RESOURCES); error = bus_dma_tag_create(sc->ndis_parent_tag, ETHER_ALIGN, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, @@ -1103,7 +1255,7 @@ ndis_alloc_mapreg(ndis_handle adapter, uint32_t dmachannel, uint8_t dmasize, if (error) { kfree(sc->ndis_mmaps, M_DEVBUF); - return(NDIS_STATUS_RESOURCES); + return (NDIS_STATUS_RESOURCES); } for (i = 0; i < physmapneeded; i++) @@ -1111,18 +1263,18 @@ ndis_alloc_mapreg(ndis_handle adapter, uint32_t dmachannel, uint8_t dmasize, sc->ndis_mmapcnt = physmapneeded; - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_free_mapreg(ndis_handle adapter) +static void +NdisMFreeMapRegisters(ndis_handle adapter) { struct ndis_softc *sc; ndis_miniport_block *block; int i; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); for (i = 0; i < sc->ndis_mmapcnt; i++) bus_dmamap_destroy(sc->ndis_mtag, sc->ndis_mmaps[i]); @@ -1130,8 +1282,6 @@ ndis_free_mapreg(ndis_handle adapter) kfree(sc->ndis_mmaps, M_DEVBUF); bus_dma_tag_destroy(sc->ndis_mtag); - - return; } static void @@ -1145,16 +1295,15 @@ ndis_mapshared_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) p = arg; p->np_quad = segs[0].ds_addr; - - return; } /* * This maps to bus_dmamem_alloc(). */ -__stdcall static void -ndis_alloc_sharedmem(ndis_handle adapter, uint32_t len, uint8_t cached, - void **vaddr, ndis_physaddr *paddr) + +static void +NdisMAllocateSharedMemory(ndis_handle adapter, uint32_t len, uint8_t cached, + void **vaddr, ndis_physaddr *paddr) { ndis_miniport_block *block; struct ndis_softc *sc; @@ -1165,12 +1314,14 @@ ndis_alloc_sharedmem(ndis_handle adapter, uint32_t len, uint8_t cached, return; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)(block->nmb_ifp); + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); - sh = kmalloc(sizeof(struct ndis_shmem), M_DEVBUF, M_INTWAIT|M_ZERO); + sh = kmalloc(sizeof(struct ndis_shmem), M_DEVBUF, M_NOWAIT|M_ZERO); if (sh == NULL) return; + InitializeListHead(&sh->ndis_list); + /* * When performing shared memory allocations, create a tag * with a lowaddr limit that restricts physical memory mappings @@ -1178,17 +1329,16 @@ ndis_alloc_sharedmem(ndis_handle adapter, uint32_t len, uint8_t cached, * At least one device/driver combination (Linksys Instant * Wireless PCI Card V2.7, Broadcom 802.11b) seems to have * problems with performing DMA operations with physical - * that lie above the 1GB mark. I don't know if this is a - * hardware limitation or if the addresses are being truncated - * within the driver, but this seems to be the only way to - * make these cards work reliably in systems with more than - * 1GB of physical memory. + * addresses that lie above the 1GB mark. I don't know if this + * is a hardware limitation or if the addresses are being + * truncated within the driver, but this seems to be the only + * way to make these cards work reliably in systems with more + * than 1GB of physical memory. */ error = bus_dma_tag_create(sc->ndis_parent_tag, 64, 0, NDIS_BUS_SPACE_SHARED_MAXADDR, BUS_SPACE_MAXADDR, NULL, - NULL, len, 1, len, BUS_DMA_ALLOCNOW, - &sh->ndis_stag); + NULL, len, 1, len, BUS_DMA_ALLOCNOW, &sh->ndis_stag); if (error) { kfree(sh, M_DEVBUF); @@ -1214,22 +1364,31 @@ ndis_alloc_sharedmem(ndis_handle adapter, uint32_t len, uint8_t cached, return; } - sh->ndis_saddr = *vaddr; - sh->ndis_next = sc->ndis_shlist; - sc->ndis_shlist = sh; + /* + * Save the physical address along with the source address. + * The AirGo MIMO driver will call NdisMFreeSharedMemory() + * with a bogus virtual address sometimes, but with a valid + * physical address. To keep this from causing trouble, we + * use the physical address to as a sanity check in case + * searching based on the virtual address fails. + */ - return; + NDIS_LOCK(sc); + sh->ndis_paddr.np_quad = paddr->np_quad; + sh->ndis_saddr = *vaddr; + InsertHeadList((&sc->ndis_shlist), (&sh->ndis_list)); + NDIS_UNLOCK(sc); } struct ndis_allocwork { - ndis_handle na_adapter; uint32_t na_len; uint8_t na_cached; void *na_ctx; + io_workitem *na_iw; }; static void -ndis_asyncmem_complete(void *arg) +ndis_asyncmem_complete(device_object *dobj, void *arg) { ndis_miniport_block *block; struct ndis_softc *sc; @@ -1239,133 +1398,150 @@ ndis_asyncmem_complete(void *arg) ndis_allocdone_handler donefunc; w = arg; - block = (ndis_miniport_block *)w->na_adapter; - sc = (struct ndis_softc *)(block->nmb_ifp); + block = (ndis_miniport_block *)dobj->do_devext; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); vaddr = NULL; paddr.np_quad = 0; - donefunc = sc->ndis_chars.nmc_allocate_complete_func; - ndis_alloc_sharedmem(w->na_adapter, w->na_len, + donefunc = sc->ndis_chars->nmc_allocate_complete_func; + NdisMAllocateSharedMemory(block, w->na_len, w->na_cached, &vaddr, &paddr); - donefunc(w->na_adapter, vaddr, &paddr, w->na_len, w->na_ctx); + MSCALL5(donefunc, block, vaddr, &paddr, w->na_len, w->na_ctx); - kfree(arg, M_DEVBUF); - - return; + IoFreeWorkItem(w->na_iw); + kfree(w, M_DEVBUF); } -__stdcall static ndis_status -ndis_alloc_sharedmem_async(ndis_handle adapter, uint32_t len, - uint8_t cached, void *ctx) +static ndis_status +NdisMAllocateSharedMemoryAsync(ndis_handle adapter, uint32_t len, + uint8_t cached, void *ctx) { + ndis_miniport_block *block; struct ndis_allocwork *w; + io_workitem *iw; + io_workitem_func ifw; if (adapter == NULL) - return(NDIS_STATUS_FAILURE); + return (NDIS_STATUS_FAILURE); + + block = adapter; + + iw = IoAllocateWorkItem(block->nmb_deviceobj); + if (iw == NULL) + return (NDIS_STATUS_FAILURE); - w = kmalloc(sizeof(struct ndis_allocwork), M_TEMP, M_INTWAIT); + w = kmalloc(sizeof(struct ndis_allocwork), M_TEMP, M_NOWAIT); if (w == NULL) - return(NDIS_STATUS_FAILURE); + return (NDIS_STATUS_FAILURE); - w->na_adapter = adapter; w->na_cached = cached; w->na_len = len; w->na_ctx = ctx; + w->na_iw = iw; - /* - * Pawn this work off on the SWI thread instead of the - * taskqueue thread, because sometimes drivers will queue - * up work items on the taskqueue thread that will block, - * which would prevent the memory allocation from completing - * when we need it. - */ - ndis_sched(ndis_asyncmem_complete, w, NDIS_SWI); + ifw = (io_workitem_func)ndis_findwrap((funcptr)ndis_asyncmem_complete); + IoQueueWorkItem(iw, ifw, WORKQUEUE_DELAYED, w); - return(NDIS_STATUS_PENDING); + return (NDIS_STATUS_PENDING); } -__stdcall static void -ndis_free_sharedmem(ndis_handle adapter, uint32_t len, uint8_t cached, - void *vaddr, ndis_physaddr paddr) +static void +NdisMFreeSharedMemory(ndis_handle adapter, uint32_t len, uint8_t cached, + void *vaddr, ndis_physaddr paddr) { ndis_miniport_block *block; struct ndis_softc *sc; - struct ndis_shmem *sh, *prev; + struct ndis_shmem *sh = NULL; + list_entry *l; if (vaddr == NULL || adapter == NULL) return; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)(block->nmb_ifp); - sh = prev = sc->ndis_shlist; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + + /* Sanity check: is list empty? */ - while (sh) { + if (IsListEmpty(&sc->ndis_shlist)) + return; + + NDIS_LOCK(sc); + l = sc->ndis_shlist.nle_flink; + while (l != &sc->ndis_shlist) { + sh = CONTAINING_RECORD(l, struct ndis_shmem, ndis_list); if (sh->ndis_saddr == vaddr) break; - prev = sh; - sh = sh->ndis_next; + /* + * Check the physaddr too, just in case the driver lied + * about the virtual address. + */ + if (sh->ndis_paddr.np_quad == paddr.np_quad) + break; + l = l->nle_flink; + } + + if (sh == NULL) { + NDIS_UNLOCK(sc); + kprintf("NDIS: buggy driver tried to free " + "invalid shared memory: vaddr: %p paddr: 0x%jx\n", + vaddr, (uintmax_t)paddr.np_quad); + return; } + RemoveEntryList(&sh->ndis_list); + + NDIS_UNLOCK(sc); + bus_dmamap_unload(sh->ndis_stag, sh->ndis_smap); - bus_dmamem_free(sh->ndis_stag, vaddr, sh->ndis_smap); + bus_dmamem_free(sh->ndis_stag, sh->ndis_saddr, sh->ndis_smap); bus_dma_tag_destroy(sh->ndis_stag); - if (sh == sc->ndis_shlist) - sc->ndis_shlist = sh->ndis_next; - else - prev->ndis_next = sh->ndis_next; - kfree(sh, M_DEVBUF); - - return; } -__stdcall static ndis_status -ndis_map_iospace(void **vaddr, ndis_handle adapter, ndis_physaddr paddr, - uint32_t len) +static ndis_status +NdisMMapIoSpace(void **vaddr, ndis_handle adapter, ndis_physaddr paddr, + uint32_t len) { - ndis_miniport_block *block; - struct ndis_softc *sc; - if (adapter == NULL) - return(NDIS_STATUS_FAILURE); + return (NDIS_STATUS_FAILURE); - block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)(block->nmb_ifp); - - if (sc->ndis_res_mem != NULL && - paddr.np_quad == rman_get_start(sc->ndis_res_mem)) - *vaddr = (void *)rman_get_virtual(sc->ndis_res_mem); - else if (sc->ndis_res_altmem != NULL && - paddr.np_quad == rman_get_start(sc->ndis_res_altmem)) - *vaddr = (void *)rman_get_virtual(sc->ndis_res_altmem); - else if (sc->ndis_res_am != NULL && - paddr.np_quad == rman_get_start(sc->ndis_res_am)) - *vaddr = (void *)rman_get_virtual(sc->ndis_res_am); - else - return(NDIS_STATUS_FAILURE); + *vaddr = MmMapIoSpace(paddr.np_quad, len, 0); + + if (*vaddr == NULL) + return (NDIS_STATUS_FAILURE); + + return (NDIS_STATUS_SUCCESS); +} - return(NDIS_STATUS_SUCCESS); +static void +NdisMUnmapIoSpace(ndis_handle adapter, void *vaddr, uint32_t len) +{ + MmUnmapIoSpace(vaddr, len); } -__stdcall static void -ndis_unmap_iospace(ndis_handle adapter, void *vaddr, uint32_t len) +static uint32_t +NdisGetCacheFillSize(void) { - return; + return (128); } -__stdcall static uint32_t -ndis_cachefill(void) +static void * +NdisGetRoutineAddress(unicode_string *ustr) { - return(128); + ansi_string astr; + + if (RtlUnicodeStringToAnsiString(&astr, ustr, TRUE)) + return (NULL); + return (ndis_get_routine_address(ndis_functbl, astr.as_buf)); } -__stdcall static uint32_t -ndis_dma_align(ndis_handle handle) +static uint32_t +NdisMGetDmaAlignment(ndis_handle handle) { - return(128); + return (16); } /* @@ -1379,21 +1555,22 @@ ndis_dma_align(ndis_handle handle) * method. */ -__stdcall static ndis_status -ndis_init_sc_dma(ndis_handle adapter, uint8_t is64, uint32_t maxphysmap) +static ndis_status +NdisMInitializeScatterGatherDma(ndis_handle adapter, uint8_t is64, + uint32_t maxphysmap) { struct ndis_softc *sc; ndis_miniport_block *block; int error; if (adapter == NULL) - return(NDIS_STATUS_FAILURE); + return (NDIS_STATUS_FAILURE); block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); /* Don't do this twice. */ if (sc->ndis_sc == 1) - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); error = bus_dma_tag_create(sc->ndis_parent_tag, ETHER_ALIGN, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, @@ -1402,153 +1579,184 @@ ndis_init_sc_dma(ndis_handle adapter, uint8_t is64, uint32_t maxphysmap) sc->ndis_sc = 1; - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_alloc_packetpool(ndis_status *status, ndis_handle *pool, - uint32_t descnum, uint32_t protrsvdlen) +void +NdisAllocatePacketPool(ndis_status *status, ndis_handle *pool, + uint32_t descnum, uint32_t protrsvdlen) { - ndis_packet *cur; + ndis_packet_pool *p; + ndis_packet *packets; int i; - *pool = kmalloc(sizeof(ndis_packet) * - ((descnum + NDIS_POOL_EXTRA) + 1), - M_DEVBUF, M_WAITOK|M_ZERO); - cur = (ndis_packet *)*pool; - cur->np_private.npp_flags = 0x1; /* mark the head of the list */ - cur->np_private.npp_totlen = 0; /* init deletetion flag */ - for (i = 0; i < (descnum + NDIS_POOL_EXTRA); i++) { - cur->np_private.npp_head = (ndis_handle)(cur + 1); - cur++; + p = ExAllocatePoolWithTag(NonPagedPool, sizeof(ndis_packet_pool), 0); + if (p == NULL) { + *status = NDIS_STATUS_RESOURCES; + return; + } + + p->np_cnt = descnum + NDIS_POOL_EXTRA; + p->np_protrsvd = protrsvdlen; + p->np_len = sizeof(ndis_packet) + protrsvdlen; + + packets = ExAllocatePoolWithTag(NonPagedPool, p->np_cnt * + p->np_len, 0); + + + if (packets == NULL) { + ExFreePool(p); + *status = NDIS_STATUS_RESOURCES; + return; } + p->np_pktmem = packets; + + for (i = 0; i < p->np_cnt; i++) + InterlockedPushEntrySList(&p->np_head, + (struct slist_entry *)&packets[i]); + +#ifdef NDIS_DEBUG_PACKETS + p->np_dead = 0; + KeInitializeSpinLock(&p->np_lock); + KeInitializeEvent(&p->np_event, EVENT_TYPE_NOTIFY, TRUE); +#endif + + *pool = p; *status = NDIS_STATUS_SUCCESS; - return; } -__stdcall static void -ndis_ex_alloc_packetpool(ndis_status *status, ndis_handle *pool, - uint32_t descnum, uint32_t oflowdescnum, - uint32_t protrsvdlen) +void +NdisAllocatePacketPoolEx(ndis_status *status, ndis_handle *pool, + uint32_t descnum, uint32_t oflowdescnum, uint32_t protrsvdlen) { - return(ndis_alloc_packetpool(status, pool, + return (NdisAllocatePacketPool(status, pool, descnum + oflowdescnum, protrsvdlen)); } -__stdcall static uint32_t -ndis_packetpool_use(ndis_handle pool) +uint32_t +NdisPacketPoolUsage(ndis_handle pool) { - ndis_packet *head; - - head = (ndis_packet *)pool; + ndis_packet_pool *p; - return(head->np_private.npp_count); + p = (ndis_packet_pool *)pool; + return (p->np_cnt - ExQueryDepthSList(&p->np_head)); } -__stdcall static void -ndis_free_packetpool(ndis_handle pool) +void +NdisFreePacketPool(ndis_handle pool) { - ndis_packet *head; - - head = pool; + ndis_packet_pool *p; + int usage; +#ifdef NDIS_DEBUG_PACKETS + uint8_t irql; +#endif - /* Mark this pool as 'going away.' */ + p = (ndis_packet_pool *)pool; - head->np_private.npp_totlen = 1; +#ifdef NDIS_DEBUG_PACKETS + KeAcquireSpinLock(&p->np_lock, &irql); +#endif - /* If there are no buffers loaned out, destroy the pool. */ + usage = NdisPacketPoolUsage(pool); - if (head->np_private.npp_count == 0) - kfree(pool, M_DEVBUF); - else - kprintf("NDIS: buggy driver deleting active packet pool!\n"); +#ifdef NDIS_DEBUG_PACKETS + if (usage) { + p->np_dead = 1; + KeResetEvent(&p->np_event); + KeReleaseSpinLock(&p->np_lock, irql); + KeWaitForSingleObject(&p->np_event, 0, 0, FALSE, NULL); + } else + KeReleaseSpinLock(&p->np_lock, irql); +#endif - return; + ExFreePool(p->np_pktmem); + ExFreePool(p); } -__stdcall static void -ndis_alloc_packet(ndis_status *status, ndis_packet **packet, ndis_handle pool) +void +NdisAllocatePacket(ndis_status *status, ndis_packet **packet, ndis_handle pool) { - ndis_packet *head, *pkt; + ndis_packet_pool *p; + ndis_packet *pkt; +#ifdef NDIS_DEBUG_PACKETS + uint8_t irql; +#endif - head = (ndis_packet *)pool; + p = (ndis_packet_pool *)pool; - if (head->np_private.npp_flags != 0x1) { - *status = NDIS_STATUS_FAILURE; +#ifdef NDIS_DEBUG_PACKETS + KeAcquireSpinLock(&p->np_lock, &irql); + if (p->np_dead) { + KeReleaseSpinLock(&p->np_lock, irql); + kprintf("NDIS: tried to allocate packet from dead pool %p\n", + pool); + *status = NDIS_STATUS_RESOURCES; return; } +#endif - /* - * If this pool is marked as 'going away' don't allocate any - * more packets out of it. - */ - - if (head->np_private.npp_totlen) { - *status = NDIS_STATUS_FAILURE; - return; - } + pkt = (ndis_packet *)InterlockedPopEntrySList(&p->np_head); - pkt = (ndis_packet *)head->np_private.npp_head; +#ifdef NDIS_DEBUG_PACKETS + KeReleaseSpinLock(&p->np_lock, irql); +#endif if (pkt == NULL) { *status = NDIS_STATUS_RESOURCES; return; } - head->np_private.npp_head = pkt->np_private.npp_head; - pkt->np_private.npp_head = pkt->np_private.npp_tail = NULL; + bzero((char *)pkt, sizeof(ndis_packet)); + /* Save pointer to the pool. */ - pkt->np_private.npp_pool = head; + pkt->np_private.npp_pool = pool; /* Set the oob offset pointer. Lots of things expect this. */ - pkt->np_private.npp_packetooboffset = - offsetof(ndis_packet, np_oob); + pkt->np_private.npp_packetooboffset = offsetof(ndis_packet, np_oob); /* * We must initialize the packet flags correctly in order * for the NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO() and - * NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO() to work correctly. + * NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO() macros to work + * correctly. */ pkt->np_private.npp_ndispktflags = NDIS_PACKET_ALLOCATED_BY_NDIS; + pkt->np_private.npp_validcounts = FALSE; *packet = pkt; - head->np_private.npp_count++; *status = NDIS_STATUS_SUCCESS; - return; } -__stdcall static void -ndis_release_packet(ndis_packet *packet) +void +NdisFreePacket(ndis_packet *packet) { - ndis_packet *head; - - if (packet == NULL || packet->np_private.npp_pool == NULL) - return; - - head = packet->np_private.npp_pool; - if (head->np_private.npp_flags != 0x1) - return; + ndis_packet_pool *p; +#ifdef NDIS_DEBUG_PACKETS + uint8_t irql; +#endif - packet->np_private.npp_head = head->np_private.npp_head; - head->np_private.npp_head = (ndis_buffer *)packet; - head->np_private.npp_count--; + p = (ndis_packet_pool *)packet->np_private.npp_pool; - /* - * If the pool has been marked for deletion and there are - * no more packets outstanding, nuke the pool. - */ +#ifdef NDIS_DEBUG_PACKETS + KeAcquireSpinLock(&p->np_lock, &irql); +#endif - if (head->np_private.npp_totlen && head->np_private.npp_count == 0) - kfree(head, M_DEVBUF); + InterlockedPushEntrySList(&p->np_head, (slist_entry *)packet); - return; +#ifdef NDIS_DEBUG_PACKETS + if (p->np_dead) { + if (ExQueryDepthSList(&p->np_head) == p->np_cnt) + KeSetEvent(&p->np_event, IO_NO_INCREMENT, FALSE); + } + KeReleaseSpinLock(&p->np_lock, irql); +#endif } -__stdcall static void -ndis_unchain_headbuf(ndis_packet *packet, ndis_buffer **buf) +static void +NdisUnchainBufferAtFront(ndis_packet *packet, ndis_buffer **buf) { ndis_packet_private *priv; @@ -1564,14 +1772,12 @@ ndis_unchain_headbuf(ndis_packet *packet, ndis_buffer **buf) priv->npp_head = priv->npp_tail = NULL; } else { *buf = priv->npp_head; - priv->npp_head = (*buf)->nb_next; + priv->npp_head = (*buf)->mdl_next; } - - return; } -__stdcall static void -ndis_unchain_tailbuf(ndis_packet *packet, ndis_buffer **buf) +static void +NdisUnchainBufferAtBack(ndis_packet *packet, ndis_buffer **buf) { ndis_packet_private *priv; ndis_buffer *tmp; @@ -1589,153 +1795,76 @@ ndis_unchain_tailbuf(ndis_packet *packet, ndis_buffer **buf) } else { *buf = priv->npp_tail; tmp = priv->npp_head; - while (tmp->nb_next != priv->npp_tail) - tmp = tmp->nb_next; + while (tmp->mdl_next != priv->npp_tail) + tmp = tmp->mdl_next; priv->npp_tail = tmp; - tmp->nb_next = NULL; + tmp->mdl_next = NULL; } - - return; } /* - * The NDIS "buffer" manipulation functions are somewhat misnamed. - * They don't really allocate buffers: they allocate buffer mappings. - * The idea is you reserve a chunk of DMA-able memory using - * NdisMAllocateSharedMemory() and then use NdisAllocateBuffer() - * to obtain the virtual address of the DMA-able region. - * ndis_alloc_bufpool() is analagous to bus_dma_tag_create(). + * The NDIS "buffer" is really an MDL (memory descriptor list) + * which is used to describe a buffer in a way that allows it + * to mapped into different contexts. We have to be careful how + * we handle them: in some versions of Windows, the NdisFreeBuffer() + * routine is an actual function in the NDIS API, but in others + * it's just a macro wrapper around IoFreeMdl(). There's really + * no way to use the 'descnum' parameter to count how many + * "buffers" are allocated since in order to use IoFreeMdl() to + * dispose of a buffer, we have to use IoAllocateMdl() to allocate + * them, and IoAllocateMdl() just grabs them out of the heap. */ -__stdcall static void -ndis_alloc_bufpool(ndis_status *status, ndis_handle *pool, - uint32_t descnum) +static void +NdisAllocateBufferPool(ndis_status *status, ndis_handle *pool, + uint32_t descnum) { - ndis_buffer *cur; - int i; - *pool = kmalloc(sizeof(ndis_buffer) * - ((descnum + NDIS_POOL_EXTRA) + 1), - M_DEVBUF, M_WAITOK|M_ZERO); - cur = (ndis_buffer *)*pool; - cur->nb_flags = 0x1; /* mark the head of the list */ - cur->nb_bytecount = 0; /* init usage count */ - cur->nb_byteoffset = 0; /* init deletetion flag */ - for (i = 0; i < (descnum + NDIS_POOL_EXTRA); i++) { - cur->nb_next = cur + 1; - cur++; - } + /* + * The only thing we can really do here is verify that descnum + * is a reasonable value, but I really don't know what to check + * it against. + */ + *pool = NonPagedPool; *status = NDIS_STATUS_SUCCESS; - return; } -__stdcall static void -ndis_free_bufpool(ndis_handle pool) +static void +NdisFreeBufferPool(ndis_handle pool) { - ndis_buffer *head; - - head = pool; - - /* Mark this pool as 'going away.' */ - - head->nb_byteoffset = 1; - - /* If there are no buffers loaned out, destroy the pool. */ - if (head->nb_bytecount == 0) - kfree(pool, M_DEVBUF); - else - kprintf("NDIS: buggy driver deleting active buffer pool!\n"); - - return; } -/* - * This maps to a bus_dmamap_create() and bus_dmamap_load(). - */ -__stdcall static void -ndis_alloc_buf(ndis_status *status, ndis_buffer **buffer, ndis_handle pool, - void *vaddr, uint32_t len) +static void +NdisAllocateBuffer(ndis_status *status, ndis_buffer **buffer, ndis_handle pool, + void *vaddr, uint32_t len) { - ndis_buffer *head, *buf; - - head = (ndis_buffer *)pool; - if (head->nb_flags != 0x1) { - *status = NDIS_STATUS_FAILURE; - return; - } - - /* - * If this pool is marked as 'going away' don't allocate any - * more buffers out of it. - */ - - if (head->nb_byteoffset) { - *status = NDIS_STATUS_FAILURE; - return; - } - - buf = head->nb_next; + ndis_buffer *buf; + buf = IoAllocateMdl(vaddr, len, FALSE, FALSE, NULL); if (buf == NULL) { *status = NDIS_STATUS_RESOURCES; return; } - head->nb_next = buf->nb_next; - - /* Save pointer to the pool. */ - buf->nb_process = head; - - MDL_INIT(buf, vaddr, len); + MmBuildMdlForNonPagedPool(buf); *buffer = buf; - - /* Increment count of busy buffers. */ - - head->nb_bytecount++; - *status = NDIS_STATUS_SUCCESS; - return; } -__stdcall static void -ndis_release_buf(ndis_buffer *buf) +static void +NdisFreeBuffer(ndis_buffer *buf) { - ndis_buffer *head; - - if (buf == NULL || buf->nb_process == NULL) - return; - - head = buf->nb_process; - - if (head->nb_flags != 0x1) - return; - - buf->nb_next = head->nb_next; - head->nb_next = buf; - - /* Decrement count of busy buffers. */ - - head->nb_bytecount--; - - /* - * If the pool has been marked for deletion and there are - * no more buffers outstanding, nuke the pool. - */ - - if (head->nb_byteoffset && head->nb_bytecount == 0) - kfree(head, M_DEVBUF); - - return; + IoFreeMdl(buf); } /* Aw c'mon. */ -__stdcall static uint32_t -ndis_buflen(ndis_buffer *buf) +static uint32_t +NdisBufferLength(ndis_buffer *buf) { - return(buf->nb_bytecount); + return (MmGetMdlByteCount(buf)); } /* @@ -1743,142 +1872,137 @@ ndis_buflen(ndis_buffer *buf) * Note: the vaddr argument is optional. */ -__stdcall static void -ndis_query_buf(ndis_buffer *buf, void **vaddr, uint32_t *len) +static void +NdisQueryBuffer(ndis_buffer *buf, void **vaddr, uint32_t *len) { if (vaddr != NULL) - *vaddr = MDL_VA(buf); - *len = buf->nb_bytecount; - - return; + *vaddr = MmGetMdlVirtualAddress(buf); + *len = MmGetMdlByteCount(buf); } /* Same as above -- we don't care about the priority. */ -__stdcall static void -ndis_query_buf_safe(ndis_buffer *buf, void **vaddr, - uint32_t *len, uint32_t prio) +static void +NdisQueryBufferSafe(ndis_buffer *buf, void **vaddr, uint32_t *len, + uint32_t prio) { if (vaddr != NULL) - *vaddr = MDL_VA(buf); - *len = buf->nb_bytecount; - - return; + *vaddr = MmGetMdlVirtualAddress(buf); + *len = MmGetMdlByteCount(buf); } /* Damnit Microsoft!! How many ways can you do the same thing?! */ -__stdcall static void * -ndis_buf_vaddr(ndis_buffer *buf) +static void * +NdisBufferVirtualAddress(ndis_buffer *buf) { - return(MDL_VA(buf)); + return (MmGetMdlVirtualAddress(buf)); } -__stdcall static void * -ndis_buf_vaddr_safe(ndis_buffer *buf, uint32_t prio) +static void * +NdisBufferVirtualAddressSafe(ndis_buffer *buf, uint32_t prio) { - return(MDL_VA(buf)); + return (MmGetMdlVirtualAddress(buf)); } -__stdcall static void -ndis_adjust_buflen(ndis_buffer *buf, int len) +static void +NdisAdjustBufferLength(ndis_buffer *buf, int len) { - buf->nb_bytecount = len; - - return; + MmGetMdlByteCount(buf) = len; } -__stdcall static uint32_t -ndis_interlock_inc(uint32_t *addend) +static uint32_t +NdisInterlockedIncrement(uint32_t *addend) { atomic_add_long((u_long *)addend, 1); - return(*addend); + return (*addend); } -__stdcall static uint32_t -ndis_interlock_dec(uint32_t *addend) +static uint32_t +NdisInterlockedDecrement(uint32_t *addend) { atomic_subtract_long((u_long *)addend, 1); - return(*addend); + return (*addend); } -__stdcall static void -ndis_init_event(ndis_event *event) +static uint32_t +NdisGetVersion(void) +{ + return (0x00050001); +} + +static void +NdisInitializeEvent(ndis_event *event) { /* * NDIS events are always notification * events, and should be initialized to the * not signaled state. */ - - ntoskrnl_init_event(&event->ne_event, EVENT_TYPE_NOTIFY, FALSE); - return; + KeInitializeEvent(&event->ne_event, EVENT_TYPE_NOTIFY, FALSE); } -__stdcall static void -ndis_set_event(ndis_event *event) +static void +NdisSetEvent(ndis_event *event) { - ntoskrnl_set_event(&event->ne_event, 0, 0); - return; + KeSetEvent(&event->ne_event, IO_NO_INCREMENT, FALSE); } -__stdcall static void -ndis_reset_event(ndis_event *event) +static void +NdisResetEvent(ndis_event *event) { - ntoskrnl_reset_event(&event->ne_event); - return; + KeResetEvent(&event->ne_event); } -__stdcall static uint8_t -ndis_wait_event(ndis_event *event, uint32_t msecs) +static uint8_t +NdisWaitEvent(ndis_event *event, uint32_t msecs) { int64_t duetime; uint32_t rval; duetime = ((int64_t)msecs * -10000); - - rval = ntoskrnl_waitforobj((nt_dispatch_header *)event, - 0, 0, TRUE, msecs ? &duetime : NULL); + rval = KeWaitForSingleObject(event, + 0, 0, TRUE, msecs ? & duetime : NULL); if (rval == STATUS_TIMEOUT) - return(FALSE); + return (FALSE); - return(TRUE); + return (TRUE); } -__stdcall static ndis_status -ndis_unicode2ansi(ndis_ansi_string *dstr, ndis_unicode_string *sstr) +static ndis_status +NdisUnicodeStringToAnsiString(ansi_string *dstr, unicode_string *sstr) { - if (dstr == NULL || sstr == NULL) - return(NDIS_STATUS_FAILURE); - if (ndis_unicode_to_ascii(sstr->nus_buf, - sstr->nus_len, &dstr->nas_buf)) - return(NDIS_STATUS_FAILURE); - dstr->nas_len = dstr->nas_maxlen = strlen(dstr->nas_buf); + uint32_t rval; + + rval = RtlUnicodeStringToAnsiString(dstr, sstr, FALSE); + + if (rval == STATUS_INSUFFICIENT_RESOURCES) + return (NDIS_STATUS_RESOURCES); + if (rval) + return (NDIS_STATUS_FAILURE); + return (NDIS_STATUS_SUCCESS); } -__stdcall static ndis_status -ndis_ansi2unicode(ndis_unicode_string *dstr, ndis_ansi_string *sstr) +static ndis_status +NdisAnsiStringToUnicodeString(unicode_string *dstr, ansi_string *sstr) { - char *str; - if (dstr == NULL || sstr == NULL) - return(NDIS_STATUS_FAILURE); - str = kmalloc(sstr->nas_len + 1, M_DEVBUF, M_WAITOK); - strncpy(str, sstr->nas_buf, sstr->nas_len); - *(str + sstr->nas_len) = '\0'; - if (ndis_ascii_to_unicode(str, &dstr->nus_buf)) { - kfree(str, M_DEVBUF); - return(NDIS_STATUS_FAILURE); - } - dstr->nus_len = dstr->nus_maxlen = sstr->nas_len * 2; - kfree(str, M_DEVBUF); + uint32_t rval; + + rval = RtlAnsiStringToUnicodeString(dstr, sstr, FALSE); + + if (rval == STATUS_INSUFFICIENT_RESOURCES) + return (NDIS_STATUS_RESOURCES); + if (rval) + return (NDIS_STATUS_FAILURE); + return (NDIS_STATUS_SUCCESS); } -__stdcall static ndis_status -ndis_assign_pcirsrc(ndis_handle adapter, uint32_t slot, - ndis_resource_list **list) +static ndis_status +NdisMPciAssignResources(ndis_handle adapter, uint32_t slot, + ndis_resource_list **list) { ndis_miniport_block *block; @@ -1891,31 +2015,143 @@ ndis_assign_pcirsrc(ndis_handle adapter, uint32_t slot, return (NDIS_STATUS_SUCCESS); } -__stdcall static ndis_status -ndis_register_intr(ndis_miniport_interrupt *intr, ndis_handle adapter, - uint32_t ivec, uint32_t ilevel, uint8_t reqisr, - uint8_t shared, ndis_interrupt_mode imode) +static uint8_t +ndis_intr(kinterrupt *iobj, void *arg) +{ + struct ndis_softc *sc; + uint8_t is_our_intr = FALSE; + int call_isr = 0; + ndis_miniport_interrupt *intr; + + sc = arg; + intr = sc->ndis_block->nmb_interrupt; + + if (intr == NULL || sc->ndis_block->nmb_miniportadapterctx == NULL) + return (FALSE); + + if (sc->ndis_block->nmb_interrupt->ni_isrreq == TRUE) + MSCALL3(intr->ni_isrfunc, &is_our_intr, &call_isr, + sc->ndis_block->nmb_miniportadapterctx); + else { + MSCALL1(sc->ndis_chars->nmc_disable_interrupts_func, + sc->ndis_block->nmb_miniportadapterctx); + call_isr = 1; + } + + if (call_isr) + IoRequestDpc(sc->ndis_block->nmb_deviceobj, NULL, sc); + + return (is_our_intr); +} + +static void +ndis_intrhand(kdpc *dpc, ndis_miniport_interrupt *intr, void *sysarg1, + void *sysarg2) { + struct ndis_softc *sc; ndis_miniport_block *block; + ndis_handle adapter; + + block = intr->ni_block; + adapter = block->nmb_miniportadapterctx; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + + if (NDIS_SERIALIZED(sc->ndis_block)) + KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); + + MSCALL1(intr->ni_dpcfunc, adapter); + + /* If there's a MiniportEnableInterrupt() routine, call it. */ + + if (sc->ndis_chars->nmc_enable_interrupts_func != NULL) + MSCALL1(sc->ndis_chars->nmc_enable_interrupts_func, adapter); + + if (NDIS_SERIALIZED(sc->ndis_block)) + KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); + + /* + * Set the completion event if we've drained all + * pending interrupts. + */ + + KeAcquireSpinLockAtDpcLevel(&intr->ni_dpccountlock); + intr->ni_dpccnt--; + if (intr->ni_dpccnt == 0) + KeSetEvent(&intr->ni_dpcevt, IO_NO_INCREMENT, FALSE); + KeReleaseSpinLockFromDpcLevel(&intr->ni_dpccountlock); +} + +static ndis_status +NdisMRegisterInterrupt(ndis_miniport_interrupt *intr, ndis_handle adapter, + uint32_t ivec, uint32_t ilevel, uint8_t reqisr, uint8_t shared, + ndis_interrupt_mode imode) +{ + ndis_miniport_block *block; + ndis_miniport_characteristics *ch; + struct ndis_softc *sc; + int error; block = adapter; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ch = IoGetDriverObjectExtension(block->nmb_deviceobj->do_drvobj, + (void *)1); + + intr->ni_rsvd = ExAllocatePoolWithTag(NonPagedPool, + sizeof(struct mtx), 0); + if (intr->ni_rsvd == NULL) + return (NDIS_STATUS_RESOURCES); intr->ni_block = adapter; intr->ni_isrreq = reqisr; intr->ni_shared = shared; + intr->ni_dpccnt = 0; + intr->ni_isrfunc = ch->nmc_isr_func; + intr->ni_dpcfunc = ch->nmc_interrupt_func; + + KeInitializeEvent(&intr->ni_dpcevt, EVENT_TYPE_NOTIFY, TRUE); + KeInitializeDpc(&intr->ni_dpc, + ndis_findwrap((funcptr)ndis_intrhand), intr); + KeSetImportanceDpc(&intr->ni_dpc, KDPC_IMPORTANCE_LOW); + + error = IoConnectInterrupt(&intr->ni_introbj, + ndis_findwrap((funcptr)ndis_intr), sc, NULL, + ivec, ilevel, 0, imode, shared, 0, FALSE); + + if (error != STATUS_SUCCESS) + return (NDIS_STATUS_FAILURE); + block->nmb_interrupt = intr; - return(NDIS_STATUS_SUCCESS); -} -__stdcall static void -ndis_deregister_intr(ndis_miniport_interrupt *intr) + return (NDIS_STATUS_SUCCESS); +} + +static void +NdisMDeregisterInterrupt(ndis_miniport_interrupt *intr) { - return; + ndis_miniport_block *block; + uint8_t irql; + + block = intr->ni_block; + + /* Should really be KeSynchronizeExecution() */ + + KeAcquireSpinLock(intr->ni_introbj->ki_lock, &irql); + block->nmb_interrupt = NULL; + KeReleaseSpinLock(intr->ni_introbj->ki_lock, irql); +/* + KeFlushQueuedDpcs(); +*/ + /* Disconnect our ISR */ + + IoDisconnectInterrupt(intr->ni_introbj); + + KeWaitForSingleObject(&intr->ni_dpcevt, 0, 0, FALSE, NULL); + KeResetEvent(&intr->ni_dpcevt); } -__stdcall static void -ndis_register_shutdown(ndis_handle adapter, void *shutdownctx, - ndis_shutdown_handler shutdownfunc) +static void +NdisMRegisterAdapterShutdownHandler(ndis_handle adapter, void *shutdownctx, + ndis_shutdown_handler shutdownfunc) { ndis_miniport_block *block; ndis_miniport_characteristics *chars; @@ -1925,17 +2161,15 @@ ndis_register_shutdown(ndis_handle adapter, void *shutdownctx, return; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)block->nmb_ifp; - chars = &sc->ndis_chars; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + chars = sc->ndis_chars; chars->nmc_shutdown_handler = shutdownfunc; chars->nmc_rsvd0 = shutdownctx; - - return; } -__stdcall static void -ndis_deregister_shutdown(ndis_handle adapter) +static void +NdisMDeregisterAdapterShutdownHandler(ndis_handle adapter) { ndis_miniport_block *block; ndis_miniport_characteristics *chars; @@ -1945,63 +2179,66 @@ ndis_deregister_shutdown(ndis_handle adapter) return; block = (ndis_miniport_block *)adapter; - sc = (struct ndis_softc *)block->nmb_ifp; - chars = &sc->ndis_chars; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + chars = sc->ndis_chars; chars->nmc_shutdown_handler = NULL; chars->nmc_rsvd0 = NULL; - - return; } -__stdcall static uint32_t -ndis_numpages(ndis_buffer *buf) +static uint32_t +NDIS_BUFFER_TO_SPAN_PAGES(ndis_buffer *buf) { if (buf == NULL) - return(0); - if (buf->nb_bytecount == 0) - return(1); - return(SPAN_PAGES(MDL_VA(buf), buf->nb_bytecount)); + return (0); + if (MmGetMdlByteCount(buf) == 0) + return (1); + return (SPAN_PAGES(MmGetMdlVirtualAddress(buf), + MmGetMdlByteCount(buf))); } -__stdcall static void -ndis_buf_physpages(ndis_buffer *buf, uint32_t *pages) +static void +NdisGetBufferPhysicalArraySize(ndis_buffer *buf, uint32_t *pages) { if (buf == NULL) return; - *pages = ndis_numpages(buf); - return; + *pages = NDIS_BUFFER_TO_SPAN_PAGES(buf); } -__stdcall static void -ndis_query_bufoffset(ndis_buffer *buf, uint32_t *off, uint32_t *len) +static void +NdisQueryBufferOffset(ndis_buffer *buf, uint32_t *off, uint32_t *len) { if (buf == NULL) return; - *off = buf->nb_byteoffset; - *len = buf->nb_bytecount; - - return; + *off = MmGetMdlByteOffset(buf); + *len = MmGetMdlByteCount(buf); } -__stdcall static void -ndis_sleep(uint32_t usecs) +void +NdisMSleep(uint32_t usecs) { - struct timeval tv; + ktimer timer; - tv.tv_sec = 0; - tv.tv_usec = usecs; - - ndis_thsuspend(curthread, 1 + usecs * hz / 1000000); + /* + * During system bootstrap, (i.e. cold == 1), we aren't + * allowed to sleep, so we have to do a hard DELAY() + * instead. + */ - return; + if (cold) + DELAY(usecs); + else { + KeInitializeTimer(&timer); + KeSetTimer(&timer, ((int64_t)usecs * -10), NULL); + KeWaitForSingleObject(&timer, 0, 0, FALSE, NULL); + } } -__stdcall static uint32_t -ndis_read_pccard_amem(ndis_handle handle, uint32_t offset, void *buf, - uint32_t len) +static uint32_t +NdisReadPcmciaAttributeMemory(ndis_handle handle, uint32_t offset, void *buf, + uint32_t len) { struct ndis_softc *sc; ndis_miniport_block *block; @@ -2011,10 +2248,10 @@ ndis_read_pccard_amem(ndis_handle handle, uint32_t offset, void *buf, int i; if (handle == NULL) - return(0); + return (0); block = (ndis_miniport_block *)handle; - sc = (struct ndis_softc *)block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); dest = buf; bh = rman_get_bushandle(sc->ndis_res_am); @@ -2023,12 +2260,12 @@ ndis_read_pccard_amem(ndis_handle handle, uint32_t offset, void *buf, for (i = 0; i < len; i++) dest[i] = bus_space_read_1(bt, bh, (offset + i) * 2); - return(i); + return (i); } -__stdcall static uint32_t -ndis_write_pccard_amem(ndis_handle handle, uint32_t offset, void *buf, - uint32_t len) +static uint32_t +NdisWritePcmciaAttributeMemory(ndis_handle handle, uint32_t offset, void *buf, + uint32_t len) { struct ndis_softc *sc; ndis_miniport_block *block; @@ -2038,10 +2275,10 @@ ndis_write_pccard_amem(ndis_handle handle, uint32_t offset, void *buf, int i; if (handle == NULL) - return(0); + return (0); block = (ndis_miniport_block *)handle; - sc = (struct ndis_softc *)block->nmb_ifp; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); src = buf; bh = rman_get_bushandle(sc->ndis_res_am); @@ -2050,202 +2287,136 @@ ndis_write_pccard_amem(ndis_handle handle, uint32_t offset, void *buf, for (i = 0; i < len; i++) bus_space_write_1(bt, bh, (offset + i) * 2, src[i]); - return(i); + return (i); } -__stdcall static list_entry * -ndis_insert_head(list_entry *head, list_entry *entry, ndis_spin_lock *lock) +static list_entry * +NdisInterlockedInsertHeadList(list_entry *head, list_entry *entry, + ndis_spin_lock *lock) { list_entry *flink; - lock->nsl_kirql = FASTCALL2(hal_lock, - &lock->nsl_spinlock, DISPATCH_LEVEL); + KeAcquireSpinLock(&lock->nsl_spinlock, &lock->nsl_kirql); flink = head->nle_flink; entry->nle_flink = flink; entry->nle_blink = head; flink->nle_blink = entry; head->nle_flink = entry; - FASTCALL2(hal_unlock, &lock->nsl_spinlock, lock->nsl_kirql); + KeReleaseSpinLock(&lock->nsl_spinlock, lock->nsl_kirql); - return(flink); + return (flink); } -__stdcall static list_entry * -ndis_remove_head(list_entry *head, ndis_spin_lock *lock) +static list_entry * +NdisInterlockedRemoveHeadList(list_entry *head, ndis_spin_lock *lock) { list_entry *flink; list_entry *entry; - lock->nsl_kirql = FASTCALL2(hal_lock, - &lock->nsl_spinlock, DISPATCH_LEVEL); + KeAcquireSpinLock(&lock->nsl_spinlock, &lock->nsl_kirql); entry = head->nle_flink; flink = entry->nle_flink; head->nle_flink = flink; flink->nle_blink = head; - FASTCALL2(hal_unlock, &lock->nsl_spinlock, lock->nsl_kirql); + KeReleaseSpinLock(&lock->nsl_spinlock, lock->nsl_kirql); - return(entry); + return (entry); } -__stdcall static list_entry * -ndis_insert_tail(list_entry *head, list_entry *entry, ndis_spin_lock *lock) +static list_entry * +NdisInterlockedInsertTailList(list_entry *head, list_entry *entry, + ndis_spin_lock *lock) { list_entry *blink; - lock->nsl_kirql = FASTCALL2(hal_lock, - &lock->nsl_spinlock, DISPATCH_LEVEL); + KeAcquireSpinLock(&lock->nsl_spinlock, &lock->nsl_kirql); blink = head->nle_blink; entry->nle_flink = head; entry->nle_blink = blink; blink->nle_flink = entry; head->nle_blink = entry; - FASTCALL2(hal_unlock, &lock->nsl_spinlock, lock->nsl_kirql); + KeReleaseSpinLock(&lock->nsl_spinlock, lock->nsl_kirql); - return(blink); + return (blink); } -__stdcall static uint8_t -ndis_sync_with_intr(ndis_miniport_interrupt *intr, void *syncfunc, - void *syncctx) +static uint8_t +NdisMSynchronizeWithInterrupt(ndis_miniport_interrupt *intr, void *syncfunc, + void *syncctx) { - struct ndis_softc *sc; - struct ifnet *ifp; - __stdcall uint8_t (*sync)(void *); - uint8_t rval; - - if (syncfunc == NULL || syncctx == NULL) - return(0); - - sc = (struct ndis_softc *)intr->ni_block->nmb_ifp; - ifp = &sc->arpcom.ac_if; - sync = syncfunc; - rval = sync(syncctx); - - return(rval); + return (KeSynchronizeExecution(intr->ni_introbj, syncfunc, syncctx)); } -/* - * Return the number of 100 nanosecond intervals since - * January 1, 1601. (?!?!) - */ -__stdcall static void -ndis_time(uint64_t *tval) +static void +NdisGetCurrentSystemTime(uint64_t *tval) { - struct timespec ts; - - nanotime(&ts); - *tval = (uint64_t)ts.tv_nsec / 100 + (uint64_t)ts.tv_sec * 10000000 + - 11644473600LL; - - return; + ntoskrnl_time(tval); } /* * Return the number of milliseconds since the system booted. */ -__stdcall static void -ndis_uptime(uint32_t *tval) +static void +NdisGetSystemUpTime(uint32_t *tval) { struct timespec ts; nanouptime(&ts); *tval = ts.tv_nsec / 1000000 + ts.tv_sec * 1000; - - return; } -__stdcall static void -ndis_init_string(ndis_unicode_string *dst, char *src) +static void +NdisInitializeString(unicode_string *dst, char *src) { - ndis_unicode_string *u; - - u = dst; - u->nus_buf = NULL; - if (ndis_ascii_to_unicode(src, &u->nus_buf)) - return; - u->nus_len = u->nus_maxlen = strlen(src) * 2; - return; + ansi_string as; + RtlInitAnsiString(&as, src); + RtlAnsiStringToUnicodeString(dst, &as, TRUE); } -__stdcall static void -ndis_free_string(ndis_unicode_string *str) +static void +NdisFreeString(unicode_string *str) { - if (str == NULL) - return; - if (str->nus_buf != NULL) - kfree(str->nus_buf, M_DEVBUF); - kfree(str, M_DEVBUF); - return; + RtlFreeUnicodeString(str); } -__stdcall static ndis_status -ndis_remove_miniport(ndis_handle *adapter) +static ndis_status +NdisMRemoveMiniport(ndis_handle *adapter) { - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_init_ansi_string(ndis_ansi_string *dst, char *src) +static void +NdisInitAnsiString(ansi_string *dst, char *src) { - ndis_ansi_string *a; - - a = dst; - if (a == NULL) - return; - if (src == NULL) { - a->nas_len = a->nas_maxlen = 0; - a->nas_buf = NULL; - } else { - a->nas_buf = src; - a->nas_len = a->nas_maxlen = strlen(src); - } - - return; + RtlInitAnsiString(dst, src); } -__stdcall static void -ndis_init_unicode_string(ndis_unicode_string *dst, uint16_t *src) +static void +NdisInitUnicodeString(unicode_string *dst, uint16_t *src) { - ndis_unicode_string *u; - int i; - - u = dst; - if (u == NULL) - return; - if (src == NULL) { - u->nus_len = u->nus_maxlen = 0; - u->nus_buf = NULL; - } else { - i = 0; - while(src[i] != 0) - i++; - u->nus_buf = src; - u->nus_len = u->nus_maxlen = i * 2; - } - - return; + RtlInitUnicodeString(dst, src); } -__stdcall static void -ndis_get_devprop(ndis_handle adapter, device_object **phydevobj, - device_object **funcdevobj, device_object **nextdevobj, - cm_resource_list *resources, cm_resource_list *transresources) +static void NdisMGetDeviceProperty(ndis_handle adapter, + device_object **phydevobj, device_object **funcdevobj, + device_object **nextdevobj, cm_resource_list *resources, + cm_resource_list *transresources) { ndis_miniport_block *block; block = (ndis_miniport_block *)adapter; if (phydevobj != NULL) - *phydevobj = &block->nmb_devobj; + *phydevobj = block->nmb_physdeviceobj; if (funcdevobj != NULL) - *funcdevobj = &block->nmb_devobj; - - return; + *funcdevobj = block->nmb_deviceobj; + if (nextdevobj != NULL) + *nextdevobj = block->nmb_nextdeviceobj; } -__stdcall static void -ndis_firstbuf(ndis_packet *packet, ndis_buffer **buf, void **firstva, - uint32_t *firstlen, uint32_t *totlen) +static void +NdisGetFirstBufferFromPacket(ndis_packet *packet, ndis_buffer **buf, + void **firstva, uint32_t *firstlen, uint32_t *totlen) { ndis_buffer *tmp; @@ -2255,54 +2426,183 @@ ndis_firstbuf(ndis_packet *packet, ndis_buffer **buf, void **firstva, *firstva = NULL; *firstlen = *totlen = 0; } else { - *firstva = MDL_VA(tmp); - *firstlen = *totlen = tmp->nb_bytecount; - for (tmp = tmp->nb_next; tmp != NULL; tmp = tmp->nb_next) - *totlen += tmp->nb_bytecount; + *firstva = MmGetMdlVirtualAddress(tmp); + *firstlen = *totlen = MmGetMdlByteCount(tmp); + for (tmp = tmp->mdl_next; tmp != NULL; tmp = tmp->mdl_next) + *totlen += MmGetMdlByteCount(tmp); } +} - return; +static void +NdisGetFirstBufferFromPacketSafe(ndis_packet *packet, ndis_buffer **buf, + void **firstva, uint32_t *firstlen, uint32_t *totlen, uint32_t prio) +{ + NdisGetFirstBufferFromPacket(packet, buf, firstva, firstlen, totlen); } -__stdcall static void -ndis_firstbuf_safe(ndis_packet *packet, ndis_buffer **buf, void **firstva, - uint32_t *firstlen, uint32_t *totlen, uint32_t prio) +static int +ndis_find_sym(linker_file_t lf, char *filename, char *suffix, caddr_t *sym) { - ndis_firstbuf(packet, buf, firstva, firstlen, totlen); + char *fullsym; + char *suf; + int i; + + fullsym = ExAllocatePoolWithTag(NonPagedPool, MAXPATHLEN, 0); + if (fullsym == NULL) + return (ENOMEM); + + bzero(fullsym, MAXPATHLEN); + strncpy(fullsym, filename, MAXPATHLEN); + if (strlen(filename) < 4) { + ExFreePool(fullsym); + return (EINVAL); + } + + /* If the filename has a .ko suffix, strip if off. */ + suf = fullsym + (strlen(filename) - 3); + if (strcmp(suf, ".ko") == 0) + *suf = '\0'; + + for (i = 0; i < strlen(fullsym); i++) { + if (fullsym[i] == '.') + fullsym[i] = '_'; + else + fullsym[i] = tolower(fullsym[i]); + } + strcat(fullsym, suffix); + linker_file_lookup_symbol(lf, fullsym, 0, sym); + ExFreePool(fullsym); + if (*sym == 0) + return (ENOENT); + + return (0); +} + +struct ndis_checkmodule { + char *afilename; + ndis_fh *fh; +}; + +/* + * See if a single module contains the symbols for a specified file. + */ +static int +NdisCheckModule(linker_file_t lf, void *context) +{ + struct ndis_checkmodule *nc; + caddr_t kldstart, kldend; + + nc = (struct ndis_checkmodule *)context; + if (ndis_find_sym(lf, nc->afilename, "_start", &kldstart)) + return (0); + if (ndis_find_sym(lf, nc->afilename, "_end", &kldend)) + return (0); + nc->fh->nf_vp = lf; + nc->fh->nf_map = NULL; + nc->fh->nf_type = NDIS_FH_TYPE_MODULE; + nc->fh->nf_maplen = (kldend - kldstart) & 0xFFFFFFFF; + return (1); } /* can also return NDIS_STATUS_RESOURCES/NDIS_STATUS_ERROR_READING_FILE */ -__stdcall static void -ndis_open_file(ndis_status *status, ndis_handle *filehandle, uint32_t *filelength, - ndis_unicode_string *filename, ndis_physaddr highestaddr) +static void +NdisOpenFile(ndis_status *status, ndis_handle *filehandle, + uint32_t *filelength, unicode_string *filename, ndis_physaddr highestaddr) { + ansi_string as; char *afilename = NULL; + struct thread *td = curthread; struct nlookupdata nd; int error; struct vattr vat; struct vattr *vap = &vat; ndis_fh *fh; + char *path; + struct ndis_checkmodule nc; struct vnode *vp; - char path[MAXPATHLEN]; - ndis_unicode_to_ascii(filename->nus_buf, - filename->nus_len, &afilename); + if (RtlUnicodeStringToAnsiString(&as, filename, TRUE)) { + *status = NDIS_STATUS_RESOURCES; + return; + } - ksprintf(path, "%s/%s", ndis_filepath, afilename); - kfree(afilename, M_DEVBUF); + afilename = kstrdup(as.as_buf, M_DEVBUF); + RtlFreeAnsiString(&as); + + fh = ExAllocatePoolWithTag(NonPagedPool, sizeof(ndis_fh), 0); + if (fh == NULL) { + kfree(afilename, M_DEVBUF); + *status = NDIS_STATUS_RESOURCES; + return; + } - fh = kmalloc(sizeof(ndis_fh), M_TEMP, M_WAITOK); + fh->nf_name = afilename; - error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW|NLC_LOCKVP); - if (error == 0) - error = vn_open(&nd, NULL, FREAD, 0); - if (error) { + /* + * During system bootstrap, it's impossible to load files + * from the rootfs since it's not mounted yet. We therefore + * offer the possibility of opening files that have been + * preloaded as modules instead. Both choices will work + * when kldloading a module from multiuser, but only the + * module option will work during bootstrap. The module + * loading option works by using the ndiscvt(8) utility + * to convert the arbitrary file into a .ko using objcopy(1). + * This file will contain two special symbols: filename_start + * and filename_end. All we have to do is traverse the KLD + * list in search of those symbols and we've found the file + * data. As an added bonus, ndiscvt(8) will also generate + * a normal .o file which can be linked statically with + * the kernel. This means that the symbols will actual reside + * in the kernel's symbol table, but that doesn't matter to + * us since the kernel appears to us as just another module. + */ + + nc.afilename = afilename; + nc.fh = fh; + if (linker_file_foreach(NdisCheckModule, &nc)) { + *filelength = fh->nf_maplen; + *filehandle = fh; + *status = NDIS_STATUS_SUCCESS; + return; + } + + if (mountlist_boot_getfirst() == NULL) { + ExFreePool(fh); *status = NDIS_STATUS_FILE_NOT_FOUND; - kfree(fh, M_TEMP); - kprintf("NDIS: open file %s failed: %d\n", path, error); - goto done; + kprintf("NDIS: could not find file %s in linker list\n", + afilename); + kprintf("NDIS: and no filesystems mounted yet, " + "aborting NdisOpenFile()\n"); + kfree(afilename, M_DEVBUF); + return; + } + + path = ExAllocatePoolWithTag(NonPagedPool, MAXPATHLEN, 0); + if (path == NULL) { + ExFreePool(fh); + kfree(afilename, M_DEVBUF); + *status = NDIS_STATUS_RESOURCES; + return; } + ksnprintf(path, MAXPATHLEN, "%s/%s", ndis_filepath, afilename); + + /* Some threads don't have a current working directory. */ + + if (td->td_proc && td->td_proc->p_fd->fd_rdir == NULL) + td->td_proc->p_fd->fd_rdir = rootvnode; + if (td->td_proc && td->td_proc->p_fd->fd_cdir == NULL) + td->td_proc->p_fd->fd_cdir = rootvnode; + + error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW|NLC_LOCKVP); + if (error) + goto init_fail; + error = vn_open(&nd, NULL, FREAD, 0); + if (error) + goto open_fail; + + ExFreePool(path); + vp = nd.nl_open_vp; nd.nl_open_vp = NULL; @@ -2310,22 +2610,34 @@ ndis_open_file(ndis_status *status, ndis_handle *filehandle, uint32_t *filelengt VOP_GETATTR(vp, vap); vn_unlock(vp); - fh->nf_vp = vp; + fh->nf_vp = nd.nl_open_vp; fh->nf_map = NULL; + fh->nf_type = NDIS_FH_TYPE_VFS; *filehandle = fh; *filelength = fh->nf_maplen = vap->va_size & 0xFFFFFFFF; *status = NDIS_STATUS_SUCCESS; -done: - nlookup_done(&nd); return; + +open_fail: + nlookup_done(&nd); +init_fail: + *status = NDIS_STATUS_FILE_NOT_FOUND; + ExFreePool(fh); + kprintf("NDIS: open file %s failed: %d\n", path, error); + ExFreePool(path); + kfree(afilename, M_DEVBUF); } -__stdcall static void -ndis_map_file(ndis_status *status, void **mappedbuffer, ndis_handle filehandle) +static void +NdisMapFile(ndis_status *status, void **mappedbuffer, ndis_handle filehandle) { ndis_fh *fh; + struct thread *td = curthread; + linker_file_t lf; + caddr_t kldstart; int error, resid; + struct vnode *vp; if (filehandle == NULL) { *status = NDIS_STATUS_FAILURE; @@ -2344,10 +2656,28 @@ ndis_map_file(ndis_status *status, void **mappedbuffer, ndis_handle filehandle) return; } - fh->nf_map = kmalloc(fh->nf_maplen, M_DEVBUF, M_WAITOK); + if (fh->nf_type == NDIS_FH_TYPE_MODULE) { + lf = fh->nf_vp; + if (ndis_find_sym(lf, fh->nf_name, "_start", &kldstart)) { + *status = NDIS_STATUS_FAILURE; + return; + } + fh->nf_map = kldstart; + *status = NDIS_STATUS_SUCCESS; + *mappedbuffer = fh->nf_map; + return; + } + + fh->nf_map = ExAllocatePoolWithTag(NonPagedPool, fh->nf_maplen, 0); - error = vn_rdwr(UIO_READ, fh->nf_vp, fh->nf_map, fh->nf_maplen, 0, - UIO_SYSSPACE, 0, proc0.p_ucred, &resid); + if (fh->nf_map == NULL) { + *status = NDIS_STATUS_RESOURCES; + return; + } + + vp = fh->nf_vp; + error = vn_rdwr(UIO_READ, vp, fh->nf_map, fh->nf_maplen, 0, + UIO_SYSSPACE, 0, td->td_ucred, &resid); if (error) *status = NDIS_STATUS_FAILURE; @@ -2355,61 +2685,79 @@ ndis_map_file(ndis_status *status, void **mappedbuffer, ndis_handle filehandle) *status = NDIS_STATUS_SUCCESS; *mappedbuffer = fh->nf_map; } - - return; } -__stdcall static void -ndis_unmap_file(ndis_handle filehandle) +static void +NdisUnmapFile(ndis_handle filehandle) { ndis_fh *fh; fh = (ndis_fh *)filehandle; if (fh->nf_map == NULL) return; - kfree(fh->nf_map, M_DEVBUF); - fh->nf_map = NULL; - return; + if (fh->nf_type == NDIS_FH_TYPE_VFS) + ExFreePool(fh->nf_map); + fh->nf_map = NULL; } -__stdcall static void -ndis_close_file(ndis_handle filehandle) +static void +NdisCloseFile(ndis_handle filehandle) { ndis_fh *fh; + struct vnode *vp; if (filehandle == NULL) return; fh = (ndis_fh *)filehandle; if (fh->nf_map != NULL) { - kfree(fh->nf_map, M_DEVBUF); + if (fh->nf_type == NDIS_FH_TYPE_VFS) + ExFreePool(fh->nf_map); fh->nf_map = NULL; } if (fh->nf_vp == NULL) return; - vn_close(fh->nf_vp, FREAD); + if (fh->nf_type == NDIS_FH_TYPE_VFS) { + vp = fh->nf_vp; + vn_close(vp, FREAD); + } fh->nf_vp = NULL; - kfree(fh, M_DEVBUF); + kfree(fh->nf_name, M_DEVBUF); + ExFreePool(fh); +} - return; +static uint8_t +NdisSystemProcessorCount(void) +{ + return (ncpus); } -__stdcall static uint8_t -ndis_cpu_cnt(void) +static void +NdisGetCurrentProcessorCounts(uint32_t *idle_count, uint32_t *kernel_and_user, + uint32_t *index) { - return(ncpus); +#if 0 /* XXX swildner */ + struct pcpu *pcpu; + + pcpu = pcpu_find(curthread->td_oncpu); + *index = pcpu->pc_cpuid; + *idle_count = pcpu->pc_cp_time[CP_IDLE]; + *kernel_and_user = pcpu->pc_cp_time[CP_INTR]; +#else + panic("%s", __func__); +#endif } -typedef __stdcall void (*ndis_statusdone_handler)(ndis_handle); -typedef __stdcall void (*ndis_status_handler)(ndis_handle, ndis_status, +typedef void (*ndis_statusdone_handler)(ndis_handle); +typedef void (*ndis_status_handler)(ndis_handle, ndis_status, void *, uint32_t); -__stdcall static void -ndis_ind_statusdone(ndis_handle adapter) +static void +NdisMIndicateStatusComplete(ndis_handle adapter) { ndis_miniport_block *block; ndis_statusdone_handler statusdonefunc; @@ -2417,13 +2765,12 @@ ndis_ind_statusdone(ndis_handle adapter) block = (ndis_miniport_block *)adapter; statusdonefunc = block->nmb_statusdone_func; - statusdonefunc(adapter); - return; + MSCALL1(statusdonefunc, adapter); } -__stdcall static void -ndis_ind_status(ndis_handle adapter, ndis_status status, void *sbuf, - uint32_t slen) +static void +NdisMIndicateStatus(ndis_handle adapter, ndis_status status, void *sbuf, + uint32_t slen) { ndis_miniport_block *block; ndis_status_handler statusfunc; @@ -2431,32 +2778,45 @@ ndis_ind_status(ndis_handle adapter, ndis_status status, void *sbuf, block = (ndis_miniport_block *)adapter; statusfunc = block->nmb_status_func; - statusfunc(adapter, status, sbuf, slen); - return; + MSCALL4(statusfunc, adapter, status, sbuf, slen); } -static void -ndis_workfunc(void *ctx) +/* + * The DDK documentation says that you should use IoQueueWorkItem() + * instead of ExQueueWorkItem(). The problem is, IoQueueWorkItem() + * is fundamentally incompatible with NdisScheduleWorkItem(), which + * depends on the API semantics of ExQueueWorkItem(). In our world, + * ExQueueWorkItem() is implemented on top of IoAllocateQueueItem() + * anyway. + * + * There are actually three distinct APIs here. NdisScheduleWorkItem() + * takes a pointer to an NDIS_WORK_ITEM. ExQueueWorkItem() takes a pointer + * to a WORK_QUEUE_ITEM. And finally, IoQueueWorkItem() takes a pointer + * to an opaque work item thingie which you get from IoAllocateWorkItem(). + * An NDIS_WORK_ITEM is not the same as a WORK_QUEUE_ITEM. However, + * the NDIS_WORK_ITEM has some opaque storage at the end of it, and we + * (ab)use this storage as a WORK_QUEUE_ITEM, which is what we submit + * to ExQueueWorkItem(). + * + * Got all that? (Sheesh.) + */ + +ndis_status +NdisScheduleWorkItem(ndis_work_item *work) { - ndis_work_item *work; - ndis_proc workfunc; + work_queue_item *wqi; - work = ctx; - workfunc = work->nwi_func; - workfunc(work, work->nwi_ctx); - return; -} + wqi = (work_queue_item *)work->nwi_wraprsvd; + ExInitializeWorkItem(wqi, + (work_item_func)work->nwi_func, work->nwi_ctx); + ExQueueWorkItem(wqi, WORKQUEUE_DELAYED); -__stdcall static ndis_status -ndis_sched_workitem(ndis_work_item *work) -{ - ndis_sched(ndis_workfunc, work, NDIS_TASKQUEUE); - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_pkt_to_pkt(ndis_packet *dpkt, uint32_t doff, uint32_t reqlen, - ndis_packet *spkt, uint32_t soff, uint32_t *cpylen) +static void +NdisCopyFromPacketToPacket(ndis_packet *dpkt, uint32_t doff, uint32_t reqlen, + ndis_packet *spkt, uint32_t soff, uint32_t *cpylen) { ndis_buffer *src, *dst; char *sptr, *dptr; @@ -2467,35 +2827,35 @@ ndis_pkt_to_pkt(ndis_packet *dpkt, uint32_t doff, uint32_t reqlen, src = spkt->np_private.npp_head; dst = dpkt->np_private.npp_head; - sptr = MDL_VA(src); - dptr = MDL_VA(dst); - scnt = src->nb_bytecount; - dcnt = dst->nb_bytecount; + sptr = MmGetMdlVirtualAddress(src); + dptr = MmGetMdlVirtualAddress(dst); + scnt = MmGetMdlByteCount(src); + dcnt = MmGetMdlByteCount(dst); while (soff) { - if (src->nb_bytecount > soff) { + if (MmGetMdlByteCount(src) > soff) { sptr += soff; - scnt = src->nb_bytecount - soff; + scnt = MmGetMdlByteCount(src)- soff; break; } - soff -= src->nb_bytecount; - src = src->nb_next; + soff -= MmGetMdlByteCount(src); + src = src->mdl_next; if (src == NULL) return; - sptr = MDL_VA(src); + sptr = MmGetMdlVirtualAddress(src); } while (doff) { - if (dst->nb_bytecount > doff) { + if (MmGetMdlByteCount(dst) > doff) { dptr += doff; - dcnt = dst->nb_bytecount - doff; + dcnt = MmGetMdlByteCount(dst) - doff; break; } - doff -= dst->nb_bytecount; - dst = dst->nb_next; + doff -= MmGetMdlByteCount(dst); + dst = dst->mdl_next; if (dst == NULL) return; - dptr = MDL_VA(dst); + dptr = MmGetMdlVirtualAddress(dst); } resid = reqlen; @@ -2518,197 +2878,231 @@ ndis_pkt_to_pkt(ndis_packet *dpkt, uint32_t doff, uint32_t reqlen, dcnt -= len; if (dcnt == 0) { - dst = dst->nb_next; + dst = dst->mdl_next; if (dst == NULL) break; - dptr = MDL_VA(dst); - dcnt = dst->nb_bytecount; + dptr = MmGetMdlVirtualAddress(dst); + dcnt = MmGetMdlByteCount(dst); } scnt -= len; if (scnt == 0) { - src = src->nb_next; + src = src->mdl_next; if (src == NULL) break; - sptr = MDL_VA(src); - scnt = src->nb_bytecount; + sptr = MmGetMdlVirtualAddress(src); + scnt = MmGetMdlByteCount(src); } } *cpylen = copied; - return; } -__stdcall static void -ndis_pkt_to_pkt_safe(ndis_packet *dpkt, uint32_t doff, uint32_t reqlen, - ndis_packet *spkt, uint32_t soff, uint32_t *cpylen, - uint32_t prio) +static void +NdisCopyFromPacketToPacketSafe(ndis_packet *dpkt, uint32_t doff, + uint32_t reqlen, ndis_packet *spkt, uint32_t soff, uint32_t *cpylen, + uint32_t prio) { - ndis_pkt_to_pkt(dpkt, doff, reqlen, spkt, soff, cpylen); - return; + NdisCopyFromPacketToPacket(dpkt, doff, reqlen, spkt, soff, cpylen); } -__stdcall static ndis_status -ndis_register_dev(ndis_handle handle, ndis_unicode_string *devname, - ndis_unicode_string *symname, driver_dispatch **majorfuncs, - void **devobj, ndis_handle *devhandle) +static void +NdisIMCopySendPerPacketInfo(ndis_packet *dpkt, ndis_packet *spkt) { - ndis_miniport_block *block; + memcpy(&dpkt->np_ext, &spkt->np_ext, sizeof(ndis_packet_extension)); +} - block = (ndis_miniport_block *)handle; - *devobj = &block->nmb_devobj; - *devhandle = handle; +static ndis_status +NdisMRegisterDevice(ndis_handle handle, unicode_string *devname, + unicode_string *symname, driver_dispatch *majorfuncs[], void **devobj, + ndis_handle *devhandle) +{ + uint32_t status; + device_object *dobj; + + status = IoCreateDevice(handle, 0, devname, + FILE_DEVICE_UNKNOWN, 0, FALSE, &dobj); - return(NDIS_STATUS_SUCCESS); + if (status == STATUS_SUCCESS) { + *devobj = dobj; + *devhandle = dobj; + } + + return (status); } -__stdcall static ndis_status -ndis_deregister_dev(ndis_handle handle) +static ndis_status +NdisMDeregisterDevice(ndis_handle handle) { - return(NDIS_STATUS_SUCCESS); + IoDeleteDevice(handle); + return (NDIS_STATUS_SUCCESS); } -__stdcall static ndis_status -ndis_query_name(ndis_unicode_string *name, ndis_handle handle) +static ndis_status +NdisMQueryAdapterInstanceName(unicode_string *name, ndis_handle handle) { ndis_miniport_block *block; + device_t dev; + ansi_string as; block = (ndis_miniport_block *)handle; - ndis_ascii_to_unicode(__DECONST(char *, - device_get_nameunit(block->nmb_dev)), &name->nus_buf); - name->nus_len = strlen(device_get_nameunit(block->nmb_dev)) * 2; + dev = block->nmb_physdeviceobj->do_devext; + + RtlInitAnsiString(&as, __DECONST(char *, device_get_nameunit(dev))); + if (RtlAnsiStringToUnicodeString(name, &as, TRUE)) + return (NDIS_STATUS_RESOURCES); - return(NDIS_STATUS_SUCCESS); + return (NDIS_STATUS_SUCCESS); } -__stdcall static void -ndis_register_unload(ndis_handle handle, void *func) +static void +NdisMRegisterUnloadHandler(ndis_handle handle, void *func) { - return; } -__stdcall static void +static void dummy(void) { - kprintf ("NDIS dummy called...\n"); - return; + kprintf("NDIS dummy called...\n"); } +/* + * Note: a couple of entries in this table specify the + * number of arguments as "foo + 1". These are routines + * that accept a 64-bit argument, passed by value. On + * x86, these arguments consume two longwords on the stack, + * so we lie and say there's one additional argument so + * that the wrapping routines will do the right thing. + */ + image_patch_table ndis_functbl[] = { - { "NdisCopyFromPacketToPacket", (FUNC)ndis_pkt_to_pkt }, - { "NdisCopyFromPacketToPacketSafe", (FUNC)ndis_pkt_to_pkt_safe }, - { "NdisScheduleWorkItem", (FUNC)ndis_sched_workitem }, - { "NdisMIndicateStatusComplete", (FUNC)ndis_ind_statusdone }, - { "NdisMIndicateStatus", (FUNC)ndis_ind_status }, - { "NdisSystemProcessorCount", (FUNC)ndis_cpu_cnt }, - { "NdisUnchainBufferAtBack", (FUNC)ndis_unchain_tailbuf, }, - { "NdisGetFirstBufferFromPacket", (FUNC)ndis_firstbuf }, - { "NdisGetFirstBufferFromPacketSafe", (FUNC)ndis_firstbuf_safe }, - { "NdisGetBufferPhysicalArraySize", (FUNC)ndis_buf_physpages }, - { "NdisMGetDeviceProperty", (FUNC)ndis_get_devprop }, - { "NdisInitAnsiString", (FUNC)ndis_init_ansi_string }, - { "NdisInitUnicodeString", (FUNC)ndis_init_unicode_string }, - { "NdisWriteConfiguration", (FUNC)ndis_write_cfg }, - { "NdisAnsiStringToUnicodeString", (FUNC)ndis_ansi2unicode }, - { "NdisTerminateWrapper", (FUNC)ndis_termwrap }, - { "NdisOpenConfigurationKeyByName", (FUNC)ndis_open_cfgbyname }, - { "NdisOpenConfigurationKeyByIndex", (FUNC)ndis_open_cfgbyidx }, - { "NdisMRemoveMiniport", (FUNC)ndis_remove_miniport }, - { "NdisInitializeString", (FUNC)ndis_init_string }, - { "NdisFreeString", (FUNC)ndis_free_string }, - { "NdisGetCurrentSystemTime", (FUNC)ndis_time }, - { "NdisGetSystemUpTime", (FUNC)ndis_uptime }, - { "NdisMSynchronizeWithInterrupt", (FUNC)ndis_sync_with_intr }, - { "NdisMAllocateSharedMemoryAsync", (FUNC)ndis_alloc_sharedmem_async }, - { "NdisInterlockedInsertHeadList", (FUNC)ndis_insert_head }, - { "NdisInterlockedInsertTailList", (FUNC)ndis_insert_tail }, - { "NdisInterlockedRemoveHeadList", (FUNC)ndis_remove_head }, - { "NdisInitializeWrapper", (FUNC)ndis_initwrap }, - { "NdisMRegisterMiniport", (FUNC)ndis_register_miniport }, - { "NdisAllocateMemoryWithTag", (FUNC)ndis_malloc_withtag }, - { "NdisAllocateMemory", (FUNC)ndis_malloc }, - { "NdisMSetAttributesEx", (FUNC)ndis_setattr_ex }, - { "NdisCloseConfiguration", (FUNC)ndis_close_cfg }, - { "NdisReadConfiguration", (FUNC)ndis_read_cfg }, - { "NdisOpenConfiguration", (FUNC)ndis_open_cfg }, - { "NdisAcquireSpinLock", (FUNC)ndis_lock }, - { "NdisReleaseSpinLock", (FUNC)ndis_unlock }, - { "NdisDprAcquireSpinLock", (FUNC)ndis_lock_dpr }, - { "NdisDprReleaseSpinLock", (FUNC)ndis_unlock_dpr }, - { "NdisAllocateSpinLock", (FUNC)ndis_create_lock }, - { "NdisFreeSpinLock", (FUNC)ndis_destroy_lock }, - { "NdisFreeMemory", (FUNC)ndis_free }, - { "NdisReadPciSlotInformation", (FUNC)ndis_read_pci }, - { "NdisWritePciSlotInformation",(FUNC)ndis_write_pci }, - { "NdisImmediateReadPciSlotInformation", (FUNC)ndis_read_pci }, - { "NdisImmediateWritePciSlotInformation", (FUNC)ndis_write_pci }, - { "NdisWriteErrorLogEntry", (FUNC)ndis_syslog }, - { "NdisMStartBufferPhysicalMapping", (FUNC)ndis_vtophys_load }, - { "NdisMCompleteBufferPhysicalMapping", (FUNC)ndis_vtophys_unload }, - { "NdisMInitializeTimer", (FUNC)ndis_create_timer }, - { "NdisInitializeTimer", (FUNC)ndis_init_timer }, - { "NdisSetTimer", (FUNC)ndis_set_timer }, - { "NdisMCancelTimer", (FUNC)ndis_cancel_timer }, - { "NdisCancelTimer", (FUNC)ndis_cancel_timer }, - { "NdisMSetPeriodicTimer", (FUNC)ndis_set_periodic_timer }, - { "NdisMQueryAdapterResources", (FUNC)ndis_query_resources }, - { "NdisMRegisterIoPortRange", (FUNC)ndis_register_ioport }, - { "NdisMDeregisterIoPortRange", (FUNC)ndis_deregister_ioport }, - { "NdisReadNetworkAddress", (FUNC)ndis_read_netaddr }, - { "NdisQueryMapRegisterCount", (FUNC)ndis_mapreg_cnt }, - { "NdisMAllocateMapRegisters", (FUNC)ndis_alloc_mapreg }, - { "NdisMFreeMapRegisters", (FUNC)ndis_free_mapreg }, - { "NdisMAllocateSharedMemory", (FUNC)ndis_alloc_sharedmem }, - { "NdisMMapIoSpace", (FUNC)ndis_map_iospace }, - { "NdisMUnmapIoSpace", (FUNC)ndis_unmap_iospace }, - { "NdisGetCacheFillSize", (FUNC)ndis_cachefill }, - { "NdisMGetDmaAlignment", (FUNC)ndis_dma_align }, - { "NdisMInitializeScatterGatherDma", (FUNC)ndis_init_sc_dma }, - { "NdisAllocatePacketPool", (FUNC)ndis_alloc_packetpool }, - { "NdisAllocatePacketPoolEx", (FUNC)ndis_ex_alloc_packetpool }, - { "NdisAllocatePacket", (FUNC)ndis_alloc_packet }, - { "NdisFreePacket", (FUNC)ndis_release_packet }, - { "NdisFreePacketPool", (FUNC)ndis_free_packetpool }, - { "NdisDprAllocatePacket", (FUNC)ndis_alloc_packet }, - { "NdisDprFreePacket", (FUNC)ndis_release_packet }, - { "NdisAllocateBufferPool", (FUNC)ndis_alloc_bufpool }, - { "NdisAllocateBuffer", (FUNC)ndis_alloc_buf }, - { "NdisQueryBuffer", (FUNC)ndis_query_buf }, - { "NdisQueryBufferSafe", (FUNC)ndis_query_buf_safe }, - { "NdisBufferVirtualAddress", (FUNC)ndis_buf_vaddr }, - { "NdisBufferVirtualAddressSafe", (FUNC)ndis_buf_vaddr_safe }, - { "NdisBufferLength", (FUNC)ndis_buflen }, - { "NdisFreeBuffer", (FUNC)ndis_release_buf }, - { "NdisFreeBufferPool", (FUNC)ndis_free_bufpool }, - { "NdisInterlockedIncrement", (FUNC)ndis_interlock_inc }, - { "NdisInterlockedDecrement", (FUNC)ndis_interlock_dec }, - { "NdisInitializeEvent", (FUNC)ndis_init_event }, - { "NdisSetEvent", (FUNC)ndis_set_event }, - { "NdisResetEvent", (FUNC)ndis_reset_event }, - { "NdisWaitEvent", (FUNC)ndis_wait_event }, - { "NdisUnicodeStringToAnsiString", (FUNC)ndis_unicode2ansi }, - { "NdisMPciAssignResources", (FUNC)ndis_assign_pcirsrc }, - { "NdisMFreeSharedMemory", (FUNC)ndis_free_sharedmem }, - { "NdisMRegisterInterrupt", (FUNC)ndis_register_intr }, - { "NdisMDeregisterInterrupt", (FUNC)ndis_deregister_intr }, - { "NdisMRegisterAdapterShutdownHandler", (FUNC)ndis_register_shutdown }, - { "NdisMDeregisterAdapterShutdownHandler", (FUNC)ndis_deregister_shutdown }, - { "NDIS_BUFFER_TO_SPAN_PAGES", (FUNC)ndis_numpages }, - { "NdisQueryBufferOffset", (FUNC)ndis_query_bufoffset }, - { "NdisAdjustBufferLength", (FUNC)ndis_adjust_buflen }, - { "NdisPacketPoolUsage", (FUNC)ndis_packetpool_use }, - { "NdisMSleep", (FUNC)ndis_sleep }, - { "NdisUnchainBufferAtFront", (FUNC)ndis_unchain_headbuf }, - { "NdisReadPcmciaAttributeMemory", (FUNC)ndis_read_pccard_amem }, - { "NdisWritePcmciaAttributeMemory", (FUNC)ndis_write_pccard_amem }, - { "NdisOpenFile", (FUNC)ndis_open_file }, - { "NdisMapFile", (FUNC)ndis_map_file }, - { "NdisUnmapFile", (FUNC)ndis_unmap_file }, - { "NdisCloseFile", (FUNC)ndis_close_file }, - { "NdisMRegisterDevice", (FUNC)ndis_register_dev }, - { "NdisMDeregisterDevice", (FUNC)ndis_deregister_dev }, - { "NdisMQueryAdapterInstanceName", (FUNC)ndis_query_name }, - { "NdisMRegisterUnloadHandler", (FUNC)ndis_register_unload }, + IMPORT_SFUNC(NdisCopyFromPacketToPacket, 6), + IMPORT_SFUNC(NdisCopyFromPacketToPacketSafe, 7), + IMPORT_SFUNC(NdisIMCopySendPerPacketInfo, 2), + IMPORT_SFUNC(NdisScheduleWorkItem, 1), + IMPORT_SFUNC(NdisMIndicateStatusComplete, 1), + IMPORT_SFUNC(NdisMIndicateStatus, 4), + IMPORT_SFUNC(NdisSystemProcessorCount, 0), + IMPORT_SFUNC(NdisGetCurrentProcessorCounts, 3), + IMPORT_SFUNC(NdisUnchainBufferAtBack, 2), + IMPORT_SFUNC(NdisGetFirstBufferFromPacket, 5), + IMPORT_SFUNC(NdisGetFirstBufferFromPacketSafe, 6), + IMPORT_SFUNC(NdisGetBufferPhysicalArraySize, 2), + IMPORT_SFUNC(NdisMGetDeviceProperty, 6), + IMPORT_SFUNC(NdisInitAnsiString, 2), + IMPORT_SFUNC(NdisInitUnicodeString, 2), + IMPORT_SFUNC(NdisWriteConfiguration, 4), + IMPORT_SFUNC(NdisAnsiStringToUnicodeString, 2), + IMPORT_SFUNC(NdisTerminateWrapper, 2), + IMPORT_SFUNC(NdisOpenConfigurationKeyByName, 4), + IMPORT_SFUNC(NdisOpenConfigurationKeyByIndex, 5), + IMPORT_SFUNC(NdisMRemoveMiniport, 1), + IMPORT_SFUNC(NdisInitializeString, 2), + IMPORT_SFUNC(NdisFreeString, 1), + IMPORT_SFUNC(NdisGetCurrentSystemTime, 1), + IMPORT_SFUNC(NdisGetRoutineAddress, 1), + IMPORT_SFUNC(NdisGetSystemUpTime, 1), + IMPORT_SFUNC(NdisGetVersion, 0), + IMPORT_SFUNC(NdisMSynchronizeWithInterrupt, 3), + IMPORT_SFUNC(NdisMAllocateSharedMemoryAsync, 4), + IMPORT_SFUNC(NdisInterlockedInsertHeadList, 3), + IMPORT_SFUNC(NdisInterlockedInsertTailList, 3), + IMPORT_SFUNC(NdisInterlockedRemoveHeadList, 2), + IMPORT_SFUNC(NdisInitializeWrapper, 4), + IMPORT_SFUNC(NdisMRegisterMiniport, 3), + IMPORT_SFUNC(NdisAllocateMemoryWithTag, 3), + IMPORT_SFUNC(NdisAllocateMemory, 4 + 1), + IMPORT_SFUNC(NdisMSetAttributesEx, 5), + IMPORT_SFUNC(NdisCloseConfiguration, 1), + IMPORT_SFUNC(NdisReadConfiguration, 5), + IMPORT_SFUNC(NdisOpenConfiguration, 3), + IMPORT_SFUNC(NdisAcquireSpinLock, 1), + IMPORT_SFUNC(NdisReleaseSpinLock, 1), + IMPORT_SFUNC(NdisDprAcquireSpinLock, 1), + IMPORT_SFUNC(NdisDprReleaseSpinLock, 1), + IMPORT_SFUNC(NdisAllocateSpinLock, 1), + IMPORT_SFUNC(NdisInitializeReadWriteLock, 1), + IMPORT_SFUNC(NdisAcquireReadWriteLock, 3), + IMPORT_SFUNC(NdisReleaseReadWriteLock, 2), + IMPORT_SFUNC(NdisFreeSpinLock, 1), + IMPORT_SFUNC(NdisFreeMemory, 3), + IMPORT_SFUNC(NdisReadPciSlotInformation, 5), + IMPORT_SFUNC(NdisWritePciSlotInformation, 5), + IMPORT_SFUNC_MAP(NdisImmediateReadPciSlotInformation, + NdisReadPciSlotInformation, 5), + IMPORT_SFUNC_MAP(NdisImmediateWritePciSlotInformation, + NdisWritePciSlotInformation, 5), + IMPORT_CFUNC(NdisWriteErrorLogEntry, 0), + IMPORT_SFUNC(NdisMStartBufferPhysicalMapping, 6), + IMPORT_SFUNC(NdisMCompleteBufferPhysicalMapping, 3), + IMPORT_SFUNC(NdisMInitializeTimer, 4), + IMPORT_SFUNC(NdisInitializeTimer, 3), + IMPORT_SFUNC(NdisSetTimer, 2), + IMPORT_SFUNC(NdisMCancelTimer, 2), + IMPORT_SFUNC_MAP(NdisCancelTimer, NdisMCancelTimer, 2), + IMPORT_SFUNC(NdisMSetPeriodicTimer, 2), + IMPORT_SFUNC(NdisMQueryAdapterResources, 4), + IMPORT_SFUNC(NdisMRegisterIoPortRange, 4), + IMPORT_SFUNC(NdisMDeregisterIoPortRange, 4), + IMPORT_SFUNC(NdisReadNetworkAddress, 4), + IMPORT_SFUNC(NdisQueryMapRegisterCount, 2), + IMPORT_SFUNC(NdisMAllocateMapRegisters, 5), + IMPORT_SFUNC(NdisMFreeMapRegisters, 1), + IMPORT_SFUNC(NdisMAllocateSharedMemory, 5), + IMPORT_SFUNC(NdisMMapIoSpace, 4 + 1), + IMPORT_SFUNC(NdisMUnmapIoSpace, 3), + IMPORT_SFUNC(NdisGetCacheFillSize, 0), + IMPORT_SFUNC(NdisMGetDmaAlignment, 1), + IMPORT_SFUNC(NdisMInitializeScatterGatherDma, 3), + IMPORT_SFUNC(NdisAllocatePacketPool, 4), + IMPORT_SFUNC(NdisAllocatePacketPoolEx, 5), + IMPORT_SFUNC(NdisAllocatePacket, 3), + IMPORT_SFUNC(NdisFreePacket, 1), + IMPORT_SFUNC(NdisFreePacketPool, 1), + IMPORT_SFUNC_MAP(NdisDprAllocatePacket, NdisAllocatePacket, 3), + IMPORT_SFUNC_MAP(NdisDprFreePacket, NdisFreePacket, 1), + IMPORT_SFUNC(NdisAllocateBufferPool, 3), + IMPORT_SFUNC(NdisAllocateBuffer, 5), + IMPORT_SFUNC(NdisQueryBuffer, 3), + IMPORT_SFUNC(NdisQueryBufferSafe, 4), + IMPORT_SFUNC(NdisBufferVirtualAddress, 1), + IMPORT_SFUNC(NdisBufferVirtualAddressSafe, 2), + IMPORT_SFUNC(NdisBufferLength, 1), + IMPORT_SFUNC(NdisFreeBuffer, 1), + IMPORT_SFUNC(NdisFreeBufferPool, 1), + IMPORT_SFUNC(NdisInterlockedIncrement, 1), + IMPORT_SFUNC(NdisInterlockedDecrement, 1), + IMPORT_SFUNC(NdisInitializeEvent, 1), + IMPORT_SFUNC(NdisSetEvent, 1), + IMPORT_SFUNC(NdisResetEvent, 1), + IMPORT_SFUNC(NdisWaitEvent, 2), + IMPORT_SFUNC(NdisUnicodeStringToAnsiString, 2), + IMPORT_SFUNC(NdisMPciAssignResources, 3), + IMPORT_SFUNC(NdisMFreeSharedMemory, 5 + 1), + IMPORT_SFUNC(NdisMRegisterInterrupt, 7), + IMPORT_SFUNC(NdisMDeregisterInterrupt, 1), + IMPORT_SFUNC(NdisMRegisterAdapterShutdownHandler, 3), + IMPORT_SFUNC(NdisMDeregisterAdapterShutdownHandler, 1), + IMPORT_SFUNC(NDIS_BUFFER_TO_SPAN_PAGES, 1), + IMPORT_SFUNC(NdisQueryBufferOffset, 3), + IMPORT_SFUNC(NdisAdjustBufferLength, 2), + IMPORT_SFUNC(NdisPacketPoolUsage, 1), + IMPORT_SFUNC(NdisMSleep, 1), + IMPORT_SFUNC(NdisUnchainBufferAtFront, 2), + IMPORT_SFUNC(NdisReadPcmciaAttributeMemory, 4), + IMPORT_SFUNC(NdisWritePcmciaAttributeMemory, 4), + IMPORT_SFUNC(NdisOpenFile, 5 + 1), + IMPORT_SFUNC(NdisMapFile, 3), + IMPORT_SFUNC(NdisUnmapFile, 1), + IMPORT_SFUNC(NdisCloseFile, 1), + IMPORT_SFUNC(NdisMRegisterDevice, 6), + IMPORT_SFUNC(NdisMDeregisterDevice, 1), + IMPORT_SFUNC(NdisMQueryAdapterInstanceName, 2), + IMPORT_SFUNC(NdisMRegisterUnloadHandler, 2), + IMPORT_SFUNC(ndis_timercall, 4), + IMPORT_SFUNC(ndis_asyncmem_complete, 2), + IMPORT_SFUNC(ndis_intr, 2), + IMPORT_SFUNC(ndis_intrhand, 4), /* * This last entry is a catch-all for any function we haven't @@ -2717,9 +3111,9 @@ image_patch_table ndis_functbl[] = { * in this table. */ - { NULL, (FUNC)dummy }, + { NULL, (FUNC)dummy, NULL, 0, WINDRV_WRAP_STDCALL }, /* End of list. */ - { NULL, NULL }, + { NULL, NULL, NULL } }; diff --git a/sys/emulation/ndis/subr_ntoskrnl.c b/sys/emulation/ndis/subr_ntoskrnl.c index 37a42a80cc..2e4dd1aa29 100644 --- a/sys/emulation/ndis/subr_ntoskrnl.c +++ b/sys/emulation/ndis/subr_ntoskrnl.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,8 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/compat/ndis/subr_ntoskrnl.c,v 1.40 2004/07/20 20:28:57 wpaul Exp $ - * $DragonFly: src/sys/emulation/ndis/subr_ntoskrnl.c,v 1.13 2006/12/23 00:27:02 swildner Exp $ + * $FreeBSD: src/sys/compat/ndis/subr_ntoskrnl.c,v 1.114 2011/02/21 09:01:34 brucec Exp $ */ #include @@ -41,555 +40,1778 @@ #include #include #include +#include +#include +#include #include -#if __FreeBSD_version > 502113 -#include -#endif #include #include +#include #include +#include +#include +#include + +#include +#include + #include #include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef NTOSKRNL_DEBUG_TIMERS +static int sysctl_show_timers(SYSCTL_HANDLER_ARGS); + +SYSCTL_PROC(_debug, OID_AUTO, ntoskrnl_timers, CTLTYPE_INT | CTLFLAG_RW, + NULL, 0, sysctl_show_timers, "I", + "Show ntoskrnl timer stats"); +#endif -#include +struct kdpc_queue { + list_entry kq_disp; + struct thread *kq_td; + int kq_cpu; + int kq_exit; + int kq_running; + kspin_lock kq_lock; + nt_kevent kq_proc; + nt_kevent kq_done; +}; -#include -#include -#include +typedef struct kdpc_queue kdpc_queue; -#include "regcall.h" -#include "pe_var.h" -#include "resource_var.h" -#include "ntoskrnl_var.h" -#include "ndis_var.h" -#include "hal_var.h" +struct wb_ext { + struct cv we_cv; + struct thread *we_td; +}; -#define __regparm __attribute__((regparm(3))) +typedef struct wb_ext wb_ext; -#define FUNC void(*)(void) +#define NTOSKRNL_TIMEOUTS 256 +#ifdef NTOSKRNL_DEBUG_TIMERS +static uint64_t ntoskrnl_timer_fires; +static uint64_t ntoskrnl_timer_sets; +static uint64_t ntoskrnl_timer_reloads; +static uint64_t ntoskrnl_timer_cancels; +#endif -__stdcall static uint8_t ntoskrnl_unicode_equal(ndis_unicode_string *, - ndis_unicode_string *, uint8_t); -__stdcall static void ntoskrnl_unicode_copy(ndis_unicode_string *, - ndis_unicode_string *); -__stdcall static ndis_status ntoskrnl_unicode_to_ansi(ndis_ansi_string *, - ndis_unicode_string *, uint8_t); -__stdcall static ndis_status ntoskrnl_ansi_to_unicode(ndis_unicode_string *, - ndis_ansi_string *, uint8_t); -__stdcall static void *ntoskrnl_iobuildsynchfsdreq(uint32_t, void *, - void *, uint32_t, uint32_t *, void *, void *); +struct callout_entry { + struct callout ce_callout; + list_entry ce_list; +}; -/* - * registerized calls - */ -__stdcall __regcall static uint32_t - ntoskrnl_iofcalldriver(REGARGS2(void *dobj, void *irp)); -__stdcall __regcall static void - ntoskrnl_iofcompletereq(REGARGS2(void *irp, uint8_t prioboost)); -__stdcall __regcall static slist_entry * - ntoskrnl_push_slist(REGARGS2(slist_header *head, slist_entry *entry)); -__stdcall __regcall static slist_entry * - ntoskrnl_pop_slist(REGARGS1(slist_header *head)); -__stdcall __regcall static slist_entry * - ntoskrnl_push_slist_ex(REGARGS2(slist_header *head, slist_entry *entry), kspin_lock *lock); -__stdcall __regcall static slist_entry * - ntoskrnl_pop_slist_ex(REGARGS2(slist_header *head, kspin_lock *lock)); - -__stdcall __regcall static uint32_t - ntoskrnl_interlock_inc(REGARGS1(volatile uint32_t *addend)); -__stdcall __regcall static uint32_t - ntoskrnl_interlock_dec(REGARGS1(volatile uint32_t *addend)); -__stdcall __regcall static void - ntoskrnl_interlock_addstat(REGARGS2(uint64_t *addend, uint32_t inc)); -__stdcall __regcall static void - ntoskrnl_objderef(REGARGS1(void *object)); - -__stdcall static uint32_t ntoskrnl_waitforobjs(uint32_t, +typedef struct callout_entry callout_entry; + +static struct list_entry ntoskrnl_calllist; +static struct mtx ntoskrnl_calllock; +struct kuser_shared_data kuser_shared_data; + +static struct list_entry ntoskrnl_intlist; +static kspin_lock ntoskrnl_intlock; + +static uint8_t RtlEqualUnicodeString(unicode_string *, + unicode_string *, uint8_t); +static void RtlCopyString(ansi_string *, const ansi_string *); +static void RtlCopyUnicodeString(unicode_string *, + unicode_string *); +static irp *IoBuildSynchronousFsdRequest(uint32_t, device_object *, + void *, uint32_t, uint64_t *, nt_kevent *, io_status_block *); +static irp *IoBuildAsynchronousFsdRequest(uint32_t, + device_object *, void *, uint32_t, uint64_t *, io_status_block *); +static irp *IoBuildDeviceIoControlRequest(uint32_t, + device_object *, void *, uint32_t, void *, uint32_t, + uint8_t, nt_kevent *, io_status_block *); +static irp *IoAllocateIrp(uint8_t, uint8_t); +static void IoReuseIrp(irp *, uint32_t); +static void IoFreeIrp(irp *); +static void IoInitializeIrp(irp *, uint16_t, uint8_t); +static irp *IoMakeAssociatedIrp(irp *, uint8_t); +static uint32_t KeWaitForMultipleObjects(uint32_t, nt_dispatch_header **, uint32_t, uint32_t, uint32_t, uint8_t, int64_t *, wait_block *); -static void ntoskrnl_wakeup(void *); +static void ntoskrnl_waittest(nt_dispatch_header *, uint32_t); +static void ntoskrnl_satisfy_wait(nt_dispatch_header *, struct thread *); +static void ntoskrnl_satisfy_multiple_waits(wait_block *); +static int ntoskrnl_is_signalled(nt_dispatch_header *, struct thread *); +static void ntoskrnl_insert_timer(ktimer *, int); +static void ntoskrnl_remove_timer(ktimer *); +#ifdef NTOSKRNL_DEBUG_TIMERS +static void ntoskrnl_show_timers(void); +#endif static void ntoskrnl_timercall(void *); -static void ntoskrnl_run_dpc(void *); -__stdcall static void ntoskrnl_writereg_ushort(uint16_t *, uint16_t); -__stdcall static uint16_t ntoskrnl_readreg_ushort(uint16_t *); -__stdcall static void ntoskrnl_writereg_ulong(uint32_t *, uint32_t); -__stdcall static uint32_t ntoskrnl_readreg_ulong(uint32_t *); -__stdcall static void ntoskrnl_writereg_uchar(uint8_t *, uint8_t); -__stdcall static uint8_t ntoskrnl_readreg_uchar(uint8_t *); -__stdcall static int64_t _allmul(int64_t, int64_t); -__stdcall static int64_t _alldiv(int64_t, int64_t); -__stdcall static int64_t _allrem(int64_t, int64_t); -__regparm static int64_t _allshr(int64_t, uint8_t); -__regparm static int64_t _allshl(int64_t, uint8_t); -__stdcall static uint64_t _aullmul(uint64_t, uint64_t); -__stdcall static uint64_t _aulldiv(uint64_t, uint64_t); -__stdcall static uint64_t _aullrem(uint64_t, uint64_t); -__regparm static uint64_t _aullshr(uint64_t, uint8_t); -__regparm static uint64_t _aullshl(uint64_t, uint8_t); -__stdcall static void *ntoskrnl_allocfunc(uint32_t, size_t, uint32_t); -__stdcall static void ntoskrnl_freefunc(void *); +static void ntoskrnl_dpc_thread(void *); +static void ntoskrnl_destroy_dpc_threads(void); +static void ntoskrnl_destroy_workitem_threads(void); +static void ntoskrnl_workitem_thread(void *); +static void ntoskrnl_workitem(device_object *, void *); +static void ntoskrnl_unicode_to_ascii(uint16_t *, char *, int); +static void ntoskrnl_ascii_to_unicode(char *, uint16_t *, int); +static uint8_t ntoskrnl_insert_dpc(list_entry *, kdpc *); +static void WRITE_REGISTER_USHORT(uint16_t *, uint16_t); +static uint16_t READ_REGISTER_USHORT(uint16_t *); +static void WRITE_REGISTER_ULONG(uint32_t *, uint32_t); +static uint32_t READ_REGISTER_ULONG(uint32_t *); +static void WRITE_REGISTER_UCHAR(uint8_t *, uint8_t); +static uint8_t READ_REGISTER_UCHAR(uint8_t *); +static int64_t _allmul(int64_t, int64_t); +static int64_t _alldiv(int64_t, int64_t); +static int64_t _allrem(int64_t, int64_t); +static int64_t _allshr(int64_t, uint8_t); +static int64_t _allshl(int64_t, uint8_t); +static uint64_t _aullmul(uint64_t, uint64_t); +static uint64_t _aulldiv(uint64_t, uint64_t); +static uint64_t _aullrem(uint64_t, uint64_t); +static uint64_t _aullshr(uint64_t, uint8_t); +static uint64_t _aullshl(uint64_t, uint8_t); static slist_entry *ntoskrnl_pushsl(slist_header *, slist_entry *); +static void InitializeSListHead(slist_header *); static slist_entry *ntoskrnl_popsl(slist_header *); -__stdcall static void ntoskrnl_init_lookaside(paged_lookaside_list *, +static void ExFreePoolWithTag(void *, uint32_t); +static void ExInitializePagedLookasideList(paged_lookaside_list *, lookaside_alloc_func *, lookaside_free_func *, uint32_t, size_t, uint32_t, uint16_t); -__stdcall static void ntoskrnl_delete_lookaside(paged_lookaside_list *); -__stdcall static void ntoskrnl_init_nplookaside(npaged_lookaside_list *, +static void ExDeletePagedLookasideList(paged_lookaside_list *); +static void ExInitializeNPagedLookasideList(npaged_lookaside_list *, lookaside_alloc_func *, lookaside_free_func *, uint32_t, size_t, uint32_t, uint16_t); -__stdcall static void ntoskrnl_delete_nplookaside(npaged_lookaside_list *); -__stdcall static void ntoskrnl_freemdl(ndis_buffer *); -__stdcall static uint32_t ntoskrnl_sizeofmdl(void *, size_t); -__stdcall static void ntoskrnl_build_npaged_mdl(ndis_buffer *); -__stdcall static void *ntoskrnl_mmaplockedpages(ndis_buffer *, uint8_t); -__stdcall static void *ntoskrnl_mmaplockedpages_cache(ndis_buffer *, +static void ExDeleteNPagedLookasideList(npaged_lookaside_list *); +static slist_entry + *ExInterlockedPushEntrySList(slist_header *, + slist_entry *, kspin_lock *); +static slist_entry + *ExInterlockedPopEntrySList(slist_header *, kspin_lock *); +static uint32_t InterlockedIncrement(volatile uint32_t *); +static uint32_t InterlockedDecrement(volatile uint32_t *); +static void ExInterlockedAddLargeStatistic(uint64_t *, uint32_t); +static void *MmAllocateContiguousMemory(uint32_t, uint64_t); +static void *MmAllocateContiguousMemorySpecifyCache(uint32_t, + uint64_t, uint64_t, uint64_t, enum nt_caching_type); +static void MmFreeContiguousMemory(void *); +static void MmFreeContiguousMemorySpecifyCache(void *, uint32_t, + enum nt_caching_type); +static uint32_t MmSizeOfMdl(void *, size_t); +static void *MmMapLockedPages(mdl *, uint8_t); +static void *MmMapLockedPagesSpecifyCache(mdl *, uint8_t, uint32_t, void *, uint32_t, uint32_t); -__stdcall static void ntoskrnl_munmaplockedpages(void *, ndis_buffer *); -__stdcall static void ntoskrnl_init_lock(kspin_lock *); -__stdcall static size_t ntoskrnl_memcmp(const void *, const void *, size_t); -__stdcall static void ntoskrnl_init_ansi_string(ndis_ansi_string *, char *); -__stdcall static void ntoskrnl_init_unicode_string(ndis_unicode_string *, - uint16_t *); -__stdcall static void ntoskrnl_free_unicode_string(ndis_unicode_string *); -__stdcall static void ntoskrnl_free_ansi_string(ndis_ansi_string *); -__stdcall static ndis_status ntoskrnl_unicode_to_int(ndis_unicode_string *, +static void MmUnmapLockedPages(void *, mdl *); +static device_t ntoskrnl_finddev(device_t, uint64_t, struct resource **); +static void RtlZeroMemory(void *, size_t); +static void RtlSecureZeroMemory(void *, size_t); +static void RtlFillMemory(void *, size_t, uint8_t); +static void RtlMoveMemory(void *, const void *, size_t); +static ndis_status RtlCharToInteger(const char *, uint32_t, uint32_t *); +static void RtlCopyMemory(void *, const void *, size_t); +static size_t RtlCompareMemory(const void *, const void *, size_t); +static ndis_status RtlUnicodeStringToInteger(unicode_string *, uint32_t, uint32_t *); static int atoi (const char *); static long atol (const char *); static int rand(void); -static void ntoskrnl_time(uint64_t *); -__stdcall static uint8_t ntoskrnl_wdmver(uint8_t, uint8_t); +static void srand(unsigned int); +static void KeQuerySystemTime(uint64_t *); +static uint32_t KeTickCount(void); +static uint8_t IoIsWdmVersionAvailable(uint8_t, uint8_t); +static int32_t IoOpenDeviceRegistryKey(struct device_object *, uint32_t, + uint32_t, void **); static void ntoskrnl_thrfunc(void *); -__stdcall static ndis_status ntoskrnl_create_thread(ndis_handle *, +static ndis_status PsCreateSystemThread(ndis_handle *, uint32_t, void *, ndis_handle, void *, void *, void *); -__stdcall static ndis_status ntoskrnl_thread_exit(ndis_status); -__stdcall static ndis_status ntoskrnl_devprop(device_object *, uint32_t, +static ndis_status PsTerminateSystemThread(ndis_status); +static ndis_status IoGetDeviceObjectPointer(unicode_string *, + uint32_t, void *, device_object *); +static ndis_status IoGetDeviceProperty(device_object *, uint32_t, uint32_t, void *, uint32_t *); -__stdcall static void ntoskrnl_init_mutex(kmutant *, uint32_t); -__stdcall static uint32_t ntoskrnl_release_mutex(kmutant *, uint8_t); -__stdcall static uint32_t ntoskrnl_read_mutex(kmutant *); -__stdcall static ndis_status ntoskrnl_objref(ndis_handle, uint32_t, void *, - uint8_t, void **, void **); -__stdcall static uint32_t ntoskrnl_zwclose(ndis_handle); -static uint32_t ntoskrnl_dbgprint(char *, ...); -__stdcall static void ntoskrnl_debugger(void); -__stdcall static void dummy(void); - -static struct lwkt_token ntoskrnl_dispatchtoken; -static kspin_lock ntoskrnl_global; +static void KeInitializeMutex(kmutant *, uint32_t); +static uint32_t KeReleaseMutex(kmutant *, uint8_t); +static uint32_t KeReadStateMutex(kmutant *); +static ndis_status ObReferenceObjectByHandle(ndis_handle, + uint32_t, void *, uint8_t, void **, void **); +static void ObfDereferenceObject(void *); +static uint32_t ZwClose(ndis_handle); +static uint32_t WmiQueryTraceInformation(uint32_t, void *, uint32_t, + uint32_t, void *); +static uint32_t WmiTraceMessage(uint64_t, uint32_t, void *, uint16_t, ...); +static uint32_t IoWMIRegistrationControl(device_object *, uint32_t); +static void *ntoskrnl_memset(void *, int, size_t); +static void *ntoskrnl_memmove(void *, void *, size_t); +static void *ntoskrnl_memchr(void *, unsigned char, size_t); +static char *ntoskrnl_strstr(char *, char *); +static char *ntoskrnl_strncat(char *, char *, size_t); +static int ntoskrnl_toupper(int); +static int ntoskrnl_tolower(int); +static funcptr ntoskrnl_findwrap(funcptr); +static uint32_t DbgPrint(char *, ...); +static void DbgBreakPoint(void); +static void KeBugCheckEx(uint32_t, u_long, u_long, u_long, u_long); +static int32_t KeDelayExecutionThread(uint8_t, uint8_t, int64_t *); +static int32_t KeSetPriorityThread(struct thread *, int32_t); +static void dummy(void); + +static struct lock ntoskrnl_dispatchlock; +static struct mtx ntoskrnl_interlock; +static kspin_lock ntoskrnl_cancellock; static int ntoskrnl_kth = 0; static struct nt_objref_head ntoskrnl_reflist; - -static MALLOC_DEFINE(M_NDIS, "ndis", "ndis emulation"); +static struct objcache *mdl_cache; +static struct objcache *iw_cache; +static struct kdpc_queue *kq_queues; +static struct kdpc_queue *wq_queues; +static int wq_idx = 0; + +static struct objcache_malloc_args mdl_alloc_args = { + MDL_ZONE_SIZE, M_DEVBUF +}; +static struct objcache_malloc_args iw_alloc_args = { + sizeof(io_workitem), M_DEVBUF +}; int ntoskrnl_libinit(void) { - lwkt_token_init(&ntoskrnl_dispatchtoken, "ndiskrnl"); - ntoskrnl_init_lock(&ntoskrnl_global); + image_patch_table *patch; + int error; + struct thread *p; + kdpc_queue *kq; + callout_entry *e; + int i; + + lockinit(&ntoskrnl_dispatchlock, MTX_NDIS_LOCK, 0, LK_CANRECURSE); + mtx_init(&ntoskrnl_interlock); + KeInitializeSpinLock(&ntoskrnl_cancellock); + KeInitializeSpinLock(&ntoskrnl_intlock); TAILQ_INIT(&ntoskrnl_reflist); - return(0); + + InitializeListHead(&ntoskrnl_calllist); + InitializeListHead(&ntoskrnl_intlist); + mtx_init(&ntoskrnl_calllock); + + kq_queues = ExAllocatePoolWithTag(NonPagedPool, +#ifdef NTOSKRNL_MULTIPLE_DPCS + sizeof(kdpc_queue) * ncpus, 0); +#else + sizeof(kdpc_queue), 0); +#endif + + if (kq_queues == NULL) + return (ENOMEM); + + wq_queues = ExAllocatePoolWithTag(NonPagedPool, + sizeof(kdpc_queue) * WORKITEM_THREADS, 0); + + if (wq_queues == NULL) + return (ENOMEM); + +#ifdef NTOSKRNL_MULTIPLE_DPCS + bzero((char *)kq_queues, sizeof(kdpc_queue) * ncpus); +#else + bzero((char *)kq_queues, sizeof(kdpc_queue)); +#endif + bzero((char *)wq_queues, sizeof(kdpc_queue) * WORKITEM_THREADS); + + /* + * Launch the DPC threads. + */ + +#ifdef NTOSKRNL_MULTIPLE_DPCS + for (i = 0; i < ncpus; i++) { +#else + for (i = 0; i < 1; i++) { +#endif + kq = kq_queues + i; + kq->kq_cpu = i; + error = kthread_create_cpu(ntoskrnl_dpc_thread, kq, &p, i, + "Win DPC %d", i); + if (error) + panic("failed to launch DPC thread"); + } + + /* + * Launch the workitem threads. + */ + + for (i = 0; i < WORKITEM_THREADS; i++) { + kq = wq_queues + i; + error = kthread_create(ntoskrnl_workitem_thread, kq, &p, + "Win Workitem %d", i); + if (error) + panic("failed to launch workitem thread"); + } + + patch = ntoskrnl_functbl; + while (patch->ipt_func != NULL) { + windrv_wrap((funcptr)patch->ipt_func, + (funcptr *)&patch->ipt_wrap, + patch->ipt_argcnt, patch->ipt_ftype); + patch++; + } + + for (i = 0; i < NTOSKRNL_TIMEOUTS; i++) { + e = ExAllocatePoolWithTag(NonPagedPool, + sizeof(callout_entry), 0); + if (e == NULL) + panic("failed to allocate timeouts"); + mtx_spinlock(&ntoskrnl_calllock); + InsertHeadList((&ntoskrnl_calllist), (&e->ce_list)); + mtx_spinunlock(&ntoskrnl_calllock); + } + + /* + * MDLs are supposed to be variable size (they describe + * buffers containing some number of pages, but we don't + * know ahead of time how many pages that will be). But + * always allocating them off the heap is very slow. As + * a compromise, we create an MDL UMA zone big enough to + * handle any buffer requiring up to 16 pages, and we + * use those for any MDLs for buffers of 16 pages or less + * in size. For buffers larger than that (which we assume + * will be few and far between, we allocate the MDLs off + * the heap. + * + * CHANGED TO USING objcache(9) IN DRAGONFLY + */ + + mdl_cache = objcache_create("Windows MDL", 0, 0, + NULL, NULL, NULL, objcache_malloc_alloc, objcache_malloc_free, + &mdl_alloc_args); + + iw_cache = objcache_create("Windows WorkItem", 0, 0, + NULL, NULL, NULL, objcache_malloc_alloc, objcache_malloc_free, + &iw_alloc_args); + + return (0); } int ntoskrnl_libfini(void) { - lwkt_token_uninit(&ntoskrnl_dispatchtoken); - return(0); + image_patch_table *patch; + callout_entry *e; + list_entry *l; + + patch = ntoskrnl_functbl; + while (patch->ipt_func != NULL) { + windrv_unwrap(patch->ipt_wrap); + patch++; + } + + /* Stop the workitem queues. */ + ntoskrnl_destroy_workitem_threads(); + /* Stop the DPC queues. */ + ntoskrnl_destroy_dpc_threads(); + + ExFreePool(kq_queues); + ExFreePool(wq_queues); + + objcache_destroy(mdl_cache); + objcache_destroy(iw_cache); + + mtx_spinlock(&ntoskrnl_calllock); + while(!IsListEmpty(&ntoskrnl_calllist)) { + l = RemoveHeadList(&ntoskrnl_calllist); + e = CONTAINING_RECORD(l, callout_entry, ce_list); + mtx_spinunlock(&ntoskrnl_calllock); + ExFreePool(e); + mtx_spinlock(&ntoskrnl_calllock); + } + mtx_spinunlock(&ntoskrnl_calllock); + + lockuninit(&ntoskrnl_dispatchlock); + mtx_uninit(&ntoskrnl_interlock); + mtx_uninit(&ntoskrnl_calllock); + + return (0); +} + +/* + * We need to be able to reference this externally from the wrapper; + * GCC only generates a local implementation of memset. + */ +static void * +ntoskrnl_memset(void *buf, int ch, size_t size) +{ + return (memset(buf, ch, size)); +} + +static void * +ntoskrnl_memmove(void *dst, void *src, size_t size) +{ + bcopy(src, dst, size); + return (dst); +} + +static void * +ntoskrnl_memchr(void *buf, unsigned char ch, size_t len) +{ + if (len != 0) { + unsigned char *p = buf; + + do { + if (*p++ == ch) + return (p - 1); + } while (--len != 0); + } + return (NULL); +} + +static char * +ntoskrnl_strstr(char *s, char *find) +{ + char c, sc; + size_t len; + + if ((c = *find++) != 0) { + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return (NULL); + } while (sc != c); + } while (strncmp(s, find, len) != 0); + s--; + } + return ((char *)s); +} + +/* Taken from libc */ +static char * +ntoskrnl_strncat(char *dst, char *src, size_t n) +{ + if (n != 0) { + char *d = dst; + const char *s = src; + + while (*d != 0) + d++; + do { + if ((*d = *s++) == 0) + break; + d++; + } while (--n != 0); + *d = 0; + } + return (dst); +} + +static int +ntoskrnl_toupper(int c) +{ + return (toupper(c)); +} + +static int +ntoskrnl_tolower(int c) +{ + return (tolower(c)); } -__stdcall static uint8_t -ntoskrnl_unicode_equal(ndis_unicode_string *str1, - ndis_unicode_string *str2, - uint8_t caseinsensitive) +static uint8_t +RtlEqualUnicodeString(unicode_string *str1, unicode_string *str2, + uint8_t caseinsensitive) { int i; - if (str1->nus_len != str2->nus_len) - return(FALSE); + if (str1->us_len != str2->us_len) + return (FALSE); - for (i = 0; i < str1->nus_len; i++) { + for (i = 0; i < str1->us_len; i++) { if (caseinsensitive == TRUE) { - if (toupper((char)(str1->nus_buf[i] & 0xFF)) != - toupper((char)(str2->nus_buf[i] & 0xFF))) - return(FALSE); + if (toupper((char)(str1->us_buf[i] & 0xFF)) != + toupper((char)(str2->us_buf[i] & 0xFF))) + return (FALSE); } else { - if (str1->nus_buf[i] != str2->nus_buf[i]) - return(FALSE); + if (str1->us_buf[i] != str2->us_buf[i]) + return (FALSE); } } - return(TRUE); + return (TRUE); +} + +static void +RtlCopyString(ansi_string *dst, const ansi_string *src) +{ + if (src != NULL && src->as_buf != NULL && dst->as_buf != NULL) { + dst->as_len = min(src->as_len, dst->as_maxlen); + memcpy(dst->as_buf, src->as_buf, dst->as_len); + if (dst->as_len < dst->as_maxlen) + dst->as_buf[dst->as_len] = 0; + } else + dst->as_len = 0; } -__stdcall static void -ntoskrnl_unicode_copy(ndis_unicode_string *dest, - ndis_unicode_string *src) +static void +RtlCopyUnicodeString(unicode_string *dest, unicode_string *src) { - if (dest->nus_maxlen >= src->nus_len) - dest->nus_len = src->nus_len; + if (dest->us_maxlen >= src->us_len) + dest->us_len = src->us_len; else - dest->nus_len = dest->nus_maxlen; - memcpy(dest->nus_buf, src->nus_buf, dest->nus_len); - return; + dest->us_len = dest->us_maxlen; + memcpy(dest->us_buf, src->us_buf, dest->us_len); +} + +static void +ntoskrnl_ascii_to_unicode(char *ascii, uint16_t *unicode, int len) +{ + int i; + uint16_t *ustr; + + ustr = unicode; + for (i = 0; i < len; i++) { + *ustr = (uint16_t)ascii[i]; + ustr++; + } } -__stdcall static ndis_status -ntoskrnl_unicode_to_ansi(ndis_ansi_string *dest, - ndis_unicode_string *src, - uint8_t allocate) +static void +ntoskrnl_unicode_to_ascii(uint16_t *unicode, char *ascii, int len) { - char *astr = NULL; + int i; + uint8_t *astr; + + astr = ascii; + for (i = 0; i < len / 2; i++) { + *astr = (uint8_t)unicode[i]; + astr++; + } +} +uint32_t +RtlUnicodeStringToAnsiString(ansi_string *dest, unicode_string *src, uint8_t allocate) +{ if (dest == NULL || src == NULL) - return(NDIS_STATUS_FAILURE); + return (STATUS_INVALID_PARAMETER); + + dest->as_len = src->us_len / 2; + if (dest->as_maxlen < dest->as_len) + dest->as_len = dest->as_maxlen; if (allocate == TRUE) { - if (ndis_unicode_to_ascii(src->nus_buf, src->nus_len, &astr)) - return(NDIS_STATUS_FAILURE); - dest->nas_buf = astr; - dest->nas_len = dest->nas_maxlen = strlen(astr); + dest->as_buf = ExAllocatePoolWithTag(NonPagedPool, + (src->us_len / 2) + 1, 0); + if (dest->as_buf == NULL) + return (STATUS_INSUFFICIENT_RESOURCES); + dest->as_len = dest->as_maxlen = src->us_len / 2; } else { - dest->nas_len = src->nus_len / 2; /* XXX */ - if (dest->nas_maxlen < dest->nas_len) - dest->nas_len = dest->nas_maxlen; - ndis_unicode_to_ascii(src->nus_buf, dest->nas_len * 2, - &dest->nas_buf); + dest->as_len = src->us_len / 2; /* XXX */ + if (dest->as_maxlen < dest->as_len) + dest->as_len = dest->as_maxlen; } - return (NDIS_STATUS_SUCCESS); + + ntoskrnl_unicode_to_ascii(src->us_buf, dest->as_buf, + dest->as_len * 2); + + return (STATUS_SUCCESS); } -__stdcall static ndis_status -ntoskrnl_ansi_to_unicode(ndis_unicode_string *dest, - ndis_ansi_string *src, - uint8_t allocate) +uint32_t +RtlAnsiStringToUnicodeString(unicode_string *dest, ansi_string *src, + uint8_t allocate) { - uint16_t *ustr = NULL; - if (dest == NULL || src == NULL) - return(NDIS_STATUS_FAILURE); + return (STATUS_INVALID_PARAMETER); if (allocate == TRUE) { - if (ndis_ascii_to_unicode(src->nas_buf, &ustr)) - return(NDIS_STATUS_FAILURE); - dest->nus_buf = ustr; - dest->nus_len = dest->nus_maxlen = strlen(src->nas_buf) * 2; + dest->us_buf = ExAllocatePoolWithTag(NonPagedPool, + src->as_len * 2, 0); + if (dest->us_buf == NULL) + return (STATUS_INSUFFICIENT_RESOURCES); + dest->us_len = dest->us_maxlen = strlen(src->as_buf) * 2; } else { - dest->nus_len = src->nas_len * 2; /* XXX */ - if (dest->nus_maxlen < dest->nus_len) - dest->nus_len = dest->nus_maxlen; - ndis_ascii_to_unicode(src->nas_buf, &dest->nus_buf); + dest->us_len = src->as_len * 2; /* XXX */ + if (dest->us_maxlen < dest->us_len) + dest->us_len = dest->us_maxlen; } - return (NDIS_STATUS_SUCCESS); + + ntoskrnl_ascii_to_unicode(src->as_buf, dest->us_buf, + dest->us_len / 2); + + return (STATUS_SUCCESS); } -__stdcall static void * -ntoskrnl_iobuildsynchfsdreq(uint32_t func, void *dobj, void *buf, - uint32_t len, uint32_t *off, - void *event, void *status) +void * +ExAllocatePoolWithTag(uint32_t pooltype, size_t len, uint32_t tag) { - return(NULL); + void *buf; + + buf = kmalloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); + if (buf == NULL) + return (NULL); + + return (buf); } - -__stdcall __regcall static uint32_t -ntoskrnl_iofcalldriver(REGARGS2(void *dobj, void *irp)) + +static void +ExFreePoolWithTag(void *buf, uint32_t tag) { - return(0); + ExFreePool(buf); } -__stdcall __regcall static void -ntoskrnl_iofcompletereq(REGARGS2(void *irp, uint8_t prioboost)) +void +ExFreePool(void *buf) { + kfree(buf, M_DEVBUF); } -static void -ntoskrnl_wakeup(void *arg) +uint32_t +IoAllocateDriverObjectExtension(driver_object *drv, void *clid, + uint32_t extlen, void **ext) { - nt_dispatch_header *obj; - wait_block *w; - list_entry *e; - struct thread *td; + custom_extension *ce; - obj = arg; + ce = ExAllocatePoolWithTag(NonPagedPool, sizeof(custom_extension) + + extlen, 0); - lwkt_gettoken(&ntoskrnl_dispatchtoken); - obj->dh_sigstate = TRUE; - e = obj->dh_waitlisthead.nle_flink; - while (e != &obj->dh_waitlisthead) { - w = (wait_block *)e; - td = w->wb_kthread; - ndis_thresume(td); - /* - * For synchronization objects, only wake up - * the first waiter. - */ - if (obj->dh_type == EVENT_TYPE_SYNC) - break; - e = e->nle_flink; - } - lwkt_reltoken(&ntoskrnl_dispatchtoken); + if (ce == NULL) + return (STATUS_INSUFFICIENT_RESOURCES); + + ce->ce_clid = clid; + InsertTailList((&drv->dro_driverext->dre_usrext), (&ce->ce_list)); + + *ext = (void *)(ce + 1); + + return (STATUS_SUCCESS); } -static void -ntoskrnl_time(uint64_t *tval) +void * +IoGetDriverObjectExtension(driver_object *drv, void *clid) { - struct timespec ts; + list_entry *e; + custom_extension *ce; - nanotime(&ts); - *tval = (uint64_t)ts.tv_nsec / 100 + (uint64_t)ts.tv_sec * 10000000 + - 11644473600LL; + /* + * Sanity check. Our dummy bus drivers don't have + * any driver extentions. + */ + + if (drv->dro_driverext == NULL) + return (NULL); + + e = drv->dro_driverext->dre_usrext.nle_flink; + while (e != &drv->dro_driverext->dre_usrext) { + ce = (custom_extension *)e; + if (ce->ce_clid == clid) + return ((void *)(ce + 1)); + e = e->nle_flink; + } - return; + return (NULL); } -/* - * KeWaitForSingleObject() is a tricky beast, because it can be used - * with several different object types: semaphores, timers, events, - * mutexes and threads. Semaphores don't appear very often, but the - * other object types are quite common. KeWaitForSingleObject() is - * what's normally used to acquire a mutex, and it can be used to - * wait for a thread termination. - * - * The Windows NDIS API is implemented in terms of Windows kernel - * primitives, and some of the object manipulation is duplicated in - * NDIS. For example, NDIS has timers and events, which are actually - * Windows kevents and ktimers. Now, you're supposed to only use the - * NDIS variants of these objects within the confines of the NDIS API, - * but there are some naughty developers out there who will use - * KeWaitForSingleObject() on NDIS timer and event objects, so we - * have to support that as well. Conseqently, our NDIS timer and event - * code has to be closely tied into our ntoskrnl timer and event code, - * just as it is in Windows. - * - * KeWaitForSingleObject() may do different things for different kinds - * of objects: - * - * - For events, we check if the event has been signalled. If the - * event is already in the signalled state, we just return immediately, - * otherwise we wait for it to be set to the signalled state by someone - * else calling KeSetEvent(). Events can be either synchronization or - * notification events. - * - * - For timers, if the timer has already fired and the timer is in - * the signalled state, we just return, otherwise we wait on the - * timer. Unlike an event, timers get signalled automatically when - * they expire rather than someone having to trip them manually. - * Timers initialized with KeInitializeTimer() are always notification - * events: KeInitializeTimerEx() lets you initialize a timer as - * either a notification or synchronization event. - * - * - For mutexes, we try to acquire the mutex and if we can't, we wait - * on the mutex until it's available and then grab it. When a mutex is - * released, it enters the signaled state, which wakes up one of the - * threads waiting to acquire it. Mutexes are always synchronization - * events. - * - * - For threads, the only thing we do is wait until the thread object - * enters a signalled state, which occurs when the thread terminates. - * Threads are always notification events. - * - * A notification event wakes up all threads waiting on an object. A - * synchronization event wakes up just one. Also, a synchronization event - * is auto-clearing, which means we automatically set the event back to - * the non-signalled state once the wakeup is done. - */ -__stdcall uint32_t -ntoskrnl_waitforobj(nt_dispatch_header *obj, uint32_t reason, - uint32_t mode, uint8_t alertable, int64_t *duetime) +uint32_t +IoCreateDevice(driver_object *drv, uint32_t devextlen, unicode_string *devname, + uint32_t devtype, uint32_t devchars, uint8_t exclusive, + device_object **newdev) { - struct thread *td = curthread; - kmutant *km; - wait_block w; - struct timeval tv; - int error = 0; - int ticks; - uint64_t curtime; + device_object *dev; - if (obj == NULL) - return(STATUS_INVALID_PARAMETER); + dev = ExAllocatePoolWithTag(NonPagedPool, sizeof(device_object), 0); + if (dev == NULL) + return (STATUS_INSUFFICIENT_RESOURCES); + + dev->do_type = devtype; + dev->do_drvobj = drv; + dev->do_currirp = NULL; + dev->do_flags = 0; - lwkt_gettoken(&ntoskrnl_dispatchtoken); + if (devextlen) { + dev->do_devext = ExAllocatePoolWithTag(NonPagedPool, + devextlen, 0); + + if (dev->do_devext == NULL) { + ExFreePool(dev); + return (STATUS_INSUFFICIENT_RESOURCES); + } + + bzero(dev->do_devext, devextlen); + } else + dev->do_devext = NULL; + + dev->do_size = sizeof(device_object) + devextlen; + dev->do_refcnt = 1; + dev->do_attacheddev = NULL; + dev->do_nextdev = NULL; + dev->do_devtype = devtype; + dev->do_stacksize = 1; + dev->do_alignreq = 1; + dev->do_characteristics = devchars; + dev->do_iotimer = NULL; + KeInitializeEvent(&dev->do_devlock, EVENT_TYPE_SYNC, TRUE); /* - * See if the object is a mutex. If so, and we already own - * it, then just increment the acquisition count and return. - * - * For any other kind of object, see if it's already in the - * signalled state, and if it is, just return. If the object - * is marked as a synchronization event, reset the state to - * unsignalled. + * Vpd is used for disk/tape devices, + * but we don't support those. (Yet.) */ + dev->do_vpb = NULL; - if (obj->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj; - if (km->km_ownerthread == NULL || - km->km_ownerthread == curthread->td_proc) { - obj->dh_sigstate = FALSE; - km->km_acquirecnt++; - km->km_ownerthread = curthread->td_proc; - lwkt_reltoken(&ntoskrnl_dispatchtoken); - return (STATUS_SUCCESS); - } - } else if (obj->dh_sigstate == TRUE) { - if (obj->dh_type == EVENT_TYPE_SYNC) - obj->dh_sigstate = FALSE; - lwkt_reltoken(&ntoskrnl_dispatchtoken); - return (STATUS_SUCCESS); - } + dev->do_devobj_ext = ExAllocatePoolWithTag(NonPagedPool, + sizeof(devobj_extension), 0); - w.wb_object = obj; - w.wb_kthread = td; + if (dev->do_devobj_ext == NULL) { + if (dev->do_devext != NULL) + ExFreePool(dev->do_devext); + ExFreePool(dev); + return (STATUS_INSUFFICIENT_RESOURCES); + } - INSERT_LIST_TAIL((&obj->dh_waitlisthead), (&w.wb_waitlist)); + dev->do_devobj_ext->dve_type = 0; + dev->do_devobj_ext->dve_size = sizeof(devobj_extension); + dev->do_devobj_ext->dve_devobj = dev; /* - * The timeout value is specified in 100 nanosecond units - * and can be a positive or negative number. If it's positive, - * then the duetime is absolute, and we need to convert it - * to an absolute offset relative to now in order to use it. - * If it's negative, then the duetime is relative and we - * just have to convert the units. + * Attach this device to the driver object's list + * of devices. Note: this is not the same as attaching + * the device to the device stack. The driver's AddDevice + * routine must explicitly call IoAddDeviceToDeviceStack() + * to do that. */ - if (duetime != NULL) { - if (*duetime < 0) { - tv.tv_sec = - (*duetime) / 10000000; - tv.tv_usec = (- (*duetime) / 10) - - (tv.tv_sec * 1000000); - } else { - ntoskrnl_time(&curtime); - if (*duetime < curtime) - tv.tv_sec = tv.tv_usec = 0; - else { - tv.tv_sec = ((*duetime) - curtime) / 10000000; - tv.tv_usec = ((*duetime) - curtime) / 10 - - (tv.tv_sec * 1000000); - } - } + if (drv->dro_devobj == NULL) { + drv->dro_devobj = dev; + dev->do_nextdev = NULL; + } else { + dev->do_nextdev = drv->dro_devobj; + drv->dro_devobj = dev; } - lwkt_reltoken(&ntoskrnl_dispatchtoken); + *newdev = dev; - ticks = 1 + tv.tv_sec * hz + tv.tv_usec * hz / 1000000; - error = ndis_thsuspend(td, duetime == NULL ? 0 : ticks); + return (STATUS_SUCCESS); +} - lwkt_gettoken(&tokref, &ntoskrnl_dispatchtoken); +void +IoDeleteDevice(device_object *dev) +{ + device_object *prev; - /* We timed out. Leave the object alone and return status. */ + if (dev == NULL) + return; - if (error == EWOULDBLOCK) { - REMOVE_LIST_ENTRY((&w.wb_waitlist)); - lwkt_reltoken(&ntoskrnl_dispatchtoken); - return(STATUS_TIMEOUT); - } + if (dev->do_devobj_ext != NULL) + ExFreePool(dev->do_devobj_ext); - /* - * Mutexes are always synchronization objects, which means - * if several threads are waiting to acquire it, only one will - * be woken up. If that one is us, and the mutex is up for grabs, - * grab it. - */ + if (dev->do_devext != NULL) + ExFreePool(dev->do_devext); - if (obj->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj; - if (km->km_ownerthread == NULL) { - km->km_ownerthread = curthread->td_proc; - km->km_acquirecnt++; - } + /* Unlink the device from the driver's device list. */ + + prev = dev->do_drvobj->dro_devobj; + if (prev == dev) + dev->do_drvobj->dro_devobj = dev->do_nextdev; + else { + while (prev->do_nextdev != dev) + prev = prev->do_nextdev; + prev->do_nextdev = dev->do_nextdev; } - if (obj->dh_type == EVENT_TYPE_SYNC) - obj->dh_sigstate = FALSE; - REMOVE_LIST_ENTRY((&w.wb_waitlist)); + ExFreePool(dev); +} + +device_object * +IoGetAttachedDevice(device_object *dev) +{ + device_object *d; + + if (dev == NULL) + return (NULL); + + d = dev; - lwkt_reltoken(&ntoskrnl_dispatchtoken); + while (d->do_attacheddev != NULL) + d = d->do_attacheddev; - return(STATUS_SUCCESS); + return (d); } -__stdcall static uint32_t -ntoskrnl_waitforobjs(uint32_t cnt, nt_dispatch_header *obj[], - uint32_t wtype, uint32_t reason, uint32_t mode, - uint8_t alertable, int64_t *duetime, - wait_block *wb_array) +static irp * +IoBuildSynchronousFsdRequest(uint32_t func, device_object *dobj, void *buf, + uint32_t len, uint64_t *off, nt_kevent *event, io_status_block *status) +{ + irp *ip; + + ip = IoBuildAsynchronousFsdRequest(func, dobj, buf, len, off, status); + if (ip == NULL) + return (NULL); + ip->irp_usrevent = event; + + return (ip); +} + +static irp * +IoBuildAsynchronousFsdRequest(uint32_t func, device_object *dobj, void *buf, + uint32_t len, uint64_t *off, io_status_block *status) +{ + irp *ip; + io_stack_location *sl; + + ip = IoAllocateIrp(dobj->do_stacksize, TRUE); + if (ip == NULL) + return (NULL); + + ip->irp_usriostat = status; + ip->irp_tail.irp_overlay.irp_thread = NULL; + + sl = IoGetNextIrpStackLocation(ip); + sl->isl_major = func; + sl->isl_minor = 0; + sl->isl_flags = 0; + sl->isl_ctl = 0; + sl->isl_devobj = dobj; + sl->isl_fileobj = NULL; + sl->isl_completionfunc = NULL; + + ip->irp_userbuf = buf; + + if (dobj->do_flags & DO_BUFFERED_IO) { + ip->irp_assoc.irp_sysbuf = + ExAllocatePoolWithTag(NonPagedPool, len, 0); + if (ip->irp_assoc.irp_sysbuf == NULL) { + IoFreeIrp(ip); + return (NULL); + } + bcopy(buf, ip->irp_assoc.irp_sysbuf, len); + } + + if (dobj->do_flags & DO_DIRECT_IO) { + ip->irp_mdl = IoAllocateMdl(buf, len, FALSE, FALSE, ip); + if (ip->irp_mdl == NULL) { + if (ip->irp_assoc.irp_sysbuf != NULL) + ExFreePool(ip->irp_assoc.irp_sysbuf); + IoFreeIrp(ip); + return (NULL); + } + ip->irp_userbuf = NULL; + ip->irp_assoc.irp_sysbuf = NULL; + } + + if (func == IRP_MJ_READ) { + sl->isl_parameters.isl_read.isl_len = len; + if (off != NULL) + sl->isl_parameters.isl_read.isl_byteoff = *off; + else + sl->isl_parameters.isl_read.isl_byteoff = 0; + } + + if (func == IRP_MJ_WRITE) { + sl->isl_parameters.isl_write.isl_len = len; + if (off != NULL) + sl->isl_parameters.isl_write.isl_byteoff = *off; + else + sl->isl_parameters.isl_write.isl_byteoff = 0; + } + + return (ip); +} + +static irp * +IoBuildDeviceIoControlRequest(uint32_t iocode, device_object *dobj, void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen, uint8_t isinternal, + nt_kevent *event, io_status_block *status) +{ + irp *ip; + io_stack_location *sl; + uint32_t buflen; + + ip = IoAllocateIrp(dobj->do_stacksize, TRUE); + if (ip == NULL) + return (NULL); + ip->irp_usrevent = event; + ip->irp_usriostat = status; + ip->irp_tail.irp_overlay.irp_thread = NULL; + + sl = IoGetNextIrpStackLocation(ip); + sl->isl_major = isinternal == TRUE ? + IRP_MJ_INTERNAL_DEVICE_CONTROL : IRP_MJ_DEVICE_CONTROL; + sl->isl_minor = 0; + sl->isl_flags = 0; + sl->isl_ctl = 0; + sl->isl_devobj = dobj; + sl->isl_fileobj = NULL; + sl->isl_completionfunc = NULL; + sl->isl_parameters.isl_ioctl.isl_iocode = iocode; + sl->isl_parameters.isl_ioctl.isl_ibuflen = ilen; + sl->isl_parameters.isl_ioctl.isl_obuflen = olen; + + switch(IO_METHOD(iocode)) { + case METHOD_BUFFERED: + if (ilen > olen) + buflen = ilen; + else + buflen = olen; + if (buflen) { + ip->irp_assoc.irp_sysbuf = + ExAllocatePoolWithTag(NonPagedPool, buflen, 0); + if (ip->irp_assoc.irp_sysbuf == NULL) { + IoFreeIrp(ip); + return (NULL); + } + } + if (ilen && ibuf != NULL) { + bcopy(ibuf, ip->irp_assoc.irp_sysbuf, ilen); + bzero((char *)ip->irp_assoc.irp_sysbuf + ilen, + buflen - ilen); + } else + bzero(ip->irp_assoc.irp_sysbuf, ilen); + ip->irp_userbuf = obuf; + break; + case METHOD_IN_DIRECT: + case METHOD_OUT_DIRECT: + if (ilen && ibuf != NULL) { + ip->irp_assoc.irp_sysbuf = + ExAllocatePoolWithTag(NonPagedPool, ilen, 0); + if (ip->irp_assoc.irp_sysbuf == NULL) { + IoFreeIrp(ip); + return (NULL); + } + bcopy(ibuf, ip->irp_assoc.irp_sysbuf, ilen); + } + if (olen && obuf != NULL) { + ip->irp_mdl = IoAllocateMdl(obuf, olen, + FALSE, FALSE, ip); + /* + * Normally we would MmProbeAndLockPages() + * here, but we don't have to in our + * imlementation. + */ + } + break; + case METHOD_NEITHER: + ip->irp_userbuf = obuf; + sl->isl_parameters.isl_ioctl.isl_type3ibuf = ibuf; + break; + default: + break; + } + + /* + * Ideally, we should associate this IRP with the calling + * thread here. + */ + + return (ip); +} + +static irp * +IoAllocateIrp(uint8_t stsize, uint8_t chargequota) +{ + irp *i; + + i = ExAllocatePoolWithTag(NonPagedPool, IoSizeOfIrp(stsize), 0); + if (i == NULL) + return (NULL); + + IoInitializeIrp(i, IoSizeOfIrp(stsize), stsize); + + return (i); +} + +static irp * +IoMakeAssociatedIrp(irp *ip, uint8_t stsize) +{ + irp *associrp; + + associrp = IoAllocateIrp(stsize, FALSE); + if (associrp == NULL) + return (NULL); + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); + associrp->irp_flags |= IRP_ASSOCIATED_IRP; + associrp->irp_tail.irp_overlay.irp_thread = + ip->irp_tail.irp_overlay.irp_thread; + associrp->irp_assoc.irp_master = ip; + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + + return (associrp); +} + +static void +IoFreeIrp(irp *ip) +{ + ExFreePool(ip); +} + +static void +IoInitializeIrp(irp *io, uint16_t psize, uint8_t ssize) +{ + bzero((char *)io, IoSizeOfIrp(ssize)); + io->irp_size = psize; + io->irp_stackcnt = ssize; + io->irp_currentstackloc = ssize; + InitializeListHead(&io->irp_thlist); + io->irp_tail.irp_overlay.irp_csl = + (io_stack_location *)(io + 1) + ssize; +} + +static void +IoReuseIrp(irp *ip, uint32_t status) +{ + uint8_t allocflags; + + allocflags = ip->irp_allocflags; + IoInitializeIrp(ip, ip->irp_size, ip->irp_stackcnt); + ip->irp_iostat.isb_status = status; + ip->irp_allocflags = allocflags; +} + +void +IoAcquireCancelSpinLock(uint8_t *irql) +{ + KeAcquireSpinLock(&ntoskrnl_cancellock, irql); +} + +void +IoReleaseCancelSpinLock(uint8_t irql) +{ + KeReleaseSpinLock(&ntoskrnl_cancellock, irql); +} + +uint8_t +IoCancelIrp(irp *ip) +{ + cancel_func cfunc; + uint8_t cancelirql; + + IoAcquireCancelSpinLock(&cancelirql); + cfunc = IoSetCancelRoutine(ip, NULL); + ip->irp_cancel = TRUE; + if (cfunc == NULL) { + IoReleaseCancelSpinLock(cancelirql); + return (FALSE); + } + ip->irp_cancelirql = cancelirql; + MSCALL2(cfunc, IoGetCurrentIrpStackLocation(ip)->isl_devobj, ip); + return (uint8_t)IoSetCancelValue(ip, TRUE); +} + +uint32_t +IofCallDriver(device_object *dobj, irp *ip) +{ + driver_object *drvobj; + io_stack_location *sl; + uint32_t status; + driver_dispatch disp; + + drvobj = dobj->do_drvobj; + + if (ip->irp_currentstackloc <= 0) + panic("IoCallDriver(): out of stack locations"); + + IoSetNextIrpStackLocation(ip); + sl = IoGetCurrentIrpStackLocation(ip); + + sl->isl_devobj = dobj; + + disp = drvobj->dro_dispatch[sl->isl_major]; + status = MSCALL2(disp, dobj, ip); + + return (status); +} + +void +IofCompleteRequest(irp *ip, uint8_t prioboost) +{ + uint32_t status; + device_object *dobj; + io_stack_location *sl; + completion_func cf; + + KASSERT(ip->irp_iostat.isb_status != STATUS_PENDING, + ("incorrect IRP(%p) status (STATUS_PENDING)", ip)); + + sl = IoGetCurrentIrpStackLocation(ip); + IoSkipCurrentIrpStackLocation(ip); + + do { + if (sl->isl_ctl & SL_PENDING_RETURNED) + ip->irp_pendingreturned = TRUE; + + if (ip->irp_currentstackloc != (ip->irp_stackcnt + 1)) + dobj = IoGetCurrentIrpStackLocation(ip)->isl_devobj; + else + dobj = NULL; + + if (sl->isl_completionfunc != NULL && + ((ip->irp_iostat.isb_status == STATUS_SUCCESS && + sl->isl_ctl & SL_INVOKE_ON_SUCCESS) || + (ip->irp_iostat.isb_status != STATUS_SUCCESS && + sl->isl_ctl & SL_INVOKE_ON_ERROR) || + (ip->irp_cancel == TRUE && + sl->isl_ctl & SL_INVOKE_ON_CANCEL))) { + cf = sl->isl_completionfunc; + status = MSCALL3(cf, dobj, ip, sl->isl_completionctx); + if (status == STATUS_MORE_PROCESSING_REQUIRED) + return; + } else { + if ((ip->irp_currentstackloc <= ip->irp_stackcnt) && + (ip->irp_pendingreturned == TRUE)) + IoMarkIrpPending(ip); + } + + /* move to the next. */ + IoSkipCurrentIrpStackLocation(ip); + sl++; + } while (ip->irp_currentstackloc <= (ip->irp_stackcnt + 1)); + + if (ip->irp_usriostat != NULL) + *ip->irp_usriostat = ip->irp_iostat; + if (ip->irp_usrevent != NULL) + KeSetEvent(ip->irp_usrevent, prioboost, FALSE); + + /* Handle any associated IRPs. */ + + if (ip->irp_flags & IRP_ASSOCIATED_IRP) { + uint32_t masterirpcnt; + irp *masterirp; + mdl *m; + + masterirp = ip->irp_assoc.irp_master; + masterirpcnt = + InterlockedDecrement(&masterirp->irp_assoc.irp_irpcnt); + + while ((m = ip->irp_mdl) != NULL) { + ip->irp_mdl = m->mdl_next; + IoFreeMdl(m); + } + IoFreeIrp(ip); + if (masterirpcnt == 0) + IoCompleteRequest(masterirp, IO_NO_INCREMENT); + return; + } + + /* With any luck, these conditions will never arise. */ + + if (ip->irp_flags & IRP_PAGING_IO) { + if (ip->irp_mdl != NULL) + IoFreeMdl(ip->irp_mdl); + IoFreeIrp(ip); + } +} + +void +ntoskrnl_intr(void *arg) +{ + kinterrupt *iobj; + uint8_t irql; + uint8_t claimed; + list_entry *l; + + KeAcquireSpinLock(&ntoskrnl_intlock, &irql); + l = ntoskrnl_intlist.nle_flink; + while (l != &ntoskrnl_intlist) { + iobj = CONTAINING_RECORD(l, kinterrupt, ki_list); + claimed = MSCALL2(iobj->ki_svcfunc, iobj, iobj->ki_svcctx); + if (claimed == TRUE) + break; + l = l->nle_flink; + } + KeReleaseSpinLock(&ntoskrnl_intlock, irql); +} + +uint8_t +KeAcquireInterruptSpinLock(kinterrupt *iobj) +{ + uint8_t irql; + KeAcquireSpinLock(&ntoskrnl_intlock, &irql); + return (irql); +} + +void +KeReleaseInterruptSpinLock(kinterrupt *iobj, uint8_t irql) +{ + KeReleaseSpinLock(&ntoskrnl_intlock, irql); +} + +uint8_t +KeSynchronizeExecution(kinterrupt *iobj, void *syncfunc, void *syncctx) +{ + uint8_t irql; + + KeAcquireSpinLock(&ntoskrnl_intlock, &irql); + MSCALL1(syncfunc, syncctx); + KeReleaseSpinLock(&ntoskrnl_intlock, irql); + + return (TRUE); +} + +/* + * IoConnectInterrupt() is passed only the interrupt vector and + * irql that a device wants to use, but no device-specific tag + * of any kind. This conflicts rather badly with FreeBSD's + * bus_setup_intr(), which needs the device_t for the device + * requesting interrupt delivery. In order to bypass this + * inconsistency, we implement a second level of interrupt + * dispatching on top of bus_setup_intr(). All devices use + * ntoskrnl_intr() as their ISR, and any device requesting + * interrupts will be registered with ntoskrnl_intr()'s interrupt + * dispatch list. When an interrupt arrives, we walk the list + * and invoke all the registered ISRs. This effectively makes all + * interrupts shared, but it's the only way to duplicate the + * semantics of IoConnectInterrupt() and IoDisconnectInterrupt() properly. + */ + +uint32_t +IoConnectInterrupt(kinterrupt **iobj, void *svcfunc, void *svcctx, + kspin_lock *lock, uint32_t vector, uint8_t irql, uint8_t syncirql, + uint8_t imode, uint8_t shared, uint32_t affinity, uint8_t savefloat) +{ + uint8_t curirql; + + *iobj = ExAllocatePoolWithTag(NonPagedPool, sizeof(kinterrupt), 0); + if (*iobj == NULL) + return (STATUS_INSUFFICIENT_RESOURCES); + + (*iobj)->ki_svcfunc = svcfunc; + (*iobj)->ki_svcctx = svcctx; + + if (lock == NULL) { + KeInitializeSpinLock(&(*iobj)->ki_lock_priv); + (*iobj)->ki_lock = &(*iobj)->ki_lock_priv; + } else + (*iobj)->ki_lock = lock; + + KeAcquireSpinLock(&ntoskrnl_intlock, &curirql); + InsertHeadList((&ntoskrnl_intlist), (&(*iobj)->ki_list)); + KeReleaseSpinLock(&ntoskrnl_intlock, curirql); + + return (STATUS_SUCCESS); +} + +void +IoDisconnectInterrupt(kinterrupt *iobj) +{ + uint8_t irql; + + if (iobj == NULL) + return; + + KeAcquireSpinLock(&ntoskrnl_intlock, &irql); + RemoveEntryList((&iobj->ki_list)); + KeReleaseSpinLock(&ntoskrnl_intlock, irql); + + ExFreePool(iobj); +} + +device_object * +IoAttachDeviceToDeviceStack(device_object *src, device_object *dst) +{ + device_object *attached; + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); + attached = IoGetAttachedDevice(dst); + attached->do_attacheddev = src; + src->do_attacheddev = NULL; + src->do_stacksize = attached->do_stacksize + 1; + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + + return (attached); +} + +void +IoDetachDevice(device_object *topdev) +{ + device_object *tail; + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); + + /* First, break the chain. */ + tail = topdev->do_attacheddev; + if (tail == NULL) { + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + return; + } + topdev->do_attacheddev = tail->do_attacheddev; + topdev->do_refcnt--; + + /* Now reduce the stacksize count for the takm_il objects. */ + + tail = topdev->do_attacheddev; + while (tail != NULL) { + tail->do_stacksize--; + tail = tail->do_attacheddev; + } + + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); +} + +/* + * For the most part, an object is considered signalled if + * dh_sigstate == TRUE. The exception is for mutant objects + * (mutexes), where the logic works like this: + * + * - If the thread already owns the object and sigstate is + * less than or equal to 0, then the object is considered + * signalled (recursive acquisition). + * - If dh_sigstate == 1, the object is also considered + * signalled. + */ + +static int +ntoskrnl_is_signalled(nt_dispatch_header *obj, struct thread *td) +{ + kmutant *km; + + if (obj->dh_type == DISP_TYPE_MUTANT) { + km = (kmutant *)obj; + if ((obj->dh_sigstate <= 0 && km->km_ownerthread == td) || + obj->dh_sigstate == 1) + return (TRUE); + return (FALSE); + } + + if (obj->dh_sigstate > 0) + return (TRUE); + return (FALSE); +} + +static void +ntoskrnl_satisfy_wait(nt_dispatch_header *obj, struct thread *td) +{ + kmutant *km; + + switch (obj->dh_type) { + case DISP_TYPE_MUTANT: + km = (struct kmutant *)obj; + obj->dh_sigstate--; + /* + * If sigstate reaches 0, the mutex is now + * non-signalled (the new thread owns it). + */ + if (obj->dh_sigstate == 0) { + km->km_ownerthread = td; + if (km->km_abandoned == TRUE) + km->km_abandoned = FALSE; + } + break; + /* Synchronization objects get reset to unsignalled. */ + case DISP_TYPE_SYNCHRONIZATION_EVENT: + case DISP_TYPE_SYNCHRONIZATION_TIMER: + obj->dh_sigstate = 0; + break; + case DISP_TYPE_SEMAPHORE: + obj->dh_sigstate--; + break; + default: + break; + } +} + +static void +ntoskrnl_satisfy_multiple_waits(wait_block *wb) +{ + wait_block *cur; + struct thread *td; + + cur = wb; + td = wb->wb_kthread; + + do { + ntoskrnl_satisfy_wait(wb->wb_object, td); + cur->wb_awakened = TRUE; + cur = cur->wb_next; + } while (cur != wb); +} + +/* Always called with dispatcher lock held. */ +static void +ntoskrnl_waittest(nt_dispatch_header *obj, uint32_t increment) +{ + wait_block *w, *next; + list_entry *e; + struct thread *td; + wb_ext *we; + int satisfied; + + /* + * Once an object has been signalled, we walk its list of + * wait blocks. If a wait block can be awakened, then satisfy + * waits as necessary and wake the thread. + * + * The rules work like this: + * + * If a wait block is marked as WAITTYPE_ANY, then + * we can satisfy the wait conditions on the current + * object and wake the thread right away. Satisfying + * the wait also has the effect of breaking us out + * of the search loop. + * + * If the object is marked as WAITTYLE_ALL, then the + * wait block will be part of a circularly linked + * list of wait blocks belonging to a waiting thread + * that's sleeping in KeWaitForMultipleObjects(). In + * order to wake the thread, all the objects in the + * wait list must be in the signalled state. If they + * are, we then satisfy all of them and wake the + * thread. + * + */ + + e = obj->dh_waitlisthead.nle_flink; + + while (e != &obj->dh_waitlisthead && obj->dh_sigstate > 0) { + w = CONTAINING_RECORD(e, wait_block, wb_waitlist); + we = w->wb_ext; + td = we->we_td; + satisfied = FALSE; + if (w->wb_waittype == WAITTYPE_ANY) { + /* + * Thread can be awakened if + * any wait is satisfied. + */ + ntoskrnl_satisfy_wait(obj, td); + satisfied = TRUE; + w->wb_awakened = TRUE; + } else { + /* + * Thread can only be woken up + * if all waits are satisfied. + * If the thread is waiting on multiple + * objects, they should all be linked + * through the wb_next pointers in the + * wait blocks. + */ + satisfied = TRUE; + next = w->wb_next; + while (next != w) { + if (ntoskrnl_is_signalled(obj, td) == FALSE) { + satisfied = FALSE; + break; + } + next = next->wb_next; + } + ntoskrnl_satisfy_multiple_waits(w); + } + + if (satisfied == TRUE) + cv_broadcastpri(&we->we_cv, + (w->wb_oldpri - (increment * 4)) > PRI_MIN_KERN ? + w->wb_oldpri - (increment * 4) : PRI_MIN_KERN); + + e = e->nle_flink; + } +} + +/* + * Return the number of 100 nanosecond intervals since + * January 1, 1601. (?!?!) + */ +void +ntoskrnl_time(uint64_t *tval) +{ + struct timespec ts; + + nanotime(&ts); + *tval = (uint64_t)ts.tv_nsec / 100 + (uint64_t)ts.tv_sec * 10000000 + + 11644473600 * 10000000; /* 100ns ticks from 1601 to 1970 */ +} + +static void +KeQuerySystemTime(uint64_t *current_time) +{ + ntoskrnl_time(current_time); +} + +static uint32_t +KeTickCount(void) +{ + struct timeval tv; + getmicrouptime(&tv); + return tvtohz_high(&tv); +} + + +/* + * KeWaitForSingleObject() is a tricky beast, because it can be used + * with several different object types: semaphores, timers, events, + * mutexes and threads. Semaphores don't appear very often, but the + * other object types are quite common. KeWaitForSingleObject() is + * what's normally used to acquire a mutex, and it can be used to + * wait for a thread termination. + * + * The Windows NDIS API is implemented in terms of Windows kernel + * primitives, and some of the object manipulation is duplicated in + * NDIS. For example, NDIS has timers and events, which are actually + * Windows kevents and ktimers. Now, you're supposed to only use the + * NDIS variants of these objects within the confines of the NDIS API, + * but there are some naughty developers out there who will use + * KeWaitForSingleObject() on NDIS timer and event objects, so we + * have to support that as well. Conseqently, our NDIS timer and event + * code has to be closely tied into our ntoskrnl timer and event code, + * just as it is in Windows. + * + * KeWaitForSingleObject() may do different things for different kinds + * of objects: + * + * - For events, we check if the event has been signalled. If the + * event is already in the signalled state, we just return immediately, + * otherwise we wait for it to be set to the signalled state by someone + * else calling KeSetEvent(). Events can be either synchronization or + * notification events. + * + * - For timers, if the timer has already fired and the timer is in + * the signalled state, we just return, otherwise we wait on the + * timer. Unlike an event, timers get signalled automatically when + * they expire rather than someone having to trip them manually. + * Timers initialized with KeInitializeTimer() are always notification + * events: KeInitializeTimerEx() lets you initialize a timer as + * either a notification or synchronization event. + * + * - For mutexes, we try to acquire the mutex and if we can't, we wait + * on the mutex until it's available and then grab it. When a mutex is + * released, it enters the signalled state, which wakes up one of the + * threads waiting to acquire it. Mutexes are always synchronization + * events. + * + * - For threads, the only thing we do is wait until the thread object + * enters a signalled state, which occurs when the thread terminates. + * Threads are always notification events. + * + * A notification event wakes up all threads waiting on an object. A + * synchronization event wakes up just one. Also, a synchronization event + * is auto-clearing, which means we automatically set the event back to + * the non-signalled state once the wakeup is done. + */ + +uint32_t +KeWaitForSingleObject(void *arg, uint32_t reason, uint32_t mode, + uint8_t alertable, int64_t *duetime) +{ + wait_block w; + struct thread *td = curthread; + struct timeval tv; + int error = 0; + uint64_t curtime; + wb_ext we; + nt_dispatch_header *obj; + + obj = arg; + + if (obj == NULL) + return (STATUS_INVALID_PARAMETER); + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); + + cv_init(&we.we_cv, "KeWFS"); + we.we_td = td; + + /* + * Check to see if this object is already signalled, + * and just return without waiting if it is. + */ + if (ntoskrnl_is_signalled(obj, td) == TRUE) { + /* Sanity check the signal state value. */ + if (obj->dh_sigstate != INT32_MIN) { + ntoskrnl_satisfy_wait(obj, curthread); + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + return (STATUS_SUCCESS); + } else { + /* + * There's a limit to how many times we can + * recursively acquire a mutant. If we hit + * the limit, something is very wrong. + */ + if (obj->dh_type == DISP_TYPE_MUTANT) { + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + panic("mutant limit exceeded"); + } + } + } + + bzero((char *)&w, sizeof(wait_block)); + w.wb_object = obj; + w.wb_ext = &we; + w.wb_waittype = WAITTYPE_ANY; + w.wb_next = &w; + w.wb_waitkey = 0; + w.wb_awakened = FALSE; + w.wb_oldpri = td->td_pri; + + InsertTailList((&obj->dh_waitlisthead), (&w.wb_waitlist)); + + /* + * The timeout value is specified in 100 nanosecond units + * and can be a positive or negative number. If it's positive, + * then the duetime is absolute, and we need to convert it + * to an absolute offset relative to now in order to use it. + * If it's negative, then the duetime is relative and we + * just have to convert the units. + */ + + if (duetime != NULL) { + if (*duetime < 0) { + tv.tv_sec = - (*duetime) / 10000000; + tv.tv_usec = (- (*duetime) / 10) - + (tv.tv_sec * 1000000); + } else { + ntoskrnl_time(&curtime); + if (*duetime < curtime) + tv.tv_sec = tv.tv_usec = 0; + else { + tv.tv_sec = ((*duetime) - curtime) / 10000000; + tv.tv_usec = ((*duetime) - curtime) / 10 - + (tv.tv_sec * 1000000); + } + } + } + + if (duetime == NULL) + cv_wait(&we.we_cv, &ntoskrnl_dispatchlock); + else + error = cv_timedwait(&we.we_cv, + &ntoskrnl_dispatchlock, tvtohz_high(&tv)); + + RemoveEntryList(&w.wb_waitlist); + + cv_destroy(&we.we_cv); + + /* We timed out. Leave the object alone and return status. */ + + if (error == EWOULDBLOCK) { + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + return (STATUS_TIMEOUT); + } + + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + + return (STATUS_SUCCESS); +/* + return (KeWaitForMultipleObjects(1, &obj, WAITTYPE_ALL, reason, + mode, alertable, duetime, &w)); +*/ +} + +static uint32_t +KeWaitForMultipleObjects(uint32_t cnt, nt_dispatch_header *obj[], uint32_t wtype, + uint32_t reason, uint32_t mode, uint8_t alertable, int64_t *duetime, + wait_block *wb_array) { struct thread *td = curthread; - kmutant *km; - wait_block _wb_array[THREAD_WAIT_OBJECTS]; - wait_block *w; + wait_block *whead, *w; + wait_block _wb_array[MAX_WAIT_OBJECTS]; + nt_dispatch_header *cur; struct timeval tv; - int i, wcnt = 0, widx = 0, error = 0; + int i, wcnt = 0, error = 0; uint64_t curtime; struct timespec t1, t2; + uint32_t status = STATUS_SUCCESS; + wb_ext we; if (cnt > MAX_WAIT_OBJECTS) - return(STATUS_INVALID_PARAMETER); + return (STATUS_INVALID_PARAMETER); if (cnt > THREAD_WAIT_OBJECTS && wb_array == NULL) - return(STATUS_INVALID_PARAMETER); + return (STATUS_INVALID_PARAMETER); + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); - lwkt_gettoken(&ntoskrnl_dispatchtoken); + cv_init(&we.we_cv, "KeWFM"); + we.we_td = td; if (wb_array == NULL) - w = &_wb_array[0]; + whead = _wb_array; else - w = wb_array; + whead = wb_array; - tv.tv_sec = 0; /* fix compiler warning */ - tv.tv_usec = 0; /* fix compiler warning */ + bzero((char *)whead, sizeof(wait_block) * cnt); /* First pass: see if we can satisfy any waits immediately. */ + wcnt = 0; + w = whead; + for (i = 0; i < cnt; i++) { - if (obj[i]->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj[i]; - if (km->km_ownerthread == NULL || - km->km_ownerthread == curthread->td_proc) { - obj[i]->dh_sigstate = FALSE; - km->km_acquirecnt++; - km->km_ownerthread = curthread->td_proc; - if (wtype == WAITTYPE_ANY) { - lwkt_reltoken(&ntoskrnl_dispatchtoken); - return (STATUS_WAIT_0 + i); - } + InsertTailList((&obj[i]->dh_waitlisthead), + (&w->wb_waitlist)); + w->wb_ext = &we; + w->wb_object = obj[i]; + w->wb_waittype = wtype; + w->wb_waitkey = i; + w->wb_awakened = FALSE; + w->wb_oldpri = td->td_pri; + w->wb_next = w + 1; + w++; + wcnt++; + if (ntoskrnl_is_signalled(obj[i], td)) { + /* + * There's a limit to how many times + * we can recursively acquire a mutant. + * If we hit the limit, something + * is very wrong. + */ + if (obj[i]->dh_sigstate == INT32_MIN && + obj[i]->dh_type == DISP_TYPE_MUTANT) { + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + panic("mutant limit exceeded"); } - } else if (obj[i]->dh_sigstate == TRUE) { - if (obj[i]->dh_type == EVENT_TYPE_SYNC) - obj[i]->dh_sigstate = FALSE; + + /* + * If this is a WAITTYPE_ANY wait, then + * satisfy the waited object and exit + * right now. + */ + if (wtype == WAITTYPE_ANY) { - lwkt_reltoken(&ntoskrnl_dispatchtoken); - return (STATUS_WAIT_0 + i); + ntoskrnl_satisfy_wait(obj[i], td); + status = STATUS_WAIT_0 + i; + goto wait_done; + } else { + w--; + wcnt--; + w->wb_object = NULL; + RemoveEntryList(&w->wb_waitlist); } } } /* - * Second pass: set up wait for anything we can't - * satisfy immediately. + * If this is a WAITTYPE_ALL wait and all objects are + * already signalled, satisfy the waits and exit now. */ - for (i = 0; i < cnt; i++) { - if (obj[i]->dh_sigstate == TRUE) - continue; - INSERT_LIST_TAIL((&obj[i]->dh_waitlisthead), - (&w[i].wb_waitlist)); - w[i].wb_kthread = td; - w[i].wb_object = obj[i]; - wcnt++; + if (wtype == WAITTYPE_ALL && wcnt == 0) { + for (i = 0; i < cnt; i++) + ntoskrnl_satisfy_wait(obj[i], td); + status = STATUS_SUCCESS; + goto wait_done; } - if (duetime) { + /* + * Create a circular waitblock list. The waitcount + * must always be non-zero when we get here. + */ + + (w - 1)->wb_next = whead; + + /* Wait on any objects that aren't yet signalled. */ + + /* Calculate timeout, if any. */ + + if (duetime != NULL) { if (*duetime < 0) { - tv.tv_sec = -*duetime / 10000000; - tv.tv_usec = (-*duetime / 10) - (tv.tv_sec * 1000000); + tv.tv_sec = - (*duetime) / 10000000; + tv.tv_usec = (- (*duetime) / 10) - + (tv.tv_sec * 1000000); } else { ntoskrnl_time(&curtime); - if (*duetime < curtime) { - tv.tv_sec = 0; - tv.tv_usec = 0; - } else { + if (*duetime < curtime) + tv.tv_sec = tv.tv_usec = 0; + else { tv.tv_sec = ((*duetime) - curtime) / 10000000; tv.tv_usec = ((*duetime) - curtime) / 10 - (tv.tv_sec * 1000000); @@ -599,544 +1821,1156 @@ ntoskrnl_waitforobjs(uint32_t cnt, nt_dispatch_header *obj[], while (wcnt) { nanotime(&t1); - lwkt_reltoken(&ntoskrnl_dispatchtoken); - if (duetime) { - ticks = 1 + tv.tv_sec * hz + tv.tv_usec * hz / 1000000; - error = ndis_thsuspend(td, ticks); - } else { - error = ndis_thsuspend(td, 0); + if (duetime == NULL) + cv_wait(&we.we_cv, &ntoskrnl_dispatchlock); + else + error = cv_timedwait(&we.we_cv, + &ntoskrnl_dispatchlock, tvtohz_high(&tv)); + + /* Wait with timeout expired. */ + + if (error) { + status = STATUS_TIMEOUT; + goto wait_done; } - lwkt_gettoken(&ntoskrnl_dispatchtoken); nanotime(&t2); - for (i = 0; i < cnt; i++) { - if (obj[i]->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj; - if (km->km_ownerthread == NULL) { - km->km_ownerthread = - curthread->td_proc; - km->km_acquirecnt++; + /* See what's been signalled. */ + + w = whead; + do { + cur = w->wb_object; + if (ntoskrnl_is_signalled(cur, td) == TRUE || + w->wb_awakened == TRUE) { + /* Sanity check the signal state value. */ + if (cur->dh_sigstate == INT32_MIN && + cur->dh_type == DISP_TYPE_MUTANT) { + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + panic("mutant limit exceeded"); } - } - if (obj[i]->dh_sigstate == TRUE) { - widx = i; - if (obj[i]->dh_type == EVENT_TYPE_SYNC) - obj[i]->dh_sigstate = FALSE; - REMOVE_LIST_ENTRY((&w[i].wb_waitlist)); wcnt--; + if (wtype == WAITTYPE_ANY) { + status = w->wb_waitkey & + STATUS_WAIT_0; + goto wait_done; + } } + w = w->wb_next; + } while (w != whead); + + /* + * If all objects have been signalled, or if this + * is a WAITTYPE_ANY wait and we were woke up by + * someone, we can bail. + */ + + if (wcnt == 0) { + status = STATUS_SUCCESS; + goto wait_done; } - if (error || wtype == WAITTYPE_ANY) - break; + /* + * If this is WAITTYPE_ALL wait, and there's still + * objects that haven't been signalled, deduct the + * time that's elapsed so far from the timeout and + * wait again (or continue waiting indefinitely if + * there's no timeout). + */ - if (duetime) { + if (duetime != NULL) { tv.tv_sec -= (t2.tv_sec - t1.tv_sec); tv.tv_usec -= (t2.tv_nsec - t1.tv_nsec) / 1000; } } - if (wcnt) { - for (i = 0; i < cnt; i++) - REMOVE_LIST_ENTRY((&w[i].wb_waitlist)); + +wait_done: + + cv_destroy(&we.we_cv); + + for (i = 0; i < cnt; i++) { + if (whead[i].wb_object != NULL) + RemoveEntryList(&whead[i].wb_waitlist); + } + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); - if (error == EWOULDBLOCK) { - lwkt_reltoken(&ntoskrnl_dispatchtoken); - return(STATUS_TIMEOUT); + return (status); +} + +static void +WRITE_REGISTER_USHORT(uint16_t *reg, uint16_t val) +{ + bus_space_write_2(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg, val); +} + +static uint16_t +READ_REGISTER_USHORT(uint16_t *reg) +{ + return (bus_space_read_2(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg)); +} + +static void +WRITE_REGISTER_ULONG(uint32_t *reg, uint32_t val) +{ + bus_space_write_4(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg, val); +} + +static uint32_t +READ_REGISTER_ULONG(uint32_t *reg) +{ + return (bus_space_read_4(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg)); +} + +static uint8_t +READ_REGISTER_UCHAR(uint8_t *reg) +{ + return (bus_space_read_1(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg)); +} + +static void +WRITE_REGISTER_UCHAR(uint8_t *reg, uint8_t val) +{ + bus_space_write_1(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg, val); +} + +static int64_t +_allmul(int64_t a, int64_t b) +{ + return (a * b); +} + +static int64_t +_alldiv(int64_t a, int64_t b) +{ + return (a / b); +} + +static int64_t +_allrem(int64_t a, int64_t b) +{ + return (a % b); +} + +static uint64_t +_aullmul(uint64_t a, uint64_t b) +{ + return (a * b); +} + +static uint64_t +_aulldiv(uint64_t a, uint64_t b) +{ + return (a / b); +} + +static uint64_t +_aullrem(uint64_t a, uint64_t b) +{ + return (a % b); +} + +static int64_t +_allshl(int64_t a, uint8_t b) +{ + return (a << b); +} + +static uint64_t +_aullshl(uint64_t a, uint8_t b) +{ + return (a << b); +} + +static int64_t +_allshr(int64_t a, uint8_t b) +{ + return (a >> b); +} + +static uint64_t +_aullshr(uint64_t a, uint8_t b) +{ + return (a >> b); +} + +static slist_entry * +ntoskrnl_pushsl(slist_header *head, slist_entry *entry) +{ + slist_entry *oldhead; + + oldhead = head->slh_list.slh_next; + entry->sl_next = head->slh_list.slh_next; + head->slh_list.slh_next = entry; + head->slh_list.slh_depth++; + head->slh_list.slh_seq++; + + return (oldhead); +} + +static void +InitializeSListHead(slist_header *head) +{ + memset(head, 0, sizeof(*head)); +} + +static slist_entry * +ntoskrnl_popsl(slist_header *head) +{ + slist_entry *first; + + first = head->slh_list.slh_next; + if (first != NULL) { + head->slh_list.slh_next = first->sl_next; + head->slh_list.slh_depth--; + head->slh_list.slh_seq++; } - if (wtype == WAITTYPE_ANY && wcnt) { - lwkt_reltoken(&ntoskrnl_dispatchtoken); - return(STATUS_WAIT_0 + widx); + return (first); +} + +/* + * We need this to make lookaside lists work for amd64. + * We pass a pointer to ExAllocatePoolWithTag() the lookaside + * list structure. For amd64 to work right, this has to be a + * pointer to the wrapped version of the routine, not the + * original. Letting the Windows driver invoke the original + * function directly will result in a convention calling + * mismatch and a pretty crash. On x86, this effectively + * becomes a no-op since ipt_func and ipt_wrap are the same. + */ + +static funcptr +ntoskrnl_findwrap(funcptr func) +{ + image_patch_table *patch; + + patch = ntoskrnl_functbl; + while (patch->ipt_func != NULL) { + if ((funcptr)patch->ipt_func == func) + return ((funcptr)patch->ipt_wrap); + patch++; } - lwkt_reltoken(&ntoskrnl_dispatchtoken); + return (NULL); +} + +static void +ExInitializePagedLookasideList(paged_lookaside_list *lookaside, + lookaside_alloc_func *allocfunc, lookaside_free_func *freefunc, + uint32_t flags, size_t size, uint32_t tag, uint16_t depth) +{ + bzero((char *)lookaside, sizeof(paged_lookaside_list)); + + if (size < sizeof(slist_entry)) + lookaside->nll_l.gl_size = sizeof(slist_entry); + else + lookaside->nll_l.gl_size = size; + lookaside->nll_l.gl_tag = tag; + if (allocfunc == NULL) + lookaside->nll_l.gl_allocfunc = + ntoskrnl_findwrap((funcptr)ExAllocatePoolWithTag); + else + lookaside->nll_l.gl_allocfunc = allocfunc; + + if (freefunc == NULL) + lookaside->nll_l.gl_freefunc = + ntoskrnl_findwrap((funcptr)ExFreePool); + else + lookaside->nll_l.gl_freefunc = freefunc; + +#ifdef __i386__ + KeInitializeSpinLock(&lookaside->nll_obsoletelock); +#endif + + lookaside->nll_l.gl_type = NonPagedPool; + lookaside->nll_l.gl_depth = depth; + lookaside->nll_l.gl_maxdepth = LOOKASIDE_DEPTH; +} + +static void +ExDeletePagedLookasideList(paged_lookaside_list *lookaside) +{ + void *buf; + void (*freefunc)(void *); + + freefunc = lookaside->nll_l.gl_freefunc; + while((buf = ntoskrnl_popsl(&lookaside->nll_l.gl_listhead)) != NULL) + MSCALL1(freefunc, buf); +} + +static void +ExInitializeNPagedLookasideList(npaged_lookaside_list *lookaside, + lookaside_alloc_func *allocfunc, lookaside_free_func *freefunc, + uint32_t flags, size_t size, uint32_t tag, uint16_t depth) +{ + bzero((char *)lookaside, sizeof(npaged_lookaside_list)); + + if (size < sizeof(slist_entry)) + lookaside->nll_l.gl_size = sizeof(slist_entry); + else + lookaside->nll_l.gl_size = size; + lookaside->nll_l.gl_tag = tag; + if (allocfunc == NULL) + lookaside->nll_l.gl_allocfunc = + ntoskrnl_findwrap((funcptr)ExAllocatePoolWithTag); + else + lookaside->nll_l.gl_allocfunc = allocfunc; + + if (freefunc == NULL) + lookaside->nll_l.gl_freefunc = + ntoskrnl_findwrap((funcptr)ExFreePool); + else + lookaside->nll_l.gl_freefunc = freefunc; + +#ifdef __i386__ + KeInitializeSpinLock(&lookaside->nll_obsoletelock); +#endif + + lookaside->nll_l.gl_type = NonPagedPool; + lookaside->nll_l.gl_depth = depth; + lookaside->nll_l.gl_maxdepth = LOOKASIDE_DEPTH; +} + +static void +ExDeleteNPagedLookasideList(npaged_lookaside_list *lookaside) +{ + void *buf; + void (*freefunc)(void *); + + freefunc = lookaside->nll_l.gl_freefunc; + while((buf = ntoskrnl_popsl(&lookaside->nll_l.gl_listhead)) != NULL) + MSCALL1(freefunc, buf); +} + +slist_entry * +InterlockedPushEntrySList(slist_header *head, slist_entry *entry) +{ + slist_entry *oldhead; + + mtx_spinlock(&ntoskrnl_interlock); + oldhead = ntoskrnl_pushsl(head, entry); + mtx_spinunlock(&ntoskrnl_interlock); - return(STATUS_SUCCESS); + return (oldhead); } -__stdcall static void -ntoskrnl_writereg_ushort(uint16_t *reg, uint16_t val) +slist_entry * +InterlockedPopEntrySList(slist_header *head) { - bus_space_write_2(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg, val); - return; + slist_entry *first; + + mtx_spinlock(&ntoskrnl_interlock); + first = ntoskrnl_popsl(head); + mtx_spinunlock(&ntoskrnl_interlock); + + return (first); } -__stdcall static uint16_t -ntoskrnl_readreg_ushort(uint16_t *reg) +static slist_entry * +ExInterlockedPushEntrySList(slist_header *head, slist_entry *entry, + kspin_lock *lock) { - return(bus_space_read_2(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg)); + return (InterlockedPushEntrySList(head, entry)); } -__stdcall static void -ntoskrnl_writereg_ulong(uint32_t *reg, uint32_t val) +static slist_entry * +ExInterlockedPopEntrySList(slist_header *head, kspin_lock *lock) { - bus_space_write_4(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg, val); - return; + return (InterlockedPopEntrySList(head)); } -__stdcall static uint32_t -ntoskrnl_readreg_ulong(uint32_t *reg) +uint16_t +ExQueryDepthSList(slist_header *head) { - return(bus_space_read_4(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg)); + uint16_t depth; + + mtx_spinlock(&ntoskrnl_interlock); + depth = head->slh_list.slh_depth; + mtx_spinunlock(&ntoskrnl_interlock); + + return (depth); } -__stdcall static uint8_t -ntoskrnl_readreg_uchar(uint8_t *reg) +void +KeInitializeSpinLock(kspin_lock *lock) { - return(bus_space_read_1(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg)); + *lock = 0; } -__stdcall static void -ntoskrnl_writereg_uchar(uint8_t *reg, uint8_t val) +#ifdef __i386__ +void +KefAcquireSpinLockAtDpcLevel(kspin_lock *lock) { - bus_space_write_1(NDIS_BUS_SPACE_MEM, 0x0, (bus_size_t)reg, val); - return; +#ifdef NTOSKRNL_DEBUG_SPINLOCKS + int i = 0; +#endif + + while (atomic_cmpset_acq_int((volatile u_int *)lock, 0, 1) == 0) { + /* sit and spin */; +#ifdef NTOSKRNL_DEBUG_SPINLOCKS + i++; + if (i > 200000000) + panic("DEADLOCK!"); +#endif + } } -__stdcall static int64_t -_allmul(int64_t a, int64_t b) +void +KefReleaseSpinLockFromDpcLevel(kspin_lock *lock) { - return (a * b); + atomic_store_rel_int((volatile u_int *)lock, 0); } -__stdcall static int64_t -_alldiv(int64_t a, int64_t b) +uint8_t +KeAcquireSpinLockRaiseToDpc(kspin_lock *lock) { - return (a / b); -} + uint8_t oldirql; -__stdcall static int64_t -_allrem(int64_t a, int64_t b) + if (KeGetCurrentIrql() > DISPATCH_LEVEL) + panic("IRQL_NOT_LESS_THAN_OR_EQUAL"); + + KeRaiseIrql(DISPATCH_LEVEL, &oldirql); + KeAcquireSpinLockAtDpcLevel(lock); + + return (oldirql); +} +#else +void +KeAcquireSpinLockAtDpcLevel(kspin_lock *lock) { - return (a % b); + while (atomic_cmpset_acq_int((volatile u_int *)lock, 0, 1) == 0) + /* sit and spin */; } -__stdcall static uint64_t -_aullmul(uint64_t a, uint64_t b) +void +KeReleaseSpinLockFromDpcLevel(kspin_lock *lock) { - return (a * b); + atomic_store_rel_int((volatile u_int *)lock, 0); } +#endif /* __i386__ */ -__stdcall static uint64_t -_aulldiv(uint64_t a, uint64_t b) +uintptr_t +InterlockedExchange(volatile uint32_t *dst, uintptr_t val) { - return (a / b); + uintptr_t r; + + mtx_spinlock(&ntoskrnl_interlock); + r = *dst; + *dst = val; + mtx_spinunlock(&ntoskrnl_interlock); + + return (r); } -__stdcall static uint64_t -_aullrem(uint64_t a, uint64_t b) +static uint32_t +InterlockedIncrement(volatile uint32_t *addend) { - return (a % b); + atomic_add_long((volatile u_long *)addend, 1); + return (*addend); } -__regparm static int64_t -_allshl(int64_t a, uint8_t b) +static uint32_t +InterlockedDecrement(volatile uint32_t *addend) { - return (a << b); + atomic_subtract_long((volatile u_long *)addend, 1); + return (*addend); } -__regparm static uint64_t -_aullshl(uint64_t a, uint8_t b) +static void +ExInterlockedAddLargeStatistic(uint64_t *addend, uint32_t inc) { - return (a << b); -} + mtx_spinlock(&ntoskrnl_interlock); + *addend += inc; + mtx_spinunlock(&ntoskrnl_interlock); +}; -__regparm static int64_t -_allshr(int64_t a, uint8_t b) +mdl * +IoAllocateMdl(void *vaddr, uint32_t len, uint8_t secondarybuf, + uint8_t chargequota, irp *iopkt) { - return (a >> b); + mdl *m; + int zone = 0; + + if (MmSizeOfMdl(vaddr, len) > MDL_ZONE_SIZE) + m = ExAllocatePoolWithTag(NonPagedPool, + MmSizeOfMdl(vaddr, len), 0); + else { + m = objcache_get(mdl_cache, M_NOWAIT); + bzero(m, sizeof(mdl)); + zone++; + } + + if (m == NULL) + return (NULL); + + MmInitializeMdl(m, vaddr, len); + + /* + * MmInitializMdl() clears the flags field, so we + * have to set this here. If the MDL came from the + * MDL UMA zone, tag it so we can release it to + * the right place later. + */ + if (zone) + m->mdl_flags = MDL_ZONE_ALLOCED; + + if (iopkt != NULL) { + if (secondarybuf == TRUE) { + mdl *last; + last = iopkt->irp_mdl; + while (last->mdl_next != NULL) + last = last->mdl_next; + last->mdl_next = m; + } else { + if (iopkt->irp_mdl != NULL) + panic("leaking an MDL in IoAllocateMdl()"); + iopkt->irp_mdl = m; + } + } + + return (m); } -__regparm static uint64_t -_aullshr(uint64_t a, uint8_t b) +void +IoFreeMdl(mdl *m) { - return (a >> b); + if (m == NULL) + return; + + if (m->mdl_flags & MDL_ZONE_ALLOCED) + objcache_put(mdl_cache, m); + else + ExFreePool(m); } -static slist_entry * -ntoskrnl_pushsl(slist_header *head, slist_entry *entry) +static void * +MmAllocateContiguousMemory(uint32_t size, uint64_t highest) { - slist_entry *oldhead; + void *addr; + size_t pagelength = roundup(size, PAGE_SIZE); - oldhead = head->slh_list.slh_next; - entry->sl_next = head->slh_list.slh_next; - head->slh_list.slh_next = entry; - head->slh_list.slh_depth++; - head->slh_list.slh_seq++; + addr = ExAllocatePoolWithTag(NonPagedPool, pagelength, 0); - return(oldhead); + return (addr); } -static slist_entry * -ntoskrnl_popsl(slist_header *head) +#if 0 /* XXX swildner */ +static void * +MmAllocateContiguousMemorySpecifyCache(uint32_t size, uint64_t lowest, + uint64_t highest, uint64_t boundary, enum nt_caching_type cachetype) { - slist_entry *first; + vm_memattr_t memattr; + void *ret; - first = head->slh_list.slh_next; - if (first != NULL) { - head->slh_list.slh_next = first->sl_next; - head->slh_list.slh_depth--; - head->slh_list.slh_seq++; + switch (cachetype) { + case MmNonCached: + memattr = VM_MEMATTR_UNCACHEABLE; + break; + case MmWriteCombined: + memattr = VM_MEMATTR_WRITE_COMBINING; + break; + case MmNonCachedUnordered: + memattr = VM_MEMATTR_UNCACHEABLE; + break; + case MmCached: + case MmHardwareCoherentCached: + case MmUSWCCached: + default: + memattr = VM_MEMATTR_DEFAULT; + break; } - return(first); + ret = (void *)kmem_alloc_contig(kernel_map, size, M_ZERO | M_NOWAIT, + lowest, highest, PAGE_SIZE, boundary, memattr); + if (ret != NULL) + malloc_type_allocated(M_DEVBUF, round_page(size)); + return (ret); } - -__stdcall static void * -ntoskrnl_allocfunc(uint32_t pooltype, size_t size, uint32_t tag) +#else +static void * +MmAllocateContiguousMemorySpecifyCache(uint32_t size, uint64_t lowest, + uint64_t highest, uint64_t boundary, enum nt_caching_type cachetype) { - return(kmalloc(size, M_DEVBUF, M_WAITOK)); +#if 0 + void *addr; + size_t pagelength = roundup(size, PAGE_SIZE); + + addr = ExAllocatePoolWithTag(NonPagedPool, pagelength, 0); + + return(addr); +#else + panic("%s", __func__); +#endif } +#endif -__stdcall static void -ntoskrnl_freefunc(void *buf) +static void +MmFreeContiguousMemory(void *base) { - kfree(buf, M_DEVBUF); - return; + ExFreePool(base); } -__stdcall static void -ntoskrnl_init_lookaside(paged_lookaside_list *lookaside, - lookaside_alloc_func *allocfunc, - lookaside_free_func *freefunc, - uint32_t flags, size_t size, - uint32_t tag, uint16_t depth) +static void +MmFreeContiguousMemorySpecifyCache(void *base, uint32_t size, + enum nt_caching_type cachetype) { - bzero((char *)lookaside, sizeof(paged_lookaside_list)); - - if (size < sizeof(slist_entry)) - lookaside->nll_l.gl_size = sizeof(slist_entry); - else - lookaside->nll_l.gl_size = size; - lookaside->nll_l.gl_tag = tag; - if (allocfunc == NULL) - lookaside->nll_l.gl_allocfunc = ntoskrnl_allocfunc; - else - lookaside->nll_l.gl_allocfunc = allocfunc; - - if (freefunc == NULL) - lookaside->nll_l.gl_freefunc = ntoskrnl_freefunc; - else - lookaside->nll_l.gl_freefunc = freefunc; - - ntoskrnl_init_lock(&lookaside->nll_obsoletelock); - - lookaside->nll_l.gl_depth = LOOKASIDE_DEPTH; - lookaside->nll_l.gl_maxdepth = LOOKASIDE_DEPTH; - - return; + contigfree(base, size, M_DEVBUF); } -__stdcall static void -ntoskrnl_delete_lookaside(paged_lookaside_list *lookaside) +static uint32_t +MmSizeOfMdl(void *vaddr, size_t len) { - void *buf; - __stdcall void (*freefunc)(void *); + uint32_t l; - freefunc = lookaside->nll_l.gl_freefunc; - while((buf = ntoskrnl_popsl(&lookaside->nll_l.gl_listhead)) != NULL) - freefunc(buf); + l = sizeof(struct mdl) + + (sizeof(vm_offset_t *) * SPAN_PAGES(vaddr, len)); - return; + return (l); } -__stdcall static void -ntoskrnl_init_nplookaside(npaged_lookaside_list *lookaside, - lookaside_alloc_func *allocfunc, - lookaside_free_func *freefunc, - uint32_t flags, size_t size, - uint32_t tag, uint16_t depth) +/* + * The Microsoft documentation says this routine fills in the + * page array of an MDL with the _physical_ page addresses that + * comprise the buffer, but we don't really want to do that here. + * Instead, we just fill in the page array with the kernel virtual + * addresses of the buffers. + */ +void +MmBuildMdlForNonPagedPool(mdl *m) { - bzero((char *)lookaside, sizeof(npaged_lookaside_list)); + vm_offset_t *mdl_pages; + int pagecnt, i; - if (size < sizeof(slist_entry)) - lookaside->nll_l.gl_size = sizeof(slist_entry); - else - lookaside->nll_l.gl_size = size; - lookaside->nll_l.gl_tag = tag; - if (allocfunc == NULL) - lookaside->nll_l.gl_allocfunc = ntoskrnl_allocfunc; - else - lookaside->nll_l.gl_allocfunc = allocfunc; + pagecnt = SPAN_PAGES(m->mdl_byteoffset, m->mdl_bytecount); - if (freefunc == NULL) - lookaside->nll_l.gl_freefunc = ntoskrnl_freefunc; - else - lookaside->nll_l.gl_freefunc = freefunc; + if (pagecnt > (m->mdl_size - sizeof(mdl)) / sizeof(vm_offset_t *)) + panic("not enough pages in MDL to describe buffer"); - ntoskrnl_init_lock(&lookaside->nll_obsoletelock); + mdl_pages = MmGetMdlPfnArray(m); - lookaside->nll_l.gl_depth = LOOKASIDE_DEPTH; - lookaside->nll_l.gl_maxdepth = LOOKASIDE_DEPTH; + for (i = 0; i < pagecnt; i++) + *mdl_pages = (vm_offset_t)m->mdl_startva + (i * PAGE_SIZE); - return; + m->mdl_flags |= MDL_SOURCE_IS_NONPAGED_POOL; + m->mdl_mappedsystemva = MmGetMdlVirtualAddress(m); } -__stdcall static void -ntoskrnl_delete_nplookaside(npaged_lookaside_list *lookaside) +static void * +MmMapLockedPages(mdl *buf, uint8_t accessmode) { - void *buf; - __stdcall void (*freefunc)(void *); + buf->mdl_flags |= MDL_MAPPED_TO_SYSTEM_VA; + return (MmGetMdlVirtualAddress(buf)); +} - freefunc = lookaside->nll_l.gl_freefunc; - while((buf = ntoskrnl_popsl(&lookaside->nll_l.gl_listhead)) != NULL) - freefunc(buf); +static void * +MmMapLockedPagesSpecifyCache(mdl *buf, uint8_t accessmode, uint32_t cachetype, + void *vaddr, uint32_t bugcheck, uint32_t prio) +{ + return (MmMapLockedPages(buf, accessmode)); +} - return; +static void +MmUnmapLockedPages(void *vaddr, mdl *buf) +{ + buf->mdl_flags &= ~MDL_MAPPED_TO_SYSTEM_VA; } /* - * Note: the interlocked slist push and pop routines are - * declared to be _fastcall in Windows. gcc 3.4 is supposed - * to have support for this calling convention, however we - * don't have that version available yet, so we kludge things - * up using some inline assembly. + * This function has a problem in that it will break if you + * compile this module without PAE and try to use it on a PAE + * kernel. Unfortunately, there's no way around this at the + * moment. It's slightly less broken that using pmap_kextract(). + * You'd think the virtual memory subsystem would help us out + * here, but it doesn't. */ -__stdcall __regcall static slist_entry * -ntoskrnl_push_slist(REGARGS2(slist_header *head, slist_entry *entry)) +static uint64_t +MmGetPhysicalAddress(void *base) { - slist_entry *oldhead; + return (pmap_extract(kernel_map.pmap, (vm_offset_t)base)); +} - oldhead = (slist_entry *)FASTCALL3(ntoskrnl_push_slist_ex, - head, entry, &ntoskrnl_global); +void * +MmGetSystemRoutineAddress(unicode_string *ustr) +{ + ansi_string astr; - return(oldhead); + if (RtlUnicodeStringToAnsiString(&astr, ustr, TRUE)) + return (NULL); + return (ndis_get_routine_address(ntoskrnl_functbl, astr.as_buf)); } -__stdcall __regcall static slist_entry * -ntoskrnl_pop_slist(REGARGS1(slist_header *head)) +uint8_t +MmIsAddressValid(void *vaddr) { - slist_entry *first; + if (pmap_extract(kernel_map.pmap, (vm_offset_t)vaddr)) + return (TRUE); + + return (FALSE); +} + +void * +MmMapIoSpace(uint64_t paddr, uint32_t len, uint32_t cachetype) +{ + devclass_t nexus_class; + device_t *nexus_devs, devp; + int nexus_count = 0; + device_t matching_dev = NULL; + struct resource *res; + int i; + vm_offset_t v; + + /* There will always be at least one nexus. */ + + nexus_class = devclass_find("nexus"); + devclass_get_devices(nexus_class, &nexus_devs, &nexus_count); + + for (i = 0; i < nexus_count; i++) { + devp = nexus_devs[i]; + matching_dev = ntoskrnl_finddev(devp, paddr, &res); + if (matching_dev) + break; + } - first = (slist_entry *)FASTCALL2(ntoskrnl_pop_slist_ex, - head, &ntoskrnl_global); + kfree(nexus_devs, M_TEMP); - return(first); + if (matching_dev == NULL) + return (NULL); + + v = (vm_offset_t)rman_get_virtual(res); + if (paddr > rman_get_start(res)) + v += paddr - rman_get_start(res); + + return ((void *)v); } -__stdcall __regcall static slist_entry * -ntoskrnl_push_slist_ex(REGARGS2(slist_header *head, slist_entry *entry), kspin_lock *lock) +void +MmUnmapIoSpace(void *vaddr, size_t len) { - slist_entry *oldhead; - uint8_t irql; +} - irql = FASTCALL2(hal_lock, lock, DISPATCH_LEVEL); - oldhead = ntoskrnl_pushsl(head, entry); - FASTCALL2(hal_unlock, lock, irql); - return(oldhead); +static device_t +ntoskrnl_finddev(device_t dev, uint64_t paddr, struct resource **res) +{ + device_t *children = NULL; + device_t matching_dev; + int childcnt; + struct resource *r; + struct resource_list *rl; + struct resource_list_entry *rle; + uint32_t flags; + int i; + + /* We only want devices that have been successfully probed. */ + + if (device_is_alive(dev) == FALSE) + return (NULL); + + rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); + if (rl != NULL) { + SLIST_FOREACH(rle, rl, link) { + r = rle->res; + + if (r == NULL) + continue; + + flags = rman_get_flags(r); + + if (rle->type == SYS_RES_MEMORY && + paddr >= rman_get_start(r) && + paddr <= rman_get_end(r)) { + if (!(flags & RF_ACTIVE)) + bus_activate_resource(dev, + SYS_RES_MEMORY, 0, r); + *res = r; + return (dev); + } + } + } + + /* + * If this device has children, do another + * level of recursion to inspect them. + */ + + device_get_children(dev, &children, &childcnt); + + for (i = 0; i < childcnt; i++) { + matching_dev = ntoskrnl_finddev(children[i], paddr, res); + if (matching_dev != NULL) { + kfree(children, M_TEMP); + return (matching_dev); + } + } + + + /* Won't somebody please think of the children! */ + + if (children != NULL) + kfree(children, M_TEMP); + + return (NULL); } -__stdcall __regcall static slist_entry * -ntoskrnl_pop_slist_ex(REGARGS2(slist_header *head, kspin_lock *lock)) +/* + * Workitems are unlike DPCs, in that they run in a user-mode thread + * context rather than at DISPATCH_LEVEL in kernel context. In our + * case we run them in kernel context anyway. + */ +static void +ntoskrnl_workitem_thread(void *arg) { - slist_entry *first; + kdpc_queue *kq; + list_entry *l; + io_workitem *iw; uint8_t irql; - irql = FASTCALL2(hal_lock, lock, DISPATCH_LEVEL); - first = ntoskrnl_popsl(head); - FASTCALL2(hal_unlock, lock, irql); + kq = arg; + + InitializeListHead(&kq->kq_disp); + kq->kq_td = curthread; + kq->kq_exit = 0; + KeInitializeSpinLock(&kq->kq_lock); + KeInitializeEvent(&kq->kq_proc, EVENT_TYPE_SYNC, FALSE); + + while (1) { + KeWaitForSingleObject(&kq->kq_proc, 0, 0, TRUE, NULL); + + KeAcquireSpinLock(&kq->kq_lock, &irql); + + if (kq->kq_exit) { + kq->kq_exit = 0; + KeReleaseSpinLock(&kq->kq_lock, irql); + break; + } + + while (!IsListEmpty(&kq->kq_disp)) { + l = RemoveHeadList(&kq->kq_disp); + iw = CONTAINING_RECORD(l, + io_workitem, iw_listentry); + InitializeListHead((&iw->iw_listentry)); + if (iw->iw_func == NULL) + continue; + KeReleaseSpinLock(&kq->kq_lock, irql); + MSCALL2(iw->iw_func, iw->iw_dobj, iw->iw_ctx); + KeAcquireSpinLock(&kq->kq_lock, &irql); + } + + KeReleaseSpinLock(&kq->kq_lock, irql); + } - return(first); + wakeup(curthread); + kthread_exit(); + return; /* notreached */ } -__stdcall __regcall void -ntoskrnl_lock_dpc(REGARGS1(kspin_lock *lock)) +static ndis_status +RtlCharToInteger(const char *src, uint32_t base, uint32_t *val) { - while (atomic_poll_acquire_int((volatile u_int *)lock) == 0) - /* sit and spin */; + int negative = 0; + uint32_t res; + + if (!src || !val) + return (STATUS_ACCESS_VIOLATION); + while (*src != '\0' && *src <= ' ') + src++; + if (*src == '+') + src++; + else if (*src == '-') { + src++; + negative = 1; + } + if (base == 0) { + base = 10; + if (*src == '0') { + src++; + if (*src == 'b') { + base = 2; + src++; + } else if (*src == 'o') { + base = 8; + src++; + } else if (*src == 'x') { + base = 16; + src++; + } + } + } else if (!(base == 2 || base == 8 || base == 10 || base == 16)) + return (STATUS_INVALID_PARAMETER); + + for (res = 0; *src; src++) { + int v; + if (isdigit(*src)) + v = *src - '0'; + else if (isxdigit(*src)) + v = tolower(*src) - 'a' + 10; + else + v = base; + if (v >= base) + return (STATUS_INVALID_PARAMETER); + res = res * base + v; + } + *val = negative ? -res : res; + return (STATUS_SUCCESS); } -__stdcall __regcall void -ntoskrnl_unlock_dpc(REGARGS1(kspin_lock *lock)) +static void +ntoskrnl_destroy_workitem_threads(void) { - atomic_poll_release_int((volatile u_int *)lock); + kdpc_queue *kq; + int i; + + for (i = 0; i < WORKITEM_THREADS; i++) { + kq = wq_queues + i; + kq->kq_exit = 1; + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); + while (kq->kq_exit) + tsleep(kq->kq_td, 0, "waitiw", hz/10); + } } -__stdcall __regcall static uint32_t -ntoskrnl_interlock_inc(REGARGS1(volatile uint32_t *addend)) +io_workitem * +IoAllocateWorkItem(device_object *dobj) { - atomic_add_long((volatile u_long *)addend, 1); - return(*addend); + io_workitem *iw; + + iw = objcache_get(iw_cache, M_NOWAIT); + if (iw == NULL) + return (NULL); + + InitializeListHead(&iw->iw_listentry); + iw->iw_dobj = dobj; + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); + iw->iw_idx = wq_idx; + WORKIDX_INC(wq_idx); + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + + return (iw); } -__stdcall __regcall static uint32_t -ntoskrnl_interlock_dec(REGARGS1(volatile uint32_t *addend)) +void +IoFreeWorkItem(io_workitem *iw) { - atomic_subtract_long((volatile u_long *)addend, 1); - return(*addend); + objcache_put(iw_cache, iw); } -__stdcall __regcall static void -ntoskrnl_interlock_addstat(REGARGS2(uint64_t *addend, uint32_t inc)) +void +IoQueueWorkItem(io_workitem *iw, io_workitem_func iw_func, uint32_t qtype, + void *ctx) { + kdpc_queue *kq; + list_entry *l; + io_workitem *cur; uint8_t irql; - irql = FASTCALL2(hal_lock, &ntoskrnl_global, DISPATCH_LEVEL); - *addend += inc; - FASTCALL2(hal_unlock, &ntoskrnl_global, irql); + kq = wq_queues + iw->iw_idx; - return; -}; + KeAcquireSpinLock(&kq->kq_lock, &irql); -__stdcall static void -ntoskrnl_freemdl(ndis_buffer *mdl) -{ - ndis_buffer *head; + /* + * Traverse the list and make sure this workitem hasn't + * already been inserted. Queuing the same workitem + * twice will hose the list but good. + */ - if (mdl == NULL || mdl->nb_process == NULL) - return; + l = kq->kq_disp.nle_flink; + while (l != &kq->kq_disp) { + cur = CONTAINING_RECORD(l, io_workitem, iw_listentry); + if (cur == iw) { + /* Already queued -- do nothing. */ + KeReleaseSpinLock(&kq->kq_lock, irql); + return; + } + l = l->nle_flink; + } - head = mdl->nb_process; + iw->iw_func = iw_func; + iw->iw_ctx = ctx; - if (head->nb_flags != 0x1) - return; + InsertTailList((&kq->kq_disp), (&iw->iw_listentry)); + KeReleaseSpinLock(&kq->kq_lock, irql); - mdl->nb_next = head->nb_next; - head->nb_next = mdl; + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); +} - /* Decrement count of busy buffers. */ +static void +ntoskrnl_workitem(device_object *dobj, void *arg) +{ + io_workitem *iw; + work_queue_item *w; + work_item_func f; + + iw = arg; + w = (work_queue_item *)dobj; + f = (work_item_func)w->wqi_func; + objcache_put(iw_cache, iw); + MSCALL2(f, w, w->wqi_ctx); +} - head->nb_bytecount--; +/* + * The ExQueueWorkItem() API is deprecated in Windows XP. Microsoft + * warns that it's unsafe and to use IoQueueWorkItem() instead. The + * problem with ExQueueWorkItem() is that it can't guard against + * the condition where a driver submits a job to the work queue and + * is then unloaded before the job is able to run. IoQueueWorkItem() + * acquires a reference to the device's device_object via the + * object manager and retains it until after the job has completed, + * which prevents the driver from being unloaded before the job + * runs. (We don't currently support this behavior, though hopefully + * that will change once the object manager API is fleshed out a bit.) + * + * Having said all that, the ExQueueWorkItem() API remains, because + * there are still other parts of Windows that use it, including + * NDIS itself: NdisScheduleWorkItem() calls ExQueueWorkItem(). + * We fake up the ExQueueWorkItem() API on top of our implementation + * of IoQueueWorkItem(). Workitem thread #3 is reserved exclusively + * for ExQueueWorkItem() jobs, and we pass a pointer to the work + * queue item (provided by the caller) in to IoAllocateWorkItem() + * instead of the device_object. We need to save this pointer so + * we can apply a sanity check: as with the DPC queue and other + * workitem queues, we can't allow the same work queue item to + * be queued twice. If it's already pending, we silently return + */ - /* - * If the pool has been marked for deletion and there are - * no more buffers outstanding, nuke the pool. - */ +void +ExQueueWorkItem(work_queue_item *w, uint32_t qtype) +{ + io_workitem *iw; + io_workitem_func iwf; + kdpc_queue *kq; + list_entry *l; + io_workitem *cur; + uint8_t irql; - if (head->nb_byteoffset && head->nb_bytecount == 0) - kfree(head, M_DEVBUF); - return; -} + /* + * We need to do a special sanity test to make sure + * the ExQueueWorkItem() API isn't used to queue + * the same workitem twice. Rather than checking the + * io_workitem pointer itself, we test the attached + * device object, which is really a pointer to the + * legacy work queue item structure. + */ -__stdcall static uint32_t -ntoskrnl_sizeofmdl(void *vaddr, size_t len) -{ - uint32_t l; + kq = wq_queues + WORKITEM_LEGACY_THREAD; + KeAcquireSpinLock(&kq->kq_lock, &irql); + l = kq->kq_disp.nle_flink; + while (l != &kq->kq_disp) { + cur = CONTAINING_RECORD(l, io_workitem, iw_listentry); + if (cur->iw_dobj == (device_object *)w) { + /* Already queued -- do nothing. */ + KeReleaseSpinLock(&kq->kq_lock, irql); + return; + } + l = l->nle_flink; + } + KeReleaseSpinLock(&kq->kq_lock, irql); - l = sizeof(struct ndis_buffer) + - (sizeof(uint32_t) * SPAN_PAGES(vaddr, len)); + iw = IoAllocateWorkItem((device_object *)w); + if (iw == NULL) + return; - return(l); + iw->iw_idx = WORKITEM_LEGACY_THREAD; + iwf = (io_workitem_func)ntoskrnl_findwrap((funcptr)ntoskrnl_workitem); + IoQueueWorkItem(iw, iwf, qtype, iw); } -__stdcall static void -ntoskrnl_build_npaged_mdl(ndis_buffer *mdl) +static void +RtlZeroMemory(void *dst, size_t len) { - mdl->nb_mappedsystemva = (char *)mdl->nb_startva + mdl->nb_byteoffset; - return; + bzero(dst, len); } -__stdcall static void * -ntoskrnl_mmaplockedpages(ndis_buffer *buf, uint8_t accessmode) +static void +RtlSecureZeroMemory(void *dst, size_t len) { - return(MDL_VA(buf)); + memset(dst, 0, len); } -__stdcall static void * -ntoskrnl_mmaplockedpages_cache(ndis_buffer *buf, uint8_t accessmode, - uint32_t cachetype, void *vaddr, - uint32_t bugcheck, uint32_t prio) +static void +RtlFillMemory(void *dst, size_t len, uint8_t c) { - return(MDL_VA(buf)); + memset(dst, c, len); } -__stdcall static void -ntoskrnl_munmaplockedpages(void *vaddr, ndis_buffer *buf) +static void +RtlMoveMemory(void *dst, const void *src, size_t len) { - return; + memmove(dst, src, len); } -/* - * The KeInitializeSpinLock(), KefAcquireSpinLockAtDpcLevel() - * and KefReleaseSpinLockFromDpcLevel() appear to be analagous - * to crit_enter()/crit_exit() in their use. We can't create a new mutex - * lock here because there is no complimentary KeFreeSpinLock() - * function. Instead, we grab a mutex from the mutex pool. - */ -__stdcall static void -ntoskrnl_init_lock(kspin_lock *lock) -{ - *lock = 0; - - return; +static void +RtlCopyMemory(void *dst, const void *src, size_t len) +{ + bcopy(src, dst, len); } -__stdcall static size_t -ntoskrnl_memcmp(const void *s1, const void *s2, size_t len) +static size_t +RtlCompareMemory(const void *s1, const void *s2, size_t len) { - size_t i, total = 0; + size_t i; uint8_t *m1, *m2; m1 = __DECONST(char *, s1); m2 = __DECONST(char *, s2); - for (i = 0; i < len; i++) { - if (m1[i] == m2[i]) - total++; - } - return(total); + for (i = 0; i < len && m1[i] == m2[i]; i++); + return (i); } -__stdcall static void -ntoskrnl_init_ansi_string(ndis_ansi_string *dst, char *src) +void +RtlInitAnsiString(ansi_string *dst, char *src) { - ndis_ansi_string *a; + ansi_string *a; a = dst; if (a == NULL) return; if (src == NULL) { - a->nas_len = a->nas_maxlen = 0; - a->nas_buf = NULL; + a->as_len = a->as_maxlen = 0; + a->as_buf = NULL; } else { - a->nas_buf = src; - a->nas_len = a->nas_maxlen = strlen(src); + a->as_buf = src; + a->as_len = a->as_maxlen = strlen(src); } - - return; } -__stdcall static void -ntoskrnl_init_unicode_string(ndis_unicode_string *dst, uint16_t *src) +void +RtlInitUnicodeString(unicode_string *dst, uint16_t *src) { - ndis_unicode_string *u; + unicode_string *u; int i; u = dst; if (u == NULL) return; if (src == NULL) { - u->nus_len = u->nus_maxlen = 0; - u->nus_buf = NULL; + u->us_len = u->us_maxlen = 0; + u->us_buf = NULL; } else { i = 0; while(src[i] != 0) i++; - u->nus_buf = src; - u->nus_len = u->nus_maxlen = i * 2; + u->us_buf = src; + u->us_len = u->us_maxlen = i * 2; } - - return; } -__stdcall ndis_status -ntoskrnl_unicode_to_int(ndis_unicode_string *ustr, uint32_t base, - uint32_t *val) +ndis_status +RtlUnicodeStringToInteger(unicode_string *ustr, uint32_t base, uint32_t *val) { uint16_t *uchr; int len, neg = 0; char abuf[64]; char *astr; - uchr = ustr->nus_buf; - len = ustr->nus_len; + uchr = ustr->us_buf; + len = ustr->us_len; bzero(abuf, sizeof(abuf)); if ((char)((*uchr) & 0xFF) == '-') { @@ -1172,30 +3006,28 @@ ntoskrnl_unicode_to_int(ndis_unicode_string *ustr, uint32_t base, astr++; } - ndis_unicode_to_ascii(uchr, len, &astr); + ntoskrnl_unicode_to_ascii(uchr, astr, len); *val = strtoul(abuf, NULL, base); - return(NDIS_STATUS_SUCCESS); + return (STATUS_SUCCESS); } -__stdcall static void -ntoskrnl_free_unicode_string(ndis_unicode_string *ustr) +void +RtlFreeUnicodeString(unicode_string *ustr) { - if (ustr->nus_buf == NULL) + if (ustr->us_buf == NULL) return; - kfree(ustr->nus_buf, M_DEVBUF); - ustr->nus_buf = NULL; - return; + ExFreePool(ustr->us_buf); + ustr->us_buf = NULL; } -__stdcall static void -ntoskrnl_free_ansi_string(ndis_ansi_string *astr) +void +RtlFreeAnsiString(ansi_string *astr) { - if (astr->nas_buf == NULL) + if (astr->as_buf == NULL) return; - kfree(astr->nas_buf, M_DEVBUF); - astr->nas_buf = NULL; - return; + ExFreePool(astr->as_buf); + astr->as_buf = NULL; } static int @@ -1217,159 +3049,283 @@ rand(void) microtime(&tv); skrandom(tv.tv_usec); - return((int)krandom()); + return ((int)krandom()); +} + +static void +srand(unsigned int seed) +{ + skrandom(seed); } -__stdcall static uint8_t -ntoskrnl_wdmver(uint8_t major, uint8_t minor) +static uint8_t +IoIsWdmVersionAvailable(uint8_t major, uint8_t minor) { if (major == WDM_MAJOR && minor == WDM_MINOR_WINXP) - return(TRUE); - return(FALSE); + return (TRUE); + return (FALSE); +} + +static int32_t +IoOpenDeviceRegistryKey(struct device_object *devobj, uint32_t type, + uint32_t mask, void **key) +{ + return (NDIS_STATUS_INVALID_DEVICE_REQUEST); } -__stdcall static ndis_status -ntoskrnl_devprop(device_object *devobj, uint32_t regprop, uint32_t buflen, - void *prop, uint32_t *reslen) +static ndis_status +IoGetDeviceObjectPointer(unicode_string *name, uint32_t reqaccess, + void *fileobj, device_object *devobj) { - ndis_miniport_block *block; + return (STATUS_SUCCESS); +} + +static ndis_status +IoGetDeviceProperty(device_object *devobj, uint32_t regprop, uint32_t buflen, + void *prop, uint32_t *reslen) +{ + driver_object *drv; + uint16_t **name; - block = devobj->do_rsvd; + drv = devobj->do_drvobj; switch (regprop) { case DEVPROP_DRIVER_KEYNAME: - ndis_ascii_to_unicode(__DECONST(char *, - device_get_nameunit(block->nmb_dev)), (uint16_t **)&prop); - *reslen = strlen(device_get_nameunit(block->nmb_dev)) * 2; + name = prop; + *name = drv->dro_drivername.us_buf; + *reslen = drv->dro_drivername.us_len; break; default: - return(STATUS_INVALID_PARAMETER_2); + return (STATUS_INVALID_PARAMETER_2); break; } - return(STATUS_SUCCESS); + return (STATUS_SUCCESS); } -__stdcall static void -ntoskrnl_init_mutex(kmutant *kmutex, uint32_t level) +static void +KeInitializeMutex(kmutant *kmutex, uint32_t level) { - INIT_LIST_HEAD((&kmutex->km_header.dh_waitlisthead)); + InitializeListHead((&kmutex->km_header.dh_waitlisthead)); kmutex->km_abandoned = FALSE; kmutex->km_apcdisable = 1; - kmutex->km_header.dh_sigstate = TRUE; - kmutex->km_header.dh_type = EVENT_TYPE_SYNC; - kmutex->km_header.dh_size = OTYPE_MUTEX; - kmutex->km_acquirecnt = 0; + kmutex->km_header.dh_sigstate = 1; + kmutex->km_header.dh_type = DISP_TYPE_MUTANT; + kmutex->km_header.dh_size = sizeof(kmutant) / sizeof(uint32_t); kmutex->km_ownerthread = NULL; - return; } -__stdcall static uint32_t -ntoskrnl_release_mutex(kmutant *kmutex, uint8_t kwait) +static uint32_t +KeReleaseMutex(kmutant *kmutex, uint8_t kwait) { - lwkt_gettoken(&ntoskrnl_dispatchtoken); - if (kmutex->km_ownerthread != curthread->td_proc) { - lwkt_reltoken(&ntoskrnl_dispatchtoken); - return(STATUS_MUTANT_NOT_OWNED); + uint32_t prevstate; + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); + prevstate = kmutex->km_header.dh_sigstate; + if (kmutex->km_ownerthread != curthread) { + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + return (STATUS_MUTANT_NOT_OWNED); } - kmutex->km_acquirecnt--; - if (kmutex->km_acquirecnt == 0) { + + kmutex->km_header.dh_sigstate++; + kmutex->km_abandoned = FALSE; + + if (kmutex->km_header.dh_sigstate == 1) { kmutex->km_ownerthread = NULL; - lwkt_reltoken(&ntoskrnl_dispatchtoken); - ntoskrnl_wakeup(&kmutex->km_header); - } else { - lwkt_reltoken(&ntoskrnl_dispatchtoken); + ntoskrnl_waittest(&kmutex->km_header, IO_NO_INCREMENT); } - return(kmutex->km_acquirecnt); + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + + return (prevstate); } -__stdcall static uint32_t -ntoskrnl_read_mutex(kmutant *kmutex) +static uint32_t +KeReadStateMutex(kmutant *kmutex) { - return(kmutex->km_header.dh_sigstate); + return (kmutex->km_header.dh_sigstate); } -__stdcall void -ntoskrnl_init_event(nt_kevent *kevent, uint32_t type, uint8_t state) +void +KeInitializeEvent(nt_kevent *kevent, uint32_t type, uint8_t state) { - INIT_LIST_HEAD((&kevent->k_header.dh_waitlisthead)); + InitializeListHead((&kevent->k_header.dh_waitlisthead)); kevent->k_header.dh_sigstate = state; - kevent->k_header.dh_type = type; - kevent->k_header.dh_size = OTYPE_EVENT; - return; + if (type == EVENT_TYPE_NOTIFY) + kevent->k_header.dh_type = DISP_TYPE_NOTIFICATION_EVENT; + else + kevent->k_header.dh_type = DISP_TYPE_SYNCHRONIZATION_EVENT; + kevent->k_header.dh_size = sizeof(nt_kevent) / sizeof(uint32_t); } -__stdcall uint32_t -ntoskrnl_reset_event(nt_kevent *kevent) +uint32_t +KeResetEvent(nt_kevent *kevent) { uint32_t prevstate; - lwkt_gettoken(&ntoskrnl_dispatchtoken); + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); prevstate = kevent->k_header.dh_sigstate; kevent->k_header.dh_sigstate = FALSE; - lwkt_reltoken(&ntoskrnl_dispatchtoken); + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); - return(prevstate); + return (prevstate); } -__stdcall uint32_t -ntoskrnl_set_event(nt_kevent *kevent, uint32_t increment, uint8_t kwait) +uint32_t +KeSetEvent(nt_kevent *kevent, uint32_t increment, uint8_t kwait) { uint32_t prevstate; + wait_block *w; + nt_dispatch_header *dh; + struct thread *td; + wb_ext *we; + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); prevstate = kevent->k_header.dh_sigstate; - ntoskrnl_wakeup(&kevent->k_header); + dh = &kevent->k_header; + + if (IsListEmpty(&dh->dh_waitlisthead)) + /* + * If there's nobody in the waitlist, just set + * the state to signalled. + */ + dh->dh_sigstate = 1; + else { + /* + * Get the first waiter. If this is a synchronization + * event, just wake up that one thread (don't bother + * setting the state to signalled since we're supposed + * to automatically clear synchronization events anyway). + * + * If it's a notification event, or the first + * waiter is doing a WAITTYPE_ALL wait, go through + * the full wait satisfaction process. + */ + w = CONTAINING_RECORD(dh->dh_waitlisthead.nle_flink, + wait_block, wb_waitlist); + we = w->wb_ext; + td = we->we_td; + if (kevent->k_header.dh_type == DISP_TYPE_NOTIFICATION_EVENT || + w->wb_waittype == WAITTYPE_ALL) { + if (prevstate == 0) { + dh->dh_sigstate = 1; + ntoskrnl_waittest(dh, increment); + } + } else { + w->wb_awakened |= TRUE; + cv_broadcastpri(&we->we_cv, + (w->wb_oldpri - (increment * 4)) > PRI_MIN_KERN ? + w->wb_oldpri - (increment * 4) : PRI_MIN_KERN); + } + } + + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); - return(prevstate); + return (prevstate); } -__stdcall void -ntoskrnl_clear_event(nt_kevent *kevent) +void +KeClearEvent(nt_kevent *kevent) { kevent->k_header.dh_sigstate = FALSE; - return; } -__stdcall uint32_t -ntoskrnl_read_event(nt_kevent *kevent) +uint32_t +KeReadStateEvent(nt_kevent *kevent) { - return(kevent->k_header.dh_sigstate); + return (kevent->k_header.dh_sigstate); } -__stdcall static ndis_status -ntoskrnl_objref(ndis_handle handle, uint32_t reqaccess, void *otype, - uint8_t accessmode, void **object, void **handleinfo) +/* + * The object manager in Windows is responsible for managing + * references and access to various types of objects, including + * device_objects, events, threads, timers and so on. However, + * there's a difference in the way objects are handled in user + * mode versus kernel mode. + * + * In user mode (i.e. Win32 applications), all objects are + * managed by the object manager. For example, when you create + * a timer or event object, you actually end up with an + * object_header (for the object manager's bookkeeping + * purposes) and an object body (which contains the actual object + * structure, e.g. ktimer, kevent, etc...). This allows Windows + * to manage resource quotas and to enforce access restrictions + * on basically every kind of system object handled by the kernel. + * + * However, in kernel mode, you only end up using the object + * manager some of the time. For example, in a driver, you create + * a timer object by simply allocating the memory for a ktimer + * structure and initializing it with KeInitializeTimer(). Hence, + * the timer has no object_header and no reference counting or + * security/resource checks are done on it. The assumption in + * this case is that if you're running in kernel mode, you know + * what you're doing, and you're already at an elevated privilege + * anyway. + * + * There are some exceptions to this. The two most important ones + * for our purposes are device_objects and threads. We need to use + * the object manager to do reference counting on device_objects, + * and for threads, you can only get a pointer to a thread's + * dispatch header by using ObReferenceObjectByHandle() on the + * handle returned by PsCreateSystemThread(). + */ + +static ndis_status +ObReferenceObjectByHandle(ndis_handle handle, uint32_t reqaccess, void *otype, + uint8_t accessmode, void **object, void **handleinfo) { nt_objref *nr; - nr = kmalloc(sizeof(nt_objref), M_DEVBUF, M_WAITOK|M_ZERO); + nr = kmalloc(sizeof(nt_objref), M_DEVBUF, M_NOWAIT|M_ZERO); + if (nr == NULL) + return (STATUS_INSUFFICIENT_RESOURCES); - INIT_LIST_HEAD((&nr->no_dh.dh_waitlisthead)); + InitializeListHead((&nr->no_dh.dh_waitlisthead)); nr->no_obj = handle; - nr->no_dh.dh_size = OTYPE_THREAD; + nr->no_dh.dh_type = DISP_TYPE_THREAD; + nr->no_dh.dh_sigstate = 0; + nr->no_dh.dh_size = (uint8_t)(sizeof(struct thread) / + sizeof(uint32_t)); TAILQ_INSERT_TAIL(&ntoskrnl_reflist, nr, link); *object = nr; - return(NDIS_STATUS_SUCCESS); + return (STATUS_SUCCESS); } -__stdcall __regcall static void -ntoskrnl_objderef(REGARGS1(void *object)) +static void +ObfDereferenceObject(void *object) { nt_objref *nr; nr = object; TAILQ_REMOVE(&ntoskrnl_reflist, nr, link); kfree(nr, M_DEVBUF); +} + +static uint32_t +ZwClose(ndis_handle handle) +{ + return (STATUS_SUCCESS); +} + +static uint32_t +WmiQueryTraceInformation(uint32_t traceclass, void *traceinfo, + uint32_t infolen, uint32_t reqlen, void *buf) +{ + return (STATUS_NOT_FOUND); +} - return; +static uint32_t +WmiTraceMessage(uint64_t loghandle, uint32_t messageflags, + void *guid, uint16_t messagenum, ...) +{ + return (STATUS_SUCCESS); } -__stdcall static uint32_t -ntoskrnl_zwclose(ndis_handle handle) +static uint32_t +IoWMIRegistrationControl(device_object *dobj, uint32_t action) { - return(STATUS_SUCCESS); + return (STATUS_SUCCESS); } /* @@ -1380,45 +3336,48 @@ static void ntoskrnl_thrfunc(void *arg) { thread_context *thrctx; - __stdcall uint32_t (*tfunc)(void *); + uint32_t (*tfunc)(void *); void *tctx; uint32_t rval; - get_mplock(); - thrctx = arg; tfunc = thrctx->tc_thrfunc; tctx = thrctx->tc_thrctx; kfree(thrctx, M_TEMP); - rval = tfunc(tctx); - ntoskrnl_thread_exit(rval); - /* not reached */ + rval = MSCALL1(tfunc, tctx); + + PsTerminateSystemThread(rval); + return; /* notreached */ } -__stdcall static ndis_status -ntoskrnl_create_thread(ndis_handle *handle, uint32_t reqaccess, - void *objattrs, ndis_handle phandle, - void *clientid, void *thrfunc, void *thrctx) +static ndis_status +PsCreateSystemThread(ndis_handle *handle, uint32_t reqaccess, void *objattrs, + ndis_handle phandle, void *clientid, void *thrfunc, void *thrctx) { int error; - char tname[128]; thread_context *tc; - thread_t td; + struct thread *p; - tc = kmalloc(sizeof(thread_context), M_TEMP, M_WAITOK); + tc = kmalloc(sizeof(thread_context), M_TEMP, M_NOWAIT); + if (tc == NULL) + return (STATUS_INSUFFICIENT_RESOURCES); tc->tc_thrctx = thrctx; tc->tc_thrfunc = thrfunc; - ksprintf(tname, "windows kthread %d", ntoskrnl_kth); - error = kthread_create_stk(ntoskrnl_thrfunc, tc, &td, - NDIS_KSTACK_PAGES * PAGE_SIZE, tname); - *handle = td; + error = kthread_create(ntoskrnl_thrfunc, tc, &p, "Win kthread %d", + ntoskrnl_kth); + + if (error) { + kfree(tc, M_TEMP); + return (STATUS_INSUFFICIENT_RESOURCES); + } + *handle = p; ntoskrnl_kth++; - return(error); + return (STATUS_SUCCESS); } /* @@ -1429,59 +3388,92 @@ ntoskrnl_create_thread(ndis_handle *handle, uint32_t reqaccess, * reference list, and if someone holds a reference to us, we poke * them. */ -__stdcall static ndis_status -ntoskrnl_thread_exit(ndis_status status) +static ndis_status +PsTerminateSystemThread(ndis_status status) { struct nt_objref *nr; + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); TAILQ_FOREACH(nr, &ntoskrnl_reflist, link) { - if (nr->no_obj != curthread) + if (nr->no_obj != curthread->td_proc) continue; - ntoskrnl_wakeup(&nr->no_dh); + nr->no_dh.dh_sigstate = 1; + ntoskrnl_waittest(&nr->no_dh, IO_NO_INCREMENT); break; } + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); ntoskrnl_kth--; - rel_mplock(); - kthread_exit(); /* call explicitly */ - - return(0); /* notreached */ + wakeup(curthread); + kthread_exit(); + return (0); /* notreached */ } static uint32_t -ntoskrnl_dbgprint(char *fmt, ...) +DbgPrint(char *fmt, ...) { - __va_list ap; + va_list ap; if (bootverbose) { - __va_start(ap, fmt); + va_start(ap, fmt); kvprintf(fmt, ap); } - return(STATUS_SUCCESS); + return (STATUS_SUCCESS); } -__stdcall static void -ntoskrnl_debugger(void) +static void +DbgBreakPoint(void) { -#if __FreeBSD_version < 502113 - Debugger("ntoskrnl_debugger(): breakpoint"); -#else - kdb_enter("ntoskrnl_debugger(): breakpoint"); -#endif + Debugger("DbgBreakPoint(): breakpoint"); +} + +static void +KeBugCheckEx(uint32_t code, u_long param1, u_long param2, u_long param3, + u_long param4) +{ + panic("KeBugCheckEx: STOP 0x%X", code); } static void ntoskrnl_timercall(void *arg) { ktimer *timer; + struct timeval tv; + kdpc *dpc; + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); timer = arg; +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_fires++; +#endif + ntoskrnl_remove_timer(timer); + + /* + * This should never happen, but complain + * if it does. + */ + + if (timer->k_header.dh_inserted == FALSE) { + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + kprintf("NTOS: timer %p fired even though " + "it was canceled\n", timer); + return; + } + + /* Mark the timer as no longer being on the timer queue. */ + timer->k_header.dh_inserted = FALSE; + /* Now signal the object and satisfy any waits on it. */ + + timer->k_header.dh_sigstate = 1; + ntoskrnl_waittest(&timer->k_header, IO_NO_INCREMENT); + /* * If this is a periodic timer, re-arm it * so it will fire again. We do this before @@ -1492,115 +3484,418 @@ ntoskrnl_timercall(void *arg) */ if (timer->k_period) { + tv.tv_sec = 0; + tv.tv_usec = timer->k_period * 1000; timer->k_header.dh_inserted = TRUE; - callout_reset(timer->k_handle, 1 + timer->k_period * hz / 1000, - ntoskrnl_timercall, timer); - } else { - callout_deactivate(timer->k_handle); - kfree(timer->k_handle, M_NDIS); - timer->k_handle = NULL; + ntoskrnl_insert_timer(timer, tvtohz_high(&tv)); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_reloads++; +#endif + } + + dpc = timer->k_dpc; + + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + + /* If there's a DPC associated with the timer, queue it up. */ + + if (dpc != NULL) + KeInsertQueueDpc(dpc, NULL, NULL); +} + +#ifdef NTOSKRNL_DEBUG_TIMERS +static int +sysctl_show_timers(SYSCTL_HANDLER_ARGS) +{ + int ret; + + ret = 0; + ntoskrnl_show_timers(); + return (sysctl_handle_int(oidp, &ret, 0, req)); +} + +static void +ntoskrnl_show_timers(void) +{ + int i = 0; + list_entry *l; + + mtx_spinlock(&ntoskrnl_calllock); + l = ntoskrnl_calllist.nle_flink; + while(l != &ntoskrnl_calllist) { + i++; + l = l->nle_flink; + } + mtx_spinunlock(&ntoskrnl_calllock); + + kprintf("\n"); + kprintf("%d timers available (out of %d)\n", i, NTOSKRNL_TIMEOUTS); + kprintf("timer sets: %qu\n", ntoskrnl_timer_sets); + kprintf("timer reloads: %qu\n", ntoskrnl_timer_reloads); + kprintf("timer cancels: %qu\n", ntoskrnl_timer_cancels); + kprintf("timer fires: %qu\n", ntoskrnl_timer_fires); + kprintf("\n"); +} +#endif + +/* + * Must be called with dispatcher lock held. + */ + +static void +ntoskrnl_insert_timer(ktimer *timer, int ticks) +{ + callout_entry *e; + list_entry *l; + struct callout *c; + + /* + * Try and allocate a timer. + */ + mtx_spinlock(&ntoskrnl_calllock); + if (IsListEmpty(&ntoskrnl_calllist)) { + mtx_spinunlock(&ntoskrnl_calllock); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_show_timers(); +#endif + panic("out of timers!"); } + l = RemoveHeadList(&ntoskrnl_calllist); + mtx_spinunlock(&ntoskrnl_calllock); - if (timer->k_dpc != NULL) - ntoskrnl_queue_dpc(timer->k_dpc, NULL, NULL); + e = CONTAINING_RECORD(l, callout_entry, ce_list); + c = &e->ce_callout; - ntoskrnl_wakeup(&timer->k_header); + timer->k_callout = c; + + callout_init(c); + callout_reset(c, ticks, ntoskrnl_timercall, timer); +} + +static void +ntoskrnl_remove_timer(ktimer *timer) +{ + callout_entry *e; + + e = (callout_entry *)timer->k_callout; + callout_stop(timer->k_callout); + + mtx_spinlock(&ntoskrnl_calllock); + InsertHeadList((&ntoskrnl_calllist), (&e->ce_list)); + mtx_spinunlock(&ntoskrnl_calllock); } -__stdcall void -ntoskrnl_init_timer(ktimer *timer) +void +KeInitializeTimer(ktimer *timer) { if (timer == NULL) return; - ntoskrnl_init_timer_ex(timer, EVENT_TYPE_NOTIFY); + KeInitializeTimerEx(timer, EVENT_TYPE_NOTIFY); } -__stdcall void -ntoskrnl_init_timer_ex(ktimer *timer, uint32_t type) +void +KeInitializeTimerEx(ktimer *timer, uint32_t type) { if (timer == NULL) return; - INIT_LIST_HEAD((&timer->k_header.dh_waitlisthead)); + bzero((char *)timer, sizeof(ktimer)); + InitializeListHead((&timer->k_header.dh_waitlisthead)); timer->k_header.dh_sigstate = FALSE; timer->k_header.dh_inserted = FALSE; - timer->k_header.dh_type = type; - timer->k_header.dh_size = OTYPE_TIMER; - timer->k_handle = NULL; - - return; + if (type == EVENT_TYPE_NOTIFY) + timer->k_header.dh_type = DISP_TYPE_NOTIFICATION_TIMER; + else + timer->k_header.dh_type = DISP_TYPE_SYNCHRONIZATION_TIMER; + timer->k_header.dh_size = sizeof(ktimer) / sizeof(uint32_t); } /* - * This is a wrapper for Windows deferred procedure calls that - * have been placed on an NDIS thread work queue. We need it - * since the DPC could be a _stdcall function. Also, as far as - * I can tell, defered procedure calls must run at DISPATCH_LEVEL. + * DPC subsystem. A Windows Defered Procedure Call has the following + * properties: + * - It runs at DISPATCH_LEVEL. + * - It can have one of 3 importance values that control when it + * runs relative to other DPCs in the queue. + * - On SMP systems, it can be set to run on a specific processor. + * In order to satisfy the last property, we create a DPC thread for + * each CPU in the system and bind it to that CPU. Each thread + * maintains three queues with different importance levels, which + * will be processed in order from lowest to highest. + * + * In Windows, interrupt handlers run as DPCs. (Not to be confused + * with ISRs, which run in interrupt context and can preempt DPCs.) + * ISRs are given the highest importance so that they'll take + * precedence over timers and other things. */ + static void -ntoskrnl_run_dpc(void *arg) +ntoskrnl_dpc_thread(void *arg) { - kdpc_func dpcfunc; - kdpc *dpc; + kdpc_queue *kq; + kdpc *d; + list_entry *l; uint8_t irql; - dpc = arg; - dpcfunc = (kdpc_func)dpc->k_deferedfunc; - irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL); - dpcfunc(dpc, dpc->k_deferredctx, dpc->k_sysarg1, dpc->k_sysarg2); - FASTCALL1(hal_lower_irql, irql); + kq = arg; + + InitializeListHead(&kq->kq_disp); + kq->kq_td = curthread; + kq->kq_exit = 0; + kq->kq_running = FALSE; + KeInitializeSpinLock(&kq->kq_lock); + KeInitializeEvent(&kq->kq_proc, EVENT_TYPE_SYNC, FALSE); + KeInitializeEvent(&kq->kq_done, EVENT_TYPE_SYNC, FALSE); + + /* + * Elevate our priority. DPCs are used to run interrupt + * handlers, and they should trigger as soon as possible + * once scheduled by an ISR. + */ + +#ifdef NTOSKRNL_MULTIPLE_DPCS + sched_bind(curthread, kq->kq_cpu); +#endif + lwkt_setpri_self(TDPRI_INT_HIGH); + + while (1) { + KeWaitForSingleObject(&kq->kq_proc, 0, 0, TRUE, NULL); + + KeAcquireSpinLock(&kq->kq_lock, &irql); + + if (kq->kq_exit) { + kq->kq_exit = 0; + KeReleaseSpinLock(&kq->kq_lock, irql); + break; + } + + kq->kq_running = TRUE; + + while (!IsListEmpty(&kq->kq_disp)) { + l = RemoveHeadList((&kq->kq_disp)); + d = CONTAINING_RECORD(l, kdpc, k_dpclistentry); + InitializeListHead((&d->k_dpclistentry)); + KeReleaseSpinLockFromDpcLevel(&kq->kq_lock); + MSCALL4(d->k_deferedfunc, d, d->k_deferredctx, + d->k_sysarg1, d->k_sysarg2); + KeAcquireSpinLockAtDpcLevel(&kq->kq_lock); + } + + kq->kq_running = FALSE; + + KeReleaseSpinLock(&kq->kq_lock, irql); + + KeSetEvent(&kq->kq_done, IO_NO_INCREMENT, FALSE); + } + + wakeup(curthread); + kthread_exit(); + return; /* notreached */ +} + +static void +ntoskrnl_destroy_dpc_threads(void) +{ + kdpc_queue *kq; + kdpc dpc; + int i; + + kq = kq_queues; +#ifdef NTOSKRNL_MULTIPLE_DPCS + for (i = 0; i < ncpus; i++) { +#else + for (i = 0; i < 1; i++) { +#endif + kq += i; + + kq->kq_exit = 1; + KeInitializeDpc(&dpc, NULL, NULL); + KeSetTargetProcessorDpc(&dpc, i); + KeInsertQueueDpc(&dpc, NULL, NULL); + while (kq->kq_exit) + tsleep(kq->kq_td, 0, "dpcw", hz/10); + } +} + +static uint8_t +ntoskrnl_insert_dpc(list_entry *head, kdpc *dpc) +{ + list_entry *l; + kdpc *d; + + l = head->nle_flink; + while (l != head) { + d = CONTAINING_RECORD(l, kdpc, k_dpclistentry); + if (d == dpc) + return (FALSE); + l = l->nle_flink; + } + + if (dpc->k_importance == KDPC_IMPORTANCE_LOW) + InsertTailList((head), (&dpc->k_dpclistentry)); + else + InsertHeadList((head), (&dpc->k_dpclistentry)); - return; + return (TRUE); } -__stdcall void -ntoskrnl_init_dpc(kdpc *dpc, void *dpcfunc, void *dpcctx) +void +KeInitializeDpc(kdpc *dpc, void *dpcfunc, void *dpcctx) { + if (dpc == NULL) return; dpc->k_deferedfunc = dpcfunc; dpc->k_deferredctx = dpcctx; + dpc->k_num = KDPC_CPU_DEFAULT; + dpc->k_importance = KDPC_IMPORTANCE_MEDIUM; + InitializeListHead((&dpc->k_dpclistentry)); +} + +uint8_t +KeInsertQueueDpc(kdpc *dpc, void *sysarg1, void *sysarg2) +{ + kdpc_queue *kq; + uint8_t r; + uint8_t irql; + + if (dpc == NULL) + return (FALSE); + + kq = kq_queues; + +#ifdef NTOSKRNL_MULTIPLE_DPCS + KeRaiseIrql(DISPATCH_LEVEL, &irql); + + /* + * By default, the DPC is queued to run on the same CPU + * that scheduled it. + */ + + if (dpc->k_num == KDPC_CPU_DEFAULT) + kq += curthread->td_oncpu; + else + kq += dpc->k_num; + KeAcquireSpinLockAtDpcLevel(&kq->kq_lock); +#else + KeAcquireSpinLock(&kq->kq_lock, &irql); +#endif + + r = ntoskrnl_insert_dpc(&kq->kq_disp, dpc); + if (r == TRUE) { + dpc->k_sysarg1 = sysarg1; + dpc->k_sysarg2 = sysarg2; + } + KeReleaseSpinLock(&kq->kq_lock, irql); + + if (r == FALSE) + return (r); + + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); + + return (r); +} + +uint8_t +KeRemoveQueueDpc(kdpc *dpc) +{ + kdpc_queue *kq; + uint8_t irql; + + if (dpc == NULL) + return (FALSE); + +#ifdef NTOSKRNL_MULTIPLE_DPCS + KeRaiseIrql(DISPATCH_LEVEL, &irql); + + kq = kq_queues + dpc->k_num; + + KeAcquireSpinLockAtDpcLevel(&kq->kq_lock); +#else + kq = kq_queues; + KeAcquireSpinLock(&kq->kq_lock, &irql); +#endif + + if (dpc->k_dpclistentry.nle_flink == &dpc->k_dpclistentry) { + KeReleaseSpinLockFromDpcLevel(&kq->kq_lock); + KeLowerIrql(irql); + return (FALSE); + } - return; + RemoveEntryList((&dpc->k_dpclistentry)); + InitializeListHead((&dpc->k_dpclistentry)); + + KeReleaseSpinLock(&kq->kq_lock, irql); + + return (TRUE); +} + +void +KeSetImportanceDpc(kdpc *dpc, uint32_t imp) +{ + if (imp != KDPC_IMPORTANCE_LOW && + imp != KDPC_IMPORTANCE_MEDIUM && + imp != KDPC_IMPORTANCE_HIGH) + return; + + dpc->k_importance = (uint8_t)imp; } -__stdcall uint8_t -ntoskrnl_queue_dpc(kdpc *dpc, void *sysarg1, void *sysarg2) +void +KeSetTargetProcessorDpc(kdpc *dpc, uint8_t cpu) { - dpc->k_sysarg1 = sysarg1; - dpc->k_sysarg2 = sysarg2; - if (ndis_sched(ntoskrnl_run_dpc, dpc, NDIS_SWI)) - return(FALSE); + if (cpu > ncpus) + return; - return(TRUE); + dpc->k_num = cpu; } -__stdcall uint8_t -ntoskrnl_dequeue_dpc(kdpc *dpc) +void +KeFlushQueuedDpcs(void) { - if (ndis_unsched(ntoskrnl_run_dpc, dpc, NDIS_SWI)) - return(FALSE); + kdpc_queue *kq; + int i; + + /* + * Poke each DPC queue and wait + * for them to drain. + */ + +#ifdef NTOSKRNL_MULTIPLE_DPCS + for (i = 0; i < ncpus; i++) { +#else + for (i = 0; i < 1; i++) { +#endif + kq = kq_queues + i; + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); + KeWaitForSingleObject(&kq->kq_done, 0, 0, TRUE, NULL); + } +} - return(TRUE); +uint32_t +KeGetCurrentProcessorNumber(void) +{ + return (curthread->td_gd->gd_cpuid); } -__stdcall uint8_t -ntoskrnl_set_timer_ex(ktimer *timer, int64_t duetime, uint32_t period, - kdpc *dpc) +uint8_t +KeSetTimerEx(ktimer *timer, int64_t duetime, uint32_t period, kdpc *dpc) { struct timeval tv; uint64_t curtime; uint8_t pending; - int ticks; if (timer == NULL) - return(FALSE); + return (FALSE); + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); if (timer->k_header.dh_inserted == TRUE) { - if (timer->k_handle != NULL) - callout_stop(timer->k_handle); + ntoskrnl_remove_timer(timer); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_cancels++; +#endif timer->k_header.dh_inserted = FALSE; pending = TRUE; } else @@ -1626,157 +3921,315 @@ ntoskrnl_set_timer_ex(ktimer *timer, int64_t duetime, uint32_t period, } } - ticks = 1 + tv.tv_sec * hz + tv.tv_usec * hz / 1000000; timer->k_header.dh_inserted = TRUE; - if (timer->k_handle == NULL) { - timer->k_handle = kmalloc(sizeof(struct callout), M_NDIS, - M_INTWAIT); - callout_init(timer->k_handle); - } - callout_reset(timer->k_handle, ticks, ntoskrnl_timercall, timer); + ntoskrnl_insert_timer(timer, tvtohz_high(&tv)); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_sets++; +#endif - return(pending); + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + + return (pending); } -__stdcall uint8_t -ntoskrnl_set_timer(ktimer *timer, int64_t duetime, kdpc *dpc) +uint8_t +KeSetTimer(ktimer *timer, int64_t duetime, kdpc *dpc) { - return (ntoskrnl_set_timer_ex(timer, duetime, 0, dpc)); + return (KeSetTimerEx(timer, duetime, 0, dpc)); } -__stdcall uint8_t -ntoskrnl_cancel_timer(ktimer *timer) +/* + * The Windows DDK documentation seems to say that cancelling + * a timer that has a DPC will result in the DPC also being + * cancelled, but this isn't really the case. + */ + +uint8_t +KeCancelTimer(ktimer *timer) { uint8_t pending; if (timer == NULL) - return(FALSE); + return (FALSE); + + lockmgr(&ntoskrnl_dispatchlock, LK_EXCLUSIVE); + + pending = timer->k_header.dh_inserted; if (timer->k_header.dh_inserted == TRUE) { - if (timer->k_handle != NULL) { - callout_stop(timer->k_handle); - kfree(timer->k_handle, M_NDIS); - timer->k_handle = NULL; - } - if (timer->k_dpc != NULL) - ntoskrnl_dequeue_dpc(timer->k_dpc); - pending = TRUE; - } else - pending = FALSE; + timer->k_header.dh_inserted = FALSE; + ntoskrnl_remove_timer(timer); +#ifdef NTOSKRNL_DEBUG_TIMERS + ntoskrnl_timer_cancels++; +#endif + } + + lockmgr(&ntoskrnl_dispatchlock, LK_RELEASE); + + return (pending); +} + +uint8_t +KeReadStateTimer(ktimer *timer) +{ + return (timer->k_header.dh_sigstate); +} + +static int32_t +KeDelayExecutionThread(uint8_t wait_mode, uint8_t alertable, int64_t *interval) +{ + ktimer timer; + + if (wait_mode != 0) + panic("invalid wait_mode %d", wait_mode); + + KeInitializeTimer(&timer); + KeSetTimer(&timer, *interval, NULL); + KeWaitForSingleObject(&timer, 0, 0, alertable, NULL); + + return STATUS_SUCCESS; +} + +static uint64_t +KeQueryInterruptTime(void) +{ + int ticks; + struct timeval tv; + + getmicrouptime(&tv); + + ticks = tvtohz_high(&tv); + + return ticks * ((10000000 + hz - 1) / hz); +} +static struct thread * +KeGetCurrentThread(void) +{ - return(pending); + return curthread; } -__stdcall uint8_t -ntoskrnl_read_timer(ktimer *timer) +static int32_t +KeSetPriorityThread(struct thread *td, int32_t pri) { - return(timer->k_header.dh_sigstate); + int32_t old; + + if (td == NULL) + return LOW_REALTIME_PRIORITY; + + if (td->td_pri >= TDPRI_INT_HIGH) + old = HIGH_PRIORITY; + else if (td->td_pri <= TDPRI_IDLE_WORK) + old = LOW_PRIORITY; + else + old = LOW_REALTIME_PRIORITY; + + if (pri == HIGH_PRIORITY) + lwkt_setpri(td, TDPRI_INT_HIGH); + if (pri == LOW_REALTIME_PRIORITY) + lwkt_setpri(td, TDPRI_SOFT_TIMER); + if (pri == LOW_PRIORITY) + lwkt_setpri(td, TDPRI_IDLE_WORK); + + return old; } -__stdcall static void +static void dummy(void) { - kprintf ("ntoskrnl dummy called...\n"); - return; + kprintf("ntoskrnl dummy called...\n"); } image_patch_table ntoskrnl_functbl[] = { - { "RtlCompareMemory", (FUNC)ntoskrnl_memcmp }, - { "RtlEqualUnicodeString", (FUNC)ntoskrnl_unicode_equal }, - { "RtlCopyUnicodeString", (FUNC)ntoskrnl_unicode_copy }, - { "RtlUnicodeStringToAnsiString", (FUNC)ntoskrnl_unicode_to_ansi }, - { "RtlAnsiStringToUnicodeString", (FUNC)ntoskrnl_ansi_to_unicode }, - { "RtlInitAnsiString", (FUNC)ntoskrnl_init_ansi_string }, - { "RtlInitUnicodeString", (FUNC)ntoskrnl_init_unicode_string }, - { "RtlFreeAnsiString", (FUNC)ntoskrnl_free_ansi_string }, - { "RtlFreeUnicodeString", (FUNC)ntoskrnl_free_unicode_string }, - { "RtlUnicodeStringToInteger", (FUNC)ntoskrnl_unicode_to_int }, - { "sprintf", (FUNC)ksprintf }, - { "vsprintf", (FUNC)kvsprintf }, - { "_snprintf", (FUNC)ksnprintf }, - { "_vsnprintf", (FUNC)kvsnprintf }, - { "DbgPrint", (FUNC)ntoskrnl_dbgprint }, - { "DbgBreakPoint", (FUNC)ntoskrnl_debugger }, - { "strncmp", (FUNC)strncmp }, - { "strcmp", (FUNC)strcmp }, - { "strncpy", (FUNC)strncpy }, - { "strcpy", (FUNC)strcpy }, - { "strlen", (FUNC)strlen }, - { "memcpy", (FUNC)memcpy }, - { "memmove", (FUNC)memcpy }, - { "memset", (FUNC)memset }, - { "IofCallDriver", (FUNC)ntoskrnl_iofcalldriver }, - { "IofCompleteRequest", (FUNC)ntoskrnl_iofcompletereq }, - { "IoBuildSynchronousFsdRequest", (FUNC)ntoskrnl_iobuildsynchfsdreq }, - { "KeWaitForSingleObject", (FUNC)ntoskrnl_waitforobj }, - { "KeWaitForMultipleObjects", (FUNC)ntoskrnl_waitforobjs }, - { "_allmul", (FUNC)_allmul }, - { "_alldiv", (FUNC)_alldiv }, - { "_allrem", (FUNC)_allrem }, - { "_allshr", (FUNC)_allshr }, - { "_allshl", (FUNC)_allshl }, - { "_aullmul", (FUNC)_aullmul }, - { "_aulldiv", (FUNC)_aulldiv }, - { "_aullrem", (FUNC)_aullrem }, - { "_aullshr", (FUNC)_aullshr }, - { "_aullshl", (FUNC)_aullshl }, - { "atoi", (FUNC)atoi }, - { "atol", (FUNC)atol }, - { "rand", (FUNC)rand }, - { "WRITE_REGISTER_USHORT", (FUNC)ntoskrnl_writereg_ushort }, - { "READ_REGISTER_USHORT", (FUNC)ntoskrnl_readreg_ushort }, - { "WRITE_REGISTER_ULONG", (FUNC)ntoskrnl_writereg_ulong }, - { "READ_REGISTER_ULONG", (FUNC)ntoskrnl_readreg_ulong }, - { "READ_REGISTER_UCHAR", (FUNC)ntoskrnl_readreg_uchar }, - { "WRITE_REGISTER_UCHAR", (FUNC)ntoskrnl_writereg_uchar }, - { "ExInitializePagedLookasideList", (FUNC)ntoskrnl_init_lookaside }, - { "ExDeletePagedLookasideList", (FUNC)ntoskrnl_delete_lookaside }, - { "ExInitializeNPagedLookasideList", (FUNC)ntoskrnl_init_nplookaside }, - { "ExDeleteNPagedLookasideList", (FUNC)ntoskrnl_delete_nplookaside }, - { "InterlockedPopEntrySList", (FUNC)ntoskrnl_pop_slist }, - { "InterlockedPushEntrySList", (FUNC)ntoskrnl_push_slist }, - { "ExInterlockedPopEntrySList", (FUNC)ntoskrnl_pop_slist_ex }, - { "ExInterlockedPushEntrySList",(FUNC)ntoskrnl_push_slist_ex }, - { "KefAcquireSpinLockAtDpcLevel", (FUNC)ntoskrnl_lock_dpc }, - { "KefReleaseSpinLockFromDpcLevel", (FUNC)ntoskrnl_unlock_dpc }, - { "InterlockedIncrement", (FUNC)ntoskrnl_interlock_inc }, - { "InterlockedDecrement", (FUNC)ntoskrnl_interlock_dec }, - { "ExInterlockedAddLargeStatistic", - (FUNC)ntoskrnl_interlock_addstat }, - { "IoFreeMdl", (FUNC)ntoskrnl_freemdl }, - { "MmSizeOfMdl", (FUNC)ntoskrnl_sizeofmdl }, - { "MmMapLockedPages", (FUNC)ntoskrnl_mmaplockedpages }, - { "MmMapLockedPagesSpecifyCache", - (FUNC)ntoskrnl_mmaplockedpages_cache }, - { "MmUnmapLockedPages", (FUNC)ntoskrnl_munmaplockedpages }, - { "MmBuildMdlForNonPagedPool", (FUNC)ntoskrnl_build_npaged_mdl }, - { "KeInitializeSpinLock", (FUNC)ntoskrnl_init_lock }, - { "IoIsWdmVersionAvailable", (FUNC)ntoskrnl_wdmver }, - { "IoGetDeviceProperty", (FUNC)ntoskrnl_devprop }, - { "KeInitializeMutex", (FUNC)ntoskrnl_init_mutex }, - { "KeReleaseMutex", (FUNC)ntoskrnl_release_mutex }, - { "KeReadStateMutex", (FUNC)ntoskrnl_read_mutex }, - { "KeInitializeEvent", (FUNC)ntoskrnl_init_event }, - { "KeSetEvent", (FUNC)ntoskrnl_set_event }, - { "KeResetEvent", (FUNC)ntoskrnl_reset_event }, - { "KeClearEvent", (FUNC)ntoskrnl_clear_event }, - { "KeReadStateEvent", (FUNC)ntoskrnl_read_event }, - { "KeInitializeTimer", (FUNC)ntoskrnl_init_timer }, - { "KeInitializeTimerEx", (FUNC)ntoskrnl_init_timer_ex }, - { "KeSetTimer", (FUNC)ntoskrnl_set_timer }, - { "KeSetTimerEx", (FUNC)ntoskrnl_set_timer_ex }, - { "KeCancelTimer", (FUNC)ntoskrnl_cancel_timer }, - { "KeReadStateTimer", (FUNC)ntoskrnl_read_timer }, - { "KeInitializeDpc", (FUNC)ntoskrnl_init_dpc }, - { "KeInsertQueueDpc", (FUNC)ntoskrnl_queue_dpc }, - { "KeRemoveQueueDpc", (FUNC)ntoskrnl_dequeue_dpc }, - { "ObReferenceObjectByHandle", (FUNC)ntoskrnl_objref }, - { "ObfDereferenceObject", (FUNC)ntoskrnl_objderef }, - { "ZwClose", (FUNC)ntoskrnl_zwclose }, - { "PsCreateSystemThread", (FUNC)ntoskrnl_create_thread }, - { "PsTerminateSystemThread", (FUNC)ntoskrnl_thread_exit }, + IMPORT_SFUNC(RtlZeroMemory, 2), + IMPORT_SFUNC(RtlSecureZeroMemory, 2), + IMPORT_SFUNC(RtlFillMemory, 3), + IMPORT_SFUNC(RtlMoveMemory, 3), + IMPORT_SFUNC(RtlCharToInteger, 3), + IMPORT_SFUNC(RtlCopyMemory, 3), + IMPORT_SFUNC(RtlCopyString, 2), + IMPORT_SFUNC(RtlCompareMemory, 3), + IMPORT_SFUNC(RtlEqualUnicodeString, 3), + IMPORT_SFUNC(RtlCopyUnicodeString, 2), + IMPORT_SFUNC(RtlUnicodeStringToAnsiString, 3), + IMPORT_SFUNC(RtlAnsiStringToUnicodeString, 3), + IMPORT_SFUNC(RtlInitAnsiString, 2), + IMPORT_SFUNC_MAP(RtlInitString, RtlInitAnsiString, 2), + IMPORT_SFUNC(RtlInitUnicodeString, 2), + IMPORT_SFUNC(RtlFreeAnsiString, 1), + IMPORT_SFUNC(RtlFreeUnicodeString, 1), + IMPORT_SFUNC(RtlUnicodeStringToInteger, 3), + IMPORT_CFUNC_MAP(sprintf, ksprintf, 0), + IMPORT_CFUNC_MAP(vsprintf, kvsprintf, 0), + IMPORT_CFUNC_MAP(_snprintf, ksnprintf, 0), + IMPORT_CFUNC_MAP(_vsnprintf, kvsnprintf, 0), + IMPORT_CFUNC(DbgPrint, 0), + IMPORT_SFUNC(DbgBreakPoint, 0), + IMPORT_SFUNC(KeBugCheckEx, 5), + IMPORT_CFUNC(strncmp, 0), + IMPORT_CFUNC(strcmp, 0), + IMPORT_CFUNC_MAP(stricmp, strcasecmp, 0), + IMPORT_CFUNC(strncpy, 0), + IMPORT_CFUNC(strcpy, 0), + IMPORT_CFUNC(strlen, 0), + IMPORT_CFUNC_MAP(toupper, ntoskrnl_toupper, 0), + IMPORT_CFUNC_MAP(tolower, ntoskrnl_tolower, 0), + IMPORT_CFUNC_MAP(strstr, ntoskrnl_strstr, 0), + IMPORT_CFUNC_MAP(strncat, ntoskrnl_strncat, 0), + IMPORT_CFUNC_MAP(strchr, index, 0), + IMPORT_CFUNC_MAP(strrchr, rindex, 0), + IMPORT_CFUNC(memcpy, 0), + IMPORT_CFUNC_MAP(memmove, ntoskrnl_memmove, 0), + IMPORT_CFUNC_MAP(memset, ntoskrnl_memset, 0), + IMPORT_CFUNC_MAP(memchr, ntoskrnl_memchr, 0), + IMPORT_SFUNC(IoAllocateDriverObjectExtension, 4), + IMPORT_SFUNC(IoGetDriverObjectExtension, 2), + IMPORT_FFUNC(IofCallDriver, 2), + IMPORT_FFUNC(IofCompleteRequest, 2), + IMPORT_SFUNC(IoAcquireCancelSpinLock, 1), + IMPORT_SFUNC(IoReleaseCancelSpinLock, 1), + IMPORT_SFUNC(IoCancelIrp, 1), + IMPORT_SFUNC(IoConnectInterrupt, 11), + IMPORT_SFUNC(IoDisconnectInterrupt, 1), + IMPORT_SFUNC(IoCreateDevice, 7), + IMPORT_SFUNC(IoDeleteDevice, 1), + IMPORT_SFUNC(IoGetAttachedDevice, 1), + IMPORT_SFUNC(IoAttachDeviceToDeviceStack, 2), + IMPORT_SFUNC(IoDetachDevice, 1), + IMPORT_SFUNC(IoBuildSynchronousFsdRequest, 7), + IMPORT_SFUNC(IoBuildAsynchronousFsdRequest, 6), + IMPORT_SFUNC(IoBuildDeviceIoControlRequest, 9), + IMPORT_SFUNC(IoAllocateIrp, 2), + IMPORT_SFUNC(IoReuseIrp, 2), + IMPORT_SFUNC(IoMakeAssociatedIrp, 2), + IMPORT_SFUNC(IoFreeIrp, 1), + IMPORT_SFUNC(IoInitializeIrp, 3), + IMPORT_SFUNC(KeAcquireInterruptSpinLock, 1), + IMPORT_SFUNC(KeReleaseInterruptSpinLock, 2), + IMPORT_SFUNC(KeSynchronizeExecution, 3), + IMPORT_SFUNC(KeWaitForSingleObject, 5), + IMPORT_SFUNC(KeWaitForMultipleObjects, 8), + IMPORT_SFUNC(_allmul, 4), + IMPORT_SFUNC(_alldiv, 4), + IMPORT_SFUNC(_allrem, 4), + IMPORT_RFUNC(_allshr, 0), + IMPORT_RFUNC(_allshl, 0), + IMPORT_SFUNC(_aullmul, 4), + IMPORT_SFUNC(_aulldiv, 4), + IMPORT_SFUNC(_aullrem, 4), + IMPORT_RFUNC(_aullshr, 0), + IMPORT_RFUNC(_aullshl, 0), + IMPORT_CFUNC(atoi, 0), + IMPORT_CFUNC(atol, 0), + IMPORT_CFUNC(rand, 0), + IMPORT_CFUNC(srand, 0), + IMPORT_SFUNC(WRITE_REGISTER_USHORT, 2), + IMPORT_SFUNC(READ_REGISTER_USHORT, 1), + IMPORT_SFUNC(WRITE_REGISTER_ULONG, 2), + IMPORT_SFUNC(READ_REGISTER_ULONG, 1), + IMPORT_SFUNC(READ_REGISTER_UCHAR, 1), + IMPORT_SFUNC(WRITE_REGISTER_UCHAR, 2), + IMPORT_SFUNC(ExInitializePagedLookasideList, 7), + IMPORT_SFUNC(ExDeletePagedLookasideList, 1), + IMPORT_SFUNC(ExInitializeNPagedLookasideList, 7), + IMPORT_SFUNC(ExDeleteNPagedLookasideList, 1), + IMPORT_FFUNC(InterlockedPopEntrySList, 1), + IMPORT_FFUNC(InitializeSListHead, 1), + IMPORT_FFUNC(InterlockedPushEntrySList, 2), + IMPORT_SFUNC(ExQueryDepthSList, 1), + IMPORT_FFUNC_MAP(ExpInterlockedPopEntrySList, + InterlockedPopEntrySList, 1), + IMPORT_FFUNC_MAP(ExpInterlockedPushEntrySList, + InterlockedPushEntrySList, 2), + IMPORT_FFUNC(ExInterlockedPopEntrySList, 2), + IMPORT_FFUNC(ExInterlockedPushEntrySList, 3), + IMPORT_SFUNC(ExAllocatePoolWithTag, 3), + IMPORT_SFUNC(ExFreePoolWithTag, 2), + IMPORT_SFUNC(ExFreePool, 1), +#ifdef __i386__ + IMPORT_FFUNC(KefAcquireSpinLockAtDpcLevel, 1), + IMPORT_FFUNC(KefReleaseSpinLockFromDpcLevel,1), + IMPORT_FFUNC(KeAcquireSpinLockRaiseToDpc, 1), +#else + /* + * For AMD64, we can get away with just mapping + * KeAcquireSpinLockRaiseToDpc() directly to KfAcquireSpinLock() + * because the calling conventions end up being the same. + * On i386, we have to be careful because KfAcquireSpinLock() + * is _fastcall but KeAcquireSpinLockRaiseToDpc() isn't. + */ + IMPORT_SFUNC(KeAcquireSpinLockAtDpcLevel, 1), + IMPORT_SFUNC(KeReleaseSpinLockFromDpcLevel, 1), + IMPORT_SFUNC_MAP(KeAcquireSpinLockRaiseToDpc, KfAcquireSpinLock, 1), +#endif + IMPORT_SFUNC_MAP(KeReleaseSpinLock, KfReleaseSpinLock, 1), + IMPORT_FFUNC(InterlockedIncrement, 1), + IMPORT_FFUNC(InterlockedDecrement, 1), + IMPORT_FFUNC(InterlockedExchange, 2), + IMPORT_FFUNC(ExInterlockedAddLargeStatistic, 2), + IMPORT_SFUNC(IoAllocateMdl, 5), + IMPORT_SFUNC(IoFreeMdl, 1), + IMPORT_SFUNC(MmAllocateContiguousMemory, 2 + 1), + IMPORT_SFUNC(MmAllocateContiguousMemorySpecifyCache, 5 + 3), + IMPORT_SFUNC(MmFreeContiguousMemory, 1), + IMPORT_SFUNC(MmFreeContiguousMemorySpecifyCache, 3), + IMPORT_SFUNC(MmSizeOfMdl, 1), + IMPORT_SFUNC(MmMapLockedPages, 2), + IMPORT_SFUNC(MmMapLockedPagesSpecifyCache, 6), + IMPORT_SFUNC(MmUnmapLockedPages, 2), + IMPORT_SFUNC(MmBuildMdlForNonPagedPool, 1), + IMPORT_SFUNC(MmGetPhysicalAddress, 1), + IMPORT_SFUNC(MmGetSystemRoutineAddress, 1), + IMPORT_SFUNC(MmIsAddressValid, 1), + IMPORT_SFUNC(MmMapIoSpace, 3 + 1), + IMPORT_SFUNC(MmUnmapIoSpace, 2), + IMPORT_SFUNC(KeInitializeSpinLock, 1), + IMPORT_SFUNC(IoIsWdmVersionAvailable, 2), + IMPORT_SFUNC(IoOpenDeviceRegistryKey, 4), + IMPORT_SFUNC(IoGetDeviceObjectPointer, 4), + IMPORT_SFUNC(IoGetDeviceProperty, 5), + IMPORT_SFUNC(IoAllocateWorkItem, 1), + IMPORT_SFUNC(IoFreeWorkItem, 1), + IMPORT_SFUNC(IoQueueWorkItem, 4), + IMPORT_SFUNC(ExQueueWorkItem, 2), + IMPORT_SFUNC(ntoskrnl_workitem, 2), + IMPORT_SFUNC(KeInitializeMutex, 2), + IMPORT_SFUNC(KeReleaseMutex, 2), + IMPORT_SFUNC(KeReadStateMutex, 1), + IMPORT_SFUNC(KeInitializeEvent, 3), + IMPORT_SFUNC(KeSetEvent, 3), + IMPORT_SFUNC(KeResetEvent, 1), + IMPORT_SFUNC(KeClearEvent, 1), + IMPORT_SFUNC(KeReadStateEvent, 1), + IMPORT_SFUNC(KeInitializeTimer, 1), + IMPORT_SFUNC(KeInitializeTimerEx, 2), + IMPORT_SFUNC(KeSetTimer, 3), + IMPORT_SFUNC(KeSetTimerEx, 4), + IMPORT_SFUNC(KeCancelTimer, 1), + IMPORT_SFUNC(KeReadStateTimer, 1), + IMPORT_SFUNC(KeInitializeDpc, 3), + IMPORT_SFUNC(KeInsertQueueDpc, 3), + IMPORT_SFUNC(KeRemoveQueueDpc, 1), + IMPORT_SFUNC(KeSetImportanceDpc, 2), + IMPORT_SFUNC(KeSetTargetProcessorDpc, 2), + IMPORT_SFUNC(KeFlushQueuedDpcs, 0), + IMPORT_SFUNC(KeGetCurrentProcessorNumber, 1), + IMPORT_SFUNC(ObReferenceObjectByHandle, 6), + IMPORT_FFUNC(ObfDereferenceObject, 1), + IMPORT_SFUNC(ZwClose, 1), + IMPORT_SFUNC(PsCreateSystemThread, 7), + IMPORT_SFUNC(PsTerminateSystemThread, 1), + IMPORT_SFUNC(IoWMIRegistrationControl, 2), + IMPORT_SFUNC(WmiQueryTraceInformation, 5), + IMPORT_CFUNC(WmiTraceMessage, 0), + IMPORT_SFUNC(KeQuerySystemTime, 1), + IMPORT_CFUNC(KeTickCount, 0), + IMPORT_SFUNC(KeDelayExecutionThread, 3), + IMPORT_SFUNC(KeQueryInterruptTime, 0), + IMPORT_SFUNC(KeGetCurrentThread, 0), + IMPORT_SFUNC(KeSetPriorityThread, 2), /* * This last entry is a catch-all for any function we haven't @@ -1785,9 +4238,9 @@ image_patch_table ntoskrnl_functbl[] = { * in this table. */ - { NULL, (FUNC)dummy }, + { NULL, (FUNC)dummy, NULL, 0, WINDRV_WRAP_STDCALL }, /* End of list. */ - { NULL, NULL }, + { NULL, NULL, NULL } }; diff --git a/sys/emulation/ndis/subr_pe.c b/sys/emulation/ndis/subr_pe.c index ba78ddf171..90ee0fa027 100644 --- a/sys/emulation/ndis/subr_pe.c +++ b/sys/emulation/ndis/subr_pe.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2003 * Bill Paul . All rights reserved. * @@ -29,8 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $DragonFly: src/sys/emulation/ndis/subr_pe.c,v 1.3 2006/12/23 00:27:02 swildner Exp $ - * $FreeBSD: src/sys/compat/ndis/subr_pe.c,v 1.7 2004/01/13 22:49:45 obrien Exp $ + * $FreeBSD: src/sys/compat/ndis/subr_pe.c,v 1.15 2009/11/02 11:07:42 rpaulo Exp $ */ /* @@ -55,14 +54,14 @@ #include #else #include +#include #include #include #include #define kprintf printf /* ndiscvt(8) uses this file */ #endif -#include "regcall.h" -#include "pe_var.h" +#include static vm_offset_t pe_functbl_match(image_patch_table *, char *); @@ -88,7 +87,7 @@ pe_get_dos_header(vm_offset_t imgbase, image_dos_header *hdr) bcopy ((char *)imgbase, (char *)hdr, sizeof(image_dos_header)); - return(0); + return (0); } /* @@ -109,10 +108,10 @@ pe_is_nt_image(vm_offset_t imgbase) dos_hdr = (image_dos_header *)imgbase; signature = *(uint32_t *)(imgbase + dos_hdr->idh_lfanew); if (signature == IMAGE_NT_SIGNATURE) - return(0); + return (0); } - return(ENOEXEC); + return (ENOEXEC); } /* @@ -128,7 +127,7 @@ pe_get_optional_header(vm_offset_t imgbase, image_optional_header *hdr) image_nt_header *nt_hdr; if (imgbase == 0 || hdr == NULL) - return(EINVAL); + return (EINVAL); if (pe_is_nt_image(imgbase)) return (EINVAL); @@ -137,9 +136,9 @@ pe_get_optional_header(vm_offset_t imgbase, image_optional_header *hdr) nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew); bcopy ((char *)&nt_hdr->inh_optionalhdr, (char *)hdr, - sizeof(image_optional_header)); + nt_hdr->inh_filehdr.ifh_optionalhdrlen); - return(0); + return (0); } /* @@ -154,7 +153,7 @@ pe_get_file_header(vm_offset_t imgbase, image_file_header *hdr) image_nt_header *nt_hdr; if (imgbase == 0 || hdr == NULL) - return(EINVAL); + return (EINVAL); if (pe_is_nt_image(imgbase)) return (EINVAL); @@ -162,10 +161,18 @@ pe_get_file_header(vm_offset_t imgbase, image_file_header *hdr) dos_hdr = (image_dos_header *)imgbase; nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew); + /* + * Note: the size of the nt_header is variable since it + * can contain optional fields, as indicated by ifh_optionalhdrlen. + * However it happens we're only interested in fields in the + * non-variant portion of the nt_header structure, so we don't + * bother copying the optional parts here. + */ + bcopy ((char *)&nt_hdr->inh_filehdr, (char *)hdr, sizeof(image_file_header)); - return(0); + return (0); } /* @@ -181,19 +188,18 @@ pe_get_section_header(vm_offset_t imgbase, image_section_header *hdr) image_section_header *sect_hdr; if (imgbase == 0 || hdr == NULL) - return(EINVAL); + return (EINVAL); if (pe_is_nt_image(imgbase)) return (EINVAL); dos_hdr = (image_dos_header *)imgbase; nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew); - sect_hdr = (image_section_header *)((vm_offset_t)nt_hdr + - sizeof(image_nt_header)); + sect_hdr = IMAGE_FIRST_SECTION(nt_hdr); bcopy ((char *)sect_hdr, (char *)hdr, sizeof(image_section_header)); - return(0); + return (0); } /* @@ -206,7 +212,7 @@ pe_numsections(vm_offset_t imgbase) image_file_header file_hdr; if (pe_get_file_header(imgbase, &file_hdr)) - return(0); + return (0); return (file_hdr.ifh_numsections); } @@ -222,7 +228,7 @@ pe_imagebase(vm_offset_t imgbase) image_optional_header optional_hdr; if (pe_get_optional_header(imgbase, &optional_hdr)) - return(0); + return (0); return (optional_hdr.ioh_imagebase); } @@ -239,18 +245,18 @@ pe_directory_offset(vm_offset_t imgbase, uint32_t diridx) vm_offset_t dir; if (pe_get_optional_header(imgbase, &opt_hdr)) - return(0); + return (0); if (diridx >= opt_hdr.ioh_rva_size_cnt) - return(0); + return (0); dir = opt_hdr.ioh_datadir[diridx].idd_vaddr; - return(pe_translate_addr(imgbase, dir)); + return (pe_translate_addr(imgbase, dir)); } vm_offset_t -pe_translate_addr(vm_offset_t imgbase, uint32_t rva) +pe_translate_addr(vm_offset_t imgbase, vm_offset_t rva) { image_optional_header opt_hdr; image_section_header *sect_hdr; @@ -259,14 +265,13 @@ pe_translate_addr(vm_offset_t imgbase, uint32_t rva) int i = 0, sections, fixedlen; if (pe_get_optional_header(imgbase, &opt_hdr)) - return(0); + return (0); sections = pe_numsections(imgbase); dos_hdr = (image_dos_header *)imgbase; nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew); - sect_hdr = (image_section_header *)((vm_offset_t)nt_hdr + - sizeof(image_nt_header)); + sect_hdr = IMAGE_FIRST_SECTION(nt_hdr); /* * The test here is to see if the RVA falls somewhere @@ -282,17 +287,17 @@ pe_translate_addr(vm_offset_t imgbase, uint32_t rva) fixedlen += ((opt_hdr.ioh_sectalign - 1) - sect_hdr->ish_misc.ish_vsize) & (opt_hdr.ioh_sectalign - 1); - if (sect_hdr->ish_vaddr <= (u_int32_t)rva && + if (sect_hdr->ish_vaddr <= (uint32_t)rva && (sect_hdr->ish_vaddr + fixedlen) > - (u_int32_t)rva) + (uint32_t)rva) break; sect_hdr++; } if (i > sections) - return(0); + return (0); - return((vm_offset_t)(imgbase + rva - sect_hdr->ish_vaddr + + return ((vm_offset_t)(imgbase + rva - sect_hdr->ish_vaddr + sect_hdr->ish_rawdataaddr)); } @@ -304,7 +309,7 @@ pe_translate_addr(vm_offset_t imgbase, uint32_t rva) int pe_get_section(vm_offset_t imgbase, image_section_header *hdr, - const char *name) + const char *name) { image_dos_header *dos_hdr; image_nt_header *nt_hdr; @@ -313,7 +318,7 @@ pe_get_section(vm_offset_t imgbase, image_section_header *hdr, int i, sections; if (imgbase == 0 || hdr == NULL) - return(EINVAL); + return (EINVAL); if (pe_is_nt_image(imgbase)) return (EINVAL); @@ -322,14 +327,13 @@ pe_get_section(vm_offset_t imgbase, image_section_header *hdr, dos_hdr = (image_dos_header *)imgbase; nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew); - sect_hdr = (image_section_header *)((vm_offset_t)nt_hdr + - sizeof(image_nt_header)); + sect_hdr = IMAGE_FIRST_SECTION(nt_hdr); for (i = 0; i < sections; i++) { if (!strcmp ((char *)§_hdr->ish_name, name)) { bcopy((char *)sect_hdr, (char *)hdr, sizeof(image_section_header)); - return(0); + return (0); } else sect_hdr++; } @@ -350,7 +354,10 @@ pe_relocate(vm_offset_t imgbase) image_section_header sect; image_base_reloc *relhdr; uint16_t rel, *sloc; - uint32_t base, delta, *lloc; + vm_offset_t base; + vm_size_t delta; + uint32_t *lloc; + uint64_t *qloc; int i, count; vm_offset_t txt; @@ -387,8 +394,15 @@ pe_relocate(vm_offset_t imgbase) relhdr->ibr_vaddr + IMR_RELOFFSET(rel)); *sloc += (delta & 0xFFFF); break; + case IMAGE_REL_BASED_DIR64: + qloc = (uint64_t *)pe_translate_addr(imgbase, + relhdr->ibr_vaddr + IMR_RELOFFSET(rel)); + *qloc = pe_translate_addr(imgbase, + (*qloc - base)); + break; + default: - kprintf ("[%d]reloc type: %d\n",i, + kprintf("[%d]reloc type: %d\n", i, IMR_RELTYPE(rel)); break; } @@ -397,7 +411,7 @@ pe_relocate(vm_offset_t imgbase) relhdr->ibr_blocksize); } while (relhdr->ibr_blocksize); - return(0); + return (0); } /* @@ -405,18 +419,20 @@ pe_relocate(vm_offset_t imgbase) * may be linked against several modules, typically HAL.dll, ntoskrnl.exe * and NDIS.SYS. For each module, there is a list of imported function * names and their addresses. + * + * Note: module names are case insensitive! */ int pe_get_import_descriptor(vm_offset_t imgbase, image_import_descriptor *desc, - char *module) + char *module) { vm_offset_t offset; image_import_descriptor *imp_desc; char *modname; if (imgbase == 0 || module == NULL || desc == NULL) - return(EINVAL); + return (EINVAL); offset = pe_directory_offset(imgbase, IMAGE_DIRECTORY_ENTRY_IMPORT); if (offset == 0) @@ -427,10 +443,10 @@ pe_get_import_descriptor(vm_offset_t imgbase, image_import_descriptor *desc, while (imp_desc->iid_nameaddr) { modname = (char *)pe_translate_addr(imgbase, imp_desc->iid_nameaddr); - if (!strncmp(module, modname, strlen(module))) { + if (!strncasecmp(module, modname, strlen(module))) { bcopy((char *)imp_desc, (char *)desc, sizeof(image_import_descriptor)); - return(0); + return (0); } imp_desc++; } @@ -448,7 +464,7 @@ pe_get_messagetable(vm_offset_t imgbase, message_resource_data **md) int i; if (imgbase == 0) - return(EINVAL); + return (EINVAL); offset = pe_directory_offset(imgbase, IMAGE_DIRECTORY_ENTRY_RESOURCE); if (offset == 0) @@ -476,15 +492,15 @@ pe_get_messagetable(vm_offset_t imgbase, message_resource_data **md) dent2->irde_dataoff); *md = (message_resource_data *)pe_translate_addr(imgbase, rent->irde_offset); - return(0); + return (0); } - return(ENOENT); + return (ENOENT); } int pe_get_message(vm_offset_t imgbase, uint32_t id, char **str, int *len, - uint16_t *flags) + uint16_t *flags) { message_resource_data *md = NULL; message_resource_block *mb; @@ -494,7 +510,7 @@ pe_get_message(vm_offset_t imgbase, uint32_t id, char **str, int *len, pe_get_messagetable(imgbase, &md); if (md == NULL) - return(ENOENT); + return (ENOENT); mb = (message_resource_block *)((uintptr_t)md + sizeof(message_resource_data)); @@ -509,12 +525,12 @@ pe_get_message(vm_offset_t imgbase, uint32_t id, char **str, int *len, *str = me->mre_text; *len = me->mre_len; *flags = me->mre_flags; - return(0); + return (0); } mb++; } - return(ENOENT); + return (ENOENT); } /* @@ -529,17 +545,25 @@ pe_functbl_match(image_patch_table *functbl, char *name) image_patch_table *p; if (functbl == NULL || name == NULL) - return(0); + return (0); p = functbl; while (p->ipt_name != NULL) { if (!strcmp(p->ipt_name, name)) - return((vm_offset_t)p->ipt_func); + return ((vm_offset_t)p->ipt_wrap); p++; } - kprintf ("no match for %s\n", name); - return((vm_offset_t)p->ipt_func); + kprintf("no match for %s\n", name); + + /* + * Return the wrapper pointer for this routine. + * For x86, this is the same as the funcptr. + * For amd64, this points to a wrapper routine + * that does calling convention translation and + * then invokes the underlying routine. + */ + return ((vm_offset_t)p->ipt_wrap); } /* @@ -552,8 +576,7 @@ pe_functbl_match(image_patch_table *functbl, char *name) */ int -pe_patch_imports(vm_offset_t imgbase, char *module, - image_patch_table *functbl) +pe_patch_imports(vm_offset_t imgbase, char *module, image_patch_table *functbl) { image_import_descriptor imp_desc; char *fname; @@ -561,10 +584,10 @@ pe_patch_imports(vm_offset_t imgbase, char *module, vm_offset_t func; if (imgbase == 0 || module == NULL || functbl == NULL) - return(EINVAL); + return (EINVAL); if (pe_get_import_descriptor(imgbase, &imp_desc, module)) - return(ENOEXEC); + return (ENOEXEC); nptr = (vm_offset_t *)pe_translate_addr(imgbase, imp_desc.iid_import_name_table_addr); @@ -578,11 +601,11 @@ pe_patch_imports(vm_offset_t imgbase, char *module, *fptr = func; #ifdef notdef if (*fptr == 0) - return(ENOENT); + return (ENOENT); #endif nptr++; fptr++; } - return(0); + return (0); } diff --git a/sys/emulation/ndis/subr_usbd.c b/sys/emulation/ndis/subr_usbd.c new file mode 100644 index 0000000000..45c309572e --- /dev/null +++ b/sys/emulation/ndis/subr_usbd.c @@ -0,0 +1,1207 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/compat/ndis/subr_usbd.c,v 1.6 2009/02/24 18:09:31 rdivacky Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static driver_object usbd_driver; + +static int32_t usbd_func_bulkintr(irp *); +static int32_t usbd_func_vendorclass(irp *); +static int32_t usbd_func_selconf(irp *); +static int32_t usbd_func_abort_pipe(irp *); +static int32_t usbd_func_getdesc(irp *); +static usbd_status usbd_get_desc_ndis(usbd_device_handle, int, int, int, + void *, int *); +static union usbd_urb *usbd_geturb(irp *); +static usbd_status usbd_init_ndispipe(irp *, usb_endpoint_descriptor_t *); +static usbd_xfer_handle usbd_init_ndisxfer(irp *, usb_endpoint_descriptor_t *, + void *, uint32_t); +static int32_t usbd_iodispatch(device_object *, irp *); +static int32_t usbd_ioinvalid(device_object *, irp *); +static int32_t usbd_pnp(device_object *, irp *); +static int32_t usbd_power(device_object *, irp *); +static void usbd_irpcancel(device_object *, irp *); +static void usbd_irpcancel_cb(void *); +static int32_t usbd_submit_urb(irp *); +static int32_t usbd_urb2nt(int32_t); +static void usbd_xfereof(usbd_xfer_handle, usbd_private_handle, + usbd_status); +static void usbd_xferadd(usbd_xfer_handle, usbd_private_handle, + usbd_status); +static void usbd_xfertask(device_object *, void *); +static void dummy(void); + +static union usbd_urb *USBD_CreateConfigurationRequestEx( + usb_config_descriptor_t *, + struct usbd_interface_list_entry *); +static union usbd_urb *USBD_CreateConfigurationRequest( + usb_config_descriptor_t *, + uint16_t *); +static void USBD_GetUSBDIVersion(usbd_version_info *); +static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptorEx( + usb_config_descriptor_t *, void *, int32_t, int32_t, + int32_t, int32_t, int32_t); +static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptor( + usb_config_descriptor_t *, uint8_t, uint8_t); + +/* + * We need to wrap these functions because these need `context switch' from + * Windows to UNIX before it's called. + */ +static funcptr usbd_iodispatch_wrap; +static funcptr usbd_ioinvalid_wrap; +static funcptr usbd_pnp_wrap; +static funcptr usbd_power_wrap; +static funcptr usbd_irpcancel_wrap; +static funcptr usbd_xfertask_wrap; + +int +usbd_libinit(void) +{ + image_patch_table *patch; + int i; + + patch = usbd_functbl; + while (patch->ipt_func != NULL) { + windrv_wrap((funcptr)patch->ipt_func, + (funcptr *)&patch->ipt_wrap, + patch->ipt_argcnt, patch->ipt_ftype); + patch++; + } + + windrv_wrap((funcptr)usbd_ioinvalid, + (funcptr *)&usbd_ioinvalid_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_iodispatch, + (funcptr *)&usbd_iodispatch_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_pnp, + (funcptr *)&usbd_pnp_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_power, + (funcptr *)&usbd_power_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_irpcancel, + (funcptr *)&usbd_irpcancel_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_xfertask, + (funcptr *)&usbd_xfertask_wrap, 2, WINDRV_WRAP_STDCALL); + + /* Create a fake USB driver instance. */ + + windrv_bus_attach(&usbd_driver, "USB Bus"); + + /* Set up our dipatch routine. */ + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) + usbd_driver.dro_dispatch[i] = + (driver_dispatch)usbd_ioinvalid_wrap; + + usbd_driver.dro_dispatch[IRP_MJ_INTERNAL_DEVICE_CONTROL] = + (driver_dispatch)usbd_iodispatch_wrap; + usbd_driver.dro_dispatch[IRP_MJ_DEVICE_CONTROL] = + (driver_dispatch)usbd_iodispatch_wrap; + usbd_driver.dro_dispatch[IRP_MJ_POWER] = + (driver_dispatch)usbd_power_wrap; + usbd_driver.dro_dispatch[IRP_MJ_PNP] = + (driver_dispatch)usbd_pnp_wrap; + + return(0); +} + +int +usbd_libfini(void) +{ + image_patch_table *patch; + + patch = usbd_functbl; + while (patch->ipt_func != NULL) { + windrv_unwrap(patch->ipt_wrap); + patch++; + } + + windrv_unwrap(usbd_ioinvalid_wrap); + windrv_unwrap(usbd_iodispatch_wrap); + windrv_unwrap(usbd_pnp_wrap); + windrv_unwrap(usbd_power_wrap); + windrv_unwrap(usbd_irpcancel_wrap); + windrv_unwrap(usbd_xfertask_wrap); + + kfree(usbd_driver.dro_drivername.us_buf, M_DEVBUF); + + return (0); +} + +static int32_t +usbd_iodispatch(device_object *dobj, irp *ip) +{ + device_t dev = dobj->do_devext; + int32_t status; + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + switch (irp_sl->isl_parameters.isl_ioctl.isl_iocode) { + case IOCTL_INTERNAL_USB_SUBMIT_URB: + IRP_NDIS_DEV(ip) = dev; + + status = usbd_submit_urb(ip); + break; + default: + device_printf(dev, "ioctl 0x%x isn't supported\n", + irp_sl->isl_parameters.isl_ioctl.isl_iocode); + status = USBD_STATUS_NOT_SUPPORTED; + break; + } + + if (status == USBD_STATUS_PENDING) + return (STATUS_PENDING); + + ip->irp_iostat.isb_status = usbd_urb2nt(status); + if (status != USBD_STATUS_SUCCESS) + ip->irp_iostat.isb_info = 0; + return (ip->irp_iostat.isb_status); +} + +static int32_t +usbd_ioinvalid(device_object *dobj, irp *ip) +{ + device_t dev = dobj->do_devext; + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + device_printf(dev, "invalid I/O dispatch %d:%d\n", irp_sl->isl_major, + irp_sl->isl_minor); + + ip->irp_iostat.isb_status = STATUS_FAILURE; + ip->irp_iostat.isb_info = 0; + + IoCompleteRequest(ip, IO_NO_INCREMENT); + + return (STATUS_FAILURE); +} + +static int32_t +usbd_pnp(device_object *dobj, irp *ip) +{ + device_t dev = dobj->do_devext; + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n", + __func__, irp_sl->isl_major, irp_sl->isl_minor); + + ip->irp_iostat.isb_status = STATUS_FAILURE; + ip->irp_iostat.isb_info = 0; + + IoCompleteRequest(ip, IO_NO_INCREMENT); + + return (STATUS_FAILURE); +} + +static int32_t +usbd_power(device_object *dobj, irp *ip) +{ + device_t dev = dobj->do_devext; + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n", + __func__, irp_sl->isl_major, irp_sl->isl_minor); + + ip->irp_iostat.isb_status = STATUS_FAILURE; + ip->irp_iostat.isb_info = 0; + + IoCompleteRequest(ip, IO_NO_INCREMENT); + + return (STATUS_FAILURE); +} + +/* Convert USBD_STATUS to NTSTATUS */ +static int32_t +usbd_urb2nt(int32_t status) +{ + + switch (status) { + case USBD_STATUS_SUCCESS: + return (STATUS_SUCCESS); + case USBD_STATUS_DEVICE_GONE: + return (STATUS_DEVICE_NOT_CONNECTED); + case USBD_STATUS_PENDING: + return (STATUS_PENDING); + case USBD_STATUS_NOT_SUPPORTED: + return (STATUS_NOT_IMPLEMENTED); + case USBD_STATUS_NO_MEMORY: + return (STATUS_NO_MEMORY); + case USBD_STATUS_REQUEST_FAILED: + return (STATUS_NOT_SUPPORTED); + case USBD_STATUS_CANCELED: + return (STATUS_CANCELLED); + default: + break; + } + + return (STATUS_FAILURE); +} + +/* Convert FreeBSD's usbd_status to USBD_STATUS */ +static int32_t +usbd_usb2urb(int status) +{ + + switch (status) { + case USBD_NORMAL_COMPLETION: + return (USBD_STATUS_SUCCESS); + case USBD_IN_PROGRESS: + return (USBD_STATUS_PENDING); + case USBD_TIMEOUT: + return (USBD_STATUS_TIMEOUT); + case USBD_SHORT_XFER: + return (USBD_STATUS_ERROR_SHORT_TRANSFER); + case USBD_IOERROR: + return (USBD_STATUS_XACT_ERROR); + case USBD_NOMEM: + return (USBD_STATUS_NO_MEMORY); + case USBD_INVAL: + return (USBD_STATUS_REQUEST_FAILED); + case USBD_NOT_STARTED: + case USBD_TOO_DEEP: + case USBD_NO_POWER: + return (USBD_STATUS_DEVICE_GONE); + case USBD_CANCELLED: + return (USBD_STATUS_CANCELED); + default: + break; + } + + return (USBD_STATUS_NOT_SUPPORTED); +} + +static union usbd_urb * +usbd_geturb(irp *ip) +{ + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + + return (irp_sl->isl_parameters.isl_others.isl_arg1); +} + +static int32_t +usbd_submit_urb(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + int32_t status; + union usbd_urb *urb; + + urb = usbd_geturb(ip); + /* + * In a case of URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER, + * USBD_URB_STATUS(urb) would be set at callback functions like + * usbd_intr() or usbd_xfereof(). + */ + switch (urb->uu_hdr.uuh_func) { + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + status = usbd_func_bulkintr(ip); + if (status != USBD_STATUS_SUCCESS && + status != USBD_STATUS_PENDING) + USBD_URB_STATUS(urb) = status; + break; + case URB_FUNCTION_VENDOR_DEVICE: + case URB_FUNCTION_VENDOR_INTERFACE: + case URB_FUNCTION_VENDOR_ENDPOINT: + case URB_FUNCTION_VENDOR_OTHER: + case URB_FUNCTION_CLASS_DEVICE: + case URB_FUNCTION_CLASS_INTERFACE: + case URB_FUNCTION_CLASS_ENDPOINT: + case URB_FUNCTION_CLASS_OTHER: + status = usbd_func_vendorclass(ip); + USBD_URB_STATUS(urb) = status; + break; + case URB_FUNCTION_SELECT_CONFIGURATION: + status = usbd_func_selconf(ip); + USBD_URB_STATUS(urb) = status; + break; + case URB_FUNCTION_ABORT_PIPE: + status = usbd_func_abort_pipe(ip); + USBD_URB_STATUS(urb) = status; + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + status = usbd_func_getdesc(ip); + USBD_URB_STATUS(urb) = status; + break; + default: + device_printf(dev, "func 0x%x isn't supported\n", + urb->uu_hdr.uuh_func); + USBD_URB_STATUS(urb) = status = USBD_STATUS_NOT_SUPPORTED; + break; + } + + return (status); +} + +static int32_t +usbd_func_getdesc(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + int actlen, i; + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct usbd_urb_control_descriptor_request *ctldesc; + uint32_t len; + union usbd_urb *urb; + usb_config_descriptor_t cd, *cdp; + usbd_status status; + + get_mplock(); + + urb = usbd_geturb(ip); + ctldesc = &urb->uu_ctldesc; + if (ctldesc->ucd_desctype == UDESC_CONFIG) { + /* Get the short config descriptor. */ + status = usbd_get_config_desc(uaa->device, ctldesc->ucd_idx, + &cd); + if (status != USBD_NORMAL_COMPLETION) { + ctldesc->ucd_trans_buflen = 0; + rel_mplock(); + return usbd_usb2urb(status); + } + /* Get the full descriptor. Try a few times for slow devices. */ + len = MIN(ctldesc->ucd_trans_buflen, UGETW(cd.wTotalLength)); + for (i = 0; i < 3; i++) { + status = usbd_get_desc_ndis(uaa->device, + ctldesc->ucd_desctype, ctldesc->ucd_idx, + len, ctldesc->ucd_trans_buf, &actlen); + if (status == USBD_NORMAL_COMPLETION) + break; + usbd_delay_ms(uaa->device, 200); + } + if (status != USBD_NORMAL_COMPLETION) { + ctldesc->ucd_trans_buflen = 0; + rel_mplock(); + return usbd_usb2urb(status); + } + + cdp = (usb_config_descriptor_t *)ctldesc->ucd_trans_buf; + if (cdp->bDescriptorType != UDESC_CONFIG) { + device_printf(dev, "bad desc %d\n", + cdp->bDescriptorType); + status = USBD_INVAL; + } + } else if (ctldesc->ucd_desctype == UDESC_STRING) { + /* Try a few times for slow devices. */ + for (i = 0; i < 3; i++) { + status = usbd_get_string_desc(uaa->device, + (UDESC_STRING << 8) + ctldesc->ucd_idx, + ctldesc->ucd_langid, ctldesc->ucd_trans_buf, + &actlen); + if (actlen > ctldesc->ucd_trans_buflen) + panic("small string buffer for UDESC_STRING"); + if (status == USBD_NORMAL_COMPLETION) + break; + usbd_delay_ms(uaa->device, 200); + } + } else + status = usbd_get_desc_ndis(uaa->device, ctldesc->ucd_desctype, + ctldesc->ucd_idx, ctldesc->ucd_trans_buflen, + ctldesc->ucd_trans_buf, &actlen); + + if (status != USBD_NORMAL_COMPLETION) { + ctldesc->ucd_trans_buflen = 0; + rel_mplock(); + return usbd_usb2urb(status); + } + + ctldesc->ucd_trans_buflen = actlen; + ip->irp_iostat.isb_info = actlen; + + rel_mplock(); + + return (USBD_STATUS_SUCCESS); +} + +/* + * FIXME: at USB1, not USB2, framework, there's no a interface to get `actlen'. + * However, we need it!!! + */ +static usbd_status +usbd_get_desc_ndis(usbd_device_handle dev, int type, int index, int len, + void *desc, int *actlen) +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_DEVICE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, type, index); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + return usbd_do_request_flags_pipe(dev, dev->default_pipe, &req, desc, + 0, actlen, USBD_DEFAULT_TIMEOUT); +} + +static int32_t +usbd_func_selconf(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + int i, j; + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct usbd_interface_information *intf; + struct usbd_pipe_information *pipe; + struct usbd_urb_select_configuration *selconf; + union usbd_urb *urb; + usb_config_descriptor_t *conf; + usb_endpoint_descriptor_t *edesc; + usbd_device_handle udev = uaa->device; + usbd_interface_handle iface; + usbd_status ret; + + urb = usbd_geturb(ip); + + selconf = &urb->uu_selconf; + conf = selconf->usc_conf; + if (conf == NULL) { + device_printf(dev, "select configuration is NULL\n"); + return usbd_usb2urb(USBD_NORMAL_COMPLETION); + } + + if (conf->bConfigurationValue > NDISUSB_CONFIG_NO) + device_printf(dev, "warning: config_no is larger than default"); + + intf = &selconf->usc_intf; + for (i = 0; i < conf->bNumInterface && intf->uii_len > 0; i++) { + ret = usbd_device2interface_handle(uaa->device, + intf->uii_intfnum, &iface); + if (ret != USBD_NORMAL_COMPLETION) { + device_printf(dev, + "getting interface handle failed: %s\n", + usbd_errstr(ret)); + return usbd_usb2urb(ret); + } + + ret = usbd_set_interface(iface, intf->uii_altset); + if (ret != USBD_NORMAL_COMPLETION && ret != USBD_IN_USE) { + device_printf(dev, + "setting alternate interface failed: %s\n", + usbd_errstr(ret)); + return usbd_usb2urb(ret); + } + + for (j = 0; j < iface->idesc->bNumEndpoints; j++) { + if (j >= intf->uii_numeps) { + device_printf(dev, + "endpoint %d and above are ignored", + intf->uii_numeps); + break; + } + edesc = iface->endpoints[j].edesc; + pipe = &intf->uii_pipes[j]; + pipe->upi_handle = edesc; + pipe->upi_epaddr = edesc->bEndpointAddress; + pipe->upi_maxpktsize = UGETW(edesc->wMaxPacketSize); + pipe->upi_type = UE_GET_XFERTYPE(edesc->bmAttributes); + if (pipe->upi_type != UE_INTERRUPT) + continue; + + /* XXX we're following linux USB's interval policy. */ + if (udev->speed == USB_SPEED_LOW) + pipe->upi_interval = edesc->bInterval + 5; + else if (udev->speed == USB_SPEED_FULL) + pipe->upi_interval = edesc->bInterval; + else { + int k0 = 0, k1 = 1; + do { + k1 = k1 * 2; + k0 = k0 + 1; + } while (k1 < edesc->bInterval); + pipe->upi_interval = k0; + } + } + + intf = (struct usbd_interface_information *)(((char *)intf) + + intf->uii_len); + } + + return (USBD_STATUS_SUCCESS); +} + +static int32_t +usbd_func_abort_pipe(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + union usbd_urb *urb; + struct usbd_urb_bulk_or_intr_transfer *ubi; + usb_endpoint_descriptor_t *ep; + uint8_t irql; + usbd_status status; + + urb = usbd_geturb(ip); + ubi = &urb->uu_bulkintr; + ep = ubi->ubi_epdesc; + if (ep == NULL) + return (USBD_STATUS_INVALID_PIPE_HANDLE); + + KeRaiseIrql(DISPATCH_LEVEL, &irql); + status = usbd_abort_pipe(sc->ndisusb_ep[NDISUSB_ENDPT_BIN]); + if (status != USBD_NORMAL_COMPLETION) + device_printf(dev, "can't be canceld"); + KeLowerIrql(irql); + + return (USBD_STATUS_SUCCESS); +} + +static int32_t +usbd_func_vendorclass(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct usbd_urb_vendor_or_class_request *vcreq; + uint8_t type = 0; + union usbd_urb *urb; + usb_device_request_t req; + usbd_status status; + + urb = usbd_geturb(ip); + vcreq = &urb->uu_vcreq; + + switch (urb->uu_hdr.uuh_func) { + case URB_FUNCTION_CLASS_DEVICE: + type = UT_CLASS | UT_DEVICE; + break; + case URB_FUNCTION_CLASS_INTERFACE: + type = UT_CLASS | UT_INTERFACE; + break; + case URB_FUNCTION_CLASS_OTHER: + type = UT_CLASS | UT_OTHER; + break; + case URB_FUNCTION_CLASS_ENDPOINT: + type = UT_CLASS | UT_ENDPOINT; + break; + case URB_FUNCTION_VENDOR_DEVICE: + type = UT_VENDOR | UT_DEVICE; + break; + case URB_FUNCTION_VENDOR_INTERFACE: + type = UT_VENDOR | UT_INTERFACE; + break; + case URB_FUNCTION_VENDOR_OTHER: + type = UT_VENDOR | UT_OTHER; + break; + case URB_FUNCTION_VENDOR_ENDPOINT: + type = UT_VENDOR | UT_ENDPOINT; + break; + default: + /* never reach. */ + break; + } + + type |= (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ? + UT_READ : UT_WRITE; + type |= vcreq->uvc_reserved1; + + req.bmRequestType = type; + req.bRequest = vcreq->uvc_req; + USETW(req.wIndex, vcreq->uvc_idx); + USETW(req.wValue, vcreq->uvc_value); + USETW(req.wLength, vcreq->uvc_trans_buflen); + + if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) { + get_mplock(); + status = usbd_do_request(uaa->device, &req, + vcreq->uvc_trans_buf); + rel_mplock(); + } else + status = usbd_do_request_async(uaa->device, &req, + vcreq->uvc_trans_buf); + + return usbd_usb2urb(status); +} + +static usbd_status +usbd_init_ndispipe(irp *ip, usb_endpoint_descriptor_t *ep) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct usb_attach_arg *uaa = device_get_ivars(dev); + usbd_interface_handle iface; + usbd_status status; + + status = usbd_device2interface_handle(uaa->device, NDISUSB_IFACE_INDEX, + &iface); + if (status != USBD_NORMAL_COMPLETION) { + device_printf(dev, "could not get interface handle\n"); + return (status); + } + + switch (UE_GET_XFERTYPE(ep->bmAttributes)) { + case UE_BULK: + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) { + /* RX (bulk IN) */ + if (sc->ndisusb_ep[NDISUSB_ENDPT_BIN] != NULL) + return (USBD_NORMAL_COMPLETION); + + status = usbd_open_pipe(iface, ep->bEndpointAddress, + USBD_EXCLUSIVE_USE, + &sc->ndisusb_ep[NDISUSB_ENDPT_BIN]); + break; + } + + /* TX (bulk OUT) */ + if (sc->ndisusb_ep[NDISUSB_ENDPT_BOUT] != NULL) + return (USBD_NORMAL_COMPLETION); + + status = usbd_open_pipe(iface, ep->bEndpointAddress, + USBD_EXCLUSIVE_USE, &sc->ndisusb_ep[NDISUSB_ENDPT_BOUT]); + break; + case UE_INTERRUPT: + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) { + /* Interrupt IN. */ + if (sc->ndisusb_ep[NDISUSB_ENDPT_IIN] != NULL) + return (USBD_NORMAL_COMPLETION); + + status = usbd_open_pipe(iface, ep->bEndpointAddress, + USBD_EXCLUSIVE_USE, + &sc->ndisusb_ep[NDISUSB_ENDPT_IIN]); + break; + } + + /* Interrupt OUT. */ + if (sc->ndisusb_ep[NDISUSB_ENDPT_IOUT] != NULL) + return (USBD_NORMAL_COMPLETION); + + status = usbd_open_pipe(iface, ep->bEndpointAddress, + USBD_EXCLUSIVE_USE, &sc->ndisusb_ep[NDISUSB_ENDPT_IOUT]); + break; + default: + device_printf(dev, "can't handle xfertype 0x%x\n", + UE_GET_XFERTYPE(ep->bmAttributes)); + return (USBD_INVAL); + } + + if (status != USBD_NORMAL_COMPLETION) + device_printf(dev, "open pipe failed: (0x%x) %s\n", + ep->bEndpointAddress, usbd_errstr(status)); + + return (status); +} + +static void +usbd_irpcancel_cb(void *priv) +{ + struct ndisusb_cancel *nc = priv; + struct ndis_softc *sc = device_get_softc(nc->dev); + usbd_status status; + usbd_xfer_handle xfer = nc->xfer; + + if (sc->ndisusb_status & NDISUSB_STATUS_DETACH) + goto exit; + + status = usbd_abort_pipe(xfer->pipe); + if (status != USBD_NORMAL_COMPLETION) + device_printf(nc->dev, "can't be canceld"); +exit: + kfree(nc, M_USBDEV); +} + +static void +usbd_irpcancel(device_object *dobj, irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndisusb_cancel *nc; + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (IRP_NDISUSB_XFER(ip) == NULL) { + ip->irp_cancel = TRUE; + IoReleaseCancelSpinLock(ip->irp_cancelirql); + return; + } + + /* + * XXX Since we're under DISPATCH_LEVEL during calling usbd_irpcancel(), + * we can't sleep at all. However, currently FreeBSD's USB stack + * requires a sleep to abort a transfer. It's inevitable! so it causes + * serveral fatal problems (e.g. kernel hangups or crashes). I think + * that there are no ways to make this reliable. In this implementation, + * I used usb_add_task() but it's not a perfect method to solve this + * because of as follows: NDIS drivers would expect that IRP's + * completely canceld when usbd_irpcancel() is returned but we need + * a sleep to do it. During canceling XFERs, usbd_intr() would be + * called with a status, USBD_CANCELLED. + */ + nc = kmalloc(sizeof(struct ndisusb_cancel), M_USBDEV, M_NOWAIT | M_ZERO); + if (nc == NULL) { + ip->irp_cancel = FALSE; + IoReleaseCancelSpinLock(ip->irp_cancelirql); + return; + } + + nc->dev = dev; + nc->xfer = IRP_NDISUSB_XFER(ip); + usb_init_task(&nc->task, usbd_irpcancel_cb, nc); + + IRP_NDISUSB_XFER(ip) = NULL; + usb_add_task(uaa->device, &nc->task, USB_TASKQ_DRIVER); + + ip->irp_cancel = TRUE; + IoReleaseCancelSpinLock(ip->irp_cancelirql); +} + +static usbd_xfer_handle +usbd_init_ndisxfer(irp *ip, usb_endpoint_descriptor_t *ep, void *buf, + uint32_t buflen) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct usb_attach_arg *uaa = device_get_ivars(dev); + usbd_xfer_handle xfer; + + xfer = usbd_alloc_xfer(uaa->device); + if (xfer == NULL) + return (NULL); + + if (buf != NULL && MmIsAddressValid(buf) == FALSE && buflen > 0) { + xfer->buffer = usbd_alloc_buffer(xfer, buflen); + if (xfer->buffer == NULL) + return (NULL); + + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_OUT) + memcpy(xfer->buffer, buf, buflen); + } else + xfer->buffer = buf; + + xfer->length = buflen; + + IoAcquireCancelSpinLock(&ip->irp_cancelirql); + IRP_NDISUSB_XFER(ip) = xfer; + ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap; + IoReleaseCancelSpinLock(ip->irp_cancelirql); + + return (xfer); +} + +static void +usbd_xferadd(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status) +{ + irp *ip = priv; + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_xfer *nx; + uint8_t irql; + + nx = kmalloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO); + if (nx == NULL) { + device_printf(dev, "out of memory"); + return; + } + nx->nx_xfer = xfer; + nx->nx_priv = priv; + nx->nx_status = status; + + KeAcquireSpinLock(&sc->ndisusb_xferlock, &irql); + InsertTailList((&sc->ndisusb_xferlist), (&nx->nx_xferlist)); + KeReleaseSpinLock(&sc->ndisusb_xferlock, irql); + + IoQueueWorkItem(sc->ndisusb_xferitem, + (io_workitem_func)usbd_xfertask_wrap, WORKQUEUE_CRITICAL, sc); +} + +static void +usbd_xfereof(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status) +{ + + usbd_xferadd(xfer, priv, status); +} + +static void +usbd_xfertask(device_object *dobj, void *arg) +{ + int error; + irp *ip; + device_t dev; + list_entry *l; + struct ndis_softc *sc = arg; + struct ndisusb_xfer *nx; + struct usbd_urb_bulk_or_intr_transfer *ubi; + uint8_t irql; + union usbd_urb *urb; + usbd_private_handle priv; + usbd_status status; + usbd_xfer_handle xfer; + + dev = sc->ndis_dev; + + if (IsListEmpty(&sc->ndisusb_xferlist)) + return; + + KeAcquireSpinLock(&sc->ndisusb_xferlock, &irql); + l = sc->ndisusb_xferlist.nle_flink; + while (l != &sc->ndisusb_xferlist) { + nx = CONTAINING_RECORD(l, struct ndisusb_xfer, nx_xferlist); + xfer = nx->nx_xfer; + priv = nx->nx_priv; + status = nx->nx_status; + error = 0; + ip = priv; + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED) { + error = 1; + goto next; + } + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(xfer->pipe); + /* + * NB: just for notice. We must handle error cases also + * because if we just return without notifying to the + * NDIS driver the driver never knows about that there + * was a error. This can cause a lot of problems like + * system hangs. + */ + device_printf(dev, "usb xfer warning (%s)\n", + usbd_errstr(status)); + } + + urb = usbd_geturb(ip); + + KASSERT(urb->uu_hdr.uuh_func == + URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER, + ("function(%d) isn't for bulk or interrupt", + urb->uu_hdr.uuh_func)); + + IoAcquireCancelSpinLock(&ip->irp_cancelirql); + + ip->irp_cancelfunc = NULL; + IRP_NDISUSB_XFER(ip) = NULL; + + switch (status) { + case USBD_NORMAL_COMPLETION: + ubi = &urb->uu_bulkintr; + ubi->ubi_trans_buflen = xfer->actlen; + if (ubi->ubi_trans_flags & USBD_TRANSFER_DIRECTION_IN) + memcpy(ubi->ubi_trans_buf, xfer->buffer, + xfer->actlen); + + ip->irp_iostat.isb_info = xfer->actlen; + ip->irp_iostat.isb_status = STATUS_SUCCESS; + USBD_URB_STATUS(urb) = USBD_STATUS_SUCCESS; + break; + case USBD_CANCELLED: + ip->irp_iostat.isb_info = 0; + ip->irp_iostat.isb_status = STATUS_CANCELLED; + USBD_URB_STATUS(urb) = USBD_STATUS_CANCELED; + break; + default: + ip->irp_iostat.isb_info = 0; + USBD_URB_STATUS(urb) = usbd_usb2urb(status); + ip->irp_iostat.isb_status = + usbd_urb2nt(USBD_URB_STATUS(urb)); + break; + } + + IoReleaseCancelSpinLock(ip->irp_cancelirql); +next: + l = l->nle_flink; + RemoveEntryList(&nx->nx_xferlist); + usbd_free_xfer(nx->nx_xfer); + kfree(nx, M_USBDEV); + if (error) + continue; + /* NB: call after cleaning */ + IoCompleteRequest(ip, IO_NO_INCREMENT); + } + KeReleaseSpinLock(&sc->ndisusb_xferlock, irql); +} + +static int32_t +usbd_func_bulkintr(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct usbd_urb_bulk_or_intr_transfer *ubi; + union usbd_urb *urb; + usb_endpoint_descriptor_t *ep; + usbd_status status; + usbd_xfer_handle xfer; + + urb = usbd_geturb(ip); + ubi = &urb->uu_bulkintr; + ep = ubi->ubi_epdesc; + if (ep == NULL) + return (USBD_STATUS_INVALID_PIPE_HANDLE); + + status = usbd_init_ndispipe(ip, ep); + if (status != USBD_NORMAL_COMPLETION) + return usbd_usb2urb(status); + + xfer = usbd_init_ndisxfer(ip, ep, ubi->ubi_trans_buf, + ubi->ubi_trans_buflen); + if (xfer == NULL) { + device_printf(IRP_NDIS_DEV(ip), "can't allocate xfer\n"); + return (USBD_STATUS_NO_MEMORY); + } + + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) { + xfer->flags |= USBD_SHORT_XFER_OK; + if (!(ubi->ubi_trans_flags & USBD_SHORT_TRANSFER_OK)) + xfer->flags &= ~USBD_SHORT_XFER_OK; + } + + if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_BULK) { + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) + /* RX (bulk IN) */ + usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_BIN], + ip, xfer->buffer, xfer->length, xfer->flags, + USBD_NO_TIMEOUT, usbd_xfereof); + else { + /* TX (bulk OUT) */ + xfer->flags |= USBD_NO_COPY; + + usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_BOUT], + ip, xfer->buffer, xfer->length, xfer->flags, + NDISUSB_TX_TIMEOUT, usbd_xfereof); + } + } else { + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) + /* Interrupt IN */ + usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_IIN], + ip, xfer->buffer, xfer->length, xfer->flags, + USBD_NO_TIMEOUT, usbd_xfereof); + else + /* Interrupt OUT */ + usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_IOUT], + ip, xfer->buffer, xfer->length, xfer->flags, + NDISUSB_INTR_TIMEOUT, usbd_xfereof); + } + + /* we've done to setup xfer. Let's transfer it. */ + ip->irp_iostat.isb_status = STATUS_PENDING; + ip->irp_iostat.isb_info = 0; + USBD_URB_STATUS(urb) = USBD_STATUS_PENDING; + IoMarkIrpPending(ip); + + status = usbd_transfer(xfer); + if (status == USBD_IN_PROGRESS) + return (USBD_STATUS_PENDING); + + usbd_free_xfer(xfer); + IRP_NDISUSB_XFER(ip) = NULL; + IoUnmarkIrpPending(ip); + USBD_URB_STATUS(urb) = usbd_usb2urb(status); + + return USBD_URB_STATUS(urb); +} + +static union usbd_urb * +USBD_CreateConfigurationRequest(usb_config_descriptor_t *conf, uint16_t *len) +{ + struct usbd_interface_list_entry list[2]; + union usbd_urb *urb; + + bzero(list, sizeof(struct usbd_interface_list_entry) * 2); + list[0].uil_intfdesc = USBD_ParseConfigurationDescriptorEx(conf, conf, + -1, -1, -1, -1, -1); + urb = USBD_CreateConfigurationRequestEx(conf, list); + if (urb == NULL) + return (NULL); + + *len = urb->uu_selconf.usc_hdr.uuh_len; + return (urb); +} + +static union usbd_urb * +USBD_CreateConfigurationRequestEx(usb_config_descriptor_t *conf, + struct usbd_interface_list_entry *list) +{ + int i, j, size; + struct usbd_interface_information *intf; + struct usbd_pipe_information *pipe; + struct usbd_urb_select_configuration *selconf; + usb_interface_descriptor_t *desc; + + for (i = 0, size = 0; i < conf->bNumInterface; i++) { + j = list[i].uil_intfdesc->bNumEndpoints; + size = size + sizeof(struct usbd_interface_information) + + sizeof(struct usbd_pipe_information) * (j - 1); + } + size += sizeof(struct usbd_urb_select_configuration) - + sizeof(struct usbd_interface_information); + + selconf = ExAllocatePoolWithTag(NonPagedPool, size, 0); + if (selconf == NULL) + return (NULL); + selconf->usc_hdr.uuh_func = URB_FUNCTION_SELECT_CONFIGURATION; + selconf->usc_hdr.uuh_len = size; + selconf->usc_handle = conf; + selconf->usc_conf = conf; + + intf = &selconf->usc_intf; + for (i = 0; i < conf->bNumInterface; i++) { + if (list[i].uil_intfdesc == NULL) + break; + + list[i].uil_intf = intf; + desc = list[i].uil_intfdesc; + + intf->uii_len = sizeof(struct usbd_interface_information) + + (desc->bNumEndpoints - 1) * + sizeof(struct usbd_pipe_information); + intf->uii_intfnum = desc->bInterfaceNumber; + intf->uii_altset = desc->bAlternateSetting; + intf->uii_intfclass = desc->bInterfaceClass; + intf->uii_intfsubclass = desc->bInterfaceSubClass; + intf->uii_intfproto = desc->bInterfaceProtocol; + intf->uii_handle = desc; + intf->uii_numeps = desc->bNumEndpoints; + + pipe = &intf->uii_pipes[0]; + for (j = 0; j < intf->uii_numeps; j++) + pipe[j].upi_maxtxsize = + USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; + + intf = (struct usbd_interface_information *)((char *)intf + + intf->uii_len); + } + + return ((union usbd_urb *)selconf); +} + +static void +USBD_GetUSBDIVersion(usbd_version_info *ui) +{ + + /* Pretend to be Windows XP. */ + + ui->uvi_usbdi_vers = USBDI_VERSION; + ui->uvi_supported_vers = USB_VER_2_0; +} + +static usb_interface_descriptor_t * +USBD_ParseConfigurationDescriptor(usb_config_descriptor_t *conf, + uint8_t intfnum, uint8_t altset) +{ + + return USBD_ParseConfigurationDescriptorEx(conf, conf, intfnum, altset, + -1, -1, -1); +} + +static usb_interface_descriptor_t * +USBD_ParseConfigurationDescriptorEx(usb_config_descriptor_t *conf, + void *start, int32_t intfnum, int32_t altset, int32_t intfclass, + int32_t intfsubclass, int32_t intfproto) +{ + char *pos; + usb_interface_descriptor_t *desc; + + for (pos = start; pos < ((char *)conf + UGETW(conf->wTotalLength)); + pos += desc->bLength) { + desc = (usb_interface_descriptor_t *)pos; + if (desc->bDescriptorType != UDESC_INTERFACE) + continue; + if (!(intfnum == -1 || desc->bInterfaceNumber == intfnum)) + continue; + if (!(altset == -1 || desc->bAlternateSetting == altset)) + continue; + if (!(intfclass == -1 || desc->bInterfaceClass == intfclass)) + continue; + if (!(intfsubclass == -1 || + desc->bInterfaceSubClass == intfsubclass)) + continue; + if (!(intfproto == -1 || desc->bInterfaceProtocol == intfproto)) + continue; + return (desc); + } + + return (NULL); +} + +static void +dummy(void) +{ + kprintf("USBD dummy called\n"); +} + +image_patch_table usbd_functbl[] = { + IMPORT_SFUNC(USBD_CreateConfigurationRequest, 2), + IMPORT_SFUNC(USBD_CreateConfigurationRequestEx, 2), + IMPORT_SFUNC_MAP(_USBD_CreateConfigurationRequestEx@8, + USBD_CreateConfigurationRequestEx, 2), + IMPORT_SFUNC(USBD_GetUSBDIVersion, 1), + IMPORT_SFUNC(USBD_ParseConfigurationDescriptor, 3), + IMPORT_SFUNC(USBD_ParseConfigurationDescriptorEx, 7), + IMPORT_SFUNC_MAP(_USBD_ParseConfigurationDescriptorEx@28, + USBD_ParseConfigurationDescriptorEx, 7), + + /* + * This last entry is a catch-all for any function we haven't + * implemented yet. The PE import list patching routine will + * use it for any function that doesn't have an explicit match + * in this table. + */ + + { NULL, (FUNC)dummy, NULL, 0, WINDRV_WRAP_STDCALL }, + + /* End of list. */ + + { NULL, NULL, NULL } +}; + +MODULE_DEPEND(ndis, usb, 1, 1, 1); diff --git a/sys/emulation/ndis/usbd_var.h b/sys/emulation/ndis/usbd_var.h new file mode 100644 index 0000000000..4aefefaea0 --- /dev/null +++ b/sys/emulation/ndis/usbd_var.h @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 2003 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/compat/ndis/usbd_var.h,v 1.2 2008/12/27 08:03:32 weongyo Exp $ + */ + +#ifndef _USBD_VAR_H_ +#define _USBD_VAR_H_ + +#define IOCTL_INTERNAL_USB_SUBMIT_URB 0x00220003 + +#define URB_FUNCTION_SELECT_CONFIGURATION 0x0000 +#define URB_FUNCTION_ABORT_PIPE 0x0002 +#define URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER 0x0009 +#define URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 0x000B +#define URB_FUNCTION_VENDOR_DEVICE 0x0017 +#define URB_FUNCTION_VENDOR_INTERFACE 0x0018 +#define URB_FUNCTION_VENDOR_ENDPOINT 0x0019 +#define URB_FUNCTION_CLASS_DEVICE 0x001A +#define URB_FUNCTION_CLASS_INTERFACE 0x001B +#define URB_FUNCTION_CLASS_ENDPOINT 0x001C +#define URB_FUNCTION_CLASS_OTHER 0x001F +#define URB_FUNCTION_VENDOR_OTHER 0x0020 + +#define USBD_STATUS_SUCCESS 0x00000000 +#define USBD_STATUS_CANCELED 0x00010000 +#define USBD_STATUS_PENDING 0x40000000 +#define USBD_STATUS_NO_MEMORY 0x80000100 +#define USBD_STATUS_REQUEST_FAILED 0x80000500 +#define USBD_STATUS_INVALID_PIPE_HANDLE 0x80000600 +#define USBD_STATUS_ERROR_SHORT_TRANSFER 0x80000900 +#define USBD_STATUS_CRC 0xC0000001 +#define USBD_STATUS_BTSTUFF 0xC0000002 +#define USBD_STATUS_DATA_TOGGLE_MISMATCH 0xC0000003 +#define USBD_STATUS_STALL_PID 0xC0000004 +#define USBD_STATUS_DEV_NOT_RESPONDING 0xC0000005 +#define USBD_STATUS_PID_CHECK_FAILURE 0xC0000006 +#define USBD_STATUS_UNEXPECTED_PID 0xC0000007 +#define USBD_STATUS_DATA_OVERRUN 0xC0000008 +#define USBD_STATUS_DATA_UNDERRUN 0xC0000009 +#define USBD_STATUS_RESERVED1 0xC000000A +#define USBD_STATUS_RESERVED2 0xC000000B +#define USBD_STATUS_BUFFER_OVERRUN 0xC000000C +#define USBD_STATUS_BUFFER_UNDERRUN 0xC000000D +#define USBD_STATUS_NOT_ACCESSED 0xC000000F +#define USBD_STATUS_FIFO 0xC0000010 +#define USBD_STATUS_XACT_ERROR 0xC0000011 +#define USBD_STATUS_BABBLE_DETECTED 0xC0000012 +#define USBD_STATUS_DATA_BUFFER_ERROR 0xC0000013 +#define USBD_STATUS_NOT_SUPPORTED 0xC0000E00 +#define USBD_STATUS_TIMEOUT 0xC0006000 +#define USBD_STATUS_DEVICE_GONE 0xC0007000 + +struct usbd_urb_header { + uint16_t uuh_len; + uint16_t uuh_func; + int32_t uuh_status; + void *uuh_handle; + uint32_t uuh_flags; +}; + +enum usbd_pipe_type { + UsbdPipeTypeControl = UE_CONTROL, + UsbdPipeTypeIsochronous = UE_ISOCHRONOUS, + UsbdPipeTypeBulk = UE_BULK, + UsbdPipeTypeInterrupt = UE_INTERRUPT +}; + +struct usbd_pipe_information { + uint16_t upi_maxpktsize; + uint8_t upi_epaddr; + uint8_t upi_interval; + enum usbd_pipe_type upi_type; + usb_endpoint_descriptor_t *upi_handle; + uint32_t upi_maxtxsize; +#define USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE PAGE_SIZE + uint32_t upi_flags; +}; + +struct usbd_interface_information { + uint16_t uii_len; + uint8_t uii_intfnum; + uint8_t uii_altset; + uint8_t uii_intfclass; + uint8_t uii_intfsubclass; + uint8_t uii_intfproto; + uint8_t uii_reserved; + void *uii_handle; + uint32_t uii_numeps; + struct usbd_pipe_information uii_pipes[1]; +}; + +struct usbd_urb_select_interface { + struct usbd_urb_header usi_hdr; + void *usi_handle; + struct usbd_interface_information uusi_intf; +}; + +struct usbd_urb_select_configuration { + struct usbd_urb_header usc_hdr; + usb_config_descriptor_t *usc_conf; + void *usc_handle; + struct usbd_interface_information usc_intf; +}; + +struct usbd_hcd_area { + void *reserved8[8]; +}; + +struct usbd_urb_bulk_or_intr_transfer { + struct usbd_urb_header ubi_hdr; + usb_endpoint_descriptor_t *ubi_epdesc; + uint32_t ubi_trans_flags; +#define USBD_SHORT_TRANSFER_OK 0x00000002 + uint32_t ubi_trans_buflen; + void *ubi_trans_buf; + struct mdl *ubi_mdl; + union usbd_urb *ubi_urblink; + struct usbd_hcd_area ubi_hca; +}; + +struct usbd_urb_control_descriptor_request { + struct usbd_urb_header ucd_hdr; + void *ucd_reserved0; + uint32_t ucd_reserved1; + uint32_t ucd_trans_buflen; + void *ucd_trans_buf; + struct mdl *ucd_mdl; + union nt_urb *ucd_urblink; + struct usbd_hcd_area ucd_hca; + uint16_t ucd_reserved2; + uint8_t ucd_idx; + uint8_t ucd_desctype; + uint16_t ucd_langid; + uint16_t ucd_reserved3; +}; + +struct usbd_urb_vendor_or_class_request { + struct usbd_urb_header uvc_hdr; + void *uvc_reserved0; + uint32_t uvc_trans_flags; +#define USBD_TRANSFER_DIRECTION_IN 1 + uint32_t uvc_trans_buflen; + void *uvc_trans_buf; + struct mdl *uvc_mdl; + union nt_urb *uvc_urblink; + struct usbd_hcd_area uvc_hca; + uint8_t uvc_reserved1; + uint8_t uvc_req; + uint16_t uvc_value; + uint16_t uvc_idx; + uint16_t uvc_reserved2; +}; + +struct usbd_interface_list_entry { + usb_interface_descriptor_t *uil_intfdesc; + struct usbd_interface_information *uil_intf; +}; + +union usbd_urb { + struct usbd_urb_header uu_hdr; + struct usbd_urb_select_configuration uu_selconf; + struct usbd_urb_bulk_or_intr_transfer uu_bulkintr; + struct usbd_urb_control_descriptor_request uu_ctldesc; + struct usbd_urb_vendor_or_class_request uu_vcreq; +}; + +#define USBD_URB_STATUS(urb) ((urb)->uu_hdr.uuh_status) + +#define USBDI_VERSION 0x00000500 +#define USB_VER_1_1 0x00000110 +#define USB_VER_2_0 0x00000200 + +struct usbd_version_info { + uint32_t uvi_usbdi_vers; + uint32_t uvi_supported_vers; +}; + +typedef struct usbd_version_info usbd_version_info; + +/* used for IRP cancel. */ +struct ndisusb_cancel { + device_t dev; + usbd_xfer_handle xfer; + struct usb_task task; +}; + +extern image_patch_table usbd_functbl[]; + +__BEGIN_DECLS +extern int usbd_libinit(void); +extern int usbd_libfini(void); +__END_DECLS + +#endif /* _USBD_VAR_H_ */ diff --git a/sys/emulation/ndis/winx32_wrap.S b/sys/emulation/ndis/winx32_wrap.S new file mode 100644 index 0000000000..b23928c63c --- /dev/null +++ b/sys/emulation/ndis/winx32_wrap.S @@ -0,0 +1,385 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/compat/ndis/winx32_wrap.S,v 1.6 2009/01/31 11:37:21 obrien Exp $ + */ + +/* The 'ret' macro doesn't work in this file if GPROF is enabled. */ +#ifdef GPROF +#undef GPROF +#endif + +#include + +/* + * This file contains assembly language wrappers for the different + * calling conventions supported by Windows on the i386 architecture. + * In FreeBSD, the whole OS typically use same C calling convention + * everywhere, namely _cdecl. Windows, on the other hand, uses several + * different C calling conventions depending on the circumstances: + * + * _stdcall: Used for most ordinary Windows APIs. With _stdcall, + * arguments are passed on the stack, and the callee unwinds the stack + * before returning control to the caller. Not suitable for variadic + * functions. + * + * _fastcall: Used for some APIs that may be invoked frequently and + * where speed is a critical factor (e.g. KeAcquireSpinLock() and + * KeReleaseSpinLock()) Similar to _stdcall, except the first 2 32-bit + * or smaller arguments are passed in the %ecx and %edx registers + * instead of on the stack. Not suitable for variadic functions. + * + * _cdecl: Used for standard C library routines and for variadic + * functions. + * + * _regparm(3): Used for certain assembly routines. All arguments + * passed in %eax, %ecx and %edx. + * + * Furthermore, there is an additional wrinkle that's not obvious + * with all code: Microsoft supports the use of exceptions in C + * (__try/__except) both in user _and_ kernel mode. Sadly, Windows + * structured exception handling uses machine-specific features + * that conflict rather badly with FreeBSD. (See utility routines + * at the end of this module for more details.) + * + * We want to support these calling conventions in as portable a manner + * as possible. The trick is doing it not only with different versions + * of GNU C, but with compilers other than GNU C (e.g. the Solaris + * SunOne C compiler). The only sure fire method is with assembly + * language trampoline code which both fixes up the argument passing, + * stack unwinding and exception/thread context all at once. + * + * You'll notice that we call the thunk/unthunk routines in the + * *_wrap() functions in an awkward way. Rather than branching + * directly to the address, we load the address into a register + * first as a literal value, then we branch to it. This is done + * to insure that the assembler doesn't translate the branch into + * a relative branch. We use the *_wrap() routines here as templates + * and create the actual trampolines at run time, at which point + * we only know the absolute addresses of the thunk and unthunk + * routines. So we need to make sure the templates have enough + * room in them for the full address. + * + * Also note that when we call the a thunk/unthunk routine after + * invoking a wrapped function, we have to make sure to preserve + * the value returned from that function. Most functions return + * a 32-bit value in %eax, however some routines return 64-bit + * values, which span both %eax and %edx. Consequently, we have + * to preserve both registers. + */ + +/* + * Handle _stdcall going from Windows to UNIX. + * This is frustrating, because to do it right you have to + * know how many arguments the called function takes, and there's + * no way to figure this out on the fly: you just have to be told + * ahead of time. We assume there will be 16 arguments. I don't + * think there are any Windows APIs that require this many. + */ + + .globl x86_stdcall_wrap_call + .globl x86_stdcall_wrap_arg + .globl x86_stdcall_wrap_end + +ENTRY(x86_stdcall_wrap) + push %esi + push %edi + sub $64,%esp + mov %esp,%esi + add $64+8+4,%esi + mov %esp,%edi + mov $16,%ecx # handle up to 16 args + rep + movsl + + movl $ctxsw_wtou, %eax + call *%eax # unthunk + +x86_stdcall_wrap_call: + movl $0,%eax + call *%eax # jump to routine + push %eax # preserve return val + push %edx + + movl $ctxsw_utow, %eax + call *%eax # thunk + + pop %edx + pop %eax # restore return val + + add $64,%esp # clean the stack + pop %edi + pop %esi +x86_stdcall_wrap_arg: + ret $0xFF +x86_stdcall_wrap_end: + + +/* + * Handle _stdcall going from UNIX to Windows. This routine + * expects to be passed the function to be called, number of + * args and the arguments for the Windows function on the stack. + */ + +ENTRY(x86_stdcall_call) + push %esi # must preserve %esi + push %edi # and %edi + + mov 16(%esp),%eax # get arg cnt + mov %eax,%ecx # save as copy count + mov %esp,%esi # Set source address register to point to + add $20,%esi # first agument to be forwarded. + shl $2,%eax # turn arg cnt into offset + sub %eax,%esp # shift stack to new location + mov %esp,%edi # store dest copy addr + rep # do the copy + movsl + + call ctxsw_utow # thunk + + call *12(%edi) # branch to stdcall routine + push %eax # preserve return val + push %edx + + call ctxsw_wtou # unthunk + + pop %edx + pop %eax # restore return val + mov %edi,%esp # restore stack + pop %edi # restore %edi + pop %esi # and %esi + ret + +/* + * Fastcall support. Similar to _stdcall, except the first + * two arguments are passed in %ecx and %edx. It happens we + * only support a small number of _fastcall APIs, none of them + * take more than three arguments. So to keep the code size + * and complexity down, we only handle 3 arguments here. + */ + +/* Call _fastcall function going from Windows to UNIX. */ + + .globl x86_fastcall_wrap_call + .globl x86_fastcall_wrap_arg + .globl x86_fastcall_wrap_end + +ENTRY(x86_fastcall_wrap) + mov 4(%esp),%eax + push %eax + push %edx + push %ecx + + movl $ctxsw_wtou, %eax + call *%eax # unthunk + +x86_fastcall_wrap_call: + mov $0,%eax + call *%eax # branch to fastcall routine + push %eax # preserve return val + push %edx + + movl $ctxsw_utow, %eax + call *%eax # thunk + + pop %edx + pop %eax # restore return val + add $12,%esp # clean the stack +x86_fastcall_wrap_arg: + ret $0xFF +x86_fastcall_wrap_end: + +/* + * Call _fastcall function going from UNIX to Windows. + * This routine isn't normally used since NDIS miniport drivers + * only have _stdcall entry points, but it's provided anyway + * to round out the API, and for testing purposes. + */ + +ENTRY(x86_fastcall_call) + mov 4(%esp),%eax + push 16(%esp) + + call ctxsw_utow # thunk + + mov 12(%esp),%ecx + mov 16(%esp),%edx + call *8(%esp) # branch to fastcall routine + push %eax # preserve return val + push %edx + + call ctxsw_wtou # unthunk + + pop %edx + pop %eax # restore return val + add $4,%esp # clean the stack + ret + +/* + * Call regparm(3) function going from Windows to UNIX. Arguments + * are passed in %eax, %edx and %ecx. Note that while additional + * arguments are passed on the stack, we never bother when them, + * since the only regparm(3) routines we need to wrap never take + * more than 3 arguments. + */ + + .globl x86_regparm_wrap_call + .globl x86_regparm_wrap_end + +ENTRY(x86_regparm_wrap) + push %ecx + push %edx + push %eax + + movl $ctxsw_wtou, %eax + call *%eax # unthunk + +x86_regparm_wrap_call: + movl $0,%eax + call *%eax # jump to routine + push %eax # preserve return val + push %edx # preserve return val + + movl $ctxsw_utow, %eax + call *%eax # thunk + + pop %edx # restore return val + pop %eax # restore return val + add $12,%esp # restore stack + ret +x86_regparm_wrap_end: + +/* + * Call regparm(3) function going from UNIX to Windows. + * This routine isn't normally used since NDIS miniport drivers + * only have _stdcall entry points, but it's provided anyway + * to round out the API, and for testing purposes. + */ + +ENTRY(x86_regparm_call) + call ctxsw_utow # thunk + + mov 8(%esp),%eax + mov 12(%esp),%edx + mov 16(%esp),%ecx + call *4(%esp) # branch to fastcall routine + push %eax # preserve return val + push %edx # preserve return val + + call ctxsw_wtou # unthunk + + pop %edx # restore return val + pop %eax # restore return val + ret + +/* + * Ugly hack alert: + * + * On Win32/i386, using __try/__except results in code that tries to + * manipulate what's supposed to be the Windows Threada Environment + * Block (TEB), which one accesses via the %fs register. In particular, + * %fs:0 (the first DWORD in the TEB) points to the exception + * registration list. Unfortunately, FreeBSD uses %fs for the + * per-cpu data structure (pcpu), and we can't allow Windows code + * to muck with that. I don't even know what Solaris uses %fs for + * (or if it even uses it at all). + * + * Even worse, in 32-bit protected mode, %fs is a selector that + * refers to an entry in either the GDT or the LDT. Ideally, we would + * like to be able to temporarily point it at another descriptor + * while Windows code executes, but to do that we need a separate + * descriptor entry of our own to play with. + * + * Therefore, we go to some trouble to learn the existing layout of + * the GDT and update it to include an extra entry that we can use. + * We need the following utility routines to help us do that. On + * FreeBSD, index #7 in the GDT happens to be unused, so we turn + * this into our own data segment descriptor. It would be better + * if we could use a private LDT entry, but there's no easy way to + * do that in SMP mode because of the way FreeBSD handles user LDTs. + * + * Once we have a custom descriptor, we have to thunk/unthunk whenever + * we cross between FreeBSD code and Windows code. The thunking is + * based on the premise that when executing instructions in the + * Windows binary itself, we won't go to sleep. This is because in + * order to yield the CPU, the code has to call back out to a FreeBSD + * routine first, and when that happens we can unthunk in order to + * restore FreeBSD context. What we're desperately trying to avoid is + * being involuntarily pre-empted with the %fs register still pointing + * to our fake TIB: if FreeBSD code runs with %fs pointing at our + * Windows TIB instead of pcpu, we'll panic the kernel. Fortunately, + * the only way involuntary preemption can occur is if an interrupt + * fires, and the trap handler saves/restores %fs for us. + * + * The thunking routines themselves, ctxsw_utow() (Context SWitch UNIX + * to Windows) and ctxsw_wtou() (Context SWitch Windows to UNIX), are + * external to this module. This is done simply because it's easier + * to manipulate data structures in C rather than assembly. + */ + +ENTRY(x86_getldt) + movl 4(%esp),%eax + sgdtl (%eax) + movl 8(%esp),%eax + sldt (%eax) + xor %eax,%eax + ret + +ENTRY(x86_setldt) + movl 4(%esp),%eax + lgdt (%eax) + jmp 1f + nop +1: + movl 8(%esp),%eax + lldt %ax + xor %eax,%eax + ret + +ENTRY(x86_getfs) + mov %fs,%ax + ret + +ENTRY(x86_setfs) + mov 4(%esp),%fs + ret + +ENTRY(x86_gettid) + mov %fs:12,%eax + ret + +ENTRY(x86_critical_enter) + cli + ret + +ENTRY(x86_critical_exit) + sti + ret diff --git a/sys/emulation/ndis/winx64_wrap.S b/sys/emulation/ndis/winx64_wrap.S new file mode 100644 index 0000000000..d651dfde9c --- /dev/null +++ b/sys/emulation/ndis/winx64_wrap.S @@ -0,0 +1,179 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * The x86_64 callback routines were written and graciously submitted + * by Ville-Pertti Keinonen . + * + * $FreeBSD: src/sys/compat/ndis/winx64_wrap.S,v 1.5 2010/11/22 20:46:38 bschmidt Exp $ + */ + +#include + +/* + * Wrapper for handling up to 16 arguments. We can't really + * know how many arguments the caller will pass us. I'm taking an + * educated guess that we'll never get over 16. Handling too + * few arguments is bad. Handling too many is inefficient, but + * not fatal. If someone can think of a way to handle an arbitrary + * number of arguments with more elegant code, freel free to let + * me know. + * + * Standard amd64 calling conventions specify the following registers + * to be used for passing the first 6 arguments: + * + * %rdi, %rsi, %rdx, %rcx, %r8, %r9 + * + * Further arguments are passed on the stack (the 7th argument is + * located immediately after the return address). + * + * Windows x86_64 calling conventions only pass the first 4 + * arguments in registers: + * + * %rcx, %rdx, %r8, %r9 + * + * Even when arguments are passed in registers, the stack must have + * space reserved for those arguments. Thus the 5th argument (the + * first non-register argument) is placed 32 bytes after the return + * address. Additionally, %rdi and %rsi must be preserved. (These + * two registers are not scratch registers in the standard convention.) + * + * Note that in this template, we load a contrived 64 bit address into + * %r11 to represent our jump address. This is to guarantee that the + * assembler leaves enough room to patch in an absolute 64-bit address + * later. The idea behind this code is that we want to avoid having to + * manually create all the wrapper functions at compile time with + * a bunch of macros. This is doable, but a) messy and b) requires + * us to maintain two separate tables (one for the UNIX function + * pointers and another with the wrappers). This means I'd have to + * update two different tables each time I added a function. + * + * To avoid this, we create the wrappers at runtime instead. The + * image patch tables now contain two pointers: one two the normal + * routine, and a blank one for the wrapper. To construct a wrapper, + * we allocate some memory and copy the template function into it, + * then patch the function pointer for the routine we want to wrap + * into the newly created wrapper. The subr_pe module can then + * simply patch the wrapper routine into the jump table into the + * windows image. As a bonus, the wrapper pointer not only serves + * as the wrapper entry point address, it's also a data pointer + * that we can pass to free() later when we unload the module. + */ + + .globl x86_64_wrap_call + .globl x86_64_wrap_end + +ENTRY(x86_64_wrap) + push %rbp # insure that the stack + mov %rsp,%rbp # is 16-byte aligned + and $-16,%rsp # + subq $96,%rsp # allocate space on stack + mov %rsi,96-8(%rsp) # save %rsi + mov %rdi,96-16(%rsp)# save %rdi + mov %rcx,%r10 # temporarily save %rcx in scratch + lea 56+8(%rbp),%rsi # source == old stack top (stack+56) + mov %rsp,%rdi # destination == new stack top + mov $10,%rcx # count == 10 quadwords + rep + movsq # copy old stack contents to new location + mov %r10,%rdi # set up arg0 (%rcx -> %rdi) + mov %rdx,%rsi # set up arg1 (%rdx -> %rsi) + mov %r8,%rdx # set up arg2 (%r8 -> %rdx) + mov %r9,%rcx # set up arg3 (%r9 -> %rcx) + mov 40+8(%rbp),%r8 # set up arg4 (stack+40 -> %r8) + mov 48+8(%rbp),%r9 # set up arg5 (stack+48 -> %r9) + xor %rax,%rax # clear return value +x86_64_wrap_call: + mov $0xFF00FF00FF00FF00,%r11 + callq *%r11 # call routine + mov 96-16(%rsp),%rdi# restore %rdi + mov 96-8(%rsp),%rsi # restore %rsi + leave # delete space on stack + ret +x86_64_wrap_end: + +/* + * Functions for invoking x86_64 callbacks. In each case, the first + * argument is a pointer to the function. + */ + +ENTRY(x86_64_call1) + subq $40,%rsp + mov %rsi,%rcx + call *%rdi + addq $40,%rsp + ret + +ENTRY(x86_64_call2) + subq $40,%rsp + mov %rsi,%rcx + /* %rdx is already correct */ + call *%rdi + addq $40,%rsp + ret + +ENTRY(x86_64_call3) + subq $40,%rsp + mov %rcx,%r8 + mov %rsi,%rcx + call *%rdi + addq $40,%rsp + ret + +ENTRY(x86_64_call4) + subq $40,%rsp + mov %r8,%r9 + mov %rcx,%r8 + mov %rsi,%rcx + call *%rdi + addq $40,%rsp + ret + +ENTRY(x86_64_call5) + subq $48,%rsp + mov %r9,32(%rsp) + mov %r8,%r9 + mov %rcx,%r8 + mov %rsi,%rcx + call *%rdi + addq $48,%rsp + ret + +ENTRY(x86_64_call6) + subq $56,%rsp + mov 56+8(%rsp),%rax + mov %r9,32(%rsp) + mov %rax,40(%rsp) + mov %r8,%r9 + mov %rcx,%r8 + mov %rsi,%rcx + call *%rdi + addq $56,%rsp + ret diff --git a/sys/platform/pc32/conf/files b/sys/platform/pc32/conf/files index 6d6eb8bdab..4a2e3bca88 100644 --- a/sys/platform/pc32/conf/files +++ b/sys/platform/pc32/conf/files @@ -58,6 +58,14 @@ emulation/linux/linux_sysctl.c optional compat_linux emulation/linux/linux_time.c optional compat_linux emulation/linux/linux_uid16.c optional compat_linux emulation/linux/linux_util.c optional compat_linux +emulation/ndis/kern_ndis.c optional ndisapi pci +emulation/ndis/kern_windrv.c optional ndisapi pci +emulation/ndis/subr_hal.c optional ndisapi pci +emulation/ndis/subr_ndis.c optional ndisapi pci +emulation/ndis/subr_ntoskrnl.c optional ndisapi pci +emulation/ndis/subr_pe.c optional ndisapi pci +emulation/ndis/subr_usbd.c optional ndisapi pci +emulation/ndis/winx32_wrap.S optional ndisapi pci # bf_enc.o optional ipsec ipsec_esp \ dependency "$S/crypto/blowfish/arch/i386/bf_enc.S $S/crypto/blowfish/arch/i386/bf_enc_586.S $S/crypto/blowfish/arch/i386/bf_enc_686.S" \ @@ -119,6 +127,11 @@ dev/powermng/longrun/longrun.c optional cpu_enable_longrun dev/netif/fe/if_fe.c optional fe dev/netif/fe/if_fe_isa.c optional fe isa dev/netif/fe/if_fe_pccard.c optional fe pccard +dev/netif/ndis/if_ndis.c optional ndis +dev/netif/ndis/if_ndis_pccard.c optional ndis pccard +dev/netif/ndis/if_ndis_pci.c optional ndis cardbus +dev/netif/ndis/if_ndis_pci.c optional ndis pci +dev/netif/ndis/if_ndis_usb.c optional ndis usb dev/misc/kbd/atkbd.c optional atkbd dev/misc/kbd/atkbdc.c optional atkbdc dev/misc/kbd/kbd.c optional atkbd diff --git a/sys/platform/pc32/conf/options b/sys/platform/pc32/conf/options index 53962cb09a..84c56786c5 100644 --- a/sys/platform/pc32/conf/options +++ b/sys/platform/pc32/conf/options @@ -22,6 +22,7 @@ COMPAT_OLDISA opt_compat_oldisa.h # (see src/sys/conf/options), except for broken debugging options. COMPAT_LINUX opt_dontuse.h LINPROCFS opt_dontuse.h #Linux compatible procfs +NDISAPI opt_dontuse.h # i386 SMP options diff --git a/sys/platform/pc32/i386/machdep.c b/sys/platform/pc32/i386/machdep.c index a46321a54a..55cf644fd3 100644 --- a/sys/platform/pc32/i386/machdep.c +++ b/sys/platform/pc32/i386/machdep.c @@ -1328,6 +1328,15 @@ struct soft_segment_descriptor gdt_segs[] = { 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTLS_END 17 TLS */ +{ 0x0, /* segment base address */ + 0x0, /* length */ + 0, /* segment type */ + 0, /* segment descriptor priority level */ + 0, /* segment descriptor present */ + 0, 0, + 0, /* default 32 vs 16 bit size */ + 0 /* limit granularity (byte/page units)*/ }, +/* GNDIS_SEL 18 NDIS Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ diff --git a/sys/platform/pc64/conf/files b/sys/platform/pc64/conf/files index e05336f395..a528858c61 100644 --- a/sys/platform/pc64/conf/files +++ b/sys/platform/pc64/conf/files @@ -51,6 +51,14 @@ emulation/linux/x86_64/linux32_locore.s optional compat_linux32 \ emulation/linux/x86_64/linux32_machdep.c optional compat_linux32 emulation/linux/x86_64/linux32_sysent.c optional compat_linux32 emulation/linux/x86_64/linux32_sysvec.c optional compat_linux32 +emulation/ndis/kern_ndis.c optional ndisapi pci +emulation/ndis/kern_windrv.c optional ndisapi pci +emulation/ndis/subr_hal.c optional ndisapi pci +emulation/ndis/subr_ndis.c optional ndisapi pci +emulation/ndis/subr_ntoskrnl.c optional ndisapi pci +emulation/ndis/subr_pe.c optional ndisapi pci +emulation/ndis/subr_usbd.c optional ndisapi pci +emulation/ndis/winx64_wrap.S optional ndisapi pci # XXX use assembler versions of bf_enc and des_enc crypto/blowfish/bf_enc.c optional ipsec ipsec_esp @@ -96,6 +104,11 @@ dev/video/fb/fb.c optional vga dev/video/fb/splash.c optional splash dev/video/fb/vga.c optional vga dev/powermng/est/est.c optional cpu_enable_est +dev/netif/ndis/if_ndis.c optional ndis +dev/netif/ndis/if_ndis_pccard.c optional ndis pccard +dev/netif/ndis/if_ndis_pci.c optional ndis cardbus +dev/netif/ndis/if_ndis_pci.c optional ndis pci +dev/netif/ndis/if_ndis_usb.c optional ndis usb dev/misc/kbd/atkbd.c optional atkbd dev/misc/kbd/atkbdc.c optional atkbdc dev/misc/kbd/kbd.c optional atkbd diff --git a/sys/platform/pc64/conf/options b/sys/platform/pc64/conf/options index 072e95845e..dccae2317a 100644 --- a/sys/platform/pc64/conf/options +++ b/sys/platform/pc64/conf/options @@ -12,6 +12,11 @@ I586_PMC_GUPROF opt_i586_guprof.h BROKEN_KEYBOARD_RESET opt_reset.h APIC_IO opt_apic.h +# Options for emulators. These should only be used at config time, so +# they are handled like options for static file systems +# (see src/sys/conf/options), except for broken debugging options. +NDISAPI opt_dontuse.h + # x86_64 SMP options CPU_AMD64X2_INTR_SPAM opt_cpu.h CPU_ENABLE_EST opt_cpu.h diff --git a/usr.sbin/802_11/Makefile b/usr.sbin/802_11/Makefile index 61ba62ab0c..548a577c23 100644 --- a/usr.sbin/802_11/Makefile +++ b/usr.sbin/802_11/Makefile @@ -1,5 +1,3 @@ -# $DragonFly: src/usr.sbin/802_11/Makefile,v 1.3 2006/09/02 05:40:35 sephe Exp $ - -SUBDIR= wpa_passphrase wpa_supplicant wpa_cli hostapd hostapd_cli +SUBDIR= hostapd hostapd_cli ndis_events wpa_cli wpa_passphrase wpa_supplicant .include diff --git a/usr.sbin/802_11/ndis_events/Makefile b/usr.sbin/802_11/ndis_events/Makefile new file mode 100644 index 0000000000..6866a2be3b --- /dev/null +++ b/usr.sbin/802_11/ndis_events/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD: src/usr.sbin/wpa/ndis_events/Makefile,v 1.1 2005/10/10 17:51:12 wpaul Exp $ + +PROG= ndis_events +MAN= ndis_events.8 +WARNS?= 6 + +.include diff --git a/usr.sbin/802_11/ndis_events/ndis_events.8 b/usr.sbin/802_11/ndis_events/ndis_events.8 new file mode 100644 index 0000000000..4b62c2a949 --- /dev/null +++ b/usr.sbin/802_11/ndis_events/ndis_events.8 @@ -0,0 +1,135 @@ +.\" Copyright (c) 2005 +.\" Bill Paul All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Bill Paul. +.\" 4. Neither the name of the author nor the names of any co-contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +.\" THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.sbin/wpa/ndis_events/ndis_events.8,v 1.7 2007/09/07 22:19:29 brueffer Exp $ +.\" +.Dd August 30, 2007 +.Dt NDIS_EVENTS 8 +.Os +.Sh NAME +.Nm ndis_events +.Nd relay events from +.Xr ndis 4 +drivers to +.Xr wpa_supplicant 8 +.Sh SYNOPSIS +.Nm +.Op Fl a +.Op Fl d +.Op Fl v +.Sh DESCRIPTION +The +.Nm +utility listens for events generated by an +.Xr ndis 4 +wireless network driver and relays them to +.Xr wpa_supplicant 8 +for possible processing. +The three event types that can occur +are media connect and disconnect events, such as when a wireless +interface joins or leaves a network, and media-specific events. +In particular, +.Xr ndis 4 +drivers that support WPA2 will generate media-specific events +containing PMKID candidate information which +.Xr wpa_supplicant 8 +needs in order to properly associate with WPA2-capable access points. +.Pp +The +.Nm +daemon works by listening for interface information events via +a routing socket. +When it detects an event that was generated by an +.Xr ndis 4 +interface, it transmits it via UDP packet on the loopback interface, +where +.Xr wpa_supplicant 8 +is presumeably listening. +The standard +.Xr wpa_supplicant 8 +distribution includes its own version of this utility for use with +.Tn Windows\[rg] . +The +.Fx +version performs the same functions as the +.Tn Windows\[rg] +one, except that it uses an +.Xr ioctl 4 +and routing socket interface instead of WMI. +.Pp +Note that a single instance of +.Nm +is sufficient to scan for events for any number of +.Xr ndis 4 +interfaces in a system. +.Sh OPTIONS +The +.Nm +daemon supports the following options: +.Bl -tag -width indent +.It Fl a +Process all events. +By default, +.Nm +will only process and forward media-specific events, which contain +PMKID candidate information, and not bother forwarding connect and +disconnect events, since +.Xr wpa_supplicant 8 +normally can determine the current link state on its own. +In some +cases, the additional connect and disconnect events only confuse it +and make the association and authentication process take longer. +.It Fl d +Run in debug mode. +This causes +.Nm +to run in the foreground and generate any output to the standard +error instead of using the +.Xr syslog 3 +facility. +.It Fl v +Run in verbose mode. +This causes +.Nm +to emit notifications when it receives events. +.El +.Sh SEE ALSO +.Xr ndis 4 , +.Xr wpa_supplicant 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 6.0 . +.Sh AUTHORS +The +.Nm +utility was written by +.An Bill Paul Aq wpaul@windriver.com . diff --git a/usr.sbin/802_11/ndis_events/ndis_events.c b/usr.sbin/802_11/ndis_events/ndis_events.c new file mode 100644 index 0000000000..76797e812e --- /dev/null +++ b/usr.sbin/802_11/ndis_events/ndis_events.c @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/wpa/ndis_events/ndis_events.c,v 1.8 2011/06/24 07:05:20 kevlo Exp $ + */ + +/* + * This program simulates the behavior of the ndis_events utility + * supplied with wpa_supplicant for Windows. The original utility + * is designed to translate Windows WMI events. We don't have WMI, + * but we need to supply certain event info to wpa_supplicant in + * order to make WPA2 work correctly, so we fake up the interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static int verbose = 0; +static int debug = 0; +static int all_events = 0; + +#define PROGNAME "ndis_events" + +#define WPA_SUPPLICANT_PORT 9876 +#define NDIS_INDICATION_LEN 2048 + +#define EVENT_CONNECT 0 +#define EVENT_DISCONNECT 1 +#define EVENT_MEDIA_SPECIFIC 2 + +#define NDIS_STATUS_MEDIA_CONNECT 0x4001000B +#define NDIS_STATUS_MEDIA_DISCONNECT 0x4001000C +#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 + +struct ndis_evt { + uint32_t ne_sts; + uint32_t ne_len; +#ifdef notdef + char ne_buf[1]; +#endif +}; + +static int find_ifname(int, char *); +static int announce_event(char *, int, struct sockaddr_in *); +static void usage(void); + +static void +dbgmsg(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (debug) + vwarnx(fmt, ap); + else + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); + + return; +} + +static int +find_ifname(int idx, char *name) +{ + int mib[6]; + size_t needed; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + char *buf, *lim, *next; + + needed = 0; + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* no flags */ + + if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0) + return(EIO); + + buf = malloc (needed); + if (buf == NULL) + return(ENOMEM); + + if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) { + free(buf); + return(EIO); + } + + lim = buf + needed; + + next = buf; + while (next < lim) { + ifm = (struct if_msghdr *)next; + if (ifm->ifm_type == RTM_IFINFO) { + sdl = (struct sockaddr_dl *)(ifm + 1); + if (ifm->ifm_index == idx) { + strncpy(name, sdl->sdl_data, sdl->sdl_nlen); + name[sdl->sdl_nlen] = '\0'; + free (buf); + return (0); + } + } + next += ifm->ifm_msglen; + } + + free (buf); + + return(ENOENT); +} + +static int +announce_event(char *ifname, int sock, struct sockaddr_in *dst) +{ + int s; + char indication[NDIS_INDICATION_LEN]; + struct ifreq ifr; + struct ndis_evt *e; + char buf[512], *pos, *end; + int len, type, _type; + + s = socket(PF_INET, SOCK_DGRAM, 0); + + if (s < 0) { + dbgmsg("socket creation failed"); + return(EINVAL); + } + + bzero((char *)&ifr, sizeof(ifr)); + e = (struct ndis_evt *)indication; + e->ne_len = NDIS_INDICATION_LEN - sizeof(struct ndis_evt); + + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + ifr.ifr_data = indication; + + if (ioctl(s, SIOCGPRIVATE_0, &ifr) < 0) { + close(s); + if (verbose) { + if (errno == ENOENT) + dbgmsg("drained all events from %s", + ifname, errno); + else + dbgmsg("failed to read event info from %s: %d", + ifname, errno); + } + return(ENOENT); + } + + if (e->ne_sts == NDIS_STATUS_MEDIA_CONNECT) { + type = EVENT_CONNECT; + if (verbose) + dbgmsg("Received a connect event for %s", ifname); + if (!all_events) { + close(s); + return(0); + } + } + if (e->ne_sts == NDIS_STATUS_MEDIA_DISCONNECT) { + type = EVENT_DISCONNECT; + if (verbose) + dbgmsg("Received a disconnect event for %s", ifname); + if (!all_events) { + close(s); + return(0); + } + } + if (e->ne_sts == NDIS_STATUS_MEDIA_SPECIFIC_INDICATION) { + type = EVENT_MEDIA_SPECIFIC; + if (verbose) + dbgmsg("Received a media-specific event for %s", + ifname); + } + + end = buf + sizeof(buf); + _type = (int) type; + memcpy(buf, &_type, sizeof(_type)); + pos = buf + sizeof(_type); + + len = snprintf(pos + 1, end - pos - 1, "%s", ifname); + if (len < 0) { + close(s); + return(ENOSPC); + } + if (len > 255) + len = 255; + *pos = (unsigned char) len; + pos += 1 + len; + if (e->ne_len) { + if (e->ne_len > 255 || 1 + e->ne_len > (u_int)(end - pos)) { + dbgmsg("Not enough room for send_event data (%d)\n", + e->ne_len); + close(s); + return(ENOSPC); + } + *pos++ = (unsigned char) e->ne_len; + memcpy(pos, (indication) + sizeof(struct ndis_evt), e->ne_len); + pos += e->ne_len; + } + + len = sendto(sock, buf, pos - buf, 0, (struct sockaddr *) dst, + sizeof(struct sockaddr_in)); + + close(s); + return(0); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: ndis_events [-a] [-d] [-v]\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int s, r, n; + struct sockaddr_in sin; + char msg[NDIS_INDICATION_LEN]; + struct rt_msghdr *rtm; + struct if_msghdr *ifm; + char ifname[IFNAMSIZ]; + int ch; + + while ((ch = getopt(argc, argv, "dva")) != -1) { + switch(ch) { + case 'd': + debug++; + break; + case 'v': + verbose++; + break; + case 'a': + all_events++; + break; + default: + usage(); + break; + } + } + + if (!debug && daemon(0, 0)) + err(1, "failed to daemonize ourselves"); + + if (!debug) + openlog(PROGNAME, LOG_PID | LOG_CONS, LOG_DAEMON); + + bzero((char *)&sin, sizeof(sin)); + + /* Create a datagram socket. */ + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + dbgmsg("socket creation failed"); + exit(1); + } + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr("127.0.0.1"); + sin.sin_port = htons(WPA_SUPPLICANT_PORT); + + /* Create a routing socket. */ + + r = socket (PF_ROUTE, SOCK_RAW, 0); + if (r < 0) { + dbgmsg("routing socket creation failed"); + exit(1); + } + + /* Now sit and spin, waiting for events. */ + + if (verbose) + dbgmsg("Listening for events"); + + while (1) { + n = read(r, msg, NDIS_INDICATION_LEN); + rtm = (struct rt_msghdr *)msg; + if (rtm->rtm_type != RTM_IFINFO) + continue; + ifm = (struct if_msghdr *)msg; + if (find_ifname(ifm->ifm_index, ifname)) + continue; + if (strstr(ifname, "ndis")) { + while(announce_event(ifname, s, &sin) == 0) + ; + } else { + if (verbose) + dbgmsg("Skipping ifinfo message from %s", + ifname); + } + } + + /* NOTREACHED */ + exit(0); +} diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index cbd446bcca..d3680dd8ed 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -68,6 +68,7 @@ SUBDIR= 802_11 \ mrouted \ mtest \ mtree \ + ndiscvt \ ndp \ newsyslog \ ngctl \ @@ -141,9 +142,6 @@ SUBDIR= 802_11 \ ypset \ zic -# XXX re-add when adjusted to the new 802.11 framework -#MAN+= ndiscvt \ - .if !defined(NO_LPR) SUBDIR+=lpr .endif diff --git a/usr.sbin/ndiscvt/Makefile b/usr.sbin/ndiscvt/Makefile index 88bc93ddd5..c2a29bcede 100644 --- a/usr.sbin/ndiscvt/Makefile +++ b/usr.sbin/ndiscvt/Makefile @@ -1,5 +1,4 @@ -# $FreeBSD: src/usr.sbin/ndiscvt/Makefile,v 1.5 2004/02/23 20:21:21 johan Exp $ -# $DragonFly: src/usr.sbin/ndiscvt/Makefile,v 1.2 2005/02/04 21:30:42 cpressey Exp $ +# $FreeBSD: src/usr.sbin/ndiscvt/Makefile,v 1.7 2005/04/24 20:21:22 wpaul Exp $ .PATH: ${.CURDIR}/../../sys/emulation/ndis @@ -8,7 +7,8 @@ SRCS= ndiscvt.c SRCS+= subr_pe.c SRCS+= inf.c inf-token.l inf-parse.y y.tab.h -MAN8= ndiscvt.8 +MAN= ndiscvt.8 +MAN+= ndisgen.8 DPADD= ${LIBL} LDADD= -ll @@ -19,4 +19,10 @@ CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/../../sys CLEANFILES= y.output +FILES= windrv_stub.c +FILESNAME= windrv_stub.c +FILESDIR= /usr/share/misc + +SCRIPTS= ndisgen.sh + .include diff --git a/usr.sbin/ndiscvt/inf-parse.y b/usr.sbin/ndiscvt/inf-parse.y index 5baedbae62..a6d47799b0 100644 --- a/usr.sbin/ndiscvt/inf-parse.y +++ b/usr.sbin/ndiscvt/inf-parse.y @@ -30,7 +30,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $DragonFly: src/usr.sbin/ndiscvt/inf-parse.y,v 1.1 2004/07/30 00:24:24 dillon Exp $ + * $FreeBSD: src/usr.sbin/ndiscvt/inf-parse.y,v 1.2 2004/01/02 04:31:06 wpaul Exp $ */ #include diff --git a/usr.sbin/ndiscvt/inf-token.l b/usr.sbin/ndiscvt/inf-token.l index 719db000b3..522678ca2a 100644 --- a/usr.sbin/ndiscvt/inf-token.l +++ b/usr.sbin/ndiscvt/inf-token.l @@ -30,16 +30,15 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $DragonFly: src/usr.sbin/ndiscvt/inf-token.l,v 1.1 2004/07/30 00:24:24 dillon Exp $ + * $FreeBSD: src/usr.sbin/ndiscvt/inf-token.l,v 1.3 2004/01/11 21:10:35 mdodd Exp $ */ -#include +#include #include #include #include #include #include -#include #include "y.tab.h" int lineno = 1; diff --git a/usr.sbin/ndiscvt/inf.c b/usr.sbin/ndiscvt/inf.c index 49342ad5de..ca04328e1c 100644 --- a/usr.sbin/ndiscvt/inf.c +++ b/usr.sbin/ndiscvt/inf.c @@ -29,7 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $DragonFly: src/usr.sbin/ndiscvt/inf.c,v 1.2 2005/12/05 02:40:27 swildner Exp $ + * $FreeBSD: src/usr.sbin/ndiscvt/inf.c,v 1.19 2008/12/27 08:03:32 weongyo Exp $ */ #include @@ -54,12 +54,17 @@ static struct assign_head ah; static char *sstrdup (const char *); static struct assign *find_assign (const char *, const char *); +static struct assign + *find_next_assign + (struct assign *); static struct section *find_section (const char *); static void dump_deviceids_pci (void); static void dump_deviceids_pcmcia (void); +static void dump_deviceids_usb (void); static void dump_pci_id (const char *); static void dump_pcmcia_id (const char *); +static void dump_usb_id (const char *); static void dump_regvals (void); static void dump_paramreg (const struct section *, const struct reg *, int); @@ -79,6 +84,7 @@ inf_parse(FILE *fp, FILE *outfp) dump_deviceids_pci(); dump_deviceids_pcmcia(); + dump_deviceids_usb(); fprintf(outfp, "#ifdef NDIS_REGVALS\n"); dump_regvals(); fprintf(outfp, "#endif /* NDIS_REGVALS */\n"); @@ -125,6 +131,24 @@ find_assign(const char *s, const char *k) return(NULL); } +static struct assign * +find_next_assign(struct assign *a) +{ + struct assign *assign; + + TAILQ_FOREACH(assign, &ah, link) { + if (assign == a) + break; + } + + assign = assign->link.tqe_next; + + if (assign == NULL || assign->section != a->section) + return(NULL); + + return (assign); +} + static const char * stringcvt(const char *s) { @@ -229,6 +253,30 @@ dump_pci_id(const char *s) return; } +static void +dump_usb_id(const char *s) +{ + char *p; + char vidstr[7], pidstr[7]; + + p = strcasestr(s, "VID_"); + if (p == NULL) + return; + p += 4; + strcpy(vidstr, "0x"); + strncat(vidstr, p, 4); + p = strcasestr(s, "PID_"); + if (p == NULL) + return; + p += 4; + strcpy(pidstr, "0x"); + strncat(pidstr, p, 4); + if (p == NULL) + return; + + fprintf(ofp, "\t\\\n\t{ %s, %s, ", vidstr, pidstr); +} + static void dump_deviceids_pci(void) { @@ -236,16 +284,19 @@ dump_deviceids_pci(void) struct section *sec; struct assign *assign; char xpsec[256]; - int found = 0; + int first = 1, found = 0; /* Find manufacturer name */ manf = find_assign("Manufacturer", NULL); +nextmanf: + /* Find manufacturer section */ if (manf->vals[1] != NULL && (strcasecmp(manf->vals[1], "NT.5.1") == 0 || strcasecmp(manf->vals[1], "NTx86") == 0 || - strcasecmp(manf->vals[1], "NTx86.5.1") == 0)) { + strcasecmp(manf->vals[1], "NTx86.5.1") == 0 || + strcasecmp(manf->vals[1], "NTamd64") == 0)) { /* Handle Windows XP INF files. */ snprintf(xpsec, sizeof(xpsec), "%s.%s", manf->vals[0], manf->vals[1]); @@ -266,12 +317,15 @@ dump_deviceids_pci(void) } if (found == 0) - return; + goto done; found = 0; - /* Emit start of PCI device table */ - fprintf (ofp, "#define NDIS_PCI_DEV_TABLE"); + if (first == 1) { + /* Emit start of PCI device table */ + fprintf (ofp, "#define NDIS_PCI_DEV_TABLE"); + first = 0; + } retry: @@ -302,10 +356,18 @@ retry: goto retry; } + /* Handle Manufacturer sections with multiple entries. */ + manf = find_next_assign(manf); + + if (manf != NULL) + goto nextmanf; + +done: /* Emit end of table */ fprintf(ofp, "\n\n"); + return; } static void @@ -315,16 +377,19 @@ dump_deviceids_pcmcia(void) struct section *sec; struct assign *assign; char xpsec[256]; - int found = 0; + int first = 1, found = 0; /* Find manufacturer name */ manf = find_assign("Manufacturer", NULL); +nextmanf: + /* Find manufacturer section */ if (manf->vals[1] != NULL && (strcasecmp(manf->vals[1], "NT.5.1") == 0 || strcasecmp(manf->vals[1], "NTx86") == 0 || - strcasecmp(manf->vals[1], "NTx86.5.1") == 0)) { + strcasecmp(manf->vals[1], "NTx86.5.1") == 0 || + strcasecmp(manf->vals[1], "NTamd64") == 0)) { /* Handle Windows XP INF files. */ snprintf(xpsec, sizeof(xpsec), "%s.%s", manf->vals[0], manf->vals[1]); @@ -345,12 +410,15 @@ dump_deviceids_pcmcia(void) } if (found == 0) - return; + goto done; found = 0; - /* Emit start of PCMCIA device table */ - fprintf (ofp, "#define NDIS_PCMCIA_DEV_TABLE"); + if (first == 1) { + /* Emit start of PCMCIA device table */ + fprintf (ofp, "#define NDIS_PCMCIA_DEV_TABLE"); + first = 0; + } retry: @@ -381,10 +449,111 @@ retry: goto retry; } + /* Handle Manufacturer sections with multiple entries. */ + manf = find_next_assign(manf); + + if (manf != NULL) + goto nextmanf; + +done: + /* Emit end of table */ + + fprintf(ofp, "\n\n"); + + return; +} + +static void +dump_deviceids_usb(void) +{ + struct assign *manf, *dev; + struct section *sec; + struct assign *assign; + char xpsec[256]; + int first = 1, found = 0; + + /* Find manufacturer name */ + manf = find_assign("Manufacturer", NULL); + +nextmanf: + + /* Find manufacturer section */ + if (manf->vals[1] != NULL && + (strcasecmp(manf->vals[1], "NT.5.1") == 0 || + strcasecmp(manf->vals[1], "NTx86") == 0 || + strcasecmp(manf->vals[1], "NTx86.5.1") == 0 || + strcasecmp(manf->vals[1], "NTamd64") == 0)) { + /* Handle Windows XP INF files. */ + snprintf(xpsec, sizeof(xpsec), "%s.%s", + manf->vals[0], manf->vals[1]); + sec = find_section(xpsec); + } else + sec = find_section(manf->vals[0]); + + /* See if there are any USB device definitions. */ + + TAILQ_FOREACH(assign, &ah, link) { + if (assign->section == sec) { + dev = find_assign("strings", assign->key); + if (strcasestr(assign->vals[1], "USB") != NULL) { + found++; + break; + } + } + } + + if (found == 0) + goto done; + + found = 0; + + if (first == 1) { + /* Emit start of USB device table */ + fprintf (ofp, "#define NDIS_USB_DEV_TABLE"); + first = 0; + } + +retry: + + /* + * Now run through all the device names listed + * in the manufacturer section and dump out the + * device descriptions and vendor/device IDs. + */ + + TAILQ_FOREACH(assign, &ah, link) { + if (assign->section == sec) { + dev = find_assign("strings", assign->key); + /* Emit device IDs. */ + if (strcasestr(assign->vals[1], "USB") != NULL) + dump_usb_id(assign->vals[1]); + else + continue; + /* Emit device description */ + fprintf (ofp, "\t\\\n\t\"%s\" },", dev->vals[0]); + found++; + } + } + + /* Someone tried to fool us. Shame on them. */ + if (!found) { + found++; + sec = find_section(manf->vals[0]); + goto retry; + } + + /* Handle Manufacturer sections with multiple entries. */ + manf = find_next_assign(manf); + + if (manf != NULL) + goto nextmanf; + +done: /* Emit end of table */ fprintf(ofp, "\n\n"); + return; } static void @@ -495,8 +664,10 @@ dump_defaultinfo(const struct section *s, const struct reg *r, int devidx) continue; fprintf(ofp, "\n\t{ \"%s\" }, %d },", reg->value == NULL ? "" : stringcvt(reg->value), devidx); - break; + return; } + /* Default registry entry missing */ + fprintf(ofp, "\n\t{ \"\" }, %d },", devidx); return; } @@ -570,14 +741,20 @@ dump_regvals(void) if (strcasecmp(assign->vals[0], "$windows nt$") == 0) is_winnt++; + /* Emit start of block */ + fprintf (ofp, "ndis_cfg ndis_regvals[] = {"); + /* Find manufacturer name */ manf = find_assign("Manufacturer", NULL); +nextmanf: + /* Find manufacturer section */ if (manf->vals[1] != NULL && (strcasecmp(manf->vals[1], "NT.5.1") == 0 || strcasecmp(manf->vals[1], "NTx86") == 0 || - strcasecmp(manf->vals[1], "NTx86.5.1") == 0)) { + strcasecmp(manf->vals[1], "NTx86.5.1") == 0 || + strcasecmp(manf->vals[1], "NTamd64") == 0)) { is_winxp++; /* Handle Windows XP INF files. */ snprintf(sname, sizeof(sname), "%s.%s", @@ -586,9 +763,6 @@ dump_regvals(void) } else sec = find_section(manf->vals[0]); - /* Emit start of block */ - fprintf (ofp, "ndis_cfg ndis_regvals[] = {"); - retry: TAILQ_FOREACH(assign, &ah, link) { @@ -599,9 +773,15 @@ retry: * Look for section names with .NT, unless * this is a WinXP .INF file. */ + if (is_winxp) { sprintf(sname, "%s.NTx86", assign->vals[0]); dev = find_assign(sname, "AddReg"); + if (dev == NULL) { + sprintf(sname, "%s.NT", + assign->vals[0]); + dev = find_assign(sname, "AddReg"); + } if (dev == NULL) dev = find_assign(assign->vals[0], "AddReg"); @@ -630,6 +810,11 @@ retry: goto retry; } + manf = find_next_assign(manf); + + if (manf != NULL) + goto nextmanf; + fprintf(ofp, "\n\t{ NULL, NULL, { 0 }, 0 }\n};\n\n"); return; diff --git a/usr.sbin/ndiscvt/inf.h b/usr.sbin/ndiscvt/inf.h index 33f78699d8..6367f3edab 100644 --- a/usr.sbin/ndiscvt/inf.h +++ b/usr.sbin/ndiscvt/inf.h @@ -2,7 +2,6 @@ * $Id: inf.h,v 1.3 2003/11/30 21:58:16 winter Exp $ * * $FreeBSD: src/usr.sbin/ndiscvt/inf.h,v 1.1 2003/12/11 22:38:14 wpaul Exp $ - * $DragonFly: src/usr.sbin/ndiscvt/inf.h,v 1.1 2004/07/30 00:24:24 dillon Exp $ */ #define W_MAX 16 diff --git a/usr.sbin/ndiscvt/ndiscvt.8 b/usr.sbin/ndiscvt/ndiscvt.8 index e880a6a7ad..f83470611f 100644 --- a/usr.sbin/ndiscvt/ndiscvt.8 +++ b/usr.sbin/ndiscvt/ndiscvt.8 @@ -28,24 +28,25 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF .\" THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $FreeBSD: src/usr.sbin/ndiscvt/ndiscvt.8,v 1.4 2004/06/13 18:03:42 ru Exp $ -.\" $DragonFly: src/usr.sbin/ndiscvt/ndiscvt.8,v 1.3 2008/07/26 18:37:43 swildner Exp $ +.\" $FreeBSD: src/usr.sbin/ndiscvt/ndiscvt.8,v 1.9 2006/10/10 07:19:30 trhodes Exp $ .\" -.Dd December 10, 2003 +.Dd May 10, 2011 .Dt NDISCVT 8 .Os .Sh NAME .Nm ndiscvt .Nd convert .Tn Windows\[rg] -NDIS drivers for use with -.Dx +NDIS drivers for use with FreeBSD .Sh SYNOPSIS .Nm +.Op Fl O .Op Fl i Ar inffile .Fl s Ar sysfile .Op Fl n Ar devname .Op Fl o Ar outfile +.Nm +.Op Fl f Ar firmfile .Sh DESCRIPTION The .Nm @@ -79,7 +80,7 @@ The .Pa .INF file is typically required since only it contains device identification data such as PCI vendor and device IDs or PCMCIA -indentifier strings. +identifier strings. The .Pa .INF file may be optionally omitted however, @@ -145,9 +146,123 @@ The module expects to find the driver data in a file called .Pa ndis_driver_data.h , so it is recommended that this name be used. +.It Fl O +Generate both an +.Pa ndis_driver_data.h +file and +an +.Pa ndis_driver.data.o +file. +The latter file will contain a copy of the +.Tn Windows\[rg] +.Pa .SYS +driver image encoded as a +.Dx +ELF object file +(created with +.Xr objcopy 1 ) . +Turning the +.Tn Windows\[rg] +driver image directly into an object code file saves disk space +and compilation time. +.It Fl f Ar firmfile +A few NDIS drivers come with additional files that the core +driver module will load during initialization time. +Typically, +these files contain firmware which the driver will transfer to +the device in order to make it fully operational. +In +.Tn Windows\[rg] , +these files are usually just copied into one of the system +directories along with the driver itself. +.Pp +In +.Dx +there are two mechanism for loading these files. +If the driver +is built as a loadable kernel module which is loaded after the +kernel has finished booting +(and after the root file system has +been mounted), +the extra files can simply be copied to the +.Pa /compat/ndis +directory, and they will be loaded into the kernel on demand when the +the driver needs them. +.Pp +If however the driver is required to bootstrap the system +(i.e., if +the NDIS-based network interface is to be used for diskless/PXE +booting), +the files need to be pre-loaded by the bootstrap +loader in order to be accessible, since the driver will need them +before the root file system has been mounted. +However, the bootstrap +loader is only able to load files that are shared +.Dx +binary objects. +.Pp +The +.Fl f +flag can be used to convert an arbitrary file +.Ar firmfile +into shared object format +(the actual conversion is done using +the +.Xr objcopy 1 +and +.Xr ld 1 +commands). +The resulting files can then be copied to the +.Pa /boot/kernel +directory, and can be pre-loaded directly from the boot loader +prompt, or automatically by editing the +.Xr loader.conf 5 +file. +If desired, the files can also be loaded into memory +at runtime using the +.Xr kldload 8 +command. +.Pp +When an NDIS driver tries to open an external file, the +.Xr ndisapi 9 +code will first search for a loaded kernel module that matches the +name specified in the open request, and if that fails, it will then +try to open the file from the +.Pa /compat/ndis +directory as well. +Note that during kernel bootstrap, the ability +to open files from +.Pa /compat/ndis +is disabled: only the module search will be performed. +.Pp +When using the +.Fl f +flag, +.Nm +will generate both a relocatable object file +(with a +.Pa .o +extension) +and a shared object file +(with a +.Pa .ko +extension). +The shared object is the one that should be placed in +the +.Pa /boot/kernel +directory. +The relocatable object file is useful if the user wishes +to create a completely static kernel image: the object file can be +linked into the kernel directly along with the driver itself. +Some +editing of the kernel configuration files will be necessary in order +to have the extra object included in the build. .El .Sh SEE ALSO -.Xr ndis 4 +.Xr ld 1 , +.Xr objcopy 1 , +.Xr ndis 4 , +.Xr kldload 8 .Sh HISTORY The .Nm diff --git a/usr.sbin/ndiscvt/ndiscvt.c b/usr.sbin/ndiscvt/ndiscvt.c index 3c0953634d..aa850e790f 100644 --- a/usr.sbin/ndiscvt/ndiscvt.c +++ b/usr.sbin/ndiscvt/ndiscvt.c @@ -29,7 +29,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $DragonFly: src/usr.sbin/ndiscvt/ndiscvt.c,v 1.2 2005/12/05 02:40:27 swildner Exp $ + * $FreeBSD: src/usr.sbin/ndiscvt/ndiscvt.c,v 1.16 2011/02/18 20:54:12 dim Exp $ */ #include @@ -37,13 +37,15 @@ #include #include #include +#include #include #include #include #include +#include #include +#include -#include #include #include "inf.h" @@ -52,33 +54,41 @@ static int insert_padding(void **, int *); extern const char *__progname; /* - * Sections in object code files can be sparse. That is, the - * section may occupy more space in memory that it does when - * stored in a disk file. In Windows PE files, each section header - * has a 'virtual size' and 'raw data size' field. The latter - * specifies the amount of section data actually stored in the - * disk file, and the former describes how much space the section - * should actually occupy in memory. If the vsize is larger than - * the rsize, we need to allocate some extra storage and fill - * it with zeros. (Think BSS.) + * Sections within Windows PE files are defined using virtual + * and physical address offsets and virtual and physical sizes. + * The physical values define how the section data is stored in + * the executable file while the virtual values describe how the + * sections will look once loaded into memory. It happens that + * the linker in the Microsoft(r) DDK will tend to generate + * binaries where the virtual and physical values are identical, + * which means in most cases we can just transfer the file + * directly to memory without any fixups. This is not always + * the case though, so we have to be prepared to handle files + * where the in-memory section layout differs from the disk file + * section layout. * - * The typical method of loading an executable file involves - * reading each segment into memory using the vaddr/vsize from - * each section header. We try to make a small optimization however - * and only pad/move segments when it's absolutely necessary, i.e. - * if the vsize is larger than the rsize. This conserves a little - * bit of memory, at the cost of having to fixup some of the values - * in the section headers. + * There are two kinds of variations that can occur: the relative + * virtual address of the section might be different from the + * physical file offset, and the virtual section size might be + * different from the physical size (for example, the physical + * size of the .data section might be 1024 bytes, but the virtual + * size might be 1384 bytes, indicating that the data section should + * actually use up 1384 bytes in RAM and be padded with zeros). What we + * do is read the original file into memory and then make an in-memory + * copy with all of the sections relocated, re-sized and zero padded + * according to the virtual values specified in the section headers. + * We then emit the fixed up image file for use by the if_ndis driver. + * This way, we don't have to do the fixups inside the kernel. */ -#define ROUND_UP(x, y) \ - (((x) + (y)) - ((x) % (y))) +#define ROUND_DOWN(n, align) (((uintptr_t)n) & ~((align) - 1l)) +#define ROUND_UP(n, align) ROUND_DOWN(((uintptr_t)n) + (align) - 1l, \ + (align)) #define SET_HDRS(x) \ dos_hdr = (image_dos_header *)x; \ nt_hdr = (image_nt_header *)(x + dos_hdr->idh_lfanew); \ - sect_hdr = (image_section_header *)((vm_offset_t)nt_hdr + \ - sizeof(image_nt_header)); + sect_hdr = IMAGE_FIRST_SECTION(nt_hdr); static int insert_padding(void **imgbase, int *imglen) @@ -88,7 +98,7 @@ insert_padding(void **imgbase, int *imglen) image_nt_header *nt_hdr; image_optional_header opt_hdr; int i = 0, sections, curlen = 0; - int offaccum = 0, diff, oldraddr, oldrlen; + int offaccum = 0, oldraddr, oldrlen; uint8_t *newimg, *tmp; newimg = malloc(*imglen); @@ -107,35 +117,24 @@ insert_padding(void **imgbase, int *imglen) SET_HDRS(newimg); for (i = 0; i < sections; i++) { - /* - * If we have accumulated any padding offset, - * add it to the raw data address of this segment. - */ oldraddr = sect_hdr->ish_rawdataaddr; oldrlen = sect_hdr->ish_rawdatasize; - if (offaccum) - sect_hdr->ish_rawdataaddr += offaccum; - if (sect_hdr->ish_misc.ish_vsize > - sect_hdr->ish_rawdatasize) { - diff = ROUND_UP(sect_hdr->ish_misc.ish_vsize - - sect_hdr->ish_rawdatasize, - opt_hdr.ioh_filealign); - offaccum += ROUND_UP(diff - - (sect_hdr->ish_misc.ish_vsize - - sect_hdr->ish_rawdatasize), - opt_hdr.ioh_filealign); - sect_hdr->ish_rawdatasize = - ROUND_UP(sect_hdr->ish_rawdatasize, - opt_hdr.ioh_filealign); - tmp = realloc(newimg, *imglen + offaccum); - if (tmp == NULL) { - free(newimg); - return(ENOMEM); - } - newimg = tmp; - SET_HDRS(newimg); - sect_hdr += i; + sect_hdr->ish_rawdataaddr = sect_hdr->ish_vaddr; + offaccum += ROUND_UP(sect_hdr->ish_vaddr - oldraddr, + opt_hdr.ioh_filealign); + offaccum += + ROUND_UP(sect_hdr->ish_misc.ish_vsize, + opt_hdr.ioh_filealign) - + ROUND_UP(sect_hdr->ish_rawdatasize, + opt_hdr.ioh_filealign); + tmp = realloc(newimg, *imglen + offaccum); + if (tmp == NULL) { + free(newimg); + return(ENOMEM); } + newimg = tmp; + SET_HDRS(newimg); + sect_hdr += i; bzero(newimg + sect_hdr->ish_rawdataaddr, ROUND_UP(sect_hdr->ish_misc.ish_vsize, opt_hdr.ioh_filealign)); @@ -155,25 +154,140 @@ insert_padding(void **imgbase, int *imglen) static void usage(void) { - fprintf(stderr, "Usage: %s [-i ] -s " + fprintf(stderr, "Usage: %s [-O] [-i ] -s " "[-n devname] [-o outfile]\n", __progname); + fprintf(stderr, " %s -f \n", __progname); + exit(1); } +static void +bincvt(char *sysfile, char *outfile, void *img, int fsize) +{ + char *ptr; + char tname[] = "/tmp/ndiscvt.XXXXXX"; + char sysbuf[1024]; + FILE *binfp; + + mkstemp(tname); + + binfp = fopen(tname, "a+"); + if (binfp == NULL) + err(1, "opening %s failed", tname); + + if (fwrite(img, fsize, 1, binfp) != 1) + err(1, "failed to output binary image"); + + fclose(binfp); + + outfile = strdup(basename(outfile)); + if (strchr(outfile, '.')) + *strchr(outfile, '.') = '\0'; + + snprintf(sysbuf, sizeof(sysbuf), +#ifdef __i386__ + "objcopy -I binary -O elf32-i386 -B i386 %s %s.o\n", +#endif +#ifdef __x86_64__ + "objcopy -I binary -O elf64-x86-64 -B i386 %s %s.o\n", +#endif + tname, outfile); + printf("%s", sysbuf); + system(sysbuf); + unlink(tname); + + ptr = tname; + while (*ptr) { + if (*ptr == '/' || *ptr == '.') + *ptr = '_'; + ptr++; + } + + snprintf(sysbuf, sizeof(sysbuf), + "objcopy --redefine-sym _binary_%s_start=ndis_%s_drv_data_start " + "--strip-symbol _binary_%s_size " + "--redefine-sym _binary_%s_end=ndis_%s_drv_data_end %s.o %s.o\n", + tname, sysfile, tname, tname, sysfile, outfile, outfile); + printf("%s", sysbuf); + system(sysbuf); + + return; +} + +static void +firmcvt(char *firmfile) +{ + char *basefile, *outfile, *ptr; + char sysbuf[1024]; + + outfile = strdup(basename(firmfile)); + basefile = strdup(outfile); + + snprintf(sysbuf, sizeof(sysbuf), +#ifdef __i386__ + "objcopy -I binary -O elf32-i386 -B i386 %s %s.o\n", +#endif +#ifdef __x86_64__ + "objcopy -I binary -O elf64-x86-64 -B i386 %s %s.o\n", +#endif + firmfile, outfile); + printf("%s", sysbuf); + system(sysbuf); + + ptr = firmfile; + while (*ptr) { + if (*ptr == '/' || *ptr == '.') + *ptr = '_'; + ptr++; + } + ptr = basefile; + while (*ptr) { + if (*ptr == '/' || *ptr == '.') + *ptr = '_'; + else + *ptr = tolower(*ptr); + ptr++; + } + + snprintf(sysbuf, sizeof(sysbuf), + "objcopy --redefine-sym _binary_%s_start=%s_start " + "--strip-symbol _binary_%s_size " + "--redefine-sym _binary_%s_end=%s_end %s.o %s.o\n", + firmfile, basefile, firmfile, firmfile, + basefile, outfile, outfile); + ptr = sysbuf; + printf("%s", sysbuf); + system(sysbuf); + + snprintf(sysbuf, sizeof(sysbuf), + "ld -Bshareable -d -warn-common -o %s.ko %s.o\n", + outfile, outfile); + printf("%s", sysbuf); + system(sysbuf); + + free(basefile); + + exit(0); +} + int main(int argc, char *argv[]) { - FILE *fp, *outfp; - void *img; - int n, fsize, cnt; - unsigned char *ptr; - int i; - char *inffile = NULL, *sysfile = NULL, *outfile = NULL; - char *dname = NULL; - int ch; - - while((ch = getopt(argc, argv, "i:s:o:n:")) != -1) { + FILE *fp, *outfp; + int i, bin = 0; + void *img; + int n, fsize, cnt; + unsigned char *ptr; + char *inffile = NULL, *sysfile = NULL; + char *outfile = NULL, *firmfile = NULL; + char *dname = NULL; + int ch; + + while((ch = getopt(argc, argv, "i:s:o:n:f:O")) != -1) { switch(ch) { + case 'f': + firmfile = optarg; + break; case 'i': inffile = optarg; break; @@ -186,12 +300,18 @@ main(int argc, char *argv[]) case 'n': dname = optarg; break; + case 'O': + bin = 1; + break; default: usage(); break; } } + if (firmfile != NULL) + firmcvt(firmfile); + if (sysfile == NULL) usage(); @@ -251,9 +371,29 @@ main(int argc, char *argv[]) } fprintf(outfp, "\n#ifdef NDIS_IMAGE\n"); + + if (bin) { + sysfile = strdup(basename(sysfile)); + ptr = (unsigned char *)sysfile; + while (*ptr) { + if (*ptr == '.') + *ptr = '_'; + ptr++; + } + fprintf(outfp, + "\nextern unsigned char ndis_%s_drv_data_start[];\n", + sysfile); + fprintf(outfp, "static unsigned char *drv_data = " + "ndis_%s_drv_data_start;\n\n", sysfile); + bincvt(sysfile, outfile, img, fsize); + goto done; + } + + fprintf(outfp, "\nextern unsigned char drv_data[];\n\n"); fprintf(outfp, "__asm__(\".data\");\n"); + fprintf(outfp, "__asm__(\".globl drv_data\");\n"); fprintf(outfp, "__asm__(\".type drv_data, @object\");\n"); fprintf(outfp, "__asm__(\".size drv_data, %d\");\n", fsize); fprintf(outfp, "__asm__(\"drv_data:\");\n"); @@ -280,6 +420,7 @@ main(int argc, char *argv[]) done: fprintf(outfp, "#endif /* NDIS_IMAGE */\n"); + if (fp != NULL) fclose(fp); fclose(outfp); diff --git a/usr.sbin/ndiscvt/ndisgen.8 b/usr.sbin/ndiscvt/ndisgen.8 new file mode 100644 index 0000000000..493d0e2f20 --- /dev/null +++ b/usr.sbin/ndiscvt/ndisgen.8 @@ -0,0 +1,86 @@ +.\" Copyright (c) 2005 +.\" Bill Paul All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Bill Paul. +.\" 4. Neither the name of the author nor the names of any co-contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +.\" THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.sbin/ndiscvt/ndisgen.8,v 1.4 2008/07/23 05:50:17 weongyo Exp $ +.\" +.Dd May 10, 2011 +.Dt NDISGEN 8 +.Os +.Sh NAME +.Nm ndisgen +.Nd generate a DragonFly driver module from a +.Tn Windows\[rg] +NDIS driver distribution +.Sh SYNOPSIS +.Nm +.Op Ar /path/to/INF /path/to/SYS +.Sh DESCRIPTION +The +.Nm +script uses the +.Xr ndiscvt 8 +utility and other tools to generate a +.Dx +loadable driver module +and a static ELF object module from a +.Tn Windows\[rg] +NDIS driver, for use with the +.Xr ndis 4 +compatibility module. +.Pp +The +.Nm +script is interactive and contains its own help section. +Unless the paths to both files are supplied on the command line, +the script will prompt the user for the +.Pa .INF +and +.Pa .SYS +files needed to generate the +.Dx +driver module. +The script will also prompt for +any firmware or other external files needed. +.Sh SEE ALSO +.Xr ld 1 , +.Xr objcopy 1 , +.Xr ndis 4 , +.Xr kldload 8 , +.Xr ndiscvt 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 6.0 . +.Sh AUTHORS +The +.Nm +utility was written by +.An Bill Paul Aq wpaul@windriver.com . diff --git a/usr.sbin/ndiscvt/ndisgen.sh b/usr.sbin/ndiscvt/ndisgen.sh new file mode 100644 index 0000000000..aa90efb973 --- /dev/null +++ b/usr.sbin/ndiscvt/ndisgen.sh @@ -0,0 +1,555 @@ +#!/bin/sh +# +# Copyright (c) 2005 +# Bill Paul . All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Bill Paul. +# 4. Neither the name of the author nor the names of any co-contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGE. +# +# $FreeBSD: src/usr.sbin/ndiscvt/ndisgen.sh,v 1.6 2008/04/15 04:17:13 thompsa Exp $ +# + +header () { +clear +echo " ==================================================================" +echo " ------------------ Windows(r) driver converter -------------------" +echo " ==================================================================" +echo "" +} + +mainmenu() { +header +echo " This script is designed to guide you through the process" +echo " of converting a Windows(r) binary driver module and .INF" +echo " specification file into a DragonFly ELF kernel module for use" +echo " with the NDIS compatibility system." +echo "" +echo " The following options are available:" +echo "" +echo " 1] Learn about the NDIS compatibility system" +echo " 2] Convert individual firmware files" +echo " 3] Convert driver" +echo " 4] Exit" +echo "" +echo -n " Enter your selection here and press return: " +read KEYPRESS +return +} + + +help1 () { +header +echo " General information" +echo "" +echo " The NDIS compatibility system is designed to let you use Windows(r)" +echo " binary drivers for networking devices with DragonFly, in cases where" +echo " a native DragonFly driver is not available due to hardware manufacturer" +echo " oversight or stupidity. NDIS stands for Network Driver Interface" +echo " Standard, and refers to the programming model used to write Windows(r)" +echo " network drivers. (These are often called \"NDIS miniport\" drivers.)" +echo "" +echo " In order to use your network device in NDIS compatibility mode," +echo " you need the Windows(r) driver that goes with it. Also, the driver" +echo " must be compiled for the same architecture as the release of DragonFly" +echo " you have installed. At this time, the i386 and x86_64 architectures" +echo " are both supported. Note that you cannot use a Windows/i386 driver" +echo " with DragonFly/x86_64: you must obtain a Windows/x86_64 driver." +echo "" +echo -n " Press return to continue... " +read KEYPRESS +return +} + +help2() { +header +echo " Where to get drivers" +echo "" +echo " If you purchased your network card separately from your computer," +echo " there should have been a driver distribution CD included with the" +echo " card which contains Windows(r) drivers. The NDIS compatibility" +echo " system is designed to emulate the NDIS API of a couple of different" +echo " Windows(r) releases, however it works best with drivers designed" +echo " for NDIS 5.0 or later. Drivers distributed for Windows 2000 should" +echo " work; however, for best results you should use a driver designed" +echo " for Windows XP or Windows Server 2003." +echo "" +echo " If your card was supplied with your computer, or is a built-in device," +echo " drivers may have been included on a special driver bundle CD shipped" +echo " with the computer." +echo "" +echo " If you don't have a driver CD, you should be able to find a driver" +echo " kit on the card or computer vendor's web site." +echo "" +echo -n " Press return to continue... " +read KEYPRESS +return +} + +help3 () { +header +echo " What files do I need?" +echo "" +echo " In most cases, you will need only two files: a .INF file and a .SYS" +echo " file. The .INF file is a text file used by the Windows(r) installer to" +echo " perform the driver installation. It contains information that tells" +echo " the installer what devices the driver supports and what registry keys" +echo " should be created to control driver configuration. The .SYS file" +echo " is the actual driver executable code in Windows(r) Portable Executable" +echo " (PE) format. Note that sometimes the .INF file is supplied in Unicode" +echo " format. Unicode .INF files must be converted to ASCII form with the" +echo " iconv(1) utility before this installer script can use them." +echo " Occasionally, a driver may require firmware or register setup" +echo " files that are external to the main .SYS file. These are provided" +echo " on the same CD with the driver itself, and sometimes have a .BIN" +echo " extension, though they can be named almost anything. You will need" +echo " these additional files to make your device work with the NDIS" +echo " compatibility system as well." +echo "" +echo -n " Press return to continue... " +read KEYPRESS +return +} + +help4 () { +header +echo " How does it all work?" +echo "" +echo " The installer script uses the ndiscvt(1) utility to convert the .INF," +echo " .SYS and optional firmware files into a DragonFly kernel module" +echo " (.ko) file. This module can be loaded via the kldload(8) utility or" +echo " loaded automatically via the /boot/loader.conf file. The ndiscvt(1)" +echo " utility extracts the device ID information and registry key data" +echo " from the .INF file and converts it into a C header file. It also uses" +echo " the objcopy(1) utility to convert the .SYS file and optional firmware" +echo " files into ELF objects. The header file is compiled into a small C" +echo " stub file which contains a small amount of code to interface with" +echo " the DragonFly module system. This stub is linked together with the" +echo " converted ELF objects to form a DragonFly kernel module. A static ELF" +echo " object (.o) file is also created. This file can be linked into a" +echo " static kernel image for those who want/need a fully linked kernel" +echo " image (possibly for embedded bootstrap purposes, or just plain old" +echo " experimentation)." +echo "" +echo -n " Press return to continue... " +read KEYPRESS +return +} + +help5 () { +header +echo " Prerequisites" +echo "" +echo " Converting a driver requires the following utilities:" +echo "" +echo " - The DragonFly C compiler, cc(1) (part of the base install)." +echo " - The DragonFly linker, ld(1) (part of the base install)." +echo " - The objcopy(1) utility (part of the base install)." +echo " - The ndiscvt(1) utility (part of the base install)." +echo "" +echo " If you happen to end up with a .INF file that's in Unicode format," +echo " then you'll also need:" +echo "" +echo " - The iconv(1) utility (part of the base install)." +echo "" +echo -n " Press return to continue... " +read KEYPRESS +return +} + +infconv () { +header +echo " INF file validation" + +if [ -z "$INFPATH" ]; then + echo "" + echo "" + echo " A .INF file is most often provided as an ASCII file, however" + echo " files with multilanguage support are provided in Unicode format." + echo " Please type in the path to your .INF file now." + echo "" + echo -n " > " + read INFPATH +fi + +if [ ${INFPATH} ] && [ -e ${INFPATH} ]; then + INFTYPE=`${EGREP} -i -c "Signature|.S.i.g.n.a.t.u.r.e" ${INFPATH}` + if [ ${INFTYPE} -le 0 ]; then + echo "" + echo " I don't recognize this file format. It may not be a valid .INF file." + echo "" + echo -n " Press enter to try again, or ^C to quit. " + read KEYPRESS + INFPATH="" + return + fi + + INFTYPE=`${EGREP} -i -c "Class.*=.*Net" ${INFPATH}` + if [ ${INFTYPE} -gt 0 ]; then + echo "" + echo " This .INF file appears to be ASCII." + echo "" + echo -n " Press return to continue... " + read KEYPRESS + return + fi + + INFTYPE=`${EGREP} -i -c ".C.l.a.s.s.*=.*N.e.t" ${INFPATH}` + if [ ${INFTYPE} -gt 0 ]; then + echo "" + echo " This .INF file appears to be Unicode." + if [ -e ${ICONVPATH} ]; then + echo " Trying to convert to ASCII..." + ${ICONVPATH} -f utf-16 -t utf-8 ${INFPATH} > ${INFFILE} + INFPATH=${INFFILE} + echo " Done." + echo "" + echo -n " Press return to continue... " + read KEYPRESS + else + echo " The iconv(1) utility does not appear to be installed." + echo " Please install this utility or convert the .INF file" + echo " to ASCII and run this utility again." + echo "" + exit + fi + return + fi + + echo "" + echo " I don't recognize this file format. It may not be a valid .INF file." + echo "" + echo -n " Press enter to try again, or ^C to quit. " + read KEYPRESS + INFPATH="" +else + echo "" + echo " The file '${INFPATH}' was not found." + echo "" + echo -n " Press enter to try again, or ^C to quit. " + read KEYPRESS + INFPATH="" +fi +return +} + +sysconv() { +header +echo " Driver file validation" + +if [ ! -r "$SYSPATH" ]; then + echo "" + echo "" + echo " Now you need to specify the name of the Windows(r) driver .SYS" + echo " file for your device. Note that if you are running DragonFly/x86_64," + echo " then you must provide a driver that has been compiled for the" + echo " 64-bit Windows(r) platform. If a 64-bit driver is not available" + echo " for your device, you must install DragonFly/i386 and use the" + echo " 32-bit driver instead." + echo "" + echo " Please type in the path to the Windows(r) driver .SYS file now." + echo "" + echo -n " > " + read SYSPATH +fi + +if [ ${SYSPATH} ] && [ -e ${SYSPATH} ]; then + SYSTYPE=`${FILE} ${SYSPATH}` + + case ${SYSTYPE} in + *Windows*) + echo "" + echo " This .SYS file appears to be in Windows(r) PE format." + echo "" + echo -n " Press return to continue... " + read KEYPRESS + SYSBASE=`${BASENAME} ${SYSPATH} | ${TR} '.' '_'` + ;; + *) + echo "" + echo " I don't recognize this file format. It may not be a valid .SYS file." + echo "" + + echo -n " Press enter to try again, or ^C to quit. " + read KEYPRESS + SYSPATH="" + ;; + esac +else + echo "" + echo " The file '${SYSPATH}' was not found." + echo "" + echo -n " Press enter to try again, or ^C to quit. " + read KEYPRESS + SYSPATH="" +fi +return +} + +ndiscvt() { +header +echo " Driver file conversion" +echo "" +echo " The script will now try to convert the .INF and .SYS files" +echo " using the ndiscvt(1) utility. This utility can handle most" +echo " .INF files; however, occasionally it can fail to parse some files" +echo " due to subtle syntax issues: the .INF syntax is very complex," +echo " and the Windows(r) parser will sometimes allow files with small" +echo " syntax errors to be processed correctly which ndiscvt(1) will" +echo " not. If the conversion fails, you may have to edit the .INF" +echo " file by hand to remove the offending lines." +echo "" +echo -n " Press enter to try converting the files now: " +read KEYPRESS +if ! ${NDISCVT} -i ${INFPATH} -s ${SYSPATH} -O -o ${DNAME}.h > /dev/null; then + echo "CONVERSION FAILED" + exit +else + echo "" + echo " Conversion was successful." + echo "" + echo -n " Press enter to continue... " + read KEYPRESS +fi +return +} + +firmcvt() { + while : ; do +header +echo " Firmware file conversion" +echo "" +echo " If your driver uses additional firmware files, please list them" +echo " below. When you're finished, just press enter to continue. (If your" +echo " driver doesn't need any extra firmware files, just press enter" +echo " to move to the next step.)" +echo "" + echo -n " > " + read FIRMPATH + + if [ ${FIRMPATH} ]; then + if [ ! -e ${FIRMPATH} ]; then + echo "" + echo " The file '${FIRMPATH}' was not found" + echo "" + echo -n " Press enter to try again, or ^C to quit. " + read KEYPRESS + continue + fi + if ! ${NDISCVT} -f ${FIRMPATH} > /dev/null; then + echo "" + echo "CONVERSION FAILED" + else + echo "" + echo " Conversion was successful." + echo "" + FRMBASE=`${BASENAME} ${FIRMPATH}` + FRMBASE="${FRMBASE}.o" + FRMLIST="${FRMLIST} ${FRMBASE}" + fi + echo -n " Press enter to continue... " + read KEYPRESS + else + break + fi + done + +header +echo "" +echo " List of files converted firmware files:" +echo "" +for i in ${FRMLIST} +do + echo " "$i +done +echo "" +echo -n " Press enter to continue... " +read KEYPRESS +return +} + +drvgen () { +header +echo " Kernel module generation" +echo "" +echo "" +echo " The script will now try to generate the kernel driver module." +echo " This is the last step. Once this module is generated, you should" +echo " be able to load it just like any other DragonFly driver module." +echo "" +echo " Press enter to compile the stub module and generate the driver" +echo -n " module now: " +read KEYPRESS +echo "" +echo -n " Generating Makefile... " +echo ".PATH: ${PWD} ${STUBPATH}" > ${MAKEFILE} +echo "KMOD= ${SYSBASE}" >> ${MAKEFILE} +echo "SRCS+= ${STUBFILE} ${DNAME}.h bus_if.h device_if.h" >> ${MAKEFILE} +echo "OBJS+=${FRMLIST} ${DNAME}.o" >> ${MAKEFILE} +echo "CFLAGS+= \\" >> ${MAKEFILE} +echo " -DDRV_DATA_START=ndis_${SYSBASE}_drv_data_start \\" >> ${MAKEFILE} +echo " -DDRV_NAME=ndis_${SYSBASE} \\" >> ${MAKEFILE} +echo " -DDRV_DATA_END=ndis_${SYSBASE}_drv_data_end" >> ${MAKEFILE} +echo "CLEANFILES+= \\" >> ${MAKEFILE} +echo " ${INFFILE} \\" >> ${MAKEFILE} +echo " ${DNAME}.h \\" >> ${MAKEFILE} +echo " ${DNAME}.o" >> ${MAKEFILE} +echo ".include " >> ${MAKEFILE} +if [ -f ${MAKEFILE} ]; then + echo "done." +else + echo "generating Makefile failed. Exiting." + echo "" + exit +fi +echo -n " Building kernel module... " +echo "" > bus_if.h +echo "" > device_if.h +if ! ${MAKE} -f ${MAKEFILE} depend > /dev/null; then + echo "build failed. Exiting." + echo "" + exit +fi +if ! ${MAKE} -f ${MAKEFILE} all > /dev/null; then + echo "build failed. Exiting." + echo "" + exit +else + if [ -f ${SYSBASE}.ko ]; then + ${MV} ${SYSBASE}.ko ${SYSBASE}.kmod + echo "done." + else + echo "build failed. Exiting." + echo "" + exit + fi +fi +echo -n " Cleaning up... " +if ! ${MAKE} -f ${MAKEFILE} clean cleandepend > /dev/null; then + echo "cleanup failed. Exiting." + echo "" + exit +else + echo "done." +fi +${RM} ${MAKEFILE} +${MV} ${SYSBASE}.kmod ${SYSBASE}.ko +echo "" +echo " The file ${SYSBASE}.ko has been successfully generated." +echo " You can kldload this module to get started." +echo "" +echo -n " Press return to exit. " +read KEYPRESS +echo "" +echo "" +return +} + +convert_driver () { + while : ; do + infconv + if [ ${INFPATH} ]; then + break + fi + done + + while : ; do + sysconv + if [ ${SYSPATH} ]; then + break + fi + done + + ndiscvt + firmcvt + drvgen + return +} + +ICONVPATH=/usr/bin/iconv +NDISCVT=/usr/sbin/ndiscvt +STUBPATH=/usr/share/misc +STUBFILE=windrv_stub.c +DNAME=windrv +CP=/bin/cp +MV=/bin/mv +RM=/bin/rm +TR=/usr/bin/tr +FILE=/usr/bin/file +EGREP=/usr/bin/egrep +MAKE=/usr/bin/make +BASENAME=/usr/bin/basename +TOUCH=/usr/bin/touch +MKTEMP=/usr/bin/mktemp + +MAKEFILE=`${MKTEMP} /tmp/Makefile.XXXXXX` +INFFILE=`${MKTEMP} /tmp/ascii_inf.XXXXXX` + +INFPATH="" +FRMLIST="" +SYSPATH="" +SYSBASE="" +FRMBASE="" + +if [ -r "$1" -a -r "$2" ]; then + # Looks like the user supplied .INF and .SYS files on the command line + INFPATH=$1 + SYSPATH=$2 + convert_driver && exit 0 +fi + +while : ; do + mainmenu + case ${KEYPRESS} in + 1) + help1 + help2 + help3 + help4 + help5 + ;; + 2) + firmcvt + ;; + 3) + convert_driver + ;; + 4) + header + echo "" + echo " Be seeing you!" + echo "" + exit + ;; + *) + header + echo "" + echo -n " Sorry, I didn't understand that. Press enter to try again: " + read KEYPRESS + ;; + esac +done +exit diff --git a/usr.sbin/ndiscvt/windrv_stub.c b/usr.sbin/ndiscvt/windrv_stub.c new file mode 100644 index 0000000000..cee29b2634 --- /dev/null +++ b/usr.sbin/ndiscvt/windrv_stub.c @@ -0,0 +1,259 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/usr.sbin/ndiscvt/windrv_stub.c,v 1.3 2008/12/27 08:03:32 weongyo Exp $ + */ + +#include +#include +#include +#include +#include + +#include + +#define NDIS_REGVALS + +struct ndis_cfg { + char *nc_cfgkey; + char *nc_cfgdesc; + char nc_val[256]; + int nc_idx; +}; + +typedef struct ndis_cfg ndis_cfg; + +#include "windrv.h" + +struct ndis_pci_type { + uint16_t ndis_vid; + uint16_t ndis_did; + uint32_t ndis_subsys; + char *ndis_name; +}; + +struct ndis_pccard_type { + const char *ndis_vid; + const char *ndis_did; + char *ndis_name; +}; + +struct ndis_usb_type { + uint16_t ndis_vid; + uint16_t ndis_did; + char *ndis_name; +}; + +#ifdef NDIS_PCI_DEV_TABLE +static struct ndis_pci_type ndis_devs_pci[] = { + NDIS_PCI_DEV_TABLE + { 0, 0, 0, NULL } +}; +#endif + +#ifdef NDIS_PCMCIA_DEV_TABLE +static struct ndis_pccard_type ndis_devs_pccard[] = { + NDIS_PCMCIA_DEV_TABLE + { NULL, NULL, NULL } +}; +#endif + +#ifdef NDIS_USB_DEV_TABLE +static struct ndis_usb_type ndis_devs_usb[] = { + NDIS_USB_DEV_TABLE + { 0, 0, NULL } +}; +#endif + +enum interface_type { + InterfaceTypeUndefined = -1, + Internal, + Isa, + Eisa, + MicroChannel, + TurboChannel, + PCIBus, + VMEBus, + NuBus, + PCMCIABus, + CBus, + MPIBus, + MPSABus, + ProcessorInternal, + InternalPowerBus, + PNPISABus, + PNPBus, + MaximumInterfaceType +}; + +typedef enum interface_type interface_type; + +/* + * XXX + * Ordinarily, device_probe_desc is defined in device_if.h, which + * is created from device_if.m. The problem is, the latter file + * is only available if you have the kernel source code installed, + * and not all users choose to install it. I'd like to let people + * load Windows driver modules with the minimal amount of hassle + * and dependencies. wants both device_if.h and bus_if.h + * to be defined, but it turns out the only thing we really need + * to get this module compiled is device_probe_desc, so we define + * that here, and let the build script create empty copies of + * device_if.h and bus_if.h to make the compiler happy. + */ + +extern struct kobjop_desc device_probe_desc; +typedef int device_probe_t(device_t dev); + +extern int windrv_load(module_t, vm_offset_t, size_t, + interface_type, void *, void *); +extern int windrv_unload(module_t, vm_offset_t, size_t); + +#ifndef DRV_DATA_START +#define DRV_DATA_START UNDEF_START +#endif + +#ifndef DRV_DATA_END +#define DRV_DATA_END UNDEF_END +#endif + +#ifndef DRV_NAME +#define DRV_NAME UNDEF_NAME +#endif + +extern uint8_t DRV_DATA_START; +extern uint8_t DRV_DATA_END; + +/* + * The following is stub code that makes it look as though we want + * to be a child device of all the buses that our supported devices + * might want to attach to. Our probe routine always fails. The + * reason we need this code is so that loading an ELF-ified Windows + * driver module will trigger a bus reprobe. + */ + +#define MODULE_DECL(x) \ + MODULE_DEPEND(x, ndis, 1, 1, 1); \ + MODULE_DEPEND(x, if_ndis, 1, 1, 1) + +MODULE_DECL(DRV_NAME); + +static int windrv_probe(device_t); +static int windrv_modevent(module_t, int, void *); +static int windrv_loaded = 0; + +static device_method_t windrv_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, windrv_probe), + + { 0, 0 } +}; + +static driver_t windrv_driver = { + "windrv_stub", + windrv_methods, + 0 +}; + +static devclass_t windrv_devclass; + +#define DRIVER_DECL(x) \ + DRIVER_MODULE(x, pci, windrv_driver, \ + windrv_devclass, windrv_modevent, NULL); \ + DRIVER_MODULE(x, cardbus, windrv_driver, \ + windrv_devclass, windrv_modevent, NULL); \ + DRIVER_MODULE(x, pccard, windrv_driver, \ + windrv_devclass, windrv_modevent, NULL); \ + DRIVER_MODULE(x, uhub, windrv_driver, \ + windrv_devclass, windrv_modevent, NULL); \ + MODULE_VERSION(x, 1) + +DRIVER_DECL(DRV_NAME); + +static int +windrv_probe(device_t dev) +{ + return (ENXIO); +} + +static int +windrv_modevent(module_t mod, int cmd, void *arg) +{ + int drv_data_len; + int error = 0; + vm_offset_t drv_data_start; + vm_offset_t drv_data_end; + + drv_data_start = (vm_offset_t)&DRV_DATA_START; + drv_data_end = (vm_offset_t)&DRV_DATA_END; + + drv_data_len = drv_data_end - drv_data_start; + switch (cmd) { + case MOD_LOAD: + windrv_loaded++; + if (windrv_loaded > 1) + break; +#ifdef NDIS_PCI_DEV_TABLE + windrv_load(mod, drv_data_start, drv_data_len, PCIBus, + ndis_devs_pci, &ndis_regvals); +#endif +#ifdef NDIS_PCMCIA_DEV_TABLE + windrv_load(mod, drv_data_start, drv_data_len, PCMCIABus, + ndis_devs_pccard, &ndis_regvals); +#endif +#ifdef NDIS_USB_DEV_TABLE + windrv_load(mod, drv_data_start, drv_data_len, PNPBus, + ndis_devs_usb, &ndis_regvals); +#endif + break; + case MOD_UNLOAD: + windrv_loaded--; + if (windrv_loaded > 0) + break; +#ifdef NDIS_PCI_DEV_TABLE + windrv_unload(mod, drv_data_start, drv_data_len); +#endif +#ifdef NDIS_PCMCIA_DEV_TABLE + windrv_unload(mod, drv_data_start, drv_data_len); +#endif +#ifdef NDIS_USB_DEV_TABLE + windrv_unload(mod, drv_data_start, drv_data_len); +#endif + break; + case MOD_SHUTDOWN: + break; + default: + error = EINVAL; + break; + } + + return (error); +}