SiS63x MAC address change tool.

David M. Wilson dw-clustermatic.org at botanicus.net
Mon Mar 24 04:59:01 CET 2003


Hi there,

I recently had hell whilest building a cluster. We thought it would be
intelligent at the time to hot-swap BIOS chips that failed flashing into
a running motherboard for re-flashing. As per documentation in previous
threads on this list, all the BIOS chips ended up with that
motherboard's MAC address.

Thanks to the author of write_cmos.c, we were able to repair the damage
(as least temporarily), but needed to make a few little small changes to
write_cmos.c so that we didn't have to re-compile it each time to change
the MAC address, and also, the PCI address of the ISA bridge was
different from the #define in the original code.

I include my version here for the sake of people who may not be able to
perform the modifications I made, along with a large comment at the top
explaining the concept.

My hardware knowledge is zilch, and these changes may be wrong. It
worked for me though..

Thanks again Steve!

David.
-------------- next part --------------
/*
   Program to set the CMOS MAC address on the SiS630e chipset.
   By Steve M. Gehlbach (steve at kesa.com), silly modifications made by
   David M. Wilson <dw-clustermatic.org at botanicus.net>.

   Based on the work of SiS for the sis900.c driver in the 2.4.19 kernel.

   1. Compile this program with "cc -o write_cmos write_cmos.c".

   2. Type 'lspci -v' or 'cat /proc/pci' at the shell prompt, and scan for 'ISA
      bridge' in the list. If using lspci, note the numbers to the left of the
      line, eg. "04:01.0", these are your bus, device, and function numbers. If
      using cat, note the bus, device and function numbers given.

   3. Run write_cmos, with the bus, device, and function number on the command
      line, followed by the MAC address bytes you would like to set the
      motherboard to. For example, if your ISA bridge is at address 04, 01, 0,
      and you want the motherboard to become 00:0A:E6:54:AB:01, your command
      line may look something like:

         root at node$ ./write_cmos 4 1 0 00 0a e6 54 ab 01

   4. The program will prompt to confirm the MAC address to write (this is just
      a safety measure). Press the return key.

   5. Verify the MAC address read back matches that which you specified on the
      command line. If they match, the modification should have been
      successful. Reboot your machine in order for the ethernet driver to pick
      up the new MAC address.

   6. Grab a fresh cup of coffee. :-)


   Expected output (successful modification):

      [root at node13 projects]# ./write_cmos 0 2 0 00 0a e6 54 ab 01
      MAC Address to write: 00:0a:e6:54:ab:01
      Press <CR> to continue.
      Setting MAC... done.
      reading back MAC: 00 0a e6 54 ab 01
 
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <sys/io.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>

extern const char *const sys_errlist[];
#define CONFIG_CMD(bus,devfn, where)   (0x80000000 | (bus << 16) | (devfn << 8) | (where & ~3))
#define u8 u_char
#define u16 u_short
#define u32 u_long
#define PCI_DEVFN(slot,func)    ((((slot) & 0x1f) << 3) | ((func) & 0x07))
#define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
#define PCI_FUNC(devfn)         ((devfn) & 0x07)

static int pci_conf1_read_config_word(unsigned char bus, int devfn, int where, u16 * value) {
	outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
	*value = inw(0xCFC + (where & 2));
	return 0;
}

static int pci_conf1_write_config_byte(unsigned char bus, int devfn, int where, u8 value) {
	outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
	outb(value, 0xCFC + (where & 3));
	return 0;
}

static int pci_conf1_read_config_byte(unsigned char bus, int devfn, int where, u8 * value) {
	outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
	*value = inb(0xCFC + (where & 3));
	return 0;
}
static int pci_conf1_write_config_word(unsigned char bus, int devfn, int where, u16 value)
{
	outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
	outw(value, 0xCFC + (where & 2));
	return 0;
}

int main (int argc, char **argv)
{
	u_char v1,reg;
	u8 mac_set[6] = {0x00,0x0a,0xe6,0x54,0xac,0xb7};
	u8 mac[6];
	char junkbuf[2];
	u_short port;
	int i, bus, devfn;

	if (iopl(3) < 0 )
	{
		perror("iopl");
		printf("%s: You must run this program as root!\n",argv[0]);
		return 1;
	}

	if (argc < 10)
	{
		fprintf(stderr,
			"Please specify ISA bridge address and MAC bytes.\n"
			"eg.: %s <bus> <dev> <fn> 00 0A E6 AA BB CC\n",
			argv[0]
		);
	
		return 1;
	}

	bus = atoi(argv[1]);
	devfn = PCI_DEVFN(atoi(argv[2]), atoi(argv[3]));

	mac_set[0] = strtoul(argv[4], NULL, 16);
	mac_set[1] = strtoul(argv[5], NULL, 16);
	mac_set[2] = strtoul(argv[6], NULL, 16);
	mac_set[3] = strtoul(argv[7], NULL, 16);
	mac_set[4] = strtoul(argv[8], NULL, 16);
	mac_set[5] = strtoul(argv[9], NULL, 16);

	printf(
		"MAC Address to write: %02x:%02x:%02x:%02x:%02x:%02x\n"
		"Press <CR> to continue.",
		mac_set[0], mac_set[1], mac_set[2],
		mac_set[3], mac_set[4], mac_set[5]
	);

	fgets(junkbuf, sizeof junkbuf, stdin);

	// map the APC registers into cmos
	pci_conf1_read_config_byte(bus, devfn, 0x48, &reg);
	pci_conf1_write_config_byte(bus, devfn, 0x48, reg | 0x40);

	// mac address is at offset 0x9
	// write it there
	printf("Setting MAC... ");
	for (i = 0; i < 6; i++) {
		outb(0x09 + i, 0x70);
		outb(mac_set[i],0x71); 
	}

	printf("done.\n");
	
	// now read it back to confirm
	printf("reading back MAC: ");
	for (i = 0; i < 6; i++) {
		outb(0x09 + i, 0x70);
		mac[i] = inb(0x71); 
		printf("%02x ",mac[i]);
	}
	printf("\n");
	
	// restore the cmos mapping to normal
	pci_conf1_write_config_byte(bus, devfn, 0x48, reg & ~0x40);
}


More information about the coreboot mailing list