// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020 Sungbo Eo * * This code is based on mkdhpimg.c and mkzcfw.c * Copyright (C) 2010 Gabor Juhos * Copyright (c) 2016 FUKAUMI Naoki * * Checksum algorithm is derived from EFM's mknas utility * found in GPL'ed T16000 source. */ #include #include #include #include #include #include #include #include #include #include #include #include #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(×tamp); return asctime(gmtime(×tamp)); } 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 \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; }