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