From matt@gsicomp.on.ca  Sat Nov 23 00:00:37 2002
Return-Path: <matt@gsicomp.on.ca>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 4199337B401
	for <FreeBSD-gnats-submit@freebsd.org>; Sat, 23 Nov 2002 00:00:37 -0800 (PST)
Received: from tomts22-srv.bellnexxia.net (tomts22.bellnexxia.net [209.226.175.184])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 6AB9B43E6E
	for <FreeBSD-gnats-submit@freebsd.org>; Sat, 23 Nov 2002 00:00:36 -0800 (PST)
	(envelope-from matt@gsicomp.on.ca)
Received: from xena.gsicomp.on.ca ([65.95.176.217])
          by tomts22-srv.bellnexxia.net
          (InterMail vM.5.01.04.19 201-253-122-122-119-20020516) with ESMTP
          id <20021123080035.KUCP19235.tomts22-srv.bellnexxia.net@xena.gsicomp.on.ca>;
          Sat, 23 Nov 2002 03:00:35 -0500
Received: from dhcp2.gsicomp.on.ca (dhcp2.gsicomp.on.ca [192.168.0.11])
	by xena.gsicomp.on.ca (8.11.3/8.11.3) with ESMTP id gAN80Yp50001;
	Sat, 23 Nov 2002 03:00:35 -0500 (EST)
	(envelope-from matt@dhcp2.gsicomp.on.ca)
Received: (from matt@localhost)
	by dhcp2.gsicomp.on.ca (8.12.6/8.12.6/Submit) id gAN80Wig000699;
	Sat, 23 Nov 2002 03:00:32 -0500 (EST)
	(envelope-from matt)
Message-Id: <200211230800.gAN80Wig000699@dhcp2.gsicomp.on.ca>
Date: Sat, 23 Nov 2002 03:00:32 -0500 (EST)
From: Matt Emmerton <matt@gsicomp.on.ca>
Reply-To: Matt Emmerton <matt@gsicomp.on.ca>
To: FreeBSD-gnats-submit@freebsd.org, chris@unixpages.org,
	imp@bsdimp.com
Cc:
Subject: wi0 device_probe_and_attach returns 6
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         45637
>Category:       kern
>Synopsis:       wi0 device_probe_and_attach returns 6
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Nov 23 00:10:01 PST 2002
>Closed-Date:    Wed Jan 01 00:16:40 PST 2003
>Last-Modified:  Wed Jan 01 00:16:40 PST 2003
>Originator:     Matt Emmerton
>Release:        FreeBSD 5.0-CURRENT i386
>Organization:
>Environment:
System: FreeBSD laptop.gsicomp.on.ca 5.0-CURRENT FreeBSD 5.0-CURRENT #42: Sat Nov 23 02:12:36 EST 2002 root@laptop.gsicomp.on.ca:/usr/obj/usr/src/sys/LAPTOP.20020914.01 i386

>Description:

> In message: <20021123004026.GE1766@unixpages.org>
>             Christian Brueffer <chris@unixpages.org> writes:
> : Hi,
> :
> : just installed DP2 on my IBM Thinkpad R32 and updated to the latest
> : -CURRENT.
> : My wlan device (Prism 2.5, internal, no pccard) fails at bootup:
> :
> : wi0: <Intersil Prism2.5> mem 0xf8000000-0xf8000fff irq 11 at device 7.0
on pci2
> : wi0: No Mem space on prism2.5?
> : device_probe_and_attach: wi0 attach returned 6
> :
> : Used to work fine with 4.7-STABLE.
> :
> : Any ideas?
>
> Nope.  More details?

The same wlan device has been broken for me on -CURRENT for a couple of
months now on a ThinkPad T23.  I never paid it much attention since I have a
PCMCIA wireless NIC which works fine.

Here's some useful output from boot -v:

pcib2: <PCIBIOS PCI-PCI bridge> at device 30.0 on pci0
pcib2:   secondary bus     2
pcib2:   subordinate bus   8
pcib2:   I/O decode     0x3000-0x7fff
pcib2:   memory decode 0xc02000000-0xcfffffff
pcib2:   prefetched decode 0xec000000-0xf7fffffff
pci2: <PCI bus> on pcib2
...
wi0:  <Intersil Prism2.5> mem 0xec000000-0xec000fff irq 11 at device 2.0 on
pci2
pcib2: device wi0 requesting range 0xec000000-0xec000fff (**)
pcib2: device wi0 requested unsupported memory range 0x0-0xcfffffff
(decoding 0xc0200000-0xcfffffff, 0xec000000-0xf7ffffff)
wi0: No Mem space on prism2.5?
device_probe_and_attach: wi0 attach returned 6

The line marked with (**) is from my own debug code in
sys/dev/pci/pci_pci.c, positioned upon entry to the pcib_alloc_resource()
function.

The code inside of #ifndef PCI_ALLOW_UNSUPPORTED_IO_RANGE (shouldn't that be
MEM_RANGE?) looks suspect since it doesn't check the start/end parameters
against the prefetched decode memory range.  The attached patch checks fixes
this, as well as cleans up the #ifdefs.

--
Matt Emmerton

>How-To-Repeat:

Boot a ThinkPad R32 or T23 with an integrated wlan card.

>Fix:

--- sys/dev/pci/pci_pci.c.orig	Sat Nov 23 01:34:54 2002
+++ sys/dev/pci/pci_pci.c	Sat Nov 23 02:16:15 2002
@@ -310,7 +310,9 @@
 			      " (decoding 0x%x-0x%x)\n",
 			      device_get_name(child), device_get_unit(child), start, end,
 			      sc->iobase, sc->iolimit);
+#ifndef PCI_ALLOW_UNSUPPORTED_IO_RANGE
 		return (NULL);
+#endif
 	    }
 	    if (bootverbose)
 		device_printf(sc->dev, "device %s%d requested decoded I/O range 0x%lx-0x%lx\n",
@@ -325,18 +327,34 @@
 	     */
 	case SYS_RES_MEMORY:
 	    if (!pcib_is_isa_mem(start)) {
-#ifndef PCI_ALLOW_UNSUPPORTED_IO_RANGE
-		if (start < sc->membase && end >= sc->membase)
-		    start = sc->membase;
-		if (end > sc->memlimit)
-		    end = sc->memlimit;
-		if (end < start)
-		    start = 0;
+#ifndef PCI_ALLOW_UNSUPPORTED_MEM_RANGE
+		if (((start < sc->membase)  || (end > sc->memlimit)) &&
+                    ((start < sc->pmembase) || (end > sc->pmemlimit))) {
+
+		    /* We've been given a request that falls outside   */
+		    /* of both the memory decode and prefetched decode */
+		    /* ranges. Force it to the memory decode range,    */
+		    /* but for no reason in particular.                */
+
+		    if (start < sc->membase && end >= sc->membase) {
+			start = sc->membase;
+                    }
+		    if (end > sc->memlimit) {
+			end = sc->memlimit;
+                    }
+		    if (end < start) {
+			start = 0;
+                    }
+		}
 #else
 		if (start < sc->membase && end > sc->membase)
 		    printf("start (%lx) < sc->membase (%x)\n", start, sc->membase);
+		if (start < sc->pmembase && end > sc->pmembase)
+		    printf("start (%lx) < sc->pmembase (%x)\n", start, sc->pmembase);
 		if (end > sc->memlimit)
 		    printf("end (%lx) > sc->memlimit (%x)\n", end, sc->memlimit);
+		if (end > sc->pmemlimit)
+		    printf("end (%lx) > sc->pmemlimit (%x)\n", end, sc->pmemlimit);
 		if (end < start) 
 		    printf("end (%lx) < start (%lx)\n", end, start);
 #endif
@@ -351,7 +369,7 @@
 			device_get_name(child), device_get_unit(child), start,
 			end, sc->membase, sc->memlimit, sc->pmembase,
 			sc->pmemlimit);
-#ifndef PCI_ALLOW_UNSUPPORTED_IO_RANGE
+#ifndef PCI_ALLOW_UNSUPPORTED_MEM_RANGE
 		return(NULL);
 #endif
 	    }

>Release-Note:
>Audit-Trail:

From: Giorgos Keramidas <keramida@freebsd.org>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: kern/45637: kern/45637: wi0 device_probe_and_attach returns 6
Date: Sun, 24 Nov 2002 18:05:07 +0200

 	Add to audit trail multiple followups that were misfiled...
 
 
 :=====================================================================
 :
 : Message-Id: <20021123.012811.85895040.imp@bsdimp.com>
 : In-Reply-To: <200211230800.gAN80Wig000699@dhcp2.gsicomp.on.ca>
 : Date: Sat, 23 Nov 2002 01:28:11 -0700 (MST)
 : From: "M. Warner Losh" <imp@bsdimp.com>
 :
 : This patch isn't quite right, but I can see it might work for you.
 : However, it does show that I've been ignoring the pmem* part of the
 : bridge and need to correct that.  Looks like the following patch will
 : do the trick.  This might let us get rid of unsupported ranges, or at
 : least reduce its need.  This should also make the nvida people happier
 : too, I hope, since they need this since their card is mapped in
 : non-prefetchable region.
 :
 : # still need fix it so drivers can request prefetch or non-prefetch
 : # memory on their own.  Cardbus needs this, for example.
 :
 : ==== //depot/user/imp/newcard/dev/pci/pci_pci.c#8 - /dell/imp/p4/newcard/src/sys/dev/pci/pci_pci.c ====
 : @@ -282,6 +282,7 @@
 :  		    u_long start, u_long end, u_long count, u_int flags)
 :  {
 :      struct pcib_softc	*sc = device_get_softc(dev);
 : +    int ok;
 :  
 :      /*
 :       * If this is a "default" allocation against this rid, we can't work
 : @@ -299,14 +300,18 @@
 :  	 */
 :  	switch (type) {
 :  	case SYS_RES_IOPORT:
 : +	    ok = 1;
 :  	    if (!pcib_is_isa_io(start)) {
 :  		if (!pci_allow_unsupported_io_range) {
 :  		    if (start < sc->iobase)
 :  			start = sc->iobase;
 :  		    if (end > sc->iolimit)
 :  			end = sc->iolimit;
 : -		    if (end < start)
 : +		    if (end < start) {
 :  			start = 0;
 : +			end = 0;
 : +			ok = 0;
 : +		    }
 :  		} else {
 :  		    if (start < sc->iobase)
 :  			printf("start (%lx) < sc->iobase (%x)\n", start,
 : @@ -318,12 +323,11 @@
 :  			printf("end (%lx) < start (%lx)\n", end, start);
 :  		}
 :  	    }
 : -	    if (!pcib_is_isa_io(start) &&
 : -	      ((start < sc->iobase) || (end > sc->iolimit))) {
 : -		device_printf(dev, "device %s%d requested unsupported I/O range 0x%lx-0x%lx"
 : -			      " (decoding 0x%x-0x%x)\n",
 : -			      device_get_name(child), device_get_unit(child), start, end,
 : -			      sc->iobase, sc->iolimit);
 : +	    if (!ok) {
 : +		device_printf(dev, "device %s%d requested unsupported I/O "
 : +		  "range 0x%lx-0x%lx (decoding 0x%x-0x%x)\n",
 : +		  device_get_name(child), device_get_unit(child), start, end,
 : +		  sc->iobase, sc->iolimit);
 :  		return (NULL);
 :  	    }
 :  	    if (bootverbose)
 : @@ -332,20 +336,35 @@
 :  	    break;
 :  
 :  	    /*
 : -	     * XXX will have to decide whether the device making the request is asking
 : -	     *     for prefetchable memory or not.  If it's coming from another bridge
 : -	     *     down the line, do we assume not, or ask the bridge to pass in another
 : -	     *     flag as the request bubbles up?
 : +	     * XXX will have to decide whether the device making the request
 : +	     * is asking for prefetchable memory or not.  If it's coming
 : +	     * from another bridge down the line, do we assume not, or ask
 : +	     * the bridge to pass in another flag as the request bubbles up?
 :  	     */
 :  	case SYS_RES_MEMORY:
 : +	    ok = 1;
 :  	    if (!pcib_is_isa_mem(start)) {
 :  		if (!pci_allow_unsupported_io_range) {
 : -		    if (start < sc->membase && end >= sc->membase)
 : -			start = sc->membase;
 : -		    if (end > sc->memlimit)
 : -			end = sc->memlimit;
 : -		    if (end < start)
 : -			start = 0;
 : +		    ok = 0;
 : +		    if (sc->membase > sc->memlimit)
 : +			ok = ok || 
 : +			  (start >= sc->membase && end <= sc->memlimit);
 : +		    if (sc->pmembase > sc->pmemlimit)
 : +			ok = ok ||
 : +			  (start >= sc->pmembase && end <= sc->pmemlimit);
 : +		    if (!ok) {
 : +			/* XXX Assume non-prefetchable */
 : +			if (start < sc->membase)
 : +			    start = sc->membase;
 : +			if (end > sc->memlimit)
 : +			    end = sc->memlimit;
 : +			ok = true;
 : +			if (end < start) {
 : +			    start = 0;
 : +			    end = 0;
 : +			    ok = false;
 : +			}
 : +		    }
 :  		} else {
 :  		    if (start < sc->membase && end > sc->membase)
 :  			printf("start (%lx) < sc->membase (%x)\n",
 : @@ -357,19 +376,15 @@
 :  			printf("end (%lx) < start (%lx)\n", end, start);
 :  		}
 :  	    }
 : -	    if (!pcib_is_isa_mem(start) &&
 : -	        (((start < sc->membase) || (end > sc->memlimit)) &&
 : -		((start < sc->pmembase) || (end > sc->pmemlimit)))) {
 : -		if (bootverbose)
 : -		    device_printf(dev,
 : -			"device %s%d requested unsupported memory range "
 : -			"0x%lx-0x%lx (decoding 0x%x-0x%x, 0x%x-0x%x)\n",
 : -			device_get_name(child), device_get_unit(child), start,
 : -			end, sc->membase, sc->memlimit, sc->pmembase,
 : -			sc->pmemlimit);
 : -		if (!pci_allow_unsupported_io_range)
 : -		    return (NULL);
 : -	    }
 : +	    if (!ok && bootverbose)
 : +		device_printf(dev,
 : +		  "device %s%d requested unsupported memory range "
 : +		  "0x%lx-0x%lx (decoding 0x%x-0x%x, 0x%x-0x%x)\n",
 : +		  device_get_name(child), device_get_unit(child), start,
 : +		  end, sc->membase, sc->memlimit, sc->pmembase,
 : +		  sc->pmemlimit);
 : +	    if (!ok)
 : +		return (NULL);
 :  	    if (bootverbose)
 :  		device_printf(sc->dev, "device %s%d requested decoded memory range 0x%lx-0x%lx\n",
 :  			      device_get_name(child), device_get_unit(child), start, end);
 :
 :=====================================================================
 :
 : Message-Id: <20021123.012850.91094754.imp@bsdimp.com>
 : In-Reply-To: <200211230800.gAN80Wig000699@dhcp2.gsicomp.on.ca>
 : Date: Sat, 23 Nov 2002 01:28:50 -0700 (MST)
 : From: "M. Warner Losh" <imp@bsdimp.com>
 :
 : P.S.  s/true/1/g;s/false/0/g
 :
 :=====================================================================
 :
 : Message-Id: <20021123.013939.109040651.imp@bsdimp.com>
 : In-Reply-To: <200211230800.gAN80Wig000699@dhcp2.gsicomp.on.ca>
 : Date: Sat, 23 Nov 2002 01:39:39 -0700 (MST)
 : From: "M. Warner Losh" <imp@bsdimp.com>
 :
 : Actually, I think this patch has all the comparisons going the right
 : way.
 :
 : Warner
 :
 : --- //depot/user/imp/freebsd-imp/sys/dev/pci/pci_pci.c	2002/11/14 23:02:51
 : +++ //depot/user/imp/newcard/dev/pci/pci_pci.c	2002/11/23 00:35:32
 : @@ -282,6 +282,7 @@
 :  		    u_long start, u_long end, u_long count, u_int flags)
 :  {
 :      struct pcib_softc	*sc = device_get_softc(dev);
 : +    int ok;
 :  
 :      /*
 :       * If this is a "default" allocation against this rid, we can't work
 : @@ -299,14 +300,18 @@
 :  	 */
 :  	switch (type) {
 :  	case SYS_RES_IOPORT:
 : +	    ok = 1;
 :  	    if (!pcib_is_isa_io(start)) {
 :  		if (!pci_allow_unsupported_io_range) {
 :  		    if (start < sc->iobase)
 :  			start = sc->iobase;
 :  		    if (end > sc->iolimit)
 :  			end = sc->iolimit;
 : -		    if (end < start)
 : +		    if (end < start) {
 :  			start = 0;
 : +			end = 0;
 : +			ok = 0;
 : +		    }
 :  		} else {
 :  		    if (start < sc->iobase)
 :  			printf("start (%lx) < sc->iobase (%x)\n", start,
 : @@ -318,12 +323,11 @@
 :  			printf("end (%lx) < start (%lx)\n", end, start);
 :  		}
 :  	    }
 : -	    if (!pcib_is_isa_io(start) &&
 : -	      ((start < sc->iobase) || (end > sc->iolimit))) {
 : -		device_printf(dev, "device %s%d requested unsupported I/O range 0x%lx-0x%lx"
 : -			      " (decoding 0x%x-0x%x)\n",
 : -			      device_get_name(child), device_get_unit(child), start, end,
 : -			      sc->iobase, sc->iolimit);
 : +	    if (!ok) {
 : +		device_printf(dev, "device %s%d requested unsupported I/O "
 : +		  "range 0x%lx-0x%lx (decoding 0x%x-0x%x)\n",
 : +		  device_get_name(child), device_get_unit(child), start, end,
 : +		  sc->iobase, sc->iolimit);
 :  		return (NULL);
 :  	    }
 :  	    if (bootverbose)
 : @@ -332,44 +336,64 @@
 :  	    break;
 :  
 :  	    /*
 : -	     * XXX will have to decide whether the device making the request is asking
 : -	     *     for prefetchable memory or not.  If it's coming from another bridge
 : -	     *     down the line, do we assume not, or ask the bridge to pass in another
 : -	     *     flag as the request bubbles up?
 : +	     * XXX will have to decide whether the device making the request
 : +	     * is asking for prefetchable memory or not.  If it's coming
 : +	     * from another bridge down the line, do we assume not, or ask
 : +	     * the bridge to pass in another flag as the request bubbles up?
 :  	     */
 :  	case SYS_RES_MEMORY:
 : +	    ok = 1;
 :  	    if (!pcib_is_isa_mem(start)) {
 : +		ok = 0;
 : +		if (sc->membase > 0 && sc->membase < sc->memlimit)
 : +		    ok = ok || (start >= sc->membase && end <= sc->memlimit);
 : +		if (sc->membase > 0 && sc->pmembase < sc->pmemlimit)
 : +		    ok = ok || (start >= sc->pmembase && end <= sc->pmemlimit);
 :  		if (!pci_allow_unsupported_io_range) {
 : -		    if (start < sc->membase && end >= sc->membase)
 : -			start = sc->membase;
 : -		    if (end > sc->memlimit)
 : -			end = sc->memlimit;
 : +		    if (!ok) {
 : +			/* XXX Assume non-prefetchable */
 : +			if (start < sc->membase)
 : +			    start = sc->membase;
 : +			if (end > sc->memlimit)
 : +			    end = sc->memlimit;
 : +			ok = 1;
 : +			if (end < start) {
 : +			    start = 0;
 : +			    end = 0;
 : +			    ok = 0;
 : +			}
 : +		    }
 : +		} else if (!ok) {
 : +		    ok = 1;	/* pci_allow_unsupported_ranges -> always ok */
 : +		    if (sc->membase > 0 && sc->membase < sc->memlimit) {
 : +			if (start < sc->membase)
 : +			    printf("start (%lx) < sc->membase (%x)\n",
 : +			      start, sc->membase);
 : +			if (end > sc->memlimit)
 : +			    printf("end (%lx) > sc->memlimit (%x)\n",
 : +			      end, sc->memlimit);
 : +		    }
 : +		    if (sc->pmembase > 0 && sc->pmembase < sc->pmemlimit) {
 : +			if (start < sc->pmembase)
 : +			    printf("start (%lx) < sc->pmembase (%x)\n",
 : +			      start, sc->pmembase);
 : +			if (end > sc->pmemlimit)
 : +			    printf("end (%lx) > sc->pmemlimit (%x)\n",
 : +			      end, sc->memlimit);
 : +		    }
 :  		    if (end < start)
 : -			start = 0;
 : -		} else {
 : -		    if (start < sc->membase && end > sc->membase)
 : -			printf("start (%lx) < sc->membase (%x)\n",
 : -				start, sc->membase);
 : -		    if (end > sc->memlimit)
 : -			printf("end (%lx) > sc->memlimit (%x)\n",
 : -				end, sc->memlimit);
 : -		    if (end < start)
 :  			printf("end (%lx) < start (%lx)\n", end, start);
 :  		}
 :  	    }
 : -	    if (!pcib_is_isa_mem(start) &&
 : -	        (((start < sc->membase) || (end > sc->memlimit)) &&
 : -		((start < sc->pmembase) || (end > sc->pmemlimit)))) {
 : -		if (bootverbose)
 : -		    device_printf(dev,
 : -			"device %s%d requested unsupported memory range "
 : -			"0x%lx-0x%lx (decoding 0x%x-0x%x, 0x%x-0x%x)\n",
 : -			device_get_name(child), device_get_unit(child), start,
 : -			end, sc->membase, sc->memlimit, sc->pmembase,
 : -			sc->pmemlimit);
 : -		if (!pci_allow_unsupported_io_range)
 : -		    return (NULL);
 : -	    }
 : +	    if (!ok && bootverbose)
 : +		device_printf(dev,
 : +		  "device %s%d requested unsupported memory range "
 : +		  "0x%lx-0x%lx (decoding 0x%x-0x%x, 0x%x-0x%x)\n",
 : +		  device_get_name(child), device_get_unit(child), start,
 : +		  end, sc->membase, sc->memlimit, sc->pmembase,
 : +		  sc->pmemlimit);
 : +	    if (!ok)
 : +		return (NULL);
 :  	    if (bootverbose)
 :  		device_printf(sc->dev, "device %s%d requested decoded memory range 0x%lx-0x%lx\n",
 :  			      device_get_name(child), device_get_unit(child), start, end);
 :
 :=====================================================================
 :
 : Message-Id: <20021123091751.GJ1766@unixpages.org>
 : In-Reply-To: <20021123.013939.109040651.imp@bsdimp.com>
 : Date: Sat, 23 Nov 2002 10:17:52 +0100
 : From: Christian Brueffer <chris@unixpages.org>
 :
 : On Sat, Nov 23, 2002 at 01:39:39AM -0700, M. Warner Losh wrote:
 : > Actually, I think this patch has all the comparisons going the right
 : > way.
 :
 : Hi,
 :
 : your patch has fixed it for me.  Thanks!
 :
 : - Christian
 :
 :=====================================================================
 :
 : Message-Id: <20021123.101017.55148442.imp@bsdimp.com>
 : In-Reply-To: <20021123091751.GJ1766@unixpages.org>
 : Date: Sat, 23 Nov 2002 10:10:17 -0700 (MST)
 : From: "M. Warner Losh" <imp@bsdimp.com>
 :
 : In message: <20021123091751.GJ1766@unixpages.org>
 :             Christian Brueffer <chris@unixpages.org> writes:
 : : your patch has fixed it for me.  Thanks!
 :
 : Thanks for the info!
 :
 : Warner
 :
 :=====================================================================

From: "Matthew Emmerton" <matt@gsicomp.on.ca>
To: <freebsd-gnats-submit@FreeBSD.org>, <matt@gsicomp.on.ca>
Cc:  
Subject: Re: kern/45637: wi0 device_probe_and_attach returns 6
Date: Tue, 31 Dec 2002 17:40:27 -0500

 Warner's code got committed on Dec/03/2002 and fixed the problem for me as
 well.
 This PR can be closed.
 
State-Changed-From-To: open->closed 
State-Changed-By: maxim 
State-Changed-When: Wed Jan 1 00:15:55 PST 2003 
State-Changed-Why:  
Closed at the submitters' request. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=45637 
>Unformatted:
