#include <stdarg.h> #include <setjmp.h> #include <stddef.h> #include <stdint.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <unistd.h> #include <cmocka.h> #include "mtd/mtd-user.h" #include "libmtd.h" #include "libmtd_int.h" #include "test_lib.h" static libmtd_t mock_libmtd_open() { /* create a mock object for libmtd, not using sysfs */ expect_open(SYSFS_ROOT "/class/mtd/mtd0/name", O_RDONLY, 4); expect_close(4,0); libmtd_t lib = libmtd_open(); assert_non_null(lib); return lib; } static void test_libmtd_open(void **state) { expect_open(SYSFS_ROOT "/class/mtd/mtd0/name", O_RDONLY, 4); expect_close(4,0); struct libmtd *lib = libmtd_open(); assert_non_null(lib); assert_string_equal(lib->sysfs_mtd, SYSFS_ROOT "/class/mtd"); assert_string_equal(lib->mtd, SYSFS_ROOT "/class/mtd/mtd%d"); assert_string_equal(lib->mtd_name, SYSFS_ROOT "/class/mtd/mtd%d/name"); assert_string_equal(lib->mtd_dev, SYSFS_ROOT "/class/mtd/mtd%d/dev"); assert_string_equal(lib->mtd_type, SYSFS_ROOT "/class/mtd/mtd%d/type"); assert_string_equal(lib->mtd_eb_size, SYSFS_ROOT "/class/mtd/mtd%d/erasesize"); assert_string_equal(lib->mtd_size, SYSFS_ROOT "/class/mtd/mtd%d/size"); assert_string_equal(lib->mtd_min_io_size, SYSFS_ROOT "/class/mtd/mtd%d/writesize"); assert_string_equal(lib->mtd_subpage_size, SYSFS_ROOT "/class/mtd/mtd%d/subpagesize"); assert_string_equal(lib->mtd_oob_size, SYSFS_ROOT "/class/mtd/mtd%d/oobsize"); assert_string_equal(lib->mtd_region_cnt, SYSFS_ROOT "/class/mtd/mtd%d/numeraseregions"); assert_string_equal(lib->mtd_flags, SYSFS_ROOT "/class/mtd/mtd%d/flags"); libmtd_close(lib); (void) state; } static void test_mtd_dev_present(void **state) { int ret; libmtd_t lib = mock_libmtd_open(); expect_stat(SYSFS_ROOT "/class/mtd/mtd0", 0); ret = mtd_dev_present(lib, 0); assert_int_equal(ret, 1); libmtd_close(lib); (void) state; } static void test_mtd_mark_bad(void **state) { struct mtd_dev_info mtd; loff_t seek; int eb = 12; memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; seek = (loff_t)eb * mtd.eb_size; expect_ioctl(MEMSETBADBLOCK, 0, &seek, sizeof(seek)); int r = mtd_mark_bad(&mtd, 4, eb); assert_int_equal(r, 0); (void) state; } static void test_mtd_is_bad(void **state) { struct mtd_dev_info mtd; loff_t seek; int eb = 0x42; memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; seek = (loff_t)eb * mtd.eb_size; expect_ioctl(MEMGETBADBLOCK, 0, &seek, sizeof(seek)); int r = mtd_is_bad(&mtd, 4, eb); assert_int_equal(r, 0); (void) state; } static void test_mtd_lock(void **state) { int eb = 0xBA; struct mtd_dev_info mtd; memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; struct erase_info_user ei; memset(&ei, 0, sizeof(ei)); ei.start = eb * mtd.eb_size; ei.length = mtd.eb_size; expect_ioctl(MEMLOCK, 0, &ei, sizeof(ei)); int r = mtd_lock(&mtd, 4, eb); assert_int_equal(r, 0); (void) state; } static void test_mtd_unlock(void **state) { int eb = 0xBA; struct mtd_dev_info mtd; memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; struct erase_info_user ei; memset(&ei, 0, sizeof(ei)); ei.start = eb * mtd.eb_size; ei.length = mtd.eb_size; expect_ioctl(MEMUNLOCK, 0, &ei, sizeof(ei)); int r = mtd_unlock(&mtd, 4, eb); assert_int_equal(r, 0); (void) state; } static void test_mtd_is_locked(void **state) { int eb = 0xBA; struct mtd_dev_info mtd; memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; struct erase_info_user ei; memset(&ei, 0, sizeof(ei)); ei.start = eb * mtd.eb_size; ei.length = mtd.eb_size; expect_ioctl(MEMISLOCKED, 0, &ei, sizeof(ei)); int r = mtd_is_locked(&mtd, 4, eb); assert_int_equal(r, 0); (void) state; } static void test_mtd_regioninfo(void **state) { struct region_info_user req; struct region_info_user rr; memset(&req, 0, sizeof(req)); memset(&rr, 0, sizeof(rr)); int mock_fd = 4; int regidx = 0xAA; rr.regionindex = regidx; expect_ioctl(MEMGETREGIONINFO, 0, &rr, sizeof(rr)); int r = mtd_regioninfo(mock_fd, regidx, &req); assert_int_equal(r, 0); (void) state; } static void test_mtd_erase_multi(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_dev_info mtd; struct erase_info_user ei; struct erase_info_user64 ei64; int eb = 0x3C; int blocks = 3; memset(&ei, 0, sizeof(ei)); memset(&ei64, 0, sizeof(ei64)); memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; ei64.start = (uint64_t)eb * mtd.eb_size; ei64.length = (uint64_t)mtd.eb_size * blocks; ei.start = ei64.start; ei.length = ei64.length; /* non offs64 first */ lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; expect_ioctl(MEMERASE, 0, &ei, sizeof(ei)); int r = mtd_erase_multi(lib, &mtd, 4, eb, blocks); assert_int_equal(r, 0); lib->offs64_ioctls = OFFS64_IOCTLS_SUPPORTED; expect_ioctl(MEMERASE64, 0, &ei64, sizeof(ei64)); r = mtd_erase_multi(lib, &mtd, 4, eb, blocks); assert_int_equal(r, 0); libmtd_close(lib); (void) state; } /* this is the same as above but with blocks == 1 and a * different function call. * libmtd is mapping mtd_erase to mtd_erase_multi with 1 block */ static void test_mtd_erase(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_dev_info mtd; struct erase_info_user ei; struct erase_info_user64 ei64; int eb = 0x3C; int blocks = 1; memset(&ei, 0, sizeof(ei)); memset(&ei64, 0, sizeof(ei64)); memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; ei64.start = (uint64_t)eb * mtd.eb_size; ei64.length = (uint64_t)mtd.eb_size * blocks; ei.start = ei64.start; ei.length = ei64.length; /* non offs64 first */ lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; expect_ioctl(MEMERASE, 0, &ei, sizeof(ei)); int r = mtd_erase(lib, &mtd, 4, eb); assert_int_equal(r, 0); lib->offs64_ioctls = OFFS64_IOCTLS_SUPPORTED; expect_ioctl(MEMERASE64, 0, &ei64, sizeof(ei64)); r = mtd_erase(lib, &mtd, 4, eb); assert_int_equal(r, 0); libmtd_close(lib); (void) state; } static void test_mtd_read(void **state) { int mock_fd = 4; int eb = 0xE0; int offs = 43; int len = 28; off_t seek; char buf[28]; struct mtd_dev_info mtd; memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; seek = (off_t)eb * mtd.eb_size + offs; expect_lseek(seek, SEEK_SET, seek); expect_read(len, len); int r = mtd_read(&mtd, mock_fd, eb, offs, &buf, len); assert_int_equal(r, 0); (void) state; } static void test_mtd_write_nooob(void **state) { struct libmtd *lib = mock_libmtd_open(); int mock_fd = 4; int eb = 0xE0; int offs = 64; int len = 64; off_t seek; char buf[64]; memset(buf, 0xAA, len); struct mtd_dev_info mtd; memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; mtd.subpage_size = 64; seek = (off_t)eb * mtd.eb_size + offs; expect_lseek(seek, SEEK_SET, seek); expect_write(buf, len, len); int r = mtd_write(lib, &mtd, mock_fd, eb, offs, buf, len, NULL, 0, 0); assert_int_equal(r, 0); libmtd_close(lib); (void)state; } static void test_mtd_write_withoob(void **state) { struct libmtd *lib = mock_libmtd_open(); int mock_fd = 4; int eb = 0xE0; int offs = 64; int len = 64; int oob_len = 64; uint8_t mode = 3; off_t seek; char buf[64], oob_data[64]; struct mtd_dev_info mtd; struct mtd_write_req req; memset(buf, 0xAA, len); memset(oob_data, 0xBA, oob_len); memset(&mtd, 0, sizeof(mtd)); memset(&req, 0, sizeof(req)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; mtd.subpage_size = 64; seek = (off_t)eb * mtd.eb_size + offs; req.start = seek; req.len = len; req.ooblen = oob_len; req.usr_data = (uint64_t)(unsigned long)buf; req.usr_oob = (uint64_t)(unsigned long)oob_data; req.mode = mode; expect_ioctl(MEMWRITE, 0, &req, sizeof(req)); int r = mtd_write(lib, &mtd, mock_fd, eb, offs, buf, len, oob_data, oob_len, mode); assert_int_equal(r, 0); libmtd_close(lib); (void) state; } static void test_mtd_read_oob(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_dev_info mtd; struct mtd_oob_buf64 oob64; struct mtd_oob_buf oob; int mock_fd = 4; uint64_t start = 0, length = 64; char buf[64]; memset(buf, 0xCD, 64); memset(&oob, 0, sizeof(oob)); memset(&oob64, 0, sizeof(oob64)); memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; mtd.subpage_size = 64; mtd.oob_size = 128; oob64.start = start; oob64.length = length; oob64.usr_ptr = (uint64_t)(unsigned long)buf; oob.start = oob64.start; oob.length = oob64.length; oob.ptr = buf; lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; expect_ioctl(MEMREADOOB, 0, &oob, sizeof(oob)); int r = mtd_read_oob(lib, &mtd, mock_fd, start, length, buf); assert_int_equal(r, 0); lib->offs64_ioctls = OFFS64_IOCTLS_SUPPORTED; expect_ioctl(MEMREADOOB64, 0, &oob64, sizeof(oob64)); r = mtd_read_oob(lib, &mtd, mock_fd, start, length, buf); assert_int_equal(r, 0); libmtd_close(lib); (void) state; } /* basically the same as above but write calls */ static void test_mtd_write_oob(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_dev_info mtd; struct mtd_oob_buf64 oob64; struct mtd_oob_buf oob; int mock_fd = 4; uint64_t start = 0, length = 64; char buf[64]; memset(buf, 0xCD, 64); memset(&oob, 0, sizeof(oob)); memset(&oob64, 0, sizeof(oob64)); memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; mtd.subpage_size = 64; mtd.oob_size = 128; oob64.start = start; oob64.length = length; oob64.usr_ptr = (uint64_t)(unsigned long)buf; oob.start = oob64.start; oob.length = oob64.length; oob.ptr = buf; lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED; expect_ioctl(MEMWRITEOOB, 0, &oob, sizeof(oob)); int r = mtd_write_oob(lib, &mtd, mock_fd, start, length, buf); assert_int_equal(r, 0); lib->offs64_ioctls = OFFS64_IOCTLS_SUPPORTED; expect_ioctl(MEMWRITEOOB64, 0, &oob64, sizeof(oob64)); r = mtd_write_oob(lib, &mtd, mock_fd, start, length, buf); assert_int_equal(r, 0); libmtd_close(lib); (void)state; } static void test_mtd_get_info(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_info info; memset(&info, 0, sizeof(info)); int r = mtd_get_info(lib, &info); assert_int_equal(info.sysfs_supported, 1); assert_int_equal(info.highest_mtd_num, 0); assert_int_equal(info.lowest_mtd_num, 0); assert_int_equal(info.mtd_dev_cnt, 1); assert_int_equal(r, 0); libmtd_close(lib); (void)state; } static void test_mtd_get_dev_info1(void **state) { struct libmtd *lib = mock_libmtd_open(); struct mtd_dev_info info; int dev_num = 0; memset(&info, 0, sizeof(info)); expect_stat(SYSFS_ROOT "/class/mtd/mtd0", 0); expect_open(SYSFS_ROOT "/class/mtd/mtd0/dev", O_RDONLY, 0); expect_read_real(50,0); expect_read(1,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/name", O_RDONLY, 0); expect_read_real(128,0); expect_read(1,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/type", O_RDONLY, 4); expect_read(65,0); expect_read(1,0); expect_close(4,0); expect_open(SYSFS_ROOT "/class/mtd/mtd0/erasesize", O_RDONLY, 0); expect_read_real(50, 0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/size", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/writesize", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/subpagesize", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/oobsize", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/numeraseregions", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); expect_open(SYSFS_ROOT "/class/mtd/mtd0/flags", O_RDONLY, 0); expect_read_real(50,0); expect_close(3,1); int r = mtd_get_dev_info1(lib, dev_num, &info); assert_int_equal(r, 0); /* TODO check values */ libmtd_close(lib); (void)state; } int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_libmtd_open), cmocka_unit_test(test_mtd_is_bad), cmocka_unit_test(test_mtd_mark_bad), cmocka_unit_test(test_mtd_lock), cmocka_unit_test(test_mtd_unlock), cmocka_unit_test(test_mtd_is_locked), cmocka_unit_test(test_mtd_regioninfo), cmocka_unit_test(test_mtd_erase_multi), cmocka_unit_test(test_mtd_erase), cmocka_unit_test(test_mtd_read), cmocka_unit_test(test_mtd_write_nooob), cmocka_unit_test(test_mtd_write_withoob), cmocka_unit_test(test_mtd_read_oob), cmocka_unit_test(test_mtd_write_oob), cmocka_unit_test(test_mtd_dev_present), cmocka_unit_test(test_mtd_get_info), cmocka_unit_test(test_mtd_get_dev_info1), }; return cmocka_run_group_tests(tests, NULL, NULL); }