1#! /usr/pkg/bin/perl -w
2
3# $Id: tme-sun-idprom.pl,v 1.2 2003/07/29 18:21:31 fredette Exp $
4
5# machine/sun/sun-idprom.pl - dumps and makes Sun IDPROM contents:
6
7# Copyright (c) 2003 Matt Fredette
8# All rights reserved.
9#
10# Redistribution and use in source and binary forms, with or without
11# modification, are permitted provided that the following conditions
12# are met:
13# 1. Redistributions of source code must retain the above copyright
14#    notice, this list of conditions and the following disclaimer.
15# 2. Redistributions in binary form must reproduce the above copyright
16#    notice, this list of conditions and the following disclaimer in the
17#    documentation and/or other materials provided with the distribution.
18# 3. All advertising materials mentioning features or use of this software
19#    must display the following acknowledgement:
20#      This product includes software developed by Matt Fredette.
21# 4. The name of the author may not be used to endorse or promote products
22#    derived from this software without specific prior written permission.
23#
24# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27# DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34# POSSIBILITY OF SUCH DAMAGE.
35
36# mapping from machine name to Sun code name:
37@machine_codename =
38    ( '2/120',			'sun2 Multibus',
39      '2/170',			'sun2 Multibus',
40      '2/50',			'sun2 VME',
41      '2/130',			'sun2 VME',
42      '2/160',			'sun2 VME',
43      '3/75',			'Carrera',
44      '3/140',			'Carrera',
45      '3/150',			'Carrera',
46      '3/160',			'Carrera',
47      '3/180',			'Carrera',
48      '3/50',			'Model 25',
49      '3/260',			'Sirius',
50      '3/280',			'Sirius',
51      '3/110',			'Prism',
52      '3/60',			'Ferrari',
53      '3/E',			'Sun3E',
54      '4/260',			'Sunrise',
55      '4/280',			'Sunrise',
56      '4/110',			'Cobra',
57      '4/150',			'Cobra',
58      '4/330',			'Stingray',
59      '4/310',			'Stingray',
60      '4/350',			'Stingray',
61      '4/360',			'Stingray',
62      '4/370',			'Stingray',
63      '4/380',			'Stingray',
64      '4/390',			'Stingray',
65      '4/470',			'Sunray',
66      '4/490',			'Sunray',
67      '386i/MB1',		'Roadrunner-MB1',
68      '386i/150',		'Roadrunner',
69      '386i/250',		'Roadrunner',
70      '3/460',			'Pegasus',
71      '3/470',			'Pegasus',
72      '3/480',			'Pegasus',
73      '3/80',			'Hydra',
74      '*SPARCStation 1',	'Campus',
75      'SS1',			'Campus',
76      '4/60',			'Campus',
77      '*SPARCstation IPC',	'Phoenix',
78      'IPC',			'Phoenix',
79      '4/40',			'Phoenix',
80      '*SPARCStation 1+',	'Campus B',
81      'SS1+',			'Campus B',
82      '4/65',			'Campus B',
83      '*SPARCstation SLC',	'Off-Campus',
84      'SLC',			'Off-Campus',
85      '4/20',			'Off-Campus',
86      '*SPARCStation 2',	'Calvin',
87      'SS2',			'Calvin',
88      '4/75',			'Calvin',
89      '*SPARCstation ELC',	'Node Warrior',
90      'ELC',			'Node Warrior',
91      '4/25',			'Node Warrior',
92      '*SPARCstation IPX',	'Hobbes',
93      'IPX',			'Hobbes',
94      '4/50',			'Hobbes',
95      '*SPARCengine 1',		'Polaris',
96      '4/E',			'Polaris',
97      '4/600',			'Galaxy',
98      '4/630',			'Galaxy',
99      '4/670',			'Galaxy',
100      '4/690',			'Galaxy',
101      '*SPARCstation 10',	'Campus-2',
102      'SS10',			'Campus-2',
103      '*SPARCstation 20',	'Kodiak',
104      'SS20',			'Kodiak',
105      '*SPARCclassic X',	'Hamlet',
106      'Classic X',		'Hamlet',
107      '4/10',			'Hamlet',
108      '*SPARCClassic',		'Sunergy',
109      '4/15',			'Sunergy',
110      'classic',		'Sunergy',
111      '4/30',			'Sunergy',
112      'SPARCstation LX',	'Sunergy',
113      'LX',			'Sunergy',
114      'Voyager',		'Gypsy',
115      'SS5',			'Aurora',
116      'SS4',			'Perigee',
117      );
118%machine_codename = @machine_codename;
119
120# mapping from Sun code name to IDPROM machine type byte:
121@codename_machtype =
122    ( 'sun2 Multibus',	0x01,
123      'sun2 VME',	0x02,
124      'Carrera',	0x11,
125      'Model 25',	0x12,
126      'Sirius',		0x13,
127      'Prism',		0x14,
128      'Ferrari',	0x17,
129      'Sun3E',		0x18,
130      'Sunrise',	0x21,
131      'Cobra',		0x22,
132      'Stingray',	0x23,
133      'Sunray',		0x24,
134      'Roadrunner',	0x31,
135      'Roadrunner-MB1',	0x3a,
136      'Pegasus',	0x41,
137      'Hydra',		0x42,
138      'Campus',		0x51,
139      'Phoenix',	0x52,
140      'Campus B',	0x53,
141      'Off-Campus',	0x54,
142      'Calvin',		0x55,
143      'Node Warrior',	0x56,
144      'Hobbes',		0x57,
145      'Polaris',	0x61,
146      'Galaxy',		0x71,
147      'Campus-2',	0x72,
148      'Kodiak',		0x72,
149      'Sunergy',	0x80,
150      'Gypsy',		0x80,
151      'Aurora',		0x80,
152      'Perigee',	0x80,
153      );
154%codename_machtype = @codename_machtype;
155
156# mapping from Sun code name to sometime during the
157# shipping/availability period, if known, for those machines that
158# shipped with a real manufacture date in the IDPROM:
159%codename_shipping =
160    ( 'sun2 Multibus',	457370182,
161      'Carrera',	587239630,
162      'Ferrari',	564526348,
163      );
164
165# if our standard input is not a terminal, assume we're checking an IDPROM there:
166unless (-t STDIN) {
167
168    # read in the IDPROM:
169    $size = sysread(STDIN, $idprom, 32);
170    if ($size != 32) {
171	print STDERR "fatal: IDPROM has wrong size ($size), must be exactly 32\n";
172	exit(1);
173    }
174
175    # unpack the IDPROM:
176    ($format,
177     $machtype,
178     $ether0,
179     $ether1,
180     $ether2,
181     $ether3,
182     $ether4,
183     $ether5,
184     $date,
185     $serial_and_cksum) =
186	(unpack("C C C6 N N", $idprom));
187    $serial = ($serial_and_cksum >> 8);
188
189    # check the format:
190    if ($format != 1) {
191	print "warning: IDPROM has invalid format byte ($format), must be 1\n";
192    }
193
194    # check the checksum, which only covers the first 16 bytes:
195    @bytes = (unpack("C16", $idprom));
196    $cksum_old = pop(@bytes);
197    $cksum_new = 0;
198    foreach $byte (@bytes) {
199	$cksum_new ^= $byte;
200    }
201    if ($cksum_new != $cksum_old) {
202	print "warning: IDPROM has bad cksum ";
203	print sprintf("(0x%02x)", $cksum_old);
204	print ", should be ";
205	print sprintf("0x%02x", $cksum_new);
206	print "\n";
207    }
208
209    # turn the machine type into a set of code names and machines:
210    @codenames = ();
211    @machines = ();
212    for ($i = 0; $i < @codename_machtype; $i += 2) {
213	if ($codename_machtype[$i + 1] == $machtype) {
214	    $codename = $codename_machtype[$i + 0];
215	    push (@codenames, $codename);
216	    @codename_machines = ();
217	    for ($j = 0; $j < @machine_codename; $j += 2) {
218		if ($machine_codename[$j + 1] eq $codename) {
219		    $machine = $machine_codename[$j + 0];
220		    if ($machine =~ /^\*(.*)$/) {
221			push (@codename_machines, $1);
222			last;
223		    }
224		    else {
225			push(@codename_machines, $machine);
226		    }
227		}
228	    }
229	    if (@codename_machines > 2) {
230		$codename_machines[$#codename_machines] = "or ".$codename_machines[$#codename_machines];
231		$codename_machines = join(", ", @codename_machines);
232	    }
233	    elsif (@codename_machines == 2) {
234		$codename_machines = join(" or ", @codename_machines);
235	    }
236	    else {
237		$codename_machines = $codename_machines[0];
238	    }
239	    push(@machines, $codename_machines);
240	}
241    }
242
243    # display a PROM-like banner:
244    if (@codenames == 0) {
245	print "warning: IDPROM has unknown machine type ";
246	print sprintf("(0x%02x)", $machtype);
247	print "\n";
248    }
249    elsif (@codenames == 1) {
250	$codename = $codenames[0];
251	$machines = $machines[0];
252	if ($machines =~ /^\d+i?\//
253	    || $machines =~ / or /) {
254	    print "Sun Workstation, Model ";
255	}
256	print $machines;
257	print " (codename \"$codename\")" unless ($codename =~ /^sun2/);
258	print "\n";
259    }
260    else {
261	print "Sun Workstation, Model ";
262	for ($i = 0; $i < @codenames; $i++) {
263	    $codename = $codenames[$i];
264	    $machines[$i] .= " (codename \"$codename\")"
265		unless ($codename =~ /^sun2/);
266	}
267	print join((@codenames == 2 ? " or " : ", or "), @machines);
268	print "\n";
269    }
270    print "Serial \#$serial, Ethernet address";
271    $sep = ' ';
272    foreach $byte ($ether0, $ether1, $ether2, $ether3, $ether4, $ether5) {
273	print $sep.sprintf("%x", $byte);
274	$sep = ':';
275    }
276    print "\n";
277    print "Manufacture date ".gmtime($date)."\n";
278}
279
280# otherwise, we're generating a new IDPROM:
281else {
282
283    # check our command line:
284    $usage = 0;
285
286    # the machine or code name:
287    if (@ARGV == 0) {
288	print STDERR "missing machine name\n";
289	$usage = 1;
290    }
291    else {
292	$name = shift(@ARGV);
293	if (defined($codename_machtype{$name})) {
294	    $codename = $name;
295	}
296	else {
297	    # this should be a machine name:
298	    $codename = $machine_codename{$name};
299	    if (!defined($codename)) {
300		$codename = $machine_codename{"*$name"};
301	    }
302	    if (!defined($codename)) {
303		print STDERR "unknown machine name '$name'\n";
304		$usage = 1;
305	    }
306	}
307    }
308
309    # the Ethernet address:
310    if (@ARGV == 0) {
311	print STDERR "missing Ethernet address\n";
312	$usage = 1;
313    }
314    else {
315	$ether = shift(@ARGV);
316	@ether = split(/:/, $ether);
317	unless ($ether =~ /^[0-9a-fA-F:]+/
318		&& @ether == 6) {
319	    print STDERR "bad Ethernet address '$ether'\n";
320	    $usage = 1;
321	}
322    }
323
324    # the optional serial number:
325    if (@ARGV > 0) {
326	$serial = shift(@ARGV);
327	unless ($serial =~ /^\d+$/) {
328	    print STDERR "bad serial number '$serial'\n";
329	    $usage = 1;
330	}
331    }
332    else {
333	$serial = 1 + int(rand(100000));
334    }
335
336    # standard output must not be a terminal:
337    if (-t STDOUT) {
338	print STDERR "standard output must not be a terminal\n";
339	$usage = 1;
340    }
341
342    # any other arguments are unexpected:
343    if (@ARGV > 0) {
344	print STDERR "unexpected argument '$ARGV[0]'\n";
345	$usage = 1;
346    }
347
348    if ($usage) {
349	print STDERR "usage: $0 MACHINE ETHERNET [ SERIAL ] > IDPROM\n";
350	print STDERR "       $0 < IDPROM\n";
351	exit(1);
352    }
353
354    # start the IDPROM:
355    $idprom = "";
356
357    # add in the format byte:
358    $idprom .= pack("C", 1);
359
360    # add in the machine type:
361    $machtype = $codename_machtype{$codename};
362    if (!defined($machtype)) {
363	die "internal error - machtype byte for codename $codename missing\n";
364    }
365    $idprom .= pack("C", $machtype);
366
367    # add in the ethernet address:
368    foreach $byte (@ether) {
369	$byte = hex($byte);
370	$idprom .= pack("C", $byte);
371    }
372    if ($ether[0] != 0x08
373	|| $ether[1] != 0x00
374	|| $ether[2] != 0x20) {
375	print STDERR "warning: Ethernet address $ether doesn't begin with Sun's OUI 8:0:20\n";
376    }
377
378    # add in a date, if possible:
379    $date = $codename_shipping{$codename};
380    if (defined($date)) {
381	# smear the date within 30 days of the known date:
382	$date += (int(rand(60)) - 30) * (24 * 60 * 60);
383    }
384    else {
385	$date = 0;
386    }
387    $idprom .= pack("N", $date);
388
389    # add in the serial number:
390    $idprom .= substr(pack("N", $serial), 1);
391
392    # calculate the checksum:
393    @bytes = (unpack("C*", $idprom));
394    $cksum_new = 0;
395    foreach $byte (@bytes) {
396	$cksum_new ^= $byte;
397    }
398    $idprom .= pack("C", $cksum_new);
399
400    # add the extra 16 bytes:
401    $idprom .= pack("NNNN", 0, 0, 0, 0);
402
403    # dump out the IDPROM:
404    print $idprom;
405}
406
407# done:
408exit(0);
409
410