1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2015 Thomas Hebb <tommyhebb@gmail.com>
4 * Copyright (C) 2016 Christian Lamparter <chunkeey@googlemail.com>
6 * The format of the header this tool generates was first documented by
7 * Chris Blake <chrisrblake93 (at) gmail.com> in a shell script of the
8 * same purpose. I have created this reimplementation at his request. The
9 * original script can be found at:
10 * <https://github.com/riptidewave93/meraki-partbuilder>
12 * Support for the old header format, which is used by the Cisco Z1 AP
13 * has been reverse engineered from the nandloader's nand_load_bk function.
14 * The original code is part of Cisco's GPL code and can be found at:
15 * <https://github.com/riptidewave93/meraki-linux>
26 #include <arpa/inet.h>
28 #define PADDING_BYTE 0xff
30 #define HDR_LENGTH 0x00000020
31 #define HDR_OFF_MAGIC1 0
32 #define HDR_OFF_LOAD_ADDR 4
33 #define HDR_OFF_IMAGELEN 8
34 #define HDR_OFF_ENTRY 12
35 #define HDR_OFF_CHECKSUM 16
36 #define HDR_OFF_FILLER0 20
37 #define HDR_OFF_FILLER1 24
38 #define HDR_OFF_FILLER2 28
52 static char *progname
;
53 static bool strip_padding
;
55 static char *board_id
;
56 static const struct board_info
*board
;
58 static const struct board_info boards
[] = {
61 .description
= "Meraki Z1 Access Point",
63 .imagelen
= 0x007e0000,
64 .load_addr
= 0x80060000,
67 /* terminating entry */
74 #define ERR(fmt, ...) do { \
76 fprintf(stderr, "[%s] *** error: " fmt "\n", \
77 progname, ## __VA_ARGS__); \
80 #define ERRS(fmt, ...) do { \
83 fprintf(stderr, "[%s] *** error: " fmt "\n", \
84 progname, ## __VA_ARGS__, strerror(save)); \
87 static const struct board_info
*find_board(const char *id
)
89 const struct board_info
*ret
;
90 const struct board_info
*board
;
93 for (board
= boards
; board
->id
!= NULL
; board
++) {
94 if (strcasecmp(id
, board
->id
) == 0) {
103 static void usage(int status
)
105 FILE *stream
= (status
!= EXIT_SUCCESS
) ? stderr
: stdout
;
106 const struct board_info
*board
;
108 fprintf(stream
, "Usage: %s [OPTIONS...]\n", progname
);
112 " -B <board> create image for the board specified with <board>\n"
113 " -i <file> read kernel image from the file <file>\n"
114 " -o <file> write output to the file <file>\n"
115 " -s strip padding from the end of the image\n"
116 " -h show this screen\n"
119 fprintf(stream
, "\nBoards:\n");
120 for (board
= boards
; board
->id
!= NULL
; board
++)
121 fprintf(stream
, " %-16s%s\n", board
->id
, board
->description
);
126 static void writel(unsigned char *buf
, size_t offset
, uint32_t value
)
128 value
= htonl(value
);
129 memcpy(buf
+ offset
, &value
, sizeof(uint32_t));
132 static const uint32_t crc32_table
[] = {
133 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
134 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
135 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
136 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
137 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
138 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
139 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
140 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
141 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
142 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
143 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
144 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
145 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
146 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
147 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
148 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
149 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
150 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
151 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
152 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
153 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
154 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
155 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
156 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
157 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
158 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
159 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
160 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
161 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
162 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
163 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
164 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
165 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
166 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
167 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
168 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
169 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
170 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
171 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
172 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
173 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
174 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
175 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
176 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
177 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
178 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
179 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
180 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
181 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
182 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
183 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
184 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
185 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
186 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
187 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
188 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
189 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
190 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
191 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
192 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
193 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
194 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
195 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
196 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
199 static inline uint32_t crc32_accumulate_8(const uint32_t crc
, const uint8_t ch
)
201 return crc32_table
[(crc
^ ch
) & 0xff] ^ (crc
>> 8);
204 static void crc32_csum(uint8_t *buf
, const size_t len
)
210 for (i
= 0; i
< len
; i
+= 4) {
211 crc
= crc32_accumulate_8(crc
, buf
[i
+ 3]);
212 crc
= crc32_accumulate_8(crc
, buf
[i
+ 2]);
213 crc
= crc32_accumulate_8(crc
, buf
[i
+ 1]);
214 crc
= crc32_accumulate_8(crc
, buf
[i
]);
218 writel(buf
, HDR_OFF_CHECKSUM
, crc
);
222 static int meraki_build_hdr(const struct board_info
*board
, const size_t klen
,
225 unsigned char *kernel
;
231 buflen
= board
->imagelen
;
232 kspace
= buflen
- HDR_LENGTH
;
235 ERR("kernel file is too big - max size: 0x%08lX\n", kspace
);
239 /* If requested, resize buffer to remove padding */
241 buflen
= klen
+ HDR_LENGTH
;
243 /* Allocate and initialize buffer for final image */
244 buf
= malloc(buflen
);
246 ERRS("no memory for buffer: %s\n");
249 memset(buf
, PADDING_BYTE
, buflen
);
252 kernel
= buf
+ HDR_LENGTH
;
253 fread(kernel
, klen
, 1, in
);
255 /* Write magic values and filler */
256 writel(buf
, HDR_OFF_MAGIC1
, board
->magic
);
257 writel(buf
, HDR_OFF_FILLER0
, 0);
258 writel(buf
, HDR_OFF_FILLER1
, 0);
259 writel(buf
, HDR_OFF_FILLER2
, 0);
261 /* Write load and kernel entry point address */
262 writel(buf
, HDR_OFF_LOAD_ADDR
, board
->load_addr
);
263 writel(buf
, HDR_OFF_ENTRY
, board
->entry
);
265 /* Write header and image length */
266 writel(buf
, HDR_OFF_IMAGELEN
, klen
);
268 /* this gets replaced later, after the checksum has been calculated */
269 writel(buf
, HDR_OFF_CHECKSUM
, 0);
272 crc32_csum(buf
, klen
+ HDR_LENGTH
);
274 rc
= fwrite(buf
, buflen
, 1, out
);
278 return rc
== 1 ? EXIT_SUCCESS
: EXIT_FAILURE
;
281 int main(int argc
, char *argv
[])
283 int ret
= EXIT_FAILURE
;
284 char *ofname
= NULL
, *ifname
= NULL
;
288 progname
= basename(argv
[0]);
293 c
= getopt(argc
, argv
, "B:i:o:sh");
308 strip_padding
= true;
319 if (board_id
== NULL
) {
320 ERR("no board specified");
324 board
= find_board(board_id
);
326 ERR("unknown board \"%s\"", board_id
);
330 if (ifname
== NULL
) {
331 ERR("no input file specified");
335 if (ofname
== NULL
) {
336 ERR("no output file specified");
340 in
= fopen(ifname
, "r");
342 ERRS("could not open \"%s\" for reading: %s", ifname
);
346 /* Get kernel length */
347 fseek(in
, 0, SEEK_END
);
351 out
= fopen(ofname
, "w");
353 ERRS("could not open \"%s\" for writing: %s", ofname
);
357 ret
= meraki_build_hdr(board
, klen
, out
, in
);