[firmware-utils 1/2] iptime-naspkg: add image header tool for ipTIME NAS series

Sungbo Eo mans0n at gorani.run
Sat Jan 22 05:40:15 PST 2022


The purpose of this tool is to pass the factory image verification process,
and it does not provide a full image with kernel and rootfs. The tool prepends
a factory header to kernel image, and rootfs should be appended separately.

This tool has been developed and tested on NAS1 and NAS1dual, but it can
also be used on other similar models with additional tweaking.

Signed-off-by: Sungbo Eo <mans0n at gorani.run>
---
 CMakeLists.txt      |   1 +
 src/iptime-naspkg.c | 228 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 229 insertions(+)
 create mode 100644 src/iptime-naspkg.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f406520..9c7e0e1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,6 +42,7 @@ FW_UTIL(encode_crc "" "" "")
 FW_UTIL(fix-u-media-header src/cyg_crc32.c "" "")
 FW_UTIL(hcsmakeimage src/bcmalgo.c "" "")
 FW_UTIL(imagetag "src/imagetag_cmdline.c;src/cyg_crc32.c" "" "")
+FW_UTIL(iptime-naspkg "" "" "")
 FW_UTIL(jcgimage "" "" "${ZLIB_LIBRARIES}")
 FW_UTIL(lxlfw "" "" "")
 FW_UTIL(lzma2eva "" "" "${ZLIB_LIBRARIES}")
diff --git a/src/iptime-naspkg.c b/src/iptime-naspkg.c
new file mode 100644
index 0000000..c0fc114
--- /dev/null
+++ b/src/iptime-naspkg.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 Sungbo Eo <mans0n at gorani.run>
+ *
+ * This code is based on mkdhpimg.c and mkzcfw.c
+ * Copyright (C) 2010 Gabor Juhos <juhosg at openwrt.org>
+ * Copyright (c) 2016 FUKAUMI Naoki <naobsd at gmail.com>
+ *
+ * Checksum algorithm is derived from EFM's mknas utility
+ * found in GPL'ed T16000 source.
+ */
+
+#include <byteswap.h>
+#include <endian.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#if !defined(__BYTE_ORDER)
+#error "Unknown byte order"
+#endif
+
+#if (__BYTE_ORDER == __BIG_ENDIAN)
+#define HOST_TO_LE32(x)	bswap_32(x)
+#elif (__BYTE_ORDER == __LITTLE_ENDIAN)
+#define HOST_TO_LE32(x)	(x)
+#else
+#error "Unsupported endianness"
+#endif
+
+#define FW_HEADER_SIZE	0x400
+#define FW_VERSION	"0.0.00"
+#define FW_MAGIC	"EFM_NAS_PKG"
+
+struct fw_header {
+	uint8_t model[32];
+	uint8_t version[32];
+	uint8_t ctime[32];
+	uint32_t size;
+	uint32_t checksum;
+	uint32_t offset_header;
+	uint32_t offset_rootfs;
+	uint32_t offset_app;
+	uint32_t checksum_kr;
+	uint8_t magic[16];
+
+	uint32_t size_kra;
+	uint32_t checksum_kra;
+	uint32_t offset_ext;
+} __attribute__ ((packed));
+
+enum board_type {
+	BOARD_KIRKWOOD,
+	BOARD_ARMADA380,
+};
+
+struct board_type_info {
+	int bootloader_size;
+	int block_size;
+};
+
+struct board_info {
+	const char *model;
+	enum board_type type;
+};
+
+struct board_type_info board_types[] = {
+	/* BOARD_KIRKWOOD */
+	{ .bootloader_size = 0x40000, .block_size = 0x0 },
+	/* BOARD_ARMADA380 */
+	{ .bootloader_size = 0x100000, .block_size = 0x10000 },
+};
+
+struct board_info boards[] = {
+	{ .model = "nas1",     .type = BOARD_KIRKWOOD  },
+	{ .model = "nas1dual", .type = BOARD_ARMADA380 },
+	{ /* sentinel */ }
+};
+
+struct board_info *find_board(const char *model)
+{
+	struct board_info *ret = NULL;
+	struct board_info *board;
+
+	for (board = boards; board->model != NULL; board++) {
+		if (strcmp(model, board->model) == 0) {
+			ret = board;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/* (FW_HEADER_SIZE + size_in + padding) % block_size == 0 */
+size_t calc_padding(enum board_type type, size_t size_in)
+{
+	int block_size, remainder;
+
+	block_size = board_types[type].block_size;
+	if (block_size == 0)
+		return 0;
+	remainder = (FW_HEADER_SIZE + size_in) % block_size;
+	return remainder ? block_size - remainder : 0;
+}
+
+char *get_ctime(void)
+{
+	char *env = getenv("SOURCE_DATE_EPOCH");
+	char *endptr = env;
+	time_t timestamp = -1;
+
+	if (env && *env) {
+		errno = 0;
+		timestamp = strtoull(env, &endptr, 10);
+
+		if (errno || (endptr && *endptr != '\0')) {
+			fprintf(stderr, "Invalid SOURCE_DATE_EPOCH\n");
+			timestamp = -1;
+		}
+	}
+
+	if (timestamp == -1)
+		time(&timestamp);
+
+	return asctime(gmtime(&timestamp));
+}
+
+uint32_t make_checksum(const char *model_name, uint8_t *bytes, int length)
+{
+	int i;
+	uint32_t sum = 0;
+	uint32_t magic = 0x19283745;
+
+	for (i = 0; i < length; i++)
+		sum += bytes[i];
+	return ((uint32_t)strlen(model_name) * magic + ~sum) ^ sum;
+}
+
+void make_header(struct board_info *board, uint8_t *buffer, size_t img_size)
+{
+	struct fw_header *header = (struct fw_header *)buffer;
+	char *time_created;
+	uint32_t checksum;
+	size_t bootloader_size, image_end_offset;
+
+	time_created = get_ctime();
+	checksum = make_checksum(board->model, buffer + FW_HEADER_SIZE, img_size);
+	bootloader_size = board_types[board->type].bootloader_size;
+	image_end_offset = bootloader_size + FW_HEADER_SIZE + img_size;
+
+	strncpy((char *)header->model, board->model, sizeof(header->model)-1);
+	strncpy((char *)header->version, FW_VERSION, sizeof(header->version)-1);
+	strncpy((char *)header->ctime, time_created, sizeof(header->ctime)-1);
+	header->size = HOST_TO_LE32(img_size);
+	header->checksum = HOST_TO_LE32(checksum);
+	header->offset_header = HOST_TO_LE32(bootloader_size);
+	header->offset_rootfs = HOST_TO_LE32(image_end_offset);
+	header->offset_app = HOST_TO_LE32(image_end_offset);
+	header->checksum_kr = HOST_TO_LE32(checksum);
+	strncpy((char *)header->magic, FW_MAGIC, sizeof(header->magic)-1);
+
+	if (board->type == BOARD_ARMADA380) {
+		header->size_kra = HOST_TO_LE32(img_size);
+		header->checksum_kra = HOST_TO_LE32(checksum);
+		header->offset_ext = HOST_TO_LE32(image_end_offset);
+	}
+}
+
+int main(int argc, const char *argv[])
+{
+	const char *model_name, *img_in, *img_out;
+	struct board_info *board;
+	int file_in, file_out;
+	struct stat stat_in;
+	size_t size_in, size_in_padded, size_out;
+	uint8_t *buffer;
+
+	if (argc != 4) {
+		fprintf(stderr, "Usage: %s <model> <input> <output>\n", argv[0]);
+		return EXIT_FAILURE;
+	}
+	model_name = argv[1];
+	img_in = argv[2];
+	img_out = argv[3];
+
+	board = find_board(model_name);
+	if (board == NULL) {
+		fprintf(stderr, "%s: Not supported model\n", model_name);
+		return EXIT_FAILURE;
+	}
+
+	if ((file_in = open(img_in, O_RDONLY)) == -1)
+		err(EXIT_FAILURE, "%s", img_in);
+
+	if (fstat(file_in, &stat_in) == -1)
+		err(EXIT_FAILURE, "%s", img_in);
+
+	size_in = stat_in.st_size;
+	size_in_padded = size_in + calc_padding(board->type, size_in);
+	size_out = FW_HEADER_SIZE + size_in_padded;
+
+	if ((buffer = malloc(size_out)) == NULL)
+		err(EXIT_FAILURE, "malloc");
+
+	read(file_in, buffer + FW_HEADER_SIZE, size_in);
+	close(file_in);
+
+	memset(buffer, 0, FW_HEADER_SIZE);
+
+	make_header(board, buffer, size_in_padded);
+
+	if ((file_out = creat(img_out, 0644)) == -1)
+		err(EXIT_FAILURE, "%s", img_out);
+	write(file_out, buffer, size_out);
+	close(file_out);
+
+	free(buffer);
+
+	return EXIT_SUCCESS;
+}
-- 
2.34.1




More information about the openwrt-devel mailing list