1256946Sbrooks/*-
2256946Sbrooks * Copyright (c) 2012 SRI International
3256946Sbrooks * Copyright (c) 2013 Bjoern A. Zeeb
4256946Sbrooks * All rights reserved.
5256946Sbrooks *
6256946Sbrooks * This software was developed by SRI International and the University of
7256946Sbrooks * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
8256946Sbrooks * ("CTSRD"), as part of the DARPA CRASH research programme.
9256946Sbrooks *
10256946Sbrooks * This software was developed by SRI International and the University of
11256946Sbrooks * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-11-C-0249)
12256946Sbrooks * ("MRC2"), as part of the DARPA MRC research programme.
13256946Sbrooks *
14256946Sbrooks * Redistribution and use in source and binary forms, with or without
15256946Sbrooks * modification, are permitted provided that the following conditions
16256946Sbrooks * are met:
17256946Sbrooks * 1. Redistributions of source code must retain the above copyright
18256946Sbrooks *    notice, this list of conditions and the following disclaimer.
19256946Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
20256946Sbrooks *    notice, this list of conditions and the following disclaimer in the
21256946Sbrooks *    documentation and/or other materials provided with the distribution.
22256946Sbrooks *
23256946Sbrooks * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24256946Sbrooks * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25256946Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26256946Sbrooks * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27256946Sbrooks * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28256946Sbrooks * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29256946Sbrooks * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30256946Sbrooks * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31256946Sbrooks * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32256946Sbrooks * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33256946Sbrooks * SUCH DAMAGE.
34256946Sbrooks *
35256946Sbrooks * $ FreeBSD: head/usr.sbin/isfctl/isfctl.c 239685 2012-08-25 18:08:20Z brooks $
36256946Sbrooks * $FreeBSD$
37256946Sbrooks */
38256946Sbrooks
39256946Sbrooks#include <sys/types.h>
40256946Sbrooks#include <sys/ioctl.h>
41256946Sbrooks#include <sys/endian.h>
42256946Sbrooks#include <sys/errno.h>
43256946Sbrooks#include <sys/socket.h>
44256946Sbrooks
45256946Sbrooks#include <assert.h>
46256946Sbrooks#include <err.h>
47256946Sbrooks#include <fcntl.h>
48256946Sbrooks#include <inttypes.h>
49256946Sbrooks#include <kenv.h>
50256946Sbrooks#include <md5.h>
51256946Sbrooks#include <stdlib.h>
52256946Sbrooks#include <stdio.h>
53256946Sbrooks#include <string.h>
54256946Sbrooks#include <unistd.h>
55256946Sbrooks
56256946Sbrooks#include <net/if_dl.h>
57256946Sbrooks#include <net/ethernet.h>
58256946Sbrooks
59256946Sbrooks
60256946Sbrooks#define	CONFIG_BLOCK (128 * 1024)
61256946Sbrooks#define	DEV_CFI0_PATH	"/dev/cfi0"
62256946Sbrooks
63256946Sbrooksstatic u_char block[CONFIG_BLOCK];
64256946Sbrooks
65256946Sbrooks#define	UNKNOWN	0
66256946Sbrooks#define	CFI	1
67256946Sbrooksstatic int fdev	= UNKNOWN;
68256946Sbrooksstatic const char *fdevs[] = {
69256946Sbrooks	"UNKNOWN",
70256946Sbrooks	"CFI"
71256946Sbrooks};
72256946Sbrooksstatic int gflag;
73256946Sbrooks
74256946Sbrooks/* XXX-BZ should include if_atsereg.h. */
75256946Sbrooks#define	ALTERA_ETHERNET_OPTION_BITS_OFF 0x00008000
76256946Sbrooks#define	ALTERA_ETHERNET_OPTION_BITS_LEN 0x00007fff
77256946Sbrooks
78256946Sbrooks
79256946Sbrooksstatic void
80256946Sbrooksusage(int rc)
81256946Sbrooks{
82256946Sbrooks
83256946Sbrooks	fprintf(stderr, "usage: atsectl [-ghlu] [-s <etheraddr>]\n");
84256946Sbrooks	exit(rc);
85256946Sbrooks}
86256946Sbrooks
87256946Sbrooksstatic void
88256946Sbrooksread_block(void)
89256946Sbrooks{
90256946Sbrooks	int fd;
91256946Sbrooks
92256946Sbrooks	fd = open(DEV_CFI0_PATH, O_RDONLY, 0);
93256946Sbrooks	if (fd == -1)
94256946Sbrooks		errx(1, "Failed to open " DEV_CFI0_PATH);
95256946Sbrooks	else
96256946Sbrooks		fdev = CFI;
97256946Sbrooks
98256946Sbrooks	if (read(fd, block, sizeof(block)) != CONFIG_BLOCK)
99256946Sbrooks		errx(1, "Short read from %s", fdevs[fdev]);
100256946Sbrooks
101256946Sbrooks	close(fd);
102256946Sbrooks}
103256946Sbrooks
104256946Sbrooksstatic void
105256946Sbrookswrite_block(void)
106256946Sbrooks{
107256946Sbrooks	int fd;
108256946Sbrooks
109256946Sbrooks	assert(fdev == CFI);
110256946Sbrooks
111256946Sbrooks	fd = open(DEV_CFI0_PATH, O_WRONLY, 0);
112256946Sbrooks	if (fd == -1)
113256946Sbrooks		errx(1, "Failed to open " DEV_CFI0_PATH);
114256946Sbrooks
115256946Sbrooks	if (write(fd, block, sizeof(block)) != CONFIG_BLOCK)
116256946Sbrooks		errx(1, "Short write on %s", fdevs[fdev]);
117256946Sbrooks
118256946Sbrooks	close(fd);
119256946Sbrooks}
120256946Sbrooks
121256946Sbrooksstatic void
122256946Sbrooksprint_eaddr(void)
123256946Sbrooks{
124256946Sbrooks	uint32_t safe;
125256946Sbrooks
126256946Sbrooks	/*
127256946Sbrooks	 * XXX-BZ we are on our own: keep in sync with atse(4).
128256946Sbrooks	 * Everything past the first address is a guess currently.
129256946Sbrooks	 * So we will always only write one address into there.
130256946Sbrooks	 */
131256946Sbrooks#if 0
132256946Sbrooksroot@cheri1:/root # dd if=/dev/isf0 bs=32k skip=1 count=1 | hd
133256946Sbrooks00000000  fe 5a 00 00 00 07 ed ff  ed 15 ff ff c0 a8 01 ea  |.Z..............|
134256946Sbrooks00000010  ff ff ff ff ff ff ff 00  c0 a8 01 ff ff ff ff ff  |................|
135256946Sbrooks00000020  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
136256946Sbrooks*
137256946Sbrooks1+0 records in
138256946Sbrooks1+0 records out
139256946Sbrooks32768 bytes transferred in 0.053036 secs (617845 bytes/sec)
140256946Sbrooks00008000
141256946Sbrooks#endif
142256946Sbrooks
143256946Sbrooks	safe  = block[ALTERA_ETHERNET_OPTION_BITS_OFF + 0] << 24;
144256946Sbrooks	safe |= block[ALTERA_ETHERNET_OPTION_BITS_OFF + 1] << 16;
145256946Sbrooks	safe |= block[ALTERA_ETHERNET_OPTION_BITS_OFF + 2] << 8;
146256946Sbrooks	safe |= block[ALTERA_ETHERNET_OPTION_BITS_OFF + 3];
147256946Sbrooks
148256946Sbrooks	printf("%02x:%02x:%02x:%02x:%02x:%02x%s\n",
149256946Sbrooks	    block[ALTERA_ETHERNET_OPTION_BITS_OFF + 4],
150256946Sbrooks	    block[ALTERA_ETHERNET_OPTION_BITS_OFF + 5],
151256946Sbrooks	    block[ALTERA_ETHERNET_OPTION_BITS_OFF + 6],
152256946Sbrooks	    block[ALTERA_ETHERNET_OPTION_BITS_OFF + 7],
153256946Sbrooks	    block[ALTERA_ETHERNET_OPTION_BITS_OFF + 8],
154256946Sbrooks	    block[ALTERA_ETHERNET_OPTION_BITS_OFF + 9],
155256946Sbrooks	    (safe != le32toh(0x00005afe)) ?
156256946Sbrooks		" (invalid control pattern)" : "");
157256946Sbrooks}
158256946Sbrooks
159256946Sbrooksstatic void
160256946Sbrookslist(void)
161256946Sbrooks{
162256946Sbrooks
163256946Sbrooks	read_block();
164256946Sbrooks	print_eaddr();
165256946Sbrooks	exit(0);
166256946Sbrooks}
167256946Sbrooks
168256946Sbrooksstatic void
169256946Sbrooks_set(uint8_t *eaddr)
170256946Sbrooks{
171256946Sbrooks	uint8_t buf[32];
172256946Sbrooks	MD5_CTX ctx;
173256946Sbrooks	int rc;
174256946Sbrooks
175256946Sbrooks	printf("Original:\n");
176256946Sbrooks	read_block();
177256946Sbrooks	print_eaddr();
178256946Sbrooks
179256946Sbrooks	if (eaddr == NULL) {
180256946Sbrooks		/* cfi0.factory_ppr="0x0123456789abcdef" */
181256946Sbrooks		rc = kenv(KENV_GET, "cfi0.factory_ppr", buf, sizeof(buf));
182256946Sbrooks		if (rc == -1)
183256946Sbrooks			err(1, "Could not find Intel flash PPR serial\n");
184256946Sbrooks
185256946Sbrooks		MD5Init(&ctx);
186256946Sbrooks		MD5Update(&ctx, buf+2, 16);
187256946Sbrooks		MD5Final(buf, &ctx);
188256946Sbrooks
189256946Sbrooks		/* Set the device specifc address (prefix). */
190256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 7] =
191256946Sbrooks		    buf[14] << 4 | buf[13] >> 4;
192256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 8] =
193256946Sbrooks		    buf[13] << 4 | buf[12] >> 4;
194256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 9] = buf[12] << 4;
195256946Sbrooks		/* Just make sure the last half-byte is really zero. */
196256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 9] &= ~0x0f;
197256946Sbrooks
198256946Sbrooks		/* Set (or clear) locally administred flag. */
199256946Sbrooks		if (gflag == 0)
200256946Sbrooks			block[ALTERA_ETHERNET_OPTION_BITS_OFF + 4] |= 2;
201256946Sbrooks		else
202256946Sbrooks			block[ALTERA_ETHERNET_OPTION_BITS_OFF + 4] &= ~2;
203256946Sbrooks		/* Make sure it is not a MC address by accident we start with. */
204256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 4] &= ~1;
205256946Sbrooks	} else {
206256946Sbrooks		int e;
207256946Sbrooks
208256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 4] = eaddr[0];
209256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 5] = eaddr[1];
210256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 6] = eaddr[2];
211256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 7] = eaddr[3];
212256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 8] = eaddr[4];
213256946Sbrooks		block[ALTERA_ETHERNET_OPTION_BITS_OFF + 9] = eaddr[5];
214256946Sbrooks
215256946Sbrooks		e = 0;
216256946Sbrooks		if ((eaddr[5] & 0xf) != 0x0) {
217256946Sbrooks			e++;
218256946Sbrooks			warnx("WARN: Selected Ethernet Address is "
219256946Sbrooks			    "not multi-MAC compatible.\n");
220256946Sbrooks		}
221256946Sbrooks		if (gflag == 0 && ((eaddr[0] & 0x2) == 0x0)) {
222256946Sbrooks			e++;
223256946Sbrooks			warnx("WARN: Locally administered bit not set.\n");
224256946Sbrooks		}
225256946Sbrooks		if ((eaddr[0] & 0x1) != 0x0) {
226256946Sbrooks			e++;
227256946Sbrooks			warnx("WARN: You are setting a Multicast address.\n");
228256946Sbrooks		}
229256946Sbrooks		if (e != 0)
230256946Sbrooks			warnx("Suggesting to re-run with: "
231256946Sbrooks			    "%02x:%02x:%02x:%02x:%02x:%02x",
232256946Sbrooks			    (eaddr[0] & 0xfe) | 0x2,
233256946Sbrooks			    eaddr[1], eaddr[2], eaddr[3], eaddr[4],
234256946Sbrooks			    eaddr[5] & 0xf0);
235256946Sbrooks	}
236256946Sbrooks
237256946Sbrooks	/* Write the "safe" out, just to be sure. */
238256946Sbrooks	block[ALTERA_ETHERNET_OPTION_BITS_OFF + 0] = 0xfe;
239256946Sbrooks	block[ALTERA_ETHERNET_OPTION_BITS_OFF + 1] = 0x5a;
240256946Sbrooks	block[ALTERA_ETHERNET_OPTION_BITS_OFF + 2] = 0x00;
241256946Sbrooks	block[ALTERA_ETHERNET_OPTION_BITS_OFF + 3] = 0x00;
242256946Sbrooks
243256946Sbrooks	write_block();
244256946Sbrooks
245256946Sbrooks	printf("Updated to:\n");
246256946Sbrooks	read_block();
247256946Sbrooks	print_eaddr();
248256946Sbrooks	exit(0);
249256946Sbrooks}
250256946Sbrooks
251256946Sbrooksstatic void
252256946Sbrooksupdate(void)
253256946Sbrooks{
254256946Sbrooks
255256946Sbrooks	_set(NULL);
256256946Sbrooks	exit(0);
257256946Sbrooks}
258256946Sbrooks
259256946Sbrooksstatic void
260256946Sbrooksset(char *eaddrstr)
261256946Sbrooks{
262256946Sbrooks	uint8_t eaddr[ETHER_ADDR_LEN];
263256946Sbrooks	char *p;
264256946Sbrooks	long l;
265256946Sbrooks	int i;
266256946Sbrooks
267256946Sbrooks	memset(eaddr, 0x00, ETHER_ADDR_LEN);
268256946Sbrooks	i = 0;
269256946Sbrooks	while ((p = strsep(&eaddrstr, ":")) != NULL && i < ETHER_ADDR_LEN) {
270256946Sbrooks		errno = 0;
271256946Sbrooks		l = strtol(p, (char **)NULL, 16);
272256946Sbrooks		if (l == 0 && errno != 0)
273256946Sbrooks			errx(1, "Failed to parse Ethernet address given: %s\n", p);
274256946Sbrooks		if (l < 0x00 || l > 0xff)
275256946Sbrooks			errx(1, "Failed to parse Ethernet address given: %lx\n", l);
276256946Sbrooks		eaddr[i++] = strtol(p, (char **)NULL, 16);
277256946Sbrooks	}
278256946Sbrooks
279256946Sbrooks	if (i != ETHER_ADDR_LEN)
280256946Sbrooks		errx(1, "Failed to parse Ethernet address given\n");
281256946Sbrooks
282256946Sbrooks	_set(eaddr);
283256946Sbrooks	exit(0);
284256946Sbrooks}
285256946Sbrooks
286256946Sbrooksint
287256946Sbrooksmain(int argc, char **argv)
288256946Sbrooks{
289256946Sbrooks	char ch, *s;
290256946Sbrooks
291256946Sbrooks	s = NULL;
292256946Sbrooks	while ((ch = getopt(argc, argv, "ghlus:")) != -1) {
293256946Sbrooks		switch (ch) {
294256946Sbrooks		case 'g':
295256946Sbrooks			gflag = 1;
296256946Sbrooks			break;
297256946Sbrooks		case 'h':
298256946Sbrooks			usage(0);
299256946Sbrooks			/* NOTREACHED */
300256946Sbrooks			break;
301256946Sbrooks		case 'l':
302256946Sbrooks			list();
303256946Sbrooks			/* NOTREACHED */
304256946Sbrooks			break;
305256946Sbrooks		case 'u':
306256946Sbrooks			update();
307256946Sbrooks			/* NOTREACHED */
308256946Sbrooks			break;
309256946Sbrooks
310256946Sbrooks		case 's':
311256946Sbrooks			set(optarg);
312256946Sbrooks			/* NOTREACHED */
313256946Sbrooks			break;
314256946Sbrooks
315256946Sbrooks		case '?':
316256946Sbrooks		default:
317256946Sbrooks			usage(1);
318256946Sbrooks			/* NOTREACHED */
319256946Sbrooks			break;
320256946Sbrooks		}
321256946Sbrooks	}
322256946Sbrooks
323256946Sbrooks	usage(1);
324256946Sbrooks	/* NOTREACHED */
325256946Sbrooks
326256946Sbrooks	return (0);
327256946Sbrooks}
328