#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

/* 
get/change hostid in NVRAM --

old version -- I should throw this away. Less friendly but perhaps
  easier to modify for other types of machines.

only tested on sun4m architecture and sun4c architecture. The
kernel patches will be different for Sun4 and I don't have one
to play with. 

This program is still experimental. Dangerous and tricky stuff.
If you are unwilling to experiment and clean up after disasters
don't use it. hid is probably safer. USE AT YOUR OWN RISK.

Note the define OFFSET below!

running this will invalidate the overall NVRAM checksum. So after it
make some trivial change with the eeprom command (change the baud
rate on a serial port or something like that
e.g.
/usr/etc/eeprom diag-switch?=false
)

first you need to turn off the protection in the OS to keep
you from modifying /dev/eeprom locations after 017730 
use adb
adb -k -w /vmunix /dev/mem
_mmeeprom+8/W 901223ff

which changes
or      %o1, 0x3d8, %o1
to
or      %o1, 0x3ff, %o1


Unfortunately this change with adb to the eeprom device seems to depend
on the exact version of Sun OS you are running. Don't assume it will
work without looking around intelligently.

e.g. For SUN OS 4.1.1 on a Sun 4c you need to:
adb -w /vmunix /dev/mem
_mmeeprom+4/W 80a6a7ff
_mmeeprom+0x28/W 80a6a7ff
which changes a couple of compare operations. 

Also be very careful. The first byte of the hostid identifies the type 
of system you are running. On modern Suns OS boot CDs depend on this 
information to get the correct architecture. If you change it you may 
be reduced to poking around in NVRAM from the monitor next time you have
to boot from CD.

Here's a mapping of what gets read into buf (at OFFSET)
buf[0] = always 01?
buf[1] = system id/first byte of hostid.
buf[2] \
buf[3]  \
buf[4]   \  HW ethernet address (6 bytes)
buf[5]    /  
buf[6]  /
buf[7]/
buf[8],...,buf[11] = always 00?
buf[12] = second byte of hostid
buf[13] = third byte of hostid
buf[14] = fourth byte of hostid
buf[15] = checksum

the checksum is buf[0] ^ buf[1] ^ ... ^ buf[14]
i.e. it must be the case that the  xor of buf[0] through buf[15] is 0

Without any parameters, it reports the hostid from NVRAM. Otherwise it
changes it. 

Mark Henderson <mch@squirrel.com>
Placed in the public domain by the author - 23 June 1994

This program is distributed in the hope that it will be useful,
but without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose. You use
this program at your own risk. The author disclaims responsibility for
any damages that might result from the use of this program, even
if they result from negligence on the part of the author.

Also, please don't use this program to steal software. The intended
use is for emergency situations where an application has to be moved from
one computer to another (e.g. in the event of a hardware malfunction)
and licence keys cannot be obtained quickly from the vendor. Many
vendors will not supply licence keys outside of business hours.

*/


#define OFFSET 017730       /* for Sun 4m, Sun OS 4.1.3 */
/* #define OFFSET 03730     /* for Sun 4c, SUN OS 4.1.1 */

#define SANITY_CHECK        /* don't turn this off unless you need to. If this
                               fails it generally means that the program won't
                               work and very well may be destructive */

#undef TEST         /* doesn't actually do mods if TEST is defined */
unsigned char buf[17]; 

main(argc,argv)
int argc;
char *argv[];
{
    unsigned int hid;
    int fd;
    int i;
    unsigned int acc;
    int read_hostid;
    unsigned int hid_reported_by_gethostid = gethostid();

    if (argc == 1) {
        /* no args - just get hostid from prom */
        read_hostid = 1;
    }
    else if (argc == 2) {
        read_hostid = 0;
        sscanf(argv[1],"%08x",&hid);
    }
    else {
        fprintf(stderr, "usage: %s [hostid]\n",argv[0]);
        fprintf(stderr, "  e.g. %s b0b1fb0b\n", argv[0]);
        exit(1);
    }
    if (read_hostid) {
        if ((fd = open("/dev/eeprom", O_RDONLY)) < 0) {
            fprintf(stderr, "cannot open /dev/eeprom for read\n");
            exit(1);
        }
    } 
    else {
#ifdef TEST
        if ((fd = open("/dev/eeprom", O_RDONLY)) < 0) {
            fprintf(stderr, "cannot open /dev/eeprom for read\n");
            exit(1);
        }
#else
        if ((fd = open("/dev/eeprom", O_RDWR)) < 0) {
            fprintf(stderr, "cannot open /dev/eeprom for read\n");
            exit(1);
        }
#endif
    }

    if (lseek(fd,OFFSET,SEEK_SET) < 0) { 
        fprintf(stderr, "lseek failed\n");
        exit(1);
    }

    if (read_hostid) {
        if (read(fd,buf,16) < 16) {
            fprintf(stderr, "read of /dev/eeprom failed\n");
            exit(1);
        }
        printf("%02x%02x%02x%02x\n", buf[1], buf[12], buf[13], buf[14]);
        for (acc = 0 , i=0; i<15; i++) acc^=buf[i];
        if (acc != buf[15])  {
            fprintf(stderr, "warning: ID CHKSUM INVALID\n");
            fprintf(stderr,"^buf = %02x\n", acc & 0xff);
            fprintf(stderr,"buf[15] = %02x\n", buf[15] & 0xff);
        }
    }
    else {
#ifdef SANITY_CHECK 
/* this will fail if you have modified the value returned by
   gethostid by using one of the other programs in this package.
   In that case I wouldn't turn this off but restore your old 
   gethostid behaviour or replace the gethostid call above with
   a reference to your "real" hostid, if your architecture 
   is weird this should detect the problem and not try and
   write into places we shouldn't. */

        if (read(fd,buf,16) < 16) {
            fprintf(stderr, "read of /dev/eeprom failed\n");
            exit(1);
        }
#ifdef TEST
        for (i=0; i<16; i++)
            printf("%02x ", buf[i]);
        printf("\n");
#endif
        for (acc = 0 , i=0; i<15; i++) acc^=buf[i];
        if ((acc != buf[15]) 
            || (buf[1] != (hid_reported_by_gethostid >> 24) )
            || (buf[12] != ((hid_reported_by_gethostid >> 16) &0xff))
            || (buf[13] != ((hid_reported_by_gethostid >> 8) &0xff))
            || (buf[14] != (hid_reported_by_gethostid &0xff))) {
        
            fprintf(stderr, "SANITY CHECK FAILED -- aborting\n");
            fprintf(stderr, 
                "perhaps the value of the define OFFSET is wrong\n");
            exit(1);
        }
            
        if (lseek(fd,OFFSET,SEEK_SET) < 0) { 
            fprintf(stderr, "lseek failed\n");
            exit(1);
        }

#endif
        buf[1] = hid >> 24;
        buf[12] = (hid >> 16) &0xff;
        buf[13] = (hid >> 8) &0xff;
        buf[14] = hid &0xff;
        for (acc = 0 , i=0; i<15; i++) acc^=buf[i];
        buf[15] = acc & 0xff;
#ifndef TEST
        if (write(fd,buf,16) < 16) {
            fprintf(stderr, "write failed\n");
        }
#else
    for (i=0; i<16; i++)
        printf("%02x ", buf[i]);
    printf("\n");

#endif
    }
    close(fd);
	return(0);
}

