From f8f2d851f0387a950df5f62e0075531efd9f097c Mon Sep 17 00:00:00 2001 From: Travis Geiselbrecht Date: Wed, 5 Nov 2014 16:49:38 -0800 Subject: [PATCH] [lib][bootimage] first stab at device side bootimage processing Add lib/bootimage to parse in memory copies of bootimages Add a few command line arguments to mkimage --- lib/bootimage/bootimage.c | 208 ++++++++++++++++++ lib/bootimage/include/lib/bootimage.h | 36 +++ .../bootimage/include/lib/bootimage_struct.h | 66 +++--- lib/bootimage/rules.mk | 13 ++ tools/Makefile | 12 +- tools/bootimage.c | 1 + tools/bootimage.h | 2 +- tools/mkimage.c | 48 ++-- 8 files changed, 332 insertions(+), 54 deletions(-) create mode 100644 lib/bootimage/bootimage.c create mode 100644 lib/bootimage/include/lib/bootimage.h rename include/bootimage.h => lib/bootimage/include/lib/bootimage_struct.h (57%) create mode 100644 lib/bootimage/rules.mk diff --git a/lib/bootimage/bootimage.c b/lib/bootimage/bootimage.c new file mode 100644 index 00000000..a8b79058 --- /dev/null +++ b/lib/bootimage/bootimage.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2014 Travis Geiselbrecht + * + * 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 + +#define LOCAL_TRACE 1 + +struct bootimage { + const uint8_t *ptr; + size_t len; +}; + +static status_t validate_bootimage(bootimage_t *bi) +{ + if (!bi) + return ERR_INVALID_ARGS; + + /* is it large enough to hold the first entry */ + if (bi->len < 4096) { + LTRACEF("bootentry too short\n"); + return ERR_BAD_LEN; + } + + bootentry *be = (bootentry *)bi->ptr; + + /* check that the first entry is a file, type boot info, and is 4096 bytes at offset 0 */ + if (be->kind != KIND_FILE || + be->file.type != TYPE_BOOT_IMAGE || + be->file.offset != 0 || + be->file.length != 4096 || + memcmp(be->file.name, BOOT_MAGIC, sizeof(be->file.name))) { + LTRACEF("invalid first entry\n"); + return ERR_INVALID_ARGS; + } + + /* check the sha256 of the rest of the first page */ + SHA256_CTX ctx; + SHA256_init(&ctx); + + SHA256_update(&ctx, be + 1, 4096 - sizeof(bootentry)); + const uint8_t *hash = SHA256_final(&ctx); + + if (memcmp(hash, be->file.sha256, sizeof(be->file.sha256)) != 0) { + LTRACEF("bad hash of first section\n"); + + return ERR_CHECKSUM_FAIL; + } + + /* look at the second entry, which should be a boot info structure */ + if (be[1].kind != KIND_BOOT_INFO) { + LTRACEF("second entry not boot info\n"); + return ERR_INVALID_ARGS; + } + + bootentry_info *info = &be[1].info; + + /* is the image a handled version */ + if (info->version > BOOT_VERSION) { + LTRACEF("unhandled version 0x%x\n", info->version); + return ERR_INVALID_ARGS; + } + + /* is the image the right size? */ + if (info->image_size > bi->len) { + LTRACEF("boot image block says image is too big (0x%x bytes)\n", info->image_size); + return ERR_INVALID_ARGS; + } + + /* trim the len to what the info block says */ + bi->len = info->image_size; + + /* iterate over the remaining entries in the list */ + for (size_t i = 2; i < info->entry_count; i++) { + if (be[i].kind == 0) + break; + + LTRACEF("%u: kind 0x%x\n", i, be[i].kind); + + switch (be[i].kind) { + case KIND_BOOT_INFO: + break; + case KIND_BOARD: + break; + case KIND_BUILD: + break; + case KIND_FILE: { + LTRACEF("\ttype %c%c%c%c offset 0x%x, length 0x%x\n", + (be[i].file.type >> 0) & 0xff, (be[i].file.type >> 8) & 0xff, + (be[i].file.type >> 16) & 0xff, (be[i].file.type >> 24) & 0xff, + be[i].file.offset, be[i].file.length); + + /* check that the file section is inside the overall image */ + uint32_t end = be[i].file.offset + be[i].file.length; + if (end < be[i].file.offset || end > info->image_size) { + LTRACEF("bad file section, size too large\n"); + return ERR_INVALID_ARGS; + } + + /* check the sha256 hash */ + SHA256_init(&ctx); + + SHA256_update(&ctx, (const uint8_t *)bi->ptr + be[i].file.offset, be[i].file.length); + const uint8_t *hash = SHA256_final(&ctx); + + if (memcmp(hash, be[i].file.sha256, sizeof(be[i].file.sha256)) != 0) { + LTRACEF("bad hash of file section\n"); + + return ERR_CHECKSUM_FAIL; + } + + break; + } + default: + LTRACEF("unknown kind 0x%x\n", be[i].kind); + return ERR_INVALID_ARGS; + } + } + + return NO_ERROR; +} + +status_t bootimage_open(const void *ptr, size_t len, bootimage_t **bi) +{ + LTRACEF("ptr %p, len %zu\n", ptr, len); + + if (!bi) + return ERR_INVALID_ARGS; + + *bi = calloc(1, sizeof(bootimage_t)); + if (!*bi) + return ERR_NO_MEMORY; + + (*bi)->ptr = ptr; + (*bi)->len = len; + + /* try to validate it */ + status_t err = validate_bootimage(*bi); + if (err < 0) { + bootimage_close(*bi); + return err; + } + + return NO_ERROR; +} + +status_t bootimage_close(bootimage_t *bi) +{ + if (bi) + free(bi); + + return NO_ERROR; +} + +status_t bootimage_get_file_section(bootimage_t *bi, uint32_t type, void **ptr, size_t *len) +{ + if (!bi) + return ERR_INVALID_ARGS; + + bootentry *be = (bootentry *)bi->ptr; + bootentry_info *info = &be[1].info; + + for (size_t i = 2; i < info->entry_count; i++) { + if (be[i].kind == 0) + break; + + if (be[i].kind != KIND_FILE) + continue; + + if (type == be[i].file.type) { + if (ptr) + *ptr = bi->ptr + be[i].file.offset; + if (len) + *len = be[i].file.length; + return NO_ERROR; + } + } + + return ERR_NOT_FOUND; +} + diff --git a/lib/bootimage/include/lib/bootimage.h b/lib/bootimage/include/lib/bootimage.h new file mode 100644 index 00000000..ec86dbbf --- /dev/null +++ b/lib/bootimage/include/lib/bootimage.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014 Travis Geiselbrecht + * + * 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 +#include + +typedef struct bootimage bootimage_t; + +status_t bootimage_open(const void *ptr, size_t len, bootimage_t **bi) __NONNULL(); +status_t bootimage_close(bootimage_t *bi) __NONNULL(); + +/* ask for a file section of the bootimage, by type */ +status_t bootimage_get_file_section(bootimage_t *bi, uint32_t type, void **ptr, size_t *len) __NONNULL((1)); + diff --git a/include/bootimage.h b/lib/bootimage/include/lib/bootimage_struct.h similarity index 57% rename from include/bootimage.h rename to lib/bootimage/include/lib/bootimage_struct.h index 7742eca1..a59768eb 100644 --- a/include/bootimage.h +++ b/lib/bootimage/include/lib/bootimage_struct.h @@ -26,38 +26,38 @@ #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]; + 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; + 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]; + 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; + uint32_t kind; + bootentry_file file; + bootentry_data data; + bootentry_info info; } bootentry; -#define BOOT_VERSION 0x00010000 /* 1.0 */ +#define BOOT_VERSION 0x00010000 /* 1.0 */ #define BOOT_MAGIC "" #define BOOT_MAGIC_LENGTH 16 @@ -65,20 +65,20 @@ typedef union { // 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 +#define KIND_FILE 0x656c6966 // 'file' +#define KIND_BOOT_INFO 0x6f666e69 // 'info' +#define KIND_BOARD 0x67726174 // 'targ' board id string +#define KIND_BUILD 0x706d7473 // 'stmp' 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 +#define TYPE_BOOT_IMAGE 0x746f6f62 // 'boot' +#define TYPE_LK 0x6b6c6b6c // 'lklk' +#define TYPE_FPGA_IMAGE 0x61677066 // 'fpga' +#define TYPE_LINUX_KERNEL 0x6b78636c // 'lcxk' +#define TYPE_LINUX_INITRD 0x64697264 // 'drid' +#define TYPE_DEVICE_TREE 0x74766564 // 'devt' +#define TYPE_SYSPARAMS 0x70737973 // 'sysp' +#define TYPE_UNKNOWN 0x6e6b6e75 // 'unkn' // first entry must be: // kind: KIND_FILE diff --git a/lib/bootimage/rules.mk b/lib/bootimage/rules.mk new file mode 100644 index 00000000..a45da2a6 --- /dev/null +++ b/lib/bootimage/rules.mk @@ -0,0 +1,13 @@ +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +GLOBAL_INCLUDES += $(LOCAL_DIR)/include + +MODULE_DEPS := \ + lib/mincrypt + +MODULE_SRCS := \ + $(LOCAL_DIR)/bootimage.c + +include make/module.mk diff --git a/tools/Makefile b/tools/Makefile index 8e60fef5..e9e8e048 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -1,14 +1,16 @@ -all: lkboot mkboot +all: lkboot mkimage 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) + gcc -Wall -o $@ $(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) +MKIMAGE_DEPS := bootimage.h ../lib/bootimage/include/lib/bootimage_struct.h +MKIMAGE_SRCS := mkimage.c bootimage.c ../lib/mincrypt/sha256.c +MKIMAGE_INCS := -I../lib/mincrypt/include -I../lib/bootimage/include +mkimage: $(MKIMAGE_SRCS) $(MKIMAGE_DEPS) + gcc -Wall -g -o $@ $(MKIMAGE_INCS) $(MKIMAGE_SRCS) clean:: rm -f lkboot mkimage diff --git a/tools/bootimage.c b/tools/bootimage.c index 5b085fb5..ec6f07b5 100644 --- a/tools/bootimage.c +++ b/tools/bootimage.c @@ -203,6 +203,7 @@ bootentry_file *bootimage_add_file(bootimage *img, unsigned type, const char *fn unsigned len; if ((data = load_file(fn, &len)) == NULL) { fprintf(stderr, "error: cannot load '%s'\n", fn); + return NULL; } return bootimage_add_filedata(img, type, data, len); } diff --git a/tools/bootimage.h b/tools/bootimage.h index 66534d2a..d66e2b14 100644 --- a/tools/bootimage.h +++ b/tools/bootimage.h @@ -23,7 +23,7 @@ #pragma once -#include "../include/bootimage.h" +#include typedef struct bootimage bootimage; diff --git a/tools/mkimage.c b/tools/mkimage.c index 6bebe16a..26ecd4b8 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -47,6 +47,11 @@ static struct { { NULL, 0 }, }; +void usage(const char *binary) { + fprintf(stderr, "usage:\n"); + fprintf(stderr, "%s [-h] [-o 1) { char *cmd = argv[1]; @@ -87,30 +89,46 @@ int main(int argc, char **argv) { argc--; argv++; - if (arg == NULL) { - fprintf(stderr, "error: invalid argument '%s'\n", cmd); - return -1; - } + if (!strcmp(cmd, "-h") || !strcmp(cmd, "--help")) { + usage(binary); + return 1; + } else if (!strcmp(cmd, "-o")) { + outname = argv[2]; + argc--; + argv++; + } else { + if (arg == NULL) { + fprintf(stderr, "error: invalid argument '%s'\n", cmd); + return 1; + } - *arg++ = 0; + *arg++ = 0; - if (process(img, cmd, arg)) { - return -1; + if (process(img, cmd, arg)) { + return 1; + } + count++; } } + if (count == 0) { + fprintf(stderr, "no sections to process\n"); + 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; + return 1; } if (bootimage_write(img, fd)) { fprintf(stderr, "error: failed to write '%s'\n", outname); unlink(outname); - return -1; + return 1; } close(fd); return 0; } +// vim: set noexpandtab: