Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 08 Jun 2007 12:09:02 -0600 (MDT)
From:      "M. Warner Losh" <imp@bsdimp.com>
To:        bkoenig@alpha-tierchen.de
Cc:        arm@freebsd.org
Subject:   Re: if_ate handles the bytes of the MAC address in a "wrong" order
Message-ID:  <20070608.120902.-399284744.imp@bsdimp.com>
In-Reply-To: <53385.2001:6f8:101e:0:20e:cff:fe6d:6adb.1181314300.squirrel@webmail.alpha-tierchen.de>
References:  <53385.2001:6f8:101e:0:20e:cff:fe6d:6adb.1181314300.squirrel@webmail.alpha-tierchen.de>

next in thread | previous in thread | raw e-mail | index | archive | help
In message: <53385.2001:6f8:101e:0:20e:cff:fe6d:6adb.1181314300.squirre=
l@webmail.alpha-tierchen.de>
            Bj=F6rn_K=F6nig <bkoenig@alpha-tierchen.de> writes:
: Hello,
: =

: while reading code of the at91 ethernet driver (ate) I noticed that i=
t
: uses an order of the MAC address registers which is not intended by A=
tmel.
: =

: In FreeBSD you typically store the MAC address in an array of six oct=
ets,
: for example
: =

:   uint8_t eaddr[] =3D { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
: =

: which will be represented as 01:02:03:04:05:06 (transmission order) .=
 0x01
: is the most significant byte and 0x06 the least significant byte. The=

: least significant bit of the most significant byte is the group/indiv=
idual
: bit.
: =

: So far so good. That's the theory.
: =

: The specification of the Atmel AT91RM9200 controller clearly says
: something about how to store the MAC address in the two address regis=
ters
: (section 36.5.5, page 602).

When I read the manual, it was anything but clear.  Looks like rev F
does add some verbage, but it is still less than clear:

	There are four 48-bit specific address registers, each taking
	up two memory locations.  The first location contains the
	first four bytes of the address; the second location contains
	the last two bytes of the address stored in its least
	significant byte positions.

	Ethernet frames are transmitted a byte at a time, LSB first.
	The first bit ... of the destination address is the
	group/individual bit ...  This bit corresponds to bit 24 of
	the first word of the specific address register.  The MSB of
	the first byte corresponds to bit 31 of the specific address
	register.

: In fact that the first four bytes
: (01:02:03:04) are stored in the low register and the last two bytes
: (05:06) in the high register. Furthermore they write that the
: group/individual bit corresponds with with bit 24 of the address regi=
ster
: which means that they really want that you store first byte in the lo=
w
: register, because bit 24 of the high register is unused.

That's indeed what it says.

: So the correct code would store the low register in eaddr[0-3] and th=
e
: high register in eaddr[4-5], but actually the ate_get_mac function in=

: src/sys/arm/at91/if_ate.c uses another order.

We use the following code:
	low =3D RD4(sc, ETH_SA1L);
	high =3D  RD4(sc, ETH_SA1H);
	if ((low | (high & 0xffff)) =3D=3D 0)
		return (ENXIO);
	eaddr[0] =3D (high >> 8) & 0xff;
	eaddr[1] =3D high & 0xff;
	eaddr[2] =3D (low >> 24) & 0xff;
	eaddr[3] =3D (low >> 16) & 0xff;
	eaddr[4] =3D (low >> 8) & 0xff;
	eaddr[5] =3D low & 0xff;

which does look like it is wrong, based on what the text says.

: This won't hurt as long as you use a loader that stores the bytes the=
 same
: way as the driver read them. I wouldn't care if I wouldn't have this
: problem: Linux and the U-Boot loader stores the bytes it in a manner =
that
: is different from the FreeBSD method and also different from Atmel's
: suggestion.

I see the problem.  There's a disconnect between the above code and
the set code:

	WR4(sc, ETH_SA1L, (eaddr[3] << 24) | (eaddr[2] << 16) |
	    (eaddr[1] << 8) | eaddr[0]);
	WR4(sc, ETH_SA1H, (eaddr[5] << 8) | (eaddr[4]));

which is different still than the document says, which is, if I'm
reading it right:

	WR4(sc, ETH_SA1L, (eaddr[0] << 24) | (eaddr[1] << 16) |
	    (eaddr[2] << 8) | eaddr[3]);
	WR4(sc, ETH_SA1H, (eaddr[4] << 8) | (eaddr[5]));

: What do you think?

I'll investigate.  However, the order that they are stored in comes
from a boot loader that was emulating redboot and able to boot Linux.
If the MAC address is wrong, the ARP won't work since it is used to
process the incoming MAC address.  I don't think that the boot
loader's tftp function would work right with the bad address.  But to
be honest, that address doesn't have the be right, just consistent.  I
don't recall doing an arp -n on the server to see what's what.

Looking at the boot code, we see:

	localMAClow =3D (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[=
5];
	localMAChigh =3D (mac[0] << 8) | mac[1];

which matches the code I quoted before:

	eaddr[0] =3D (high >> 8) & 0xff;
	eaddr[1] =3D high & 0xff;
	eaddr[2] =3D (low >> 24) & 0xff;
	eaddr[3] =3D (low >> 16) & 0xff;
	eaddr[4] =3D (low >> 8) & 0xff;
	eaddr[5] =3D low & 0xff;

which is why we get the 'right' MAC on the probe line:

ate0: Ethernet address: 00:30:96:00:00:07

And on one of the units I have up, I see:

ate0: flags=3D8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        inet 206.168.13.132 netmask 0xffffff80 broadcast 206.168.13.255=

        ether 00:30:96:00:00:07

and on the other end of my telnet connection I see:

? (206.168.13.132) at 00:30:96:00:00:07 on bge0 [ethernet]

from arp.

Time for pencil and paper to see why this all works :-(.

Warner



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