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>