Merge branch 'for-next/imx-bbu-nand-fcb'
This commit is contained in:
commit
fd50a8d758
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
12
common/bbu.c
12
common/bbu.c
|
@ -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
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__ */
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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 */
|
|
@ -52,9 +52,6 @@ config LIBSCAN
|
|||
config LIBUBIGEN
|
||||
bool
|
||||
|
||||
config LIBMTD
|
||||
bool
|
||||
|
||||
config STMP_DEVICE
|
||||
bool
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
368
lib/libmtd.c
368
lib/libmtd.c
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue