ramips: add support for ZyXEL NWA50AX / NWA55AXE
[openwrt/staging/nbd.git] / package / utils / zyxel-bootconfig / src / zyxel-bootconfig.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /* Copyright (C) 2022 David Bauer <mail@david-bauer.net> */
3
4 /*
5 * First byte: Image status
6 *
7 * Possible status-codes:
8 * 0x0: none
9 * 0x1: new
10 * 0x2: valid
11 * 0x3: invalid
12 *
13 * Example: Image 0 valid; Image 1 invalid
14 * 11001000
15 * || ||
16 * img1||
17 * img0
18 *
19 * Second byte: Active Image
20 * Possible values:
21 * 0x0: Image0 active
22 * 0x1: Image1 active
23 */
24
25 #include <stdio.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29
30 #include <fcntl.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include <sys/ioctl.h>
35 #include <mtd/mtd-user.h>
36
37 #define BOOTCONFIG_SIZE 0x20
38 #define BOOTCONFIG_IMAGE_STATUS 0x0
39 #define BOOTCONFIG_ACTIVE_IMAGE 0x1
40
41 #define IMAGE_0_SHIFT 2
42 #define IMAGE_0_MASK 0x0c
43 #define IMAGE_1_SHIFT 6
44 #define IMAGE_1_MASK 0xc0
45
46 #define IMAGE_STATUS(img0, img1) (((img0 << IMAGE_0_SHIFT) & IMAGE_0_MASK) | ((img1 << IMAGE_1_SHIFT) & IMAGE_1_MASK))
47
48 #define ACTIVE_IMAGE_MASK 0x1
49 #define ACTIVE_IMAGE(img) (img & ACTIVE_IMAGE_MASK)
50
51 enum zyxel_bootconfig_image_status {
52 IMAGE_STATUS_NONE = 0x0,
53 IMAGE_STATUS_NEW = 0x1,
54 IMAGE_STATUS_VALID = 0x2,
55 IMAGE_STATUS_INVALID = 0x3,
56 __IMAGE_STATUS_EINVAL,
57 };
58
59 struct zyxel_bootconfig {
60 enum zyxel_bootconfig_image_status image0_status;
61 enum zyxel_bootconfig_image_status image1_status;
62 unsigned int active_image;
63 };
64
65 struct zyxel_bootconfig_mtd {
66 struct mtd_info_user mtd_info;
67 int fd;
68 };
69
70 struct zyxel_image_status {
71 enum zyxel_bootconfig_image_status code;
72 const char *name;
73 };
74
75 struct zyxel_image_status image_status_codes[] = {
76 { .code = IMAGE_STATUS_NONE, .name = "none" },
77 { .code = IMAGE_STATUS_NEW, .name = "new" },
78 { .code = IMAGE_STATUS_VALID, .name = "valid" },
79 { .code = IMAGE_STATUS_INVALID, .name = "invalid" },
80 {},
81 };
82
83
84 static enum zyxel_bootconfig_image_status zyxel_bootconfig_image_status_parse(const char *status) {
85 struct zyxel_image_status* s;
86
87 for (s = image_status_codes; s->name; s++) {
88 if (!strcmp(status, s->name)) {
89 return s->code;
90 }
91 }
92
93 return __IMAGE_STATUS_EINVAL;
94 }
95
96 const char *zyxel_bootconfig_image_status_name(const enum zyxel_bootconfig_image_status bootconfig) {
97 struct zyxel_image_status* s;
98
99 for (s = image_status_codes; s->name; s++) {
100 if (bootconfig == s->code) {
101 return s->name;
102 }
103 }
104
105 return "N/A";
106 }
107
108 static void zyxel_bootconfig_mtd_close(struct zyxel_bootconfig_mtd *mtd) {
109 close(mtd->fd);
110 }
111
112
113 static int zyxel_bootconfig_mtd_open(struct zyxel_bootconfig_mtd *mtd, const char *mtd_name) {
114 int ret = 0;
115
116 mtd->fd = open(mtd_name, O_RDWR | O_SYNC);
117 if (mtd->fd < 0) {
118 fprintf(stderr, "Could not open mtd device: %s\n", mtd_name);
119 ret = -1;
120 goto out;
121 }
122
123 if (ioctl(mtd->fd, MEMGETINFO, &mtd->mtd_info)) {
124 fprintf(stderr, "Could not get MTD device info from %s\n", mtd_name);
125 ret = -1;
126 zyxel_bootconfig_mtd_close(mtd);
127 goto out;
128 }
129
130 out:
131 return ret;
132 }
133
134
135 static int zyxel_bootconfig_read(struct zyxel_bootconfig *config, struct zyxel_bootconfig_mtd *mtd) {
136 char *args = NULL;
137 int ret = 0;
138
139 /* Allocate memory for reading boot-config partition */
140 args = calloc(1, mtd->mtd_info.erasesize);
141 if (!args) {
142 fprintf(stderr, "Could not allocate memory!\n");
143 ret = -1;
144 goto out;
145 }
146
147 /* Read bootconfig partition */
148 pread(mtd->fd, args, mtd->mtd_info.erasesize, 0);
149
150 /* Parse config */
151 memset(config, 0, sizeof(*config));
152
153 config->image0_status = (args[BOOTCONFIG_IMAGE_STATUS] & IMAGE_0_MASK) >> IMAGE_0_SHIFT;
154 config->image1_status = (args[BOOTCONFIG_IMAGE_STATUS] & IMAGE_1_MASK) >> IMAGE_1_SHIFT;
155 config->active_image = (args[BOOTCONFIG_ACTIVE_IMAGE] & ACTIVE_IMAGE_MASK);
156
157 out:
158 if (args)
159 free(args);
160 return ret;
161 }
162
163
164 static int zyxel_bootconfig_write(struct zyxel_bootconfig *config, struct zyxel_bootconfig_mtd *mtd)
165 {
166 struct erase_info_user erase_info;
167 char img_status, img_active;
168 char *args = NULL;
169 int ret = 0;
170
171 /* Allocate memory for reading boot-config partition */
172 args = calloc(1, mtd->mtd_info.erasesize);
173 if (!args) {
174 fprintf(stderr, "Could not allocate memory!\n");
175 ret = -1;
176 goto out;
177 }
178
179 /* Read bootconfig partition */
180 pread(mtd->fd, args, mtd->mtd_info.erasesize, 0);
181
182 img_status = IMAGE_STATUS(config->image0_status, config->image1_status);
183 img_active = ACTIVE_IMAGE(config->active_image);
184
185 /* Check if bootconfig has to be written */
186 if (args[BOOTCONFIG_IMAGE_STATUS] == img_status && args[BOOTCONFIG_ACTIVE_IMAGE] == img_active) {
187 ret = 0;
188 goto out;
189 }
190
191 /* Erase first block (containing the magic) */
192 erase_info.start = 0;
193 erase_info.length = mtd->mtd_info.erasesize;
194 ret = ioctl(mtd->fd, MEMERASE, &erase_info);
195 if (ret < 0) {
196 fprintf(stderr, "Failed to erase block: %i\n", ret);
197 goto out;
198 }
199
200
201 /* Write bootconfig */
202 args[BOOTCONFIG_IMAGE_STATUS] = img_status;
203 args[BOOTCONFIG_ACTIVE_IMAGE] = img_active;
204
205 if (pwrite(mtd->fd, args, mtd->mtd_info.erasesize, 0) != mtd->mtd_info.erasesize) {
206 fprintf(stderr, "Error writing bootconfig!\n");
207 ret = -1;
208 goto out;
209 }
210
211 out:
212 if (args)
213 free(args);
214 return ret;
215 }
216
217
218 static void zyxel_bootconfig_print_usage(char *programm)
219 {
220 struct zyxel_image_status* s = image_status_codes;
221
222 printf("Usage: %s <mtd-device> <command> [args]\n", programm);
223 printf("Available commands:\n");
224 printf(" get-status\n");
225 printf(" set-image-status [0/1] [");
226
227 while (s->name) {
228 printf("%s", s->name);
229 s++;
230
231 if (s->name)
232 printf(",");
233 }
234
235 printf("]\n");
236 printf(" set-active-image [0/1]\n");
237 }
238
239 int main(int argc, char *argv[])
240 {
241 enum zyxel_bootconfig_image_status image_status;
242 struct zyxel_bootconfig_mtd mtd;
243 struct zyxel_bootconfig config;
244 const char *mtd_name, *command;
245 bool writeback = false;
246 int image_idx;
247
248 if (argc < 3) {
249 zyxel_bootconfig_print_usage(argv[0]);
250 return 1;
251 }
252
253 mtd_name = argv[1];
254 command = argv[2];
255
256 if (zyxel_bootconfig_mtd_open(&mtd, mtd_name)) {
257 fprintf(stderr, "Error opening %s!\n", mtd_name);
258 return 1;
259 }
260
261 if (zyxel_bootconfig_read(&config, &mtd)) {
262 fprintf(stderr, "Error reading bootconfig!\n");
263 zyxel_bootconfig_mtd_close(&mtd);
264 return 1;
265 }
266
267 if (!strcmp(command, "set-image-status")) {
268 if (argc < 5) {
269 zyxel_bootconfig_print_usage(argv[0]);
270 zyxel_bootconfig_mtd_close(&mtd);
271 return 1;
272 }
273
274 image_idx = atoi(argv[3]);
275 if (image_idx > 1 || image_idx < 0) {
276 fprintf(stderr, "Invalid image-slot set!\n");
277 zyxel_bootconfig_mtd_close(&mtd);
278 return 1;
279 }
280
281 image_status = zyxel_bootconfig_image_status_parse(argv[4]);
282 if (image_status == __IMAGE_STATUS_EINVAL) {
283 fprintf(stderr, "Invalid image-status!\n");
284 zyxel_bootconfig_mtd_close(&mtd);
285 return 1;
286 }
287
288 if (image_idx == 0) {
289 config.image0_status = image_status;
290 } else {
291 config.image1_status = image_status;
292 }
293
294 writeback = true;
295 } else if (!strcmp(command, "set-active-image")) {
296 if (argc < 4) {
297 zyxel_bootconfig_print_usage(argv[0]);
298 zyxel_bootconfig_mtd_close(&mtd);
299 return 1;
300 }
301
302 image_idx = atoi(argv[3]);
303 if (image_idx > 1 || image_idx < 0) {
304 fprintf(stderr, "Invalid image-slot set!\n");
305 zyxel_bootconfig_mtd_close(&mtd);
306 return 1;
307 }
308
309 config.active_image = image_idx;
310
311 writeback = true;
312 } else if (!strcmp(command, "get-status")) {
313 printf("Active Image: %d\n", config.active_image);
314 printf("Image 0 Status: %s\n", zyxel_bootconfig_image_status_name(config.image0_status));
315 printf("Image 1 Status: %s\n", zyxel_bootconfig_image_status_name(config.image1_status));
316
317 writeback = false;
318 }
319
320 if (writeback) {
321 if (zyxel_bootconfig_write(&config, &mtd)) {
322 fprintf(stderr, "Error writing bootconfig!\n");
323 zyxel_bootconfig_mtd_close(&mtd);
324 return 1;
325 }
326
327 zyxel_bootconfig_mtd_close(&mtd);
328 }
329
330 return 0;
331 }