Add noecc and oob image properties for NAND devices to match
nandwrite --noecc/--oob. noecc enables raw writes via MTDFILEMODE and
MTD_OPS_RAW. oob treats input as [data][oob] per page, passes OOB to
mtd_write(), and skips pages only when both data and OOB are 0xFF.
Validate NAND-only use and non-zero OOB size, and adjust
buffering for page+OOB input.
Document the properties and add unit tests for raw-mode ioctl and OOB
handling.
Signed-off-by: James Hilliard <
james.h...@gmail.com>
---
doc/source/sw-description.rst | 22 ++++
handlers/flash_handler.c | 133 +++++++++++++++-----
test/test_flash_handler.c | 224 +++++++++++++++++++++++++++++++++-
3 files changed, 342 insertions(+), 37 deletions(-)
diff --git a/doc/source/sw-description.rst b/doc/source/sw-description.rst
index 2150dcc0..975eb502 100644
--- a/doc/source/sw-description.rst
+++ b/doc/source/sw-description.rst
@@ -708,6 +708,28 @@ Alternatively, for the handler “flash”, the *mtdname* can be specified, inst
type = "flash";
}
+The flash handler supports optional properties for NAND devices:
+
+- ``noecc = "true"``: write in raw mode without ECC (equivalent to
+ ``nandwrite --noecc``).
+- ``oob = "true"``: the input image contains OOB data and SWUpdate writes it
+ alongside page data (equivalent to ``nandwrite --oob``). The input layout is
+ ``[page data][oob]`` repeated for each page.
+
+Example:
+
+::
+
+ {
+ filename = "raw-nand.bin";
+ device = "mtd7";
+ type = "flash";
+ properties = {
+ noecc = "true";
+ oob = "true";
+ };
+ }
+
Files
-----
diff --git a/handlers/flash_handler.c b/handlers/flash_handler.c
index bf534e70..4bfe8bb3 100644
--- a/handlers/flash_handler.c
+++ b/handlers/flash_handler.c
@@ -20,9 +20,9 @@
#include <fcntl.h>
#include <stdlib.h>
#include <stdbool.h>
+#include <stdint.h>
#include <errno.h>
#include <assert.h>
-#include <linux/version.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
@@ -111,7 +111,7 @@ struct flash_priv {
unsigned char *filebuf;
/* Amount of bytes we've read from image file into filebuf: */
int filebuf_len;
- /* An offset within filebuf to write to flash starting from: */
+ /* An offset within the erase block (data bytes) to write from: */
int writebuf_offset;
/* Current flash erase block: */
int eb;
@@ -125,6 +125,11 @@ struct flash_priv {
bool check_bad; /* do we need to check for bad blocks? */
bool check_locked; /* do we need to check whether a block is locked? */
bool do_unlock; /* do we need to try to unlock a block? */
+ bool write_oob; /* input contains OOB data */
+ bool no_ecc; /* write without ECC */
+ uint8_t write_mode; /* MTD_OPS_* mode */
+ int page_len; /* data + oob per page when write_oob is set */
+ int filebuf_size; /* bytes allocated for filebuf */
unsigned char *readout_buf; /* a buffer to read erase block into */
};
@@ -186,15 +191,19 @@ static int read_data(struct flash_priv *priv, const unsigned char **p_in_buf,
size_t *p_in_len, unsigned char **p_wbuf, int *p_to_write)
{
int read_available, to_read, write_available, to_write;
+ int page_len = priv->page_len;
+ int page_index = priv->writebuf_offset / priv->mtd->min_io_size;
+ int filebuf_offset = page_index * page_len;
size_t in_len = *p_in_len;
- assert(priv->filebuf_len <= priv->mtd->eb_size);
- assert(priv->writebuf_offset <= priv->filebuf_len);
+ assert(priv->filebuf_len <= priv->filebuf_size);
+ assert(priv->writebuf_offset <= priv->mtd->eb_size);
assert((priv->writebuf_offset % priv->mtd->min_io_size) == 0);
+ assert(filebuf_offset <= priv->filebuf_len);
assert(priv->imglen >= in_len);
/* Read as much as possible data from (*p_in_buf) to priv->filebuf: */
- read_available = priv->mtd->eb_size - priv->filebuf_len;
+ read_available = priv->filebuf_size - priv->filebuf_len;
assert(read_available >= 0);
to_read = (in_len > read_available) ? read_available : in_len;
assert(to_read <= read_available);
@@ -209,38 +218,40 @@ static int read_data(struct flash_priv *priv, const unsigned char **p_in_buf,
/* We've read all image data available.
* Add padding to priv->filebuf if necessary: */
int len, pad_bytes;
- len = ROUND_UP(priv->filebuf_len, priv->mtd->min_io_size);
+ len = ROUND_UP(priv->filebuf_len, page_len);
assert(len >= priv->filebuf_len);
- assert(len <= priv->mtd->eb_size);
+ assert(len <= priv->filebuf_size);
pad_bytes = len - priv->filebuf_len;
assert(pad_bytes >= 0);
erase_buffer(priv->filebuf + priv->filebuf_len, pad_bytes);
priv->filebuf_len = len;
}
- write_available = priv->filebuf_len - priv->writebuf_offset;
+ write_available = priv->filebuf_len - filebuf_offset;
assert(write_available >= 0);
- to_write = ROUND_DOWN(write_available, priv->mtd->min_io_size);
- assert(to_write <= write_available);
- assert((to_write % priv->mtd->min_io_size) == 0);
-
- if (to_write == 0) {
- /* Got not enough data to write. */
- return -1; /* Wait for more data in next flash_write() call. */
- }
-
if (priv->is_nand) {
+ if (write_available < page_len) {
+ /* Got not enough data to write. */
+ return -1; /* Wait for more data in next flash_write() call. */
+ }
/* For NAND flash limit amount of data to be written to a
* single page. This allows us to skip writing "empty" pages
- * (filled with FLASH_EMPTY_BYTE).
- *
- * Note: for NOR flash typical min_io_size is 1. Writing 1 byte
- * at time is not practical. */
+ * (filled with FLASH_EMPTY_BYTE). */
to_write = priv->mtd->min_io_size;
+ } else {
+ /* For NOR flash typical min_io_size is 1. Writing 1 byte at a
+ * time is not practical. */
+ to_write = ROUND_DOWN(write_available, priv->mtd->min_io_size);
+ assert(to_write <= write_available);
+ assert((to_write % priv->mtd->min_io_size) == 0);
+ if (to_write == 0) {
+ /* Got not enough data to write. */
+ return -1; /* Wait for more data in next flash_write() call. */
+ }
}
- *p_wbuf = priv->filebuf + priv->writebuf_offset;
+ *p_wbuf = priv->filebuf + filebuf_offset;
*p_to_write = to_write;
return 0; /* Need to write some data. */
}
@@ -359,6 +370,14 @@ static int prepare_new_erase_block(struct flash_priv *priv)
return too_many_bad_blocks(priv->mtdnum);
}
+static inline bool flash_has_pending_data(const struct flash_priv *priv)
+{
+ int page_index = priv->writebuf_offset / priv->mtd->min_io_size;
+ int filebuf_offset = page_index * priv->page_len;
+
+ return filebuf_offset < priv->filebuf_len;
+}
+
/*
* A callback to be passed to copyimage().
* Write as much input data to flash as possible at this time.
@@ -374,9 +393,11 @@ static int flash_write(void *out, const void *buf, size_t len)
struct flash_priv *priv = (struct flash_priv *)out;
const unsigned char **pbuf = (const unsigned char**)&buf;
- while ((len > 0) || (priv->writebuf_offset < priv->filebuf_len)) {
+ while ((len > 0) || flash_has_pending_data(priv)) {
unsigned char *wbuf;
+ unsigned char *oob = NULL;
int to_write;
+ int data_offset;
assert(priv->eb <= priv->eb_end);
if (priv->eb == priv->eb_end)
@@ -399,14 +420,23 @@ static int flash_write(void *out, const void *buf, size_t len)
/* Now priv->eb points to a valid erased block we can write
* data into. */
+ data_offset = priv->writebuf_offset;
+ if (priv->write_oob) {
+ oob = wbuf + priv->mtd->min_io_size;
+ }
+
ret = buffer_check_pattern(wbuf, to_write, FLASH_EMPTY_BYTE);
+ if (ret && priv->write_oob)
+ ret = buffer_check_pattern(oob, priv->mtd->oob_size,
+ FLASH_EMPTY_BYTE);
if (ret) {
ret = 0; /* There is no need to write "empty" bytes. */
} else {
/* Write data to flash: */
ret = mtd_write(priv->libmtd, priv->mtd, priv->fdout,
- priv->eb, priv->writebuf_offset, wbuf,
- to_write, NULL, 0, MTD_OPS_PLACE_OOB);
+ priv->eb, data_offset, wbuf, to_write,
+ oob, oob ? priv->mtd->oob_size : 0,
+ priv->write_mode);
}
if (ret) {
if (errno != EIO)
@@ -428,8 +458,8 @@ static int flash_write(void *out, const void *buf, size_t len)
}
priv->writebuf_offset += to_write;
- assert(priv->writebuf_offset <= priv->filebuf_len);
- assert(priv->filebuf_len <= priv->mtd->eb_size);
+ assert(priv->writebuf_offset <= priv->mtd->eb_size);
+ assert(priv->filebuf_len <= priv->filebuf_size);
if (priv->writebuf_offset == priv->mtd->eb_size) {
priv->eb++;
priv->writebuf_offset = 0;
@@ -445,9 +475,34 @@ static int flash_write_image(int mtdnum, struct img_type *img)
struct flash_priv priv;
char mtd_device[LINESIZE];
int ret;
+ long long data_len;
struct flash_description *flash = get_flash_info();
priv.mtd = &flash->mtd_info[mtdnum].mtd;
assert((priv.mtd->eb_size % priv.mtd->min_io_size) == 0);
+ priv.libmtd = flash->libmtd;
+ priv.is_nand = isNand(flash, mtdnum);
+ priv.write_oob = strtobool(dict_get_value(&img->properties, "oob"));
+ priv.no_ecc = strtobool(dict_get_value(&img->properties, "noecc"));
+ if (priv.write_oob && !priv.is_nand) {
+ ERROR("OOB write is supported only for NAND flashes");
+ return -EINVAL;
+ }
+ if (priv.write_oob && !priv.mtd->oob_size) {
+ ERROR("OOB write requested but no OOB size is available");
+ return -EINVAL;
+ }
+ if (priv.no_ecc && !priv.is_nand) {
+ WARN("noecc property ignored for non-NAND flashes");
+ priv.no_ecc = false;
+ }
+ priv.write_mode = priv.no_ecc ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB;
+ priv.page_len = priv.mtd->min_io_size +
+ (priv.write_oob ? priv.mtd->oob_size : 0);
+ priv.filebuf_size = priv.mtd->eb_size;
+ if (priv.write_oob) {
+ int pages_per_eb = priv.mtd->eb_size / priv.mtd->min_io_size;
+ priv.filebuf_size = pages_per_eb * priv.page_len;
+ }
if (!mtd_dev_present(flash->libmtd, mtdnum)) {
ERROR("MTD %d does not exist", mtdnum);
@@ -474,7 +529,13 @@ static int flash_write_image(int mtdnum, struct img_type *img)
if (!priv.imglen)
return 0;
- if (priv.imglen > priv.mtd->size - img->seek) {
+ data_len = priv.imglen;
+ if (priv.write_oob) {
+ long long pages = (priv.imglen + priv.page_len - 1) /
+ priv.page_len;
+ data_len = pages * priv.mtd->min_io_size;
+ }
+ if (data_len > priv.mtd->size - img->seek) {
ERROR("Image %s does not fit into mtd%d", img->fname, mtdnum);
return -ENOSPC;
}
@@ -487,17 +548,25 @@ static int flash_write_image(int mtdnum, struct img_type *img)
return -ret;
}
+ if (priv.no_ecc) {
+ if (ioctl(priv.fdout, MTDFILEMODE,
+ (void *) MTD_FILE_MODE_RAW)) {
+ ret = errno;
+ ERROR("RAW mode access failed: %s", STRERROR(ret));
+ ret = -ret;
+ goto end;
+ }
+ }
+
priv.mtdnum = mtdnum;
priv.filebuf_len = 0;
priv.writebuf_offset = 0;
priv.eb = (int)(img->seek / priv.mtd->eb_size);
priv.eb_end = (int)(priv.mtd->size / priv.mtd->eb_size);
- priv.libmtd = flash->libmtd;
- priv.is_nand = isNand(flash, mtdnum);
priv.check_bad = true;
priv.check_locked = true;
- ret = priv.mtd->eb_size;
+ ret = priv.filebuf_size;
if (!priv.is_nand)
ret += priv.mtd->eb_size;
priv.filebuf = malloc(ret);
@@ -507,7 +576,7 @@ static int flash_write_image(int mtdnum, struct img_type *img)
goto end;
}
if (!priv.is_nand)
- priv.readout_buf = priv.filebuf + priv.mtd->eb_size;
+ priv.readout_buf = priv.filebuf + priv.filebuf_size;
ret = copyimage(&priv, img, flash_write);
free(priv.filebuf);
diff --git a/test/test_flash_handler.c b/test/test_flash_handler.c
index 0ec9e6fa..daf3a3b9 100644
--- a/test/test_flash_handler.c
+++ b/test/test_flash_handler.c
@@ -11,6 +11,7 @@
#include <stdint.h>
#include <libmtd.h>
+#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
#include <stdbool.h>
@@ -69,11 +70,24 @@ static unsigned char *bad_blocks;
static unsigned char *locked_blocks;
static unsigned char *written_pages;
static unsigned char *flash_memory;
+static unsigned char *oob_memory;
/* Expected flash state after test run: */
static unsigned char *expected_bad_blocks;
static unsigned char *expected_locked_blocks;
static unsigned char *expected_written_pages;
static unsigned char *expected_flash_memory;
+static unsigned char *expected_oob_memory;
+static int oob_bytes;
+
+static bool expect_oob;
+static int expected_oob_len;
+static bool expect_write_mode;
+static uint8_t expected_write_mode;
+static bool expect_ioctl;
+static unsigned long expected_ioctl_request;
+static uintptr_t expected_ioctl_arg;
+static int ioctl_return;
+static int ioctl_errno;
void *__real_malloc(size_t size);
static void *(*impl_malloc)(size_t size) = __real_malloc;
@@ -124,6 +138,26 @@ off_t __wrap_lseek(int fd, off_t offset, int whence)
return __real_lseek(fd, offset, whence);
}
+int __wrap_ioctl(int fd, unsigned long request, ...);
+int __wrap_ioctl(int fd, unsigned long request, ...)
+{
+ va_list ap;
+ void *arg;
+
+ va_start(ap, request);
+ arg = va_arg(ap, void *);
+ va_end(ap);
+
+ assert_true(expect_ioctl);
+ assert_int_equal(MTD_FD, fd);
+ assert_true(request == expected_ioctl_request);
+ assert_true((uintptr_t)arg == expected_ioctl_arg);
+
+ if (ioctl_return < 0)
+ errno = ioctl_errno;
+ return ioctl_return;
+}
+
int __wrap_mtd_dev_present(libmtd_t desc, int mtd_num);
int __wrap_mtd_dev_present(libmtd_t UNUSED desc, int mtd_num)
{
@@ -275,8 +309,12 @@ static int default_mtd_erase(libmtd_t UNUSED desc,
unsigned int page;
memset(flash_memory + eb * mtd->eb_size, FLASH_EMPTY_BYTE,
mtd->eb_size);
- FOREACH_PAGE_IN_EB(page, mtd, eb)
+ FOREACH_PAGE_IN_EB(page, mtd, eb) {
clear_bit(written_pages, page);
+ if (oob_memory)
+ memset(oob_memory + page * mtd->oob_size,
+ FLASH_EMPTY_BYTE, mtd->oob_size);
+ }
return 0;
}
static int (*impl_mtd_erase)(libmtd_t desc, const struct mtd_dev_info *mtd,
@@ -294,8 +332,8 @@ int __wrap_mtd_erase(libmtd_t desc, const struct mtd_dev_info *mtd, int fd,
static int default_mtd_write(libmtd_t UNUSED desc,
const struct mtd_dev_info *mtd, int UNUSED fd, int eb,
- int offs, void *data, int len, void UNUSED *oob,
- int UNUSED ooblen, uint8_t UNUSED mode)
+ int offs, void *data, int len, void *oob,
+ int ooblen, uint8_t UNUSED mode)
{
unsigned int page;
unsigned int flash_offset = eb * mtd->eb_size + offs;
@@ -306,6 +344,14 @@ static int default_mtd_write(libmtd_t UNUSED desc,
flash_offset += mtd->min_io_size;
pdata += mtd->min_io_size;
}
+ if (oob && oob_memory) {
+ const unsigned char *poob = (const unsigned char *)oob;
+ FOREACH_PAGE_WRITTEN(page, mtd, eb, offs, len) {
+ memcpy(oob_memory + page * mtd->oob_size, poob,
+ mtd->oob_size);
+ poob += ooblen;
+ }
+ }
return 0;
}
@@ -326,13 +372,20 @@ int __wrap_mtd_write(libmtd_t desc, const struct mtd_dev_info *mtd, int fd,
assert_ptr_not_equal(NULL, data);
assert_true(len > 0);
assert_true(offs + len <= mtd->eb_size);
- assert_ptr_equal(NULL, oob);
- assert_int_equal(0, ooblen);
+ if (expect_oob) {
+ assert_ptr_not_equal(NULL, oob);
+ assert_int_equal(expected_oob_len, ooblen);
+ } else {
+ assert_ptr_equal(NULL, oob);
+ assert_int_equal(0, ooblen);
+ }
assert_false(get_bit(bad_blocks, eb));
assert_false(get_bit(locked_blocks, eb));
assert_true(offs <= mtd->eb_size - mtd->min_io_size);
assert_int_equal(0, offs % mtd->min_io_size);
assert_int_equal(0, len % mtd->min_io_size);
+ if (expect_write_mode)
+ assert_int_equal(expected_write_mode, mode);
if (mtd->type == MTD_NANDFLASH) {
/* Follow "write once rule" for NAND flash. */
@@ -466,6 +519,13 @@ static void copy_flash_state(void)
expected_flash_memory = malloc(mtd->size);
assert_ptr_not_equal(NULL, expected_flash_memory);
memcpy(expected_flash_memory, flash_memory, mtd->size);
+
+ expected_oob_memory = NULL;
+ if (oob_memory) {
+ expected_oob_memory = malloc(oob_bytes);
+ assert_ptr_not_equal(NULL, expected_oob_memory);
+ memcpy(expected_oob_memory, oob_memory, oob_bytes);
+ }
}
static void init_flash_state(void)
@@ -496,6 +556,17 @@ static void init_flash_state(void)
assert_ptr_not_equal(NULL, flash_memory);
/* Fill flash memory with initial pattern: */
memset(flash_memory, 0xA5, mtd->size);
+
+ oob_memory = NULL;
+ oob_bytes = 0;
+ if ((mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH) &&
+ mtd->oob_size > 0) {
+ int pages = (int)(mtd->size / mtd->min_io_size);
+ oob_bytes = pages * mtd->oob_size;
+ oob_memory = malloc(oob_bytes);
+ assert_ptr_not_equal(NULL, oob_memory);
+ memset(oob_memory, 0xA5, oob_bytes);
+ }
}
static void test_init(void)
@@ -525,6 +596,8 @@ static void verify_flash_state(void)
assert_memory_equal(expected_written_pages, written_pages,
pages_bytes);
assert_memory_equal(expected_flash_memory, flash_memory, mtd->size);
+ if (oob_memory)
+ assert_memory_equal(expected_oob_memory, oob_memory, oob_bytes);
}
static void run_flash_test(void UNUSED **state, int expected_return_code)
@@ -556,11 +629,24 @@ static int test_setup(void UNUSED **state)
locked_blocks = NULL;
written_pages = NULL;
flash_memory = NULL;
+ oob_memory = NULL;
expected_bad_blocks = NULL;
expected_locked_blocks = NULL;
expected_written_pages = NULL;
expected_flash_memory = NULL;
+ expected_oob_memory = NULL;
+ oob_bytes = 0;
+
+ expect_oob = false;
+ expected_oob_len = 0;
+ expect_write_mode = false;
+ expected_write_mode = 0;
+ expect_ioctl = false;
+ expected_ioctl_request = 0;
+ expected_ioctl_arg = 0;
+ ioctl_return = 0;
+ ioctl_errno = 0;
/* Tests can overwrite the following default implementations to inject
* errors: */
@@ -582,6 +668,8 @@ static int test_setup(void UNUSED **state)
mtd_ubi_info_s.mtd.size = 1024;
mtd_ubi_info_s.mtd.eb_size = 16;
mtd_ubi_info_s.mtd.min_io_size = 8;
+ mtd_ubi_info_s.mtd.oob_size = 0;
+ dict_drop_db(&image.properties);
return 0;
}
@@ -592,11 +680,13 @@ static int test_teardown(void UNUSED **state)
locked_blocks,
written_pages,
flash_memory,
+ oob_memory,
expected_bad_blocks,
expected_locked_blocks,
expected_written_pages,
expected_flash_memory,
+ expected_oob_memory,
image_buf,
};
@@ -607,6 +697,7 @@ static int test_teardown(void UNUSED **state)
int ret = close(image.fdin);
assert_int_equal(0, ret);
}
+ dict_drop_db(&image.properties);
return 0;
}
@@ -627,6 +718,87 @@ static void test_simple(void **state)
run_flash_test(state, 0);
}
+static void test_noecc_write_mode(void **state)
+{
+ image.seek = 0;
+ image.size = 16;
+ mtd_ubi_info_s.mtd.size = 1024;
+ mtd_ubi_info_s.mtd.eb_size = 16;
+ mtd_ubi_info_s.mtd.min_io_size = 8;
+ dict_set_value(&image.properties, "noecc", "true");
+ test_init();
+
+ expect_write_mode = true;
+ expected_write_mode = MTD_OPS_RAW;
+ expect_ioctl = true;
+ expected_ioctl_request = MTDFILEMODE;
+ expected_ioctl_arg = (uintptr_t)MTD_FILE_MODE_RAW;
+ ioctl_return = 0;
+
+ copy_flash_state();
+ memcpy(expected_flash_memory, image_buf, image.size);
+ clear_bit(expected_locked_blocks, 0);
+
+ run_flash_test(state, 0);
+}
+
+static void test_oob_write_basic(void **state)
+{
+ image.seek = 0;
+ image.size = 24;
+ mtd_ubi_info_s.mtd.size = 1024;
+ mtd_ubi_info_s.mtd.eb_size = 16;
+ mtd_ubi_info_s.mtd.min_io_size = 8;
+ mtd_ubi_info_s.mtd.oob_size = 4;
+ dict_set_value(&image.properties, "oob", "true");
+ test_init();
+
+ expect_oob = true;
+ expected_oob_len = mtd_ubi_info_s.mtd.oob_size;
+ expect_write_mode = true;
+ expected_write_mode = MTD_OPS_PLACE_OOB;
+
+ copy_flash_state();
+ memcpy(expected_flash_memory, image_buf, 8);
+ memcpy(expected_flash_memory + 8, image_buf + 12, 8);
+ memcpy(expected_oob_memory, image_buf + 8, 4);
+ memcpy(expected_oob_memory + 4, image_buf + 20, 4);
+ clear_bit(expected_locked_blocks, 0);
+
+ run_flash_test(state, 0);
+}
+
+static void test_oob_write_noecc(void **state)
+{
+ image.seek = 0;
+ image.size = 24;
+ mtd_ubi_info_s.mtd.size = 1024;
+ mtd_ubi_info_s.mtd.eb_size = 16;
+ mtd_ubi_info_s.mtd.min_io_size = 8;
+ mtd_ubi_info_s.mtd.oob_size = 4;
+ dict_set_value(&image.properties, "oob", "true");
+ dict_set_value(&image.properties, "noecc", "true");
+ test_init();
+
+ expect_oob = true;
+ expected_oob_len = mtd_ubi_info_s.mtd.oob_size;
+ expect_write_mode = true;
+ expected_write_mode = MTD_OPS_RAW;
+ expect_ioctl = true;
+ expected_ioctl_request = MTDFILEMODE;
+ expected_ioctl_arg = (uintptr_t)MTD_FILE_MODE_RAW;
+ ioctl_return = 0;
+
+ copy_flash_state();
+ memcpy(expected_flash_memory, image_buf, 8);
+ memcpy(expected_flash_memory + 8, image_buf + 12, 8);
+ memcpy(expected_oob_memory, image_buf + 8, 4);
+ memcpy(expected_oob_memory + 4, image_buf + 20, 4);
+ clear_bit(expected_locked_blocks, 0);
+
+ run_flash_test(state, 0);
+}
+
static void test_simple_NOR(void **state)
{
image.seek = 0;
@@ -1177,6 +1349,17 @@ static void patch_image_buf_empty_bytes_2(unsigned char *buf)
memset(buf, FLASH_EMPTY_BYTE, 16);
}
+static void patch_image_buf_oob_data_empty(unsigned char *buf)
+{
+ struct mtd_dev_info *mtd = &mtd_ubi_info_s.mtd;
+ int page_len = mtd->min_io_size + mtd->oob_size;
+ int pages = image.size / page_len;
+
+ for (int page = 0; page < pages; page++)
+ memset(buf + page * page_len, FLASH_EMPTY_BYTE,
+ mtd->min_io_size);
+}
+
static void test_no_mtd_write_empty_image_bytes(void **state)
{
image.seek = 0;
@@ -1201,6 +1384,33 @@ static void test_no_mtd_write_empty_image_bytes(void **state)
run_flash_test(state, 0);
}
+static void test_oob_write_data_empty(void **state)
+{
+ image.seek = 0;
+ image.size = 24;
+ mtd_ubi_info_s.mtd.size = 1024;
+ mtd_ubi_info_s.mtd.eb_size = 16;
+ mtd_ubi_info_s.mtd.min_io_size = 8;
+ mtd_ubi_info_s.mtd.oob_size = 4;
+ patch_image_buf = patch_image_buf_oob_data_empty;
+ dict_set_value(&image.properties, "oob", "true");
+ test_init();
+
+ expect_oob = true;
+ expected_oob_len = mtd_ubi_info_s.mtd.oob_size;
+ expect_write_mode = true;
+ expected_write_mode = MTD_OPS_PLACE_OOB;
+
+ copy_flash_state();
+ memset(expected_flash_memory, FLASH_EMPTY_BYTE,
+ mtd_ubi_info_s.mtd.eb_size);
+ memcpy(expected_oob_memory, image_buf + 8, 4);
+ memcpy(expected_oob_memory + 4, image_buf + 20, 4);
+ clear_bit(expected_locked_blocks, 0);
+
+ run_flash_test(state, 0);
+}
+
static void test_mtd_write_failure(void **state)
{
image.seek = 0;
@@ -1422,6 +1632,9 @@ int main(void)
{
static const struct CMUnitTest tests[] = {
TEST(simple),
+ TEST(noecc_write_mode),
+ TEST(oob_write_basic),
+ TEST(oob_write_noecc),
TEST(simple_NOR),
TEST(padding_less_than_page),
TEST(padding_page),
@@ -1450,6 +1663,7 @@ int main(void)
TEST(mtd_erase_failure),
TEST(mtd_erase_failure_EIO),
TEST(no_mtd_write_empty_image_bytes),
+ TEST(oob_write_data_empty),
TEST(mtd_write_failure),
TEST(mtd_write_bad_block),
TEST(mtd_write_bad_block_erase_failure),
--
2.43.0