xref: /netbsd-src/usr.sbin/flashctl/flashctl.c (revision 8e33eff89e26cf71871ead62f0d5063e1313c33a)
1 /*	$NetBSD: flashctl.c,v 1.11 2024/05/13 20:38:05 rillig Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010 Department of Software Engineering,
5  *		      University of Szeged, Hungary
6  * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by the Department of Software Engineering, University of Szeged, Hungary
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 __RCSID("$NetBSD: flashctl.c,v 1.11 2024/05/13 20:38:05 rillig Exp $");
36 
37 #include <sys/ioctl.h>
38 #include <sys/flashio.h>
39 #include <fcntl.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <err.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <limits.h>
46 #include <inttypes.h>
47 #include <ctype.h>
48 #include <errno.h>
49 
50 
51 static void usage(void);
52 static int to_intmax(intmax_t *, const char *);
53 
54 int
55 main(int argc, char **argv)
56 {
57 	char *device, *command;
58 	int fd, error = 0;
59 	intmax_t n = -1;
60 
61 	setprogname(argv[0]);
62 
63 	if (argc < 3) {
64 		usage();
65 		exit(1);
66 	}
67 
68 	device = argv[1];
69 	command = argv[2];
70 	argc -= 3;
71 	argv += 3;
72 
73 	fd = open(device, O_RDWR, 0);
74 	if (fd == -1) {
75 		err(EXIT_FAILURE, "can't open flash device");
76 	}
77 
78 	if (strcmp("erase", command) == 0) {
79 		struct flash_info_params ip;
80 		struct flash_erase_params ep;
81 
82 		error = ioctl(fd, FLASH_GET_INFO, &ip);
83 		if (error != 0) {
84 			warn("ioctl: FLASH_GET_INFO");
85 			goto out;
86 		}
87 
88 		if (argc == 2) {
89 			error = to_intmax(&n, argv[0]);
90 			if (error != 0) {
91 				warnx("%s", strerror(error));
92 				goto out;
93 			}
94 			ep.ep_addr = n;
95 
96 			if (strcmp("all", argv[1]) == 0) {
97 				ep.ep_len = ip.ip_flash_size;
98 			} else {
99 				error = to_intmax(&n, argv[1]);
100 				if (error != 0) {
101 					warnx("%s", strerror(error));
102 					goto out;
103 				}
104 				ep.ep_len = n;
105 			}
106 		} else {
107 			warnx("invalid number of arguments");
108 			error = 1;
109 			goto out;
110 		}
111 
112 		printf("Erasing %jx bytes starting from %jx\n",
113 		    (uintmax_t)ep.ep_len, (uintmax_t)ep.ep_addr);
114 
115 		error = ioctl(fd, FLASH_ERASE_BLOCK, &ep);
116 		if (error != 0) {
117 			warn("ioctl: FLASH_ERASE_BLOCK");
118 			goto out;
119 		}
120 	} else if (strcmp("identify", command) == 0) {
121 		struct flash_info_params ip;
122 
123 		error = ioctl(fd, FLASH_GET_INFO, &ip);
124 		if (error != 0) {
125 			warn("ioctl: FLASH_GET_INFO");
126 			goto out;
127 		}
128 
129 		printf("Device type: ");
130 		switch (ip.ip_flash_type) {
131 		case FLASH_TYPE_NOR:
132 			printf("NOR flash");
133 			break;
134 		case FLASH_TYPE_NAND:
135 			printf("NAND flash");
136 			break;
137 		default:
138 			printf("unknown (%d)", ip.ip_flash_type);
139 		}
140 		printf("\n");
141 
142 		/* TODO: humanize */
143 		printf("Capacity %jd Mbytes, %jd pages, %ju bytes/page\n",
144 		    (intmax_t)ip.ip_flash_size / 1024 / 1024,
145 		    (intmax_t)ip.ip_flash_size / ip.ip_page_size,
146 		    (intmax_t)ip.ip_page_size);
147 
148 		if (ip.ip_flash_type == FLASH_TYPE_NAND) {
149 			printf("Block size %jd Kbytes, %jd pages/block\n",
150 			    (intmax_t)ip.ip_erase_size / 1024,
151 			    (intmax_t)ip.ip_erase_size / ip.ip_page_size);
152 		}
153 	} else if (strcmp("badblocks", command) == 0) {
154 		struct flash_info_params ip;
155 		struct flash_badblock_params bbp;
156 		flash_off_t addr;
157 		bool hasbad = false;
158 
159 		error = ioctl(fd, FLASH_GET_INFO, &ip);
160 		if (error != 0) {
161 			warn("ioctl: FLASH_GET_INFO");
162 			goto out;
163 		}
164 
165 		printf("Scanning for bad blocks: ");
166 
167 		addr = 0;
168 		while (addr < ip.ip_flash_size) {
169 			bbp.bbp_addr = addr;
170 
171 			error = ioctl(fd, FLASH_BLOCK_ISBAD, &bbp);
172 			if (error != 0) {
173 				warn("ioctl: FLASH_BLOCK_ISBAD");
174 				goto out;
175 			}
176 
177 			if (bbp.bbp_isbad) {
178 				hasbad = true;
179 				printf("0x%jx ", addr);
180 			}
181 
182 			addr += ip.ip_erase_size;
183 		}
184 
185 		if (hasbad) {
186 			printf("Done.\n");
187 		} else {
188 			printf("No bad blocks found.\n");
189 		}
190 	} else if (strcmp("markbad", command) == 0) {
191 		flash_off_t address;
192 
193 		/* TODO: maybe we should let the user specify
194 		 * multiple blocks?
195 		 */
196 		if (argc != 1) {
197 			warnx("invalid number of arguments");
198 			error = 1;
199 			goto out;
200 		}
201 
202 		error = to_intmax(&n, argv[0]);
203 		if (error != 0) {
204 			warnx("%s", strerror(error));
205 			goto out;
206 		}
207 
208 		address = n;
209 
210 		printf("Marking block 0x%jx as bad.\n",
211 		    (intmax_t)address);
212 
213 		error = ioctl(fd, FLASH_BLOCK_MARKBAD, &address);
214 		if (error != 0) {
215 			warn("ioctl: FLASH_BLOCK_MARKBAD");
216 			goto out;
217 		}
218 	} else {
219 		warnx("Unknown command");
220 		error = EXIT_FAILURE;
221 		goto out;
222 	}
223 
224 out:
225 	close(fd);
226 	return error;
227 }
228 
229 int
230 to_intmax(intmax_t *num, const char *str)
231 {
232 	char *endptr;
233 
234 	errno = 0;
235 	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
236 		if (!isxdigit((unsigned char)str[2]))
237 			return EINVAL;
238 		*num = strtoimax(str, &endptr, 16);
239 	} else {
240 		if (!isdigit((unsigned char)str[0]))
241 			return EINVAL;
242 		*num = strtoimax(str, &endptr, 10);
243 	}
244 
245 	if (errno == ERANGE && (*num == INTMAX_MIN || *num == INTMAX_MAX)) {
246 		return ERANGE;
247 	}
248 
249 	return 0;
250 }
251 
252 void
253 usage(void)
254 {
255 	fprintf(stderr, "usage: %s <device> identify\n",
256 	    getprogname());
257 	fprintf(stderr, "       %s <device> erase <start address> <size>|all\n",
258 	    getprogname());
259 	fprintf(stderr, "       %s <device> badblocks\n",
260 	    getprogname());
261 	fprintf(stderr, "       %s <device> markbad <address>\n",
262 	    getprogname());
263 }
264