/************************************************************************ Calculate ROM size for 3rd byte and ROM checksum for 6th byte in ROM image. -3 option makes the last two bytes of an 8k ROM 0x80. The 3c503 ASIC will report this value regardless of the ROM contents, so we need to make the checksum work properly. A 3Com EtherStart ROM I have handy sets these to 0x80, so we'll use that for now. 0x04 has also been reported. Any more offers? Added capability to handle PCI and PnP headers. Detection is automatic. Need to add command line argument to change Vendor and Device ID. ************************************************************************/ #include #include #include #include #if defined(__TURBOC__) || defined(__BORLANDC__) typedef long off_t; #endif /* should be powers of 2 and MAX a multiple of MIN */ #define MINROMSIZE 8192L #define MAXROMSIZE 65536L #define MAGIC_3C503 0x80 #define PCI_PTR_LOC 0x18 /* from beginning of ROM */ #define PCI_HDR_SIZE 0x18 #define PNP_PTR_LOC 0x1a /* from beginning of ROM */ #define PNP_HDR_SIZE 0x20 #define PNP_CHKSUM_OFF 0x9 /* bytes from beginning of PnP header */ #define PCI_VEND_ID_OFF 0x4 /* bytes from beginning of PCI header */ #define PCI_DEV_ID_OFF 0x6 /* bytes from beginning of PCI header */ #define PCI_SIZE_OFF 0x10 /* bytes from beginning of PCI header */ unsigned char *rom; long romsize = 0L; /* for autosizing */ char *identstring = 0; int verbose = 0; int pnp_hdr_offset = 0, pci_hdr_offset = 0; int pci_vendor_id = 0, pci_device_id = 0; extern int getopt(int argc, char *argv[], char *options); /* read the first three bytes to get the ROM size */ long getromsize(FILE *fd) { unsigned char buffer[3]; long size, i; if (fread(buffer, sizeof(char), 3, fd) != 3) { fprintf(stderr, "Cannot get first 3 bytes of file\n"); exit(1); } /* reset pointer to beginning of file */ if (fseek(fd, (off_t)0, SEEK_SET) < 0) { perror("fseek"); exit(1); } size = buffer[2] * 512L; /* sizes are usually powers of two, warn if not */ for (i = MINROMSIZE; i < MAXROMSIZE && i < size; i *= 2) ; if (size > 0 && i > size) fprintf(stderr, "%ld is a strange size for a boot ROM\n", size); return (size); } void addident(void) { int len = strlen(identstring); /* Put the identifier in only if the space is blank */ if (strspn(&rom[romsize-len-2], "\377") >= len) memcpy(&rom[romsize-len-2], identstring, len); } /* Accepts a spec of the form vendorid,deviceid where the ids are numeric strings accepted by strtoul */ void getpciids(char *spec) { char *vendor, *device; unsigned long value; char *endptr; vendor = spec; device = strchr(spec, ','); if (device != 0) *device++ = '\0'; value = strtoul(vendor, &endptr, 0); if (*vendor != '\0' && endptr != vendor && *endptr == '\0') pci_vendor_id = value; if (device == 0) return; value = strtoul(device, &endptr, 0); if (*device != '\0' && endptr != device && *endptr == '\0') pci_device_id = value; } int main(int argc, char **argv) { int i; long fs; FILE *fd; unsigned int sum; char *progname; int is3c503; extern int optind; extern char *optarg; progname = argv[0]; is3c503 = 0; while ((i = getopt(argc, argv, "3i:p:s:v")) >= 0) { switch (i) { case '3': is3c503 = 1; break; case 'i': identstring = optarg; break; case 'p': getpciids(optarg); break; case 's': romsize = atol(optarg); if (romsize <= 0) romsize = 32768L; break; case 'v': ++verbose; break; } } argc -= optind; argv += optind; if (argc < 1) { #if defined(__TURBOC__) || defined(__BORLANDC__) fprintf(stderr, "Usage: %s [/s romsize] [/i ident] [/p vendorid,deviceid] [/3] rom-file\n", progname); #else fprintf(stderr, "Usage: %s [-s romsize] [-i ident] [-p vendorid,deviceid] [-3] rom-file\n", progname); #endif exit(1); } if ((fd = fopen(argv[0], "rb+")) == NULL) { perror(argv[0]); exit(1); } /* If size not specified, infer it from 3rd byte */ if (romsize == 0) romsize = getromsize(fd); /* If that is 0, choose the right size */ if (romsize == 0) romsize = MAXROMSIZE; if ((rom = malloc(romsize)) == 0) { fprintf(stderr, "Cannot malloc memory for ROM buffer\n"); exit(1); } /* fill with FFs, slightly less work for PROM burner and allows limited patching */ memset(rom, 0xFF, romsize); if ((fs = fread(rom, sizeof(char), romsize, fd)) < 0) { perror("fread"); exit(1); } if (verbose) printf("%ld bytes read\n", fs); if (fs == romsize && fgetc(fd) != EOF) { fprintf(stderr, "ROM size of %ld not big enough for data\n", romsize); exit(1); } /* shrink it down to the smallest size that will do */ for (romsize = MAXROMSIZE; romsize > MINROMSIZE && romsize >= 2*fs; ) romsize /= 2L; rom[2] = romsize / 512L; rom[5] = 0; if (verbose) printf("ROM size is %ld\n", romsize); if (identstring != 0) addident(); pci_hdr_offset = rom[PCI_PTR_LOC] + (rom[PCI_PTR_LOC+1] << 8); pnp_hdr_offset = rom[PNP_PTR_LOC] + (rom[PNP_PTR_LOC+1] << 8); /* sanity checks */ if (pci_hdr_offset < PCI_PTR_LOC + 2 || pci_hdr_offset > romsize - PCI_HDR_SIZE || pnp_hdr_offset <= PCI_PTR_LOC + 2 || pnp_hdr_offset > romsize - PNP_HDR_SIZE) pci_hdr_offset = pnp_hdr_offset = 0; else if (memcmp(&rom[pci_hdr_offset], "PCIR", sizeof("PCIR")-1) != 0 || memcmp(&rom[pnp_hdr_offset], "$PnP", sizeof("$PnP")-1) != 0) pci_hdr_offset = pnp_hdr_offset = 0; else printf("PCI header at 0x%x and PnP header at 0x%x\n", pci_hdr_offset, pnp_hdr_offset); if (pci_hdr_offset) { /* we only fill in the low byte, this limits us to ROMs of 255 * 512 bytes = 127.5kB or so */ rom[pci_hdr_offset+PCI_SIZE_OFF] = rom[2]; if (pci_vendor_id != 0) { rom[pci_hdr_offset+PCI_VEND_ID_OFF] = pci_vendor_id & 0xff; rom[pci_hdr_offset+PCI_VEND_ID_OFF+1] = pci_vendor_id >> 8; } if (pci_device_id != 0) { rom[pci_hdr_offset+PCI_DEV_ID_OFF] = pci_device_id & 0xff; rom[pci_hdr_offset+PCI_DEV_ID_OFF+1] = pci_device_id >> 8; } } if (pnp_hdr_offset) { for (i = pnp_hdr_offset, sum = 0; i < pnp_hdr_offset + PNP_HDR_SIZE; ++i) sum += rom[i]; rom[pnp_hdr_offset+PNP_CHKSUM_OFF] = -sum; } /* 3c503 requires last two bytes to be MAGIC_3C503 */ if (is3c503 && romsize == MINROMSIZE) { rom[MINROMSIZE - 1] = rom[MINROMSIZE - 2] = MAGIC_3C503; } for (i = 0, sum = 0; i < romsize; i++) sum += rom[i]; rom[5] = -sum; /* double check */ for (i = 0, sum = 0; i < romsize; i++) sum += rom[i]; if (sum & 0xFF) printf("Checksum fails.\n"); else if (verbose) printf("Checksum ok\n"); if (fseek(fd, (off_t)0, SEEK_SET) < 0) { perror("fseek"); exit(1); } if (fwrite(rom, sizeof(char), romsize, fd) != romsize) { perror(argv[0]); exit(1); } fclose(fd); exit(0); } /* * Local variables: * c-basic-offset: 8 * End: */