From c3b046a81750b5bec58ce8ec03ad0f818831fece Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 22 Mar 2015 22:15:40 +0100 Subject: [PATCH] initial checkin of project --- Makefile | 11 + README | 12 ++ rtl8168-eeprom.c | 523 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 546 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 rtl8168-eeprom.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fde02af --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +CFLAGS=-Wall -g `pkg-config --cflags libpci` +LDFLAGS=`pkg-config --libs libpci` + +%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $^ + +rtl8168-eeprom: rtl8168-eeprom.o + $(CC) $(LDFLAGS) -o $@ $^ + +clean: + rm -f rtl8168-eeprom rtl8168-eeprom.o diff --git a/README b/README new file mode 100644 index 0000000..be741c6 --- /dev/null +++ b/README @@ -0,0 +1,12 @@ +rtl8168-mac-eeprom utility + +Usage: + + ./rtl8168_mac_eeprom +will generate an EEPROM backup file for all supported cards + + ./rtl8168_mac_eeprom -s 1:0 +will generate an EEPROM backup file for PCI slot 1:0 + + ./rtl8168_mac_eeprom -s 1:0 -m 01:02:03:04:05:06 +will set the new mac address 01:02:03:04:05:06 to PCI slot 1:0 diff --git a/rtl8168-eeprom.c b/rtl8168-eeprom.c new file mode 100644 index 0000000..4dee70f --- /dev/null +++ b/rtl8168-eeprom.c @@ -0,0 +1,523 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* utility to program MAC address of RTL8168 / RTL8111E EEPROM + * (C) 2015 by Harald Welte + */ + +static void die(char *msg, ...) +{ + va_list args; + + va_start(args, msg); + vfprintf(stderr, msg, args); + fputc('\n', stderr); + exit(1); +} + +static int macaddr_parse(uint8_t *out, const char *in) +{ + /* 00:00:00:00:00:00 */ + char tmp[18]; + char *tok; + unsigned int i = 0; + + if (strlen(in) < 17) + return -1; + + strncpy(tmp, in, sizeof(tmp)-1); + tmp[sizeof(tmp)-1] = '\0'; + + for (tok = strtok(tmp, ":"); tok && (i < 6); tok = strtok(NULL, ":")) { + unsigned long ul = strtoul(tok, NULL, 16); + out[i++] = ul & 0xff; + } + + return 0; +} + + +/* + * RTL8188E SPI Access + */ + +enum rtlspi_pin { + RTLSPI_CS, + RTLSPI_CK, + RTLSPI_SI, + RTLSPI_SO, +}; + +const uint8_t pin2bit[] = { +#if 0 + /* according to RTL8111F Series EEPROM & eFuse data sheet */ + [RTLSPI_CS] = 0x40, + [RTLSPI_CK] = 0x20, + [RTLSPI_SI] = 0x10, + [RTLSPI_SO] = 0x08, +#define RTLSPI_C0_REG 0x51 +#else + /* according to non-mainline driver patch, works on 8111E */ + [RTLSPI_CS] = 0x08, + [RTLSPI_CK] = 0x04, + [RTLSPI_SI] = 0x02, + [RTLSPI_SO] = 0x01, +#define RTLSPI_C0_REG 0x50 +#endif +}; + + +static void rtlspi_delay(void) +{ + usleep(3); +} + +/* this is ugly and should go */ +int g_memfd; +void *g_mapped_base; + +#define rtl_reg_readb(offs) *((uint8_t *) g_mapped_base + offs) +#define rtl_reg_writeb(offs, val) *((uint8_t *) g_mapped_base + offs) = (val) + +int rtlspi_init(struct pci_dev *d) +{ + size_t page_size = (size_t) sysconf (_SC_PAGESIZE); + + g_memfd = open("/dev/mem", O_RDWR | O_SYNC); + if (g_memfd < 0) { + perror("open"); + return -1; + } + g_mapped_base = mmap(0, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, g_memfd, d->base_addr[2] & PCI_ADDR_MEM_MASK); + if (g_mapped_base == MAP_FAILED) { + perror("mmap"); + close(g_memfd); + return -1; + } + +#if 0 + uint8_t v; + v = rtl_reg_readb(0x52); + v &= ~0xc0; + rtl_reg_writeb(0x52, v); +#endif + + return 0; +} + +int rtlspi_fini(struct pci_dev *d) +{ + munmap(g_mapped_base, d->size[2]); + close(g_memfd); + return 0; +} + +int rtlspi_pin_set(struct pci_dev *d, enum rtlspi_pin pin, int lvl) +{ + uint8_t val; + + if (pin > (sizeof(pin2bit)/sizeof(pin2bit[0]))) + return -1; + + val = rtl_reg_readb(RTLSPI_C0_REG); + val |= 0x80; + val &= ~0x40; + if (lvl) + val |= pin2bit[pin]; + else + val &= ~pin2bit[pin]; + + rtl_reg_writeb(RTLSPI_C0_REG, val); + + return 0; +} + +int rtlspi_pin_get(struct pci_dev *d, enum rtlspi_pin pin) +{ + uint8_t val; + + if (pin > (sizeof(pin2bit)/sizeof(pin2bit[0]))) + return -1; + + val = rtl_reg_readb(RTLSPI_C0_REG); + + if (val & pin2bit[pin]) + return 1; + else + return 0; +} + +int rtlspi_xceive_bit(struct pci_dev *d, int lvl) +{ + int rc; + + /* set the bit */ + rc = rtlspi_pin_set(d, RTLSPI_SI, lvl); + if (rc < 0) + return rc; + + rtlspi_delay(); + + /* raising edge on CK */ + rc = rtlspi_pin_set(d, RTLSPI_CK, 1); + if (rc < 0) + return rc; + + rtlspi_delay(); + + /* falling edge of clock */ + rc = rtlspi_pin_set(d, RTLSPI_CK, 0); + if (rc < 0) + return rc; + + rc = rtlspi_pin_get(d, RTLSPI_SO); + + rtlspi_delay(); + + return rc; +} + +int rtlspi_xceive_bits(struct pci_dev *d, uint32_t data, int num_bits) +{ + int i, rc; + uint32_t in = 0; + + for (i = 0; i < num_bits; i ++) { + uint32_t bit_out = (data >> (num_bits - i -1)) & 1; + in <<= 1; + rc = rtlspi_xceive_bit(d, bit_out); + if (rc < 0) + return rc; + in |= rc & 1; + } + + return in; +} + + +/* + * AT93 EEPROM + */ + +enum at93_op { + AT93_OP_MISC = 0, + AT93_OP_WRITE = 1, + AT93_OP_READ = 2, + AT93_OP_ERASE = 3, +}; + +static int at93c46_op(struct pci_dev *d, enum at93_op op, uint8_t addr, uint16_t data) +{ + uint32_t in; + int rc; + + /* set initial state of pins: CS/SK/DI low */ + rc = rtlspi_pin_set(d, RTLSPI_CS, 0); + rc |= rtlspi_pin_set(d, RTLSPI_CK, 0); + rc |= rtlspi_pin_set(d, RTLSPI_SI, 0); + if (rc) + return -1; + + rtlspi_delay(); + + /* start with raising edge on CS */ + rc = rtlspi_pin_set(d, RTLSPI_CS, 1); + + rtlspi_delay(); + + /* start bit */ + rc = rtlspi_xceive_bit(d, 1); + + /* READ command bits */ + rc = rtlspi_xceive_bit(d, op & 2); + rc = rtlspi_xceive_bit(d, op & 1); + + /* send address */ + rc = rtlspi_xceive_bits(d, addr, 6); + + switch (op) { + case AT93_OP_ERASE: + case AT93_OP_MISC: + in = 0; + break; + default: + /* read/write data */ + in = rtlspi_xceive_bits(d, data, 16); + break; + } + + /* stop with falling edge on CS */ + rc = rtlspi_pin_set(d, RTLSPI_CS, 0); + + if (rc) + return -1; + + return in; +} + +/* read one word */ +int at93c46_op_read(struct pci_dev *d, uint8_t addr) +{ + return at93c46_op(d, AT93_OP_READ, addr, 0); +} + +/* write one word */ +int at93c46_op_write(struct pci_dev *d, uint8_t addr, uint16_t data) +{ + int rc = at93c46_op(d, AT93_OP_WRITE, addr, data); + if (rc < 0) + return rc; + return 0; +} + +/* endable write access */ +int at93c46_op_ewen(struct pci_dev *d) +{ + return at93c46_op(d, AT93_OP_MISC, 0x30, 0); +} + +/* endable write access */ +int at93c46_op_erase(struct pci_dev *d, uint8_t addr) +{ + int i, rc; + + rc = at93c46_op(d, AT93_OP_ERASE, addr, 0); + if (rc < 0) + return rc; + + /* wait until ERASE cycle has finished */ + rtlspi_delay(); + rtlspi_pin_set(d, RTLSPI_CS, 1); + for (i = 0; i < 0xffff; i++) { + rc = rtlspi_pin_get(d, RTLSPI_SO); + //rc = rtlspi_xceive_bit(d, 0); + if (rc == 1) + break; + } + rtlspi_pin_set(d, RTLSPI_CS, 0); + + if (i == 0xffff) { + fprintf(stderr, "timeout during ERASE\n"); + return -1; + } + + return 0; +} + +/* disable write access */ +int at93c46_op_ewds(struct pci_dev *d) +{ + return at93c46_op(d, AT93_OP_MISC, 0x00, 0); +} + +static int eeprom_magic_ok(struct pci_dev *p) +{ + if (at93c46_op_read(p, 0) != 0x8129) + return 0; + if (at93c46_op_read(p, 1) != 0x10ec) + return 0; + if (at93c46_op_read(p, 2) != 0x8168) + return 0; + return 1; +} + +static int eeprom_get_mac(struct pci_dev *p, uint8_t *mac_addr) +{ + uint16_t tmp; + + if (!eeprom_magic_ok(p)) + return -1; + + tmp = at93c46_op_read(p, 7); + mac_addr[0] = tmp & 0xff; + mac_addr[1] = tmp >> 8; + tmp = at93c46_op_read(p, 8); + mac_addr[2] = tmp & 0xff; + mac_addr[3] = tmp >> 8; + tmp = at93c46_op_read(p, 9); + mac_addr[4] = tmp & 0xff; + mac_addr[5] = tmp >> 8; + + return 0; +} + +static int eeprom_set_mac(struct pci_dev *p, const uint8_t *mac_addr) +{ + uint16_t tmp; + int rc = 0; + + if (!eeprom_magic_ok(p)) + return -1; + + printf("Writing new MAC address %02x:%02x:%02x:%02x:%02x:%02x...\n", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], + mac_addr[4], mac_addr[5]); + + at93c46_op_ewen(p); + + tmp = mac_addr[0] | mac_addr[1] << 8; + rc |= at93c46_op_erase(p, 7); + rc |= at93c46_op_write(p, 7, tmp); + + tmp = mac_addr[2] | mac_addr[3] << 8; + rc |= at93c46_op_erase(p, 8); + rc |= at93c46_op_write(p, 8, tmp); + tmp = mac_addr[4] | mac_addr[5] << 8; + + rc |= at93c46_op_erase(p, 9); + rc |= at93c46_op_write(p, 9, tmp); + if (rc) + die("Error during EEPROM WRITE, MAC address is corrupt!\n"); + + at93c46_op_ewds(p); + + printf("You need to COLD BOOT for the new address to be used\n"); + + return 0; +} + +/* generate a full dump */ +static int eeprom_backup(struct pci_dev *p) +{ + uint8_t mac_addr[6]; + char fname[PATH_MAX]; + int i, rc, outfd; + + rc = eeprom_get_mac(p, mac_addr); + if (rc < 0) + return rc; + + snprintf(fname, sizeof(fname), "%02X%02X%02X%02X%02X%02X.backup", + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + + outfd = open(fname, O_CREAT|O_WRONLY); + if (!outfd) + die("Can't open/create %s\n", fname); + + printf("Saving EEPROM backup to %s\n", fname); + + for (i = 0; i < 64; i++) { + uint8_t tmp[2]; + int rc = at93c46_op_read(p, i); + if (rc < 0) + die("Error reading EEPROM addr %d\n", i); + + tmp[0] = rc & 0xff; + tmp[1] = rc >> 8; + if (write(outfd, tmp, 2) != 2) + die("Error writing to backup file %s\n", fname); + } + close(outfd); + + return 0; +} + +static void iterate_devices(struct pci_access *pa, char *filter_id, char *filter_slot, + const uint8_t *new_mac) +{ + struct pci_filter filt; + struct pci_dev *p; + char *msg; + + /* filter for specific devices only */ + printf("building filter\n"); + pci_filter_init(pa, &filt); + + if (filter_id) { + msg = pci_filter_parse_id(&filt, filter_id); + if (msg) + die(msg); + } + if (filter_slot) { + msg = pci_filter_parse_slot(&filt, filter_slot); + if (msg) + die(msg); + } + + printf("starting bus iteration\n"); + for (p = pa->devices; p; p = p->next) { + uint8_t old_mac[6]; + if (!pci_filter_match(&filt, p)) + continue; + + /* our own clumsy implementation of filtering */ + if (p->vendor_id != 0x10ec || p->device_id != 0x8168) + continue; + + printf("found matching device (%02x:%02x.%d), ", p->bus, p->dev, p->func); + printf("base_addr=0x%lx (len=%lu)\n", p->base_addr[2] & PCI_ADDR_MEM_MASK, p->size[2]); + + if (rtlspi_init(p) < 0) + die("Cannot initialize RTL SPI mode\n"); + + if (!eeprom_magic_ok(p)) + die("EEPROM Magic !OK\n"); + + if (eeprom_get_mac(p, old_mac) < 0) + die("Cannot read existing MAC addr from EEPROM\n"); + + printf("Existing/Old MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", + old_mac[0], old_mac[1], old_mac[2], + old_mac[3], old_mac[4], old_mac[5]); + + /* will die itself */ + eeprom_backup(p); + + if (new_mac) + eeprom_set_mac(p, new_mac); + + rtlspi_fini(p); + + /* we support only one device per execution (for now) */ + if (new_mac) + exit(0); + } +} + +int main(int argc, char **argv) +{ + int i; + char *filter_id = NULL; + char *filter_slot = NULL; + uint8_t new_macbuf[6]; + uint8_t *new_mac = NULL; + struct pci_access *pa = pci_alloc(); + + /* same syntax as lspci */ + while ((i = getopt(argc, argv, "d:s:m:")) != -1) { + switch (i) { + case 'd': + filter_id = optarg; + break; + case 's': + filter_slot = optarg; + break; + case 'm': + if (macaddr_parse(new_macbuf, optarg)) + die("Unable to parse `%s' as mac address\n", optarg); + new_mac = new_macbuf; + break; + default: + die("Syntax error"); + } + } + + printf("initializing pci access\n"); + pa->error = die; + pa->writeable = 1; + pci_init(pa); + pci_scan_bus(pa); + + iterate_devices(pa, filter_id, filter_slot, new_mac); + + exit(0); +}