xref: /netbsd-src/usr.sbin/flashctl/flashctl.c (revision deb6f0161a9109e7de9b519dc8dfb9478668dcdd)
1 /*	$NetBSD: flashctl.c,v 1.4 2011/05/24 13:01:53 joerg 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/ioctl.h>
35 #include <sys/flashio.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <err.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <limits.h>
43 #include <inttypes.h>
44 #include <ctype.h>
45 #include <errno.h>
46 
47 
48 void usage(void);
49 int to_intmax(intmax_t *, const char *);
50 
51 int
52 main(int argc, char **argv)
53 {
54 	char *device, *command;
55 	int fd, error = 0;
56 	intmax_t n = -1;
57 
58 	setprogname(argv[0]);
59 
60 	if (argc < 3) {
61 		usage();
62 		exit(1);
63 	}
64 
65 	device = argv[1];
66 	command = argv[2];
67 	argc -= 3;
68 	argv += 3;
69 
70 	fd = open(device, O_RDWR, 0);
71 	if (fd == -1) {
72 		err(EXIT_FAILURE, "can't open flash device");
73 	}
74 
75 	if (!strcmp("erase", command)) {
76 		struct flash_info_params ip;
77 		struct flash_erase_params ep;
78 
79 		error = ioctl(fd, FLASH_GET_INFO, &ip);
80 		if (error) {
81 			warn("ioctl: FLASH_GET_INFO");
82 			goto out;
83 		}
84 
85 		if (argc == 2) {
86 			error = to_intmax(&n, argv[0]);
87 			if (error) {
88 				warnx("%s", strerror(error));
89 				goto out;
90 			}
91 			ep.ep_addr = n;
92 
93 			if (!strcmp("all", argv[1])) {
94 				ep.ep_len = ip.ip_flash_size;
95 			} else {
96 				error = to_intmax(&n, argv[1]);
97 				if (error) {
98 					warnx("%s", strerror(error));
99 					goto out;
100 				}
101 				ep.ep_len = n;
102 			}
103 		} else {
104 			warnx("invalid number of arguments");
105 			error = 1;
106 			goto out;
107 		}
108 
109 		printf("Erasing %jx bytes starting from %jx\n",
110 		    (uintmax_t )ep.ep_len, (uintmax_t )ep.ep_addr);
111 
112 		error = ioctl(fd, FLASH_ERASE_BLOCK, &ep);
113 		if (error) {
114 			warn("ioctl: FLASH_ERASE_BLOCK");
115 			goto out;
116 		}
117 	} else if (!strcmp("identify", command)) {
118 		struct flash_info_params ip;
119 
120 		error = ioctl(fd, FLASH_GET_INFO, &ip);
121 		if (error) {
122 			warn("ioctl: FLASH_GET_INFO");
123 			goto out;
124 		}
125 
126 		printf("Device type: ");
127 		switch (ip.ip_flash_type) {
128 		case FLASH_TYPE_NOR:
129 			printf("NOR flash");
130 			break;
131 		case FLASH_TYPE_NAND:
132 			printf("NAND flash");
133 			break;
134 		default:
135 			printf("unknown (%d)", ip.ip_flash_type);
136 		}
137 		printf("\n");
138 
139 		/* TODO: humanize */
140 		printf("Capacity %jd Mbytes, %jd pages, %ju bytes/page\n",
141 		    (intmax_t )ip.ip_flash_size / 1024 / 1024,
142 		    (intmax_t )ip.ip_flash_size / ip.ip_page_size,
143 		    (intmax_t )ip.ip_page_size);
144 
145 		if (ip.ip_flash_type == FLASH_TYPE_NAND) {
146 			printf("Block size %jd Kbytes, %jd pages/block\n",
147 			    (intmax_t )ip.ip_erase_size / 1024,
148 			    (intmax_t )ip.ip_erase_size / ip.ip_page_size);
149 		}
150 	} else if (!strcmp("badblocks", command)) {
151 		struct flash_info_params ip;
152 		struct flash_badblock_params bbp;
153 		flash_off_t addr;
154 		bool hasbad = false;
155 
156 		error = ioctl(fd, FLASH_GET_INFO, &ip);
157 		if (error) {
158 			warn("ioctl: FLASH_GET_INFO");
159 			goto out;
160 		}
161 
162 		printf("Scanning for bad blocks: ");
163 
164 		addr = 0;
165 		while (addr < ip.ip_flash_size) {
166 			bbp.bbp_addr = addr;
167 
168 			error = ioctl(fd, FLASH_BLOCK_ISBAD, &bbp);
169 			if (error) {
170 				warn("ioctl: FLASH_BLOCK_ISBAD");
171 				goto out;
172 			}
173 
174 			if (bbp.bbp_isbad) {
175 				hasbad = true;
176 				printf("0x%jx ", addr);
177 			}
178 
179 			addr += ip.ip_erase_size;
180 		}
181 
182 		if (hasbad) {
183 			printf("Done.\n");
184 		} else {
185 			printf("No bad blocks found.\n");
186 		}
187 	} else if (!strcmp("markbad", command)) {
188 		flash_off_t address;
189 
190 		/* TODO: maybe we should let the user specify
191 		 * multiple blocks?
192 		 */
193 		if (argc != 1) {
194 			warnx("invalid number of arguments");
195 			error = 1;
196 			goto out;
197 		}
198 
199 		error = to_intmax(&n, argv[0]);
200 		if (error) {
201 			warnx("%s", strerror(error));
202 			goto out;
203 		}
204 
205 		address = n;
206 
207 		printf("Marking block 0x%jx as bad.\n",
208 		    (intmax_t )address);
209 
210 		error = ioctl(fd, FLASH_BLOCK_MARKBAD, &address);
211 		if (error) {
212 			warn("ioctl: FLASH_BLOCK_MARKBAD");
213 			goto out;
214 		}
215 	} else {
216 		warnx("Unknown command");
217 		error = 1;
218 		goto out;
219 	}
220 
221 out:
222 	close(fd);
223 	return error;
224 }
225 
226 int
227 to_intmax(intmax_t *num, const char *str)
228 {
229 	char *endptr;
230 
231 	errno = 0;
232 	if (str[0] == '0' && tolower((int )str[1]) == 'x') {
233 		if (!isxdigit((int )str[0]))
234 			return EINVAL;
235 		*num = strtoimax(str, &endptr, 16);
236 	} else {
237 		if (!isdigit((int )str[0]))
238 			return EINVAL;
239 		*num = strtoimax(str, &endptr, 10);
240 	}
241 
242 	if (errno == ERANGE && (*num == INTMAX_MIN || *num == INTMAX_MAX)) {
243 		return ERANGE;
244 	}
245 
246 	return 0;
247 }
248 
249 void
250 usage(void)
251 {
252 	fprintf(stderr, "usage: %s device identify\n",
253 	    getprogname());
254 	fprintf(stderr, "       %s device erase <start address> <size>|all\n",
255 	    getprogname());
256 	fprintf(stderr, "       %s device badblocks\n",
257 	    getprogname());
258 	fprintf(stderr, "       %s device markbad <address>\n",
259 	    getprogname());
260 }
261