xref: /netbsd-src/usr.sbin/flashctl/flashctl.c (revision e040c2414600ba91adaa673c9aa2d1f8922ac5d8)
1*e040c241Srillig /*	$NetBSD: flashctl.c,v 1.11 2024/05/13 20:38:05 rillig Exp $	*/
22b6ee221Sahoka 
32b6ee221Sahoka /*-
42b6ee221Sahoka  * Copyright (c) 2010 Department of Software Engineering,
52b6ee221Sahoka  *		      University of Szeged, Hungary
62b6ee221Sahoka  * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
72b6ee221Sahoka  * All rights reserved.
82b6ee221Sahoka  *
92b6ee221Sahoka  * This code is derived from software contributed to The NetBSD Foundation
102b6ee221Sahoka  * by the Department of Software Engineering, University of Szeged, Hungary
112b6ee221Sahoka  *
122b6ee221Sahoka  * Redistribution and use in source and binary forms, with or without
132b6ee221Sahoka  * modification, are permitted provided that the following conditions
142b6ee221Sahoka  * are met:
152b6ee221Sahoka  * 1. Redistributions of source code must retain the above copyright
162b6ee221Sahoka  *    notice, this list of conditions and the following disclaimer.
172b6ee221Sahoka  * 2. Redistributions in binary form must reproduce the above copyright
182b6ee221Sahoka  *    notice, this list of conditions and the following disclaimer in the
192b6ee221Sahoka  *    documentation and/or other materials provided with the distribution.
202b6ee221Sahoka  *
212b6ee221Sahoka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
222b6ee221Sahoka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
232b6ee221Sahoka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
242b6ee221Sahoka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
252b6ee221Sahoka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
262b6ee221Sahoka  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
272b6ee221Sahoka  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
282b6ee221Sahoka  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
292b6ee221Sahoka  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
302b6ee221Sahoka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
312b6ee221Sahoka  * SUCH DAMAGE.
322b6ee221Sahoka  */
332b6ee221Sahoka 
3433f9a384Srillig #include <sys/cdefs.h>
35*e040c241Srillig __RCSID("$NetBSD: flashctl.c,v 1.11 2024/05/13 20:38:05 rillig Exp $");
3633f9a384Srillig 
372b6ee221Sahoka #include <sys/ioctl.h>
382b6ee221Sahoka #include <sys/flashio.h>
392b6ee221Sahoka #include <fcntl.h>
402b6ee221Sahoka #include <stdlib.h>
412b6ee221Sahoka #include <stdio.h>
422b6ee221Sahoka #include <err.h>
432b6ee221Sahoka #include <string.h>
442b6ee221Sahoka #include <unistd.h>
452b6ee221Sahoka #include <limits.h>
462b6ee221Sahoka #include <inttypes.h>
472b6ee221Sahoka #include <ctype.h>
482b6ee221Sahoka #include <errno.h>
492b6ee221Sahoka 
502b6ee221Sahoka 
5133f9a384Srillig static void usage(void);
5233f9a384Srillig static int to_intmax(intmax_t *, const char *);
532b6ee221Sahoka 
542b6ee221Sahoka int
main(int argc,char ** argv)552b6ee221Sahoka main(int argc, char **argv)
562b6ee221Sahoka {
572b6ee221Sahoka 	char *device, *command;
582b6ee221Sahoka 	int fd, error = 0;
592b6ee221Sahoka 	intmax_t n = -1;
602b6ee221Sahoka 
612b6ee221Sahoka 	setprogname(argv[0]);
622b6ee221Sahoka 
63ab7aeea3Sahoka 	if (argc < 3) {
642b6ee221Sahoka 		usage();
65ab7aeea3Sahoka 		exit(1);
66ab7aeea3Sahoka 	}
672b6ee221Sahoka 
682b6ee221Sahoka 	device = argv[1];
692b6ee221Sahoka 	command = argv[2];
702b6ee221Sahoka 	argc -= 3;
712b6ee221Sahoka 	argv += 3;
722b6ee221Sahoka 
732b6ee221Sahoka 	fd = open(device, O_RDWR, 0);
74ab7aeea3Sahoka 	if (fd == -1) {
752b6ee221Sahoka 		err(EXIT_FAILURE, "can't open flash device");
76ab7aeea3Sahoka 	}
772b6ee221Sahoka 
788f0dc85cSrillig 	if (strcmp("erase", command) == 0) {
792b6ee221Sahoka 		struct flash_info_params ip;
802b6ee221Sahoka 		struct flash_erase_params ep;
812b6ee221Sahoka 
822b6ee221Sahoka 		error = ioctl(fd, FLASH_GET_INFO, &ip);
838f0dc85cSrillig 		if (error != 0) {
842b6ee221Sahoka 			warn("ioctl: FLASH_GET_INFO");
852b6ee221Sahoka 			goto out;
862b6ee221Sahoka 		}
872b6ee221Sahoka 
882b6ee221Sahoka 		if (argc == 2) {
892b6ee221Sahoka 			error = to_intmax(&n, argv[0]);
908f0dc85cSrillig 			if (error != 0) {
91e2f3fd3fSjoerg 				warnx("%s", strerror(error));
922b6ee221Sahoka 				goto out;
932b6ee221Sahoka 			}
942b6ee221Sahoka 			ep.ep_addr = n;
952b6ee221Sahoka 
968f0dc85cSrillig 			if (strcmp("all", argv[1]) == 0) {
972b6ee221Sahoka 				ep.ep_len = ip.ip_flash_size;
982b6ee221Sahoka 			} else {
992b6ee221Sahoka 				error = to_intmax(&n, argv[1]);
1008f0dc85cSrillig 				if (error != 0) {
101e2f3fd3fSjoerg 					warnx("%s", strerror(error));
1022b6ee221Sahoka 					goto out;
1032b6ee221Sahoka 				}
1042b6ee221Sahoka 				ep.ep_len = n;
1052b6ee221Sahoka 			}
1062b6ee221Sahoka 		} else {
1072b6ee221Sahoka 			warnx("invalid number of arguments");
1082b6ee221Sahoka 			error = 1;
1092b6ee221Sahoka 			goto out;
1102b6ee221Sahoka 		}
1112b6ee221Sahoka 
1122b6ee221Sahoka 		printf("Erasing %jx bytes starting from %jx\n",
1132b6ee221Sahoka 		    (uintmax_t)ep.ep_len, (uintmax_t)ep.ep_addr);
1142b6ee221Sahoka 
1152b6ee221Sahoka 		error = ioctl(fd, FLASH_ERASE_BLOCK, &ep);
1168f0dc85cSrillig 		if (error != 0) {
1172b6ee221Sahoka 			warn("ioctl: FLASH_ERASE_BLOCK");
1182b6ee221Sahoka 			goto out;
1192b6ee221Sahoka 		}
1208f0dc85cSrillig 	} else if (strcmp("identify", command) == 0) {
1212b6ee221Sahoka 		struct flash_info_params ip;
1222b6ee221Sahoka 
1232b6ee221Sahoka 		error = ioctl(fd, FLASH_GET_INFO, &ip);
1248f0dc85cSrillig 		if (error != 0) {
1252b6ee221Sahoka 			warn("ioctl: FLASH_GET_INFO");
1262b6ee221Sahoka 			goto out;
1272b6ee221Sahoka 		}
1282b6ee221Sahoka 
1292b6ee221Sahoka 		printf("Device type: ");
1302b6ee221Sahoka 		switch (ip.ip_flash_type) {
1312b6ee221Sahoka 		case FLASH_TYPE_NOR:
1322b6ee221Sahoka 			printf("NOR flash");
1332b6ee221Sahoka 			break;
1342b6ee221Sahoka 		case FLASH_TYPE_NAND:
1352b6ee221Sahoka 			printf("NAND flash");
1362b6ee221Sahoka 			break;
1372b6ee221Sahoka 		default:
1382b6ee221Sahoka 			printf("unknown (%d)", ip.ip_flash_type);
1392b6ee221Sahoka 		}
1402b6ee221Sahoka 		printf("\n");
1412b6ee221Sahoka 
1422b6ee221Sahoka 		/* TODO: humanize */
14376a36edbSahoka 		printf("Capacity %jd Mbytes, %jd pages, %ju bytes/page\n",
1442b6ee221Sahoka 		    (intmax_t)ip.ip_flash_size / 1024 / 1024,
1452b6ee221Sahoka 		    (intmax_t)ip.ip_flash_size / ip.ip_page_size,
14676a36edbSahoka 		    (intmax_t)ip.ip_page_size);
1472b6ee221Sahoka 
1482b6ee221Sahoka 		if (ip.ip_flash_type == FLASH_TYPE_NAND) {
1492b6ee221Sahoka 			printf("Block size %jd Kbytes, %jd pages/block\n",
1502b6ee221Sahoka 			    (intmax_t)ip.ip_erase_size / 1024,
1512b6ee221Sahoka 			    (intmax_t)ip.ip_erase_size / ip.ip_page_size);
1522b6ee221Sahoka 		}
1538f0dc85cSrillig 	} else if (strcmp("badblocks", command) == 0) {
1542b6ee221Sahoka 		struct flash_info_params ip;
1552b6ee221Sahoka 		struct flash_badblock_params bbp;
15676a36edbSahoka 		flash_off_t addr;
1572b6ee221Sahoka 		bool hasbad = false;
1582b6ee221Sahoka 
1592b6ee221Sahoka 		error = ioctl(fd, FLASH_GET_INFO, &ip);
1608f0dc85cSrillig 		if (error != 0) {
1612b6ee221Sahoka 			warn("ioctl: FLASH_GET_INFO");
1622b6ee221Sahoka 			goto out;
1632b6ee221Sahoka 		}
1642b6ee221Sahoka 
1652b6ee221Sahoka 		printf("Scanning for bad blocks: ");
1662b6ee221Sahoka 
1672b6ee221Sahoka 		addr = 0;
1682b6ee221Sahoka 		while (addr < ip.ip_flash_size) {
1692b6ee221Sahoka 			bbp.bbp_addr = addr;
1702b6ee221Sahoka 
1712b6ee221Sahoka 			error = ioctl(fd, FLASH_BLOCK_ISBAD, &bbp);
1728f0dc85cSrillig 			if (error != 0) {
1732b6ee221Sahoka 				warn("ioctl: FLASH_BLOCK_ISBAD");
1742b6ee221Sahoka 				goto out;
1752b6ee221Sahoka 			}
1762b6ee221Sahoka 
1772b6ee221Sahoka 			if (bbp.bbp_isbad) {
1782b6ee221Sahoka 				hasbad = true;
1792b6ee221Sahoka 				printf("0x%jx ", addr);
1802b6ee221Sahoka 			}
1812b6ee221Sahoka 
1822b6ee221Sahoka 			addr += ip.ip_erase_size;
1832b6ee221Sahoka 		}
1842b6ee221Sahoka 
185ab7aeea3Sahoka 		if (hasbad) {
1862b6ee221Sahoka 			printf("Done.\n");
187ab7aeea3Sahoka 		} else {
1882b6ee221Sahoka 			printf("No bad blocks found.\n");
189ab7aeea3Sahoka 		}
1908f0dc85cSrillig 	} else if (strcmp("markbad", command) == 0) {
19176a36edbSahoka 		flash_off_t address;
1922b6ee221Sahoka 
193ab7aeea3Sahoka 		/* TODO: maybe we should let the user specify
194ab7aeea3Sahoka 		 * multiple blocks?
195ab7aeea3Sahoka 		 */
196ab7aeea3Sahoka 		if (argc != 1) {
197ab7aeea3Sahoka 			warnx("invalid number of arguments");
198ab7aeea3Sahoka 			error = 1;
199ab7aeea3Sahoka 			goto out;
200ab7aeea3Sahoka 		}
201ab7aeea3Sahoka 
202ab7aeea3Sahoka 		error = to_intmax(&n, argv[0]);
2038f0dc85cSrillig 		if (error != 0) {
204e2f3fd3fSjoerg 			warnx("%s", strerror(error));
2052b6ee221Sahoka 			goto out;
2062b6ee221Sahoka 		}
2072b6ee221Sahoka 
2082b6ee221Sahoka 		address = n;
2092b6ee221Sahoka 
2102b6ee221Sahoka 		printf("Marking block 0x%jx as bad.\n",
2112b6ee221Sahoka 		    (intmax_t)address);
2122b6ee221Sahoka 
2132b6ee221Sahoka 		error = ioctl(fd, FLASH_BLOCK_MARKBAD, &address);
2148f0dc85cSrillig 		if (error != 0) {
2152b6ee221Sahoka 			warn("ioctl: FLASH_BLOCK_MARKBAD");
2162b6ee221Sahoka 			goto out;
2172b6ee221Sahoka 		}
2182b6ee221Sahoka 	} else {
2192b6ee221Sahoka 		warnx("Unknown command");
2208f0dc85cSrillig 		error = EXIT_FAILURE;
2212b6ee221Sahoka 		goto out;
2222b6ee221Sahoka 	}
2232b6ee221Sahoka 
2242b6ee221Sahoka out:
2252b6ee221Sahoka 	close(fd);
2262b6ee221Sahoka 	return error;
2272b6ee221Sahoka }
2282b6ee221Sahoka 
2292b6ee221Sahoka int
to_intmax(intmax_t * num,const char * str)2302b6ee221Sahoka to_intmax(intmax_t *num, const char *str)
2312b6ee221Sahoka {
2322b6ee221Sahoka 	char *endptr;
2332b6ee221Sahoka 
2342b6ee221Sahoka 	errno = 0;
2358d4a11b7Srillig 	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
236*e040c241Srillig 		if (!isxdigit((unsigned char)str[2]))
2372b6ee221Sahoka 			return EINVAL;
2382b6ee221Sahoka 		*num = strtoimax(str, &endptr, 16);
2392b6ee221Sahoka 	} else {
240*e040c241Srillig 		if (!isdigit((unsigned char)str[0]))
2412b6ee221Sahoka 			return EINVAL;
2422b6ee221Sahoka 		*num = strtoimax(str, &endptr, 10);
2432b6ee221Sahoka 	}
2442b6ee221Sahoka 
2452b6ee221Sahoka 	if (errno == ERANGE && (*num == INTMAX_MIN || *num == INTMAX_MAX)) {
2462b6ee221Sahoka 		return ERANGE;
2472b6ee221Sahoka 	}
2482b6ee221Sahoka 
2492b6ee221Sahoka 	return 0;
2502b6ee221Sahoka }
2512b6ee221Sahoka 
2522b6ee221Sahoka void
usage(void)2532b6ee221Sahoka usage(void)
2542b6ee221Sahoka {
2553dab7e4bSrillig 	fprintf(stderr, "usage: %s <device> identify\n",
2562b6ee221Sahoka 	    getprogname());
2573dab7e4bSrillig 	fprintf(stderr, "       %s <device> erase <start address> <size>|all\n",
2582b6ee221Sahoka 	    getprogname());
2593dab7e4bSrillig 	fprintf(stderr, "       %s <device> badblocks\n",
2602b6ee221Sahoka 	    getprogname());
2613dab7e4bSrillig 	fprintf(stderr, "       %s <device> markbad <address>\n",
2622b6ee221Sahoka 	    getprogname());
2632b6ee221Sahoka }
264