diff --git a/scripts/Makefile b/scripts/Makefile index 1af5f9f..7d64da6 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -13,7 +13,7 @@ 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}