Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 28 Mar 2015 23:40:30 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r280798 - head/sys/mips/atheros
Message-ID:  <201503282340.t2SNeUXf031777@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Sat Mar 28 23:40:29 2015
New Revision: 280798
URL: https://svnweb.freebsd.org/changeset/base/280798

Log:
  Begin moving support for board MAC addresses over to being explicitly defined.
  
  A lot of these dinky atheros based MIPS boards don't have a nice, well,
  anything consistent defining their MAC addresses for things.
  
  The Atheros reference design boards will happily put MAC addresses
  into the wifi module calibration data like they should, and individual
  ethernet MAC addresses into the calibration area in flash.
  That makes my life easy - "hint.arge.X.eeprommac=<addr>" reads from
  that flash address to extract a MAC, and everything works fine.
  
  However, aside from some very well behaved vendors (eg the Carambola 2
  board), everyone else does something odd.
  
  eg:
  
  * a MAC address in the environment (eg ubiquiti routerstation/RSPRO)
     that you derive arge0/arge1 MAC addresses from.
  * a MAC address in flash that you derive arge0/arge1 MAC addresses from.
  * The wifi devices having their own MAC addresses in calibration data,
    like normal.
  * The wifi devices having a fixed, default or garbage value for a MAC
    address in calibration data, and it has to be derived from the
    system MAC.
  
  So to support this complete nonsense of a situation, there needs to be
  a few hacks:
  
  * The "board" MAC address needs to be derived from somewhere and squirreled
    away.  For now it's either redboot or a MAC address stored in calibration
    flash.
  
  * Then, a "map" set of hints to populate kenv with some MAC addresses
    that are derived/local, based on the board address.  Each board has
    a totally different idea of what you do to derive things, so each
    map entry has an "offset" (+ve or -ve) that's added to the board
    MAC address.
  
  * Then if_arge (and later, if_ath) should check kenv for said hint and
    if it's found, use that rather than the EEPROM MAC address - which may
    be totally garbage and not actually work right.
  
  In order to do this, I've undone some of the custom redboot expecting
  hacks in if_arge and the stuff that magically adds one to the MAC
  address supplied by the board - instead, as I continue to test this
  out on more hardware, I'll update the hints file with a map explaining
  (a) where the board MAC should come from, and (b) what offsets to use
  for each device.
  
  The aim is to have all of the tplink, dlink and other random hardware
  we run on have valid MAC addresses at boot, so (a) people don't get
  random B:S:D:x:x:x ethernet MACs, and (b) the wifi MAC is valid
  so it works rather than trying to use an invalid address that
  actually upsets systems (think: multicast bit set in BSSID.)
  
  Tested:
  
  * TP-Link TL_WDR3600 - subsequent commits will add the hints map
    and the if_ath support.
  
  TODO:
  
  * Since this is -HEAD, and I'm all for debugging, there's a lot of
    printf()s in here.  They'll eventually go under bootverbose.
  * I'd like to turn the macaddr routines into something available
    to all drivers - too many places hand-roll random MAC addresses
    and parser stuff.  I'd rather it just be shared code.
    However, that'll require more formal review.
  * More boards.

Modified:
  head/sys/mips/atheros/ar71xx_macaddr.c
  head/sys/mips/atheros/ar71xx_macaddr.h
  head/sys/mips/atheros/ar71xx_machdep.c
  head/sys/mips/atheros/if_arge.c

Modified: head/sys/mips/atheros/ar71xx_macaddr.c
==============================================================================
--- head/sys/mips/atheros/ar71xx_macaddr.c	Sat Mar 28 23:30:51 2015	(r280797)
+++ head/sys/mips/atheros/ar71xx_macaddr.c	Sat Mar 28 23:40:29 2015	(r280798)
@@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$");
 #include <sys/types.h>
 #include <sys/libkern.h>
 
+#include <net/ethernet.h>
+
 #include <mips/atheros/ar71xx_macaddr.h>
 
 /*
@@ -40,6 +42,7 @@ __FBSDID("$FreeBSD$");
  * device on-board, but instead need to derive them from a single MAC
  * address stored somewhere.
  */
+uint8_t ar71xx_board_mac_addr[ETHER_ADDR_LEN];
 
 /*
  * Initialise a MAC address 'dst' from a MAC address 'src'.

Modified: head/sys/mips/atheros/ar71xx_macaddr.h
==============================================================================
--- head/sys/mips/atheros/ar71xx_macaddr.h	Sat Mar 28 23:30:51 2015	(r280797)
+++ head/sys/mips/atheros/ar71xx_macaddr.h	Sat Mar 28 23:40:29 2015	(r280798)
@@ -30,6 +30,8 @@
 #ifndef	__ATHEROS_AR71XX_MACADDR_H__
 #define	__ATHEROS_AR71XX_MACADDR_H__
 
+extern	uint8_t ar71xx_board_mac_addr[ETHER_ADDR_LEN];
+
 extern	int ar71xx_mac_addr_init(unsigned char *dst, const unsigned char *src,
 	    int offset, int is_local);
 extern	int ar71xx_mac_addr_random_init(unsigned char *dst);

Modified: head/sys/mips/atheros/ar71xx_machdep.c
==============================================================================
--- head/sys/mips/atheros/ar71xx_machdep.c	Sat Mar 28 23:30:51 2015	(r280797)
+++ head/sys/mips/atheros/ar71xx_machdep.c	Sat Mar 28 23:40:29 2015	(r280798)
@@ -56,12 +56,12 @@ __FBSDID("$FreeBSD$");
 
 #include <mips/atheros/ar71xx_setup.h>
 #include <mips/atheros/ar71xx_cpudef.h>
+#include <mips/atheros/ar71xx_macaddr.h>
 
 #include <mips/sentry5/s5reg.h>
 
 extern char edata[], end[];
 
-uint32_t ar711_base_mac[ETHER_ADDR_LEN];
 /* 4KB static data aread to keep a copy of the bootload env until
    the dynamic kenv is setup */
 char boot1_env[4096];
@@ -117,11 +117,13 @@ platform_reset(void)
 /*
  * Obtain the MAC address via the Redboot environment.
  */
-static void
+static int
 ar71xx_redboot_get_macaddr(void)
 {
 	char *var;
-	int count = 0;
+	int count = 0, i;
+	uint32_t macaddr[ETHER_ADDR_LEN];
+	uint8_t tmpmac[ETHER_ADDR_LEN];
 
 	/*
 	 * "ethaddr" is passed via envp on RedBoot platforms
@@ -130,14 +132,25 @@ ar71xx_redboot_get_macaddr(void)
 	if ((var = kern_getenv("ethaddr")) != NULL ||
 	    (var = kern_getenv("kmac")) != NULL) {
 		count = sscanf(var, "%x%*c%x%*c%x%*c%x%*c%x%*c%x",
-		    &ar711_base_mac[0], &ar711_base_mac[1],
-		    &ar711_base_mac[2], &ar711_base_mac[3],
-		    &ar711_base_mac[4], &ar711_base_mac[5]);
-		if (count < 6)
-			memset(ar711_base_mac, 0,
-			    sizeof(ar711_base_mac));
+		    &macaddr[0], &macaddr[1],
+		    &macaddr[2], &macaddr[3],
+		    &macaddr[4], &macaddr[5]);
+
+		if (count < 6) {
+			memset(macaddr, 0,
+			    sizeof(macaddr));
+		} else {
+			for (i = 0; i < ETHER_ADDR_LEN; i++)
+				tmpmac[i] = macaddr[i] & 0xff;
+			(void) ar71xx_mac_addr_init(ar71xx_board_mac_addr,
+			    tmpmac,
+			    0, /* offset */
+			    0); /* is_local */
+		}
 		freeenv(var);
+		return (0);
 	}
+	return (-1);
 }
 
 #ifdef	AR71XX_ENV_ROUTERBOOT
@@ -168,6 +181,129 @@ ar71xx_routerboot_get_mem(int argc, char
 }
 #endif
 
+/*
+ * Handle initialising the MAC address from a specific EEPROM
+ * offset.
+ *
+ * This is done during (very) early boot.
+ *
+ * hint.ar71xx.0.eeprom_mac_addr=<address to read from>
+ * hint.ar71xx.0.eeprom_mac_isascii=<0|1>
+ */
+static int
+ar71xx_platform_read_eeprom_mac(void)
+{
+	long eeprom_mac_addr = 0;
+	const char *mac;
+	int i, readascii = 0;
+	uint8_t macaddr[ETHER_ADDR_LEN];
+
+	if (resource_long_value("ar71xx", 0, "eeprom_mac_addr",
+	    &eeprom_mac_addr) != 0)
+		return (-1);
+
+	/* get a pointer to the EEPROM MAC address */
+
+	mac = (const char *) MIPS_PHYS_TO_KSEG1(eeprom_mac_addr);
+
+	/* Check if it's ASCII or not */
+	if (resource_int_value("ar71xx", 0, "eeprom_mac_isascii",
+	    &readascii) == 0 && readascii == 1) {
+		printf("ar71xx: Overriding MAC from EEPROM (ascii)\n");
+		for (i = 0; i < 6; i++) {
+			macaddr[i] = strtol(&(mac[i*3]), NULL, 16);
+		}
+	} else {
+		printf("ar71xx: Overriding MAC from EEPROM\n");
+		for (i = 0; i < 6; i++) {
+			macaddr[i] = mac[i];
+		}
+	}
+
+	/* Set the default board MAC */
+	(void) ar71xx_mac_addr_init(ar71xx_board_mac_addr,
+	    macaddr,
+	    0, /* offset */
+	    0); /* is_local */
+	printf("ar71xx: Board MAC: %6D\n", ar71xx_board_mac_addr, ":");
+	return (0);
+}
+
+/*
+ * Populate a kenv hint for the given device based on the given
+ * MAC address and offset.
+ *
+ * Returns 0 if ok, < 0 on error.
+ */
+static int
+ar71xx_platform_set_mac_hint(const char *dev, int unit,
+    const uint8_t *macaddr, int offset, int islocal)
+{
+	char macstr[32];
+	uint8_t lclmac[ETHER_ADDR_LEN];
+	char devstr[32];
+
+	/* Initialise the MAC address, plus/minus the offset */
+	if (ar71xx_mac_addr_init(lclmac, macaddr, offset, islocal) != 0) {
+		return (-1);
+	}
+
+	/* Turn it into a string */
+	snprintf(macstr, 32, "%6D", lclmac, ":");
+	snprintf(devstr, 32, "hint.%s.%d.macaddr", dev, unit);
+
+	printf("  %s => %s\n", devstr, macstr);
+
+	/* Call setenv */
+	if (kern_setenv(devstr, macstr) != 0) {
+		printf("%s: failed to set hint (%s => %s)\n",
+		    __func__,
+		    devstr,
+		    macstr);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Iterate through the list of boot time hints that populate
+ * a device MAC address hint based on the "board" MAC address.
+ *
+ * ar71xx_mac_map.X.devid=<device id, eg ath>
+ * ar71xx_mac_map.X.unitid=<unit id, eg 0>
+ * ar71xx_mac_map.X.offset=<mac address value offset>
+ * ar71xx_mac_map.X.is_local=<1 or 0>
+ */
+static int
+ar71xx_platform_check_mac_hints(void)
+{
+	int i;
+	const char *devid;
+	int offset, is_local, unitid;
+
+	for (i = 0; i < 8; i++) {
+		if (resource_string_value("ar71xx_mac_map", i, "devid",
+		    &devid) != 0)
+			break;
+		if (resource_int_value("ar71xx_mac_map", i, "unitid",
+		    &unitid) != 0)
+			break;
+		if (resource_int_value("ar71xx_mac_map", i, "offset",
+		    &offset) != 0)
+			break;
+		if (resource_int_value("ar71xx_mac_map", i, "is_local",
+		    &is_local) != 0)
+			break;
+		printf("ar71xx: devid '%s.%d', MAC offset '%d'\n",
+		    devid, unitid, offset);
+		(void) ar71xx_platform_set_mac_hint(devid, unitid,
+		    ar71xx_board_mac_addr, offset, is_local);
+	}
+
+	return (0);
+}
+
 void
 platform_start(__register_t a0 __unused, __register_t a1 __unused, 
     __register_t a2 __unused, __register_t a3 __unused)
@@ -208,8 +344,11 @@ platform_start(__register_t a0 __unused,
 		for (i = 0; envp[i]; i += 2) {
 			if (strcmp(envp[i], "memsize") == 0)
 				realmem = btoc(strtoul(envp[i+1], NULL, 16));
+			else if (strcmp(envp[i], "bootverbose") == 0)
+				bootverbose = btoc(strtoul(envp[i+1], NULL, 10));
 		}
 	}
+	bootverbose = 1;
 
 #ifdef	AR71XX_ENV_ROUTERBOOT
 	/*
@@ -302,9 +441,7 @@ platform_start(__register_t a0 __unused,
 	else 
 		printf ("envp is invalid\n");
 
-	/* Redboot if_arge MAC address is in the environment */
-	ar71xx_redboot_get_macaddr();
-
+	/* Platform setup */
 	init_param2(physmem);
 	mips_cpu_init();
 	pmap_bootstrap();
@@ -326,6 +463,15 @@ platform_start(__register_t a0 __unused,
 	 */
 	ar71xx_init_gmac();
 
+	/* Redboot if_arge MAC address is in the environment */
+	(void) ar71xx_redboot_get_macaddr();
+
+	/* Various other boards need things to come out of EEPROM */
+	(void) ar71xx_platform_read_eeprom_mac();
+
+	/* Initialise the MAC address hint map */
+	ar71xx_platform_check_mac_hints();
+
 	kdb_init();
 #ifdef KDB
 	if (boothowto & RB_KDB)

Modified: head/sys/mips/atheros/if_arge.c
==============================================================================
--- head/sys/mips/atheros/if_arge.c	Sat Mar 28 23:30:51 2015	(r280797)
+++ head/sys/mips/atheros/if_arge.c	Sat Mar 28 23:40:29 2015	(r280798)
@@ -90,6 +90,8 @@ MODULE_VERSION(arge, 1);
 
 #include "miibus_if.h"
 
+#include <net/ethernet.h>
+
 #include <mips/atheros/ar71xxreg.h>
 #include <mips/atheros/ar934xreg.h>	/* XXX tsk! */
 #include <mips/atheros/qca955xreg.h>	/* XXX tsk! */
@@ -240,12 +242,6 @@ DRIVER_MODULE(argemdio, nexus, argemdio_
 DRIVER_MODULE(mdio, argemdio, mdio_driver, mdio_devclass, 0, 0);
 #endif
 
-/*
- * RedBoot passes MAC address to entry point as environment
- * variable. platfrom_start parses it and stores in this variable
- */
-extern uint32_t ar711_base_mac[ETHER_ADDR_LEN];
-
 static struct mtx miibus_mtx;
 
 MTX_SYSINIT(miibus_mtx, &miibus_mtx, "arge mii lock", MTX_DEF);
@@ -567,19 +563,57 @@ arge_attach(device_t dev)
 {
 	struct ifnet		*ifp;
 	struct arge_softc	*sc;
-	int			error = 0, rid;
-	int			is_base_mac_empty, i;
+	int			error = 0, rid, i;
 	uint32_t		hint;
 	long			eeprom_mac_addr = 0;
 	int			miicfg = 0;
 	int			readascii = 0;
 	int			local_mac = 0;
+	uint8_t			local_macaddr[ETHER_ADDR_LEN];
+	char *			local_macstr;
+	char			devid_str[32];
+	int			count;
 
 	sc = device_get_softc(dev);
 	sc->arge_dev = dev;
 	sc->arge_mac_unit = device_get_unit(dev);
 
 	/*
+	 * See if there's a "board" MAC address hint available for
+	 * this particular device.
+	 *
+	 * This is in the environment - it'd be nice to use the resource_*()
+	 * routines, but at the moment the system is booting, the resource hints
+	 * are set to the 'static' map so they're not pulling from kenv.
+	 */
+	snprintf(devid_str, 32, "hint.%s.%d.macaddr",
+	    device_get_name(dev),
+	    device_get_unit(dev));
+	if ((local_macstr = kern_getenv(devid_str)) != NULL) {
+		uint32_t tmpmac[ETHER_ADDR_LEN];
+
+		/* Have a MAC address; should use it */
+		device_printf(dev, "Overriding MAC address from environment: '%s'\n",
+		    local_macstr);
+
+		/* Extract out the MAC address */
+		/* XXX this should all be a generic method */
+		count = sscanf(local_macstr, "%x%*c%x%*c%x%*c%x%*c%x%*c%x",
+		    &tmpmac[0], &tmpmac[1],
+		    &tmpmac[2], &tmpmac[3],
+		    &tmpmac[4], &tmpmac[5]);
+		if (count == 6) {
+			/* Valid! */
+			local_mac = 1;
+			for (i = 0; i < ETHER_ADDR_LEN; i++)
+				local_macaddr[i] = tmpmac[i];
+		}
+		/* Done! */
+		freeenv(local_macstr);
+		local_macstr = NULL;
+	}
+
+	/*
 	 * Some units (eg the TP-Link WR-1043ND) do not have a convenient
 	 * EEPROM location to read the ethernet MAC address from.
 	 * OpenWRT simply snaffles it from a fixed location.
@@ -593,8 +627,8 @@ arge_attach(device_t dev)
 	 * an array of numbers.  Expose a hint to turn on this conversion
 	 * feature via strtol()
 	 */
-	 if (resource_long_value(device_get_name(dev), device_get_unit(dev),
-	    "eeprommac", &eeprom_mac_addr) == 0) {
+	 if (local_mac == 0 && resource_long_value(device_get_name(dev),
+	     device_get_unit(dev), "eeprommac", &eeprom_mac_addr) == 0) {
 		local_mac = 1;
 		int i;
 		const char *mac =
@@ -604,11 +638,11 @@ arge_attach(device_t dev)
 			"readascii", &readascii) == 0) {
 			device_printf(dev, "Vendor stores MAC in ASCII format\n");
 			for (i = 0; i < 6; i++) {
-				ar711_base_mac[i] = strtol(&(mac[i*3]), NULL, 16);
+				local_macaddr[i] = strtol(&(mac[i*3]), NULL, 16);
 			}
 		} else {
 			for (i = 0; i < 6; i++) {
-				ar711_base_mac[i] = mac[i];
+				local_macaddr[i] = mac[i];
 			}
 		}
 	}
@@ -732,14 +766,11 @@ arge_attach(device_t dev)
 	ifp->if_capabilities |= IFCAP_POLLING;
 #endif
 
-	is_base_mac_empty = 1;
-	for (i = 0; i < ETHER_ADDR_LEN; i++) {
-		sc->arge_eaddr[i] = ar711_base_mac[i] & 0xff;
-		if (sc->arge_eaddr[i] != 0)
-			is_base_mac_empty = 0;
-	}
-
-	if (is_base_mac_empty) {
+	/* If there's a local mac defined, copy that in */
+	if (local_mac == 1) {
+		(void) ar71xx_mac_addr_init(sc->arge_eaddr,
+		    local_macaddr, 0, 0);
+	} else {
 		/*
 		 * No MAC address configured. Generate the random one.
 		 */
@@ -749,23 +780,6 @@ arge_attach(device_t dev)
 		(void) ar71xx_mac_addr_random_init(sc->arge_eaddr);
 	}
 
-	/*
-	 * This is a little hairy and stupid.
-	 *
-	 * For some older boards, the arge1 mac isn't pulled from anywhere.
-	 * It's just assumed the MAC is the base MAC + 1.
-	 *
-	 * For other boards, there's multiple MAC addresses stored in EEPROM.
-	 *
-	 * So, if we did read the eeprommac for this particular interface,
-	 * let's use the address as given.  Otherwise, just add the MAC unit
-	 * counter to it.
-	 *
-	 * XXX TODO: we really should handle MAC byte wraparound!
-	 */
-	if (local_mac == 0 && sc->arge_mac_unit != 0)
-		sc->arge_eaddr[5] +=  sc->arge_mac_unit;
-
 	if (arge_dma_alloc(sc) != 0) {
 		error = ENXIO;
 		goto fail;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201503282340.t2SNeUXf031777>