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