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