1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /* Copyright (C) 2022 David Bauer <mail@david-bauer.net> */
5 * First byte: Image status
7 * Possible status-codes:
13 * Example: Image 0 valid; Image 1 invalid
19 * Second byte: Active Image
34 #include <sys/ioctl.h>
35 #include <mtd/mtd-user.h>
37 #define BOOTCONFIG_SIZE 0x20
38 #define BOOTCONFIG_IMAGE_STATUS 0x0
39 #define BOOTCONFIG_ACTIVE_IMAGE 0x1
41 #define IMAGE_0_SHIFT 2
42 #define IMAGE_0_MASK 0x0c
43 #define IMAGE_1_SHIFT 6
44 #define IMAGE_1_MASK 0xc0
46 #define IMAGE_STATUS(img0, img1) (((img0 << IMAGE_0_SHIFT) & IMAGE_0_MASK) | ((img1 << IMAGE_1_SHIFT) & IMAGE_1_MASK))
48 #define ACTIVE_IMAGE_MASK 0x1
49 #define ACTIVE_IMAGE(img) (img & ACTIVE_IMAGE_MASK)
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
,
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
;
65 struct zyxel_bootconfig_mtd
{
66 struct mtd_info_user mtd_info
;
70 struct zyxel_image_status
{
71 enum zyxel_bootconfig_image_status code
;
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" },
84 static enum zyxel_bootconfig_image_status
zyxel_bootconfig_image_status_parse(const char *status
) {
85 struct zyxel_image_status
* s
;
87 for (s
= image_status_codes
; s
->name
; s
++) {
88 if (!strcmp(status
, s
->name
)) {
93 return __IMAGE_STATUS_EINVAL
;
96 const char *zyxel_bootconfig_image_status_name(const enum zyxel_bootconfig_image_status bootconfig
) {
97 struct zyxel_image_status
* s
;
99 for (s
= image_status_codes
; s
->name
; s
++) {
100 if (bootconfig
== s
->code
) {
108 static void zyxel_bootconfig_mtd_close(struct zyxel_bootconfig_mtd
*mtd
) {
113 static int zyxel_bootconfig_mtd_open(struct zyxel_bootconfig_mtd
*mtd
, const char *mtd_name
) {
116 mtd
->fd
= open(mtd_name
, O_RDWR
| O_SYNC
);
118 fprintf(stderr
, "Could not open mtd device: %s\n", mtd_name
);
123 if (ioctl(mtd
->fd
, MEMGETINFO
, &mtd
->mtd_info
)) {
124 fprintf(stderr
, "Could not get MTD device info from %s\n", mtd_name
);
126 zyxel_bootconfig_mtd_close(mtd
);
135 static int zyxel_bootconfig_read(struct zyxel_bootconfig
*config
, struct zyxel_bootconfig_mtd
*mtd
) {
139 /* Allocate memory for reading boot-config partition */
140 args
= calloc(1, mtd
->mtd_info
.erasesize
);
142 fprintf(stderr
, "Could not allocate memory!\n");
147 /* Read bootconfig partition */
148 pread(mtd
->fd
, args
, mtd
->mtd_info
.erasesize
, 0);
151 memset(config
, 0, sizeof(*config
));
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
);
164 static int zyxel_bootconfig_write(struct zyxel_bootconfig
*config
, struct zyxel_bootconfig_mtd
*mtd
)
166 struct erase_info_user erase_info
;
167 char img_status
, img_active
;
171 /* Allocate memory for reading boot-config partition */
172 args
= calloc(1, mtd
->mtd_info
.erasesize
);
174 fprintf(stderr
, "Could not allocate memory!\n");
179 /* Read bootconfig partition */
180 pread(mtd
->fd
, args
, mtd
->mtd_info
.erasesize
, 0);
182 img_status
= IMAGE_STATUS(config
->image0_status
, config
->image1_status
);
183 img_active
= ACTIVE_IMAGE(config
->active_image
);
185 /* Check if bootconfig has to be written */
186 if (args
[BOOTCONFIG_IMAGE_STATUS
] == img_status
&& args
[BOOTCONFIG_ACTIVE_IMAGE
] == img_active
) {
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
);
196 fprintf(stderr
, "Failed to erase block: %i\n", ret
);
201 /* Write bootconfig */
202 args
[BOOTCONFIG_IMAGE_STATUS
] = img_status
;
203 args
[BOOTCONFIG_ACTIVE_IMAGE
] = img_active
;
205 if (pwrite(mtd
->fd
, args
, mtd
->mtd_info
.erasesize
, 0) != mtd
->mtd_info
.erasesize
) {
206 fprintf(stderr
, "Error writing bootconfig!\n");
218 static void zyxel_bootconfig_print_usage(char *programm
)
220 struct zyxel_image_status
* s
= image_status_codes
;
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] [");
228 printf("%s", s
->name
);
236 printf(" set-active-image [0/1]\n");
239 int main(int argc
, char *argv
[])
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;
249 zyxel_bootconfig_print_usage(argv
[0]);
256 if (zyxel_bootconfig_mtd_open(&mtd
, mtd_name
)) {
257 fprintf(stderr
, "Error opening %s!\n", mtd_name
);
261 if (zyxel_bootconfig_read(&config
, &mtd
)) {
262 fprintf(stderr
, "Error reading bootconfig!\n");
263 zyxel_bootconfig_mtd_close(&mtd
);
267 if (!strcmp(command
, "set-image-status")) {
269 zyxel_bootconfig_print_usage(argv
[0]);
270 zyxel_bootconfig_mtd_close(&mtd
);
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
);
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
);
288 if (image_idx
== 0) {
289 config
.image0_status
= image_status
;
291 config
.image1_status
= image_status
;
295 } else if (!strcmp(command
, "set-active-image")) {
297 zyxel_bootconfig_print_usage(argv
[0]);
298 zyxel_bootconfig_mtd_close(&mtd
);
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
);
309 config
.active_image
= image_idx
;
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
));
321 if (zyxel_bootconfig_write(&config
, &mtd
)) {
322 fprintf(stderr
, "Error writing bootconfig!\n");
323 zyxel_bootconfig_mtd_close(&mtd
);
327 zyxel_bootconfig_mtd_close(&mtd
);