302efc601065cdbb79f0a9a0c2271819fe978888
[openwrt/openwrt.git] / tools / firmware-utils / src / zytrx.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * zytrx - add header to images for ZyXEL NR7101
4 *
5 * Based on add_header.c - partially based on OpenWrt's
6 * motorola-bin.c
7 *
8 * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
9 * Gabor Juhos <juhosg@openwrt.org>
10 * Copyright (C) 2021 Bjørn Mork <bjorn@mork.no>
11
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License,
14 * version 2 as published by the Free Software Foundation.
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stddef.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #include <string.h>
26 #include <netinet/in.h>
27 #include <inttypes.h>
28
29 #define BPB 8 /* bits/byte */
30
31 static uint32_t crc32[1<<BPB];
32
33 static void init_crc32(void)
34 {
35 const uint32_t poly = ntohl(0x2083b8ed);
36 int n;
37
38 for (n = 0; n < 1<<BPB; n++) {
39 uint32_t crc = n;
40 int bit;
41
42 for (bit = 0; bit < BPB; bit++)
43 crc = (crc & 1) ? (poly ^ (crc >> 1)) : (crc >> 1);
44 crc32[n] = crc;
45 }
46 }
47
48 static uint32_t crc32buf(const unsigned char *buf, size_t len)
49 {
50 uint32_t crc = 0xFFFFFFFF;
51
52 for (; len; len--, buf++)
53 crc = crc32[(uint8_t)crc ^ *buf] ^ (crc >> BPB);
54 return ~crc;
55 }
56
57 /* HDR0 reversed, to be stored as BE */
58 #define MAGIC 0x30524448 /* HDR0 reversed, to be stored as BE */
59
60 /* All numbers are stored as BE */
61 struct zytrx_t {
62 uint32_t magic;
63 uint32_t len_h; /* Length of this header */
64 uint32_t len_t; /* Total length of file */
65 uint32_t crc32_p; /* Bit inverted 32-bit CRC of image payload */
66 uint8_t verInt[32]; /* String "5.0.0.0\n" zero padded */
67 uint8_t verExt[32]; /* String "\n" zero padded */
68 uint32_t len_p; /* Length of image payload */
69 uint8_t pad1[12]; /* zero padding(?) */
70 uint8_t code[164]; /* string "3 6035 122 0\n" zero padded */
71 uint8_t chipid[8]; /* string "MT7621A" zero padded */
72 uint8_t boardid[16]; /* string "NR7101" zero padded */
73 uint32_t modelid; /* modelid as 4 BCD digits: 0x07010001 */
74 uint8_t pad2[8]; /* zero padding(?) */
75 uint8_t swVersionInt[32]; /* ZyXEL version string: "1.00(ABUV.0)D0" zero padded */
76 uint8_t swVersionExt[32]; /* identical to swVersionInt */
77 uint8_t pad4[4]; /* zero padding(?) */
78 uint32_t kernelChksum; /* no idea how this is computed - reported but not validated */
79 uint8_t pad5[4]; /* zero padding(?) */
80 uint32_t crc32_h; /* Bit inverted 32-bit CRC of this header payload */
81 uint8_t pad6[4]; /* zero padding(?) */
82 };
83
84 /* static?() field values of unknown meaning - maybe ove to board
85 * table when we know the significance
86 */
87 #define VER_INT "5.0.0.0\n"
88 #define VER_EXT "\n"
89 #define CODE "3 6035 122 0\n"
90 #define KERNELCHKSUM 0x12345678
91
92 /* table of supported devices using this header format */
93 static struct board_t {
94 uint8_t chipid[8];
95 uint8_t boardid[16];
96 uint32_t modelid;
97 } boards[] = {
98 { "MT7621A", "NR7101", 0x07010001 },
99 {}
100 };
101
102 static int find_board(struct zytrx_t *h, char *board)
103 {
104 struct board_t *p;
105
106 for (p = boards; p->modelid; p++) {
107 if (strncmp((const char *)p->boardid, board, sizeof(p->boardid)))
108 continue;
109 memcpy(h->chipid, p->chipid, sizeof(h->chipid));
110 memcpy(h->boardid, p->boardid, sizeof(h->boardid));
111 h->modelid = htonl(p->modelid);
112 return 0;
113 }
114 return -1;
115 }
116
117 static void usage(const char *name)
118 {
119 struct board_t *p;
120
121 fprintf(stderr, "Usage:\n");
122 fprintf(stderr, " %s -B <board> -v <versionstr> -i <file> [-o <outputfile>]\n\n", name);
123 fprintf(stderr, "Supported <board> values:\n");
124 for (p = boards; p->modelid; p++)
125 fprintf(stderr, "\t%-12s\n", p->boardid);
126 fprintf(stderr, "\nExample:\n");
127 fprintf(stderr, " %s -B %s -v foobar-1.0 -i my.img -o out.img\n\n", name,
128 boards[0].boardid);
129 exit(EXIT_FAILURE);
130 }
131
132 static void errexit(const char *msg)
133 {
134 fprintf(stderr, "ERR: %s: %s\n", msg, errno ? strerror(errno) : "unknown");
135 exit(EXIT_FAILURE);
136 }
137
138 static void *map_input(const char *name, size_t *len)
139 {
140 struct stat stat;
141 void *mapped;
142 int fd;
143
144 fd = open(name, O_RDONLY);
145 if (fd < 0)
146 return NULL;
147 if (fstat(fd, &stat) < 0) {
148 close(fd);
149 return NULL;
150 }
151 *len = stat.st_size;
152 mapped = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
153 if (close(fd) < 0)
154 return NULL;
155 return mapped;
156 }
157
158 int main(int argc, char **argv)
159 {
160 int c, fdout = STDOUT_FILENO;
161 void *input_file = NULL;
162 size_t file_len, len;
163 uint32_t crc;
164 struct zytrx_t h = {
165 .magic = htonl(MAGIC),
166 .len_h = htonl(sizeof(h)),
167 .verInt = VER_INT,
168 .verExt = VER_EXT,
169 .code = CODE,
170 .kernelChksum = htonl(KERNELCHKSUM),
171 };
172
173 while ((c = getopt(argc, argv, "B:v:i:o:")) != -1) {
174 switch (c) {
175 case 'B':
176 if (find_board(&h, optarg) < 0)
177 errexit("unsupported board");
178 break;
179 case 'v':
180 len = strlen(optarg);
181 if (len > sizeof(h.swVersionInt))
182 errexit("version string too long");
183 memcpy(h.swVersionInt, optarg, len);
184 memcpy(h.swVersionExt, optarg, len);
185 break;
186 case 'i':
187 input_file = map_input(optarg, &file_len);
188 if (!input_file)
189 errexit(optarg);
190 break;
191 case 'o':
192 fdout = open(optarg, O_WRONLY | O_CREAT, 0644);
193 if (fdout < 0)
194 errexit(optarg);
195 break;
196 default:
197 usage(argv[0]);
198 }
199 }
200
201 /* required paremeters */
202 if (!input_file || !h.modelid || !h.swVersionInt[0])
203 usage(argv[0]);
204
205 /* length fields */
206 h.len_t = htonl(sizeof(h) + file_len);
207 h.len_p = htonl(file_len);
208
209 /* crc fields */
210 init_crc32();
211 crc = crc32buf(input_file, file_len);
212 h.crc32_p = htonl(~crc);
213 crc = crc32buf((unsigned char *)&h, sizeof(h));
214 h.crc32_h = htonl(~crc);
215
216 /* dump new image */
217 write(fdout, &h, sizeof(h));
218 write(fdout, input_file, file_len);
219
220 /* close files */
221 munmap(input_file, file_len);
222 if (fdout != STDOUT_FILENO)
223 close(fdout);
224
225 return EXIT_SUCCESS;
226 }