diff options
Diffstat (limited to 'nor-utils/rfddump.c')
-rw-r--r-- | nor-utils/rfddump.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/nor-utils/rfddump.c b/nor-utils/rfddump.c new file mode 100644 index 0000000..0375bac --- /dev/null +++ b/nor-utils/rfddump.c @@ -0,0 +1,337 @@ +/* + * rfddump.c + * + * Copyright (C) 2005 Sean Young <sean@mess.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#define PROGRAM_NAME "rfddump" +#define VERSION "$Revision 1.0 $" + +#define _XOPEN_SOURCE 500 /* For pread */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> + +#include <mtd/mtd-user.h> +#include <linux/types.h> +#include <mtd_swab.h> + +/* next is an array of mapping for each corresponding sector */ +#define RFD_MAGIC 0x9193 +#define HEADER_MAP_OFFSET 3 +#define SECTOR_DELETED 0x0000 +#define SECTOR_ZERO 0xfffe +#define SECTOR_FREE 0xffff + +#define SECTOR_SIZE 512 + +#define SECTORS_PER_TRACK 63 + + +struct rfd { + int block_size; + int block_count; + int header_sectors; + int data_sectors; + int header_size; + uint16_t *header; + int sector_count; + int *sector_map; + const char *mtd_filename; + const char *out_filename; + int verbose; +}; + +void display_help(void) +{ + printf("Usage: %s [OPTIONS] MTD-device filename\n" + "Dumps the contents of a resident flash disk\n" + "\n" + "-h --help display this help and exit\n" + "-V --version output version information and exit\n" + "-v --verbose Be verbose\n" + "-b size --blocksize Block size (defaults to erase unit)\n", + PROGRAM_NAME); + exit(0); +} + +void display_version(void) +{ + printf("%s " VERSION "\n" + "\n" + "This is free software; see the source for copying conditions. There is NO\n" + "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", + PROGRAM_NAME); + + exit(0); +} + +void process_options(int argc, char *argv[], struct rfd *rfd) +{ + int error = 0; + + rfd->block_size = 0; + rfd->verbose = 0; + + for (;;) { + int option_index = 0; + static const char *short_options = "hvVb:"; + static const struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'V', }, + { "blocksize", required_argument, 0, 'b' }, + { "verbose", no_argument, 0, 'v' }, + { NULL, 0, 0, 0 } + }; + + int c = getopt_long(argc, argv, short_options, + long_options, &option_index); + if (c == EOF) + break; + + switch (c) { + case 'h': + display_help(); + break; + case 'V': + display_version(); + break; + case 'v': + rfd->verbose = 1; + break; + case 'b': + rfd->block_size = atoi(optarg); + break; + case '?': + error = 1; + break; + } + } + + if ((argc - optind) != 2 || error) + display_help(); + + rfd->mtd_filename = argv[optind]; + rfd->out_filename = argv[optind + 1]; +} + +int build_block_map(struct rfd *rfd, int fd, int block) +{ + int i; + int sectors; + + if (pread(fd, rfd->header, rfd->header_size, block * rfd->block_size) + != rfd->header_size) { + return -1; + } + + if (le16_to_cpu(rfd->header[0]) != RFD_MAGIC) { + if (rfd->verbose) + printf("Block #%02d: Magic missing\n", block); + + return 0; + } + + sectors = 0; + for (i=0; i<rfd->data_sectors; i++) { + uint16_t entry = le16_to_cpu(rfd->header[i + HEADER_MAP_OFFSET]); + + if (entry == SECTOR_FREE || entry == SECTOR_DELETED) + continue; + + if (entry == SECTOR_ZERO) + entry = 0; + + if (entry >= rfd->sector_count) { + fprintf(stderr, "%s: warning: sector %d out of range\n", + rfd->mtd_filename, entry); + continue; + } + + if (rfd->sector_map[entry] != -1) { + fprintf(stderr, "%s: warning: more than one entry " + "for sector %d\n", rfd->mtd_filename, entry); + continue; + } + + rfd->sector_map[entry] = rfd->block_size * block + + (i + rfd->header_sectors) * SECTOR_SIZE; + sectors++; + } + + if (rfd->verbose) + printf("Block #%02d: %d sectors\n", block, sectors); + + return 1; +} + +int main(int argc, char *argv[]) +{ + int fd, sectors_per_block; + mtd_info_t mtd_info; + struct rfd rfd; + int i, blocks_found; + int out_fd = 0; + uint8_t sector[512]; + int blank, rc, cylinders; + + process_options(argc, argv, &rfd); + + fd = open(rfd.mtd_filename, O_RDONLY); + if (fd == -1) { + perror(rfd.mtd_filename); + return 1; + } + + if (rfd.block_size == 0) { + if (ioctl(fd, MEMGETINFO, &mtd_info)) { + perror(rfd.mtd_filename); + close(fd); + return 1; + } + + if (mtd_info.type != MTD_NORFLASH) { + fprintf(stderr, "%s: wrong type\n", rfd.mtd_filename); + close(fd); + return 2; + } + + sectors_per_block = mtd_info.erasesize / SECTOR_SIZE; + + rfd.block_size = mtd_info.erasesize; + rfd.block_count = mtd_info.size / mtd_info.erasesize; + } else { + struct stat st; + + if (fstat(fd, &st) == -1) { + perror(rfd.mtd_filename); + close(fd); + return 1; + } + + if (st.st_size % SECTOR_SIZE) + fprintf(stderr, "%s: warning: not a multiple of sectors (512 bytes)\n", rfd.mtd_filename); + + sectors_per_block = rfd.block_size / SECTOR_SIZE; + + if (st.st_size % rfd.block_size) + fprintf(stderr, "%s: warning: not a multiple of block size\n", rfd.mtd_filename); + + rfd.block_count = st.st_size / rfd.block_size; + + if (!rfd.block_count) { + fprintf(stderr, "%s: not large enough for one block\n", rfd.mtd_filename); + close(fd); + return 2; + } + } + + rfd.header_sectors = + ((HEADER_MAP_OFFSET + sectors_per_block) * + sizeof(uint16_t) + SECTOR_SIZE - 1) / SECTOR_SIZE; + rfd.data_sectors = sectors_per_block - rfd.header_sectors; + cylinders = ((rfd.block_count - 1) * rfd.data_sectors - 1) + / SECTORS_PER_TRACK; + rfd.sector_count = cylinders * SECTORS_PER_TRACK; + rfd.header_size = + (HEADER_MAP_OFFSET + rfd.data_sectors) * sizeof(uint16_t); + + rfd.header = malloc(rfd.header_size); + if (!rfd.header) { + perror(PROGRAM_NAME); + close(fd); + return 2; + } + rfd.sector_map = malloc(rfd.sector_count * sizeof(int)); + if (!rfd.sector_map) { + perror(PROGRAM_NAME); + close(fd); + free(rfd.sector_map); + return 2; + } + + rfd.mtd_filename = rfd.mtd_filename; + + for (i=0; i<rfd.sector_count; i++) + rfd.sector_map[i] = -1; + + for (blocks_found=i=0; i<rfd.block_count; i++) { + rc = build_block_map(&rfd, fd, i); + if (rc > 0) + blocks_found++; + if (rc < 0) + goto err; + } + + if (!blocks_found) { + fprintf(stderr, "%s: no RFD blocks found\n", rfd.mtd_filename); + goto err; + } + + for (i=0; i<rfd.sector_count; i++) { + if (rfd.sector_map[i] != -1) + break; + } + + if (i == rfd.sector_count) { + fprintf(stderr, "%s: no sectors found\n", rfd.mtd_filename); + goto err; + } + + out_fd = open(rfd.out_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (out_fd == -1) { + perror(rfd.out_filename); + goto err; + } + + blank = 0; + for (i=0; i<rfd.sector_count; i++) { + if (rfd.sector_map[i] == -1) { + memset(sector, 0, SECTOR_SIZE); + blank++; + } else { + if (pread(fd, sector, SECTOR_SIZE, rfd.sector_map[i]) + != SECTOR_SIZE) { + perror(rfd.mtd_filename); + goto err; + } + } + + if (write(out_fd, sector, SECTOR_SIZE) != SECTOR_SIZE) { + perror(rfd.out_filename); + goto err; + } + } + + if (rfd.verbose) + printf("Copied %d sectors (%d blank)\n", rfd.sector_count, blank); + + close(out_fd); + close(fd); + free(rfd.header); + free(rfd.sector_map); + + return 0; + +err: + if (out_fd) + close(out_fd); + + close(fd); + free(rfd.header); + free(rfd.sector_map); + + return 2; +} |