diff --git a/include/bootimage.h b/include/bootimage.h new file mode 100644 index 00000000..7742eca1 --- /dev/null +++ b/include/bootimage.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +typedef struct { + uint32_t kind; + uint32_t type; + uint32_t offset; /* byte offset from start of file */ + uint32_t length; /* length in bytes */ + uint8_t name[16]; + uint8_t sha256[32]; +} __attribute__ ((packed)) bootentry_file; + +typedef struct { + uint32_t kind; + union { + uint32_t u[15]; + uint8_t b[60]; + } __attribute__((packed)) u; +} __attribute__((packed)) bootentry_data; + +typedef struct { + uint32_t kind; + uint32_t version; /* bootimage version */ + uint32_t image_size; /* byte size of entire image */ + uint32_t entry_count; /* number of valid bootentries */ + uint32_t reserved[12]; +} __attribute__((packed)) bootentry_info; + +typedef union { + uint32_t kind; + bootentry_file file; + bootentry_data data; + bootentry_info info; +} bootentry; + +#define BOOT_VERSION 0x00010000 /* 1.0 */ + +#define BOOT_MAGIC "" +#define BOOT_MAGIC_LENGTH 16 + +// header (bootentry_file, but special): + +// bootentry kinds: +#define KIND_BOOT_INFO 0x6f666e69 +#define KIND_FILE 0x656c6966 +#define KIND_BOARD 0x67726174 // board id string +#define KIND_BUILD 0x706d7473 // build id string + +// bootentry_file types: +#define TYPE_LK 0x6b6c6b6c +#define TYPE_FPGA_IMAGE 0x61677066 +#define TYPE_LINUX_KERNEL 0x6b78636c +#define TYPE_LINUX_INITRD 0x64697264 +#define TYPE_DEVICE_TREE 0x74766564 +#define TYPE_SYSPARAMS 0x70737973 +#define TYPE_UNKNOWN 0x6e6b6e75 +#define TYPE_BOOT_IMAGE 0x746f6f62 + +// first entry must be: +// kind: KIND_FILE +// type: TYPE_BOOT_IMAGE +// offset: 0 +// length: 4096 +// name: BOOT_MAGIC +// sha256: of bootentry[1..63] + +// second entry must be: +// kind: KIND_BOOT_INFO + +// offsets should be multiple-of-4096 diff --git a/tools/Makefile b/tools/Makefile index f0357c8f..8e60fef5 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,8 +1,14 @@ -all: lkboot +all: lkboot mkboot -lkboot: lkboot.c liblkboot.c network.c network.h liblkboot.h ../app/lkboot/lkboot.h - gcc -Wall -o lkboot lkboot.c liblkboot.c network.c +LKBOOT_SRCS := lkboot.c liblkboot.c network.c +LKBOOT_DEPS := network.h liblkboot.h ../app/lkboot/lkboot.h +lkboot: $(LKBOOT_SRCS) $(LKBOOT_DEPS) + gcc -Wall -o lkboot $(LKBOOT_SRCS) + +MKBOOT_SRCS := mkimage.c bootimage.c ../lib/mincrypt/sha256.c +mkboot: $(MKBOOT_SRCS) + gcc -Wall -g -o mkimage -I../lib/mincrypt/include $(MKBOOT_SRCS) clean:: - rm -f lkboot + rm -f lkboot mkimage diff --git a/tools/bootimage.c b/tools/bootimage.c new file mode 100644 index 00000000..3355a03b --- /dev/null +++ b/tools/bootimage.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2014 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "bootimage.h" + +struct bootimage { + bootentry entry[64]; + void *data[64]; + uint32_t offset[64]; + uint32_t length[64]; + unsigned count; + uint32_t next_offset; +}; + +bootimage *bootimage_init(void) { + bootimage *img; + + if ((img = malloc(sizeof(bootimage))) == NULL) { + return NULL; + } + memset(img, 0, sizeof(bootimage)); + img->count = 2; + img->next_offset = 4096; + memset(img->entry, 0, 4096); + img->entry[0].file.kind = KIND_FILE; + img->entry[0].file.type = TYPE_BOOT_IMAGE; + img->entry[0].file.offset = 0; + img->entry[0].file.length = 4096; + img->entry[1].info.kind = KIND_BOOT_INFO; + img->entry[1].info.version = BOOT_VERSION; + memcpy(img->entry[0].file.name, BOOT_MAGIC, BOOT_MAGIC_LENGTH); + return img; +} + +bootentry_data *bootimage_add_string(bootimage *img, unsigned kind, const char *s) { + unsigned n = img->count; + int len = strlen(s); + if (img->count == 64) return NULL; + if (len > 59) return NULL; + img->count++; + + img->entry[n].data.kind = kind; + strcpy((char*) img->entry[n].data.u.b, s); + return &(img->entry[n].data); +} + +bootentry_file *bootimage_add_filedata(bootimage *img, unsigned type, void *data, unsigned len) { + unsigned n = img->count; + if (img->count == 64) return NULL; + img->count++; + + // align to page boundary + img->next_offset = (img->next_offset + 4095) & (~4095); + + img->entry[n].file.kind = KIND_FILE; + img->entry[n].file.type = type; + img->entry[n].file.offset = img->next_offset; + img->entry[n].file.length = len; + SHA256_hash(data, len, img->entry[n].file.sha256); + + img->data[n] = data; + img->offset[n] = img->next_offset; + img->length[n] = len; + + img->next_offset += len; + + return &(img->entry[n].file); +} + +void bootimage_done(bootimage *img) { + unsigned sz = img->next_offset; + if (sz & 4095) { + sz += (4096 - (sz & 4095)); + } + img->entry[1].info.image_size = sz; + img->entry[1].info.entry_count = img->count; + SHA256_hash((void*) &(img->entry[1]), 4096 - 64, img->entry[0].file.sha256); +} + +static int writex(int fd, void *data, size_t len) { + int r; + char *x = data; + while (len > 0) { + r = write(fd, x, len); + if (r < 0) { + if (errno == EINTR) { + continue; + } + return -1; + } + len -= r; + x += r; + } + return 0; +} + +static uint8_t filler[4096] = { 0, }; + +int bootimage_write(bootimage *img, int fd) { + unsigned off = 4096; + unsigned n, s; + if (writex(fd, img->entry, 4096)) { + return -1; + } + for (n = 1; n < 64; n++) { + if (img->offset[n] == 0) continue; + if (img->offset[n] < off) return -1; + s = img->offset[n] - off; + if (s > 4095) return -1; + if (writex(fd, filler, s)) { + return -1; + } + off += s; + if (writex(fd, img->data[n], img->length[n])) { + return -1; + } + off += img->length[n]; + } + if (off & 4095) { + if (writex(fd, filler, 4096 - (off & 4095))) return -1; + } + return 0; +} + +static void *load_file(const char *fn, unsigned *len) { + off_t sz; + void *data = NULL; + char *x; + int fd, r; + + if((fd = open(fn, O_RDONLY)) < 0) { + return NULL; + } + + if ((sz = lseek(fd, 0, SEEK_END)) < 0) { + goto fail; + } + if (lseek(fd, 0, SEEK_SET) != 0) { + goto fail; + } + + if ((data = malloc(sz)) == NULL) { + goto fail; + } + x = data; + if (len) { + *len = sz; + } + while (sz > 0) { + r = read(fd, x, sz); + if (r < 0) { + if (errno == EINTR) { + continue; + } + goto fail; + } + sz -= r; + x += r; + } + close(fd); + return data; + +fail: + if (data) { + free(data); + } + close(fd); + return NULL; +} + +bootentry_file *bootimage_add_file(bootimage *img, unsigned type, const char *fn) { + void *data; + unsigned len; + if ((data = load_file(fn, &len)) == NULL) { + fprintf(stderr, "error: cannot load '%s'\n", fn); + } + return bootimage_add_filedata(img, type, data, len); +} diff --git a/tools/bootimage.h b/tools/bootimage.h new file mode 100644 index 00000000..66534d2a --- /dev/null +++ b/tools/bootimage.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "../include/bootimage.h" + +typedef struct bootimage bootimage; + +bootimage *bootimage_init(void); + +bootentry_data *bootimage_add_string( + bootimage *img, unsigned kind, const char *s); + +bootentry_file *bootimage_add_filedata( + bootimage *img, unsigned type, void *data, unsigned len); + +bootentry_file *bootimage_add_file( + bootimage *img, unsigned type, const char *fn); + +void bootimage_done(bootimage *img); + +int bootimage_write(bootimage *img, int fd); diff --git a/tools/mkimage.c b/tools/mkimage.c new file mode 100644 index 00000000..6bebe16a --- /dev/null +++ b/tools/mkimage.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014 Brian Swetland + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "bootimage.h" + +static const char *outname = "boot.img"; + +static struct { + const char *cmd; + unsigned kind; + unsigned type; +} types[] = { + { "lk", KIND_FILE, TYPE_LK }, + { "fpga", KIND_FILE, TYPE_FPGA_IMAGE }, + { "linux", KIND_FILE, TYPE_LINUX_KERNEL }, + { "initrd", KIND_FILE, TYPE_LINUX_INITRD }, + { "devicetree", KIND_FILE, TYPE_DEVICE_TREE }, + { "sysparams", KIND_FILE, TYPE_SYSPARAMS }, + { "board", KIND_BOARD, 0 }, + { "build", KIND_BUILD, 0 }, + { NULL, 0 }, +}; + +int process(bootimage *img, char *cmd, char *arg) { + unsigned n; + + for (n = 0; types[n].cmd != NULL; n++) { + if (strcmp(cmd, types[n].cmd)) { + continue; + } + if (types[n].kind == KIND_FILE) { + if (bootimage_add_file(img, types[n].type, arg) == NULL) { + return -1; + } + } else { + if (bootimage_add_string(img, types[n].kind, arg) == NULL) { + return -1; + } + } + return 0; + } + + if (!strcmp(cmd, "output")) { + outname = arg; + return 0; + } + + fprintf(stderr, "unknown command '%s'\n", cmd); + return -1; +} + +int main(int argc, char **argv) { + bootimage *img; + int fd; + + img = bootimage_init(); + + while (argc > 1) { + char *cmd = argv[1]; + char *arg = strchr(cmd, ':'); + argc--; + argv++; + + if (arg == NULL) { + fprintf(stderr, "error: invalid argument '%s'\n", cmd); + return -1; + } + + *arg++ = 0; + + if (process(img, cmd, arg)) { + return -1; + } + } + + bootimage_done(img); + + if ((fd = open(outname, O_CREAT|O_TRUNC|O_WRONLY, 0644)) < 0) { + fprintf(stderr, "error; cannot open '%s' for writing\n", outname); + return -1; + } + if (bootimage_write(img, fd)) { + fprintf(stderr, "error: failed to write '%s'\n", outname); + unlink(outname); + return -1; + } + close(fd); + return 0; +} +