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