9
0
Fork 0

Merge branch 'for-next/imx-bbu-nand-fcb'

This commit is contained in:
Sascha Hauer 2016-04-08 13:37:28 +02:00
commit fd50a8d758
37 changed files with 2134 additions and 1325 deletions

View File

@ -30,3 +30,11 @@ commands. The exact commands are board specific.
**NOTE** barebox images can be enriched with metadata which can be used to check
if a given image is suitable for updating barebox, see :ref:`imd`.
Repairing existing boot images
------------------------------
Some SoCs allow to store multiple boot images on a device in order to
improve robustness. When an update handler supports it the handler can
repair and/or refresh an image from this redundant information. This is
done with the '-r' option to :ref:`command_barebox_update`.

View File

@ -694,7 +694,6 @@ config CMD_UBI
config CMD_UBIFORMAT
tristate
depends on MTD_UBI
select LIBMTD
select LIBSCAN
select LIBUBIGEN
prompt "ubiformat"
@ -1909,6 +1908,23 @@ config CMD_NANDTEST
-o OFFS start offset on flash
-l LEN length of flash to test
config CMD_NAND_BITFLIP
tristate
depends on NAND
prompt "nand_bitflip"
help
nand_bitflip - Create bitflips on Nand pages. This command is useful for testing
purposes.
Usage: nand_bitflip NANDDEV
This command creates bitflips on Nand pages.
Options:
-b <block> block to work on
-o <offset> offset in Nand
-r flip random bits
-n <numbitflips> Specify maximum number of bitflips to generate
config CMD_POWEROFF
tristate
depends on HAS_POWEROFF

View File

@ -116,3 +116,4 @@ obj-$(CONFIG_CMD_DHCP) += dhcp.o
obj-$(CONFIG_CMD_DHRYSTONE) += dhrystone.o
obj-$(CONFIG_CMD_SPD_DECODE) += spd_decode.o
obj-$(CONFIG_CMD_MMC_EXTCSD) += mmc_extcsd.o
obj-$(CONFIG_CMD_NAND_BITFLIP) += nand-bitflip.o

View File

@ -26,10 +26,10 @@
static int do_barebox_update(int argc, char *argv[])
{
int opt, ret;
int opt, ret, repair = 0;
struct bbu_data data = {};
while ((opt = getopt(argc, argv, "t:yf:ld:")) > 0) {
while ((opt = getopt(argc, argv, "t:yf:ld:r")) > 0) {
switch (opt) {
case 'd':
data.devicefile = optarg;
@ -48,19 +48,24 @@ static int do_barebox_update(int argc, char *argv[])
printf("registered update handlers:\n");
bbu_handlers_list();
return 0;
case 'r':
repair = 1;
break;
default:
return COMMAND_ERROR_USAGE;
}
}
if (!(argc - optind))
return COMMAND_ERROR_USAGE;
if (argc - optind > 0) {
data.imagefile = argv[optind];
data.imagefile = argv[optind];
data.image = read_file(data.imagefile, &data.len);
if (!data.image)
return -errno;
data.image = read_file(data.imagefile, &data.len);
if (!data.image)
return -errno;
} else {
if (!repair)
return COMMAND_ERROR_USAGE;
}
ret = barebox_update(&data);
@ -74,6 +79,7 @@ BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT("-l\t", "list registered targets")
BAREBOX_CMD_HELP_OPT("-t TARGET", "specify data target handler name")
BAREBOX_CMD_HELP_OPT("-d DEVICE", "write image to DEVICE")
BAREBOX_CMD_HELP_OPT("-r\t", "refresh or repair. Do not update, but repair an existing image")
BAREBOX_CMD_HELP_OPT("-y\t", "autom. use 'yes' when asking confirmations")
BAREBOX_CMD_HELP_OPT("-f LEVEL", "set force level")
BAREBOX_CMD_HELP_END
@ -81,7 +87,7 @@ BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(barebox_update)
.cmd = do_barebox_update,
BAREBOX_CMD_DESC("update barebox to persistent media")
BAREBOX_CMD_OPTS("[-ltdyf] [IMAGE]")
BAREBOX_CMD_OPTS("[-ltdyfr] [IMAGE]")
BAREBOX_CMD_GROUP(CMD_GRP_MISC)
BAREBOX_CMD_HELP(cmd_barebox_update_help)
BAREBOX_CMD_END

117
commands/nand-bitflip.c Normal file
View File

@ -0,0 +1,117 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <common.h>
#include <command.h>
#include <malloc.h>
#include <getopt.h>
#include <fs.h>
#include <fcntl.h>
#include <ioctl.h>
#include <linux/mtd/mtd.h>
#include <mtd/mtd-peb.h>
static int do_nand_bitflip(int argc, char *argv[])
{
int opt, ret, fd;
static struct mtd_info_user meminfo;
int block = 0;
int random = 0;
int num_bitflips = 1;
loff_t offset = 0, roffset;
int check = 0;
size_t r;
void *buf;
while ((opt = getopt(argc, argv, "b:rn:o:c")) > 0) {
switch (opt) {
case 'r':
random = 1;
break;
case 'n':
num_bitflips = simple_strtoul(optarg, NULL, 0);
break;
case 'b':
block = simple_strtoul(optarg, NULL, 0);
break;
case 'o':
offset = simple_strtoull(optarg, NULL, 0);
break;
case 'c':
check = 1;
break;
default:
return COMMAND_ERROR_USAGE;
}
}
if (optind >= argc)
return COMMAND_ERROR_USAGE;
fd = open(argv[optind], O_RDWR);
if (fd < 0)
return fd;
ret = ioctl(fd, MEMGETINFO, &meminfo);
close(fd);
if (ret)
return ret;
block += mtd_div_by_eb(offset, meminfo.mtd);
offset = mtd_mod_by_eb(offset, meminfo.mtd);
if (!check) {
ret = mtd_peb_create_bitflips(meminfo.mtd, block, offset, meminfo.writesize,
num_bitflips, random, 1);
if (ret) {
printf("Creating bitflips failed with: %s\n", strerror(-ret));
return ret;
}
}
buf = xzalloc(meminfo.writesize);
roffset = (loff_t)block * meminfo.mtd->erasesize + offset;
ret = meminfo.mtd->read(meminfo.mtd, roffset, meminfo.writesize, &r, buf);
if (ret > 0) {
printf("page at block %d, offset 0x%08llx has %d bitflips%s\n",
block, offset, ret,
ret >= meminfo.mtd->bitflip_threshold ? ", needs cleanup" : "");
} else if (!ret) {
printf("No bitflips found on block %d, offset 0x%08llx\n", block, offset);
} else {
printf("Reading block %d, offset 0x%08llx failed with: %s\n", block, offset,
strerror(-ret));
}
free(buf);
return 0;
}
BAREBOX_CMD_HELP_START(nand_bitflip)
BAREBOX_CMD_HELP_TEXT("This command creates bitflips on Nand pages.")
BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT ("-b <block>", "block to work on")
BAREBOX_CMD_HELP_OPT ("-o <offset>", "offset in Nand")
BAREBOX_CMD_HELP_OPT ("-r\t", "flip random bits")
BAREBOX_CMD_HELP_OPT ("-n <numbitflips>", "Specify maximum number of bitflips to generate")
BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(nand_bitflip)
.cmd = do_nand_bitflip,
BAREBOX_CMD_DESC("Create bitflips on Nand pages")
BAREBOX_CMD_OPTS("NANDDEV")
BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
BAREBOX_CMD_HELP(cmd_nand_bitflip_help)
BAREBOX_CMD_END

View File

@ -32,14 +32,19 @@
#define NAND_ADD 1
#define NAND_DEL 2
#define NAND_MARKBAD 3
#define NAND_MARKGOOD 4
#define NAND_INFO 5
static int do_nand(int argc, char *argv[])
{
int opt;
int command = 0;
loff_t badblock = 0;
int fd;
int ret;
struct mtd_info_user mtdinfo;
while((opt = getopt(argc, argv, "adb:")) > 0) {
while((opt = getopt(argc, argv, "adb:g:i")) > 0) {
if (command) {
printf("only one command may be given\n");
return 1;
@ -55,12 +60,27 @@ static int do_nand(int argc, char *argv[])
case 'b':
command = NAND_MARKBAD;
badblock = strtoull_suffix(optarg, NULL, 0);
break;
case 'g':
command = NAND_MARKGOOD;
badblock = strtoull_suffix(optarg, NULL, 0);
break;
case 'i':
command = NAND_INFO;
break;
default:
return COMMAND_ERROR_USAGE;
}
}
if (optind >= argc)
return COMMAND_ERROR_USAGE;
if (!command) {
printf("No action given\n");
return COMMAND_ERROR_USAGE;
}
if (command == NAND_ADD) {
while (optind < argc) {
if (dev_add_bb_dev(basename(argv[optind]), NULL))
@ -77,19 +97,32 @@ static int do_nand(int argc, char *argv[])
}
}
if (command == NAND_MARKBAD) {
int ret = 0, fd;
fd = open(argv[optind], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
printf("marking block at 0x%08llx on %s as bad\n",
badblock, argv[optind]);
ret = ioctl(fd, MEMGETINFO, &mtdinfo);
if (ret)
goto out;
fd = open(argv[optind], O_RDWR);
if (fd < 0) {
perror("open");
return 1;
if (command == NAND_MARKBAD || command == NAND_MARKGOOD) {
const char *str;
int ctl;
if (command == NAND_MARKBAD) {
str = "bad";
ctl = MEMSETBADBLOCK;
} else {
str = "good";
ctl = MEMSETGOODBLOCK;
}
ret = ioctl(fd, MEMSETBADBLOCK, &badblock);
printf("marking block at 0x%08llx on %s as %s\n",
badblock, argv[optind], str);
ret = ioctl(fd, ctl, &badblock);
if (ret) {
if (ret == -EINVAL)
printf("Maybe offset %lld is out of range.\n",
@ -98,11 +131,29 @@ static int do_nand(int argc, char *argv[])
perror("ioctl");
}
close(fd);
return ret;
goto out;
}
return 0;
if (command == NAND_INFO) {
loff_t ofs;
int bad = 0;
for (ofs = 0; ofs < mtdinfo.size; ofs += mtdinfo.erasesize) {
if (ioctl(fd, MEMGETBADBLOCK, &ofs)) {
printf("Block at 0x%08llx is bad\n", ofs);
bad = 1;
}
}
if (!bad)
printf("No bad blocks\n");
}
ret = 0;
out:
close(fd);
return ret;
}
BAREBOX_CMD_HELP_START(nand)
@ -110,6 +161,8 @@ BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT ("-a", "register a bad block aware device ontop of a normal NAND device")
BAREBOX_CMD_HELP_OPT ("-d", "deregister a bad block aware device")
BAREBOX_CMD_HELP_OPT ("-b OFFS", "mark block at OFFSet as bad")
BAREBOX_CMD_HELP_OPT ("-g OFFS", "mark block at OFFSet as good")
BAREBOX_CMD_HELP_OPT ("-i", "info. Show information about bad blocks")
BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(nand)

View File

@ -46,12 +46,12 @@
#include <linux/stat.h>
#include <linux/log2.h>
#include <linux/mtd/mtd-abi.h>
#include <mtd/libmtd.h>
#include <mtd/libscan.h>
#include <mtd/libubigen.h>
#include <mtd/ubi-user.h>
#include <mtd/utils.h>
#include <mtd/ubi-media.h>
#include <mtd/mtd-peb.h>
/* The variables below are set by command line arguments */
struct args {
@ -68,7 +68,6 @@ struct args {
long long ec;
const char *image;
const char *node;
int node_fd;
};
static struct args args;
@ -166,16 +165,18 @@ static int parse_opt(int argc, char *argv[])
return 0;
}
static void print_bad_eraseblocks(const struct mtd_dev_info *mtd,
static void print_bad_eraseblocks(struct mtd_info *mtd,
const struct ubi_scan_info *si)
{
int first = 1, eb;
int first = 1, eb, eb_cnt;
eb_cnt = mtd_div_by_eb(mtd->size, mtd);
if (si->bad_cnt == 0)
return;
normsg_cont("%d bad eraseblocks found, numbers: ", si->bad_cnt);
for (eb = 0; eb < mtd->eb_cnt; eb++) {
for (eb = 0; eb < eb_cnt; eb++) {
if (si->ec[eb] != EB_BAD)
continue;
if (first) {
@ -210,7 +211,7 @@ static int change_ech(struct ubi_ec_hdr *hdr, uint32_t image_seq,
return 0;
}
static int drop_ffs(const struct mtd_dev_info *mtd, const void *buf, int len)
static int drop_ffs(struct mtd_info *mtd, const void *buf, int len)
{
int i;
@ -220,8 +221,8 @@ static int drop_ffs(const struct mtd_dev_info *mtd, const void *buf, int len)
/* The resulting length must be aligned to the minimum flash I/O size */
len = i + 1;
len = (len + mtd->min_io_size - 1) / mtd->min_io_size;
len *= mtd->min_io_size;
len = (len + mtd->writesize - 1) / mtd->writesize;
len *= mtd->writesize;
return len;
}
@ -271,20 +272,20 @@ static int consecutive_bad_check(int eb)
}
/* TODO: we should actually torture the PEB before marking it as bad */
static int mark_bad(const struct mtd_dev_info *mtd, struct ubi_scan_info *si, int eb)
static int mark_bad(struct mtd_info *mtd, struct ubi_scan_info *si, int eb)
{
int err;
if (!args.quiet)
normsg_cont("marking block %d bad\n", eb);
if (!mtd->bb_allowed) {
if (!mtd_can_have_bb(mtd)) {
if (!args.quiet)
printf("\n");
return errmsg("bad blocks not supported by this flash");
}
err = mtd_mark_bad(mtd, args.node_fd, eb);
err = mtd_peb_mark_bad(mtd, eb);
if (err)
return err;
@ -294,24 +295,26 @@ static int mark_bad(const struct mtd_dev_info *mtd, struct ubi_scan_info *si, in
return consecutive_bad_check(eb);
}
static int flash_image(const struct mtd_dev_info *mtd,
static int flash_image(struct mtd_info *mtd,
const struct ubigen_info *ui, struct ubi_scan_info *si)
{
int fd, img_ebs, eb, written_ebs = 0, ret = -1;
int fd, img_ebs, eb, written_ebs = 0, ret = -1, eb_cnt;
off_t st_size;
char *buf = NULL;
eb_cnt = mtd_num_pebs(mtd);
fd = open_file(&st_size);
if (fd < 0)
return fd;
buf = malloc(mtd->eb_size);
buf = malloc(mtd->erasesize);
if (!buf) {
sys_errmsg("cannot allocate %d bytes of memory", mtd->eb_size);
sys_errmsg("cannot allocate %d bytes of memory", mtd->erasesize);
goto out_close;
}
img_ebs = st_size / mtd->eb_size;
img_ebs = st_size / mtd->erasesize;
if (img_ebs > si->good_cnt) {
sys_errmsg("file \"%s\" is too large (%lld bytes)",
@ -319,10 +322,10 @@ static int flash_image(const struct mtd_dev_info *mtd,
goto out_close;
}
if (st_size % mtd->eb_size) {
if (st_size % mtd->erasesize) {
sys_errmsg("file \"%s\" (size %lld bytes) is not multiple of "
"eraseblock size (%d bytes)",
args.image, (long long)st_size, mtd->eb_size);
args.image, (long long)st_size, mtd->erasesize);
goto out_close;
}
@ -332,13 +335,13 @@ static int flash_image(const struct mtd_dev_info *mtd,
}
verbose(args.verbose, "will write %d eraseblocks", img_ebs);
for (eb = 0; eb < mtd->eb_cnt; eb++) {
for (eb = 0; eb < eb_cnt; eb++) {
int err, new_len;
long long ec;
if (!args.quiet && !args.verbose) {
printf("\rubiformat: flashing eraseblock %d -- %2u %% complete ",
eb, (eb + 1) * 100 / mtd->eb_cnt);
eb, (eb + 1) * 100 / eb_cnt);
}
if (si->ec[eb] == EB_BAD)
@ -348,13 +351,13 @@ static int flash_image(const struct mtd_dev_info *mtd,
normsg_cont("eraseblock %d: erase", eb);
}
err = libmtd_erase(mtd, args.node_fd, eb);
err = mtd_peb_erase(mtd, eb);
if (err) {
if (!args.quiet)
printf("\n");
sys_errmsg("failed to erase eraseblock %d", eb);
if (errno != EIO)
if (err != EIO)
goto out_close;
if (mark_bad(mtd, si, eb))
@ -363,7 +366,7 @@ static int flash_image(const struct mtd_dev_info *mtd,
continue;
}
err = read_full(fd, buf, mtd->eb_size);
err = read_full(fd, buf, mtd->erasesize);
if (err < 0) {
sys_errmsg("failed to read eraseblock %d from \"%s\"",
written_ebs, args.image);
@ -392,20 +395,21 @@ static int flash_image(const struct mtd_dev_info *mtd,
printf(", write data\n");
}
new_len = drop_ffs(mtd, buf, mtd->eb_size);
new_len = drop_ffs(mtd, buf, mtd->erasesize);
err = libmtd_write(mtd, args.node_fd, eb, 0, buf, new_len);
err = mtd_peb_write(mtd, buf, eb, 0, new_len);
if (err) {
sys_errmsg("cannot write eraseblock %d", eb);
if (errno != EIO)
if (err != EIO)
goto out_close;
err = mtd_peb_torture(mtd, eb);
if (err < 0 && err != -EIO)
goto out_close;
if (err == -EIO && consecutive_bad_check(eb))
goto out_close;
err = mtd_torture(mtd, args.node_fd, eb);
if (err) {
if (mark_bad(mtd, si, eb))
goto out_close;
}
continue;
}
if (++written_ebs >= img_ebs)
@ -423,30 +427,32 @@ out_close:
return ret;
}
static int format(const struct mtd_dev_info *mtd,
static int format(struct mtd_info *mtd,
const struct ubigen_info *ui, struct ubi_scan_info *si,
int start_eb, int novtbl)
int start_eb, int novtbl, int subpage_size)
{
int eb, err, write_size;
int eb, err, write_size, eb_cnt;
struct ubi_ec_hdr *hdr;
struct ubi_vtbl_record *vtbl;
int eb1 = -1, eb2 = -1;
long long ec1 = -1, ec2 = -1;
write_size = UBI_EC_HDR_SIZE + mtd->subpage_size - 1;
write_size /= mtd->subpage_size;
write_size *= mtd->subpage_size;
eb_cnt = mtd_num_pebs(mtd);
write_size = UBI_EC_HDR_SIZE + subpage_size - 1;
write_size /= subpage_size;
write_size *= subpage_size;
hdr = malloc(write_size);
if (!hdr)
return sys_errmsg("cannot allocate %d bytes of memory", write_size);
memset(hdr, 0xFF, write_size);
for (eb = start_eb; eb < mtd->eb_cnt; eb++) {
for (eb = start_eb; eb < eb_cnt; eb++) {
long long ec;
if (!args.quiet && !args.verbose) {
printf("\rubiformat: formatting eraseblock %d -- %2u %% complete ",
eb, (eb + 1 - start_eb) * 100 / (mtd->eb_cnt - start_eb));
eb, (eb + 1 - start_eb) * 100 / (eb_cnt - start_eb));
}
if (si->ec[eb] == EB_BAD)
@ -464,13 +470,13 @@ static int format(const struct mtd_dev_info *mtd,
normsg_cont("eraseblock %d: erase", eb);
}
err = libmtd_erase(mtd, args.node_fd, eb);
err = mtd_peb_erase(mtd, eb);
if (err) {
if (!args.quiet)
printf("\n");
sys_errmsg("failed to erase eraseblock %d", eb);
if (errno != EIO)
if (err != EIO)
goto out_free;
if (mark_bad(mtd, si, eb))
@ -495,7 +501,7 @@ static int format(const struct mtd_dev_info *mtd,
printf(", write EC %lld\n", ec);
}
err = libmtd_write(mtd, args.node_fd, eb, 0, hdr, write_size);
err = mtd_peb_write(mtd, hdr, eb, 0, write_size);
if (err) {
if (!args.quiet && !args.verbose)
printf("\n");
@ -503,16 +509,17 @@ static int format(const struct mtd_dev_info *mtd,
write_size, eb);
if (errno != EIO) {
if (args.subpage_size != mtd->min_io_size)
if (args.subpage_size != mtd->writesize)
normsg("may be sub-page size is incorrect?");
goto out_free;
}
err = mtd_torture(mtd, args.node_fd, eb);
if (err) {
if (mark_bad(mtd, si, eb))
goto out_free;
}
err = mtd_peb_torture(mtd, eb);
if (err < 0 && err != -EIO)
goto out_free;
if (err == -EIO && consecutive_bad_check(eb))
goto out_free;
continue;
}
@ -532,8 +539,7 @@ static int format(const struct mtd_dev_info *mtd,
if (!vtbl)
goto out_free;
err = ubigen_write_layout_vol(ui, eb1, eb2, ec1, ec2, vtbl,
args.node_fd);
err = ubigen_write_layout_vol(ui, eb1, eb2, ec1, ec2, vtbl, mtd);
free(vtbl);
if (err) {
errmsg("cannot write layout volume");
@ -551,78 +557,82 @@ out_free:
int do_ubiformat(int argc, char *argv[])
{
int err, verbose;
struct mtd_dev_info mtd;
int err, verbose, fd, eb_cnt;
struct mtd_info *mtd;
struct ubigen_info ui;
struct ubi_scan_info *si;
struct mtd_info_user mtd_user;
err = parse_opt(argc, argv);
if (err)
return err;
err = mtd_get_dev_info(args.node, &mtd);
if (err) {
sys_errmsg("cannot get information about \"%s\"", args.node);
goto out_close_mtd;
fd = open(args.node, O_RDWR);
if (fd < 0)
return sys_errmsg("cannot open \"%s\"", args.node);
if (ioctl(fd, MEMGETINFO, &mtd_user)) {
sys_errmsg("MEMGETINFO ioctl request failed");
goto out_close;
}
if (!is_power_of_2(mtd.min_io_size)) {
mtd = mtd_user.mtd;
if (!is_power_of_2(mtd->writesize)) {
errmsg("min. I/O size is %d, but should be power of 2",
mtd.min_io_size);
goto out_close_mtd;
mtd->writesize);
goto out_close;
}
if (args.subpage_size && args.subpage_size != mtd.subpage_size) {
mtd.subpage_size = args.subpage_size;
args.manual_subpage = 1;
if (args.subpage_size) {
if (args.subpage_size != mtd->writesize >> mtd->subpage_sft)
args.manual_subpage = 1;
} else {
args.subpage_size = mtd->writesize >> mtd->subpage_sft;
}
if (args.manual_subpage) {
/* Do some sanity check */
if (args.subpage_size > mtd.min_io_size) {
if (args.subpage_size > mtd->writesize) {
errmsg("sub-page cannot be larger than min. I/O unit");
goto out_close_mtd;
goto out_close;
}
if (mtd.min_io_size % args.subpage_size) {
if (mtd->writesize % args.subpage_size) {
errmsg("min. I/O unit size should be multiple of "
"sub-page size");
goto out_close_mtd;
goto out_close;
}
}
args.node_fd = open(args.node, O_RDWR);
if (args.node_fd < 0) {
sys_errmsg("cannot open \"%s\"", args.node);
goto out_close_mtd;
}
/* Validate VID header offset if it was specified */
if (args.vid_hdr_offs != 0) {
if (args.vid_hdr_offs % 8) {
errmsg("VID header offset has to be multiple of min. I/O unit size");
goto out_close;
}
if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE > mtd.eb_size) {
if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE > mtd->erasesize) {
errmsg("bad VID header offset");
goto out_close;
}
}
if (!mtd.writable) {
errmsg("%s (%s) is a read-only device", mtd.node, args.node);
if (!(mtd->flags & MTD_WRITEABLE)) {
errmsg("%s is a read-only device", args.node);
goto out_close;
}
/* Make sure this MTD device is not attached to UBI */
/* FIXME! Find a proper way to do this in barebox! */
eb_cnt = mtd_div_by_eb(mtd->size, mtd);
if (!args.quiet) {
normsg_cont("%s (%s), size %lld bytes (%s)", mtd.node, mtd.type_str,
mtd.size, size_human_readable(mtd.size));
printf(", %d eraseblocks of %d bytes (%s)", mtd.eb_cnt,
mtd.eb_size, size_human_readable(mtd.eb_size));
printf(", min. I/O size %d bytes\n", mtd.min_io_size);
normsg_cont("%s (%s), size %lld bytes (%s)", args.node, mtd_type_str(mtd),
mtd->size, size_human_readable(mtd->size));
printf(", %d eraseblocks of %d bytes (%s)", eb_cnt,
mtd->erasesize, size_human_readable(mtd->erasesize));
printf(", min. I/O size %d bytes\n", mtd->writesize);
}
if (args.quiet)
@ -631,9 +641,9 @@ int do_ubiformat(int argc, char *argv[])
verbose = 2;
else
verbose = 1;
err = libscan_ubi_scan(&mtd, args.node_fd, &si, verbose);
err = libscan_ubi_scan(mtd, &si, verbose);
if (err) {
errmsg("failed to scan %s (%s)", mtd.node, args.node);
errmsg("failed to scan %s", args.node);
goto out_close;
}
@ -644,7 +654,7 @@ int do_ubiformat(int argc, char *argv[])
if (si->good_cnt < 2 && (!args.novtbl || args.image)) {
errmsg("too few non-bad eraseblocks (%d) on %s",
si->good_cnt, mtd.node);
si->good_cnt, args.node);
goto out_free;
}
@ -656,7 +666,7 @@ int do_ubiformat(int argc, char *argv[])
normsg("%d eraseblocks are supposedly empty", si->empty_cnt);
if (si->corrupted_cnt)
normsg("%d corrupted erase counters", si->corrupted_cnt);
print_bad_eraseblocks(&mtd, si);
print_bad_eraseblocks(mtd, si);
}
if (si->alien_cnt) {
@ -717,7 +727,7 @@ int do_ubiformat(int argc, char *argv[])
if (!args.quiet && args.override_ec)
normsg("use erase counter %lld for all eraseblocks", args.ec);
ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size,
ubigen_info_init(&ui, mtd->erasesize, mtd->writesize, args.subpage_size,
args.vid_hdr_offs, args.ubi_ver, args.image_seq);
if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) {
@ -735,28 +745,27 @@ int do_ubiformat(int argc, char *argv[])
}
if (args.image) {
err = flash_image(&mtd, &ui, si);
err = flash_image(mtd, &ui, si);
if (err < 0)
goto out_free;
err = format(&mtd, &ui, si, err, 1);
err = format(mtd, &ui, si, err, 1, args.subpage_size);
if (err)
goto out_free;
} else {
err = format(&mtd, &ui, si, 0, args.novtbl);
err = format(mtd, &ui, si, 0, args.novtbl, args.subpage_size);
if (err)
goto out_free;
}
libscan_ubi_scan_free(si);
close(args.node_fd);
return 0;
out_free:
libscan_ubi_scan_free(si);
out_close:
close(args.node_fd);
out_close_mtd:
close(fd);
return 1;
}

View File

@ -66,9 +66,13 @@ int bbu_confirm(struct bbu_data *data)
if (data->flags & BBU_FLAG_YES)
return 0;
printf("update barebox from %s using handler %s to %s (y/n)?\n",
if (data->imagefile)
printf("update barebox from %s using handler %s to %s (y/n)?\n",
data->imagefile, data->handler_name,
data->devicefile);
else
printf("Refresh barebox on %s using handler %s (y/n)?\n",
data->devicefile, data->handler_name);
key = read_key();
@ -215,6 +219,12 @@ int barebox_update(struct bbu_data *data)
if (!handler)
return -ENODEV;
if (!data->image && !data->imagefile &&
!(handler->flags & BBU_HANDLER_CAN_REFRESH)) {
pr_err("No Image file given\n");
return -EINVAL;
}
if (!data->handler_name)
data->handler_name = handler->name;

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,15 @@ config MTD_CONCAT
needs driver support, currently only the cfi-flash driver supports
concatenating MTD devices.
comment "MTD debug options"
config MTD_PEB_DEBUG
bool "MTD PEB debug"
help
When enabled the MTD PEB API will emulate bitflips. Random read
operations will return that bits are flipped. The MTD PEB API
is used by UBI and ubiformat
source "drivers/mtd/devices/Kconfig"
source "drivers/mtd/nor/Kconfig"
source "drivers/mtd/nand/Kconfig"

View File

@ -3,7 +3,7 @@ obj-$(CONFIG_DRIVER_CFI) += nor/
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-$(CONFIG_MTD_UBI) += ubi/
obj-y += devices/
obj-$(CONFIG_MTD) += core.o partition.o
obj-$(CONFIG_MTD) += core.o partition.o peb.o
obj-$(CONFIG_MTD_OOB_DEVICE) += mtdoob.o
obj-$(CONFIG_MTD_RAW_DEVICE) += mtdraw.o
obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o

View File

@ -34,7 +34,15 @@
static LIST_HEAD(mtd_register_hooks);
int mtd_all_ff(const void *buf, unsigned int len)
/**
* mtd_buf_all_ff - check if buffer contains only 0xff
* @buf: buffer to check
* @size: buffer size in bytes
*
* This function returns %1 if there are only 0xff bytes in @buf, and %0 if
* something else was also found.
*/
int mtd_buf_all_ff(const void *buf, unsigned int len)
{
while ((unsigned long)buf & 0x3) {
if (*(const uint8_t *)buf != 0xff)
@ -66,6 +74,25 @@ int mtd_all_ff(const void *buf, unsigned int len)
return 1;
}
/**
* mtd_buf_check_pattern - check if buffer contains only a certain byte pattern.
* @buf: buffer to check
* @patt: the pattern to check
* @size: buffer size in bytes
*
* This function returns %1 in there are only @patt bytes in @buf, and %0 if
* something else was also found.
*/
int mtd_buf_check_pattern(const void *buf, uint8_t patt, int size)
{
int i;
for (i = 0; i < size; i++)
if (((const uint8_t *)buf)[i] != patt)
return 0;
return 1;
}
static ssize_t mtd_op_read(struct cdev *cdev, void* buf, size_t count,
loff_t offset, ulong flags)
{
@ -231,6 +258,10 @@ int mtd_ioctl(struct cdev *cdev, int request, void *buf)
dev_dbg(cdev->dev, "MEMSETBADBLOCK: 0x%08llx\n", *offset);
ret = mtd_block_markbad(mtd, *offset);
break;
case MEMSETGOODBLOCK:
dev_dbg(cdev->dev, "MEMSETGOODBLOCK: 0x%08llx\n", *offset);
ret = mtd_block_markgood(mtd, *offset);
break;
case MEMERASE:
ret = mtd_op_erase(cdev, ei->length, ei->start + cdev->offset);
break;
@ -320,6 +351,18 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
return ret;
}
int mtd_block_markgood(struct mtd_info *mtd, loff_t ofs)
{
int ret;
if (mtd->block_markgood)
ret = mtd->block_markgood(mtd, ofs);
else
ret = -ENOSYS;
return ret;
}
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
u_char *buf)
{
@ -717,3 +760,25 @@ void mtdcore_add_hook(struct mtddev_hook *hook)
{
list_add(&hook->hook, &mtd_register_hooks);
}
const char *mtd_type_str(struct mtd_info *mtd)
{
switch (mtd->type) {
case MTD_ABSENT:
return "absent";
case MTD_RAM:
return "ram";
case MTD_ROM:
return "rom";
case MTD_NORFLASH:
return "nor";
case MTD_NANDFLASH:
return "nand";
case MTD_DATAFLASH:
return"dataflash";
case MTD_UBIVOLUME:
return "ubi";
default:
return "unknown";
}
}

View File

@ -506,6 +506,28 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
return err;
}
static int concat_block_markgood(struct mtd_info *mtd, loff_t ofs)
{
struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL;
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
if (ofs >= subdev->size) {
ofs -= subdev->size;
continue;
}
err = mtd_block_markgood(subdev, ofs);
if (!err)
mtd->ecc_stats.badblocks--;
break;
}
return err;
}
/*
* This function constructs a virtual MTD device by concatenating
* num_devs MTD devices. A pointer to the new device object is
@ -565,6 +587,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd.block_isbad = concat_block_isbad;
if (subdev[0]->block_markbad)
concat->mtd.block_markbad = concat_block_markbad;
if (subdev[0]->block_markgood)
concat->mtd.block_markgood = concat_block_markgood;
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;

View File

@ -456,6 +456,38 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
return chip->block_bad(mtd, ofs, getchip);
}
/**
* nand_default_block_markgood - [DEFAULT] mark a block good
* @mtd: MTD device structure
* @ofs: offset from device start
*
* This is the default implementation, which can be overridden by
* a hardware specific driver.
*/
static __maybe_unused int nand_default_block_markgood(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *chip = mtd->priv;
int block, res, ret = 0;
/* Get block number */
block = (int)(ofs >> chip->bbt_erase_shift);
/* Mark block good in memory-based BBT */
if (chip->bbt)
chip->bbt[block >> 2] &= ~(0x01 << ((block & 0x03) << 1));
/* Update flash-based bad block table */
if (IS_ENABLED(CONFIG_NAND_BBT) && chip->bbt_options & NAND_BBT_USE_FLASH) {
res = nand_update_bbt(mtd, ofs);
if (!ret)
ret = res;
}
if (!ret)
mtd->ecc_stats.badblocks++;
return ret;
}
/* Wait for the ready pin, after a command. The timeout is caught later. */
void nand_wait_ready(struct mtd_info *mtd)
{
@ -925,7 +957,7 @@ EXPORT_SYMBOL(nand_lock);
* bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
* threshold.
*/
static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
{
const unsigned char *bitmap = buf;
int bitflips = 0;
@ -2371,7 +2403,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
memset(chip->oob_poi, 0xff, mtd->oobsize);
}
if (oob || !mtd_all_ff(wbuf, mtd->writesize)) {
if (oob || !mtd_buf_all_ff(wbuf, mtd->writesize)) {
ret = chip->write_page(mtd, chip, column, bytes, wbuf,
oob_required, page, cached,
(ops->mode == MTD_OPS_RAW));
@ -2774,6 +2806,30 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
return chip->block_markbad(mtd, ofs);
}
/**
* nand_block_markgood - [MTD Interface] Mark block at the given offset as bad
* @mtd: MTD device structure
* @ofs: offset relative to mtd start
*/
static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *chip = mtd->priv;
int ret;
if (!IS_ENABLED(CONFIG_MTD_WRITE))
return -ENOTSUPP;
ret = nand_block_isbad(mtd, ofs);
if (ret < 0)
return ret;
/* If it was good already, return success and do nothing */
if (!ret)
return 0;
return chip->block_markgood(mtd, ofs);
}
/**
* nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
* @mtd: MTD device structure
@ -2844,6 +2900,8 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
#ifdef CONFIG_MTD_WRITE
if (!chip->block_markbad)
chip->block_markbad = nand_default_block_markbad;
if (!chip->block_markgood)
chip->block_markgood = nand_default_block_markgood;
if (!chip->write_buf)
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
#endif
@ -3707,6 +3765,7 @@ int nand_scan_tail(struct mtd_info *mtd)
mtd->unlock = NULL;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
mtd->block_markgood = nand_block_markgood;
mtd->writebufsize = mtd->writesize;
/* propagate ecc info to mtd_info */

View File

@ -18,6 +18,26 @@ static int mtd_part_read(struct mtd_info *mtd, loff_t from, size_t len,
return res;
}
static int mtd_part_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int res;
if (from >= mtd->size)
return -EINVAL;
if (ops->datbuf && from + ops->len > mtd->size)
return -EINVAL;
res = mtd->master->read_oob(mtd->master, from + mtd->master_offset, ops);
if (unlikely(res)) {
if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected++;
if (mtd_is_eccerr(res))
mtd->ecc_stats.failed++;
}
return res;
}
static int mtd_part_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
@ -114,6 +134,21 @@ static int mtd_part_block_markbad(struct mtd_info *mtd, loff_t ofs)
return res;
}
static int mtd_part_block_markgood(struct mtd_info *mtd, loff_t ofs)
{
int res;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
if (ofs >= mtd->size)
return -EINVAL;
ofs += mtd->master_offset;
res = mtd->master->block_markgood(mtd->master, ofs);
if (!res)
mtd->ecc_stats.badblocks--;
return res;
}
struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
uint64_t size, unsigned long flags, const char *name)
{
@ -168,10 +203,13 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
part->lock = mtd_part_lock;
part->unlock = mtd_part_unlock;
part->block_markbad = mtd->block_markbad ? mtd_part_block_markbad : NULL;
part->block_markgood = mtd->block_markgood ? mtd_part_block_markgood : NULL;
}
if (mtd->write_oob)
part->write_oob = mtd_part_write_oob;
if (mtd->read_oob)
part->read_oob = mtd_part_read_oob;
part->block_isbad = mtd->block_isbad ? mtd_part_block_isbad : NULL;
part->size = size;

658
drivers/mtd/peb.c Normal file
View File

@ -0,0 +1,658 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <common.h>
#include <malloc.h>
#include <stdlib.h>
#include <init.h>
#include <magicvar.h>
#include <globalvar.h>
#include <linux/mtd/mtd.h>
#include <mtd/mtd-peb.h>
#include <linux/err.h>
#define MTD_IO_RETRIES 3
static int __mtd_peb_chk_io;
static int mtd_peb_chk_io(void)
{
if (!IS_ENABLED(CONFIG_MTD_PEB_DEBUG))
return 0;
if (!__mtd_peb_chk_io)
return 0;
return 1;
}
static u32 __mtd_peb_emulate_bitflip;
static int mtd_peb_emulate_bitflip(void)
{
if (!IS_ENABLED(CONFIG_MTD_PEB_DEBUG))
return 0;
if (!__mtd_peb_emulate_bitflip)
return 0;
return !(random32() % __mtd_peb_emulate_bitflip);
}
static u32 __mtd_peb_emulate_write_failure;
static int mtd_peb_emulate_write_failure(void)
{
if (!IS_ENABLED(CONFIG_MTD_PEB_DEBUG))
return 0;
if (!__mtd_peb_emulate_write_failure)
return 0;
return !(random32() % __mtd_peb_emulate_write_failure);
}
static u32 __mtd_peb_emulate_erase_failures;
static int mtd_peb_emulate_erase_failure(void)
{
if (!IS_ENABLED(CONFIG_MTD_PEB_DEBUG))
return 0;
if (!__mtd_peb_emulate_erase_failures)
return 0;
return !(random32() % __mtd_peb_emulate_erase_failures);
}
#ifdef CONFIG_MTD_PEB_DEBUG
static int mtd_peb_debug_init(void)
{
globalvar_add_simple_int("mtd_peb.mtd_peb_emulate_bitflip",
&__mtd_peb_emulate_bitflip, "%u");
globalvar_add_simple_int("mtd_peb.mtd_peb_emulate_write_failure",
&__mtd_peb_emulate_write_failure, "%u");
globalvar_add_simple_int("mtd_peb.mtd_peb_emulate_erase_failures",
&__mtd_peb_emulate_erase_failures, "%u");
globalvar_add_simple_bool("mtd_peb.mtd_peb_chk_io",
&__mtd_peb_chk_io);
return 0;
}
device_initcall(mtd_peb_debug_init);
BAREBOX_MAGICVAR_NAMED(global_mtd_peb_emulate_bitflip,
global.mtd_peb.emulate_bitflip,
"random bitflips, on average every #nth access returns -EUCLEAN");
BAREBOX_MAGICVAR_NAMED(global_mtd_peb_emulate_write_failure,
global.mtd_peb.emulate_write_failure,
"random write failures, on average every #nth access returns write failure");
BAREBOX_MAGICVAR_NAMED(global_mtd_peb_emulate_erase_failures,
global.mtd_peb.emulate_erase_failures,
"random erase failures, on average every #nth access returns erase failure");
BAREBOX_MAGICVAR_NAMED(global_mtd_peb_chk_io,
global.mtd_peb.chk_io,
"If true, written data will be verified");
#endif
static int mtd_peb_valid(struct mtd_info *mtd, int pnum)
{
if (pnum < 0)
return false;
if ((uint64_t)pnum * mtd->erasesize >= mtd->size)
return false;
if (mtd->numeraseregions)
return false;
return true;
}
/**
* mtd_num_pebs - return number of PEBs for this device
* @mtd: mtd device
*
* This function returns the number of physical erase blocks this device
* has.
*/
int mtd_num_pebs(struct mtd_info *mtd)
{
return mtd_div_by_eb(mtd->size, mtd);
}
/**
* mtd_peb_mark_bad - mark a physical eraseblock as bad
* @mtd: mtd device
* @pnum: The number of the block
*
* This function marks a physical eraseblock as bad.
*/
int mtd_peb_mark_bad(struct mtd_info *mtd, int pnum)
{
return mtd_block_markbad(mtd, (loff_t)pnum * mtd->erasesize);
}
/**
* mtd_peb_is_bad - test if a physical eraseblock is bad
* @mtd: mtd device
* @pnum: The number of the block
*
* This function tests if a physical eraseblock is bad. Returns
* 0 if it is good, 1 if it is bad or a negative error value if the
* block is invalid
*/
int mtd_peb_is_bad(struct mtd_info *mtd, int pnum)
{
if (!mtd_peb_valid(mtd, pnum))
return -EINVAL;
return mtd_block_isbad(mtd, (loff_t)pnum * mtd->erasesize);
}
/**
* mtd_peb_read - read data from a physical eraseblock.
* @mtd: mtd device
* @buf: buffer where to store the read data
* @pnum: physical eraseblock number to read from
* @offset: offset within the physical eraseblock from where to read
* @len: how many bytes to read
*
* This function reads data from offset @offset of physical eraseblock @pnum
* and stores the read data in the @buf buffer. The following return codes are
* possible:
*
* o %0 if all the requested data were successfully read;
* o %-EUCLEAN if all the requested data were successfully read, but
* correctable bit-flips were detected; this is harmless but may indicate
* that this eraseblock may become bad soon (but do not have to);
* o %-EBADMSG if the MTD subsystem reported about data integrity problems, for
* example it can be an ECC error in case of NAND; this most probably means
* that the data is corrupted;
* o %-EIO if some I/O error occurred;
* o other negative error codes in case of other errors.
*/
int mtd_peb_read(struct mtd_info *mtd, void *buf, int pnum, int offset,
int len)
{
int err, retries = 0;
size_t read;
loff_t addr;
if (!mtd_peb_valid(mtd, pnum))
return -EINVAL;
if (offset < 0 || offset + len > mtd->erasesize)
return -EINVAL;
if (len <= 0)
return -EINVAL;
if (mtd_peb_is_bad(mtd, pnum))
return -EINVAL;
/* Deliberately corrupt the buffer */
*((uint8_t *)buf) ^= 0xFF;
addr = (loff_t)pnum * mtd->erasesize + offset;
retry:
err = mtd_read(mtd, addr, len, &read, buf);
if (err) {
const char *errstr = mtd_is_eccerr(err) ? " (ECC error)" : "";
if (mtd_is_bitflip(err)) {
/*
* -EUCLEAN is reported if there was a bit-flip which
* was corrected, so this is harmless.
*
* We do not report about it here unless debugging is
* enabled. A corresponding message will be printed
* later, when it is has been scrubbed.
*/
dev_dbg(&mtd->class_dev, "fixable bit-flip detected at PEB %d\n", pnum);
if (len != read)
return -EIO;
return -EUCLEAN;
}
if (mtd_is_eccerr(err) && retries++ < MTD_IO_RETRIES)
goto retry;
dev_err(&mtd->class_dev, "error %d%s while reading %d bytes from PEB %d:%d\n",
err, errstr, len, pnum, offset);
return err;
}
if (len != read)
return -EIO;
if (mtd_peb_emulate_bitflip())
return -EUCLEAN;
return 0;
}
/**
* mtd_peb_check_all_ff - check that a region of flash is empty.
* @mtd: mtd device
* @pnum: the physical eraseblock number to check
* @offset: the starting offset within the physical eraseblock to check
* @len: the length of the region to check
*
* This function returns zero if only 0xFF bytes are present at offset
* @offset of the physical eraseblock @pnum, -EBADMSG if the buffer does
* not contain all 0xFF or other negative error codes when other errors
* occured
*/
int mtd_peb_check_all_ff(struct mtd_info *mtd, int pnum, int offset, int len,
int warn)
{
size_t read;
int err;
void *buf;
buf = malloc(len);
if (!buf)
return -ENOMEM;
err = mtd_peb_read(mtd, buf, pnum, offset, len);
if (err && !mtd_is_bitflip(err)) {
dev_err(&mtd->class_dev,
"error %d while reading %d bytes from PEB %d:%d, read %zd bytes\n",
err, len, pnum, offset, read);
goto out;
}
err = mtd_buf_all_ff(buf, len);
if (err == 0) {
if (warn)
dev_err(&mtd->class_dev, "all-ff check failed for PEB %d\n",
pnum);
err = -EBADMSG;
goto out;
}
err = 0;
out:
free(buf);
return err;
}
/**
* mtd_peb_verify - make sure write succeeded.
* @mtd: mtd device
* @buf: buffer with data which were written
* @pnum: physical eraseblock number the data were written to
* @offset: offset within the physical eraseblock the data were written to
* @len: how many bytes were written
*
* This functions reads data which were recently written and compares it with
* the original data buffer - the data have to match. Returns zero if the data
* match and a negative error code if not or in case of failure.
*/
int mtd_peb_verify(struct mtd_info *mtd, const void *buf, int pnum,
int offset, int len)
{
int err, i;
size_t read;
void *buf1;
loff_t addr = (loff_t)pnum * mtd->erasesize + offset;
buf1 = malloc(len);
if (!buf1)
return 0;
err = mtd_read(mtd, addr, len, &read, buf1);
if (err && !mtd_is_bitflip(err))
goto out_free;
for (i = 0; i < len; i++) {
uint8_t c = ((uint8_t *)buf)[i];
uint8_t c1 = ((uint8_t *)buf1)[i];
int dump_len;
if (c == c1)
continue;
dev_err(&mtd->class_dev, "self-check failed for PEB %d:%d, len %d\n",
pnum, offset, len);
dev_info(&mtd->class_dev, "data differs at position %d\n", i);
dump_len = max_t(int, 128, len - i);
#ifdef DEBUG
dev_info(&mtd->class_dev, "hex dump of the original buffer from %d to %d\n",
i, i + dump_len);
memory_display(buf + i, i, dump_len, 4, 0);
dev_info(&mtd->class_dev, "hex dump of the read buffer from %d to %d\n",
i, i + dump_len);
memory_display(buf1 + i, i, dump_len, 4, 0);
dump_stack();
#endif
err = -EBADMSG;
goto out_free;
}
err = 0;
out_free:
free(buf1);
return err;
}
/**
* mtd_peb_write - write data to a physical eraseblock.
* @mtd: mtd device
* @buf: buffer with the data to write
* @pnum: physical eraseblock number to write to
* @offset: offset within the physical eraseblock where to write
* @len: how many bytes to write
*
* This function writes @len bytes of data from buffer @buf to offset @offset
* of physical eraseblock @pnum. If all the data was successfully written,
* zero is returned. If an error occurred, this function returns a negative
* error code. If %-EBADMSG is returned, the physical eraseblock most probably
* went bad.
*
* Note, in case of an error, it is possible that something was still written
* to the flash media, but may be some garbage.
*/
int mtd_peb_write(struct mtd_info *mtd, const void *buf, int pnum, int offset,
int len)
{
int err;
size_t written;
loff_t addr;
dev_dbg(&mtd->class_dev, "write %d bytes to PEB %d:%d\n", len, pnum, offset);
if (!mtd_peb_valid(mtd, pnum))
return -EINVAL;
if (offset < 0 || offset + len > mtd->erasesize)
return -EINVAL;
if (len <= 0)
return -EINVAL;
if (len % (mtd->writesize >> mtd->subpage_sft))
return -EINVAL;
if (mtd_peb_is_bad(mtd, pnum))
return -EINVAL;
if (mtd_peb_emulate_write_failure()) {
dev_err(&mtd->class_dev, "Cannot write %d bytes to PEB %d:%d (emulated)\n",
len, pnum, offset);
return -EIO;
}
if (mtd_peb_chk_io()) {
/* The area we are writing to has to contain all 0xFF bytes */
err = mtd_peb_check_all_ff(mtd, pnum, offset, len, 1);
if (err)
return err;
}
addr = (loff_t)pnum * mtd->erasesize + offset;
err = mtd_write(mtd, addr, len, &written, buf);
if (err) {
dev_err(&mtd->class_dev, "error %d while writing %d bytes to PEB %d:%d, written %zd bytes\n",
err, len, pnum, offset, written);
} else {
if (written != len)
err = -EIO;
}
if (!err && mtd_peb_chk_io())
err = mtd_peb_verify(mtd, buf, pnum, offset, len);
return err;
}
/**
* mtd_peb_erase - erase a physical eraseblock.
* @mtd: mtd device
* @pnum: physical eraseblock number to erase
*
* This function erases physical eraseblock @pnum.
*
* This function returns 0 in case of success, %-EIO if the erasure failed,
* and other negative error codes in case of other errors. Note, %-EIO means
* that the physical eraseblock is bad.
*/
int mtd_peb_erase(struct mtd_info *mtd, int pnum)
{
int ret;
struct erase_info ei = {};
dev_dbg(&mtd->class_dev, "erase PEB %d\n", pnum);
if (!mtd_peb_valid(mtd, pnum))
return -EINVAL;
ei.mtd = mtd;
ei.addr = (loff_t)pnum * mtd->erasesize;
ei.len = mtd->erasesize;
ret = mtd_erase(mtd, &ei);
if (ret)
return ret;
if (mtd_peb_chk_io()) {
ret = mtd_peb_check_all_ff(mtd, pnum, 0, mtd->erasesize, 1);
if (ret == -EBADMSG)
ret = -EIO;
}
if (mtd_peb_emulate_erase_failure()) {
dev_err(&mtd->class_dev, "cannot erase PEB %d (emulated)", pnum);
return -EIO;
}
return 0;
}
/* Patterns to write to a physical eraseblock when torturing it */
static uint8_t patterns[] = {0xa5, 0x5a, 0x0};
/**
* mtd_peb_torture - test a supposedly bad physical eraseblock.
* @mtd: mtd device
* @pnum: the physical eraseblock number to test
*
* This function is a last resort when an eraseblock is assumed to be
* bad. It will write and check some patterns to the block. If the test
* is passed then this function will with the block freshly erased and
* the positive number returned indicaties how often the block has been
* erased during this test.
* If the block does not pass the test the block is marked as bad and
* -EIO is returned.
* Other negative errors are returned in case of other errors.
*/
int mtd_peb_torture(struct mtd_info *mtd, int pnum)
{
int err, i, patt_count;
void *peb_buf;
if (!mtd_peb_valid(mtd, pnum))
return -EINVAL;
peb_buf = malloc(mtd->erasesize);
if (!peb_buf)
return -ENOMEM;
dev_dbg(&mtd->class_dev, "run torture test for PEB %d\n", pnum);
patt_count = ARRAY_SIZE(patterns);
for (i = 0; i < patt_count; i++) {
err = mtd_peb_erase(mtd, pnum);
if (err)
goto out;
/* Make sure the PEB contains only 0xFF bytes after erasing */
err = mtd_peb_check_all_ff(mtd, pnum, 0, mtd->writesize, 0);
if (err)
goto out;
/* Write a pattern and check it */
memset(peb_buf, patterns[i], mtd->erasesize);
err = mtd_peb_write(mtd, peb_buf, pnum, 0, mtd->erasesize);
if (err)
goto out;
err = mtd_peb_verify(mtd, peb_buf, pnum, 0, mtd->erasesize);
if (err)
goto out;
}
err = mtd_peb_erase(mtd, pnum);
if (err)
goto out;
err = patt_count + 1;
dev_dbg(&mtd->class_dev, "PEB %d passed torture test, do not mark it as bad\n",
pnum);
out:
if (err == -EUCLEAN || mtd_is_eccerr(err)) {
/*
* If a bit-flip or data integrity error was detected, the test
* has not passed because it happened on a freshly erased
* physical eraseblock which means something is wrong with it.
*/
dev_err(&mtd->class_dev, "read problems on freshly erased PEB %d, marking it bad\n",
pnum);
mtd_peb_mark_bad(mtd, pnum);
err = -EIO;
}
free(peb_buf);
return err;
}
/**
* mtd_peb_create_bitflips - create bitflips on Nand pages
* @mtd: mtd device
* @pnum: Physical erase block number
* @offset: offset within erase block
* @len: The length of the area to create bitflips in
* @num_bitflips: The number of bitflips to create
* @random: If true, create bitflips at random offsets
* @info: If true, print information where bitflips are created
*
* This uses the mtd raw ops to create bitflips on a Nand page for
* testing purposes. If %random is false then the positions to flip are
* reproducible (thus, a second call with the same arguments reverts the
* bitflips).
*
* Return: 0 for success, otherwise a negative error code is returned
*/
int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset,
int len, int num_bitflips, int random,
int info)
{
struct mtd_oob_ops ops;
int pages_per_block = mtd->erasesize / mtd->writesize;
int i;
int ret;
void *buf = NULL, *oobbuf = NULL;
int step;
if (offset < 0 || offset + len > mtd->erasesize)
return -EINVAL;
if (len <= 0)
return -EINVAL;
if (num_bitflips <= 0)
return -EINVAL;
if (mtd_peb_is_bad(mtd, pnum))
return -EINVAL;
buf = malloc(mtd->writesize * pages_per_block);
if (!buf) {
ret = -ENOMEM;
goto err;
}
oobbuf = malloc(mtd->oobsize * pages_per_block);
if (!oobbuf) {
ret = -ENOMEM;
goto err;
}
ops.mode = MTD_OPS_RAW;
ops.ooboffs = 0;
ops.len = mtd->writesize;
ops.ooblen = mtd->oobsize;
for (i = 0; i < pages_per_block; i++) {
loff_t offs = (loff_t)pnum * mtd->erasesize + i * mtd->writesize;
ops.datbuf = buf + i * mtd->writesize;
ops.oobbuf = oobbuf + i * mtd->oobsize;
ret = mtd_read_oob(mtd, offs, &ops);
if (ret) {
dev_err(&mtd->class_dev, "Cannot read raw data at 0x%08llx\n", offs);
goto err;
}
}
if (random)
step = random32() % num_bitflips;
else
step = len / num_bitflips;
for (i = 0; i < num_bitflips; i++) {
int offs;
int bit;
u8 *pos = buf;
if (random) {
offs = random32() % len;
bit = random32() % 8;
} else {
offs = i * len / num_bitflips;
bit = i % 8;
}
pos[offs] ^= 1 << bit;
if (info)
dev_info(&mtd->class_dev, "Flipping bit %d @ %d\n", bit, offs);
}
ret = mtd_peb_erase(mtd, pnum);
if (ret < 0) {
dev_err(&mtd->class_dev, "Cannot erase PEB %d\n", pnum);
goto err;
}
for (i = 0; i < pages_per_block; i++) {
loff_t offs = (loff_t)pnum * mtd->erasesize + i * mtd->writesize;
ops.datbuf = buf + i * mtd->writesize;
ops.oobbuf = oobbuf + i * mtd->oobsize;
ret = mtd_write_oob(mtd, offs, &ops);
if (ret) {
dev_err(&mtd->class_dev, "Cannot write page at 0x%08llx\n", offs);
goto err;
}
}
ret = 0;
err:
if (ret)
dev_err(&mtd->class_dev, "Failed to create bitflips: %s\n", strerror(-ret));
free(buf);
free(oobbuf);
return ret;
}

View File

@ -77,4 +77,17 @@ config MTD_UBI_FASTMAP
If in doubt, say "N".
comment "UBI debugging options"
config MTD_UBI_CHECK_IO
bool "Check IO operations"
help
When enabled UBI will check if erased blocks are really erased and if areas
written to are empty before writing.
config MTD_UBI_GENERAL_EXTRA_CHECKS
bool "general extra checks"
help
This enables some general extra checks in UBI
endif # MTD_UBI

View File

@ -772,7 +772,7 @@ static int check_corruption(struct ubi_device *ubi, struct ubi_vid_hdr *vid_hdr,
if (err)
goto out_unlock;
if (ubi_check_pattern(ubi->peb_buf, 0xFF, ubi->leb_size))
if (mtd_buf_all_ff(ubi->peb_buf, ubi->leb_size))
goto out_unlock;
ubi_err("PEB %d contains corrupted VID header, and the data does not contain all 0xFF",

View File

@ -72,54 +72,19 @@ static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi)
return ubi->dbg.disable_bgt;
}
/**
* ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
* @ubi: UBI device description object
*
* Returns non-zero if a bit-flip should be emulated, otherwise returns zero.
*/
static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi)
{
if (ubi->dbg.emulate_bitflips)
return !(random32() % 200);
return 0;
}
/**
* ubi_dbg_is_write_failure - if it is time to emulate a write failure.
* @ubi: UBI device description object
*
* Returns non-zero if a write failure should be emulated, otherwise returns
* zero.
*/
static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi)
{
if (ubi->dbg.emulate_io_failures)
return !(random32() % 500);
return 0;
}
/**
* ubi_dbg_is_erase_failure - if its time to emulate an erase failure.
* @ubi: UBI device description object
*
* Returns non-zero if an erase failure should be emulated, otherwise returns
* zero.
*/
static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi)
{
if (ubi->dbg.emulate_io_failures)
return !(random32() % 400);
return 0;
}
static inline int ubi_dbg_chk_io(const struct ubi_device *ubi)
{
return ubi->dbg.chk_io;
if (IS_ENABLED(CONFIG_MTD_UBI_CHECK_IO))
return 1;
else
return 0;
}
static inline int ubi_dbg_chk_gen(const struct ubi_device *ubi)
{
return ubi->dbg.chk_gen;
if (IS_ENABLED(CONFIG_MTD_UBI_GENERAL_EXTRA_CHECKS))
return 1;
else
return 0;
}
#endif /* !__UBI_DEBUG_H__ */

View File

@ -83,6 +83,7 @@
*/
#include <linux/err.h>
#include <mtd/mtd-peb.h>
#include "ubi.h"
static int self_check_not_bad(const struct ubi_device *ubi, int pnum);
@ -92,8 +93,6 @@ static int self_check_ec_hdr(const struct ubi_device *ubi, int pnum,
static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum);
static int self_check_vid_hdr(const struct ubi_device *ubi, int pnum,
const struct ubi_vid_hdr *vid_hdr);
static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum,
int offset, int len);
/**
* ubi_io_read - read data from a physical eraseblock.
@ -120,91 +119,12 @@ static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum,
int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
int len)
{
int err, retries = 0;
size_t read;
loff_t addr;
int ret;
dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset);
ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
ubi_assert(offset >= 0 && offset + len <= ubi->peb_size);
ubi_assert(len > 0);
err = self_check_not_bad(ubi, pnum);
if (err)
return err;
/*
* Deliberately corrupt the buffer to improve robustness. Indeed, if we
* do not do this, the following may happen:
* 1. The buffer contains data from previous operation, e.g., read from
* another PEB previously. The data looks like expected, e.g., if we
* just do not read anything and return - the caller would not
* notice this. E.g., if we are reading a VID header, the buffer may
* contain a valid VID header from another PEB.
* 2. The driver is buggy and returns us success or -EBADMSG or
* -EUCLEAN, but it does not actually put any data to the buffer.
*
* This may confuse UBI or upper layers - they may think the buffer
* contains valid data while in fact it is just old data. This is
* especially possible because UBI (and UBIFS) relies on CRC, and
* treats data as correct even in case of ECC errors if the CRC is
* correct.
*
* Try to prevent this situation by changing the first byte of the
* buffer.
*/
*((uint8_t *)buf) ^= 0xFF;
addr = (loff_t)pnum * ubi->peb_size + offset;
retry:
err = mtd_read(ubi->mtd, addr, len, &read, buf);
if (err) {
const char *errstr = mtd_is_eccerr(err) ? " (ECC error)" : "";
if (mtd_is_bitflip(err)) {
/*
* -EUCLEAN is reported if there was a bit-flip which
* was corrected, so this is harmless.
*
* We do not report about it here unless debugging is
* enabled. A corresponding message will be printed
* later, when it is has been scrubbed.
*/
ubi_msg("fixable bit-flip detected at PEB %d", pnum);
ubi_assert(len == read);
return UBI_IO_BITFLIPS;
}
if (retries++ < UBI_IO_RETRIES) {
ubi_warn("error %d%s while reading %d bytes from PEB %d:%d, read only %zd bytes, retry",
err, errstr, len, pnum, offset, read);
goto retry;
}
ubi_err("error %d%s while reading %d bytes from PEB %d:%d, read %zd bytes",
err, errstr, len, pnum, offset, read);
dump_stack();
/*
* The driver should never return -EBADMSG if it failed to read
* all the requested data. But some buggy drivers might do
* this, so we change it to -EIO.
*/
if (read != len && mtd_is_eccerr(err)) {
ubi_assert(0);
err = -EIO;
}
} else {
ubi_assert(len == read);
if (ubi_dbg_is_bitflip(ubi)) {
dbg_gen("bit-flip (emulated)");
err = UBI_IO_BITFLIPS;
}
}
return err;
ret = mtd_peb_read(ubi->mtd, buf, pnum, offset, len);
if (mtd_is_bitflip(ret))
return UBI_IO_BITFLIPS;
return ret;
}
/**
@ -228,8 +148,6 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
int len)
{
int err;
size_t written;
loff_t addr;
dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset);
@ -243,15 +161,6 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
return -EROFS;
}
err = self_check_not_bad(ubi, pnum);
if (err)
return err;
/* The area we are writing to has to contain all 0xFF bytes */
err = ubi_self_check_all_ff(ubi, pnum, offset, len);
if (err)
return err;
if (offset >= ubi->leb_start) {
/*
* We write to the data area of the physical eraseblock. Make
@ -265,50 +174,7 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
return err;
}
if (ubi_dbg_is_write_failure(ubi)) {
ubi_err("cannot write %d bytes to PEB %d:%d (emulated)",
len, pnum, offset);
dump_stack();
return -EIO;
}
addr = (loff_t)pnum * ubi->peb_size + offset;
err = mtd_write(ubi->mtd, addr, len, &written, buf);
if (err) {
ubi_err("error %d while writing %d bytes to PEB %d:%d, written %zd bytes",
err, len, pnum, offset, written);
dump_stack();
ubi_dump_flash(ubi, pnum, offset, len);
} else
ubi_assert(written == len);
if (!err) {
err = self_check_write(ubi, buf, pnum, offset, len);
if (err)
return err;
/*
* Since we always write sequentially, the rest of the PEB has
* to contain only 0xFF bytes.
*/
offset += len;
len = ubi->peb_size - offset;
if (len)
err = ubi_self_check_all_ff(ubi, pnum, offset, len);
}
return err;
}
/**
* erase_callback - MTD erasure call-back.
* @ei: MTD erase information object.
*
* Note, even though MTD erase interface is asynchronous, all the current
* implementations are synchronous anyway.
*/
static void erase_callback(struct erase_info *ei)
{
return mtd_peb_write(ubi->mtd, buf, pnum, offset, len);
}
/**
@ -322,9 +188,6 @@ static void erase_callback(struct erase_info *ei)
*/
static int do_sync_erase(struct ubi_device *ubi, int pnum)
{
int err, retries = 0;
struct erase_info ei;
dbg_io("erase PEB %d", pnum);
ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
@ -333,122 +196,7 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum)
return -EROFS;
}
retry:
memset(&ei, 0, sizeof(struct erase_info));
ei.mtd = ubi->mtd;
ei.addr = (loff_t)pnum * ubi->peb_size;
ei.len = ubi->peb_size;
ei.callback = erase_callback;
err = mtd_erase(ubi->mtd, &ei);
if (err) {
if (retries++ < UBI_IO_RETRIES) {
ubi_warn("error %d while erasing PEB %d, retry",
err, pnum);
goto retry;
}
ubi_err("cannot erase PEB %d, error %d", pnum, err);
dump_stack();
return err;
}
if (ei.state == MTD_ERASE_FAILED) {
if (retries++ < UBI_IO_RETRIES) {
ubi_warn("error while erasing PEB %d, retry", pnum);
goto retry;
}
ubi_err("cannot erase PEB %d", pnum);
dump_stack();
return -EIO;
}
err = ubi_self_check_all_ff(ubi, pnum, 0, ubi->peb_size);
if (err)
return err;
if (ubi_dbg_is_erase_failure(ubi)) {
ubi_err("cannot erase PEB %d (emulated)", pnum);
return -EIO;
}
return 0;
}
/* Patterns to write to a physical eraseblock when torturing it */
static uint8_t patterns[] = {0xa5, 0x5a, 0x0};
/**
* torture_peb - test a supposedly bad physical eraseblock.
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to test
*
* This function returns %-EIO if the physical eraseblock did not pass the
* test, a positive number of erase operations done if the test was
* successfully passed, and other negative error codes in case of other errors.
*/
static int torture_peb(struct ubi_device *ubi, int pnum)
{
int err, i, patt_count;
ubi_msg("run torture test for PEB %d", pnum);
patt_count = ARRAY_SIZE(patterns);
ubi_assert(patt_count > 0);
for (i = 0; i < patt_count; i++) {
err = do_sync_erase(ubi, pnum);
if (err)
goto out;
/* Make sure the PEB contains only 0xFF bytes */
err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size);
if (err)
goto out;
err = ubi_check_pattern(ubi->peb_buf, 0xFF, ubi->peb_size);
if (err == 0) {
ubi_err("erased PEB %d, but a non-0xFF byte found",
pnum);
err = -EIO;
goto out;
}
/* Write a pattern and check it */
memset(ubi->peb_buf, patterns[i], ubi->peb_size);
err = ubi_io_write(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size);
if (err)
goto out;
memset(ubi->peb_buf, ~patterns[i], ubi->peb_size);
err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size);
if (err)
goto out;
err = ubi_check_pattern(ubi->peb_buf, patterns[i],
ubi->peb_size);
if (err == 0) {
ubi_err("pattern %x checking failed for PEB %d",
patterns[i], pnum);
err = -EIO;
goto out;
}
}
err = patt_count;
ubi_msg("PEB %d passed torture test, do not mark it as bad", pnum);
out:
if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err)) {
/*
* If a bit-flip or data integrity error was detected, the test
* has not passed because it happened on a freshly erased
* physical eraseblock which means something is wrong with it.
*/
ubi_err("read problems on freshly erased PEB %d, must be bad",
pnum);
err = -EIO;
}
return err;
return mtd_peb_erase(ubi->mtd, pnum);
}
/**
@ -563,15 +311,15 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture)
}
if (torture) {
ret = torture_peb(ubi, pnum);
ret = mtd_peb_torture(ubi->mtd, pnum);
if (ret < 0)
return ret;
} else {
err = do_sync_erase(ubi, pnum);
if (err)
return err;
}
err = do_sync_erase(ubi, pnum);
if (err)
return err;
return ret + 1;
}
@ -604,35 +352,6 @@ int ubi_io_is_bad(const struct ubi_device *ubi, int pnum)
return 0;
}
/**
* ubi_io_mark_bad - mark a physical eraseblock as bad.
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to mark
*
* This function returns zero in case of success and a negative error code in
* case of failure.
*/
int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum)
{
int err;
struct mtd_info *mtd = ubi->mtd;
ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
if (ubi->ro_mode) {
ubi_err("read-only mode");
return -EROFS;
}
if (!ubi->bad_allowed)
return 0;
err = mtd_block_markbad(mtd, (loff_t)pnum * ubi->peb_size);
if (err)
ubi_err("cannot mark PEB %d bad, error %d", pnum, err);
return err;
}
/**
* validate_ec_hdr - validate an erase counter header.
* @ubi: UBI device description object
@ -740,7 +459,7 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
* 0xFF. If yes, this physical eraseblock is assumed to be
* empty.
*/
if (ubi_check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) {
if (mtd_buf_all_ff(ec_hdr, UBI_EC_HDR_SIZE)) {
/* The physical eraseblock is supposedly empty */
if (verbose)
ubi_warn("no EC header found at PEB %d, only 0xFF bytes",
@ -996,7 +715,7 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
if (mtd_is_eccerr(read_err))
return UBI_IO_BAD_HDR_EBADMSG;
if (ubi_check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) {
if (mtd_buf_all_ff(vid_hdr, UBI_VID_HDR_SIZE)) {
if (verbose)
ubi_warn("no VID header found at PEB %d, only 0xFF bytes",
pnum);
@ -1282,72 +1001,6 @@ exit:
return err;
}
/**
* self_check_write - make sure write succeeded.
* @ubi: UBI device description object
* @buf: buffer with data which were written
* @pnum: physical eraseblock number the data were written to
* @offset: offset within the physical eraseblock the data were written to
* @len: how many bytes were written
*
* This functions reads data which were recently written and compares it with
* the original data buffer - the data have to match. Returns zero if the data
* match and a negative error code if not or in case of failure.
*/
static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum,
int offset, int len)
{
int err, i;
size_t read;
void *buf1;
loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
if (!ubi_dbg_chk_io(ubi))
return 0;
buf1 = kmalloc(len, GFP_KERNEL);
if (!buf1) {
ubi_err("cannot allocate memory to check writes");
return 0;
}
err = mtd_read(ubi->mtd, addr, len, &read, buf1);
if (err && !mtd_is_bitflip(err))
goto out_free;
for (i = 0; i < len; i++) {
uint8_t c = ((uint8_t *)buf)[i];
uint8_t c1 = ((uint8_t *)buf1)[i];
int dump_len;
if (c == c1)
continue;
ubi_err("self-check failed for PEB %d:%d, len %d",
pnum, offset, len);
ubi_msg("data differ at position %d", i);
dump_len = max_t(int, 128, len - i);
ubi_msg("hex dump of the original buffer from %d to %d",
i, i + dump_len);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
buf + i, dump_len, 1);
ubi_msg("hex dump of the read buffer from %d to %d",
i, i + dump_len);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
buf1 + i, dump_len, 1);
dump_stack();
err = -EINVAL;
goto out_free;
}
vfree(buf1);
return 0;
out_free:
vfree(buf1);
return err;
}
/**
* ubi_self_check_all_ff - check that a region of flash is empty.
* @ubi: UBI device description object
@ -1361,44 +1014,8 @@ out_free:
*/
int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len)
{
size_t read;
int err;
void *buf;
loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
if (!ubi_dbg_chk_io(ubi))
return 0;
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
ubi_err("cannot allocate memory to check for 0xFFs");
return 0;
}
err = mtd_read(ubi->mtd, addr, len, &read, buf);
if (err && !mtd_is_bitflip(err)) {
ubi_err("error %d while reading %d bytes from PEB %d:%d, read %zd bytes",
err, len, pnum, offset, read);
goto error;
}
err = ubi_check_pattern(buf, 0xFF, len);
if (err == 0) {
ubi_err("flash region at PEB %d:%d, length %d does not contain all 0xFF bytes",
pnum, offset, len);
goto fail;
}
vfree(buf);
return 0;
fail:
ubi_err("self-check failed for PEB %d", pnum);
ubi_msg("hex dump of the %d-%d region", offset, offset + len);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1);
err = -EINVAL;
error:
dump_stack();
vfree(buf);
return err;
return mtd_peb_check_all_ff(ubi->mtd, pnum, offset, len, 1);
}

View File

@ -128,22 +128,3 @@ void ubi_calculate_reserved(struct ubi_device *ubi)
ubi->bad_peb_count, ubi->bad_peb_limit);
}
}
/**
* ubi_check_pattern - check if buffer contains only a certain byte pattern.
* @buf: buffer to check
* @patt: the pattern to check
* @size: buffer size in bytes
*
* This function returns %1 in there are only @patt bytes in @buf, and %0 if
* something else was also found.
*/
int ubi_check_pattern(const void *buf, uint8_t patt, int size)
{
int i;
for (i = 0; i < size; i++)
if (((const uint8_t *)buf)[i] != patt)
return 0;
return 1;
}

View File

@ -757,7 +757,6 @@ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
int ubi_check_volume(struct ubi_device *ubi, int vol_id);
void ubi_update_reserved(struct ubi_device *ubi);
void ubi_calculate_reserved(struct ubi_device *ubi);
int ubi_check_pattern(const void *buf, uint8_t patt, int size);
/* eba.c */
int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol,
@ -800,7 +799,6 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
int len);
int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture);
int ubi_io_is_bad(const struct ubi_device *ubi, int pnum);
int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum);
int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
struct ubi_ec_hdr *ec_hdr, int verbose);
int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,

View File

@ -1421,11 +1421,6 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
available_consumed = 1;
}
ubi_msg("mark PEB %d as bad", pnum);
err = ubi_io_mark_bad(ubi, pnum);
if (err)
goto out_ro;
if (ubi->beb_rsvd_pebs > 0) {
if (available_consumed) {
/*

View File

@ -192,6 +192,7 @@ static int partition_ioctl(struct cdev *cdev, int request, void *buf)
switch (request) {
case MEMSETBADBLOCK:
case MEMSETGOODBLOCK:
case MEMGETBADBLOCK:
offset = *_buf;
offset += cdev->offset;

View File

@ -24,6 +24,7 @@ struct bbu_handler {
const char *name;
struct list_head list;
#define BBU_HANDLER_FLAG_DEFAULT (1 << 0)
#define BBU_HANDLER_CAN_REFRESH (1 << 1)
unsigned long flags;
/* default device file, can be overwritten on the command line */

View File

@ -118,6 +118,7 @@ struct otp_info {
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
#define MTDFILEMODE _IO('M', 19)
#define MEMSETGOODBLOCK _IOW('M', 20, loff_t)
/*
* Obsolete legacy interface. Keep it in order not to break userspace

View File

@ -189,6 +189,7 @@ struct mtd_info {
/* Bad block management functions */
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markgood) (struct mtd_info *mtd, loff_t ofs);
/* ECC status information */
struct mtd_ecc_stats ecc_stats;
@ -280,6 +281,7 @@ extern struct mtd_info *get_mtd_device_nm(const char *name);
extern void put_mtd_device(struct mtd_info *mtd);
const char *mtd_type_str(struct mtd_info *mtd);
struct mtd_notifier {
void (*add)(struct mtd_info *mtd);
@ -308,8 +310,10 @@ int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs);
int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs);
int mtd_block_markgood(struct mtd_info *mtd, loff_t ofs);
int mtd_all_ff(const void *buf, unsigned int len);
int mtd_buf_all_ff(const void *buf, unsigned int len);
int mtd_buf_check_pattern(const void *buf, uint8_t patt, int size);
/*
* Debugging macro and defines

View File

@ -50,6 +50,7 @@ extern int nand_check_erased_ecc_chunk(void *data, int datalen,
void *ecc, int ecclen,
void *extraoob, int extraooblen,
int bitflips_threshold);
int nand_check_erased_buf(void *buf, int len, int bitflips_threshold);
/* The maximum number of NAND chips in an array */
#define NAND_MAX_CHIPS 8
@ -394,6 +395,7 @@ struct nand_buffers {
* @select_chip: [REPLACEABLE] select chip nr
* @block_bad: [REPLACEABLE] check, if the block is bad
* @block_markbad: [REPLACEABLE] mark the block bad
* @block_markgood: [REPLACEABLE] mark the block good
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling
* ALE/CLE/nCE. Also used to write command and address
* @init_size: [BOARDSPECIFIC] hardwarespecific function for setting
@ -479,6 +481,7 @@ struct nand_chip {
void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
int (*block_markgood)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
u8 *id_data);

View File

@ -1,149 +0,0 @@
/*
* Copyright (C) 2008, 2009 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
* Author: Artem Bityutskiy
*
* MTD library.
*/
#ifndef __LIBMTD_H__
#define __LIBMTD_H__
/* Maximum MTD device name length */
#define MTD_NAME_MAX 127
/* Maximum MTD device type string length */
#define MTD_TYPE_MAX 64
/**
* struct mtd_dev_info - information about an MTD device.
* @node: node pointing to device
* @type: flash type (constants like %MTD_NANDFLASH defined in mtd-abi.h)
* @type_str: static R/O flash type string
* @name: device name
* @size: device size in bytes
* @eb_cnt: count of eraseblocks
* @eb_size: eraseblock size
* @min_io_size: minimum input/output unit size
* @subpage_size: sub-page size
* @oob_size: OOB size (zero if the device does not have OOB area)
* @region_cnt: count of additional erase regions
* @writable: zero if the device is read-only
* @bb_allowed: non-zero if the MTD device may have bad eraseblocks
*/
struct mtd_dev_info
{
const char *node;
int type;
const char type_str[MTD_TYPE_MAX + 1];
long long size;
int eb_cnt;
int eb_size;
int min_io_size;
int subpage_size;
int oob_size;
int region_cnt;
unsigned int writable:1;
unsigned int bb_allowed:1;
};
/**
* mtd_get_dev_info - get information about an MTD device.
* @desc: MTD library descriptor
* @node: name of the MTD device node
* @mtd: the MTD device information is returned here
*
* This function gets information about MTD device defined by the @node device
* node file and saves this information in the @mtd object. Returns %0 in case
* of success and %-1 in case of failure. If MTD subsystem is not present in the
* system, or the MTD device does not exist, errno is set to @ENODEV.
*/
int mtd_get_dev_info(const char *node, struct mtd_dev_info *mtd);
/**
* libmtd_erase - erase an eraseblock.
* @desc: MTD library descriptor
* @mtd: MTD device description object
* @fd: MTD device node file descriptor
* @eb: eraseblock to erase
*
* This function erases eraseblock @eb of MTD device described by @fd. Returns
* %0 in case of success and %-1 in case of failure.
*/
int libmtd_erase(const struct mtd_dev_info *mtd, int fd, int eb);
/**
* mtd_torture - torture an eraseblock.
* @desc: MTD library descriptor
* @mtd: MTD device description object
* @fd: MTD device node file descriptor
* @eb: eraseblock to torture
*
* This function tortures eraseblock @eb. Returns %0 in case of success and %-1
* in case of failure.
*/
int mtd_torture(const struct mtd_dev_info *mtd, int fd, int eb);
/**
* mtd_is_bad - check if eraseblock is bad.
* @mtd: MTD device description object
* @fd: MTD device node file descriptor
* @eb: eraseblock to check
*
* This function checks if eraseblock @eb is bad. Returns %0 if not, %1 if yes,
* and %-1 in case of failure.
*/
int mtd_is_bad(const struct mtd_dev_info *mtd, int fd, int eb);
/**
* mtd_mark_bad - mark an eraseblock as bad.
* @mtd: MTD device description object
* @fd: MTD device node file descriptor
* @eb: eraseblock to mark as bad
*
* This function marks eraseblock @eb as bad. Returns %0 in case of success and
* %-1 in case of failure.
*/
int mtd_mark_bad(const struct mtd_dev_info *mtd, int fd, int eb);
/**
* mtd_read - read data from an MTD device.
* @mtd: MTD device description object
* @fd: MTD device node file descriptor
* @eb: eraseblock to read from
* @offs: offset withing the eraseblock to read from
* @buf: buffer to read data to
* @len: how many bytes to read
*
* This function reads @len bytes of data from eraseblock @eb and offset @offs
* of the MTD device defined by @mtd and stores the read data at buffer @buf.
* Returns %0 in case of success and %-1 in case of failure.
*/
int libmtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
void *buf, int len);
/**
* mtd_write - write data to an MTD device.
* @mtd: MTD device description object
* @fd: MTD device node file descriptor
* @eb: eraseblock to write to
* @offs: offset withing the eraseblock to write to
* @buf: buffer to write
* @len: how many bytes to write
*
* This function writes @len bytes of data to eraseblock @eb and offset @offs
* of the MTD device defined by @mtd. Returns %0 in case of success and %-1 in
* case of failure.
*/
int libmtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
void *buf, int len);
#endif /* __LIBMTD_H__ */

View File

@ -84,12 +84,11 @@ struct mtd_dev_info;
/**
* ubi_scan - scan an MTD device.
* @mtd: information about the MTD device to scan
* @fd: MTD device node file descriptor
* @info: the result of the scanning is returned here
* @verbose: verbose mode: %0 - be silent, %1 - output progress information,
* 2 - debugging output mode
*/
int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info,
int libscan_ubi_scan(struct mtd_info *mtd, struct ubi_scan_info **info,
int verbose);
/**

View File

@ -170,13 +170,13 @@ int ubigen_write_volume(const struct ubigen_info *ui,
* @ec1: erase counter value for @peb1
* @ec2: erase counter value for @peb1
* @vtbl: volume table
* @fd: output file descriptor seeked to the proper position
* @mtd: The mtd device
*
* This function creates the UBI layout volume which contains 2 copies of the
* volume table. Returns zero in case of success and %-1 in case of failure.
*/
int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2,
long long ec1, long long ec2,
struct ubi_vtbl_record *vtbl, int fd);
struct ubi_vtbl_record *vtbl, struct mtd_info *mtd);
#endif /* !__LIBUBIGEN_H__ */

24
include/mtd/mtd-peb.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef __LINUX_MTD_MTDPEB_H
#define __LINUX_MTD_MTDPEB_H
#include <linux/mtd/mtd.h>
int mtd_peb_read(struct mtd_info *mtd, void *buf, int pnum, int offset,
int len);
int mtd_peb_write(struct mtd_info *mtd, const void *buf, int pnum, int offset,
int len);
int mtd_peb_torture(struct mtd_info *mtd, int pnum);
int mtd_peb_erase(struct mtd_info *mtd, int pnum);
int mtd_peb_mark_bad(struct mtd_info *mtd, int pnum);
int mtd_peb_is_bad(struct mtd_info *mtd, int pnum);
int mtd_peb_check_all_ff(struct mtd_info *mtd, int pnum, int offset, int len,
int warn);
int mtd_peb_verify(struct mtd_info *mtd, const void *buf, int pnum,
int offset, int len);
int mtd_num_pebs(struct mtd_info *mtd);
int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset,
int len, int num_bitflips, int random,
int info);
#endif /* __LINUX_MTD_MTDPEB_H */

View File

@ -52,9 +52,6 @@ config LIBSCAN
config LIBUBIGEN
bool
config LIBMTD
bool
config STMP_DEVICE
bool

View File

@ -44,7 +44,6 @@ obj-$(CONFIG_BITREV) += bitrev.o
obj-$(CONFIG_QSORT) += qsort.o
obj-$(CONFIG_LIBSCAN) += libscan.o
obj-$(CONFIG_LIBUBIGEN) += libubigen.o
obj-$(CONFIG_LIBMTD) += libmtd.o
obj-y += gui/
obj-$(CONFIG_XYMODEM) += xymodem.o
obj-y += unlink-recursive.o

View File

@ -1,368 +0,0 @@
/*
* Copyright (C) 2009 Nokia Corporation
* Copyright (C) 2012 Wolfram Sang, Pengutronix e.K. <w.sang@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* Author: Artem Bityutskiy
* Author: Wolfram Sang
*
* This file is part of the MTD library. Based on pre-2.6.30 kernels support,
* now adapted to barebox.
*
* NOTE: No support for 64 bit sizes yet!
*/
#include <common.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <crc.h>
#include <fs.h>
#include <fcntl.h>
#include <ioctl.h>
#include <linux/stat.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/mtd-abi.h>
#include <mtd/libmtd.h>
#include <mtd/utils.h>
#define PROGRAM_NAME "libmtd"
static inline int mtd_ioctl_error(const struct mtd_dev_info *mtd, int eb,
const char *sreq)
{
return sys_errmsg("%s ioctl failed for eraseblock %d (%s)",
sreq, eb, mtd->node);
}
static int mtd_valid_erase_block(const struct mtd_dev_info *mtd, int eb)
{
if (eb < 0 || eb >= mtd->eb_cnt) {
errmsg("bad eraseblock number %d, %s has %d eraseblocks",
eb, mtd->node, mtd->eb_cnt);
errno = EINVAL;
return -1;
}
return 0;
}
int libmtd_erase(const struct mtd_dev_info *mtd, int fd, int eb)
{
int ret;
struct erase_info_user ei;
ret = mtd_valid_erase_block(mtd, eb);
if (ret)
return ret;
ei.start = (__u64)eb * mtd->eb_size;
ei.length = mtd->eb_size;
ret = ioctl(fd, MEMERASE, &ei);
if (ret < 0)
return mtd_ioctl_error(mtd, eb, "MEMERASE");
return 0;
}
/* Patterns to write to a physical eraseblock when torturing it */
static uint8_t patterns[] = {0xa5, 0x5a, 0x0};
/**
* check_pattern - check if buffer contains only a certain byte pattern.
* @buf: buffer to check
* @patt: the pattern to check
* @size: buffer size in bytes
*
* This function returns %1 in there are only @patt bytes in @buf, and %0 if
* something else was also found.
*/
static int check_pattern(const void *buf, uint8_t patt, int size)
{
int i;
for (i = 0; i < size; i++)
if (((const uint8_t *)buf)[i] != patt)
return 0;
return 1;
}
int mtd_torture(const struct mtd_dev_info *mtd, int fd, int eb)
{
int err, i, patt_count;
void *buf;
normsg("run torture test for PEB %d", eb);
patt_count = ARRAY_SIZE(patterns);
buf = xmalloc(mtd->eb_size);
for (i = 0; i < patt_count; i++) {
err = libmtd_erase(mtd, fd, eb);
if (err)
goto out;
/* Make sure the PEB contains only 0xFF bytes */
err = libmtd_read(mtd, fd, eb, 0, buf, mtd->eb_size);
if (err)
goto out;
err = check_pattern(buf, 0xFF, mtd->eb_size);
if (err == 0) {
errmsg("erased PEB %d, but a non-0xFF byte found", eb);
errno = EIO;
goto out;
}
/* Write a pattern and check it */
memset(buf, patterns[i], mtd->eb_size);
err = libmtd_write(mtd, fd, eb, 0, buf, mtd->eb_size);
if (err)
goto out;
memset(buf, ~patterns[i], mtd->eb_size);
err = libmtd_read(mtd, fd, eb, 0, buf, mtd->eb_size);
if (err)
goto out;
err = check_pattern(buf, patterns[i], mtd->eb_size);
if (err == 0) {
errmsg("pattern %x checking failed for PEB %d",
patterns[i], eb);
errno = EIO;
goto out;
}
}
err = 0;
normsg("PEB %d passed torture test, do not mark it a bad", eb);
out:
free(buf);
return -1;
}
int mtd_is_bad(const struct mtd_dev_info *mtd, int fd, int eb)
{
int ret;
loff_t seek;
ret = mtd_valid_erase_block(mtd, eb);
if (ret)
return ret;
if (!mtd->bb_allowed)
return 0;
seek = (loff_t)eb * mtd->eb_size;
ret = ioctl(fd, MEMGETBADBLOCK, &seek);
if (ret == -1)
return mtd_ioctl_error(mtd, eb, "MEMGETBADBLOCK");
return ret;
}
int mtd_mark_bad(const struct mtd_dev_info *mtd, int fd, int eb)
{
int ret;
loff_t seek;
if (!mtd->bb_allowed) {
errno = EINVAL;
return -1;
}
ret = mtd_valid_erase_block(mtd, eb);
if (ret)
return ret;
seek = (loff_t)eb * mtd->eb_size;
ret = ioctl(fd, MEMSETBADBLOCK, &seek);
if (ret == -1)
return mtd_ioctl_error(mtd, eb, "MEMSETBADBLOCK");
return 0;
}
int libmtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
void *buf, int len)
{
int ret, rd = 0;
loff_t seek;
ret = mtd_valid_erase_block(mtd, eb);
if (ret)
return ret;
if (offs < 0 || offs + len > mtd->eb_size) {
errmsg("bad offset %d or length %d, %s eraseblock size is %d",
offs, len, mtd->node, mtd->eb_size);
errno = EINVAL;
return -1;
}
/* Seek to the beginning of the eraseblock */
seek = (loff_t)eb * mtd->eb_size + offs;
if (lseek(fd, seek, SEEK_SET) != seek)
return sys_errmsg("cannot seek %s to offset %llu",
mtd->node, (unsigned long long)seek);
while (rd < len) {
ret = read(fd, buf, len);
if (ret < 0)
return sys_errmsg("cannot read %d bytes from %s (eraseblock %d, offset %d)",
len, mtd->node, eb, offs);
rd += ret;
}
return 0;
}
int libmtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs,
void *buf, int len)
{
int ret;
loff_t seek;
ret = mtd_valid_erase_block(mtd, eb);
if (ret)
return ret;
if (offs < 0 || offs + len > mtd->eb_size) {
errmsg("bad offset %d or length %d, %s eraseblock size is %d",
offs, len, mtd->node, mtd->eb_size);
errno = EINVAL;
return -1;
}
if (offs % mtd->subpage_size) {
errmsg("write offset %d is not aligned to %s min. I/O size %d",
offs, mtd->node, mtd->subpage_size);
errno = EINVAL;
return -1;
}
if (len % mtd->subpage_size) {
errmsg("write length %d is not aligned to %s min. I/O size %d",
len, mtd->node, mtd->subpage_size);
errno = EINVAL;
return -1;
}
/* Seek to the beginning of the eraseblock */
seek = (loff_t)eb * mtd->eb_size + offs;
if (lseek(fd, seek, SEEK_SET) != seek)
return sys_errmsg("cannot seek %s to offset %llu",
mtd->node, (unsigned long long)seek);
ret = write(fd, buf, len);
if (ret != len)
return sys_errmsg("cannot write %d bytes to %s (eraseblock %d, offset %d)",
len, mtd->node, eb, offs);
return 0;
}
/**
* mtd_get_dev_info - fill the mtd_dev_info structure
* @node: name of the MTD device node
* @mtd: the MTD device information is returned here
*/
int mtd_get_dev_info(const char *node, struct mtd_dev_info *mtd)
{
struct mtd_info_user ui;
int fd, ret;
loff_t offs = 0;
memset(mtd, '\0', sizeof(struct mtd_dev_info));
mtd->node = node;
fd = open(node, O_RDWR);
if (fd < 0)
return sys_errmsg("cannot open \"%s\"", node);
if (ioctl(fd, MEMGETINFO, &ui)) {
sys_errmsg("MEMGETINFO ioctl request failed");
goto out_close;
}
ret = ioctl(fd, MEMGETBADBLOCK, &offs);
if (ret == -1) {
if (errno != EOPNOTSUPP) {
sys_errmsg("MEMGETBADBLOCK ioctl failed");
goto out_close;
}
errno = 0;
mtd->bb_allowed = 0;
} else
mtd->bb_allowed = 1;
mtd->type = ui.type;
mtd->size = ui.size;
mtd->eb_size = ui.erasesize;
mtd->min_io_size = ui.writesize;
mtd->oob_size = ui.oobsize;
mtd->subpage_size = ui.subpagesize;
if (mtd->min_io_size <= 0) {
errmsg("%s has insane min. I/O unit size %d",
node, mtd->min_io_size);
goto out_close;
}
if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) {
errmsg("%s has insane eraseblock size %d",
node, mtd->eb_size);
goto out_close;
}
if (mtd->size <= 0 || mtd->size < mtd->eb_size) {
errmsg("%s has insane size %lld",
node, mtd->size);
goto out_close;
}
mtd->eb_cnt = mtd_user_div_by_eb(ui.size, &ui);
switch(mtd->type) {
case MTD_ABSENT:
errmsg("%s (%s) is removable and is not present",
mtd->node, node);
goto out_close;
case MTD_RAM:
strcpy((char *)mtd->type_str, "ram");
break;
case MTD_ROM:
strcpy((char *)mtd->type_str, "rom");
break;
case MTD_NORFLASH:
strcpy((char *)mtd->type_str, "nor");
break;
case MTD_NANDFLASH:
strcpy((char *)mtd->type_str, "nand");
break;
case MTD_DATAFLASH:
strcpy((char *)mtd->type_str, "dataflash");
break;
case MTD_UBIVOLUME:
strcpy((char *)mtd->type_str, "ubi");
break;
default:
goto out_close;
}
if (ui.flags & MTD_WRITEABLE)
mtd->writable = 1;
close(fd);
return 0;
out_close:
close(fd);
return -1;
}

View File

@ -26,26 +26,28 @@
#include <linux/mtd/mtd.h>
#include <linux/stat.h>
#include <linux/mtd/mtd-abi.h>
#include <mtd/libmtd.h>
#include <mtd/mtd-peb.h>
#include <mtd/libscan.h>
#include <mtd/ubi-user.h>
#include <mtd/utils.h>
#include <mtd/ubi-media.h>
#include <asm-generic/div64.h>
int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info,
int libscan_ubi_scan(struct mtd_info *mtd, struct ubi_scan_info **info,
int verbose)
{
int eb, v = (verbose == 2), pr = (verbose == 1);
int eb, v = (verbose == 2), pr = (verbose == 1), eb_cnt;
struct ubi_scan_info *si;
unsigned long long sum = 0;
eb_cnt = mtd_div_by_eb(mtd->size, mtd);
si = calloc(1, sizeof(struct ubi_scan_info));
if (!si)
return sys_errmsg("cannot allocate %zd bytes of memory",
sizeof(struct ubi_scan_info));
si->ec = calloc(mtd->eb_cnt, sizeof(uint32_t));
si->ec = calloc(eb_cnt, sizeof(uint32_t));
if (!si->ec) {
sys_errmsg("cannot allocate %zd bytes of memory",
sizeof(struct ubi_scan_info));
@ -54,8 +56,8 @@ int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **in
si->vid_hdr_offs = si->data_offs = -1;
verbose(v, "start scanning eraseblocks 0-%d", mtd->eb_cnt);
for (eb = 0; eb < mtd->eb_cnt; eb++) {
verbose(v, "start scanning eraseblocks 0-%d", eb_cnt);
for (eb = 0; eb < eb_cnt; eb++) {
int ret;
uint32_t crc;
struct ubi_ec_hdr ech;
@ -65,10 +67,10 @@ int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **in
normsg_cont("scanning eraseblock %d", eb);
if (pr) {
printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2u %% complete ",
eb, (eb + 1) * 100 / mtd->eb_cnt);
eb, (eb + 1) * 100 / eb_cnt);
}
ret = mtd_is_bad(mtd, fd, eb);
ret = mtd_peb_is_bad(mtd, eb);
if (ret == -1)
goto out_ec;
if (ret) {
@ -79,12 +81,12 @@ int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **in
continue;
}
ret = libmtd_read(mtd, fd, eb, 0, &ech, sizeof(struct ubi_ec_hdr));
ret = mtd_peb_read(mtd, &ech, eb, 0, sizeof(struct ubi_ec_hdr));
if (ret < 0)
goto out_ec;
if (be32_to_cpu(ech.magic) != UBI_EC_HDR_MAGIC) {
if (mtd_all_ff(&ech, sizeof(struct ubi_ec_hdr))) {
if (mtd_buf_all_ff(&ech, sizeof(struct ubi_ec_hdr))) {
si->empty_cnt += 1;
si->ec[eb] = EB_EMPTY;
if (v)
@ -121,14 +123,14 @@ int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **in
if (si->vid_hdr_offs == -1) {
si->vid_hdr_offs = be32_to_cpu(ech.vid_hdr_offset);
si->data_offs = be32_to_cpu(ech.data_offset);
if (si->data_offs % mtd->min_io_size) {
if (si->data_offs % mtd->writesize) {
if (pr)
printf("\n");
if (v)
printf(": corrupted because of the below\n");
warnmsg("bad data offset %d at eraseblock %d (n"
"of multiple of min. I/O unit size %d)",
si->data_offs, eb, mtd->min_io_size);
si->data_offs, eb, mtd->writesize);
warnmsg("treat eraseblock %d as corrupted", eb);
si->corrupted_cnt += 1;
si->ec[eb] = EB_CORRUPTED;
@ -174,7 +176,7 @@ int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **in
if (si->ok_cnt != 0) {
/* Calculate mean erase counter */
for (eb = 0; eb < mtd->eb_cnt; eb++) {
for (eb = 0; eb < eb_cnt; eb++) {
if (si->ec[eb] > EC_MAX)
continue;
sum += si->ec[eb];
@ -183,7 +185,7 @@ int libscan_ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **in
si->mean_ec = sum;
}
si->good_cnt = mtd->eb_cnt - si->bad_cnt;
si->good_cnt = eb_cnt - si->bad_cnt;
verbose(v, "finished, mean EC %lld, %d OK, %d corrupted, %d empty, %d "
"alien, bad %d", si->mean_ec, si->ok_cnt, si->corrupted_cnt,
si->empty_cnt, si->alien_cnt, si->bad_cnt);

View File

@ -34,6 +34,7 @@
#include <mtd/utils.h>
#include <mtd/ubi-media.h>
#include <mtd/libubigen.h>
#include <mtd/mtd-peb.h>
void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size,
int subpage_size, int vid_hdr_offs, int ubi_ver,
@ -247,13 +248,12 @@ out_free:
int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2,
long long ec1, long long ec2,
struct ubi_vtbl_record *vtbl, int fd)
struct ubi_vtbl_record *vtbl, struct mtd_info *mtd)
{
int ret;
struct ubigen_vol_info vi;
char *outbuf;
struct ubi_vid_hdr *vid_hdr;
off_t seek;
vi.bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS;
vi.id = UBI_LAYOUT_VOLUME_ID;
@ -277,29 +277,18 @@ int ubigen_write_layout_vol(const struct ubigen_info *ui, int peb1, int peb2,
memset(outbuf + ui->data_offs + ui->vtbl_size, 0xFF,
ui->peb_size - ui->data_offs - ui->vtbl_size);
seek = (off_t) peb1 * ui->peb_size;
if (lseek(fd, seek, SEEK_SET) != seek) {
sys_errmsg("cannot seek output file");
goto out_free;
}
ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec1);
ubigen_init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0);
ret = write(fd, outbuf, ui->peb_size);
if (ret != ui->peb_size) {
ret = mtd_peb_write(mtd, outbuf, peb1, 0, ui->peb_size);
if (ret < 0) {
sys_errmsg("cannot write %d bytes", ui->peb_size);
goto out_free;
}
seek = (off_t) peb2 * ui->peb_size;
if (lseek(fd, seek, SEEK_SET) != seek) {
sys_errmsg("cannot seek output file");
goto out_free;
}
ubigen_init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf, ec2);
ubigen_init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0);
ret = write(fd, outbuf, ui->peb_size);
if (ret != ui->peb_size) {
ret = mtd_peb_write(mtd, outbuf, peb2, 0, ui->peb_size);
if (ret < 0) {
sys_errmsg("cannot write %d bytes", ui->peb_size);
goto out_free;
}