On Sun, May 17, 2015 at 07:51:03PM +0200, Alessandro DE LAURENZIS wrote:
> Folks,
> 
> I discussed this topic with stsp@ some months ago, without doing a real
> follow-up.
> 
> The bge driver lacks WOL support in the official tree, but a first
> version of the needed modifications were ready since OBSD 5.4 (see [1]).
> 
> I reviewed the patches and made them compatible with the 5.7 branch
> (hoping that they are still applicable to -current), adding also a note
> into the man page.
> 
> I tested them with my DELL Latitude D810 and can confirm that WOL is
> perfectly working, without introducing any regressions.
> 
> Hope Stefan or someone else is interested in committing this code (after
> the necessary review, of course).
> 
> All the best
> 
> [1] http://marc.info/?l=openbsd-misc&m=139848956915604&w=2

AFAIK, the idea was to always leave WOL disabled if ASF is enabled
because ASF is not considered trustworthy. ASF is essentially an
embedded OS running on the bge card's processor able to access memory
of the host system. It's the precursor of Intel AMT. There are concerns
the OS running the WOL logic inside bge cards can be exploited by attackers
to take over a machine. So it's deemed safer to leave WOL disabled.

It seems the patch does not detect ASF on your bge card since WoL works
for you. However, it's unclear to me whether the patch is even able to
reliably detect the presence of ASF in general.

OTOH, many laptops nowadays ship with Intel AMT and suffer the same issue
or worse. Yet we still run on them. Current AMT versions have an attack
surface that dwarfs ASF's. Perhaps this is a lost cause and we'll simply
have to accept that a lot of hardware is insecure by design.

This picture illustrates the specific horrors of ASF/AMT:
https://software.intel.com/sites/default/files/71/eb/mngstages.jpg
The functionality we want is in the column labeled "WfM". Yet we run a risk
of exposing all sorts of other crap if we enable WoL on modern hardware.
A minimal attack surface was clearly not one of the design goals.
Rather, hardware manufacturers leave users exposed to bugs that in most
cases cannot be patched.

You probably don't care about any of this and just want the feature to
work. That's understandable. But I don't think the project as a whole
has made a decision about this problem yet. So far we've erred on the
side of caution where possible and refrained from adding knobs that are
known to enable this sort of thing (one exception being the
console-over-Ethernet feature of AMT which is supported).

> --- ./brgphyreg.h.orig        Sun Jan 13 06:40:05 2013
> +++ ./brgphyreg.h     Sun May 17 17:22:32 2015
> @@ -206,6 +206,7 @@
>  #define BRGPHY_AUXCTL_TX_TST 0x0400  /* TX test, always 1 */
>  #define BRGPHY_AUXCTL_DIS_PRF        0x0080  /* dis part resp filter */
>  #define BRGPHY_AUXCTL_DIAG_MODE      0x0004  /* Diagnostic mode */
> +#define BRGPHY_AUXCTL_WOL_ENBL       0x000A  /* Enable WOL */
>  
>  #define BRGPHY_MII_AUXSTS    0x19    /* AUX status */
>  #define BRGPHY_AUXSTS_ACOMP  0x8000  /* autoneg complete */
> 
> 
> 
> --- ./if_bge.c.orig   Mon Feb  9 10:51:16 2015
> +++ ./if_bge.c        Sun May 17 17:23:20 2015
> @@ -199,6 +199,10 @@
>  void bge_stop_fw(struct bge_softc *, int);
>  void bge_reset(struct bge_softc *);
>  void bge_link_upd(struct bge_softc *);
> +#ifndef SMALL_KERNEL
> +int bge_wol(struct ifnet *, int);
> +void bge_wol_power(struct bge_softc *);
> +#endif
>  
>  void bge_ape_lock_init(struct bge_softc *);
>  void bge_ape_read_fw_ver(struct bge_softc *);
> @@ -3079,6 +3083,35 @@
>               CSR_WRITE_4(sc, BGE_MSI_MODE, CSR_READ_4(sc, BGE_MSI_MODE) &
>                   ~BGE_MSIMODE_ONE_SHOT_DISABLE);
>  
> +     #ifndef SMALL_KERNEL
> +     if (hwcfg & BGE_HWCFG_NO_GPIO2)
> +             sc->bge_flags |= BGE_NO_GPIO2;
> +
> +     if (BGE_ASICREV(sc->bge_chipid) != BGE_ASICREV_BCM5700) {
> +             /* Check if ASF is enabled. */
> +             if (!(sc->bge_flags & BGE_NO_EEPROM)) {
> +                     if (bge_read_eeprom(sc, (caddr_t)&hwcfg,
> +                         BGE_EE_FEATURE_CFG_OFFSET, sizeof(hwcfg)) == 0) {
> +                             hwcfg = ntohl(hwcfg);
> +                             if (hwcfg & BGE_HWCFG_ASF)
> +                                     sc->bge_flags |= BGE_ASF_MODE;
> +                     }
> +             } else if (hwcfg & BGE_HWCFG_ASF) {
> +                     sc->bge_flags |= BGE_ASF_MODE;
> +             }
> +     }
> +
> +     /* Allow WoL if ASF is unsupported or disabled. */
> +     if (!(sc->bge_flags & BGE_ASF_MODE)) {
> +             ifp->if_capabilities |= IFCAP_WOL;
> +             ifp->if_wol = bge_wol;
> +
> +             /* This heuristic matches the Linux driver. */
> +             if (!(hwcfg & BGE_HWCFG_EEPROM_WRITE_PROTECT))
> +                     sc->bge_flags |= BGE_WOL_NEEDS_VAUX;
> +     }
> +     #endif
> +
>       /* Hookup IRQ last. */
>       DPRINTFN(5, ("pci_intr_establish\n"));
>       sc->bge_intrhand = pci_intr_establish(pc, ih, IPL_NET, bge_intr, sc,
> @@ -3174,11 +3207,22 @@
>               rv = config_activate_children(self, act);
>               if (ifp->if_flags & IFF_RUNNING)
>                       bge_stop(sc);
> +             #ifndef SMALL_KERNEL
> +             bge_wol_power(sc);
> +             #endif
>               break;
>       case DVACT_RESUME:
>               if (ifp->if_flags & IFF_UP)
>                       bge_init(sc);
>               break;
> +     case DVACT_POWERDOWN:
> +             rv = config_activate_children(self, act);
> +             if (ifp->if_flags & IFF_RUNNING)
> +                     bge_stop(sc);
> +             #ifndef SMALL_KERNEL
> +             bge_wol_power(sc);
> +             #endif
> +             break;
>       default:
>               rv = config_activate_children(self, act);
>               break;
> @@ -4783,3 +4827,177 @@
>           BGE_MACSTAT_CFG_CHANGED|BGE_MACSTAT_MI_COMPLETE|
>           BGE_MACSTAT_LINK_CHANGED);
>  }
> +
> +#ifndef SMALL_KERNEL
> +int
> +bge_wol(struct ifnet *ifp, int enable)
> +{
> +     struct bge_softc *sc = ifp->if_softc;
> +
> +     if (enable)
> +             sc->bge_flags |= BGE_WOL;
> +     else
> +             sc->bge_flags &= ~BGE_WOL;
> +
> +     return (0);
> +}
> +
> +void
> +bge_wol_power(struct bge_softc *sc)
> +{
> +     struct ifnet *ifp = &sc->arpcom.ac_if;
> +     struct pci_attach_args *pa = &sc->bge_pa;
> +     pcireg_t pcireg;
> +     int s, offset, if_flags;
> +     u_int32_t reg;
> +
> +     if (!(sc->bge_flags & BGE_WOL))
> +             return;
> +
> +     s = splnet();
> +
> +     /* 
> +      * In case the interface was never up we need to init the
> +      * chip for WOL to work.
> +      * XXX Need a smaller hammer than bge_init()/bge_stop().
> +      */
> +     bge_init(sc);
> +
> +     /* Tell the firmware we're taking control of WOL. */
> +     bge_writemem_ind(sc, BGE_SOFTWARE_GENCOMM_WOL, BGE_MAGIC_WOL_NUMBER);
> +     DELAY(100);
> +
> +     bge_stop(sc);
> +
> +     /* Disable host interrupts. */
> +     BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_MASK_PCI_INTR);
> +
> +     /* Clear the PME status bit. */
> +     if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PWRMGMT,
> +         &offset, &pcireg))
> +             pci_conf_write(pa->pa_pc, pa->pa_tag, offset + PCI_PMCSR,
> +                 pcireg|PCI_PMCSR_PME_STATUS);
> +
> +     /* Configure 10Mbps, the chip draws too much power in D3cold. */
> +     if (!(sc->bge_flags & BGE_FIBER_TBI) &&
> +         !(sc->bge_flags & BGE_FIBER_MII)) {
> +             sc->bge_ifmedia.ifm_media = IFM_ETHER|IFM_10_T;
> +             if_flags = ifp->if_flags;
> +             ifp->if_flags |= IFF_UP;
> +             bge_ifmedia_upd(ifp);
> +             ifp->if_flags = if_flags;
> +     }
> +
> +     /* Disable DMA. */
> +     BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_FRMHDR_DMA_ENB|
> +         BGE_MACMODE_TXDMA_ENB|BGE_MACMODE_RXDMA_ENB);
> +
> +     /* Halt CPUs. */
> +     BGE_SETBIT(sc, BGE_TXCPU_MODE, BGE_TXCPUMODE_HALTCPU);
> +     BGE_SETBIT(sc, BGE_RXCPU_MODE, BGE_RXCPUMODE_HALTCPU);
> +
> +     /* Configure the PHY for WOL mode. */
> +     bge_miibus_writereg(&sc->bge_dev, 1, BRGPHY_MII_AUXCTL,
> +         BRGPHY_AUXCTL_WOL_ENBL);
> +     BGE_CLRBIT(sc, BGE_MAC_MODE, BGE_MACMODE_PORTMODE);
> +     CSR_WRITE_4(sc, BGE_MAC_MODE, BGE_PORTMODE_MII|
> +         BGE_MACMODE_LINK_POLARITY|BGE_MACMODE_MAGIC_PKT_ENB);
> +     DELAY(100);
> +
> +     /* Disable RX and TX CPUs, enable alternate clock. */
> +     BGE_SETBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_CLOCKCTL_RW);
> +     BGE_SETBIT(sc, BGE_PCI_CLKCTL,
> +         BGE_PCICLOCKCTL_RXCPU_CLK_DIS|BGE_PCICLOCKCTL_TXCPU_CLK_DIS|
> +         BGE_PCICLOCKCTL_ALTCLK|BGE_PCICLOCKCTL_LOW_SPEED_PLL);
> +     DELAY(500);
> +     BGE_CLRBIT(sc, BGE_PCI_CLKCTL, BGE_PCICLOCKCTL_ALTCLK);
> +     BGE_CLRBIT(sc, BGE_PCI_MISC_CTL, BGE_PCIMISCCTL_CLOCKCTL_RW);
> +
> +     if (sc->bge_flags & BGE_WOL_NEEDS_VAUX) {
> +             /* Switch from main power to aux power. */
> +             if (BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5700 ||
> +                 BGE_ASICREV(sc->bge_chipid) == BGE_ASICREV_BCM5701) {
> +                     BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL,
> +                         BGE_MLC_INTR_ONATTN|BGE_MLC_AUTO_EEPROM|
> +                         BGE_MLC_MISCIO_OUTEN0|BGE_MLC_MISCIO_OUTEN1|
> +                         BGE_MLC_MISCIO_OUTEN2|
> +                         BGE_MLC_MISCIO_OUT0|BGE_MLC_MISCIO_OUT1);
> +                     DELAY(100);
> +             } else if (PCI_PRODUCT(pa->pa_id) ==
> +                 PCI_PRODUCT_BROADCOM_BCM5761 ||
> +                 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_BROADCOM_BCM5761S) {
> +                     /* These have GPIO 0 and GPIO 2 swapped. */
> +                     reg = (BGE_MLC_INTR_ONATTN|BGE_MLC_AUTO_EEPROM|
> +                         BGE_MLC_MISCIO_OUTEN0|BGE_MLC_MISCIO_OUTEN1|
> +                         BGE_MLC_MISCIO_OUTEN2|
> +                         BGE_MLC_MISCIO_OUT0|BGE_MLC_MISCIO_OUT1);
> +                     BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg);
> +                     DELAY(100);
> +                     reg |= BGE_MLC_MISCIO_OUT2;
> +                     BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg);
> +                     DELAY(100);
> +                     reg &= ~BGE_MLC_MISCIO_OUT0;
> +                     BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg);
> +                     DELAY(100);
> +             } else {
> +                     reg = 0;
> +
> +                     /* Workaround for drawing too much power. */
> +                     if (BGE_ASICREV(sc->bge_chipid) ==
> +                         BGE_ASICREV_BCM5714) {
> +                             reg |= BGE_MLC_MISCIO_OUTEN3;   
> +                             BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg);
> +                             DELAY(100);
> +                     }
> +
> +                     if (sc->bge_flags & BGE_NO_GPIO2) {
> +                             reg |= (BGE_MLC_MISCIO_OUTEN0|
> +                                 BGE_MLC_MISCIO_OUTEN1|
> +                                 BGE_MLC_MISCIO_OUT1);
> +                     } else {
> +                             reg |= (BGE_MLC_MISCIO_OUTEN0|
> +                                 BGE_MLC_MISCIO_OUTEN1|
> +                                 BGE_MLC_MISCIO_OUTEN2|
> +                                 BGE_MLC_MISCIO_OUT1|
> +                                 BGE_MLC_MISCIO_OUT2);
> +                     }
> +
> +                     BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg);
> +                     DELAY(100);
> +
> +                     reg |= BGE_MLC_MISCIO_OUT0;
> +                     BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg);
> +                     DELAY(100);
> +
> +                     if (!(sc->bge_flags & BGE_NO_GPIO2)) {
> +                             reg &= ~BGE_MLC_MISCIO_OUT2;
> +                             BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, reg);
> +                             DELAY(100);
> +                     }
> +             }
> +     } else if (BGE_ASICREV(sc->bge_chipid) != BGE_ASICREV_BCM5700 &&
> +                BGE_ASICREV(sc->bge_chipid) != BGE_ASICREV_BCM5701) {
> +             /* Die with vmain power. */
> +             BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL,
> +                 BGE_MLC_MISCIO_OUT1|BGE_MLC_MISCIO_OUTEN1);
> +             DELAY(100);
> +             BGE_CLRBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_MISCIO_OUT1);
> +             DELAY(100);
> +             BGE_SETBIT(sc, BGE_MISC_LOCAL_CTL, BGE_MLC_MISCIO_OUT1);
> +             DELAY(100);
> +     }
> +
> +     /* Re-enable RX in promiscuous mode. */
> +     BGE_SETBIT(sc, BGE_RX_MODE, BGE_RXMODE_ENABLE|BGE_RXMODE_RX_PROMISC);
> +
> +     /* Enable PME assertion and put the device to sleep. */
> +     if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PWRMGMT,
> +         &offset, &reg)) {
> +             pci_conf_write(pa->pa_pc, pa->pa_tag, offset + PCI_PMCSR,
> +                 reg|PCI_PMCSR_PME_EN);
> +             pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D3);
> +     }
> +
> +     splx(s);
> +}
> +#endif
> 
> 
> 
> --- ./if_bgereg.h.orig        Tue Sep  2 12:14:55 2014
> +++ ./if_bgereg.h     Sun May 17 17:23:46 2015
> @@ -77,16 +77,17 @@
>  #define      BGE_SOFTWARE_GENCOMM_SIG        0x00000B54
>  #define      BGE_SOFTWARE_GENCOMM_NICCFG     0x00000B58
>  #define      BGE_SOFTWARE_GENCOMM_VER        0x00000B5C
> -#define         BGE_VER_SHIFT                        16
> +#define      BGE_VER_SHIFT                   16
>  #define      BGE_SOFTWARE_GENCOMM_FW         0x00000B78
> -#define         BGE_FW_PAUSE                         0x00000002
> +#define      BGE_FW_PAUSE                    0x00000002
> +#define BGE_SOFTWARE_GENCOMM_WOL     0x00000D30
>  #define      BGE_SOFTWARE_GENCOMM_NICCFG2    0x00000D38
>  #define      BGE_SOFTWARE_GENCOMM_NICCFG3    0x00000D3C
>  #define      BGE_SOFTWARE_GENCOMM_NICCFG4    0x00000D60
> -#define         BGE_NICCFG4_GMII_MODE                0x00000002
> -#define         BGE_NICCFG4_RGMII_STD_IBND_DISABLE   0x00000004
> -#define         BGE_NICCFG4_RGMII_EXT_IBND_RX_EN     0x00000008
> -#define         BGE_NICCFG4_RGMII_EXT_IBND_TX_EN     0x00000010
> +#define      BGE_NICCFG4_GMII_MODE           0x00000002
> +#define      BGE_NICCFG4_RGMII_STD_IBND_DISABLE      0x00000004
> +#define      BGE_NICCFG4_RGMII_EXT_IBND_RX_EN        0x00000008
> +#define      BGE_NICCFG4_RGMII_EXT_IBND_TX_EN        0x00000010
>  #define      BGE_SOFTWARE_GENCOMM_END        0x00000FFF
>  #define      BGE_UNMAPPED                    0x00001000
>  #define      BGE_UNMAPPED_END                0x00001FFF
> @@ -438,6 +439,7 @@
>  #define      BGE_PCICLOCKCTL_PCIPLL_DISABLE  0x00004000
>  #define      BGE_PCICLOCKCTL_SYSPLL_DISABLE  0x00008000
>  #define      BGE_PCICLOCKCTL_BIST_ENABLE     0x00010000
> +#define BGE_PCICLOCKCTL_LOW_SPEED_PLL        0x00020000
>  
>  /*
>   * High priority mailbox registers
> @@ -2102,6 +2104,8 @@
>  #define      BGE_MLC_INTR_CLR                0x00000002
>  #define      BGE_MLC_INTR_SET                0x00000004
>  #define      BGE_MLC_INTR_ONATTN             0x00000008
> +#define      BGE_MLC_MISCIO_OUTEN3           0x00000040
> +#define      BGE_MLC_MISCIO_OUT3             0x00000080
>  #define      BGE_MLC_MISCIO_IN0              0x00000100
>  #define      BGE_MLC_MISCIO_IN1              0x00000200
>  #define      BGE_MLC_MISCIO_IN2              0x00000400
> @@ -2292,6 +2296,12 @@
>   */
>  #define      BGE_MAGIC_NUMBER                0x4B657654
>  
> +/*
> + * This magic number needs to be written to the firmware mailbox at
> + * 0xd30 before WOL is configured.
> + */
> +#define BGE_MAGIC_WOL_NUMBER         0x474C0000
> +
>  typedef struct {
>       u_int32_t               bge_addr_hi;
>       u_int32_t               bge_addr_lo;
> @@ -2481,12 +2491,15 @@
>   */
>  #define      BGE_EE_MAC_OFFSET               0x7C
>  #define      BGE_EE_MAC_OFFSET_5906          0x10
> +#define      BGE_EE_FEATURE_CFG_OFFSET       0xC4
>  #define      BGE_EE_HWCFG_OFFSET             0xC8
>  
>  #define      BGE_HWCFG_VOLTAGE               0x00000003
>  #define      BGE_HWCFG_PHYLED_MODE           0x0000000C
>  #define      BGE_HWCFG_MEDIA                 0x00000030
>  #define      BGE_HWCFG_ASF                   0x00000080
> +#define BGE_HWCFG_EEPROM_WRITE_PROTECT       0x00000100
> +#define BGE_HWCFG_NO_GPIO2           0x00100000
>  
>  #define      BGE_VOLTAGE_1POINT3             0x00000000
>  #define      BGE_VOLTAGE_1POINT8             0x00000001
> @@ -2878,6 +2891,9 @@
>  #define      BGE_APE                 0x00080000
>  #define      BGE_CPMU_PRESENT        0x00100000
>  #define      BGE_TAGGED_STATUS       0x00200000
> +#define BGE_WOL                      0x04000000
> +#define BGE_WOL_NEEDS_VAUX   0x08000000
> +#define BGE_NO_GPIO2         0x10000000
>  #define      BGE_MSI                 0x00400000
>  #define      BGE_RDMA_BUG            0x00800000
>  #define      BGE_JUMBO_RING          0x01000000
> 
> 
> 
> --- ./bge.4.orig      Sun May 17 19:20:38 2015
> +++ ./bge.4   Sun May 17 19:23:23 2015
> @@ -114,6 +114,15 @@
>  .Pp
>  The
>  .Nm
> +driver additionally supports Wake on LAN (WoL).
> +See
> +.Xr arp 8
> +and
> +.Xr ifconfig 8
> +for more details.
> +.Pp
> +The
> +.Nm
>  driver supports the following media types:
>  .Bl -tag -width 1000baseSX
>  .It Cm autoselect
> 
> -- 
> Alessandro DE LAURENZIS
> [mailto:just22....@gmail.com]
> LinkedIn: http://it.linkedin.com/in/delaurenzis

Reply via email to