From: Jan-Benedict G. <jb...@us...> - 2004-07-23 17:44:56
|
Update of /cvsroot/linux-vax/usr/firmware_dumper In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20302 Added Files: Makefile README backend_vax.c main.c main.h serial.c serial.h Log Message: - This is my firmware dumper, a nice hack to read out ROM areas. --- NEW FILE: backend_vax.c --- #include <stdio.h> #include <string.h> #include "main.h" #include "serial.h" int vax_init (int fd) { unsigned char one_byte; int ret; serial_send_break (fd, 0); do { ret = serial_read_byte (fd, &one_byte, 1, 0); } while (ret == 0); return 0; } int vax_get_byte (int fd, unsigned char *output, unsigned long long int address) { unsigned char output_buffer[100]; unsigned char input_buffer[200]; unsigned char *ptr; int ret; int input_len = 0; sprintf (output_buffer, "EXAMINE /B /P /U %08lx\r\n", (unsigned long) address); serial_write (fd, output_buffer, strlen (output_buffer)); bzero (input_buffer, sizeof (input_buffer)); do { ret = serial_read_byte (fd, &input_buffer[input_len], 1, 0); if (ret == 0) input_len++; } while (ret == 0 && input_len < sizeof (input_buffer) && !strstr (input_buffer, ">>> ")); if (ret) return -1; ptr = input_buffer; /* Over-read first line */ while (*ptr != '\n' && *ptr != '\r') ptr++; while (*ptr == '\n' || *ptr == '\r') ptr++; ptr++; /* Strip leading spaces of second line */ while (*ptr == ' ') ptr++; /* Over-read 'P' */ ptr++; /* Strip leading spaces of second line */ while (*ptr == ' ') ptr++; /* Strip address */ while ( ((*ptr >= '0') && (*ptr <= '9')) || ((*ptr >= 'a') && (*ptr <= 'z')) || ((*ptr >= 'A') && (*ptr <= 'Z'))) *ptr++; /* Strip leading spaces */ while (*ptr == ' ') ptr++; /* Now get the byte */ if (*ptr >= 'A' && *ptr <= 'Z') *ptr = *ptr - 'A' + 'a'; if (*ptr >= '0' && *ptr <= '9') *output = *ptr - '0'; else *output = *ptr - 'a' + 10; *output <<= 4; ptr++; if (*ptr >= 'A' && *ptr <= 'Z') *ptr = *ptr - 'A' + 'a'; if (*ptr >= '0' && *ptr <= '9') *output |= *ptr - '0'; else *output |= *ptr - 'a' + 10; return 0; } int vax_fini (int fd) { return 0; } --- NEW FILE: serial.c --- #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <termios.h> #include <time.h> #include <unistd.h> #include <sys/select.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> #include "serial.h" int serial_open (unsigned char *devname) { int fd; fd = open (devname, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd < 0) return -1; return fd; } int serial_close (int fd) { close (fd); return 0; } int serial_set_bits (int fd, unsigned char bits) { struct termios termios; int termios_bits; int ret; ret = tcgetattr (fd, &termios); if (ret != 0) return -1; termios.c_cflag = termios.c_cflag & ~CSIZE; switch (bits) { case 5: termios_bits = CS5; break; case 6: termios_bits = CS6; break; case 7: termios_bits = CS7; break; case 8: termios_bits = CS8; break; default: termios_bits = CS8; break; } termios.c_cflag |= termios_bits; ret = tcsetattr (fd, TCSANOW, &termios); if (ret != 0) return -2; return 0; } int serial_set_parity (int fd, unsigned char parity) { struct termios termios; int ret; ret = tcgetattr (fd, &termios); if (ret != 0) return -1; switch (parity) { case 'E': case 'e': termios.c_cflag &= ~PARODD; termios.c_cflag |= PARENB; break; case 'O': case 'o': termios.c_cflag |= ~PARODD; termios.c_cflag |= PARENB; break; case 'N': case 'n': termios.c_cflag &= ~PARODD; termios.c_cflag &= ~PARENB; break; default: termios.c_cflag &= ~PARODD; termios.c_cflag &= ~PARENB; break; } ret = tcsetattr (fd, TCSANOW, &termios); if (ret != 0) return -2; return 0; } int serial_set_stop (int fd, unsigned char stop) { struct termios termios; int ret; ret = tcgetattr (fd, &termios); if (ret != 0) return -1; switch (stop) { case 1: termios.c_cflag &= ~CSTOPB; break; case 2: termios.c_cflag |= CSTOPB; break; default: termios.c_cflag &= ~CSTOPB; break; } ret = tcsetattr (fd, TCSANOW, &termios); if (ret != 0) return -2; return 0; } int serial_set_one_byte_each (int fd) { struct termios termios; int ret; ret = tcgetattr (fd, &termios); if (ret != 0) return -1; termios.c_iflag |= IGNPAR; /* Ignore bytes with parity errors */ termios.c_cflag |= CLOCAL; /* No modem control lines */ termios.c_cflag |= CREAD; /* Enable serial receiver */ termios.c_cflag &= ~CRTSCTS; /* Disable handshake */ termios.c_oflag = 0; /* Raw output */ termios.c_lflag = 0; /* Raw local handling */ termios.c_cc[VTIME] = 0; /* Inter-character timer unused */ termios.c_cc[VMIN] = 1; /* One byte per read */ ret = tcsetattr (fd, TCSANOW, &termios); if (ret != 0) return -2; return 0; } int serial_set_speed (int fd, unsigned int speed) { struct termios termios; unsigned int termios_speed; int ret; ret = tcgetattr (fd, &termios); if (ret != 0) return -1; termios.c_cflag = termios.c_cflag & ~CBAUD; termios.c_cflag = termios.c_cflag & ~CBAUDEX; switch (speed) { #ifdef B0 case 0: termios_speed = B0; break; #endif #ifdef B50 case 50: termios_speed = B50; break; #endif #ifdef B75 case 75: termios_speed = B75; break; #endif #ifdef B110 case 110: termios_speed = B110; break; #endif #ifdef B134 case 134: termios_speed = B134; break; #endif #ifdef B150 case 150: termios_speed = B150; break; #endif #ifdef B200 case 200: termios_speed = B200; break; #endif #ifdef B300 case 300: termios_speed = B300; break; #endif #ifdef B600 case 600: termios_speed = B600; break; #endif #ifdef B1200 case 1200: termios_speed = B1200; break; #endif #ifdef B1800 case 1800: termios_speed = B1800; break; #endif #ifdef B2400 case 2400: termios_speed = B2400; break; #endif #ifdef B4800 case 4800: termios_speed = B4800; break; #endif #ifdef B9600 case 9600: termios_speed = B9600; break; #endif #ifdef B19200 case 19200: termios_speed = B19200; break; #endif #ifdef B38400 case 38400: termios_speed = B38400; break; #endif #ifdef B57600 case 57600: termios_speed = B57600; break; #endif #ifdef B115200 case 115200: termios_speed = B115200; break; #endif #ifdef B230400 case 230400: termios_speed = B230400; break; #endif #ifdef B460800 case 460800: termios_speed = B460800; break; #endif #ifdef B5000000 case 500000: termios_speed = B500000; break; #endif #ifdef B576000 case 576000: termios_speed = B576000; break; #endif #ifdef B921600 case 921600: termios_speed = B921600; break; #endif #ifdef B1000000 case 1000000: termios_speed = B1000000; break; #endif #ifdef B1152000 case 1152000: termios_speed = B1152000; break; #endif #ifdef B1500000 case 1500000: termios_speed = B1500000; break; #endif #ifdef B2000000 case 2000000: termios_speed = B2000000; break; #endif #ifdef B2500000 case 2500000: termios_speed = B2500000; break; #endif #ifdef B3000000 case 3000000: termios_speed = B3000000; break; #endif #ifdef B3500000 case 3500000: termios_speed = B3500000; break; #endif #ifdef B4000000 case 4000000: termios_speed = B4000000; break; #endif default: termios_speed = B9600; break; } termios.c_cflag |= termios_speed; ret = tcsetattr (fd, TCSANOW, &termios); if (ret != 0) return -2; return 0; } int serial_write_byte (int fd, unsigned char byte) { ssize_t ret; ret = write (fd, &byte, 1); if (ret != 1) return -1; return 0; } size_t serial_write (int fd, unsigned char *buf, size_t len) { ssize_t ret; size_t written = 0; int errcount = 0; while (written < len) { ret = write (fd, buf + written, len - written); if (ret >= 0) { written += ret; errcount = 0; } else { switch (errno) { /* "Soft" errors allow retransmission */ case EINTR: case ENOSPC: case EAGAIN: if (errcount++ > 200) return len - written; break; /* Anything else is considered fatal */ default: return len - written; break; } } } return 0; } int serial_read_byte (int fd, unsigned char *byte, time_t sec, suseconds_t usec) { struct timeval timeout; fd_set read_set; unsigned char my_byte; int ret; ssize_t len; timeout.tv_sec = sec; timeout.tv_usec = usec; FD_ZERO (&read_set); FD_SET (fd, &read_set); ret = select (fd + 1, &read_set, NULL, NULL, &timeout); if (ret > 0) { len = read (fd, &my_byte, 1); if (len == 1) { *byte = my_byte; return 0; } else return -1; } else return -2; } int serial_set_line_parameters (int fd, unsigned int speed, unsigned char bits, unsigned char parity, unsigned char stop) { int ret; ret = serial_set_speed (fd, speed); if (ret != 0) return -1; ret = serial_set_bits (fd, bits); if (ret != 0) return -2; ret = serial_set_parity (fd, parity); if (ret != 0) return -3; ret = serial_set_stop (fd, stop); if (ret != 0) return -4; return 0; } int serial_open_luxury (unsigned char *device, unsigned int speed, unsigned char bits, unsigned char parity, unsigned char stop) { int fd; int ret; fd = serial_open (device); if (fd < 0) return -1; ret = serial_set_line_parameters (fd, speed, bits, parity, stop); if (ret != 0) { close (fd); return -2; } ret = serial_set_one_byte_each (fd); if (ret != 0) { close (fd); return -3; } return fd; } int serial_get_modem (int fd, int what, int *result) { int ret; int status; status = what; ret = ioctl (fd, TIOCMGET, &status); if (ret == 0) { *result = status; return 0; } else return -1; } int serial_set_modem (int fd, int what, int state) { int ret; int old_state; int new_state; ret = ioctl (fd, TIOCMGET, &old_state); if (ret != 0) return -1; if (state) new_state = old_state | what; else new_state = old_state & ~what; ret = ioctl (fd, TIOCMSET, &new_state); if (ret != 0) return -1; return 0; } int serial_send_break (int fd, int duration) { return tcsendbreak (fd, duration); } --- NEW FILE: main.h --- #ifndef _MAIN_H #define _MAIN_H extern int vax_init (int fd); extern int vax_get_byte (int fd, unsigned char *output, unsigned long long int address); extern int vax_fini (int fd); #endif /* _MAIN_H */ --- NEW FILE: README --- This is a firmware dumper, designed to make use of firmware examine functionality available over a serial console. Currently, only the VAX backend is implemented, but it should be simple to add more backends. Just look into the backend_vax.c file. Other memory access schemes are also simple to implement, just look at the scheduler in main.c. This software is subject to the terms of the GNU General Public License, Version 2 or any later version, at your opinion. (C)opyright 2004 by Jan-Benedict Glaw <jb...@lu...> EXAMPLE: ~~~~~~~~ $ ./dumper --target vax --device /dev/ttyS1 --speed 9600 --bits 8 --parity N \ --stop 1 --outfile LCG-ROM.image --start 0x20140000 --len 0x40000 \ --scheme 1 This'll dump the LCG ROM of a VAXstation 4000m60. The machine is attached to /dev/ttyS1, uses 9k6 8N1. The LCG ROM starts at address 0x20140000 and is addressed with scheme 1 (that is, only the low byte of a longword is used). Thus, we need a length to read of 0x40000 (that is the memory length; the ROM itself is only 1/4 of it in size). $ ./dumper --help ./dumper -- read area of memory through firmware --target xxx --device /dev/ttySn --speed nnnn --bits {5,6,7,8} --parity {E,O,N} --stop {1,2} --outfile xxxx --start 0xNNNNNNNN --len nnnn --scheme n scheme: 1 - XXXXXXnn (low byte of byte longword) 2 - XXXXnnnn (low word of 4 byte longword) 3 - nnnnnnnn (full 4 byte longwords) 4 - XXXXXXXXXXXXXXnn (low byte of 8 byte longword) 5 - XXXXXXXXXXXXnnnn (low word of 8 byte longword) 6 - XXXXXXXXnnnnnnnn (low 4 byte longword of 8 byte longword) --- NEW FILE: Makefile --- #!/usr/bin/make -f OBJECTS= main.o \ backend_vax.o \ serial.o all: dumper clean: -rm -f $(OBJECTS) dumper rom.dump a.out core .c.o: gcc -Wall -Werror -g -c $*.c dumper: $(OBJECTS) gcc -Wall -Werror -g -o dumper $(OBJECTS) strip: dumper strip dumper --- NEW FILE: main.c --- #include <fcntl.h> #include <getopt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include "serial.h" #include "main.h" #ifndef ARRAY_SIZE # define ARRAY_SIZE(x) ((sizeof (x))/(sizeof ((x)[0]))) #endif #define FWDUMP_VERSION "fwdump-0.0.2" #define DEFAULT_TARGET "*invalid_target*" #define DEFAULT_DEVICE "*invalid_device*" #define DEFAULT_SPEED "9600" #define DEFAULT_BITS "8" #define DEFAULT_PARITY "N" #define DEFAULT_STOP "1" #define DEFAULT_OUTFILE "rom.dump" #define DEFAULT_START "0x00000000" #define DEFAULT_LEN "0" #define DEFAULT_SCHEME "3" static struct backend_descriptor { unsigned char *name; int (*init) (int fd); int (*get_byte) (int fd, unsigned char *output, unsigned long long int address); int (*fini) (int fd); int wordsize; } backend[] = { { .name = "vax", .wordsize = 32, .init = &vax_init, .get_byte = &vax_get_byte, .fini = &vax_fini, }, }; struct backend_descriptor * get_backend (unsigned char *name) { int i; for (i = 0; i < ARRAY_SIZE (backend); i++) if (strcmp (name, backend[i].name) == 0) return &backend[i]; return NULL; } static void help (unsigned char *program_name) { int i; fprintf (stderr, "Firmware dumper: %s\n", FWDUMP_VERSION); fprintf (stderr, "%s -- read area of memory through firmware\n", program_name); fprintf (stderr, "\n"); fprintf (stderr, "--target xxx\n"); fprintf (stderr, "--device /dev/ttySn\n"); fprintf (stderr, "--speed nnnn\n"); fprintf (stderr, "--bits {5,6,7,8}\n"); fprintf (stderr, "--parity {E,O,N}\n"); fprintf (stderr, "--stop {1,2}\n"); fprintf (stderr, "--outfile xxxx\n"); fprintf (stderr, "--start 0xNNNNNNNN\n"); fprintf (stderr, "--len nnnn\n"); fprintf (stderr, "--scheme n\n"); fprintf (stderr, "\n"); fprintf (stderr, "scheme:"); fprintf (stderr, "\t1 - XXXXXXnn (low byte of byte longword)\n"); fprintf (stderr, "\t2 - XXXXnnnn (low word of 4 byte longword)\n"); fprintf (stderr, "\t3 - nnnnnnnn (full 4 byte longwords)\n"); fprintf (stderr, "\t4 - XXXXXXXXXXXXXXnn (low byte of 8 byte longword)\n"); fprintf (stderr, "\t5 - XXXXXXXXXXXXnnnn (low word of 8 byte longword)\n"); fprintf (stderr, "\t6 - XXXXXXXXnnnnnnnn (low 4 byte longword of 8 byte longword)\n"); fprintf (stderr, "\n"); fprintf (stderr, "backends:\t"); for (i = 0; i < ARRAY_SIZE (backend); i++) { if (i == 0) fprintf (stderr, "%s\n", backend[i].name); else fprintf (stderr, "\t\t%s\n", backend[i].name); } exit (EXIT_FAILURE); } static int set_option (unsigned char **store, char *option) { if (*store) { free (*store); *store = NULL; } printf ("Processing option \"%s\"\n", option); *store = strdup (option); return (*store == NULL)? -1: 0; } static int read_memory_byte (struct backend_descriptor *target, int serial_fd, int outfile_fd, unsigned long long int address) { int ret1; unsigned char one_byte; ssize_t ret2; ret1 = target->get_byte (serial_fd, &one_byte, address); if (ret1 != 0) { fprintf (stderr, "Failed to read byte at address %lld\n", address); return -1; } ret2 = write (outfile_fd, &one_byte, sizeof (one_byte)); if (ret2 != sizeof (one_byte)) { fprintf (stderr, "Failed to write byte at address %lld\n", address); return -2; } return 0; } static int read_memory (struct backend_descriptor *target, int serial_fd, unsigned char *outfile, unsigned long long start, size_t len, int scheme) { int outfile_fd; size_t i; int ret; int read_this_address; if (scheme < 1 || scheme > 6) { fprintf (stderr, "Don't know how to handle scheme %d\n", scheme); return -1; } outfile_fd = open (outfile, O_WRONLY | O_CREAT | O_NOCTTY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP); if (outfile_fd < 0) { perror ("Failed to open output file"); return -2; } for (i = 0; i < len; i++) { read_this_address = 0; switch (scheme) { case 1: if (i % 4 == 0) read_this_address = 1; break; case 2: if (i % 4 == 0 || i % 4 == 1) read_this_address = 1; break; case 3: read_this_address = 1; break; case 4: if (i % 8 == 0) read_this_address = 1; break; case 5: if (i % 8 == 0 || i % 8 == 1) read_this_address = 1; break; case 6: if (i % 8 == 0 || i % 8 == 1 || i % 8 == 2 || i % 8 == 3) read_this_address = 1; break; } if (read_this_address) { ret = read_memory_byte (target, serial_fd, outfile_fd, start + i); if (ret != 0) { fprintf (stderr, "Failed to read byte at offset %lld\n", (unsigned long long int) i); return -3; } } } close (outfile_fd); return 0; } int main (int argc, char *argv[]) { /* Variables we work with */ struct backend_descriptor *back; int fd; int ret; int c; int show_help = 0; /* Variables the user supplied */ unsigned char *target_string = strdup (DEFAULT_TARGET); unsigned char *start_string = strdup (DEFAULT_START); unsigned char *len_string = strdup (DEFAULT_LEN); unsigned char *outfile_string = strdup (DEFAULT_OUTFILE); unsigned char *scheme_string = strdup (DEFAULT_SCHEME); unsigned char *device_string = strdup (DEFAULT_DEVICE); unsigned char *speed_string = strdup (DEFAULT_SPEED); unsigned char *bits_string = strdup (DEFAULT_BITS); unsigned char *parity_string = strdup (DEFAULT_PARITY); unsigned char *stop_string = strdup (DEFAULT_STOP); while (1) { int option_index = 0; static struct option long_options[] = { { .name = "target", .has_arg = required_argument, .flag = NULL, .val = 'a', }, { .name = "start", .has_arg = required_argument, .flag = NULL, .val = 'b', }, { .name = "len", .has_arg = required_argument, .flag = NULL, .val = 'c', }, { .name = "device", .has_arg = required_argument, .flag = NULL, .val = 'd', }, { .name = "speed", .has_arg = required_argument, .flag = NULL, .val = 'e', }, { .name = "bits", .has_arg = required_argument, .flag = NULL, .val = 'f', }, { .name = "parity", .has_arg = required_argument, .flag = NULL, .val = 'g', }, { .name = "stop", .has_arg = required_argument, .flag = NULL, .val = 'h', }, { .name = "help", .has_arg = no_argument, .flag = NULL, .val = 'i', }, { .name = "outfile", .has_arg = required_argument, .flag = NULL, .val = 'j', }, { .name = "scheme", .has_arg = required_argument, .flag = NULL, .val = 'k', }, }; c = getopt_long (argc, argv, "a:b:c:d:e:f:g:h:i", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': if (set_option (&target_string, optarg)) { fprintf (stderr, "Failed to set target\n"); exit (EXIT_FAILURE); } break; case 'b': if (set_option (&start_string, optarg)) { fprintf (stderr, "Failed to set start address\n"); exit (EXIT_FAILURE); } break; case 'c': if (set_option (&len_string, optarg)) { fprintf (stderr, "Failed to set len to dump\n"); exit (EXIT_FAILURE); } break; case 'd': if (set_option (&device_string, optarg)) { fprintf (stderr, "Failed to set device name\n"); exit (EXIT_FAILURE); } break; case 'e': if (set_option (&speed_string, optarg)) { fprintf (stderr, "Failed to set speed\n"); exit (EXIT_FAILURE); } break; case 'f': if (set_option (&bits_string, optarg)) { fprintf (stderr, "Failed to set number of bits\n"); exit (EXIT_FAILURE); } break; case 'g': if (set_option (&parity_string, optarg)) { fprintf (stderr, "Failed to set parity\n"); exit (EXIT_FAILURE); } break; case 'h': if (set_option (&stop_string, optarg)) { fprintf (stderr, "Failed to set number of stop-bits\n"); exit (EXIT_FAILURE); } break; case 'j': if (set_option (&outfile_string, optarg)) { fprintf (stderr, "Failed to set name of output file\n"); exit (EXIT_FAILURE); } break; case 'k': if (set_option (&scheme_string, optarg)) { fprintf (stderr, "Failed to set byte gathering scheme\n"); exit (EXIT_FAILURE); } break; case 'i': default: show_help = 1; break; } } if (show_help) { help (argv[0]); return EXIT_FAILURE; } back = get_backend (target_string); if (!back) { fprintf (stderr, "Didn't find target \"%s\"\n", target_string); return EXIT_FAILURE; } fd = serial_open_luxury ( device_string, atoi (speed_string), atoi (bits_string), (parity_string && parity_string[0])? *parity_string: DEFAULT_PARITY[0], atoi (stop_string)); if (fd < 0) { fprintf (stderr, "Failed to open serial interface \"%s\"\n", device_string); exit (EXIT_FAILURE); } if (back->init) back->init (fd); ret = read_memory (back, fd, outfile_string, strtoull (start_string, NULL, 0), strtoul (len_string, NULL, 0), atoi (scheme_string)); if (back->fini) back->fini (fd); if (ret != 0) { fprintf (stderr, "Failed to read memory\n"); close (fd); return EXIT_FAILURE; } close (fd); return EXIT_SUCCESS; } --- NEW FILE: serial.h --- #ifndef _SERIAL_H #define _SERIAL_H #include <sys/types.h> #include <sys/ioctl.h> #define SER_STX 0x02 #define SER_ETX 0x03 #define SER_EOT 0x04 #define SER_ENQ 0x05 #define SER_ACK 0x06 #define SER_DLE 0x10 #define SER_NAK 0x15 #define SER_ETB 0x17 #define SER_RS 0x1e extern int serial_open (unsigned char *device); extern int serial_open_luxury (unsigned char *device, unsigned int speed, unsigned char bits, unsigned char parity, unsigned char stop); extern int serial_close (int fd); extern int serial_set_speed (int fd, unsigned int speed); extern int serial_set_bits (int fd, unsigned char bits); extern int serial_set_parity (int fd, unsigned char parity); extern int serial_set_stop (int fd, unsigned char stop); extern int serial_set_one_byte_each (int fd); extern int serial_write_byte (int fd, unsigned char byte); extern size_t serial_write (int fd, unsigned char *buf, size_t len); /* returning remaining byte # not sent */ extern int serial_read_byte (int fd, unsigned char *byte, time_t sec, suseconds_t usec); extern int serial_get_modem (int fd, int what, int *result); extern int serial_set_modem (int fd, int what, int state); extern int serial_send_break (int fd, int duration); #endif /* _SERIAL_H */ |