firmware-utils: add JBOOT bootloader image support
Tested on D-Link DWR-116. Based on mktplinkfw. Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
This commit is contained in:
parent
d11aa1d4af
commit
84c1b786b9
4 changed files with 921 additions and 0 deletions
|
@ -81,6 +81,7 @@ define Host/Compile
|
|||
$(call cc,mkbuffaloimg, -Wall)
|
||||
$(call cc,zyimage, -Wall)
|
||||
$(call cc,mkdhpimg buffalo-lib, -Wall)
|
||||
$(call cc,mkdlinkfw mkdlinkfw-lib, -lz -Wall)
|
||||
endef
|
||||
|
||||
define Host/Install
|
||||
|
|
172
tools/firmware-utils/src/mkdlinkfw-lib.c
Normal file
172
tools/firmware-utils/src/mkdlinkfw-lib.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* mkdlinkfw
|
||||
*
|
||||
* Copyright (C) 2018 Paweł Dembicki <paweldembicki@gmail.com>
|
||||
*
|
||||
* This tool is based on mktplinkfw.
|
||||
* Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
|
||||
* Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h> /* for unlink() */
|
||||
#include <libgen.h>
|
||||
#include <getopt.h> /* for getopt() */
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <zlib.h> /*for crc32 */
|
||||
|
||||
#include "mkdlinkfw-lib.h"
|
||||
|
||||
extern char *progname;
|
||||
|
||||
static unsigned char jffs2_eof_mark[4] = { 0xde, 0xad, 0xc0, 0xde };
|
||||
|
||||
uint32_t jboot_timestamp(void)
|
||||
{
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
return (((uint32_t) rawtime) - TIMESTAMP_MAGIC) >> 2;
|
||||
}
|
||||
|
||||
uint16_t jboot_checksum(uint16_t start_val, uint16_t *data, int size)
|
||||
{
|
||||
uint32_t counter = start_val;
|
||||
uint16_t *ptr = data;
|
||||
|
||||
while (size > 1) {
|
||||
counter += *ptr;
|
||||
++ptr;
|
||||
while (counter >> 16)
|
||||
counter = (uint16_t) counter + (counter >> 16);
|
||||
size -= 2;
|
||||
}
|
||||
if (size > 0) {
|
||||
counter += *(uint8_t *) ptr;
|
||||
counter -= 0xFF;
|
||||
}
|
||||
while (counter >> 16)
|
||||
counter = (uint16_t) counter + (counter >> 16);
|
||||
return counter;
|
||||
}
|
||||
|
||||
int get_file_stat(struct file_info *fdata)
|
||||
{
|
||||
struct stat st;
|
||||
int res;
|
||||
|
||||
if (fdata->file_name == NULL)
|
||||
return 0;
|
||||
|
||||
res = stat(fdata->file_name, &st);
|
||||
if (res) {
|
||||
ERRS("stat failed on %s", fdata->file_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
fdata->file_size = st.st_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_to_buf(const struct file_info *fdata, char *buf)
|
||||
{
|
||||
FILE *f;
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
f = fopen(fdata->file_name, "r");
|
||||
if (f == NULL) {
|
||||
ERRS("could not open \"%s\" for reading", fdata->file_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
fread(buf, fdata->file_size, 1, f);
|
||||
if (errno != 0) {
|
||||
ERRS("unable to read from file \"%s\"", fdata->file_name);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
out_close:
|
||||
fclose(f);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pad_jffs2(char *buf, int currlen, int maxlen)
|
||||
{
|
||||
int len;
|
||||
uint32_t pad_mask;
|
||||
|
||||
len = currlen;
|
||||
pad_mask = (4 * 1024) | (64 * 1024); /* EOF at 4KB and at 64KB */
|
||||
while ((len < maxlen) && (pad_mask != 0)) {
|
||||
uint32_t mask;
|
||||
int i;
|
||||
|
||||
for (i = 10; i < 32; i++) {
|
||||
mask = 1 << i;
|
||||
if (pad_mask & mask)
|
||||
break;
|
||||
}
|
||||
|
||||
len = ALIGN(len, mask);
|
||||
|
||||
for (i = 10; i < 32; i++) {
|
||||
mask = 1 << i;
|
||||
if ((len & (mask - 1)) == 0)
|
||||
pad_mask &= ~mask;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(jffs2_eof_mark); i++)
|
||||
buf[len + i] = jffs2_eof_mark[i];
|
||||
|
||||
len += sizeof(jffs2_eof_mark);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int write_fw(const char *ofname, const char *data, int len)
|
||||
{
|
||||
FILE *f;
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
f = fopen(ofname, "w");
|
||||
if (f == NULL) {
|
||||
ERRS("could not open \"%s\" for writing", ofname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
fwrite(data, len, 1, f);
|
||||
if (errno) {
|
||||
ERRS("unable to write output file");
|
||||
goto out_flush;
|
||||
}
|
||||
|
||||
DBG("firmware file \"%s\" completed", ofname);
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
out_flush:
|
||||
fflush(f);
|
||||
fclose(f);
|
||||
if (ret != EXIT_SUCCESS)
|
||||
unlink(ofname);
|
||||
out:
|
||||
return ret;
|
||||
}
|
83
tools/firmware-utils/src/mkdlinkfw-lib.h
Normal file
83
tools/firmware-utils/src/mkdlinkfw-lib.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* mkdlinkfw
|
||||
*
|
||||
* Copyright (C) 2018 Paweł Dembicki <paweldembicki@gmail.com>
|
||||
*
|
||||
* This tool is based on mktplinkfw.
|
||||
* Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
|
||||
* Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef mkdlinkfw_lib_h
|
||||
#define mkdlinkfw_lib_h
|
||||
|
||||
#define AUH_MAGIC "DLK"
|
||||
#define AUH_SIZE 80
|
||||
#define AUH_LVPS 0x01
|
||||
#define AUH_HDR_ID 0x4842
|
||||
#define AUH_HDR_VER 0x02
|
||||
#define AUH_SEC_ID 0x04
|
||||
#define AUH_INFO_TYPE 0x04
|
||||
|
||||
#define STAG_SIZE 16
|
||||
#define STAG_ID 0x04
|
||||
#define STAG_MAGIC 0x2B24
|
||||
#define STAG_CMARK_FACTORY 0xFF
|
||||
|
||||
#define SCH2_SIZE 40
|
||||
#define SCH2_MAGIC 0x2124
|
||||
#define SCH2_VER 0x02
|
||||
|
||||
#define FLAT 0
|
||||
#define JZ 1
|
||||
#define GZIP 2
|
||||
#define LZMA 3
|
||||
|
||||
#define RAM_ENTRY_ADDR 0x80000000
|
||||
#define RAM_LOAD_ADDR 0x80000000
|
||||
#define JBOOT_SIZE 0x10000
|
||||
|
||||
#define ALL_HEADERS_SIZE (AUH_SIZE + STAG_SIZE + SCH2_SIZE)
|
||||
#define MAX_HEADER_COUNTER 10
|
||||
#define TIMESTAMP_MAGIC 0x35016f00L
|
||||
|
||||
#define FACTORY 0
|
||||
#define SYSUPGRADE 1
|
||||
|
||||
#define ALIGN(x, a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
|
||||
|
||||
#define ERR(fmt, ...) do { \
|
||||
fflush(0); \
|
||||
fprintf(stderr, "[%s] *** error: " fmt "\n", \
|
||||
progname, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define ERRS(fmt, ...) do { \
|
||||
int save = errno; \
|
||||
fflush(0); \
|
||||
fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \
|
||||
progname, ## __VA_ARGS__, strerror(save)); \
|
||||
} while (0)
|
||||
|
||||
#define DBG(fmt, ...) do { \
|
||||
fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
struct file_info {
|
||||
char *file_name; /* name of the file */
|
||||
uint32_t file_size; /* length of the file */
|
||||
};
|
||||
|
||||
uint32_t jboot_timestamp(void);
|
||||
uint16_t jboot_checksum(uint16_t start_val, uint16_t *data, int size);
|
||||
int get_file_stat(struct file_info *fdata);
|
||||
int read_to_buf(const struct file_info *fdata, char *buf);
|
||||
int pad_jffs2(char *buf, int currlen, int maxlen);
|
||||
int write_fw(const char *ofname, const char *data, int len);
|
||||
|
||||
#endif /* mkdlinkfw_lib_h */
|
665
tools/firmware-utils/src/mkdlinkfw.c
Normal file
665
tools/firmware-utils/src/mkdlinkfw.c
Normal file
|
@ -0,0 +1,665 @@
|
|||
/*
|
||||
* mkdlinkfw
|
||||
*
|
||||
* Copyright (C) 2018 Paweł Dembicki <paweldembicki@gmail.com>
|
||||
*
|
||||
* This tool is based on mktplinkfw.
|
||||
* Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
|
||||
* Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h> /* for unlink() */
|
||||
#include <libgen.h>
|
||||
#include <getopt.h> /* for getopt() */
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <zlib.h> /*for crc32 */
|
||||
|
||||
#include "mkdlinkfw-lib.h"
|
||||
|
||||
/* ARM update header 2.0
|
||||
* used only in factory images to erase and flash selected area
|
||||
*/
|
||||
struct auh_header {
|
||||
uint8_t rom_id[12]; /* 12-bit rom-id unique per router type */
|
||||
uint16_t derange; /* used for scramble header */
|
||||
uint16_t image_checksum; /* jboot_checksum of flashed data */
|
||||
|
||||
uint32_t space1; /* zeros */
|
||||
uint32_t space2; /* zeros */
|
||||
uint16_t space3; /* zerosu */
|
||||
uint8_t lpvs; /* must be 0x01 */
|
||||
uint8_t mbz; /* bust be 0 */
|
||||
uint32_t time_stamp; /* timestamp calculated in jboot way */
|
||||
|
||||
uint32_t erase_start; /* erase start address */
|
||||
uint32_t erase_length; /* erase length address */
|
||||
uint32_t data_offset; /* data start address */
|
||||
uint32_t data_length; /* data length address */
|
||||
|
||||
uint32_t space4; /* zeros */
|
||||
uint32_t space5; /* zeros */
|
||||
uint32_t space6; /* zeros */
|
||||
uint32_t space7; /* zeros */
|
||||
|
||||
uint16_t header_id; /* magic 0x4842 */
|
||||
uint16_t header_version; /* 0x02 for 2.0 */
|
||||
uint16_t space8; /* zeros */
|
||||
uint8_t section_id; /* section id */
|
||||
uint8_t image_info_type; /* (?) 0x04 in factory images */
|
||||
uint32_t image_info_offset; /* (?) zeros in factory images */
|
||||
uint16_t family_member; /* unique per router type */
|
||||
uint16_t header_checksum; /* negated jboot_checksum of header data */
|
||||
};
|
||||
|
||||
struct stag_header { /* used only of sch2 wrapped kernel data */
|
||||
uint8_t cmark; /* in factory 0xFF ,in sysuograde must be the same as id */
|
||||
uint8_t id; /* 0x04 */
|
||||
uint16_t magic; /* magic 0x2B24 */
|
||||
uint32_t time_stamp; /* timestamp calculated in jboot way */
|
||||
uint32_t image_length; /* lentgh of kernel + sch2 header */
|
||||
uint16_t image_checksum; /* negated jboot_checksum of sch2 + kernel */
|
||||
uint16_t tag_checksum; /* negated jboot_checksum of stag header data */
|
||||
};
|
||||
|
||||
struct sch2_header { /* used only in kernel partitions */
|
||||
uint16_t magic; /* magic 0x2124 */
|
||||
uint8_t cp_type; /* 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma */
|
||||
uint8_t version; /* 0x02 for sch2 */
|
||||
uint32_t ram_addr; /* ram entry address */
|
||||
uint32_t image_len; /* kernel image length */
|
||||
uint32_t image_crc32; /* kernel image crc */
|
||||
uint32_t start_addr; /* ram start address */
|
||||
uint32_t rootfs_addr; /* rootfs flash address */
|
||||
uint32_t rootfs_len; /* rootfls length */
|
||||
uint32_t rootfs_crc32; /* rootfs crc32 */
|
||||
uint32_t header_crc32; /* sch2 header crc32, durring calculation this area is replaced by zero */
|
||||
uint16_t header_length; /* sch2 header length: 0x28 */
|
||||
uint16_t cmd_line_length; /* cmd line length, known zeros */
|
||||
};
|
||||
|
||||
/* globals */
|
||||
static struct file_info inspect_info;
|
||||
struct file_info kernel_info;
|
||||
struct file_info rootfs_info;
|
||||
struct file_info image_info;
|
||||
|
||||
char *ofname;
|
||||
char *progname;
|
||||
uint32_t firmware_size;
|
||||
uint16_t family_member;
|
||||
char *rom_id[12] = { 0 };
|
||||
|
||||
char image_type;
|
||||
int add_jffs2_eof;
|
||||
|
||||
static void usage(int status)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS...]\n", progname);
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -i <file> inspect given firmware file <file>\n"
|
||||
" -f set family member id (hexval prefixed with 0x)\n"
|
||||
" -F <file> read image and convert it to FACTORY\n"
|
||||
" -k <file> read kernel image from the file <file>\n"
|
||||
" -r <file> read rootfs image from the file <file>\n"
|
||||
" -o <file> write output to the file <file>\n"
|
||||
" -s <size> set firmware partition size\n"
|
||||
" -m <version> set rom id to <version> (12-bit string val: \"DLK*********\")\n"
|
||||
" -h show this screen\n");
|
||||
|
||||
exit(status);
|
||||
}
|
||||
|
||||
void print_auh_header(struct auh_header *printed_header)
|
||||
{
|
||||
printf("\trom_id: %s\n"
|
||||
"\tderange: 0x%04X\n"
|
||||
"\timage_checksum: 0x%04X\n"
|
||||
"\tspace1: 0x%08X\n"
|
||||
"\tspace2: 0x%08X\n"
|
||||
"\tspace3: 0x%04X\n"
|
||||
"\tlpvs: 0x%02X\n"
|
||||
"\tmbz: 0x%02X\n"
|
||||
"\ttime_stamp: 0x%08X\n"
|
||||
"\terase_start: 0x%08X\n"
|
||||
"\terase_length: 0x%08X\n"
|
||||
"\tdata_offset: 0x%08X\n"
|
||||
"\tdata_length: 0x%08X\n"
|
||||
"\tspace4: 0x%08X\n"
|
||||
"\tspace5: 0x%08X\n"
|
||||
"\tspace6: 0x%08X\n"
|
||||
"\tspace7: 0x%08X\n"
|
||||
"\theader_id: 0x%04X\n"
|
||||
"\theader_version: 0x%02X\n"
|
||||
"\tspace8: 0x%04X\n"
|
||||
"\tsection_id: 0x%02X\n"
|
||||
"\timage_info_type: 0x%02X\n"
|
||||
"\timage_info_offset 0x%08X\n"
|
||||
"\tfamily_member: 0x%04X\n"
|
||||
"\theader_checksum: 0x%04X\n",
|
||||
printed_header->rom_id,
|
||||
printed_header->derange,
|
||||
printed_header->image_checksum,
|
||||
printed_header->space1,
|
||||
printed_header->space2,
|
||||
printed_header->space3,
|
||||
printed_header->lpvs,
|
||||
printed_header->mbz,
|
||||
printed_header->time_stamp,
|
||||
printed_header->erase_start,
|
||||
printed_header->erase_length,
|
||||
printed_header->data_offset,
|
||||
printed_header->data_length,
|
||||
printed_header->space4,
|
||||
printed_header->space5,
|
||||
printed_header->space6,
|
||||
printed_header->space7,
|
||||
printed_header->header_id,
|
||||
printed_header->header_version,
|
||||
printed_header->space8,
|
||||
printed_header->section_id,
|
||||
printed_header->image_info_type,
|
||||
printed_header->image_info_offset,
|
||||
printed_header->family_member, printed_header->header_checksum);
|
||||
}
|
||||
|
||||
void print_stag_header(struct stag_header *printed_header)
|
||||
{
|
||||
printf("\tcmark: 0x%02X\n"
|
||||
"\tid: 0x%02X\n"
|
||||
"\tmagic: 0x%04X\n"
|
||||
"\ttime_stamp: 0x%08X\n"
|
||||
"\timage_length: 0x%04X\n"
|
||||
"\timage_checksum: 0x%04X\n"
|
||||
"\ttag_checksum: 0x%04X\n",
|
||||
printed_header->cmark,
|
||||
printed_header->id,
|
||||
printed_header->magic,
|
||||
printed_header->time_stamp,
|
||||
printed_header->image_length,
|
||||
printed_header->image_checksum, printed_header->tag_checksum);
|
||||
}
|
||||
|
||||
void print_sch2_header(struct sch2_header *printed_header)
|
||||
{
|
||||
printf("\tmagic: 0x%04X\n"
|
||||
"\tcp_type: 0x%02X\n"
|
||||
"\tversion: 0x%02X\n"
|
||||
"\tram_addr: 0x%08X\n"
|
||||
"\timage_len: 0x%08X\n"
|
||||
"\timage_crc32: 0x%08X\n"
|
||||
"\tstart_addr: 0x%08X\n"
|
||||
"\trootfs_addr: 0x%08X\n"
|
||||
"\trootfs_len: 0x%08X\n"
|
||||
"\trootfs_crc32: 0x%08X\n"
|
||||
"\theader_crc32: 0x%08X\n"
|
||||
"\theader_length: 0x%04X\n"
|
||||
"\tcmd_line_length: 0x%04X\n",
|
||||
printed_header->magic,
|
||||
printed_header->cp_type,
|
||||
printed_header->version,
|
||||
printed_header->ram_addr,
|
||||
printed_header->image_len,
|
||||
printed_header->image_crc32,
|
||||
printed_header->start_addr,
|
||||
printed_header->rootfs_addr,
|
||||
printed_header->rootfs_len,
|
||||
printed_header->rootfs_crc32,
|
||||
printed_header->header_crc32,
|
||||
printed_header->header_length, printed_header->cmd_line_length);
|
||||
}
|
||||
|
||||
static int find_auh_headers(char *buf)
|
||||
{
|
||||
char *tmp_buf = buf;
|
||||
struct auh_header *tmp_header[MAX_HEADER_COUNTER];
|
||||
int header_counter = 0;
|
||||
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
while (tmp_buf - buf <= inspect_info.file_size - AUH_SIZE) {
|
||||
if (!memcmp(tmp_buf, AUH_MAGIC, 3)) {
|
||||
if (((struct auh_header *)tmp_buf)->header_checksum ==
|
||||
(uint16_t) ~jboot_checksum(0, (uint16_t *) tmp_buf,
|
||||
AUH_SIZE - 2)) {
|
||||
uint16_t checksum = 0;
|
||||
printf("Find proper AUH header at: 0x%lX!\n",
|
||||
tmp_buf - buf);
|
||||
tmp_header[header_counter] =
|
||||
(struct auh_header *)tmp_buf;
|
||||
checksum =
|
||||
jboot_checksum(0, (uint16_t *) ((char *)
|
||||
tmp_header
|
||||
[header_counter]
|
||||
+ AUH_SIZE),
|
||||
tmp_header
|
||||
[header_counter]->data_length);
|
||||
if (tmp_header[header_counter]->image_checksum
|
||||
== checksum)
|
||||
printf("Image checksum ok.\n");
|
||||
else
|
||||
ERR("Image checksum incorrect! Stored: 0x%X Calculated: 0x%X\n", tmp_header[header_counter]->image_checksum, checksum);
|
||||
header_counter++;
|
||||
if (header_counter > MAX_HEADER_COUNTER)
|
||||
break;
|
||||
}
|
||||
}
|
||||
tmp_buf++;
|
||||
}
|
||||
|
||||
if (header_counter == 0)
|
||||
ERR("Can't find proper AUH header!\n");
|
||||
else if (header_counter > MAX_HEADER_COUNTER)
|
||||
ERR("To many AUH headers!\n");
|
||||
else {
|
||||
for (int i = 0; i < header_counter; i++) {
|
||||
printf("AUH %d:\n", i);
|
||||
print_auh_header(tmp_header[i]);
|
||||
}
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_stag_header(char *buf, struct stag_header *header)
|
||||
{
|
||||
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
uint8_t cmark_tmp = header->cmark;
|
||||
header->cmark = header->id;
|
||||
|
||||
if (header->tag_checksum ==
|
||||
(uint16_t) ~jboot_checksum(0, (uint16_t *) header,
|
||||
STAG_SIZE - 2)) {
|
||||
uint16_t checksum = 0;
|
||||
printf("Find proper STAG header at: 0x%lX!\n",
|
||||
(char *)header - buf);
|
||||
checksum =
|
||||
jboot_checksum(0, (uint16_t *) ((char *)header + STAG_SIZE),
|
||||
header->image_length);
|
||||
if (header->image_checksum == checksum) {
|
||||
printf("Image checksum ok.\n");
|
||||
header->cmark = cmark_tmp;
|
||||
print_stag_header(header);
|
||||
ret = EXIT_SUCCESS;
|
||||
} else
|
||||
ERR("Image checksum incorrect! Stored: 0x%X Calculated: 0x%X\n", header->image_checksum, checksum);
|
||||
} else
|
||||
ERR("STAG header checksum incorrect!");
|
||||
|
||||
header->cmark = cmark_tmp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_sch2_header(char *buf, struct sch2_header *header)
|
||||
{
|
||||
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
uint32_t crc32_tmp = header->header_crc32;
|
||||
header->header_crc32 = 0;
|
||||
|
||||
if (crc32_tmp == crc32(0, (uint8_t *) header, header->header_length)) {
|
||||
uint32_t crc32_val;
|
||||
printf("Find proper SCH2 header at: 0x%lX!\n",
|
||||
(char *)header - buf);
|
||||
|
||||
crc32_val =
|
||||
crc32(0, (uint8_t *) header + header->header_length,
|
||||
header->image_len);
|
||||
if (header->image_crc32 == crc32_val) {
|
||||
printf("Kernel checksum ok.\n");
|
||||
|
||||
header->header_crc32 = crc32_tmp;
|
||||
print_sch2_header(header);
|
||||
ret = EXIT_SUCCESS;
|
||||
} else
|
||||
ERR("Kernel checksum incorrect! Stored: 0x%X Calculated: 0x%X\n", header->image_crc32, crc32_val);
|
||||
|
||||
} else
|
||||
ERR("SCH2 header checksum incorrect!");
|
||||
|
||||
header->header_crc32 = crc32_tmp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int inspect_fw(void)
|
||||
{
|
||||
char *buf;
|
||||
struct stag_header *stag_header_kernel;
|
||||
struct sch2_header *sch2_header_kernel;
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
buf = malloc(inspect_info.file_size);
|
||||
if (!buf) {
|
||||
ERR("no memory for buffer!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = read_to_buf(&inspect_info, buf);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
ret = find_auh_headers(buf);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
stag_header_kernel = (struct stag_header *)(buf + AUH_SIZE);
|
||||
|
||||
ret = check_stag_header(buf, stag_header_kernel);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
sch2_header_kernel = (struct sch2_header *)(buf + AUH_SIZE + STAG_SIZE);
|
||||
|
||||
ret = check_sch2_header(buf, sch2_header_kernel);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
out_free_buf:
|
||||
free(buf);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_options(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (inspect_info.file_name) {
|
||||
ret = get_file_stat(&inspect_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fill_sch2(struct sch2_header *header, char *kernel_ptr, char *rootfs_ptr)
|
||||
{
|
||||
|
||||
header->magic = SCH2_MAGIC;
|
||||
header->cp_type = LZMA;
|
||||
header->version = SCH2_VER;
|
||||
header->ram_addr = RAM_LOAD_ADDR;
|
||||
header->image_len = kernel_info.file_size;
|
||||
header->image_crc32 = crc32(0, (uint8_t *) kernel_ptr, kernel_info.file_size);
|
||||
header->start_addr = RAM_ENTRY_ADDR;
|
||||
header->rootfs_addr =
|
||||
JBOOT_SIZE + STAG_SIZE + SCH2_SIZE + kernel_info.file_size;
|
||||
header->rootfs_len = rootfs_info.file_size;
|
||||
header->rootfs_crc32 = crc32(0, (uint8_t *) rootfs_ptr, rootfs_info.file_size);
|
||||
header->header_crc32 = 0;
|
||||
header->header_length = SCH2_SIZE;
|
||||
header->cmd_line_length = 0;
|
||||
|
||||
header->header_crc32 = crc32(0, (uint8_t *) header, header->header_length);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int fill_stag(struct stag_header *header, uint32_t length)
|
||||
{
|
||||
header->cmark = STAG_ID;
|
||||
header->id = STAG_ID;
|
||||
header->magic = STAG_MAGIC;
|
||||
header->time_stamp = jboot_timestamp();
|
||||
header->image_length = length + SCH2_SIZE;
|
||||
header->image_checksum =
|
||||
jboot_checksum(0, (uint16_t *) ((char *)header + STAG_SIZE),
|
||||
header->image_length);
|
||||
header->tag_checksum =
|
||||
~jboot_checksum(0, (uint16_t *) header, STAG_SIZE - 2);
|
||||
|
||||
if (image_type == FACTORY)
|
||||
header->cmark = STAG_CMARK_FACTORY;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
};
|
||||
|
||||
int fill_auh(struct auh_header *header, uint32_t length)
|
||||
{
|
||||
memcpy(header->rom_id, rom_id, 12);
|
||||
header->derange = 0;
|
||||
header->image_checksum =
|
||||
jboot_checksum(0, (uint16_t *) ((char *)header + AUH_SIZE), length);
|
||||
header->space1 = 0;
|
||||
header->space2 = 0;
|
||||
header->space3 = 0;
|
||||
header->lpvs = AUH_LVPS;
|
||||
header->mbz = 0;
|
||||
header->time_stamp = jboot_timestamp();
|
||||
header->erase_start = JBOOT_SIZE;
|
||||
header->erase_length = firmware_size;
|
||||
header->data_offset = JBOOT_SIZE;
|
||||
header->data_length = length;
|
||||
header->space4 = 0;
|
||||
header->space5 = 0;
|
||||
header->space6 = 0;
|
||||
header->space7 = 0;
|
||||
header->header_id = AUH_HDR_ID;
|
||||
header->header_version = AUH_HDR_VER;
|
||||
header->space8 = 0;
|
||||
header->section_id = AUH_SEC_ID;
|
||||
header->image_info_type = AUH_INFO_TYPE;
|
||||
header->image_info_offset = 0;
|
||||
header->family_member = family_member;
|
||||
header->header_checksum =
|
||||
~jboot_checksum(0, (uint16_t *) header, AUH_SIZE - 2);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int build_fw(void)
|
||||
{
|
||||
char *buf;
|
||||
char *kernel_ptr;
|
||||
char *rootfs_ptr;
|
||||
int ret = EXIT_FAILURE;
|
||||
int writelen;
|
||||
|
||||
struct stag_header *stag_header_kernel;
|
||||
struct sch2_header *sch2_header_kernel;
|
||||
|
||||
if (!kernel_info.file_name | !rootfs_info.file_name)
|
||||
goto out;
|
||||
|
||||
ret = get_file_stat(&kernel_info);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = get_file_stat(&rootfs_info);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
buf = malloc(firmware_size);
|
||||
if (!buf) {
|
||||
ERR("no memory for buffer\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rootfs_info.file_size + kernel_info.file_size + ALL_HEADERS_SIZE >
|
||||
firmware_size) {
|
||||
ERR("data is bigger than firmware_size!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(buf, 0xff, firmware_size);
|
||||
|
||||
stag_header_kernel = (struct stag_header *)buf;
|
||||
|
||||
sch2_header_kernel =
|
||||
(struct sch2_header *)((char *)stag_header_kernel + STAG_SIZE);
|
||||
kernel_ptr = (char *)sch2_header_kernel + SCH2_SIZE;
|
||||
|
||||
ret = read_to_buf(&kernel_info, kernel_ptr);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
rootfs_ptr = kernel_ptr + kernel_info.file_size;
|
||||
|
||||
ret = read_to_buf(&rootfs_info, rootfs_ptr);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
writelen = rootfs_ptr + rootfs_info.file_size - buf;
|
||||
|
||||
fill_sch2(sch2_header_kernel, kernel_ptr, rootfs_ptr);
|
||||
fill_stag(stag_header_kernel, kernel_info.file_size);
|
||||
|
||||
ret = write_fw(ofname, buf, writelen);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
out_free_buf:
|
||||
free(buf);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wrap_fw(void)
|
||||
{
|
||||
char *buf;
|
||||
char *image_ptr;
|
||||
int ret = EXIT_FAILURE;
|
||||
int writelen;
|
||||
|
||||
struct auh_header *auh_header_kernel;
|
||||
|
||||
if (!image_info.file_name)
|
||||
goto out;
|
||||
|
||||
ret = get_file_stat(&image_info);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
buf = malloc(firmware_size);
|
||||
if (!buf) {
|
||||
ERR("no memory for buffer\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (image_info.file_size + AUH_SIZE >
|
||||
firmware_size) {
|
||||
ERR("data is bigger than firmware_size!\n");
|
||||
goto out;
|
||||
}
|
||||
if (!family_member) {
|
||||
ERR("No family_member!\n");
|
||||
goto out;
|
||||
}
|
||||
if (!(rom_id[0])) {
|
||||
ERR("No rom_id!\n");
|
||||
goto out;
|
||||
}
|
||||
memset(buf, 0xff, firmware_size);
|
||||
|
||||
image_ptr = (char *)(buf + AUH_SIZE);
|
||||
|
||||
ret = read_to_buf(&image_info, image_ptr);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
writelen = image_ptr + image_info.file_size - buf;
|
||||
|
||||
auh_header_kernel = (struct auh_header *)buf;
|
||||
fill_auh(auh_header_kernel, writelen - AUH_SIZE);
|
||||
|
||||
ret = write_fw(ofname, buf, writelen);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
out_free_buf:
|
||||
free(buf);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
progname = basename(argv[0]);
|
||||
image_type = SYSUPGRADE;
|
||||
family_member = 0;
|
||||
firmware_size = 0;
|
||||
|
||||
while (1) {
|
||||
int c;
|
||||
|
||||
c = getopt(argc, argv, "f:F:i:hk:m:o:r:s:");
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'f':
|
||||
sscanf(optarg, "0x%hx", &family_member);
|
||||
break;
|
||||
case 'F':
|
||||
image_info.file_name = optarg;
|
||||
image_type = FACTORY;
|
||||
break;
|
||||
case 'i':
|
||||
inspect_info.file_name = optarg;
|
||||
break;
|
||||
case 'k':
|
||||
kernel_info.file_name = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
if (strlen(optarg) == 12)
|
||||
memcpy(rom_id, optarg, 12);
|
||||
break;
|
||||
case 'r':
|
||||
rootfs_info.file_name = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
ofname = optarg;
|
||||
break;
|
||||
case 's':
|
||||
sscanf(optarg, "0x%x", &firmware_size);
|
||||
break;
|
||||
default:
|
||||
usage(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = check_options();
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!inspect_info.file_name) {
|
||||
if (image_type == FACTORY)
|
||||
ret = wrap_fw();
|
||||
else
|
||||
ret = build_fw();
|
||||
}
|
||||
else
|
||||
ret = inspect_fw();
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
}
|
Loading…
Reference in a new issue