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
main(int argc,char ** argv)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
to_intmax(intmax_t * num,const char * str)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
usage(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