1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2020 Sungbo Eo <mans0n@gorani.run>
5 * This code is based on mkdhpimg.c and mkzcfw.c
6 * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
7 * Copyright (c) 2016 FUKAUMI Naoki <naobsd@gmail.com>
9 * Checksum algorithm is derived from EFM's mknas utility
10 * found in GPL'ed T16000 source.
26 #if !defined(__BYTE_ORDER)
27 #error "Unknown byte order"
30 #if (__BYTE_ORDER == __BIG_ENDIAN)
31 #define HOST_TO_LE32(x) bswap_32(x)
32 #elif (__BYTE_ORDER == __LITTLE_ENDIAN)
33 #define HOST_TO_LE32(x) (x)
35 #error "Unsupported endianness"
38 #define FW_HEADER_SIZE 0x400
39 #define FW_VERSION "0.0.00"
40 #define FW_MAGIC "EFM_NAS_PKG"
48 uint32_t offset_header
;
49 uint32_t offset_rootfs
;
55 uint32_t checksum_kra
;
57 } __attribute__ ((packed
));
64 struct board_type_info
{
74 struct board_type_info board_types
[] = {
76 { .bootloader_size
= 0x40000, .block_size
= 0x0 },
78 { .bootloader_size
= 0x100000, .block_size
= 0x10000 },
81 struct board_info boards
[] = {
82 { .model
= "nas1", .type
= BOARD_KIRKWOOD
},
83 { .model
= "nas1dual", .type
= BOARD_ARMADA380
},
87 struct board_info
*find_board(const char *model
)
89 struct board_info
*ret
= NULL
;
90 struct board_info
*board
;
92 for (board
= boards
; board
->model
!= NULL
; board
++) {
93 if (strcmp(model
, board
->model
) == 0) {
102 /* (FW_HEADER_SIZE + size_in + padding) % block_size == 0 */
103 size_t calc_padding(enum board_type type
, size_t size_in
)
105 int block_size
, remainder
;
107 block_size
= board_types
[type
].block_size
;
110 remainder
= (FW_HEADER_SIZE
+ size_in
) % block_size
;
111 return remainder
? block_size
- remainder
: 0;
114 char *get_ctime(void)
116 char *env
= getenv("SOURCE_DATE_EPOCH");
118 time_t timestamp
= -1;
122 timestamp
= strtoull(env
, &endptr
, 10);
124 if (errno
|| (endptr
&& *endptr
!= '\0')) {
125 fprintf(stderr
, "Invalid SOURCE_DATE_EPOCH\n");
133 return asctime(gmtime(×tamp
));
136 uint32_t make_checksum(const char *model_name
, uint8_t *bytes
, int length
)
140 uint32_t magic
= 0x19283745;
142 for (i
= 0; i
< length
; i
++)
144 return ((uint32_t)strlen(model_name
) * magic
+ ~sum
) ^ sum
;
147 void make_header(struct board_info
*board
, uint8_t *buffer
, size_t img_size
)
149 struct fw_header
*header
= (struct fw_header
*)buffer
;
152 size_t bootloader_size
, image_end_offset
;
154 time_created
= get_ctime();
155 checksum
= make_checksum(board
->model
, buffer
+ FW_HEADER_SIZE
, img_size
);
156 bootloader_size
= board_types
[board
->type
].bootloader_size
;
157 image_end_offset
= bootloader_size
+ FW_HEADER_SIZE
+ img_size
;
159 strncpy((char *)header
->model
, board
->model
, sizeof(header
->model
)-1);
160 strncpy((char *)header
->version
, FW_VERSION
, sizeof(header
->version
)-1);
161 strncpy((char *)header
->ctime
, time_created
, sizeof(header
->ctime
)-1);
162 header
->size
= HOST_TO_LE32(img_size
);
163 header
->checksum
= HOST_TO_LE32(checksum
);
164 header
->offset_header
= HOST_TO_LE32(bootloader_size
);
165 header
->offset_rootfs
= HOST_TO_LE32(image_end_offset
);
166 header
->offset_app
= HOST_TO_LE32(image_end_offset
);
167 header
->checksum_kr
= HOST_TO_LE32(checksum
);
168 strncpy((char *)header
->magic
, FW_MAGIC
, sizeof(header
->magic
)-1);
170 if (board
->type
== BOARD_ARMADA380
) {
171 header
->size_kra
= HOST_TO_LE32(img_size
);
172 header
->checksum_kra
= HOST_TO_LE32(checksum
);
173 header
->offset_ext
= HOST_TO_LE32(image_end_offset
);
177 int main(int argc
, const char *argv
[])
179 const char *model_name
, *img_in
, *img_out
;
180 struct board_info
*board
;
181 int file_in
, file_out
;
183 size_t size_in
, size_in_padded
, size_out
;
187 fprintf(stderr
, "Usage: %s <model> <input> <output>\n", argv
[0]);
190 model_name
= argv
[1];
194 board
= find_board(model_name
);
196 fprintf(stderr
, "%s: Not supported model\n", model_name
);
200 if ((file_in
= open(img_in
, O_RDONLY
)) == -1)
201 err(EXIT_FAILURE
, "%s", img_in
);
203 if (fstat(file_in
, &stat_in
) == -1)
204 err(EXIT_FAILURE
, "%s", img_in
);
206 size_in
= stat_in
.st_size
;
207 size_in_padded
= size_in
+ calc_padding(board
->type
, size_in
);
208 size_out
= FW_HEADER_SIZE
+ size_in_padded
;
210 if ((buffer
= malloc(size_out
)) == NULL
)
211 err(EXIT_FAILURE
, "malloc");
213 read(file_in
, buffer
+ FW_HEADER_SIZE
, size_in
);
216 memset(buffer
, 0, FW_HEADER_SIZE
);
218 make_header(board
, buffer
, size_in_padded
);
220 if ((file_out
= creat(img_out
, 0644)) == -1)
221 err(EXIT_FAILURE
, "%s", img_out
);
222 write(file_out
, buffer
, size_out
);