test: vpnor: Add dump_flash test

The test is intended to read and verify the content of the flash, and
verify that the read completes without error in the face of unusual
flash size with respect to the window configuration.

Specifically, the test is arranged such that the reserved memory exceeds
the flash size, and the flash layout conspires such that the final
request is for a window whose flash offset and window size exceed the
flash size. This currently triggers an error condition in the mbox
window handling, and causes the host to receive an error response to its
CREATE_READ_WINDOW request. On the host side this results in the reading
process receiving an EIO.

Due to what is probably an oversight in the mbox window handling, some
care needs to be taken in the test configuration: The current behaviour
is that copy_flash() will return a length that may be less than the size
of the reserved memory window. The returned value is aligned up to the
next block and assigned as the current window's size. However, when
evicting a window, we do not reset the size to the default size. As a
consequence, windows can shrink and remain at a size below the default
window size. Without careful control of the test parameters this can
lead to the appearance that there is no bug in the window handling as,
serendipitously, a window of the correct size can be evicted for the
final CREATE_READ_WINDOW request.

Change-Id: I436595f428bf4e93392315ec1110b6b6f4a11821
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
diff --git a/test/vpnor/Makefile.am.include b/test/vpnor/Makefile.am.include
index 09a4cc8..0d24807 100644
--- a/test/vpnor/Makefile.am.include
+++ b/test/vpnor/Makefile.am.include
@@ -250,6 +250,19 @@
 test_vpnor_write_patch_resize_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 test_vpnor_write_patch_resize_LDADD = $(VPNOR_LDADD)
 
+test_vpnor_dump_flash_SOURCES = \
+	$(TEST_MBOX_VPNOR_SRCS) $(TEST_MOCK_SRCS) \
+	mboxd_msg.c \
+	mboxd_windows.c \
+	mboxd_lpc.c \
+	mboxd_lpc_virtual.cpp \
+	mboxd_pnor_partition_table.cpp \
+	mboxd_flash_virtual.cpp \
+	pnor_partition.cpp \
+	%reldir%/dump_flash.cpp
+test_vpnor_dump_flash_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
+test_vpnor_dump_flash_LDADD = $(VPNOR_LDADD)
+
 if VIRTUAL_PNOR_ENABLED
 check_PROGRAMS += \
 	%reldir%/create_pnor_partition_table \
@@ -273,5 +286,8 @@
 	%reldir%/create_read_window_straddle_partitions \
 	%reldir%/create_read_window_partition_invalid \
 	%reldir%/read_patch \
-	%reldir%/write_patch_resize
+	%reldir%/write_patch_resize \
+	%reldir%/dump_flash
+
+XFAIL_TESTS += %reldir%/dump_flash
 endif
diff --git a/test/vpnor/dump_flash.cpp b/test/vpnor/dump_flash.cpp
new file mode 100644
index 0000000..8ee4b05
--- /dev/null
+++ b/test/vpnor/dump_flash.cpp
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2018 IBM Corp.
+
+#include <assert.h>
+#include <string.h>
+
+#include "config.h"
+#include "mboxd_msg.h"
+#include "mboxd_pnor_partition_table.h"
+
+extern "C" {
+#include "test/mbox.h"
+#include "test/system.h"
+}
+
+#include "test/vpnor/tmpd.hpp"
+
+struct test_context
+{
+    uint8_t seq;
+    struct mbox_context *ctx;
+};
+
+// Configure the system and the paritions such that we eventually request a
+// window that covers the last section of flash, but the remaining flash is
+// smaller than the window size
+static constexpr auto BLOCK_SIZE = 4096;
+static constexpr auto ERASE_SIZE = BLOCK_SIZE;
+static constexpr auto N_WINDOWS = 3;
+static constexpr auto WINDOW_SIZE = 2 * BLOCK_SIZE;
+static constexpr auto MEM_SIZE = N_WINDOWS * WINDOW_SIZE;
+static constexpr auto PNOR_SIZE = (4 * BLOCK_SIZE);
+
+const std::string toc[] = {
+    "partition01=ONE,00001000,00003000,80,ECC,READONLY",
+};
+
+static const uint8_t get_info[] = {0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
+                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                   0x00, 0x00, 0x00, 0x00};
+
+static constexpr auto MBOX_CREATE_READ_WINDOW = 4;
+
+static int mbox_create_read_window(struct test_context *tctx, size_t offset,
+                                   size_t len)
+{
+    union mbox_regs regs;
+
+    memset(&regs, 0, sizeof(regs));
+    regs.msg.command = MBOX_CREATE_READ_WINDOW;
+    regs.msg.seq = ++tctx->seq;
+    put_u16(&regs.msg.args[0], offset);
+    put_u16(&regs.msg.args[2], len);
+
+    return mbox_command_dispatch(tctx->ctx, regs.raw, sizeof(regs.raw));
+}
+
+int main()
+{
+    namespace test = openpower::virtual_pnor::test;
+
+    struct test_context _tctx = {0}, *tctx = &_tctx;
+    size_t len;
+    size_t pos;
+    int rc;
+
+    system_set_reserved_size(MEM_SIZE);
+    system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
+
+    tctx->ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(tctx->ctx, toc, BLOCK_SIZE);
+    init_vpnor_from_paths(tctx->ctx);
+
+    rc = mbox_command_dispatch(tctx->ctx, get_info, sizeof(get_info));
+    assert(rc == 1);
+
+    pos = 0;
+    while (pos < (PNOR_SIZE / BLOCK_SIZE))
+    {
+        struct mbox_msg _msg, *msg = &_msg;
+
+        rc = mbox_create_read_window(tctx, pos, (WINDOW_SIZE / BLOCK_SIZE));
+        assert(rc == 1);
+
+        mbox_rspcpy(tctx->ctx, msg);
+
+        len = get_u16(&msg->args[2]);
+        pos = get_u16(&msg->args[4]) + len;
+    }
+
+    return 0;
+}