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