2023-01-08 18:49:51 +03:00
|
|
|
/* $NetBSD: flashctl.c,v 1.6 2023/01/08 15:49:51 rillig Exp $ */
|
2011-02-26 21:07:13 +03:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2010 Department of Software Engineering,
|
|
|
|
* University of Szeged, Hungary
|
|
|
|
* Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org>
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by the Department of Software Engineering, University of Szeged, Hungary
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/flashio.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <err.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
|
|
void usage(void);
|
|
|
|
int to_intmax(intmax_t *, const char *);
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *device, *command;
|
|
|
|
int fd, error = 0;
|
|
|
|
intmax_t n = -1;
|
|
|
|
|
|
|
|
setprogname(argv[0]);
|
|
|
|
|
2011-03-20 09:10:27 +03:00
|
|
|
if (argc < 3) {
|
2011-02-26 21:07:13 +03:00
|
|
|
usage();
|
2011-03-20 09:10:27 +03:00
|
|
|
exit(1);
|
|
|
|
}
|
2011-02-26 21:07:13 +03:00
|
|
|
|
|
|
|
device = argv[1];
|
|
|
|
command = argv[2];
|
|
|
|
argc -= 3;
|
|
|
|
argv += 3;
|
|
|
|
|
|
|
|
fd = open(device, O_RDWR, 0);
|
2011-03-20 09:10:27 +03:00
|
|
|
if (fd == -1) {
|
2011-02-26 21:07:13 +03:00
|
|
|
err(EXIT_FAILURE, "can't open flash device");
|
2011-03-20 09:10:27 +03:00
|
|
|
}
|
2011-02-26 21:07:13 +03:00
|
|
|
|
2023-01-08 18:49:51 +03:00
|
|
|
if (strcmp("erase", command) == 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
struct flash_info_params ip;
|
|
|
|
struct flash_erase_params ep;
|
|
|
|
|
|
|
|
error = ioctl(fd, FLASH_GET_INFO, &ip);
|
2023-01-08 18:49:51 +03:00
|
|
|
if (error != 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
warn("ioctl: FLASH_GET_INFO");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc == 2) {
|
|
|
|
error = to_intmax(&n, argv[0]);
|
2023-01-08 18:49:51 +03:00
|
|
|
if (error != 0) {
|
2011-05-24 17:01:53 +04:00
|
|
|
warnx("%s", strerror(error));
|
2011-02-26 21:07:13 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ep.ep_addr = n;
|
|
|
|
|
2023-01-08 18:49:51 +03:00
|
|
|
if (strcmp("all", argv[1]) == 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
ep.ep_len = ip.ip_flash_size;
|
|
|
|
} else {
|
|
|
|
error = to_intmax(&n, argv[1]);
|
2023-01-08 18:49:51 +03:00
|
|
|
if (error != 0) {
|
2011-05-24 17:01:53 +04:00
|
|
|
warnx("%s", strerror(error));
|
2011-02-26 21:07:13 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ep.ep_len = n;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warnx("invalid number of arguments");
|
|
|
|
error = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
2023-01-08 18:49:51 +03:00
|
|
|
|
2011-02-26 21:07:13 +03:00
|
|
|
printf("Erasing %jx bytes starting from %jx\n",
|
2023-01-08 18:49:51 +03:00
|
|
|
(uintmax_t)ep.ep_len, (uintmax_t)ep.ep_addr);
|
|
|
|
|
2011-02-26 21:07:13 +03:00
|
|
|
error = ioctl(fd, FLASH_ERASE_BLOCK, &ep);
|
2023-01-08 18:49:51 +03:00
|
|
|
if (error != 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
warn("ioctl: FLASH_ERASE_BLOCK");
|
|
|
|
goto out;
|
|
|
|
}
|
2023-01-08 18:49:51 +03:00
|
|
|
} else if (strcmp("identify", command) == 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
struct flash_info_params ip;
|
2023-01-08 18:49:51 +03:00
|
|
|
|
2011-02-26 21:07:13 +03:00
|
|
|
error = ioctl(fd, FLASH_GET_INFO, &ip);
|
2023-01-08 18:49:51 +03:00
|
|
|
if (error != 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
warn("ioctl: FLASH_GET_INFO");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Device type: ");
|
|
|
|
switch (ip.ip_flash_type) {
|
|
|
|
case FLASH_TYPE_NOR:
|
|
|
|
printf("NOR flash");
|
|
|
|
break;
|
|
|
|
case FLASH_TYPE_NAND:
|
|
|
|
printf("NAND flash");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("unknown (%d)", ip.ip_flash_type);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
/* TODO: humanize */
|
2011-04-04 22:30:07 +04:00
|
|
|
printf("Capacity %jd Mbytes, %jd pages, %ju bytes/page\n",
|
2023-01-08 18:49:51 +03:00
|
|
|
(intmax_t)ip.ip_flash_size / 1024 / 1024,
|
|
|
|
(intmax_t)ip.ip_flash_size / ip.ip_page_size,
|
|
|
|
(intmax_t)ip.ip_page_size);
|
2011-02-26 21:07:13 +03:00
|
|
|
|
|
|
|
if (ip.ip_flash_type == FLASH_TYPE_NAND) {
|
|
|
|
printf("Block size %jd Kbytes, %jd pages/block\n",
|
2023-01-08 18:49:51 +03:00
|
|
|
(intmax_t)ip.ip_erase_size / 1024,
|
|
|
|
(intmax_t)ip.ip_erase_size / ip.ip_page_size);
|
2011-02-26 21:07:13 +03:00
|
|
|
}
|
2023-01-08 18:49:51 +03:00
|
|
|
} else if (strcmp("badblocks", command) == 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
struct flash_info_params ip;
|
|
|
|
struct flash_badblock_params bbp;
|
2011-04-04 22:30:07 +04:00
|
|
|
flash_off_t addr;
|
2011-02-26 21:07:13 +03:00
|
|
|
bool hasbad = false;
|
|
|
|
|
|
|
|
error = ioctl(fd, FLASH_GET_INFO, &ip);
|
2023-01-08 18:49:51 +03:00
|
|
|
if (error != 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
warn("ioctl: FLASH_GET_INFO");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Scanning for bad blocks: ");
|
|
|
|
|
|
|
|
addr = 0;
|
|
|
|
while (addr < ip.ip_flash_size) {
|
|
|
|
bbp.bbp_addr = addr;
|
2023-01-08 18:49:51 +03:00
|
|
|
|
2011-02-26 21:07:13 +03:00
|
|
|
error = ioctl(fd, FLASH_BLOCK_ISBAD, &bbp);
|
2023-01-08 18:49:51 +03:00
|
|
|
if (error != 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
warn("ioctl: FLASH_BLOCK_ISBAD");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bbp.bbp_isbad) {
|
|
|
|
hasbad = true;
|
|
|
|
printf("0x%jx ", addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
addr += ip.ip_erase_size;
|
|
|
|
}
|
|
|
|
|
2011-03-20 09:10:27 +03:00
|
|
|
if (hasbad) {
|
2011-02-26 21:07:13 +03:00
|
|
|
printf("Done.\n");
|
2011-03-20 09:10:27 +03:00
|
|
|
} else {
|
2011-02-26 21:07:13 +03:00
|
|
|
printf("No bad blocks found.\n");
|
2011-03-20 09:10:27 +03:00
|
|
|
}
|
2023-01-08 18:49:51 +03:00
|
|
|
} else if (strcmp("markbad", command) == 0) {
|
2011-04-04 22:30:07 +04:00
|
|
|
flash_off_t address;
|
2011-03-20 09:10:27 +03:00
|
|
|
|
|
|
|
/* TODO: maybe we should let the user specify
|
|
|
|
* multiple blocks?
|
|
|
|
*/
|
|
|
|
if (argc != 1) {
|
|
|
|
warnx("invalid number of arguments");
|
|
|
|
error = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
2023-01-08 18:49:51 +03:00
|
|
|
|
2011-03-20 09:10:27 +03:00
|
|
|
error = to_intmax(&n, argv[0]);
|
2023-01-08 18:49:51 +03:00
|
|
|
if (error != 0) {
|
2011-05-24 17:01:53 +04:00
|
|
|
warnx("%s", strerror(error));
|
2011-02-26 21:07:13 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
address = n;
|
2023-01-08 18:49:51 +03:00
|
|
|
|
2011-02-26 21:07:13 +03:00
|
|
|
printf("Marking block 0x%jx as bad.\n",
|
2023-01-08 18:49:51 +03:00
|
|
|
(intmax_t)address);
|
2011-02-26 21:07:13 +03:00
|
|
|
|
|
|
|
error = ioctl(fd, FLASH_BLOCK_MARKBAD, &address);
|
2023-01-08 18:49:51 +03:00
|
|
|
if (error != 0) {
|
2011-02-26 21:07:13 +03:00
|
|
|
warn("ioctl: FLASH_BLOCK_MARKBAD");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warnx("Unknown command");
|
2023-01-08 18:49:51 +03:00
|
|
|
error = EXIT_FAILURE;
|
2011-02-26 21:07:13 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
close(fd);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
to_intmax(intmax_t *num, const char *str)
|
|
|
|
{
|
|
|
|
char *endptr;
|
|
|
|
|
|
|
|
errno = 0;
|
2023-01-08 18:37:56 +03:00
|
|
|
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
|
|
|
|
if (!isxdigit((unsigned char)str[2]))
|
2011-02-26 21:07:13 +03:00
|
|
|
return EINVAL;
|
|
|
|
*num = strtoimax(str, &endptr, 16);
|
|
|
|
} else {
|
2023-01-08 18:37:56 +03:00
|
|
|
if (!isdigit((unsigned char)str[0]))
|
2011-02-26 21:07:13 +03:00
|
|
|
return EINVAL;
|
|
|
|
*num = strtoimax(str, &endptr, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errno == ERANGE && (*num == INTMAX_MIN || *num == INTMAX_MAX)) {
|
|
|
|
return ERANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
usage(void)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "usage: %s device identify\n",
|
|
|
|
getprogname());
|
|
|
|
fprintf(stderr, " %s device erase <start address> <size>|all\n",
|
|
|
|
getprogname());
|
|
|
|
fprintf(stderr, " %s device badblocks\n",
|
|
|
|
getprogname());
|
|
|
|
fprintf(stderr, " %s device markbad <address>\n",
|
|
|
|
getprogname());
|
|
|
|
}
|