ao-tools: Add ao-makebin
authorKeith Packard <keithp@keithp.com>
Fri, 18 Mar 2016 18:15:57 +0000 (11:15 -0700)
committerKeith Packard <keithp@keithp.com>
Fri, 18 Mar 2016 18:15:57 +0000 (11:15 -0700)
This constructs a raw binary or DFU format file for use with dfu-util,
which can be used with a bare STM processor to load code before the
boot loader is available.

Signed-off-by: Keith Packard <keithp@keithp.com>
ao-tools/Makefile.am
ao-tools/ao-makebin/Makefile.am [new file with mode: 0644]
ao-tools/ao-makebin/ao-makebin.1 [new file with mode: 0644]
ao-tools/ao-makebin/ao-makebin.c [new file with mode: 0644]
ao-tools/lib/Makefile.am
ao-tools/lib/ao-dfu.c [new file with mode: 0644]
ao-tools/lib/ao-dfu.h [new file with mode: 0644]
configure.ac

index dad85e83e7390de7d9e1f4c0075cd06c364c2029..fb5548576e0fb1a5be5d9e930294d2a6305a6410 100644 (file)
@@ -3,7 +3,7 @@ SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list \
        ao-dumpflash ao-edit-telem ao-dump-up ao-elftohex \
        ao-flash ao-usbload ao-test-igniter ao-test-baro \
        ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng \
-       ao-cal-freq ao-chaosread
+       ao-cal-freq ao-chaosread ao-makebin
 if LIBSTLINK
 SUBDIRS += ao-stmload
 endif
diff --git a/ao-tools/ao-makebin/Makefile.am b/ao-tools/ao-makebin/Makefile.am
new file mode 100644 (file)
index 0000000..758097a
--- /dev/null
@@ -0,0 +1,12 @@
+bin_PROGRAMS=ao-makebin
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+AO_ELFTOHEX_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_makebin_DEPENDENCIES = $(AO_ELFTOHEX_LIBS)
+
+ao_makebin_LDADD=$(AO_ELFTOHEX_LIBS) -lelf
+
+ao_makebin_SOURCES=ao-makebin.c
+
+man_MANS = ao-makebin.1
diff --git a/ao-tools/ao-makebin/ao-makebin.1 b/ao-tools/ao-makebin/ao-makebin.1
new file mode 100644 (file)
index 0000000..e19ec2c
--- /dev/null
@@ -0,0 +1,47 @@
+.\"
+.\" Copyright © 2013 Keith Packard <keithp@keithp.com>
+.\"
+.\" 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.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-LOAD 1 "ao-makebin" ""
+.SH NAME
+ao-makebin \- construct raw binary file or DFU image from collection of ELF files
+.SH SYNOPSIS
+.B "ao-makebin"
+[\--base=\fIbase-address\fP]
+[\--output=\fIoutput.bin\fP]
+[\--dfu]
+[\--verbose]
+\fIinput.elf ...\fP
+.SH DESCRIPTION
+.I ao-makebin
+reads the specified .elf files and writes out a raw binary flash image
+.SH OPTIONS
+.TP
+\--base=\fIbase-address\fP
+This specifies the target address for the first byte of the file (default is 0)
+.TP
+\--output=\fIoutput.bin\fP
+This specifies the output file (default is stdout)
+.TP
+\--dfu
+Creates a DFU file (as documented by ST's UM0391 user manual) instead
+of a raw binary file.
+.TP
+\--verbose
+Dumps some debug information.
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-makebin/ao-makebin.c b/ao-tools/ao-makebin/ao-makebin.c
new file mode 100644 (file)
index 0000000..31ce188
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "ao-hex.h"
+#include "ao-elf.h"
+#include "ao-dfu.h"
+
+static const struct option options[] = {
+       { .name = "verbose", .has_arg = 0, .val = 'v' },
+       { .name = "output", .has_arg = 1, .val = 'o' },
+       { .name = "base", .has_arg = 1, .val = 'b' },
+       { .name = "align", .has_arg = 1, .val = 'a' },
+       { .name = "dfu", .has_arg = 0, .val = 'd' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s [--verbose=<level>] [--output=<output.bin>] [--base=<base-address>] [--align=<align>] [--dfu] <input.elf> ...\n", program);
+       exit(1);
+}
+
+static int
+ends_with(char *whole, char *suffix)
+{
+       int whole_len = strlen(whole);
+       int suffix_len = strlen(suffix);
+
+       if (suffix_len > whole_len)
+               return 0;
+       return strcmp(whole + whole_len - suffix_len, suffix) == 0;
+}
+
+static struct ao_dfu_info dfu_info = {
+       .bcdDevice = 0x0000,
+       .idProduct = 0xdf11,
+       .idVendor = 0x0483,
+};
+
+int
+main (int argc, char **argv)
+{
+       char                    *output = NULL;
+       struct ao_hex_image     *image = NULL;
+       struct ao_sym           *file_symbols;
+       int                     num_file_symbols;
+       FILE                    *file;
+       int                     c;
+       uint32_t                base = 0xffffffff;
+       uint32_t                align = 0;
+       uint32_t                length;
+       int                     verbose = 0;
+       int                     dfu = 0;
+
+       while ((c = getopt_long(argc, argv, "dvo:b:a:", options, NULL)) != -1) {
+               switch (c) {
+               case 'o':
+                       output = optarg;
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               case 'b':
+                       base = strtoul(optarg, NULL, 0);
+                       break;
+               case 'a':
+                       align = strtoul(optarg, NULL, 0);
+                       break;
+               case 'd':
+                       dfu = 1;
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+
+       while (argv[optind]) {
+               char                    *input = argv[optind];
+               struct ao_hex_image     *tmp;
+
+               if (ends_with (input, ".ihx"))
+                       tmp = ao_hex_load(input, &file_symbols, &num_file_symbols);
+               else
+                       tmp = ao_load_elf(input, &file_symbols, &num_file_symbols);
+
+               if (!tmp)
+                       usage(argv[0]);
+
+               if (verbose)
+                       fprintf(stderr, "%s: 0x%x %d\n", input, tmp->address, tmp->length);
+
+               if (image) {
+                       image = ao_hex_image_cat(image, tmp);
+                       if (!image)
+                               usage(argv[0]);
+               } else
+                       image = tmp;
+               optind++;
+       }
+
+       if (base != 0xffffffff && base > image->address) {
+               fprintf(stderr, "requested base 0x%x is after image address 0x%x\n",
+                       base, image->address);
+               usage(argv[0]);
+       }
+
+       if (verbose)
+               fprintf(stderr, "%s: base 0x%x length %d\n", output ? output : "<stdout>", image->address, image->length);
+
+       if (!output)
+               file = stdout;
+       else {
+               file = fopen(output, "w");
+               if (!file) {
+                       perror(output);
+                       exit(1);
+               }
+       }
+
+       if (dfu) {
+               if (!ao_dfu_write(file, &dfu_info, 1, image)) {
+                       fprintf(stderr, "%s: dfu_write failed: %s\n", output, strerror(errno));
+                       if (output)
+                               unlink(output);
+                       exit(1);
+               }
+       } else {
+               while (base < image->address) {
+                       fputc(0xff, file);
+                       base++;
+               }
+
+               if (fwrite(image->data, 1, image->length, file) != image->length) {
+                       fprintf(stderr, "%s: failed to write bin file\n", output ? output : "<stdout>");
+                       if (output)
+                               unlink(output);
+                       exit(1);
+               }
+
+               if (align) {
+                       length = image->length;
+
+                       while (length % align) {
+                               fputc(0xff, file);
+                               length++;
+                       }
+               }
+               fflush(file);
+       }
+
+       exit(0);
+}
index a03a976cd59b3f397790965ee338524975aef925..a33d682d054992018df9e5264b602d0411b8d401 100644 (file)
@@ -46,6 +46,8 @@ libao_tools_a_SOURCES = \
        ao-editaltos.h \
        ao-elf.c \
        ao-elf.h \
+       ao-dfu.c \
+       ao-dfu.h \
        ao-selfload.c \
        ao-selfload.h \
        ao-verbose.c \
diff --git a/ao-tools/lib/ao-dfu.c b/ao-tools/lib/ao-dfu.c
new file mode 100644 (file)
index 0000000..b677849
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include "ao-hex.h"
+#include "ao-dfu.h"
+
+static uint32_t        dfu_crc;
+static FILE    *dfu_file;
+static int     dfu_failed;
+static int     dfu_error;
+
+static uint32_t update_crc(uint32_t crc, uint8_t byte)
+{
+       int j;
+       uint32_t mask;
+
+       crc = crc ^ byte;
+       for (j = 0; j < 8; j++) {
+               mask = -(crc & 1);
+               crc = (crc >> 1) ^ (0xEDB88320 & mask);
+       }
+       return crc;
+}
+
+static void dfu_init(FILE *file)
+{
+       dfu_crc = 0xffffffff;
+       dfu_file = file;
+       dfu_failed = 0;
+       dfu_error = 0;
+}
+
+static int dfu_fini(void)
+{
+       if (fflush(dfu_file) == EOF) {
+               if (!dfu_failed) {
+                       dfu_failed = 1;
+                       dfu_error = errno;
+               }
+       }
+       if (dfu_failed)
+               errno = dfu_error;
+       return !dfu_failed;
+}
+
+static void dfu_8(uint8_t byte) {
+       if (putc(byte, dfu_file) == EOF) {
+               if (!dfu_failed) {
+                       dfu_failed = 1;
+                       dfu_error = errno;
+               }
+       }
+       dfu_crc = update_crc(dfu_crc, byte);
+}
+
+static void dfu_pad(int len) {
+       while (len--)
+               dfu_8(0);
+}
+
+static void dfu_string(char *string) {
+       char    c;
+
+       while ((c = *string++))
+               dfu_8((uint8_t) c);
+}
+
+static void dfu_string_pad(char *string, int len) {
+       char    c;
+
+       while ((c = *string++)) {
+               dfu_8((uint8_t) c);
+               len--;
+       }
+       dfu_pad(len);
+}
+
+static void dfu_block(uint8_t *bytes, int len) {
+       while (len--)
+               dfu_8(*bytes++);
+}
+
+static void dfu_lsb16(uint16_t value) {
+       dfu_8(value);
+       dfu_8(value>>8);
+}
+
+static void dfu_lsb32(uint32_t value) {
+       dfu_8(value);
+       dfu_8(value >> 8);
+       dfu_8(value >> 16);
+       dfu_8(value >> 24);
+}
+
+static uint32_t dfu_image_size(struct ao_hex_image *image) {
+       return 8 + image->length;
+}
+
+static uint32_t dfu_images_size(int num_image, struct ao_hex_image images[])
+{
+       uint32_t        size = 0;
+       int             i;
+
+       for (i = 0; i < num_image; i++)
+               size += dfu_image_size(&images[i]);
+       return size;
+}
+
+static void dfu_image(struct ao_hex_image *image)
+{
+       dfu_lsb32(image->address);
+       dfu_lsb32(image->length);
+       dfu_block(image->data, image->length);
+}
+
+static void dfu_target(char *name, int num_image, struct ao_hex_image images[])
+{
+       uint32_t        images_size = dfu_images_size(num_image, images);
+       int             i;
+
+       dfu_string("Target");
+       dfu_8(0);
+       if (name) {
+               dfu_8(1);
+               dfu_pad(3);
+               dfu_string_pad(name, 255);
+       } else {
+               dfu_8(0);
+               dfu_pad(3);
+               dfu_pad(255);
+       }
+       dfu_lsb32(images_size);
+       dfu_lsb32(num_image);
+       for (i = 0; i < num_image; i++)
+               dfu_image(&images[i]);
+}
+
+static uint32_t dfu_target_size(int num_image, struct ao_hex_image images[])
+{
+       return 274 + dfu_images_size(num_image, images);
+}
+
+static uint32_t
+dfu_size(int num_image, struct ao_hex_image images[])
+{
+       uint32_t        size = 0;
+       size += 11;     /* DFU Prefix */
+
+       size += dfu_target_size(num_image, images);
+
+       return size;
+}
+
+int
+ao_dfu_write(FILE *file, struct ao_dfu_info *info, int num_image, struct ao_hex_image images[])
+{
+       uint32_t        total_size;
+
+       total_size = dfu_size(num_image, images);
+
+       dfu_init(file);
+       /* DFU Prefix */
+       dfu_string(DFU_SIGNATURE);
+       dfu_8(0x01);
+       dfu_lsb32(total_size);
+       dfu_8(0x01);
+
+       dfu_target("ST...", num_image, images);
+
+       /* DFU Suffix */
+       dfu_lsb16(info->bcdDevice);
+       dfu_lsb16(info->idProduct);
+       dfu_lsb16(info->idVendor);
+       dfu_lsb16(DFU_SPEC_VERSION);
+       dfu_string("UFD");
+       dfu_8(16);
+       dfu_lsb32(dfu_crc);
+       return dfu_fini();
+}
+
diff --git a/ao-tools/lib/ao-dfu.h b/ao-tools/lib/ao-dfu.h
new file mode 100644 (file)
index 0000000..c3dfc49
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_DFU_H_
+#define _AO_DFU_H_
+
+struct ao_dfu_info {
+       uint16_t        bcdDevice;
+       uint16_t        idProduct;
+       uint16_t        idVendor;
+};
+
+#define DFU_SIGNATURE          "DfuSe"
+#define DFU_SPEC_VERSION       0x011a
+
+#define DFU_TARGET_SIGNATURE   "Target"
+
+int
+ao_dfu_write(FILE *file, struct ao_dfu_info *info, int num_image, struct ao_hex_image images[]);
+
+#endif /* _AO_DFU_H_ */
index 1cb162595cc449b439511aa53413c7737541c54a..b82009524925a214ce7872f98d8e84030e754d7c 100644 (file)
@@ -558,6 +558,7 @@ ao-tools/ao-cal-freq/Makefile
 ao-tools/ao-test-gps/Makefile
 ao-tools/ao-usbtrng/Makefile
 ao-tools/ao-chaosread/Makefile
+ao-tools/ao-makebin/Makefile
 ao-utils/Makefile
 src/Version
 ])