[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/bsp/lk/lib/fs/spifs/test/spifstest.c b/src/bsp/lk/lib/fs/spifs/test/spifstest.c
new file mode 100644
index 0000000..d2de090
--- /dev/null
+++ b/src/bsp/lk/lib/fs/spifs/test/spifstest.c
@@ -0,0 +1,684 @@
+/*
+ * Copyright (c) 2015 Gurjant Kalsi <me@gurjantkalsi.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#if LK_DEBUGLEVEL > 1
+
+#include <string.h>
+#include <err.h>
+#include <stdlib.h>
+
+#include <lib/bio.h>
+#include <lib/console.h>
+#include <lib/fs/spifs.h>
+
+#define FS_NAME "spifs"
+#define MNT_PATH "/s"
+#define TEST_FILE_PATH "/s/test"
+#define TEST_PATH_MAX_SIZE 16
+
+typedef bool(*test_func)(const char *);
+
+typedef struct {
+    test_func func;
+    const char* name;
+    uint32_t toc_pages;
+} test;
+
+static bool test_empty_after_format(const char *);
+static bool test_double_create_file(const char *);
+static bool test_write_read_normal(const char *);
+static bool test_write_past_eof(const char *);
+static bool test_full_toc(const char *);
+static bool test_full_fs(const char *);
+static bool test_write_past_end_of_capacity(const char *);
+static bool test_rm_reclaim(const char *);
+static bool test_corrupt_toc(const char *);
+static bool test_write_with_offset(const char *);
+static bool test_read_write_big(const char *);
+static bool test_rm_active_dirent(const char *);
+
+static test tests[] = {
+    {&test_empty_after_format, "Test no files in ToC after format.", 1},
+    {&test_write_read_normal, "Test the normal read/write file paths.", 1},
+    {&test_double_create_file, "Test file cannot be created if it already exists.", 1},
+    {&test_write_past_eof, "Test that file can grow up to capacity.", 1},
+    {&test_full_toc, "Test that files cannot be created once the ToC is full.", 2},
+    {&test_full_fs, "Test that files cannot be created once the device is full.", 1},
+    {&test_rm_reclaim, "Test that files can be deleted and that used space is reclaimed.", 1},
+    {&test_write_past_end_of_capacity, "Test that we cannot write past the capacity of a file.", 1},
+    {&test_corrupt_toc, "Test that FS can be mounted with one corrupt ToC.", 1},
+    {&test_write_with_offset, "Test that files can be written to at an offset.", 1},
+    {&test_read_write_big, "Test that an unaligned ~10kb buffer can be written and read.", 1},
+    {&test_rm_active_dirent, "Test that we can remove a file with an open dirent.", 1},
+};
+
+bool test_setup(const char *dev_name, uint32_t toc_pages)
+{
+    spifs_format_args_t args = {
+        .toc_pages = toc_pages,
+    };
+
+    status_t res = fs_format_device(FS_NAME, dev_name, (void*)&args);
+    if (res != NO_ERROR) {
+        printf("spifs_format failed dev = %s, toc_pages = %u, retcode = %d\n",
+               dev_name, toc_pages, res);
+        return false;
+    }
+
+    res = fs_mount(MNT_PATH, FS_NAME, dev_name);
+    if (res != NO_ERROR) {
+        printf("fs_mount failed path = %s, fs name = %s, dev name = %s,"
+               " retcode = %d\n", MNT_PATH, FS_NAME, dev_name, res);
+        return false;
+    }
+
+    return true;
+}
+
+bool test_teardown(void)
+{
+    if (fs_unmount(MNT_PATH) != NO_ERROR) {
+        printf("Unmount failed\n");
+        return false;
+    }
+
+    return true;;
+}
+
+static bool test_empty_after_format(const char *dev_name)
+{
+    dirhandle *dhandle;
+    status_t err = fs_open_dir(MNT_PATH, &dhandle);
+    if (err != NO_ERROR) {
+        return false;
+    }
+
+    struct dirent ent;
+    if (fs_read_dir(dhandle, &ent) >= 0) {
+        fs_close_dir(dhandle);
+        return false;
+    }
+
+    fs_close_dir(dhandle);
+    return true;
+}
+
+static bool test_double_create_file(const char *dev_name)
+{
+    status_t status;
+
+    struct dirent *ent = malloc(sizeof(*ent));
+    size_t num_files = 0;
+
+    filehandle *handle;
+    status = fs_create_file(TEST_FILE_PATH, &handle, 10);
+    if (status != NO_ERROR) {
+        goto err;
+    }
+    fs_close_file(handle);
+
+    filehandle *duphandle;
+    status = fs_create_file(TEST_FILE_PATH, &duphandle, 20);
+    if (status != ERR_ALREADY_EXISTS) {
+        goto err;
+    }
+
+    dirhandle *dhandle;
+    status = fs_open_dir(MNT_PATH, &dhandle);
+    if (status != NO_ERROR) {
+        goto err;
+    }
+
+    while ((status = fs_read_dir(dhandle, ent)) >= 0) {
+        num_files++;
+    }
+
+    status = NO_ERROR;
+
+    fs_close_dir(dhandle);
+
+
+err:
+    free(ent);
+
+    return status == NO_ERROR ? num_files == 1 : false;
+}
+
+static bool test_write_read_normal(const char *dev_name)
+{
+    char test_message[] = "spifs test";
+    char test_buf[sizeof(test_message)];
+
+    bdev_t *dev = bio_open(dev_name);
+    if (!dev) {
+        return false;
+    }
+    uint8_t erase_byte = dev->erase_byte;
+    bio_close(dev);
+
+    filehandle *handle;
+    status_t status =
+        fs_create_file(TEST_FILE_PATH, &handle, sizeof(test_message));
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    ssize_t bytes;
+
+    // New files should be initialized to 'erase_byte'
+    bytes = fs_read_file(handle, test_buf, 0, sizeof(test_buf));
+    if (bytes != sizeof(test_buf)) {
+        return false;
+    }
+
+    for (size_t i = 0; i < sizeof(test_buf); i++) {
+        if (test_buf[i] != erase_byte) {
+            return false;
+        }
+    }
+
+    bytes = fs_write_file(handle, test_message, 0, sizeof(test_message));
+    if (bytes != sizeof(test_message)) {
+        return false;
+    }
+
+    bytes = fs_read_file(handle, test_buf, 0, sizeof(test_buf));
+    if (bytes != sizeof(test_buf)) {
+        return false;
+    }
+
+    status = fs_close_file(handle);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    return strncmp(test_message, test_buf, sizeof(test_message)) == 0;
+}
+
+static bool test_write_past_eof(const char *dev_name)
+{
+    char test_message[] = "spifs test";
+
+    // Create a 0 length file.
+    filehandle *handle;
+    status_t status =
+        fs_create_file(TEST_FILE_PATH, &handle, 0);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    ssize_t bytes = fs_write_file(handle, test_message, 0, sizeof(test_message));
+    if (bytes != sizeof(test_message)) {
+        return false;
+    }
+
+    // Make sure the file grows.
+    struct file_stat stat;
+    fs_stat_file(handle, &stat);
+    if (stat.is_dir != false && stat.size != sizeof(test_message)) {
+        return false;
+    }
+
+    fs_close_file(handle);
+
+    return true;
+}
+
+static bool test_full_toc(const char *dev_name)
+{
+    struct fs_stat stat;
+
+    fs_stat_fs(MNT_PATH, &stat);
+
+    char test_file_name[TEST_PATH_MAX_SIZE];
+
+    filehandle *handle;
+    for (size_t i = 0; i < stat.free_inodes; i++) {
+        memset(test_file_name, 0, TEST_PATH_MAX_SIZE);
+
+        char filenum[] = "000";
+        filenum[0] += i / 100;
+        filenum[1] += (i / 10) % 10;
+        filenum[2] += i % 10;
+
+        strlcat(test_file_name, MNT_PATH, sizeof(test_file_name));
+        strlcat(test_file_name, "/", sizeof(test_file_name));
+        strlcat(test_file_name, filenum, sizeof(test_file_name));
+
+        status_t status =
+            fs_create_file(test_file_name, &handle, 1);
+
+        if (status != NO_ERROR) {
+            return false;
+        }
+
+        fs_close_file(handle);
+    }
+
+    // There shouldn't be enough space for this file since we've exhausted all
+    // the inodes.
+
+    status_t status = fs_create_file(TEST_FILE_PATH, &handle, 1);
+    if (status != ERR_TOO_BIG) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool test_rm_reclaim(const char *dev_name)
+{
+    // Create some number of files that's a power of 2;
+    size_t n_files = 4;
+
+    struct fs_stat stat;
+
+    fs_stat_fs(MNT_PATH, &stat);
+
+    size_t file_size = stat.free_space / (n_files + 1);
+
+    char test_file_name[TEST_PATH_MAX_SIZE];
+
+    filehandle *handle;
+    for (size_t i = 0; i < n_files; i++) {
+        memset(test_file_name, 0, TEST_PATH_MAX_SIZE);
+
+        char filenum[] = "000";
+        filenum[0] += i / 100;
+        filenum[1] += (i / 10) % 10;
+        filenum[2] += i % 10;
+
+        strcat(test_file_name, MNT_PATH);
+        strcat(test_file_name, filenum);
+
+        status_t status =
+            fs_create_file(test_file_name, &handle, file_size);
+
+        if (status != NO_ERROR) {
+            return false;
+        }
+
+        fs_close_file(handle);
+    }
+
+    // Try to create a new Big file.
+    char filename[] = "BIGFILE";
+    memset(test_file_name, 0, TEST_PATH_MAX_SIZE);
+    strcat(test_file_name, MNT_PATH);
+    strcat(test_file_name, filename);
+
+    status_t status;
+
+    // This should fail because there's no more space.
+    fs_stat_fs(MNT_PATH, &stat);
+    status = fs_create_file(test_file_name, &handle, stat.free_space + 1);
+    if (status != ERR_TOO_BIG) {
+        return false;
+    }
+
+    // Delete an existing file to make space for the new file.
+    char existing_filename[] = "001";
+    memset(test_file_name, 0, TEST_PATH_MAX_SIZE);
+    strcat(test_file_name, MNT_PATH);
+    strcat(test_file_name, existing_filename);
+
+    status = fs_remove_file(test_file_name);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+
+    // Now this should go through because we've reclaimed the space.
+    status = fs_create_file(test_file_name, &handle, stat.free_space + 1);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    fs_close_file(handle);
+    return true;
+}
+
+static bool test_full_fs(const char *dev_name)
+{
+    struct fs_stat stat;
+
+    fs_stat_fs(MNT_PATH, &stat);
+
+    char second_file_path[TEST_PATH_MAX_SIZE];
+    memset(second_file_path, 0, TEST_PATH_MAX_SIZE);
+    strcpy(second_file_path, MNT_PATH);
+    strcat(second_file_path, "/fail");
+
+    filehandle *handle;
+    status_t status = fs_create_file(TEST_FILE_PATH, &handle, stat.free_space);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    fs_close_file(handle);
+
+    // There shouldn't be enough space for this file since we've used all the
+    // space.
+    status = fs_create_file(second_file_path, &handle, 1);
+    if (status != ERR_TOO_BIG) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool test_write_past_end_of_capacity(const char *dev_name)
+{
+    filehandle *handle;
+    status_t status = fs_create_file(TEST_FILE_PATH, &handle, 0);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    struct file_stat stat;
+    status = fs_stat_file(handle, &stat);
+    if (status != NO_ERROR) {
+        goto finish;
+    }
+
+    // We shouldn't be able to write past the capacity of a file.
+    char buf[1];
+    status = fs_write_file(handle, buf, stat.capacity, 1);
+    if (status == ERR_OUT_OF_RANGE) {
+        status = NO_ERROR;
+    } else {
+        status = ERR_IO;
+    }
+
+finish:
+    fs_close_file(handle);
+    return status == NO_ERROR;
+}
+
+static bool test_corrupt_toc(const char *dev_name)
+{
+    // Create a zero byte file.
+    filehandle *handle;
+    status_t status = fs_create_file(TEST_FILE_PATH, &handle, 0);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    // Grow the file to one byte. This should trigger a ToC flush. Now the
+    // ToC record for this file should exist in both ToCs. Therefore corrupting
+    // either of the ToCs will still yield this file readable.
+    char buf[1] = { 'a' };
+    status = fs_write_file(handle, buf, 0, 1);
+    if (status != 1) {
+        return false;
+    }
+
+    status = fs_close_file(handle);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    status = fs_unmount(MNT_PATH);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    // Now we're going to manually corrupt one of the ToCs
+    bdev_t *dev = bio_open(dev_name);
+    if (!dev) {
+        return false;
+    }
+
+    // Directly write 0s to the block that contains the front-ToC.
+    size_t block_size = dev->block_size;
+    uint8_t *block_buf = memalign(CACHE_LINE, block_size);
+    if (!block_buf) {
+        return false;
+    }
+    memset(block_buf, 0, block_size);
+
+    ssize_t bytes = bio_write_block(dev, block_buf, 0, 1);
+
+    free(block_buf);
+
+    bio_close(dev);
+
+    if (bytes != (ssize_t)block_size) {
+        return false;
+    }
+
+    // Mount the FS again and make sure that the file we created is still there.
+    status = fs_mount(MNT_PATH, FS_NAME, dev_name);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    status = fs_open_file(TEST_FILE_PATH, &handle);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    struct file_stat stat;
+    status = fs_stat_file(handle, &stat);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    status = fs_close_file(handle);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool test_write_with_offset(const char *dev_name)
+{
+    size_t repeats = 3;
+    char test_message[] = "test";
+    size_t msg_len = strnlen(test_message, sizeof(test_message));
+    char test_buf[msg_len * repeats];
+
+    filehandle *handle;
+    status_t status = fs_create_file(TEST_FILE_PATH, &handle, msg_len);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    ssize_t bytes;
+    for (size_t pos = 0; pos < repeats; pos++) {
+        bytes = fs_write_file(handle, test_message, pos * msg_len, msg_len);
+        if ((size_t)bytes != msg_len) {
+            return false;
+        }
+    }
+
+    bytes = fs_read_file(handle, test_buf, 0, msg_len * repeats);
+    if ((size_t)bytes != msg_len * repeats) {
+        return false;
+    }
+
+    status = fs_close_file(handle);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    bool success = true;
+    for (size_t i = 0; i < repeats; i++) {
+        success &= (memcmp(test_message,
+                           test_buf + i * msg_len,
+                           msg_len) == 0);
+    }
+    return success;
+}
+
+static bool test_read_write_big(const char *dev_name)
+{
+    bool success = true;
+
+    size_t buflen = 10013;
+
+    uint8_t *rbuf = malloc(buflen);
+    if (!rbuf) {
+        return false;
+    }
+
+    uint8_t *wbuf = malloc(buflen);
+    if (!wbuf) {
+        free(rbuf);
+        return false;
+    }
+
+    for (size_t i = 0; i < buflen; i++) {
+        wbuf[i] = rand() % sizeof(uint8_t);
+    }
+
+    filehandle *handle;
+    status_t status = fs_create_file(TEST_FILE_PATH, &handle, buflen);
+    if (status != NO_ERROR) {
+        success = false;
+        goto err;
+    }
+
+    ssize_t bytes = fs_write_file(handle, wbuf, 0, buflen);
+    if ((size_t)bytes != buflen) {
+        success = false;
+        goto err;
+    }
+
+    bytes = fs_read_file(handle, rbuf, 0, buflen);
+    if ((size_t)bytes != buflen) {
+        success = false;
+        goto err;
+    }
+
+    for (size_t i = 0; i < buflen; i++) {
+        if (wbuf[i] != rbuf[i]) {
+            success = false;
+            break;
+        }
+    }
+
+err:
+    success &= fs_close_file(handle) == NO_ERROR;
+
+    free(rbuf);
+    free(wbuf);
+    return success;
+}
+
+static bool test_rm_active_dirent(const char *dev_name)
+{
+    filehandle *handle;
+    status_t status = fs_create_file(TEST_FILE_PATH, &handle, 0);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    status = fs_close_file(handle);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    dirhandle *dhandle;
+    status = fs_open_dir(MNT_PATH, &dhandle);
+    if (status != NO_ERROR) {
+        return false;
+    }
+
+    // Dir handle should now be pointing to the only file in our FS.
+    status = fs_remove_file(TEST_FILE_PATH);
+    if (status != NO_ERROR) {
+        fs_close_dir(dhandle);
+        return false;
+    }
+
+    bool success = true;
+    struct dirent *ent = malloc(sizeof(*ent));
+    if (fs_read_dir(dhandle, ent) >= 0) {
+        success = false;
+    }
+
+    success &= fs_close_dir(dhandle) == NO_ERROR;
+    free(ent);
+
+    return success;
+}
+
+static int cmd_spifs(int argc, const cmd_args *argv)
+{
+    if (argc < 3) {
+notenoughargs:
+        printf("not enough arguments:\n");
+usage:
+        printf("%s test <device>\n", argv[0].str);
+        return -1;
+    }
+
+    if (strcmp(argv[1].str, "test")) {
+        goto usage;
+    }
+
+    // Make sure this block device is legit.
+    bdev_t *dev = bio_open(argv[2].str);
+    if (!dev) {
+        printf("error: could not open block device %s\n", argv[2].str);
+        return -1;
+    }
+    bio_close(dev);
+
+    size_t passed = 0;
+    size_t attempted = 0;
+    for (size_t i = 0; i < countof(tests); i++) {
+        ++attempted;
+        if (!test_setup(argv[2].str, tests[i].toc_pages)) {
+            printf("Test Setup failed before %s. Exiting.\n", tests[i].name);
+            break;
+        }
+
+        if (tests[i].func(argv[2].str)) {
+            printf(" [Passed] %s\n", tests[i].name);
+            ++passed;
+        } else {
+            printf(" [Failed] %s\n", tests[i].name);
+        }
+
+        if (!test_teardown()) {
+            printf("Test teardown failed after %s. Exiting.\n", tests[i].name);
+            break;
+        }
+    }
+    printf("\nPassed %u of %u tests.\n", passed, attempted);
+
+    if (attempted != countof(tests)) {
+        printf("(Skipped %u)\n", countof(tests) - attempted);
+    }
+
+    return countof(tests) - passed;
+}
+
+STATIC_COMMAND_START
+STATIC_COMMAND("spifs", "commands related to the spifs implementation.", &cmd_spifs)
+STATIC_COMMAND_END(spifs);
+
+#endif  // LK_DEBUGLEVEL > 1
\ No newline at end of file