ptgen: add Chromium OS kernel partition support
[project/firmware-utils.git] / src / mkfwimage.c
index 8e505d7298c1a9b47066abf3f97208c79488da7a..7f15d1f1969bd3382db2b115824227e94069149d 100644 (file)
@@ -1,24 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * Copyright (C) 2007 Ubiquiti Networks, Inc.
  * Copyright (C) 2008 Lukas Kuna <ValXdater@seznam.cz>
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <inttypes.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
+#include <stdbool.h>
 #include "fw.h"
+#include "utils.h"
 
 typedef struct fw_layout_data {
-       char            name[PATH_MAX];
        u_int32_t       kern_start;
        u_int32_t       kern_entry;
        u_int32_t       firmware_max_length;
 } fw_layout_t;
 
-fw_layout_t fw_layout_data[] = {
+struct fw_info {
+       char                    name[PATH_MAX];
+       struct fw_layout_data   fw_layout;
+       bool                    sign;
+};
+
+struct fw_info fw_info[] = {
        {
-               .name           =       "XS2",
-               .kern_start     =       0xbfc30000,
-               .kern_entry     =       0x80041000,
-               .firmware_max_length=   0x00390000,
+               .name = "XS2",
+               .fw_layout = {
+                       .kern_start     =       0xbfc30000,
+                       .kern_entry     =       0x80041000,
+                       .firmware_max_length=   0x00390000,
+               },
+               .sign = false,
        },
        {
-               .name           =       "XS5",
-               .kern_start     =       0xbe030000,
-               .kern_entry     =       0x80041000,
-               .firmware_max_length=   0x00390000,
+               .name = "XS5",
+               .fw_layout = {
+                       .kern_start     =       0xbe030000,
+                       .kern_entry     =       0x80041000,
+                       .firmware_max_length=   0x00390000,
+               },
+               .sign = false,
        },
        {
-               .name           =       "RS",
-               .kern_start     =       0xbf030000,
-               .kern_entry     =       0x80060000,
-               .firmware_max_length=   0x00640000,
+               .name = "RS",
+               .fw_layout = {
+                       .kern_start     =       0xbf030000,
+                       .kern_entry     =       0x80060000,
+                       .firmware_max_length=   0x00B00000,
+               },
+               .sign = false,
        },
-       {       .name           =       "",
+       {
+               .name = "RSPRO",
+               .fw_layout = {
+                       .kern_start     =       0xbf030000,
+                       .kern_entry     =       0x80060000,
+                       .firmware_max_length=   0x00F00000,
+               },
+               .sign = false,
+       },
+       {
+               .name = "LS-SR71",
+               .fw_layout = {
+                       .kern_start     =       0xbf030000,
+                       .kern_entry     =       0x80060000,
+                       .firmware_max_length=   0x00640000,
+               },
+               .sign = false,
+       },
+       {
+               .name = "XS2-8",
+               .fw_layout = {
+                       .kern_start     =       0xa8030000,
+                       .kern_entry     =       0x80041000,
+                       .firmware_max_length=   0x006C0000,
+               },
+               .sign = false,
+
+       },
+       {
+               .name = "XM",
+               .fw_layout = {
+                       .kern_start     =       0x9f050000,
+                       .kern_entry     =       0x80002000,
+                       .firmware_max_length=   0x00760000,
+               },
+               .sign = false,
+       },
+       {
+               .name = "SW",
+               .fw_layout = {
+                       .kern_start     =       0x9f050000,
+                       .kern_entry     =       0x80002000,
+                       .firmware_max_length=   0x00760000,
+               },
+               .sign = false,
+       },
+       {
+               .name = "UBDEV01",
+               .fw_layout = {
+                       .kern_start     =       0x9f050000,
+                       .kern_entry     =       0x80002000,
+                       .firmware_max_length=   0x006A0000,
+               },
+               .sign = false,
+       },
+       {
+               .name = "WA",
+               .fw_layout = {
+                       .kern_start     =       0x9f050000,
+                       .kern_entry     =       0x80002000,
+                       .firmware_max_length=   0x00F60000,
+               },
+               .sign = true,
+       },
+       {
+               .name = "XC",
+               .fw_layout = {
+                       .kern_start     =       0x9f050000,
+                       .kern_entry     =       0x80002000,
+                       .firmware_max_length=   0x00F60000,
+               },
+               .sign = true,
+       },
+       {
+               .name = "ACB",
+               .fw_layout = {
+                       .kern_start     =       0x9f050000,
+                       .kern_entry     =       0x80002000,
+                       .firmware_max_length=   0x00F60000,
+               },
+               .sign = true,
+       },
+       {
+               .name = "",
        },
 };
 
@@ -78,41 +165,62 @@ typedef struct part_data {
 #define DEFAULT_OUTPUT_FILE    "firmware-image.bin"
 #define DEFAULT_VERSION                "UNKNOWN"
 
-#define OPTIONS "B:hv:o:r:k:"
-
-static int debug = 0;
+#define OPTIONS "B:hv:m:o:r:k:"
 
 typedef struct image_info {
+       char magic[16];
        char version[256];
        char outputfile[PATH_MAX];
        u_int32_t       part_count;
        part_data_t parts[MAX_SECTIONS];
+       struct fw_info* fwinfo;
 } image_info_t;
 
-static void write_header(void* mem, const char* version)
+static struct fw_info* get_fwinfo(char* board_name) {
+       struct fw_info *fwinfo = fw_info;
+       while(strlen(fwinfo->name)) {
+               if(strcmp(fwinfo->name, board_name) == 0) {
+                       return fwinfo;
+               }
+               fwinfo++;
+       }
+       return NULL;
+}
+
+static void write_header(void* mem, const char *magic, const char* version)
 {
        header_t* header = mem;
        memset(header, 0, sizeof(header_t));
 
-       memcpy(header->magic, MAGIC_HEADER, MAGIC_LENGTH);
-       strncpy(header->version, version, sizeof(header->version));
-       header->crc = htonl(crc32(0L, (unsigned char *)header,
-                               sizeof(header_t) - 2 * sizeof(u_int32_t)));
+       FW_MEMCPY_STR(header->magic, magic);
+       FW_MEMCPY_STR(header->version, version);
+       header->crc = htonl(crc32(0L, (uint8_t*) header,
+                           sizeof(header_t) - 2 * sizeof(u_int32_t)));
        header->pad = 0L;
 }
 
-
 static void write_signature(void* mem, u_int32_t sig_offset)
 {
        /* write signature */
        signature_t* sign = (signature_t*)(mem + sig_offset);
        memset(sign, 0, sizeof(signature_t));
 
-       memcpy(sign->magic, MAGIC_END, MAGIC_LENGTH);
+       FW_MEMCPY_STR(sign->magic, MAGIC_END);
        sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset));
        sign->pad = 0L;
 }
 
+static void write_signature_rsa(void* mem, u_int32_t sig_offset)
+{
+       /* write signature */
+       signature_rsa_t* sign = (signature_rsa_t*)(mem + sig_offset);
+       memset(sign, 0, sizeof(signature_rsa_t));
+
+       FW_MEMCPY_STR(sign->magic, MAGIC_ENDS);
+//     sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset));
+       sign->pad = 0L;
+}
+
 static int write_part(void* mem, part_data_t* d)
 {
        char* addr;
@@ -137,9 +245,10 @@ static int write_part(void* mem, part_data_t* d)
        memcpy(mem + sizeof(part_t), addr, d->stats.st_size);
        munmap(addr, d->stats.st_size);
 
-       memset(p->name, 0, sizeof(p->name));
-       strncpy(p->magic, MAGIC_PART, MAGIC_LENGTH);
-       strncpy(p->name, d->partition_name, sizeof(p->name));
+       memset(p->name, 0, PART_NAME_LENGTH);
+       FW_MEMCPY_STR(p->magic, MAGIC_PART);
+       FW_MEMCPY_STR(p->name, d->partition_name);
+
        p->index = htonl(d->partition_index);
        p->data_size = htonl(d->stats.st_size);
        p->part_size = htonl(d->partition_length);
@@ -159,16 +268,18 @@ static void usage(const char* progname)
              "Usage: %s [options]\n"
             "\t-v <version string>\t - firmware version information, default: %s\n"
             "\t-o <output file>\t - firmware output file, default: %s\n"
+            "\t-m <magic>\t - firmware magic, default: %s\n"
             "\t-k <kernel file>\t\t - kernel file\n"
             "\t-r <rootfs file>\t\t - rootfs file\n"
-            "\t-B <board name>\t\t - choose firmware layout for specified board (XS2, XS5, RS)\n"
+            "\t-B <board name>\t\t - choose firmware layout for specified board (XS2, XS5, RS, XM)\n"
             "\t-h\t\t\t - this help\n", VERSION,
-            progname, DEFAULT_VERSION, DEFAULT_OUTPUT_FILE);
+            progname, DEFAULT_VERSION, DEFAULT_OUTPUT_FILE, MAGIC_HEADER);
 }
 
 static void print_image_info(const image_info_t* im)
 {
-       int i = 0;
+       unsigned int i = 0;
+
        INFO("Firmware version: '%s'\n"
             "Output file: '%s'\n"
             "Part count: %u\n",
@@ -178,15 +289,13 @@ static void print_image_info(const image_info_t* im)
        for (i = 0; i < im->part_count; ++i)
        {
                const part_data_t* d = &im->parts[i];
-               INFO(" %10s: %8ld bytes (free: %8ld)\n",
+               INFO(" %10s: %8" PRId64 " bytes (free: %8" PRId64 ")\n",
                     d->partition_name,
                     d->stats.st_size,
                     d->partition_length - d->stats.st_size);
        }
 }
 
-
-
 static u_int32_t filelength(const char* file)
 {
        FILE *p;
@@ -202,32 +311,30 @@ static u_int32_t filelength(const char* file)
        return (ret);
 }
 
-static int create_image_layout(const char* kernelfile, const char* rootfsfile, char* board_name, image_info_t* im)
+static int create_image_layout(const char* kernelfile, const char* rootfsfile, image_info_t* im)
 {
+       uint32_t rootfs_len = 0;
        part_data_t* kernel = &im->parts[0];
        part_data_t* rootfs = &im->parts[1];
 
-       fw_layout_t* p;
+       fw_layout_t* p = &im->fwinfo->fw_layout;
 
-       p = &fw_layout_data[0];
-       while ((strlen(p->name) != 0) && (strncmp(p->name, board_name, sizeof(board_name)) != 0))
-               p++;
-       if (p->name == NULL) {
-               printf("BUG! Unable to find default fw layout!\n");
-               exit(-1);
-       }
-
-       printf("board = %s\n", p->name);
+       printf("board = %s\n", im->fwinfo->name);
        strcpy(kernel->partition_name, "kernel");
        kernel->partition_index = 1;
        kernel->partition_baseaddr = p->kern_start;
-       if ( (kernel->partition_length = filelength(kernelfile)) < 0) return (-1);
+       if ( (kernel->partition_length = filelength(kernelfile)) == (u_int32_t)-1) return (-1);
        kernel->partition_memaddr = p->kern_entry;
        kernel->partition_entryaddr = p->kern_entry;
        strncpy(kernel->filename, kernelfile, sizeof(kernel->filename));
 
-       if (filelength(rootfsfile) + kernel->partition_length > p->firmware_max_length)
+       rootfs_len = filelength(rootfsfile);
+       if (rootfs_len + kernel->partition_length > p->firmware_max_length) {
+               ERROR("File '%s' too big (0x%08X) - max size: 0x%08X (exceeds %u bytes)\n",
+                      rootfsfile, rootfs_len, p->firmware_max_length,
+                      (rootfs_len + kernel->partition_length) - p->firmware_max_length);
                return (-2);
+       }
 
        strcpy(rootfs->partition_name, "rootfs");
        rootfs->partition_index = 2;
@@ -237,8 +344,8 @@ static int create_image_layout(const char* kernelfile, const char* rootfsfile, c
        rootfs->partition_entryaddr = 0x00000000;
        strncpy(rootfs->filename, rootfsfile, sizeof(rootfs->filename));
 
-printf("kernel: %d 0x%08x\n", kernel->partition_length, kernel->partition_baseaddr);
-printf("root: %d 0x%08x\n", rootfs->partition_length, rootfs->partition_baseaddr);
+       printf("kernel: %d 0x%08x\n", kernel->partition_length, kernel->partition_baseaddr);
+       printf("root: %d 0x%08x\n", rootfs->partition_length, rootfs->partition_baseaddr);
        im->part_count = 2;
 
        return 0;
@@ -250,7 +357,7 @@ printf("root: %d 0x%08x\n", rootfs->partition_length, rootfs->partition_baseaddr
  */
 static int validate_image_layout(image_info_t* im)
 {
-       int i;
+       unsigned int i;
 
        if (im->part_count == 0 || im->part_count > MAX_SECTIONS)
        {
@@ -281,7 +388,7 @@ static int validate_image_layout(image_info_t* im)
                        return -3;
                }
                if (d->stats.st_size > d->partition_length) {
-                       ERROR("File '%s' too big (%d) - max size: 0x%08X (exceeds %lu bytes)\n",
+                       ERROR("File '%s' too big (%d) - max size: 0x%08X (exceeds %" PRId64 " bytes)\n",
                                        d->filename, i, d->partition_length,
                                        d->stats.st_size - d->partition_length);
                        return -4;
@@ -297,10 +404,15 @@ static int build_image(image_info_t* im)
        char* ptr;
        u_int32_t mem_size;
        FILE* f;
-       int i;
+       unsigned int i;
 
        // build in-memory buffer
-       mem_size = sizeof(header_t) + sizeof(signature_t);
+       mem_size = sizeof(header_t);
+       if(im->fwinfo->sign) {
+               mem_size += sizeof(signature_rsa_t);
+       } else {
+               mem_size += sizeof(signature_t);
+       }
        for (i = 0; i < im->part_count; ++i)
        {
                part_data_t* d = &im->parts[i];
@@ -315,7 +427,7 @@ static int build_image(image_info_t* im)
        }
 
        // write header
-       write_header(mem, im->version);
+       write_header(mem, im->magic, im->version);
        ptr = mem + sizeof(header_t);
        // write all parts
        for (i = 0; i < im->part_count; ++i)
@@ -329,12 +441,17 @@ static int build_image(image_info_t* im)
                ptr += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t);
        }
        // write signature
-       write_signature(mem, mem_size - sizeof(signature_t));
+       if(im->fwinfo->sign) {
+               write_signature_rsa(mem, mem_size - sizeof(signature_rsa_t));
+       } else {
+               write_signature(mem, mem_size - sizeof(signature_t));
+       }
 
        // write in-memory buffer into file
        if ((f = fopen(im->outputfile, "w")) == NULL)
        {
                ERROR("Can not create output file: '%s'\n", im->outputfile);
+               free(mem);
                return -10;
        }
 
@@ -342,6 +459,8 @@ static int build_image(image_info_t* im)
        {
                ERROR("Could not write %d bytes into file: '%s'\n",
                                mem_size, im->outputfile);
+               free(mem);
+               fclose(f);
                return -11;
        }
 
@@ -358,6 +477,7 @@ int main(int argc, char* argv[])
        char board_name[PATH_MAX];
        int o, rc;
        image_info_t im;
+       struct fw_info *fwinfo;
 
        memset(&im, 0, sizeof(im));
        memset(kernelfile, 0, sizeof(kernelfile));
@@ -366,32 +486,37 @@ int main(int argc, char* argv[])
 
        strcpy(im.outputfile, DEFAULT_OUTPUT_FILE);
        strcpy(im.version, DEFAULT_VERSION);
+       strncpy(im.magic, MAGIC_HEADER, sizeof(im.magic));
 
        while ((o = getopt(argc, argv, OPTIONS)) != -1)
        {
                switch (o) {
                case 'v':
                        if (optarg)
-                               strncpy(im.version, optarg, sizeof(im.version));
+                               strncpy(im.version, optarg, sizeof(im.version) - 1);
                        break;
                case 'o':
                        if (optarg)
-                               strncpy(im.outputfile, optarg, sizeof(im.outputfile));
+                               strncpy(im.outputfile, optarg, sizeof(im.outputfile) - 1);
+                       break;
+               case 'm':
+                       if (optarg)
+                               strncpy(im.magic, optarg, sizeof(im.magic) - 1);
                        break;
                case 'h':
                        usage(argv[0]);
                        return -1;
                case 'k':
                        if (optarg)
-                               strncpy(kernelfile, optarg, sizeof(kernelfile));
+                               strncpy(kernelfile, optarg, sizeof(kernelfile) - 1);
                        break;
                case 'r':
                        if (optarg)
-                               strncpy(rootfsfile, optarg, sizeof(rootfsfile));
+                               strncpy(rootfsfile, optarg, sizeof(rootfsfile) - 1);
                        break;
                case 'B':
                        if (optarg)
-                               strncpy(board_name, optarg, sizeof(board_name));
+                               strncpy(board_name, optarg, sizeof(board_name) - 1);
                        break;
                }
        }
@@ -412,7 +537,15 @@ int main(int argc, char* argv[])
                return -2;
        }
 
-       if ((rc = create_image_layout(kernelfile, rootfsfile, board_name, &im)) != 0)
+       if ((fwinfo = get_fwinfo(board_name)) == NULL) {
+               ERROR("Invalid baord name '%s'\n", board_name);
+               usage(argv[0]);
+               return -2;
+       }
+
+       im.fwinfo = fwinfo;
+
+       if ((rc = create_image_layout(kernelfile, rootfsfile, &im)) != 0)
        {
                ERROR("Failed creating firmware layout description - error code: %d\n", rc);
                return -3;