summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2019-06-11 11:02:02 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2019-06-13 07:56:05 +0200
commit98723024413d745d6c053cd6823ebe426d8b04dc (patch)
treedb83dfd44ce09fd5387edfe265fb5f152b9c1d92
parentb8740d6022fee0523310ac15104e7f81f0e19246 (diff)
mvebuimg: new tool to create kwb version1 boot images
The purpose is the same as kwbimage, but the code is saner (in my eyes at least). Currently it only supports creating v1 images, but can be easily expanded to support v0 and extraction, too. The main advantage is that the file name of the binary headers is passed via cmdline and so the barebox build system can know about them without specifying them twice. Additionally it supports more than one binary header and doesn't create broken images when running on a big endian platform. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--scripts/Makefile2
-rw-r--r--scripts/mvebuimg.c569
2 files changed, 570 insertions, 1 deletions
diff --git a/scripts/Makefile b/scripts/Makefile
index 1af5f9f..7d64da6 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -13,7 +13,7 @@ hostprogs-y += kernel-install
hostprogs-$(CONFIG_IMD) += bareboximd
hostprogs-$(CONFIG_KALLSYMS) += kallsyms
hostprogs-$(CONFIG_MIPS) += mips-relocs
-hostprogs-$(CONFIG_MVEBU_HOSTTOOLS) += kwbimage kwboot
+hostprogs-$(CONFIG_MVEBU_HOSTTOOLS) += kwbimage kwboot mvebuimg
hostprogs-$(CONFIG_ARCH_NETX) += gen_netx_image
hostprogs-$(CONFIG_ARCH_OMAP) += omap_signGP mk-omap-image
hostprogs-$(CONFIG_ARCH_S5PCxx) += s5p_cksum
diff --git a/scripts/mvebuimg.c b/scripts/mvebuimg.c
new file mode 100644
index 0000000..8e9b1f9
--- /dev/null
+++ b/scripts/mvebuimg.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define ALIGN_SUP(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define is_power_of_two(a) (((a) & ((a) - 1)) == 0)
+#define container_of(ptr, type, member) \
+ (type *)((char *)(ptr) - (char *) &((type *)0)->member)
+
+
+#define pr_err(fmt, arg...) fprintf(stderr, "ERROR: " fmt, ##arg)
+#define pr_info(fmt, arg...) fprintf(stderr, "INFO: " fmt, ##arg)
+
+static const struct {
+ unsigned char id;
+ char *name;
+} boot_ids[] = {
+ { 0x4d, "i2c" },
+ { 0x5a, "spi" },
+ { 0x69, "uart" },
+ { 0x78, "sata" },
+ { 0x8b, "nand" },
+ { 0x9c, "pex" },
+ { /* sentinel */ }
+};
+
+struct mvebuimg_checksum_method {
+ void (*update)(struct mvebuimg_checksum_method *self,
+ const void *buf, size_t len);
+};
+
+struct mvebuimg_checksum_method8 {
+ struct mvebuimg_checksum_method method;
+ uint8_t csum;
+};
+
+static void mvebuimg_checksum8(struct mvebuimg_checksum_method *self,
+ const void *buf, size_t len)
+{
+ struct mvebuimg_checksum_method8 *realself =
+ container_of(self, struct mvebuimg_checksum_method8, method);
+
+ while (len--) {
+ realself->csum += *(uint8_t *)buf;
+ buf++;
+ }
+}
+
+struct mvebuimg_checksum_method32 {
+ struct mvebuimg_checksum_method method;
+ uint32_t csum;
+ int mod;
+};
+
+static void mvebuimg_checksum32(struct mvebuimg_checksum_method *self,
+ const void *buf, size_t len)
+{
+ struct mvebuimg_checksum_method32 *realself =
+ container_of(self, struct mvebuimg_checksum_method32, method);
+
+ while (len--) {
+ realself->csum += *(uint8_t *)buf << (8 * realself->mod);
+ realself->mod = (realself->mod + 1) % 4;
+ buf++;
+ }
+}
+
+static ssize_t mvebuimg_sendfile(int outfd, int infd,
+ struct mvebuimg_checksum_method *csumm,
+ size_t count)
+{
+ char buf[1024];
+ size_t head = 0, tail = 0;
+ int ret;
+ size_t count_read = 0, count_written = 0;
+
+ while (count_written < count) {
+ if (head == tail) {
+ head = 0;
+ tail = 0;
+ }
+
+ if (tail < sizeof(buf) && count_read < count) {
+ ret = read(infd, buf + tail,
+ min(count, sizeof(buf) - tail));
+ if (ret < 0) {
+ pr_err("read error\n");
+ return ret;
+ }
+
+ if (ret == 0) {
+ pr_err("hit EOF\n");
+ return -EINVAL;
+ }
+
+ if (csumm)
+ csumm->update(csumm, buf + tail, ret);
+
+ tail += ret;
+ count_read += ret;
+ }
+
+ if (head < tail) {
+ ret = write(outfd, buf + head, tail - head);
+ if (ret < 0)
+ return ret;
+
+ head += ret;
+ count_written += ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mvebuimg_v1_write_binhdr(int fd, size_t *offset,
+ const char *filename,
+ struct mvebuimg_checksum_method8 *summ,
+ int is_last_header)
+{
+ int binhdr_fd;
+ char binhdr[16] = { 0, };
+ struct stat s;
+ uint32_t hdrsize;
+ int ret;
+ off_t ret_lseek;
+ char trailer[4] = { 0, };
+
+ binhdr[0] = 0x2;
+
+ binhdr_fd = open(filename, O_RDONLY);
+ if (binhdr_fd < 0) {
+ pr_err("Failed to open binhdr source \"%s\" (%s)\n",
+ filename, strerror(errno));
+ return -errno;
+ }
+
+ ret = fstat(binhdr_fd, &s);
+ if (ret < 0) {
+ pr_err("Failed to stat binhdr source \"%s\" (%s)\n",
+ filename, strerror(errno));
+ return -errno;
+ }
+
+ if (ALIGN_SUP(s.st_size, 4) > 0xffffff - 0x14) {
+ pr_err("binhdr \"%s\" too big\n", filename);
+ return -EINVAL;
+ }
+
+ hdrsize = /* len of hdr's header */ 0x10 +
+ /* actual image */ ALIGN_SUP(s.st_size, 4) +
+ /* trailer word */ 4;
+
+ binhdr[1] = hdrsize >> 16;
+ binhdr[2] = hdrsize;
+ binhdr[3] = hdrsize >> 8;
+
+ /* parameters that are used by all images I know of */
+ binhdr[4] = 2;
+ binhdr[8] = 0x5b;
+ binhdr[12] = 0x68;
+
+ summ->method.update(&summ->method, binhdr, sizeof(binhdr));
+ ret = pwrite(fd, binhdr, sizeof(binhdr), *offset);
+ if (ret < 0) {
+ pr_err("failed to write header of binhdr \"%s\"\n",
+ filename);
+ return -errno;
+ }
+
+ *offset += sizeof(binhdr);
+
+ ret_lseek = lseek(fd, *offset, SEEK_SET);
+ if (ret_lseek == (off_t)-1) {
+ pr_err("failed to seek to binhdr offset\n");
+ return -errno;
+ }
+
+ ret = mvebuimg_sendfile(fd, binhdr_fd, &summ->method, s.st_size);
+ if (ret < 0)
+ return ret;
+
+ *offset += ALIGN_SUP(s.st_size, 4);
+ trailer[0] = is_last_header ? 0 : 1;
+
+ summ->method.update(&summ->method, trailer, sizeof(4));
+ ret = pwrite(fd, trailer, sizeof(trailer), *offset);
+ if (ret < 0) {
+ pr_err("failed to write trailer of binhdr \"%s\"\n",
+ filename);
+ return -errno;
+ }
+
+ *offset += 4;
+
+ return 0;
+}
+
+static int mvebuimg_v1_write_payload(int fd, size_t *offset,
+ const char *filename)
+{
+ int payload_fd;
+ struct stat s;
+ int ret;
+ char pad[4] = { 0, };
+ struct mvebuimg_checksum_method32 csumm32 = {
+ .method = {
+ .update = mvebuimg_checksum32,
+ },
+ .csum = 0,
+ };
+
+ payload_fd = open(filename, O_RDONLY);
+ if (payload_fd < 0) {
+ pr_err("Failed to open payload \"%s\" (%s)\n",
+ filename, strerror(errno));
+ return -errno;
+ }
+
+ ret = fstat(payload_fd, &s);
+ if (ret < 0) {
+ pr_err("Failed to stat payload \"%s\" (%s)\n",
+ filename, strerror(errno));
+ return -errno;
+ }
+
+ lseek(fd, *offset, SEEK_SET);
+
+ ret = mvebuimg_sendfile(fd, payload_fd, &csumm32.method, s.st_size);
+ if (ret < 0)
+ return ret;
+
+ /* pad to a multiple of 4 bytes */
+ ret = pwrite(fd, pad, ALIGN_SUP(s.st_size, 4) - s.st_size,
+ *offset + s.st_size);
+ if (ret < 0) {
+ pr_err("failed to write padding of payload\n");
+ return -errno;
+ }
+ *offset += ALIGN_SUP(s.st_size, 4);
+
+ /* For version 1 images the checksum doesn't seem to be necessary. We're
+ * still adding it for now to create bytewise identical images as
+ * kwbimage.
+ */
+ pad[0] = csumm32.csum;
+ pad[1] = csumm32.csum >> 8;
+ pad[2] = csumm32.csum >> 16;
+ pad[3] = csumm32.csum >> 24;
+
+ ret = pwrite(fd, pad, 4, *offset);
+ *offset += 4;
+
+ return 0;
+}
+
+/*
+ * Usage: create [-b binhdr]* [-o outputfilename] bootsrc payload
+ */
+static int mvebuimg_v1_create(int argc, char *const argv[])
+{
+ int opt;
+ char *output = NULL;
+ unsigned int extensionheaders = 0;
+ int fd, ret;
+ long l;
+ char *endptr;
+ char mainhdr[0x20] = { 0, };
+ struct mvebuimg_checksum_method8 csumm = {
+ .method = {
+ .update = mvebuimg_checksum8,
+ },
+ .csum = 0,
+ };
+ size_t offset, offset_payload;
+ size_t size_payload;
+ unsigned long payload_align = 4096;
+ int debug = 0;
+ long destaddr, execaddr;
+ int destaddr_provided = 0, execaddr_provided = 0;
+ uint8_t nand_blksz = 0;
+ uint8_t nand_bbloc = 0;
+
+ optind = 0;
+ while ((opt = getopt(argc, argv, "+a:b:B:Dd:e:o:")) != -1) {
+ switch (opt) {
+ case 'a':
+ errno = 0;
+ payload_align = strtol(optarg, &endptr, 0);
+ if (errno || *endptr != '\0' ||
+ !is_power_of_two(payload_align)) {
+ pr_err("Invalid payload alignment, must be a power of two\n");
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'b':
+ ++extensionheaders;
+ break;
+
+ case 'B':
+ errno = 0;
+ l = strtol(optarg, &endptr, 0);
+ if (errno || (*endptr != '\0' && *endptr != ':') ||
+ (l != 0 && (l < 0x10000 || l > 0xff0000 ||
+ !is_power_of_two(l)))) {
+ pr_err("Invalid NAND block size\n");
+ return EXIT_FAILURE;
+ }
+ nand_blksz = l >> 16;
+
+ if (*endptr != ':')
+ break;
+
+ if ((*(endptr + 1) == '0' || *(endptr + 1) == '1') &&
+ *(endptr + 2) == '\0') {
+ nand_bbloc = *(endptr + 1) - '0';
+ } else {
+ pr_err("Invalid NAND bad block location\n");
+ return EXIT_FAILURE;
+ }
+
+ break;
+
+ case 'D':
+ debug = 1;
+ break;
+
+ case 'd':
+ errno = 0;
+ destaddr = strtol(optarg, &endptr, 0);
+ if (errno || *endptr != '\0' ||
+ destaddr < 0 || destaddr > 0xffffffff) {
+ pr_err("invalid dest address\n");
+ return EXIT_FAILURE;
+ }
+ destaddr_provided = 1;
+ break;
+
+ case 'e':
+ errno = 0;
+ execaddr = strtol(optarg, &endptr, 0);
+ if (errno || *endptr != '\0' ||
+ execaddr < 0 || execaddr > 0xffffffff) {
+ pr_err("invalid exec address\n");
+ return EXIT_FAILURE;
+ }
+ execaddr_provided = 1;
+ break;
+
+ case 'o':
+ if (output) {
+ pr_err("more than one output file specified\n");
+ return EXIT_FAILURE;
+ }
+ output = optarg;
+ break;
+
+ default:
+ pr_err("Unsupported option\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (argc - optind < 2) {
+ pr_err("too few parameters\n");
+ return EXIT_FAILURE;
+ }
+
+ errno = 0;
+ l = strtol(argv[optind], &endptr, 0);
+ if (errno || *endptr != '\0' || l < 0 || l > 0xff) {
+ int i;
+ int idfound = 0;
+
+ for (i = 0; boot_ids[i].name; ++i) {
+ if (strcmp(boot_ids[i].name, argv[optind]) == 0) {
+ mainhdr[0] = boot_ids[i].id;
+ idfound = 1;
+ break;
+ }
+ }
+
+ if (!idfound) {
+ fprintf(stderr, "failed to parse bootsrc\n");
+ return EXIT_FAILURE;
+ }
+
+ } else {
+ mainhdr[0] = l;
+ }
+
+ if (!destaddr_provided)
+ destaddr = 0x1000000;
+
+ if (!execaddr_provided)
+ execaddr = destaddr;
+
+ if (!output) {
+ pr_err("no output option given\n");
+ return EXIT_FAILURE;
+ }
+
+ fd = open(output, O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ if (fd < 0) {
+ pr_err("failed to open output\n");
+ return EXIT_FAILURE;
+ }
+
+ if (extensionheaders)
+ mainhdr[0x1e] = 1;
+
+ offset = 0x20;
+ optind = 0;
+ while ((opt = getopt(argc, argv, "+a:b:B:Dd:e:o:")) != -1) {
+ switch (opt) {
+ case 'b':
+ --extensionheaders;
+
+ ret = mvebuimg_v1_write_binhdr(fd, &offset, optarg,
+ &csumm,
+ extensionheaders == 0);
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Align the payload to a reasonable boundary */
+ offset = offset_payload = ALIGN_SUP(offset, 4096);
+
+ /* BootROM debugging */
+ mainhdr[0x01] = debug ? 0x1 : 0x0;
+
+ /* Boot Image Format Version */
+ mainhdr[0x08] = 0x1;
+
+ /* Header size */
+ mainhdr[0x09] = offset_payload >> 16;
+ mainhdr[0x0a] = offset_payload;
+ mainhdr[0x0b] = offset_payload >> 8;
+
+ /* Offset of payload in the image */
+ mainhdr[0x0c] = (char)offset_payload;
+ mainhdr[0x0d] = (char)(offset_payload >> 8);
+ mainhdr[0x0e] = (char)(offset_payload >> 16);
+ mainhdr[0x0f] = (char)(offset_payload >> 24);
+
+ /* Address to load the image to */
+ mainhdr[0x10] = (char)destaddr;
+ mainhdr[0x11] = (char)(destaddr >> 8);
+ mainhdr[0x12] = (char)(destaddr >> 16);
+ mainhdr[0x13] = (char)(destaddr >> 24);
+
+ /* Entry point */
+ mainhdr[0x14] = (char)execaddr;
+ mainhdr[0x15] = (char)(execaddr >> 8);
+ mainhdr[0x16] = (char)(execaddr >> 16);
+ mainhdr[0x17] = (char)(execaddr >> 24);
+
+ /* For NAND images set block size and bad block location */
+ mainhdr[0x19] = nand_blksz;
+ mainhdr[0x1a] = nand_bbloc;
+
+ ret = mvebuimg_v1_write_payload(fd, &offset, argv[optind + 1]);
+ if (ret < 0)
+ return EXIT_FAILURE;
+
+ size_payload = offset - offset_payload;
+ mainhdr[0x04] = (char)size_payload;
+ mainhdr[0x05] = (char)(size_payload >> 8);
+ mainhdr[0x06] = (char)(size_payload >> 16);
+ mainhdr[0x07] = (char)(size_payload >> 24);
+
+ csumm.method.update(&csumm.method, mainhdr, sizeof(mainhdr));
+ mainhdr[0x1f] = csumm.csum;
+ ret = pwrite(fd, mainhdr, sizeof(mainhdr), 0);
+ if (ret < 0) {
+ pr_err("failure write main header (%s)\n",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ ret = close(fd);
+ if (ret) {
+ pr_err("failure during output closing (%s)\n",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static const struct {
+ const char *name;
+ int (*func[2])(int argc, char *const argv[]);
+} commands[] = {
+ {
+ .name = "create",
+ .func = { NULL, mvebuimg_v1_create }
+ }, {
+ /* sentinel */
+ }
+};
+
+int main(int argc, char *const argv[])
+{
+ int opt;
+ long version = -1;
+ int i;
+
+ char *endptr;
+
+ optind = 0;
+ while ((opt = getopt(argc, argv, "+v:")) != -1) {
+ switch (opt) {
+ case 'v':
+ errno = 0;
+ version = strtol(optarg, &endptr, 0);
+ if (errno || *endptr != '\0' ||
+ version < 0 || version > 1) {
+ pr_err("Unsupported version\n");
+ return EXIT_FAILURE;
+ }
+ break;
+ default:
+ pr_err("Unsupported option\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (version < 0) {
+ pr_info("Defaulting to version 1\n");
+ version = 1;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ pr_err("No command given\n");
+ return EXIT_FAILURE;
+ }
+
+ for (i = 0; commands[i].name != NULL; ++i) {
+ if (strcmp(commands[i].name, argv[0]) == 0) {
+ if (!commands[i].func[version]) {
+ pr_err("ERROR: command not implemented\n");
+ return EXIT_FAILURE;
+ }
+ return commands[i].func[version](argc, argv);
+ }
+ }
+
+ pr_err("command \"%s\" not found\n", argv[0]);
+ return EXIT_FAILURE;
+}