test: write the vpnor file(flash)

Add the test binary which runs the following test cases
1) Write to the read only partition
2) Write to the RW partition
3) Write to the preserved partition.
4) Write beyond the partition file length.
5) other boundary test cases.

Resolves openbmc/openbmc#1479

Change-Id: Ifd4f0e89e434a26e2579415a973fe1bfdbb3e66f
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 4f49644..4e59529 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -164,6 +164,20 @@
 	$(PHOSPHOR_LOGGING_LIBS) \
 	$(PHOSPHOR_DBUS_INTERFACES_LIBS)
 
+test_write_flash_vpnor_SOURCES = \
+	$(TEST_MBOX_VPNOR_SRCS) \
+	mboxd_flash.c \
+	mboxd_pnor_partition_table.cpp \
+	mboxd_flash_virtual.cpp \
+	mtd.c \
+	pnor_partition.cpp \
+	test/write_flash_vpnor.cpp
+test_write_flash_vpnor_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
+test_write_flash_vpnor_LDADD = -lstdc++fs \
+	$(SDBUSPLUS_LIBS) \
+	$(PHOSPHOR_LOGGING_LIBS) \
+	$(PHOSPHOR_DBUS_INTERFACES_LIBS)
+
 check_PROGRAMS = test/sanity \
 		 test/copy_flash \
 		 test/erase_flash \
@@ -195,7 +209,8 @@
 if VIRTUAL_PNOR_ENABLED
 check_PROGRAMS += \
 	test/create_pnor_partition_table \
-	test/create_read_window_vpnor
+	test/create_read_window_vpnor \
+	test/write_flash_vpnor
 endif
 
 TESTS = $(check_PROGRAMS)
diff --git a/test/write_flash_vpnor.cpp b/test/write_flash_vpnor.cpp
new file mode 100644
index 0000000..30c9ff7
--- /dev/null
+++ b/test/write_flash_vpnor.cpp
@@ -0,0 +1,217 @@
+/*
+ * MBox Daemon Test File
+ *
+ * Copyright 2017 IBM
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "config.h"
+#include "mboxd_pnor_partition_table.h"
+
+extern "C" {
+#include "mbox.h"
+#include "test/tmpf.h"
+#include "mboxd_flash.h"
+}
+
+#include <assert.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <experimental/filesystem>
+
+#include <sys/mman.h>
+#include <sys/syslog.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+
+uint8_t data[8] = { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7 };
+
+#define BLOCK_SIZE  4096
+#define OFFSET  BLOCK_SIZE
+#define MEM_SIZE    (BLOCK_SIZE *2)
+#define DATA_SIZE sizeof(data)
+#define ERASE_SIZE BLOCK_SIZE
+#define BLOCK_SIZE_SHIFT 12
+
+
+void init(struct mbox_context* ctx)
+{
+
+    namespace fs = std::experimental::filesystem;
+    using namespace std::string_literals;
+
+    std::string  tocData = "partition01=TEST1,00000000,00000400,ECC,READONLY\n"s
+                           + "partition02=TEST2,00000000,00000008,ECC,READWRITE\n"s
+                           + "partition03=TEST3,00000000,00000400,ECC,PRESERVED"s;
+
+    std::vector<std::string> templatePaths = { "/tmp/ro.XXXXXX",
+                                               "/tmp/rw.XXXXXX" ,
+                                               "/tmp/prsv.XXXXXX"
+                                             };
+
+    std::vector<std::string>partitions = { "TEST1", "TEST2", "TEST3" };
+
+
+    // create various partition directory
+
+    std::string tmpROdir = mkdtemp(const_cast<char*>(templatePaths[0].c_str()));
+    assert(tmpROdir.length() != 0);
+
+    std::string tmpRWdir = mkdtemp(const_cast<char*>(templatePaths[1].c_str()));
+    assert(tmpRWdir.length() != 0);
+
+    std::string tmpPRSVdir = mkdtemp(const_cast<char*>(templatePaths[2].c_str()));
+    assert(tmpPRSVdir.length() != 0);
+
+    // create the toc file
+    fs::path tocFilePath = tmpROdir;
+
+    tocFilePath /= PARTITION_TOC_FILE;
+    std::ofstream tocFile(tocFilePath.c_str());
+    tocFile.write(tocData.c_str(), static_cast<int>(tocData.length()));
+    tocFile.close();
+
+    // create the partition files in the ro directory
+    for (auto partition : partitions)
+    {
+        fs::path partitionFilePath {tmpROdir};
+        partitionFilePath /= partition;
+        std::ofstream partitionFile(partitionFilePath.c_str());
+        partitionFile.write(reinterpret_cast<char*>(data), sizeof(data));
+        partitionFile.close();
+
+    }
+
+    // copy partition2 file from ro to rw
+    std::string roFile = tmpROdir + "/" + "TEST2";
+    std::string rwFile = tmpRWdir + "/" + "TEST2";
+    assert(fs::copy_file(roFile, rwFile) == true);
+
+    mbox_vlog = &mbox_log_console;
+    verbosity = (verbose)2;
+
+    // setting context parameters
+    ctx->erase_size_shift = BLOCK_SIZE_SHIFT;
+    ctx->block_size_shift = BLOCK_SIZE_SHIFT;
+    ctx->flash_bmap = reinterpret_cast<uint8_t*>(
+                          calloc(MEM_SIZE >> ctx->erase_size_shift,
+                                 sizeof(*ctx->flash_bmap)));
+
+    strcpy(ctx->paths.ro_loc, tmpROdir.c_str());
+    strcpy(ctx->paths.rw_loc, tmpRWdir.c_str());
+    strcpy(ctx->paths.prsv_loc, tmpPRSVdir.c_str());
+
+}
+
+
+int main(void)
+{
+    namespace fs = std::experimental::filesystem;
+
+    int rc {};
+    char src[DATA_SIZE] {0};
+    struct mbox_context context;
+    struct mbox_context* ctx = &context;
+    memset(ctx, 0, sizeof(mbox_context));
+
+    //Initialize the context before running the test case.
+    init(ctx);
+
+    std::string tmpROdir = ctx->paths.ro_loc;
+    std::string tmpRWdir = ctx->paths.rw_loc;
+    std::string tmpPRSVdir = ctx->paths.prsv_loc;
+
+    // create the partition table
+    vpnor_create_partition_table_from_path(ctx, tmpROdir.c_str());
+
+    // Write to psrv partition
+
+    // As file doesn't exist there, so it copies
+    // the file from RO to PRSV and write the file in PRSV partition.
+
+    memset(src, 0xaa, sizeof(src));
+
+    rc = write_flash(ctx, (OFFSET * 3) , src, sizeof(src));
+    assert(rc == 0);
+
+    auto fd = open((tmpPRSVdir + "/" + "TEST3").c_str(), O_RDONLY);
+    auto map = mmap(NULL, MEM_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
+    assert(map != MAP_FAILED);
+
+    // verify it is written
+    rc = memcmp(src, map, sizeof(src));
+    assert(rc == 0);
+    munmap(map, MEM_SIZE);
+    close(fd);
+
+    // Write to the RO partition
+    memset(src, 0x55, sizeof(src));
+    fd = open((tmpROdir + "/" + "TEST1").c_str(), O_RDONLY);
+    map = mmap(NULL, MEM_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
+    assert(map != MAP_FAILED);
+    rc = write_flash(ctx, (OFFSET), src, sizeof(src));
+    // Should not be allowed to write on RO
+    assert(rc != 0);
+
+    munmap(map, MEM_SIZE);
+    close(fd);
+
+    // Write to the RW partition
+    memset(src, 0xbb, sizeof(src));
+    fd = open((tmpRWdir + "/" + "TEST2").c_str(), O_RDONLY);
+    map = mmap(NULL, MEM_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
+    assert(map != MAP_FAILED);
+    rc = write_flash(ctx, (OFFSET * 2), src, sizeof(src));
+    assert(rc == 0);
+    rc = memcmp(src, map, sizeof(src));
+    assert(rc == 0);
+
+    // write beyond the partition length as the partition
+    // file length is 8 byte(TEST2).
+    rc = write_flash(ctx, (OFFSET * 2 + 3), src, sizeof(src) + 20);
+    assert(rc == -1);
+
+    memset(src, 0xcc, sizeof(src));
+    rc = write_flash(ctx, (OFFSET * 2), src, sizeof(src));
+    assert(rc == 0);
+    rc = memcmp(src, map, sizeof(src));
+    assert(rc == 0);
+
+    src[0] = 0xff;
+    rc = write_flash(ctx, (OFFSET * 2), src, 1);
+    assert(rc == 0);
+    rc = memcmp(src, map, sizeof(src));
+    assert(rc == 0);
+
+    src[1] = 0xff;
+    rc = write_flash(ctx, (OFFSET * 2) + 1, &src[1], 1);
+    assert(rc == 0);
+    rc = memcmp(src, map, sizeof(src));
+    assert(rc == 0);
+
+    src[2] = 0xff;
+    rc = write_flash(ctx, (OFFSET * 2) + 2, &src[2], 1);
+    assert(rc == 0);
+    rc = memcmp(src, map, sizeof(src));
+    assert(rc == 0);
+
+    fs::remove_all(fs::path {tmpROdir});
+    fs::remove_all(fs::path {tmpRWdir});
+    fs::remove_all(fs::path {tmpPRSVdir});
+
+    return rc;
+}