#include #include #include #include #include #include #include #include #include #ifndef OLD_DB_FORMAT # include #endif #define __packed __attribute__((__packed__)) #define MAC_NUM 3 #define XIOH_DATA_OFFSET 0x100000 #define XIOH_DATA_VERSION 2 #define FIRST_SERIAL_NUMBER 42 static const char xioh_data_magic[] = {'X','I','O','H'}; enum xioh_hardware_version { XIOH_HARDWARE_VERSION_4 = 1, XIOH_HARDWARE_VERSION_5 = 2, }; #define XIOH_HARDWARE_VERSION XIOH_HARDWARE_VERSION_5 typedef union mac { uint8_t b[6]; uint64_t n:48; } __packed mac; struct serial { uint16_t date; unsigned version:4; unsigned number:12; } __packed; struct xioh_data { char magic[4]; uint16_t version; uint8_t cksum; uint8_t reserved; struct serial serial; mac addr[MAC_NUM]; } __packed; void record_use(char *fname, struct xioh_data *xioh_data); bool mac_used(const char *fname, mac *addr); bool serial_used(const char *fname, int serial); bool parse_mac(char* src, mac *dst); mac mac_between(mac from, mac to); unsigned get_next_serial(void); void set_next_serial(unsigned serial); void choose_macs(mac from, mac to, struct xioh_data *xioh_data); void print_mac_le(FILE* h, mac mac); void print_mac_be(FILE* h, mac mac); void reverse(uint8_t *t, int n); void write_serial(struct serial *serial, int serial_number); void dump_xioh_data(const struct xioh_data *xioh_data); void write_xioh_data(const char *fname, const struct xioh_data *xioh_data); void write_standalone(const char *fname, const struct xioh_data *xioh_data); uint8_t cksum(const uint8_t* addr, size_t sz); void get_xioh_data(char *fname, int serial, struct xioh_data *xioh_data); int rand_range(int max) { return (int)(rand()/(double)RAND_MAX*max); } unsigned date(void) { return time(NULL) / 86400; } static char *next_serial = "next_serial.txt"; #ifdef OLD_DB_FORMAT static char *dbf = "db.bin"; #else static char *dbf = "db.sqlite"; #endif static void print_usage(char *name) { fprintf(stderr, "Usage: %s [options]\n" "\t-c FILE\tCoreboot ROM file where the addresses are " "to be written\n" "\t-s FILE\tCreate a file for use with cbfstool\n" "\t-S FILE\tGet and store the next serial number in " "this file\n" "\t-d FILE\tDatabase file for used serial numbers and " "MAC addresses\n" "\t-m MAC\tUse this MAC address + the 2 next\n" "\t-f MAC\tUse random MAC addresses (start of range)\n" "\t-t MAC\tUse random MAC addresses (end of range)\n" "\t-n NUM\tUse this number for the serial\n" "\t-r NUM\tInstead of building a new ROM, reflash the one with this number\n" , name); } int main(int argc, char *argv[]) { struct xioh_data xioh_data; int i; char *fname = "coreboot.rom"; char *standalone = NULL; mac from, to; bool hasfrom = false, hasto = false; int serial_number = -1; enum { MODE_UNSET, RANDOM, CONTINUOUS, REFLASH } mode = MODE_UNSET; srand(time(NULL)+getpid()); while (true) { int c = getopt(argc, argv, "s:m:f:t:n:c:d:S:r:"); if (c == -1) break; switch (c) { case 'c': fname = optarg; break; case 's': standalone = optarg; break; case 'd': dbf = optarg; break; case 'S': next_serial = optarg; break; case 'm': mode = CONTINUOUS; parse_mac(optarg, &xioh_data.addr[0]); break; case 'f': mode = RANDOM; hasfrom = true; parse_mac(optarg, &from); break; case 't': mode = RANDOM; hasto = true; parse_mac(optarg, &to); break; case 'r': mode = REFLASH; /* intentional fallthrough */ case 'n': { char *endptr; serial_number = strtol(optarg, &endptr, 0); if (!*optarg || *endptr || serial_number < 0) { fprintf(stderr, "-%c requires a positive number\n", c); exit(1); } } break; default: print_usage(argv[0]); exit(1); } } switch (mode) { case CONTINUOUS: for(i = 1; i < MAC_NUM; i++) do xioh_data.addr[i].n = xioh_data.addr[i-1].n + 1; while (mac_used(dbf, &xioh_data.addr[i])); break; case RANDOM: if (!(hasfrom && hasto)) { fprintf(stderr, "Needs both -f and -t\n"); exit(1); } choose_macs(from, to, &xioh_data); break; case REFLASH: get_xioh_data(dbf, serial_number, &xioh_data); break; case MODE_UNSET: print_usage(argv[0]); exit(1); } if (mode != REFLASH) { memcpy(xioh_data.magic, xioh_data_magic, sizeof(xioh_data.magic)); xioh_data.version = XIOH_DATA_VERSION; write_serial(&xioh_data.serial, serial_number); for (i = 0; i < MAC_NUM; i++) reverse(xioh_data.addr[i].b, 6); xioh_data.reserved = 0; xioh_data.cksum = 0; xioh_data.cksum = cksum((uint8_t*)&xioh_data, sizeof(xioh_data)); } dump_xioh_data(&xioh_data); if (standalone) write_standalone(standalone, &xioh_data); else write_xioh_data(fname, &xioh_data); if (mode != REFLASH) record_use(dbf, &xioh_data); return 0; } uint8_t cksum(const uint8_t* addr, size_t sz) { uint8_t r = 0; for (; sz; sz--) r ^= *addr++; return r; } void write_standalone(const char *fname, const struct xioh_data *xioh_data) { FILE *f = fopen(fname, "w"); if (!f) { fprintf(stderr, "Can't open file \"%s\": %s\n", fname, strerror(errno)); exit(errno); } fwrite(xioh_data, sizeof(*xioh_data), 1, f); fclose(f); } void write_xioh_data(const char *fname, const struct xioh_data *xioh_data) { FILE *rom = fopen(fname, "r+"); if (!rom) { fprintf(stderr, "Can't open file \"%s\": %s\n", fname, strerror(errno)); exit(errno); } if(fseek(rom, XIOH_DATA_OFFSET, SEEK_SET)) { fprintf(stderr, "Can't seek: %s\n", strerror(errno)); exit(errno); } fwrite(xioh_data, sizeof(*xioh_data), 1, rom); fclose(rom); } void dump_xioh_data(const struct xioh_data *xioh_data) { int i; time_t time = xioh_data->serial.date * 86400; char *timestr = ctime(&time); timestr[strlen(timestr)-1] = '\0'; printf("Magic: %c%c%c%c\n" "Version: %u\n" "Checksum: %02x\n" "Serial:\n" "\tDate: %04x (%s)\n" "\tHardware version: %u\n" "\tNumber: %u\n", xioh_data->magic[0], xioh_data->magic[1], xioh_data->magic[2], xioh_data->magic[3], xioh_data->version, xioh_data->cksum, xioh_data->serial.date, timestr, xioh_data->serial.version, xioh_data->serial.number); printf("MAC addresses:\n"); for (i = 0; i < MAC_NUM; i++) { printf("\t"); print_mac_be(stdout, xioh_data->addr[i]); printf("\n"); } } void choose_macs(mac from, mac to, struct xioh_data *xioh_data) { int i, j; for(i = 0; i < MAC_NUM; i++) { retry: xioh_data->addr[i] = mac_between(from, to); for (j = 0; j < i; j++) if (xioh_data->addr[i].n == xioh_data->addr[j].n) goto retry; if (mac_used(dbf, &xioh_data->addr[i])) goto retry; } } void write_serial(struct serial *serial, int serial_number) { serial->date = date(); serial->version = XIOH_HARDWARE_VERSION; if (serial_number < 0) { serial->number = get_next_serial(); } else if (serial_used(dbf, serial_number)) { fprintf(stderr, "Serial number %d already used!\n", serial_number); exit(2); } else { serial->number = serial_number; } } void set_next_serial(unsigned serial) { FILE *f = fopen(next_serial, "w"); if (!f) { fprintf(stderr, "Can't open file \"%s\": %s\n", next_serial, strerror(errno)); exit(errno); } fprintf(f, "%u", serial); fclose(f); } unsigned get_next_serial(void) { unsigned cur = FIRST_SERIAL_NUMBER; FILE *f = fopen(next_serial, "r"); if (f) { fscanf(f, "%u", &cur); fclose(f); } while (serial_used(dbf, cur)) { fprintf(stderr, "Serial number %d already used! Trying %d.\n", cur, cur+1); cur++; } set_next_serial(cur+1); return cur; } void get_xioh_data(char *fname, int serial, struct xioh_data *xioh_data) { sqlite3 *db; sqlite3_stmt *stmt; int rc; const struct xioh_data *db_data = NULL; printf("%s\n", fname); rc = sqlite3_open(fname, &db); if (SQLITE_OK != rc) { fprintf(stderr, "could not open sqlite database \"%s\" " "(error %d)\n", fname, rc); sqlite3_close(db); goto opened; } rc = sqlite3_prepare_v2(db, "SELECT xioh_data FROM xioh_data " "WHERE serial_number == ?", -1, &stmt, NULL); if (SQLITE_OK != rc) { fprintf(stderr, "could not compile statement (error %d)\n", rc); sqlite3_close(db); goto prepared; } sqlite3_bind_int(stmt, 1, serial); rc = sqlite3_step(stmt); switch (rc) { case SQLITE_ROW: db_data = sqlite3_column_blob(stmt, 0); if (sqlite3_column_bytes(stmt, 0) != sizeof(*xioh_data)) { fprintf(stderr, "Blob size mismatch! (%d should be %d)\n", sqlite3_column_bytes(stmt, 0), sizeof(*xioh_data)); db_data = NULL; goto prepared; } memcpy(xioh_data, db_data, sizeof(*xioh_data)); break; case SQLITE_DONE: fprintf(stderr, "No data for serial number %d\n", serial); break; default: fprintf(stderr, "could not execute statement (error %d)\n", rc); break; } prepared: \ sqlite3_finalize(stmt); \ opened: \ sqlite3_close(db); \ if (!db_data) exit(1); } #ifndef OLD_DB_FORMAT # define TEST_IN_SQLITE3(cond, bind) \ sqlite3 *db; \ sqlite3_stmt *stmt; \ int rc, res; \ \ rc = sqlite3_open(fname, &db); \ if (SQLITE_OK != rc) { \ fprintf(stderr, "could not open sqlite database \"%s\" " \ "(error %d)\n", fname, rc); \ sqlite3_close(db); \ res = -2; \ goto opened; \ } \ \ rc = sqlite3_prepare_v2(db, "SELECT 1 FROM xioh_data " \ "WHERE " cond, \ -1, &stmt, NULL); \ if (SQLITE_OK != rc) { \ fprintf(stderr, "could not compile statement (error %d)\n", rc); \ sqlite3_close(db); \ res = -4; \ goto prepared; \ } \ \ bind; \ \ rc = sqlite3_step(stmt); \ \ switch (rc) { \ case SQLITE_DONE: \ res = false; \ break; \ case SQLITE_ROW: \ res = true; \ break; \ default: \ fprintf(stderr, "could not execute statement (error %d)\n", rc); \ res = -5; \ } \ \ prepared: \ sqlite3_finalize(stmt); \ opened: \ sqlite3_close(db); \ \ if (res < 0) \ exit(-res); \ else \ return res; #endif bool mac_used(const char *fname, mac *addr) { #ifdef OLD_DB_FORMAT FILE *f = fopen(fname, "r"); struct xioh_data xioh_data; unsigned i; if (!f) { fprintf(stderr, "Can't open file \"%s\": %s\n", fname, strerror(errno)); exit(errno); } while (0 < fread(&xioh_data, sizeof(xioh_data), 1, f)) { for (i = 0; i < MAC_NUM; i++) if (xioh_data.addr[i].n == addr->n) { print_mac_le(stderr, *addr); fprintf(stderr, " already used\n"); fclose(f); return true; } } fclose(f); return false; #else TEST_IN_SQLITE3("mac1 == ? OR mac2 == ? OR mac3 == ?", int i; for (i = 1; i <= 3; i++) sqlite3_bind_blob(stmt, i, addr, sizeof(*addr), SQLITE_STATIC)) #endif } bool serial_used(const char *fname, int serial) { #ifdef OLD_DB_FORMAT FILE *f = fopen(fname, "r"); struct xioh_data xioh_data; if (!f) { fprintf(stderr, "Can't open file \"%s\": %s\n", fname, strerror(errno)); exit(errno); } while (0 < fread(&xioh_data, sizeof(xioh_data), 1, f)) { if (xioh_data.serial.number == serial) { fclose(f); return true; } } fclose(f); return false; #else TEST_IN_SQLITE3("serial_number == ?", sqlite3_bind_int(stmt, 1, serial)) #endif } void record_use(char *fname, struct xioh_data *xioh_data) { #ifdef OLD_DB_FORMAT FILE *f = fopen(fname, "a"); if (!f) { fprintf(stderr, "Can't open file \"%s\": %s\n", fname, strerror(errno)); exit(errno); } fwrite(xioh_data, sizeof(*xioh_data), 1, f); fclose(f); #else int rc, i; sqlite3 *db; sqlite3_stmt *stmt; rc = sqlite3_open(fname, &db); if (SQLITE_OK != rc) { fprintf(stderr, "could not open sqlite database \"%s\" (error %d)\n", fname, rc); sqlite3_close(db); exit(2); } rc = sqlite3_prepare_v2(db, "INSERT INTO xioh_data (xioh_data, version, date," "hardware_version, serial_number, mac1, mac2, mac3 ) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?)", -1, &stmt, NULL); if (SQLITE_OK != rc) { fprintf(stderr, "could not compile statement (error %d)\n", rc); sqlite3_close(db); exit(5); } sqlite3_bind_blob(stmt, 1, xioh_data, sizeof(*xioh_data), SQLITE_TRANSIENT); sqlite3_bind_int(stmt, 2, xioh_data->version); sqlite3_bind_int(stmt, 3, xioh_data->serial.date); sqlite3_bind_int(stmt, 4, xioh_data->serial.version); sqlite3_bind_int(stmt, 5, xioh_data->serial.number); for (i = 0; i < MAC_NUM; i++) sqlite3_bind_blob(stmt, 6, &xioh_data->addr[i], sizeof(*xioh_data->addr), SQLITE_TRANSIENT); rc = sqlite3_step(stmt); if (SQLITE_DONE != rc) { fprintf(stderr, "could not insert into database: (error %d)\n", rc); dump_xioh_data(xioh_data); sqlite3_finalize(stmt); sqlite3_close(db); exit(4); } sqlite3_finalize(stmt); rc = sqlite3_close(db); if (SQLITE_OK != rc) { fprintf(stderr, "Error on closing database (error %d)\n", rc); exit(2); } #endif } mac mac_between(mac from, mac to) { return (mac){.n = from.n + rand_range(to.n - from.n)}; } bool parse_mac(char* src, union mac *dst) { return 6 == sscanf(src, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", dst->b+5, dst->b+4, dst->b+3, dst->b+2, dst->b+1, dst->b+0); } void print_mac_le(FILE* h, union mac mac) { fprintf(h, "%02x:%02x:%02x:%02x:%02x:%02x", mac.b[5], mac.b[4], mac.b[3], mac.b[2], mac.b[1], mac.b[0]); } void print_mac_be(FILE* h, union mac mac) { fprintf(h, "%02x:%02x:%02x:%02x:%02x:%02x", mac.b[0], mac.b[1], mac.b[2], mac.b[3], mac.b[4], mac.b[5]); } void reverse(uint8_t *t, int n) { uint8_t s; int i; for (i = 0; i < n/2; i++) { s = t[i]; t[i] = t[n-1-i]; t[n-1-i] = s; } }