/* 
 * hid2.c - placed in the public domain by the author - Mark Henderson
 *                                                      mch@squirrel.com
 *                                                      mch@squirrel.com
 * 
 * changes hw_serial via direct writes to /dev/kmem
 * see the file INSTRUCTIONS for more detailed usage instructions
 * must generally run as root (at least with rw permission to /dev/kmem)
 * compile with 
 *   gcc -o hid2 hid2.c -lelf
 * hid2 without any parameters simply reads hw_serial
 * hid2 80808080 changes hw_serial so that hostid appears to be 0x80808080
 *
 * June 1996
 */

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <nlist.h>

struct nlist nl[2];

main(argc,argv)
int argc;
char *argv[];
{
    int kmem;
    off_t where;
    char *s;
    unsigned int new_hostid;
    unsigned char hw_serial[12];
    unsigned char new_hw_serial[12];
    if ((kmem = open("/dev/kmem", O_RDWR)) < 0) {
        fprintf(stderr, "cannot open /dev/kmem\n");
        exit(1);
    }
    nl[0].n_name="hw_serial";
    nl[1].n_name = NULL;
    if (nlist("/dev/ksyms", nl) < 0) {
        fprintf(stderr, "cannot read namelist out of /dev/ksyms\n");
        exit(1);
    }
    if ((where = nl[0].n_value) == 0) {
        fprintf(stderr, "unknown kernel variable hw_serial\n");
        exit(1);
    }
    if (lseek(kmem, where, SEEK_SET) == (-1)) {
        fprintf(stderr, "lseek on /dev/kmem failed\n");
        exit(1);
    }
    if (read(kmem, (char *)&hw_serial[0], 12) < 12) {
        fprintf(stderr, "read from /dev/kmem failed\n");
        exit(1);
    }
    if (lseek(kmem, where, SEEK_SET) == (-1)) {
        fprintf(stderr, "lseek on /dev/kmem failed\n");
        exit(1);
    }
    printf("current hostid is 0x%08x\n", strtoul(hw_serial,NULL,10));
    if (argc > 1) {
        if (lseek(kmem, where, SEEK_SET) == (-1)) {
            fprintf(stderr, "lseek on /dev/kmem failed\n");
            exit(1);
        }
        new_hostid = strtoul(argv[1], NULL, 16);
        printf("setting hostid to 0x%08x\n", new_hostid);
        sprintf((char *)&new_hw_serial, "%u", new_hostid);
        if (write(kmem, (char *)&new_hw_serial[0], strlen(new_hw_serial)+1) 
            < strlen(new_hw_serial) + 1) {
            fprintf(stderr, "write to /dev/kmem failed\n");
            exit(1);
        }
    }
        
    close(kmem);
	return(0);
}
