aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/libflash/test
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/libflash/test
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/libflash/test')
-rw-r--r--roms/skiboot/libflash/test/Makefile.check167
-rw-r--r--roms/skiboot/libflash/test/mbox-server.c514
-rw-r--r--roms/skiboot/libflash/test/mbox-server.h13
-rw-r--r--roms/skiboot/libflash/test/stubs.c102
-rw-r--r--roms/skiboot/libflash/test/stubs.h20
-rw-r--r--roms/skiboot/libflash/test/test-blocklevel.c664
-rw-r--r--roms/skiboot/libflash/test/test-ecc.c510
-rw-r--r--roms/skiboot/libflash/test/test-flash.c448
-rw-r--r--roms/skiboot/libflash/test/test-ipmi-hiomap.c3388
-rw-r--r--roms/skiboot/libflash/test/test-mbox.c333
10 files changed, 6159 insertions, 0 deletions
diff --git a/roms/skiboot/libflash/test/Makefile.check b/roms/skiboot/libflash/test/Makefile.check
new file mode 100644
index 000000000..4dbd7ee75
--- /dev/null
+++ b/roms/skiboot/libflash/test/Makefile.check
@@ -0,0 +1,167 @@
+# -*-Makefile-*-
+libflash_test_test_ipmi_hiomap_SOURCES = \
+ libflash/test/test-ipmi-hiomap.c \
+ libflash/test/stubs.c \
+ libflash/ipmi-hiomap.c
+
+libflash_test_test_blocklevel_SOURCES = \
+ libflash/test/test-blocklevel.c \
+ libflash/test/stubs.c
+
+libflash_test_test_flash_SOURCES = \
+ libflash/test/test-flash.c \
+ libflash/test/stubs.c \
+ libflash/test/mbox-server.c
+
+libflash_test_test_ecc_SOURCES = \
+ libflash/test/test-ecc.c \
+ libflash/test/stubs.c \
+ libflash/test/mbox-server.c
+
+libflash_test_test_mbox_SOURCES = \
+ libflash/test/test-mbox.c \
+ libflash/test/stubs.c \
+ libflash/test/mbox-server.c
+
+check_PROGRAMS = \
+ libflash/test/test-ipmi-hiomap \
+ libflash/test/test-blocklevel \
+ libflash/test/test-flash \
+ libflash/test/test-ecc \
+ libflash/test/test-mbox
+
+TEST_FLAGS = -D__TEST__ -MMD -MP
+
+.PHONY: libflash-check libflash-coverage
+libflash-check: $(check_PROGRAMS:%=%-check)
+libflash-coverage: $(check_PROGRAMS:%=%-gcov-run)
+clean: libflash-test-clean
+check: libflash-check
+coverage: libflash-coverage
+strict-check: TEST_FLAGS += -D__STRICT_TEST__
+strict-check: check
+
+LCOV_EXCLUDE += $(check_PROGRAMS:%=%.c)
+
+$(check_PROGRAMS:%=%-check) : %-check : %
+ $(call QTEST, RUN-TEST , $(VALGRIND) $<, $<)
+
+# Transform a prerequisite into something approximating a variable name. This
+# is used to map check_PROGRAMS prerequisits to the corresponding _SOURCES
+# variable.
+#
+# For example:
+#
+# $(call prereq2var,libflash/test/test-mbox)
+#
+# Will output:
+#
+# 'libflash_test_test_mbox'
+#
+prereq2var = $(subst /,_,$(subst -,_,$(1)))
+
+# Generate prerequisites from a target based on the target's corresponding
+# _SOURCES variable.
+#
+# For example, with:
+#
+# libflash_test_test_mbox_SOURCES = \
+# libflash/test/test-mbox.c \
+# libflash/test/stubs.c \
+# libflash/test/mbox-server.c
+# HOST_TRIPLE = x86_64-linux-gnu
+#
+# A call to target2prereq where the target is libflash/test/test-mbox:
+#
+# $(call target2prereq,$@,$(HOST_TRIPLE)/)
+#
+# Will output:
+#
+# x86_64-linux-gnu/libflash/test/test-mbox.o
+# x86_64-linux-gnu/libflash/test/stubs.o
+# x86_64-linux-gnu/libflash/test/mbox-server.o
+target2prereq = $(patsubst %.c,%.o,$(addprefix $(2),$($(call prereq2var,$(1))_SOURCES)))
+
+# Generate path stems for all applications in check_PROGRAMS. This is usef
+#
+# For example, with:
+#
+# libflash_test_test_mbox_SOURCES = \
+# libflash/test/test-mbox.c \
+# libflash/test/stubs.c \
+# libflash/test/mbox-server.c
+# libflash_test_test_ecc_SOURCES = \
+# libflash/test/test-ecc.c \
+# libflash/test/stubs.c \
+# libflash/test/mbox-server.c
+# check_PROGRAMS = libflash/test/test-mbox libflash/test/test-ecc
+# HOST_TRIPLE = x86_64-linux-gnu
+#
+# A call to:
+#
+# $(call objstem,$(check_PROGRAMS),$(HOST_TRIPLE)/)
+#
+# Will output:
+#
+# x86_64-linux-gnu/libflash/test/test-mbox
+# x86_64-linux-gnu/libflash/test/stubs
+# x86_64-linux-gnu/libflash/test/mbox-server
+# x86_64-linux-gnu/libflash/test/test-ecc
+# x86_64-linux-gnu/libflash/test/stubs
+# x86_64-linux-gnu/libflash/test/mbox-server
+objstem = $(patsubst %.c,%,$(addprefix $(2),$(foreach bin,$(1),$($(call prereq2var,$(bin))_SOURCES))))
+
+# Record the host platform triple to separate test vs production objects.
+HOST_TRIPLE = $(shell $(HOSTCC) -dumpmachine)
+
+# Mirror the skiboot directory structure under a directory named after the host
+# triple in the skiboot root directory, and place the built objects in this
+# mirrored structure.
+$(HOST_TRIPLE)/%.o : %.c
+ @mkdir -p $(dir $@)
+ $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) $(TEST_FLAGS) -Wno-suggest-attribute=const -g -c -o $@ $<, $@)
+
+# Use GNU make metaprogramming dynamically define targets and prequisites for
+# binaries listed in check_PROGRAMS.
+#
+# Secondary expansion[1] allows us to use the target automatic variable ($@) in
+# the prequisite list. Knowing the target we can map to the corresponding
+# _SOURCES variable to learn what to build and link. Finally, make sure the
+# artifacts are output under the $(HOST_TRIPLE) directory to separate them from
+# objects intended for skiboot proper.
+#
+# [1] https://www.gnu.org/software/make/manual/html_node/Secondary-Expansion.html#Secondary-Expansion
+.SECONDEXPANSION:
+$(check_PROGRAMS) : $$(call target2prereq,$$@,$(HOST_TRIPLE)/)
+ $(call Q, HOSTCC , $(HOSTCC) $(HOSTCFLAGS) $(TEST_FLAGS) -Wno-suggest-attribute=const -O0 -g -o $@ $^, $@)
+
+.PHONY: libflash-test-clean
+libflash-test-clean: OBJ_STEMS = $(call objstem,$(check_PROGRAMS),$(HOST_TRIPLE)/)
+libflash-test-clean: libflash-test-gcov-clean
+ $(RM) $(check_PROGRAMS)
+ $(RM) $(OBJ_STEMS:%=%.o)
+ $(RM) $(OBJ_STEMS:%=%.d)
+
+# gcov support: Build objects under $(HOST_TRIPLE)/gcov/
+$(check_PROGRAMS:%=%-gcov-run) : %-run: %
+ $(call QTEST, TEST-COVERAGE ,$< , $<)
+
+$(HOST_TRIPLE)/gcov/%.o : %.c
+ @mkdir -p $(dir $@)
+ $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) $(HOSTGCOVCFLAGS) $(TEST_FLAGS) -Wno-suggest-attribute=const -g -c -o $@ $<, $@)
+
+.SECONDEXPANSION:
+$(check_PROGRAMS:%=%-gcov) : $$(call target2prereq,$$(patsubst %-gcov,%,$$@),$(HOST_TRIPLE)/gcov/)
+ $(call Q, HOSTCC , $(HOSTCC) $(HOSTCFLAGS) $(HOSTGCOVCFLAGS) $(TEST_FLAGS) -Wno-suggest-attribute=const -O0 -g -o $@ $^, $@)
+
+.PHONY: libflash-test-gcov-clean
+libflash-test-gcov-clean: GCOV_OBJ_STEMS = $(call objstem,$(check_PROGRAMS),$(HOST_TRIPLE)/gcov/)
+libflash-test-gcov-clean:
+ $(RM) $(check_PROGRAMS:%=%-gcov)
+ $(RM) $(GCOV_OBJ_STEMS:%=%.o)
+ $(RM) $(GCOV_OBJ_STEMS:%=%.d)
+ $(RM) $(GCOV_OBJ_STEMS:%=%.gcda)
+ $(RM) $(GCOV_OBJ_STEMS:%=%.gcno)
+
+-include $(patsubst %,%.d,$(call objstem,$(check_PROGRAMS),$(HOST_TRIPLE)/))
+-include $(patsubst %,%.d,$(call objstem,$(check_PROGRAMS),$(HOST_TRIPLE)/gcov/))
diff --git a/roms/skiboot/libflash/test/mbox-server.c b/roms/skiboot/libflash/test/mbox-server.c
new file mode 100644
index 000000000..8a68cfff6
--- /dev/null
+++ b/roms/skiboot/libflash/test/mbox-server.c
@@ -0,0 +1,514 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2017 IBM Corp. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <inttypes.h>
+
+#include <sys/mman.h> /* for mprotect() */
+
+#define pr_fmt(fmt) "MBOX-SERVER: " fmt
+#include "skiboot.h"
+#include "opal-api.h"
+
+#include "mbox-server.h"
+#include "stubs.h"
+
+#define ERASE_GRANULE 0x100
+
+#define LPC_BLOCKS 256
+
+#define __unused __attribute__((unused))
+
+enum win_type {
+ WIN_CLOSED,
+ WIN_READ,
+ WIN_WRITE
+};
+
+typedef void (*mbox_data_cb)(struct bmc_mbox_msg *msg, void *priv);
+typedef void (*mbox_attn_cb)(uint8_t reg, void *priv);
+
+struct {
+ mbox_data_cb fn;
+ void *cb_data;
+ struct bmc_mbox_msg *msg;
+ mbox_attn_cb attn;
+ void *cb_attn;
+} mbox_data;
+
+static struct {
+ int api;
+ bool reset;
+
+ void *lpc_base;
+ size_t lpc_size;
+
+ uint8_t attn_reg;
+
+ uint32_t block_shift;
+ uint32_t erase_granule;
+
+ uint16_t def_read_win; /* default window size in blocks */
+ uint16_t def_write_win;
+
+ uint16_t max_read_win; /* max window size in blocks */
+ uint16_t max_write_win;
+
+ enum win_type win_type;
+ uint32_t win_base;
+ uint32_t win_size;
+ bool win_dirty;
+} server_state;
+
+
+static bool check_window(uint32_t pos, uint32_t size)
+{
+ /* If size is zero then all is well */
+ if (size == 0)
+ return true;
+
+ if (server_state.api == 1) {
+ /*
+ * Can actually be stricter in v1 because pos is relative to
+ * flash not window
+ */
+ if (pos < server_state.win_base ||
+ pos + size > server_state.win_base + server_state.win_size) {
+ fprintf(stderr, "pos: 0x%08x size: 0x%08x aren't in active window\n",
+ pos, size);
+ fprintf(stderr, "window pos: 0x%08x window size: 0x%08x\n",
+ server_state.win_base, server_state.win_size);
+ return false;
+ }
+ } else {
+ if (pos + size > server_state.win_base + server_state.win_size)
+ return false;
+ }
+ return true;
+}
+
+/* skiboot test stubs */
+int64_t lpc_read(enum OpalLPCAddressType __unused addr_type, uint32_t addr,
+ uint32_t *data, uint32_t sz);
+int64_t lpc_read(enum OpalLPCAddressType __unused addr_type, uint32_t addr,
+ uint32_t *data, uint32_t sz)
+{
+ /* Let it read from a write window... Spec says it ok! */
+ if (!check_window(addr, sz) || server_state.win_type == WIN_CLOSED)
+ return 1;
+
+ switch (sz) {
+ case 1:
+ *(uint8_t *)data = *(uint8_t *)(server_state.lpc_base + addr);
+ break;
+ case 2:
+ *(uint16_t *)data = be16_to_cpu(*(uint16_t *)(server_state.lpc_base + addr));
+ break;
+ case 4:
+ *(uint32_t *)data = be32_to_cpu(*(uint32_t *)(server_state.lpc_base + addr));
+ break;
+ default:
+ prerror("Invalid data size %d\n", sz);
+ return 1;
+ }
+ return 0;
+}
+
+int64_t lpc_write(enum OpalLPCAddressType __unused addr_type, uint32_t addr,
+ uint32_t data, uint32_t sz);
+int64_t lpc_write(enum OpalLPCAddressType __unused addr_type, uint32_t addr,
+ uint32_t data, uint32_t sz)
+{
+ if (!check_window(addr, sz) || server_state.win_type != WIN_WRITE)
+ return 1;
+ switch (sz) {
+ case 1:
+ *(uint8_t *)(server_state.lpc_base + addr) = data;
+ break;
+ case 2:
+ *(uint16_t *)(server_state.lpc_base + addr) = cpu_to_be16(data);
+ break;
+ case 4:
+ *(uint32_t *)(server_state.lpc_base + addr) = cpu_to_be32(data);
+ break;
+ default:
+ prerror("Invalid data size %d\n", sz);
+ return 1;
+ }
+ return 0;
+}
+
+int bmc_mbox_register_attn(mbox_attn_cb handler, void *drv_data)
+{
+ mbox_data.attn = handler;
+ mbox_data.cb_attn = drv_data;
+
+ return 0;
+}
+
+uint8_t bmc_mbox_get_attn_reg(void)
+{
+ return server_state.attn_reg;
+}
+
+int bmc_mbox_register_callback(mbox_data_cb handler, void *drv_data)
+{
+ mbox_data.fn = handler;
+ mbox_data.cb_data = drv_data;
+
+ return 0;
+}
+
+static int close_window(bool check)
+{
+ /*
+ * This isn't strictly prohibited and some daemons let you close
+ * windows even if none are open.
+ * I've made the test fail because closing with no windows open is
+ * a sign that something 'interesting' has happened.
+ * You should investigate why
+ *
+ * If check is false it is because we just want to do the logic
+ * because open window has been called - you can open a window
+ * over a closed window obviously
+ */
+ if (check && server_state.win_type == WIN_CLOSED)
+ return MBOX_R_PARAM_ERROR;
+
+ server_state.win_type = WIN_CLOSED;
+ mprotect(server_state.lpc_base, server_state.lpc_size, PROT_NONE);
+
+ return MBOX_R_SUCCESS;
+}
+
+static int do_dirty(uint32_t pos, uint32_t size)
+{
+ pos <<= server_state.block_shift;
+ if (server_state.api > 1)
+ size <<= server_state.block_shift;
+ if (!check_window(pos, size)) {
+ prlog(PR_ERR, "Trying to dirty not in open window range\n");
+ return MBOX_R_PARAM_ERROR;
+ }
+ if (server_state.win_type != WIN_WRITE) {
+ prlog(PR_ERR, "Trying to dirty not write window\n");
+ return MBOX_R_PARAM_ERROR;
+ }
+
+ /* Thats about all actually */
+ return MBOX_R_SUCCESS;
+}
+
+void check_timers(bool __unused unused)
+{
+ /* now that we've handled the message, holla-back */
+ if (mbox_data.msg) {
+ mbox_data.fn(mbox_data.msg, mbox_data.cb_data);
+ mbox_data.msg = NULL;
+ }
+}
+
+static int open_window(struct bmc_mbox_msg *msg, bool write, u32 offset, u32 size)
+{
+ int max_size = server_state.max_read_win << server_state.block_shift;
+ //int win_size = server_state.def_read_win;
+ enum win_type type = WIN_READ;
+ int prot = PROT_READ;
+
+ assert(server_state.win_type == WIN_CLOSED);
+
+ /* Shift params up */
+ offset <<= server_state.block_shift;
+ size <<= server_state.block_shift;
+
+ if (!size || server_state.api == 1)
+ size = server_state.def_read_win << server_state.block_shift;
+
+ if (write) {
+ max_size = server_state.max_write_win << server_state.block_shift;
+ //win_size = server_state.def_write_win;
+ prot |= PROT_WRITE;
+ type = WIN_WRITE;
+ /* Use the default size if zero size is set */
+ if (!size || server_state.api == 1)
+ size = server_state.def_write_win << server_state.block_shift;
+ }
+
+
+ prlog(PR_INFO, "Opening range %#.8x, %#.8x for %s\n",
+ offset, offset + size - 1, write ? "writing" : "reading");
+
+ /* XXX: Document this behaviour */
+ if ((size + offset) > server_state.lpc_size) {
+ prlog(PR_INFO, "tried to open beyond end of flash\n");
+ return MBOX_R_PARAM_ERROR;
+ }
+
+ /* XXX: should we do this before or after checking for errors?
+ * Doing it afterwards ensures consistency between
+ * implementations
+ */
+ if (server_state.api == 2)
+ size = MIN(size, max_size);
+
+ mprotect(server_state.lpc_base + offset, size, prot);
+ server_state.win_type = type;
+ server_state.win_base = offset;
+ server_state.win_size = size;
+
+ memset(msg->args, 0, sizeof(msg->args));
+ bmc_put_u16(msg, 0, offset >> server_state.block_shift);
+ if (server_state.api == 1) {
+ /*
+ * Put nonsense in here because v1 mbox-flash shouldn't know about it.
+ * If v1 mbox-flash does read this, 0xffff should trigger a big mistake.
+ */
+ bmc_put_u16(msg, 2, 0xffff >> server_state.block_shift);
+ bmc_put_u16(msg, 4, 0xffff >> server_state.block_shift);
+ } else {
+ bmc_put_u16(msg, 2, size >> server_state.block_shift);
+ bmc_put_u16(msg, 4, offset >> server_state.block_shift);
+ }
+ return MBOX_R_SUCCESS;
+}
+
+int bmc_mbox_enqueue(struct bmc_mbox_msg *msg,
+ unsigned int __unused timeout_sec)
+{
+ /*
+ * FIXME: should we be using the same storage for message
+ * and response?
+ */
+ int rc = MBOX_R_SUCCESS;
+ uint32_t start, size;
+
+ if (server_state.reset && msg->command != MBOX_C_GET_MBOX_INFO &&
+ msg->command != MBOX_C_BMC_EVENT_ACK) {
+ /*
+ * Real daemons should return an error, but for testing we'll
+ * be a bit more strict
+ */
+ prlog(PR_EMERG, "Server was in reset state - illegal command %d\n",
+ msg->command);
+ exit(1);
+ }
+
+ switch (msg->command) {
+ case MBOX_C_RESET_STATE:
+ prlog(PR_INFO, "RESET_STATE\n");
+ server_state.win_type = WIN_CLOSED;
+ rc = open_window(msg, false, 0, LPC_BLOCKS);
+ memset(msg->args, 0, sizeof(msg->args));
+ break;
+
+ case MBOX_C_GET_MBOX_INFO:
+ prlog(PR_INFO, "GET_MBOX_INFO version = %d, block_shift = %d\n",
+ server_state.api, server_state.block_shift);
+ msg->args[0] = server_state.api;
+ if (server_state.api == 1) {
+ prlog(PR_INFO, "\tread_size = 0x%08x, write_size = 0x%08x\n",
+ server_state.def_read_win, server_state.def_write_win);
+ bmc_put_u16(msg, 1, server_state.def_read_win);
+ bmc_put_u16(msg, 3, server_state.def_write_win);
+ msg->args[5] = 0xff; /* If v1 reads this, 0xff will force the mistake */
+ } else {
+ msg->args[5] = server_state.block_shift;
+ }
+ server_state.reset = false;
+ break;
+
+ case MBOX_C_GET_FLASH_INFO:
+ prlog(PR_INFO, "GET_FLASH_INFO: size: 0x%" PRIu64 ", erase: 0x%08x\n",
+ server_state.lpc_size, server_state.erase_granule);
+ if (server_state.api == 1) {
+ bmc_put_u32(msg, 0, server_state.lpc_size);
+ bmc_put_u32(msg, 4, server_state.erase_granule);
+ } else {
+ bmc_put_u16(msg, 0, server_state.lpc_size >> server_state.block_shift);
+ bmc_put_u16(msg, 2, server_state.erase_granule >> server_state.block_shift);
+ }
+ break;
+
+ case MBOX_C_CREATE_READ_WINDOW:
+ start = bmc_get_u16(msg, 0);
+ size = bmc_get_u16(msg, 2);
+ prlog(PR_INFO, "CREATE_READ_WINDOW: pos: 0x%08x, len: 0x%08x\n", start, size);
+ rc = close_window(false);
+ if (rc != MBOX_R_SUCCESS)
+ break;
+ rc = open_window(msg, false, start, size);
+ break;
+
+ case MBOX_C_CLOSE_WINDOW:
+ rc = close_window(true);
+ break;
+
+ case MBOX_C_CREATE_WRITE_WINDOW:
+ start = bmc_get_u16(msg, 0);
+ size = bmc_get_u16(msg, 2);
+ prlog(PR_INFO, "CREATE_WRITE_WINDOW: pos: 0x%08x, len: 0x%08x\n", start, size);
+ rc = close_window(false);
+ if (rc != MBOX_R_SUCCESS)
+ break;
+ rc = open_window(msg, true, start, size);
+ break;
+
+ /* TODO: make these do something */
+ case MBOX_C_WRITE_FLUSH:
+ prlog(PR_INFO, "WRITE_FLUSH\n");
+ /*
+ * This behaviour isn't strictly illegal however it could
+ * be a sign of bad behaviour
+ */
+ if (server_state.api > 1 && !server_state.win_dirty) {
+ prlog(PR_EMERG, "Version >1 called FLUSH without a previous DIRTY\n");
+ exit (1);
+ }
+ server_state.win_dirty = false;
+ if (server_state.api > 1)
+ break;
+
+ /* This is only done on V1 */
+ start = bmc_get_u16(msg, 0);
+ if (server_state.api == 1)
+ size = bmc_get_u32(msg, 2);
+ else
+ size = bmc_get_u16(msg, 2);
+ prlog(PR_INFO, "\tpos: 0x%08x len: 0x%08x\n", start, size);
+ rc = do_dirty(start, size);
+ break;
+ case MBOX_C_MARK_WRITE_DIRTY:
+ start = bmc_get_u16(msg, 0);
+ if (server_state.api == 1)
+ size = bmc_get_u32(msg, 2);
+ else
+ size = bmc_get_u16(msg, 2);
+ prlog(PR_INFO, "MARK_WRITE_DIRTY: pos: 0x%08x, len: %08x\n", start, size);
+ server_state.win_dirty = true;
+ rc = do_dirty(start, size);
+ break;
+ case MBOX_C_BMC_EVENT_ACK:
+ /*
+ * Clear any BMC notifier flags. Don't clear the server
+ * reset state here, it is a permitted command but only
+ * GET_INFO should clear it.
+ *
+ * Make sure that msg->args[0] is only acking bits we told
+ * it about, in server_state.attn_reg. The caveat is that
+ * it could NOT ack some bits...
+ */
+ prlog(PR_INFO, "BMC_EVENT_ACK 0x%02x\n", msg->args[0]);
+ if ((msg->args[0] | server_state.attn_reg) != server_state.attn_reg) {
+ prlog(PR_EMERG, "Tried to ack bits we didn't say!\n");
+ exit(1);
+ }
+ msg->bmc &= ~msg->args[0];
+ server_state.attn_reg &= ~msg->args[0];
+ break;
+ case MBOX_C_MARK_WRITE_ERASED:
+ start = bmc_get_u16(msg, 0) << server_state.block_shift;
+ size = bmc_get_u16(msg, 2) << server_state.block_shift;
+ /* If we've negotiated v1 this should never be called */
+ if (server_state.api == 1) {
+ prlog(PR_EMERG, "Version 1 protocol called a V2 only command\n");
+ exit(1);
+ }
+ /*
+ * This will likely result in flush (but not
+ * dirty) being called. This is the point.
+ */
+ server_state.win_dirty = true;
+ /* This should really be done when they call flush */
+ memset(server_state.lpc_base + server_state.win_base + start, 0xff, size);
+ break;
+ default:
+ prlog(PR_EMERG, "Got unknown command code from mbox: %d\n", msg->command);
+ }
+
+ prerror("command response = %d\n", rc);
+ msg->response = rc;
+
+ mbox_data.msg = msg;
+
+ return 0;
+}
+
+int mbox_server_memcmp(int off, const void *buf, size_t len)
+{
+ return memcmp(server_state.lpc_base + off, buf, len);
+}
+
+void mbox_server_memset(int c)
+{
+ memset(server_state.lpc_base, c, server_state.lpc_size);
+}
+
+uint32_t mbox_server_total_size(void)
+{
+ /* Not actually but for this server we don't differentiate */
+ return server_state.lpc_size;
+}
+
+uint32_t mbox_server_erase_granule(void)
+{
+ return server_state.erase_granule;
+}
+
+int mbox_server_version(void)
+{
+ return server_state.api;
+}
+
+int mbox_server_reset(unsigned int version, uint8_t block_shift)
+{
+ if (version > 3)
+ return 1;
+
+ server_state.api = version;
+ if (block_shift)
+ server_state.block_shift = block_shift;
+ if (server_state.erase_granule < (1 << server_state.block_shift))
+ server_state.erase_granule = 1 << server_state.block_shift;
+ server_state.lpc_size = LPC_BLOCKS * (1 << server_state.block_shift);
+ free(server_state.lpc_base);
+ server_state.lpc_base = malloc(server_state.lpc_size);
+ server_state.attn_reg = MBOX_ATTN_BMC_REBOOT | MBOX_ATTN_BMC_DAEMON_READY;
+ server_state.win_type = WIN_CLOSED;
+ server_state.reset = true;
+ mbox_data.attn(MBOX_ATTN_BMC_REBOOT, mbox_data.cb_attn);
+
+ return 0;
+}
+
+int mbox_server_init(void)
+{
+ server_state.api = 1;
+ server_state.reset = true;
+
+ /* We're always ready! */
+ server_state.attn_reg = MBOX_ATTN_BMC_DAEMON_READY;
+
+ /* setup server */
+ server_state.block_shift = 12;
+ server_state.erase_granule = 0x1000;
+ server_state.lpc_size = LPC_BLOCKS * (1 << server_state.block_shift);
+ server_state.lpc_base = malloc(server_state.lpc_size);
+
+ server_state.def_read_win = 1; /* These are in units of block shift "= 1 is 4K" */
+ server_state.def_write_win = 1; /* These are in units of block shift "= 1 is 4K" */
+
+ server_state.max_read_win = LPC_BLOCKS;
+ server_state.max_write_win = LPC_BLOCKS;
+ server_state.win_type = WIN_CLOSED;
+
+ return 0;
+}
+
+void mbox_server_destroy(void)
+{
+ free(server_state.lpc_base);
+}
diff --git a/roms/skiboot/libflash/test/mbox-server.h b/roms/skiboot/libflash/test/mbox-server.h
new file mode 100644
index 000000000..e7aec817f
--- /dev/null
+++ b/roms/skiboot/libflash/test/mbox-server.h
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2017 IBM Corp. */
+
+#include <stdint.h>
+
+uint32_t mbox_server_total_size(void);
+uint32_t mbox_server_erase_granule(void);
+int mbox_server_version(void);
+void mbox_server_memset(int c);
+int mbox_server_memcmp(int off, const void *buf, size_t len);
+int mbox_server_reset(unsigned int version, uint8_t block_shift);
+int mbox_server_init(void);
+void mbox_server_destroy(void);
diff --git a/roms/skiboot/libflash/test/stubs.c b/roms/skiboot/libflash/test/stubs.c
new file mode 100644
index 000000000..09f004359
--- /dev/null
+++ b/roms/skiboot/libflash/test/stubs.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Stubs for libflash test
+ *
+ * Copyright 2013-2018 IBM Corp.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/unistd.h> /* for usleep */
+
+#include "../../include/lpc-mbox.h"
+#include "stubs.h"
+
+#define __unused __attribute__((unused))
+
+__attribute__((weak)) void check_timers(bool __unused unused)
+{
+ return;
+}
+
+void time_wait_ms(unsigned long ms)
+{
+ usleep(ms * 1000);
+}
+
+/* skiboot stubs */
+unsigned long mftb(void)
+{
+ return 42;
+}
+unsigned long tb_hz = 512000000ul;
+
+void _prlog(int __unused log_level, const char* fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+/* accessor junk */
+
+void bmc_put_u16(struct bmc_mbox_msg *msg, int offset, uint16_t data)
+{
+ msg->args[offset + 0] = data & 0xff;
+ msg->args[offset + 1] = data >> 8;
+}
+
+void bmc_put_u32(struct bmc_mbox_msg *msg, int offset, uint32_t data)
+{
+ msg->args[offset + 0] = (data) & 0xff;
+ msg->args[offset + 1] = (data >> 8) & 0xff;
+ msg->args[offset + 2] = (data >> 16) & 0xff;
+ msg->args[offset + 3] = (data >> 24) & 0xff;
+}
+
+u32 bmc_get_u32(struct bmc_mbox_msg *msg, int offset)
+{
+ u32 data = 0;
+
+ data |= msg->args[offset + 0];
+ data |= msg->args[offset + 1] << 8;
+ data |= msg->args[offset + 2] << 16;
+ data |= msg->args[offset + 3] << 24;
+
+ return data;
+}
+
+u16 bmc_get_u16(struct bmc_mbox_msg *msg, int offset)
+{
+ u16 data = 0;
+
+ data |= msg->args[offset + 0];
+ data |= msg->args[offset + 1] << 8;
+
+ return data;
+}
+
+void *__zalloc(size_t sz)
+{
+ return calloc(1, sz);
+}
+
+void __free(const void *p)
+{
+ free((void *)p);
+}
+
+void lock_caller(struct lock *l __attribute__((unused)),
+ const char *caller __attribute__((unused)))
+{
+}
+
+void unlock(struct lock *l __attribute__((unused)))
+{
+}
diff --git a/roms/skiboot/libflash/test/stubs.h b/roms/skiboot/libflash/test/stubs.h
new file mode 100644
index 000000000..3f890c930
--- /dev/null
+++ b/roms/skiboot/libflash/test/stubs.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2018 IBM Corp. */
+
+#include <lock.h>
+#include <stdint.h>
+
+#include "../../include/lpc-mbox.h"
+
+void check_timers(bool unused);
+void time_wait_ms(unsigned long ms);
+unsigned long mftb(void);
+void _prlog(int log_level, const char* fmt, ...);
+void bmc_put_u16(struct bmc_mbox_msg *msg, int offset, uint16_t data);
+void bmc_put_u32(struct bmc_mbox_msg *msg, int offset, uint32_t data);
+u16 bmc_get_u16(struct bmc_mbox_msg *msg, int offset);
+u32 bmc_get_u32(struct bmc_mbox_msg *msg, int offset);
+void *__zalloc(size_t sz);
+void __free(const void *p);
+void lock_caller(struct lock *l, const char *caller);
+void unlock(struct lock *l);
diff --git a/roms/skiboot/libflash/test/test-blocklevel.c b/roms/skiboot/libflash/test/test-blocklevel.c
new file mode 100644
index 000000000..7a4fe19ac
--- /dev/null
+++ b/roms/skiboot/libflash/test/test-blocklevel.c
@@ -0,0 +1,664 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2018 IBM Corp. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <libflash/blocklevel.h>
+
+#include "../ecc.c"
+#include "../blocklevel.c"
+
+#define __unused __attribute__((unused))
+
+#define ERR(fmt...) fprintf(stderr, fmt)
+
+bool libflash_debug;
+
+static int bl_test_bad_read(struct blocklevel_device *bl __unused, uint64_t pos __unused,
+ void *buf __unused, uint64_t len __unused)
+{
+ return FLASH_ERR_PARM_ERROR;
+}
+
+static int bl_test_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len)
+{
+ if (pos + len > 0x1000)
+ return FLASH_ERR_PARM_ERROR;
+
+ memcpy(buf, bl->priv + pos, len);
+
+ return 0;
+}
+
+static int bl_test_bad_write(struct blocklevel_device *bl __unused, uint64_t pos __unused,
+ const void *buf __unused, uint64_t len __unused)
+{
+ return FLASH_ERR_PARM_ERROR;
+}
+
+static int bl_test_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len)
+{
+ if (pos + len > 0x1000)
+ return FLASH_ERR_PARM_ERROR;
+
+ memcpy(bl->priv + pos, buf, len);
+
+ return 0;
+}
+
+static int bl_test_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len)
+{
+ if (pos + len > 0x1000)
+ return FLASH_ERR_PARM_ERROR;
+
+ memset(bl->priv + pos, 0xff, len);
+
+ return 0;
+}
+
+static void dump_buf(uint8_t *buf, int start, int end, int miss)
+{
+ int i;
+
+ printf("pos: value\n");
+ for (i = start; i < end; i++)
+ printf("%04x: %c%s\n", i, buf[i] == 0xff ? '-' : buf[i], i == miss ? " <- First missmatch" : "");
+}
+
+/*
+ * Returns zero if the buffer is ok. Otherwise returns the position of
+ * the mismatch. If the mismatch is at zero -1 is returned
+ */
+static int check_buf(uint8_t *buf, int zero_start, int zero_end)
+{
+ int i;
+
+ for (i = 0; i < 0x1000; i++) {
+ if (i >= zero_start && i < zero_end && buf[i] != 0xff)
+ return i == 0 ? -1 : i;
+ if ((i < zero_start || i >= zero_end) && buf[i] != (i % 26) + 'a')
+ return i == 0 ? -1 : i;
+ }
+
+ return 0;
+}
+
+static void reset_buf(uint8_t *buf)
+{
+ int i;
+
+ for (i = 0; i < 0x1000; i++) {
+ /* This gives repeating a - z which will be nice to visualise */
+ buf[i] = (i % 26) + 'a';
+ }
+}
+
+static void print_ptr(void *ptr, int len)
+{
+ int i;
+ char *p = ptr;
+
+ printf("0x");
+ for (i = 0; i < len; i++) {
+ putchar(*p);
+ if (i && i % 8 == 0) {
+ putchar('\n');
+ if (len - i)
+ printf("0x");
+ }
+ }
+ putchar('\n');
+}
+
+int main(void)
+{
+ struct blocklevel_device bl_mem = { 0 };
+ struct blocklevel_device *bl = &bl_mem;
+ uint64_t with_ecc[10], without_ecc[10];
+ char *buf = NULL, *data = NULL;
+ int i, rc, miss;
+
+ if (blocklevel_ecc_protect(bl, 0, 0x1000)) {
+ ERR("Failed to blocklevel_ecc_protect!\n");
+ return 1;
+ }
+
+ /* 0x1000 -> 0x3000 should remain unprotected */
+
+ if (blocklevel_ecc_protect(bl, 0x3000, 0x1000)) {
+ ERR("Failed to blocklevel_ecc_protect(0x3000, 0x1000)\n");
+ return 1;
+ }
+ if (blocklevel_ecc_protect(bl, 0x2f00, 0x1100)) {
+ ERR("Failed to blocklevel_ecc_protect(0x2f00, 0x1100)\n");
+ return 1;
+ }
+
+ /* Zero length protection */
+ if (!blocklevel_ecc_protect(bl, 0x4000, 0)) {
+ ERR("Shouldn't have succeeded blocklevel_ecc_protect(0x4000, 0)\n");
+ return 1;
+ }
+
+ /* Minimum creatable size */
+ if (blocklevel_ecc_protect(bl, 0x4000, BYTES_PER_ECC)) {
+ ERR("Failed to blocklevel_ecc_protect(0x4000, BYTES_PER_ECC)\n");
+ return 1;
+ }
+
+ /* Deal with overlapping protections */
+ if (blocklevel_ecc_protect(bl, 0x100, 0x1000)) {
+ ERR("Failed to protect overlaping region blocklevel_ecc_protect(0x100, 0x1000)\n");
+ return 1;
+ }
+
+ /* Deal with overflow */
+ if (!blocklevel_ecc_protect(bl, 1, 0xFFFFFFFF)) {
+ ERR("Added an 'overflow' protection blocklevel_ecc_protect(1, 0xFFFFFFFF)\n");
+ return 1;
+ }
+
+ /* Protect everything */
+ if (blocklevel_ecc_protect(bl, 0, 0xFFFFFFFF)) {
+ ERR("Couldn't protect everything blocklevel_ecc_protect(0, 0xFFFFFFFF)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0, 1, NULL) != 1) {
+ ERR("Invaid result for ecc_protected(0, 1)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0, 0x1000, NULL) != 1) {
+ ERR("Invalid result for ecc_protected(0, 0x1000)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0x100, 0x100, NULL) != 1) {
+ ERR("Invalid result for ecc_protected(0x0100, 0x100)\n");
+ return 1;
+ }
+
+ /* Clear the protections */
+ bl->ecc_prot.n_prot = 0;
+ /* Reprotect */
+ if (blocklevel_ecc_protect(bl, 0x3000, 0x1000)) {
+ ERR("Failed to blocklevel_ecc_protect(0x3000, 0x1000)\n");
+ return 1;
+ }
+ /* Deal with overlapping protections */
+ if (blocklevel_ecc_protect(bl, 0x100, 0x1000)) {
+ ERR("Failed to protect overlaping region blocklevel_ecc_protect(0x100, 0x1000)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0x1000, 0, NULL) != 1) {
+ ERR("Invalid result for ecc_protected(0x1000, 0)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0x1000, 0x1000, NULL) != -1) {
+ ERR("Invalid result for ecc_protected(0x1000, 0x1000)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0x1000, 0x100, NULL) != 1) {
+ ERR("Invalid result for ecc_protected(0x1000, 0x100)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0x2000, 0, NULL) != 0) {
+ ERR("Invalid result for ecc_protected(0x2000, 0)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0x4000, 1, NULL) != 0) {
+ ERR("Invalid result for ecc_protected(0x4000, 1)\n");
+ return 1;
+ }
+
+ /* Check for asking for a region with mixed protection */
+ if (ecc_protected(bl, 0x100, 0x2000, NULL) != -1) {
+ ERR("Invalid result for ecc_protected(0x100, 0x2000)\n");
+ return 1;
+ }
+
+ /* Test the auto extending of regions */
+ if (blocklevel_ecc_protect(bl, 0x5000, 0x100)) {
+ ERR("Failed to blocklevel_ecc_protect(0x5000, 0x100)\n");
+ return 1;
+ }
+
+ if (blocklevel_ecc_protect(bl, 0x5100, 0x100)) {
+ ERR("Failed to blocklevel_ecc_protect(0x5100, 0x100)\n");
+ return 1;
+ }
+
+ if (blocklevel_ecc_protect(bl, 0x5200, 0x100)) {
+ ERR("Failed to blocklevel_ecc_protect(0x5200, 0x100)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0x5120, 0x10, NULL) != 1) {
+ ERR("Invalid result for ecc_protected(0x5120, 0x10)\n");
+ return 1;
+ }
+
+ if (blocklevel_ecc_protect(bl, 0x4f00, 0x100)) {
+ ERR("Failed to blocklevel_ecc_protected(0x4900, 0x100)\n");
+ return 1;
+ }
+
+ if (blocklevel_ecc_protect(bl, 0x4900, 0x100)) {
+ ERR("Failed to blocklevel_ecc_protected(0x4900, 0x100)\n");
+ return 1;
+ }
+
+ if (ecc_protected(bl, 0x4920, 0x10, NULL) != 1) {
+ ERR("Invalid result for ecc_protected(0x4920, 0x10)\n");
+ return 1;
+ }
+
+ if (blocklevel_ecc_protect(bl, 0x5290, 0x10)) {
+ ERR("Failed to blocklevel_ecc_protect(0x5290, 0x10)\n");
+ return 1;
+ }
+
+ /* Test the auto extending of regions */
+ if (blocklevel_ecc_protect(bl, 0x6000, 0x100)) {
+ ERR("Failed to blocklevel_ecc_protect(0x6000, 0x100)\n");
+ return 1;
+ }
+
+ if (blocklevel_ecc_protect(bl, 0x6200, 0x100)) {
+ ERR("Failed to blocklevel_ecc_protect(0x6200, 0x100)\n");
+ return 1;
+ }
+
+ /* Test ECC reading and writing being 100% transparent to the
+ * caller */
+ buf = malloc(0x1000);
+ data = malloc(0x100);
+ if (!buf || !data) {
+ ERR("Malloc failed\n");
+ rc = 1;
+ goto out;
+ }
+ memset(bl, 0, sizeof(*bl));
+ bl_mem.read = &bl_test_read;
+ bl_mem.write = &bl_test_write;
+ bl_mem.erase = &bl_test_erase;
+ bl_mem.erase_mask = 0xff;
+ bl_mem.priv = buf;
+ reset_buf(buf);
+
+
+ /*
+ * Test 1: One full and exact erase block, this shouldn't call
+ * read or write, ensure this fails if it does.
+ */
+ bl_mem.write = &bl_test_bad_write;
+ bl_mem.read = &bl_test_bad_read;
+ if (blocklevel_smart_erase(bl, 0x100, 0x100)) {
+ ERR("Failed to blocklevel_smart_erase(0x100, 0x100)\n");
+ goto out;
+ }
+ miss = check_buf(buf, 0x100, 0x200);
+ if (miss) {
+ ERR("Buffer mismatch after blocklevel_smart_erase(0x100, 0x100) at 0x%0x\n",
+ miss == -1 ? 0 : miss);
+ dump_buf(buf, 0xfc, 0x105, miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x1fc, 0x205, miss == -1 ? 0 : miss);
+ goto out;
+ }
+ bl_mem.read = &bl_test_read;
+ bl_mem.write = &bl_test_write;
+
+ reset_buf(buf);
+ /* Test 2: Only touch one erase block */
+ if (blocklevel_smart_erase(bl, 0x20, 0x40)) {
+ ERR("Failed to blocklevel_smart_erase(0x20, 0x40)\n");
+ goto out;
+ }
+ miss = check_buf(buf, 0x20, 0x60);
+ if (miss) {
+ ERR("Buffer mismatch after blocklevel_smart_erase(0x20, 0x40) at 0x%x\n",
+ miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x1c, 0x65, miss == -1 ? 0 : miss);
+ goto out;
+ }
+
+ reset_buf(buf);
+ /* Test 3: Start aligned but finish somewhere in it */
+ if (blocklevel_smart_erase(bl, 0x100, 0x50)) {
+ ERR("Failed to blocklevel_smart_erase(0x100, 0x50)\n");
+ goto out;
+ }
+ miss = check_buf(buf, 0x100, 0x150);
+ if (miss) {
+ ERR("Buffer mismatch after blocklevel_smart_erase(0x100, 0x50) at 0x%0x\n",
+ miss == -1 ? 0 : miss);
+ dump_buf(buf, 0xfc, 0x105, miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x14c, 0x155, miss == -1 ? 0 : miss);
+ goto out;
+ }
+
+ reset_buf(buf);
+ /* Test 4: Start somewhere in it, finish aligned */
+ if (blocklevel_smart_erase(bl, 0x50, 0xb0)) {
+ ERR("Failed to blocklevel_smart_erase(0x50, 0xb0)\n");
+ goto out;
+ }
+ miss = check_buf(buf, 0x50, 0x100);
+ if (miss) {
+ ERR("Buffer mismatch after blocklevel_smart_erase(0x50, 0xb0) at 0x%x\n",
+ miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x4c, 0x55, miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x100, 0x105, miss == -1 ? 0 : miss);
+ goto out;
+ }
+
+ reset_buf(buf);
+ /* Test 5: Cover two erase blocks exactly */
+ if (blocklevel_smart_erase(bl, 0x100, 0x200)) {
+ ERR("Failed to blocklevel_smart_erase(0x100, 0x200)\n");
+ goto out;
+ }
+ miss = check_buf(buf, 0x100, 0x300);
+ if (miss) {
+ ERR("Buffer mismatch after blocklevel_smart_erase(0x100, 0x200) at 0x%x\n",
+ miss == -1 ? 0 : miss);
+ dump_buf(buf, 0xfc, 0x105, miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x2fc, 0x305, miss == -1 ? 0 : miss);
+ goto out;
+ }
+
+ reset_buf(buf);
+ /* Test 6: Erase 1.5 blocks (start aligned) */
+ if (blocklevel_smart_erase(bl, 0x100, 0x180)) {
+ ERR("Failed to blocklevel_smart_erase(0x100, 0x180)\n");
+ goto out;
+ }
+ miss = check_buf(buf, 0x100, 0x280);
+ if (miss) {
+ ERR("Buffer mismatch after blocklevel_smart_erase(0x100, 0x180) at 0x%x\n",
+ miss == -1 ? 0 : miss);
+ dump_buf(buf, 0xfc, 0x105, miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x27c, 0x285, miss == -1 ? 0 : miss);
+ goto out;
+ }
+
+ reset_buf(buf);
+ /* Test 7: Erase 1.5 blocks (end aligned) */
+ if (blocklevel_smart_erase(bl, 0x80, 0x180)) {
+ ERR("Failed to blocklevel_smart_erase(0x80, 0x180)\n");
+ goto out;
+ }
+ miss = check_buf(buf, 0x80, 0x200);
+ if (miss) {
+ ERR("Buffer mismatch after blocklevel_smart_erase(0x80, 0x180) at 0x%x\n",
+ miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x7c, 0x85, miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x1fc, 0x205, miss == -1 ? 0 : miss);
+ goto out;
+ }
+
+ reset_buf(buf);
+ /* Test 8: Erase a big section, not aligned */
+ if (blocklevel_smart_erase(bl, 0x120, 0x544)) {
+ ERR("Failed to blocklevel_smart_erase(0x120, 0x544)\n");
+ goto out;
+ }
+ miss = check_buf(buf, 0x120, 0x664);
+ if (miss) {
+ ERR("Buffer mismatch after blocklevel_smart_erase(0x120, 0x544) at 0x%x\n",
+ miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x11c, 0x125, miss == -1 ? 0 : miss);
+ dump_buf(buf, 0x65f, 0x669, miss == -1 ? 0 : miss);
+ goto out;
+ }
+
+ bl_mem.priv = buf;
+ reset_buf(buf);
+
+ for (i = 0; i < 0x100; i++)
+ data[i] = i;
+
+ /* This really shouldn't fail */
+ rc = blocklevel_ecc_protect(bl, 0, 0x100);
+ if (rc) {
+ ERR("Couldn't blocklevel_ecc_protect(0, 0x100)\n");
+ goto out;
+ }
+
+ rc = blocklevel_write(bl, 0, data, 0x100);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0, 0x100)\n");
+ goto out;
+ }
+
+ rc = blocklevel_write(bl, 0x200, data, 0x100);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0x200, 0x100)\n");
+ goto out;
+ }
+
+ /*
+ * 0x50 once adjusted for the presence of ECC becomes 0x5a which
+ * is ECC aligned.
+ */
+ rc = blocklevel_read(bl, 0x50, with_ecc, 8);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x50, 8) with ecc rc=%d\n", rc);
+ goto out;
+ }
+ rc = blocklevel_read(bl, 0x250, without_ecc, 8);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x250, 8) without ecc rc=%d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, without_ecc, 8) || memcmp(with_ecc, &data[0x50], 8)) {
+ ERR("ECC read and non-ECC read don't match or are wrong line: %d\n", __LINE__);
+ print_ptr(with_ecc, 8);
+ print_ptr(without_ecc, 8);
+ print_ptr(&data[50], 8);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * 0x50 once adjusted for the presence of ECC becomes 0x5a which
+ * is ECC aligned.
+ * So 0x4f won't be aligned!
+ */
+ rc = blocklevel_read(bl, 0x4f, with_ecc, 8);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x4f, 8) with ecc %d\n", rc);
+ goto out;
+ }
+ rc = blocklevel_read(bl, 0x24f, without_ecc, 8);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x24f, 8) without ecc %d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, without_ecc, 8) || memcmp(with_ecc, &data[0x4f], 8)) {
+ ERR("ECC read and non-ECC read don't match or are wrong line: %d\n", __LINE__);
+ print_ptr(with_ecc, 8);
+ print_ptr(without_ecc, 8);
+ print_ptr(&data[0x4f], 8);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * 0x50 once adjusted for the presence of ECC becomes 0x5a which
+ * is ECC aligned.
+ */
+ rc = blocklevel_read(bl, 0x50, with_ecc, 16);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x50, 16) with ecc %d\n", rc);
+ goto out;
+ }
+ rc = blocklevel_read(bl, 0x250, without_ecc, 16);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x250, 16) without ecc %d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, without_ecc, 16)|| memcmp(with_ecc, &data[0x50], 16)) {
+ ERR("(long read )ECC read and non-ECC read don't match or are wrong line: %d\n", __LINE__);
+ print_ptr(with_ecc, 16);
+ print_ptr(without_ecc, 16);
+ print_ptr(&data[0x50], 16);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * 0x50 once adjusted for the presence of ECC becomes 0x5a which
+ * is ECC aligned. So 4f won't be.
+ */
+ rc = blocklevel_read(bl, 0x4f, with_ecc, 24);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x4f, 24) with ecc %d\n", rc);
+ goto out;
+ }
+ rc = blocklevel_read(bl, 0x24f, without_ecc, 24);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x24f, 24) without ecc %d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, without_ecc, 24)|| memcmp(with_ecc, &data[0x4f], 24)) {
+ ERR("(long read )ECC read and non-ECC read don't match or are wrong: %d\n", __LINE__);
+ print_ptr(with_ecc, 24);
+ print_ptr(without_ecc, 24);
+ print_ptr(&data[0x4f], 24);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * Now lets try to write at non ECC aligned positions
+ * Go easy first, 0x50 becomes 0x5a which is ECC byte aligned but
+ * not aligned to the start of the partition
+ */
+
+ rc = blocklevel_write(bl, 0x50, data, 0xb0);
+ if (rc) {
+ ERR("Couldn't blocklevel_write()\n");
+ goto out;
+ }
+ /* Read 8 bytes before to make sure we didn't ruin that */
+ rc = blocklevel_read(bl, 0x48, with_ecc, 24);
+ if (rc) {
+ ERR("Couldn't blocklevel_read() with ecc %d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, data + 0x48, 8) || memcmp(with_ecc + 1, data, 16)) {
+ rc = 1;
+ ERR("Couldn't read back what we thought we wrote line: %d\n", __LINE__);
+ print_ptr(with_ecc, 24);
+ print_ptr(&data[0x48], 8);
+ print_ptr(data, 16);
+ goto out;
+ }
+
+ /* Ok lets get tricky */
+ rc = blocklevel_write(bl, 0x31, data, 0xcf);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0x31, 0xcf)\n");
+ goto out;
+ }
+ /* Read 8 bytes before to make sure we didn't ruin that */
+ rc = blocklevel_read(bl, 0x29, with_ecc, 24);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0x29, 24) with ecc rc=%d\n", rc);
+ goto out;
+ }
+ if (memcmp(with_ecc, &data[0x29], 8) || memcmp(with_ecc + 1, data, 16)) {
+ ERR("Couldn't read back what we thought we wrote line: %d\n", __LINE__);
+ print_ptr(with_ecc, 24);
+ print_ptr(&data[0x29], 8);
+ print_ptr(data, 16);
+ rc = 1;
+ goto out;
+ }
+
+ /*
+ * Rewrite the pattern that we've messed up
+ */
+ rc = blocklevel_write(bl, 0, data, 0x100);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0, 0x100) to reset\n");
+ goto out;
+ }
+
+ /* Be unalignmed as possible from now on, starting somewhat easy */
+ rc = blocklevel_read(bl, 0, with_ecc, 5);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(0, 5)\n");
+ goto out;
+ }
+ if (memcmp(with_ecc, data, 5)) {
+ ERR("blocklevel_read 5, 0) didn't match line: %d\n", __LINE__);
+ print_ptr(with_ecc, 5);
+ print_ptr(data, 5);
+ rc = 1;
+ goto out;
+ }
+
+ /* 39 is neither divisible by 8 or by 9 */
+ rc = blocklevel_read(bl, 39, with_ecc, 5);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(39, 5)\n");
+ goto out;
+ }
+ if (memcmp(with_ecc, &data[39], 5)) {
+ ERR("blocklevel_read(5, 39() didn't match line: %d\n", __LINE__);
+ print_ptr(with_ecc, 5);
+ print_ptr(&data[39], 5);
+ rc = 1;
+ goto out;
+ }
+
+ rc = blocklevel_read(bl, 0xb, &with_ecc, 39);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(0xb, 39)\n");
+ goto out;
+ }
+ if (memcmp(with_ecc, &data[0xb], 39)) {
+ ERR("Strange sized and positioned read failed, blocklevel_read(0xb, 39) line: %d\n", __LINE__);
+ print_ptr(with_ecc, 39);
+ print_ptr(&data[0xb], 39);
+ rc = 1;
+ goto out;
+ }
+
+ rc = blocklevel_write(bl, 39, data, 50);
+ if (rc) {
+ ERR("Couldn't blocklevel_write(39, 50)\n");
+ goto out;
+ }
+
+ rc = blocklevel_read(bl, 32, with_ecc, 39);
+ if (rc) {
+ ERR("Couldn't blocklevel_read(32, 39)\n");
+ goto out;
+ }
+
+ if (memcmp(with_ecc, &data[32], 7) || memcmp(((char *)with_ecc) + 7, data, 32)) {
+ ERR("Read back of odd placed/odd sized write failed, blocklevel_read(32, 39) line: %d\n", __LINE__);
+ print_ptr(with_ecc, 39);
+ print_ptr(&data[32], 7);
+ print_ptr(data, 32);
+ rc = 1;
+ goto out;
+ }
+
+out:
+ free(buf);
+ free(data);
+return rc;
+}
diff --git a/roms/skiboot/libflash/test/test-ecc.c b/roms/skiboot/libflash/test/test-ecc.c
new file mode 100644
index 000000000..b9489f8ec
--- /dev/null
+++ b/roms/skiboot/libflash/test/test-ecc.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2018 IBM Corp. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <libflash/ecc.h>
+
+#include "../ecc.c"
+
+#define __unused __attribute__((unused))
+
+#define ERR(fmt...) fprintf(stderr, fmt)
+
+#define NUM_ECC_ROWS 320
+
+/*
+ * Note this data is big endian as this is what the ecc code expects.
+ * The ECC code returns IBM bit numbers assuming the word was in CPU
+ * endian!
+ */
+
+/* 8 data bytes 1 ecc byte per row */
+struct ecc64 ecc_data[] = {
+ { 0xfeffffffffffffff, 0x00 }, /* This row will have ecc correct bit 63 */
+ { 0xfdffffffffffffff, 0x00 }, /* This row will have ecc correct bit 62 */
+ { 0xfbffffffffffffff, 0x00 }, /* This row will have ecc correct bit 61 */
+ { 0xf7ffffffffffffff, 0x00 }, /* This row will have ecc correct bit 60 */
+ { 0xefffffffffffffff, 0x00 }, /* This row will have ecc correct bit 59 */
+ { 0xdfffffffffffffff, 0x00 }, /* This row will have ecc correct bit 58 */
+ { 0xbfffffffffffffff, 0x00 }, /* This row will have ecc correct bit 57 */
+ { 0x7fffffffffffffff, 0x00 }, /* This row will have ecc correct bit 56 */
+ { 0xfffeffffffffffff, 0x00 }, /* This row will have ecc correct bit 55 */
+ { 0xfffdffffffffffff, 0x00 }, /* This row will have ecc correct bit 54 */
+ { 0xfffbffffffffffff, 0x00 }, /* This row will have ecc correct bit 53 */
+ { 0xfff7ffffffffffff, 0x00 }, /* This row will have ecc correct bit 52 */
+ { 0xffefffffffffffff, 0x00 }, /* This row will have ecc correct bit 51 */
+ { 0xffdfffffffffffff, 0x00 }, /* This row will have ecc correct bit 50 */
+ { 0xffbfffffffffffff, 0x00 }, /* This row will have ecc correct bit 49 */
+ { 0xff7fffffffffffff, 0x00 }, /* This row will have ecc correct bit 48 */
+ { 0xfffffeffffffffff, 0x00 }, /* This row will have ecc correct bit 47 */
+ { 0xfffffdffffffffff, 0x00 }, /* This row will have ecc correct bit 46 */
+ { 0xfffffbffffffffff, 0x00 }, /* This row will have ecc correct bit 45 */
+ { 0xfffff7ffffffffff, 0x00 }, /* This row will have ecc correct bit 44 */
+ { 0xffffefffffffffff, 0x00 }, /* This row will have ecc correct bit 43 */
+ { 0xffffdfffffffffff, 0x00 }, /* This row will have ecc correct bit 42 */
+ { 0xffffbfffffffffff, 0x00 }, /* This row will have ecc correct bit 41 */
+ { 0xffff7fffffffffff, 0x00 }, /* This row will have ecc correct bit 40 */
+ { 0xfffffffeffffffff, 0x00 }, /* This row will have ecc correct bit 39 */
+ { 0xfffffffdffffffff, 0x00 }, /* This row will have ecc correct bit 38 */
+ { 0xfffffffbffffffff, 0x00 }, /* This row will have ecc correct bit 37 */
+ { 0xfffffff7ffffffff, 0x00 }, /* This row will have ecc correct bit 36 */
+ { 0xffffffefffffffff, 0x00 }, /* This row will have ecc correct bit 35 */
+ { 0xffffffdfffffffff, 0x00 }, /* This row will have ecc correct bit 34 */
+ { 0xffffffbfffffffff, 0x00 }, /* This row will have ecc correct bit 33 */
+ { 0xffffff7fffffffff, 0x00 }, /* This row will have ecc correct bit 32 */
+ { 0xfffffffffeffffff, 0x00 }, /* This row will have ecc correct bit 31 */
+ { 0xfffffffffdffffff, 0x00 }, /* This row will have ecc correct bit 30 */
+ { 0xfffffffffbffffff, 0x00 }, /* This row will have ecc correct bit 29 */
+ { 0xfffffffff7ffffff, 0x00 }, /* This row will have ecc correct bit 28 */
+ { 0xffffffffefffffff, 0x00 }, /* This row will have ecc correct bit 27 */
+ { 0xffffffffdfffffff, 0x00 }, /* This row will have ecc correct bit 26 */
+ { 0xffffffffbfffffff, 0x00 }, /* This row will have ecc correct bit 25 */
+ { 0xffffffff7fffffff, 0x00 }, /* This row will have ecc correct bit 24 */
+ { 0xfffffffffffeffff, 0x00 }, /* This row will have ecc correct bit 23 */
+ { 0xfffffffffffdffff, 0x00 }, /* This row will have ecc correct bit 22 */
+ { 0xfffffffffffbffff, 0x00 }, /* This row will have ecc correct bit 21 */
+ { 0xfffffffffff7ffff, 0x00 }, /* This row will have ecc correct bit 20 */
+ { 0xffffffffffefffff, 0x00 }, /* This row will have ecc correct bit 19 */
+ { 0xffffffffffdfffff, 0x00 }, /* This row will have ecc correct bit 18 */
+ { 0xffffffffffbfffff, 0x00 }, /* This row will have ecc correct bit 17 */
+ { 0xffffffffff7fffff, 0x00 }, /* This row will have ecc correct bit 16 */
+ { 0xfffffffffffffeff, 0x00 }, /* This row will have ecc correct bit 15 */
+ { 0xfffffffffffffdff, 0x00 }, /* This row will have ecc correct bit 14 */
+ { 0xfffffffffffffbff, 0x00 }, /* This row will have ecc correct bit 13 */
+ { 0xfffffffffffff7ff, 0x00 }, /* This row will have ecc correct bit 12 */
+ { 0xffffffffffffefff, 0x00 }, /* This row will have ecc correct bit 11 */
+ { 0xffffffffffffdfff, 0x00 }, /* This row will have ecc correct bit 10 */
+ { 0xffffffffffffbfff, 0x00 }, /* This row will have ecc correct bit 9 */
+ { 0xffffffffffff7fff, 0x00 }, /* This row will have ecc correct bit 8 */
+ { 0xfffffffffffffffe, 0x00 }, /* This row will have ecc correct bit 7 */
+ { 0xfffffffffffffffd, 0x00 }, /* This row will have ecc correct bit 6 */
+ { 0xfffffffffffffffb, 0x00 }, /* This row will have ecc correct bit 5 */
+ { 0xfffffffffffffff7, 0x00 }, /* This row will have ecc correct bit 4 */
+ { 0xffffffffffffffef, 0x00 }, /* This row will have ecc correct bit 3 */
+ { 0xffffffffffffffdf, 0x00 }, /* This row will have ecc correct bit 2 */
+ { 0xffffffffffffffbf, 0x00 }, /* This row will have ecc correct bit 1 */
+ { 0xffffffffffffff7f, 0x00 }, /* This row will have ecc correct bit 0 */
+ /*
+ * 'Randomised' input into eccgenerate 0x54f7c5d1 was seeded to rand()
+ * Note: eccgenerate from skiboot commit 6cfaa3ba1015c6ac9cc4a06f878b4289022cff54
+ * was used to generate these ecc numbers
+ */
+ { 0x29d87c7c8ab7d46d, 0xb9 }, /* Use this row to check eccgenerate() */
+ { 0x9064174098381641, 0x3b }, /* Use this row to check eccgenerate() */
+ { 0x77fd7d2fc7d22154, 0xe4 }, /* Use this row to check eccgenerate() */
+ { 0x6b02ba39b64a6168, 0xbf }, /* Use this row to check eccgenerate() */
+ { 0x68fa9c633eef0544, 0x2a }, /* Use this row to check eccgenerate() */
+ { 0xe814b258b3f92e55, 0x35 }, /* Use this row to check eccgenerate() */
+ { 0xc3e2bd658db4db6d, 0xda }, /* Use this row to check eccgenerate() */
+ { 0xe1dd487b6209876a, 0x45 }, /* Use this row to check eccgenerate() */
+ { 0x309f9e6b91831433, 0xe4 }, /* Use this row to check eccgenerate() */
+ { 0xd8b77d39f4d66410, 0x6c }, /* Use this row to check eccgenerate() */
+ { 0x83ba293cf30a9e6a, 0xc9 }, /* Use this row to check eccgenerate() */
+ { 0x3aeaef79af97ec1a, 0x09 }, /* Use this row to check eccgenerate() */
+ { 0xa90ef431e4778c43, 0x91 }, /* Use this row to check eccgenerate() */
+ { 0xa74bbf1e6b6fda00, 0xc5 }, /* Use this row to check eccgenerate() */
+ { 0x67b5a872efa57c30, 0xb9 }, /* Use this row to check eccgenerate() */
+ { 0x795d511e3605ff67, 0x03 }, /* Use this row to check eccgenerate() */
+ { 0xce3d1529918d256f, 0x36 }, /* Use this row to check eccgenerate() */
+ { 0x586047430ac2685e, 0xab }, /* Use this row to check eccgenerate() */
+ { 0xc00cca46463b9358, 0x42 }, /* Use this row to check eccgenerate() */
+ { 0x842a991cc362017d, 0xb2 }, /* Use this row to check eccgenerate() */
+ { 0x765c30522807672a, 0x26 }, /* Use this row to check eccgenerate() */
+ { 0xb5bb42186c3f4b75, 0x2b }, /* Use this row to check eccgenerate() */
+ { 0xce48d25f393fee37, 0x90 }, /* Use this row to check eccgenerate() */
+ { 0xcbc2026b96998b13, 0x40 }, /* Use this row to check eccgenerate() */
+ { 0x8b70f023ffe7704b, 0x23 }, /* Use this row to check eccgenerate() */
+ { 0xf2f20e36a37a8024, 0x19 }, /* Use this row to check eccgenerate() */
+ { 0x52126d3f0e2b1a60, 0xa0 }, /* Use this row to check eccgenerate() */
+ { 0xf2a2a6232dddfe2f, 0xc4 }, /* Use this row to check eccgenerate() */
+ { 0x984cd930fb206171, 0xa5 }, /* Use this row to check eccgenerate() */
+ { 0xeac6dd2199ee6542, 0xea }, /* Use this row to check eccgenerate() */
+ { 0xd0f3642aff018223, 0x3b }, /* Use this row to check eccgenerate() */
+ { 0x908fa71263242f40, 0x0a }, /* Use this row to check eccgenerate() */
+ { 0x6de6971e9e317a53, 0xa6 }, /* Use this row to check eccgenerate() */
+ { 0xe46c0d2ce8efee55, 0xa4 }, /* Use this row to check eccgenerate() */
+ { 0xab52f0522df36165, 0x06 }, /* Use this row to check eccgenerate() */
+ { 0x55fac80f6997a648, 0x9a }, /* Use this row to check eccgenerate() */
+ { 0xd5d6f13d21af2025, 0xed }, /* Use this row to check eccgenerate() */
+ { 0x5bee0e5d0bb60b28, 0x66 }, /* Use this row to check eccgenerate() */
+ { 0xa14f973ba41fc41d, 0xa8 }, /* Use this row to check eccgenerate() */
+ { 0xa307356926b11148, 0x5a }, /* Use this row to check eccgenerate() */
+ { 0xc92b926c2cc0875f, 0x7e }, /* Use this row to check eccgenerate() */
+ { 0x3aeba13f95fa431f, 0x92 }, /* Use this row to check eccgenerate() */
+ { 0xc2d7424f1b3eff2b, 0xe6 }, /* Use this row to check eccgenerate() */
+ { 0x165f601d2c8e4863, 0x2b }, /* Use this row to check eccgenerate() */
+ { 0xc67cae255a241c00, 0x78 }, /* Use this row to check eccgenerate() */
+ { 0x5a269e2300263e3f, 0x07 }, /* Use this row to check eccgenerate() */
+ { 0x634a6d7f96701350, 0xe9 }, /* Use this row to check eccgenerate() */
+ { 0x34a28d23eab54536, 0xd2 }, /* Use this row to check eccgenerate() */
+ { 0xd3a5340cd130051e, 0x48 }, /* Use this row to check eccgenerate() */
+ { 0xfe236703190f9b4f, 0x7e }, /* Use this row to check eccgenerate() */
+ { 0x82a641187ef8245f, 0x20 }, /* Use this row to check eccgenerate() */
+ { 0xa0a74504541e3013, 0xc7 }, /* Use this row to check eccgenerate() */
+ { 0x5fd43b3b577d3356, 0x85 }, /* Use this row to check eccgenerate() */
+ { 0xfb9cf773fb955461, 0x06 }, /* Use this row to check eccgenerate() */
+ { 0x214766290024d376, 0x80 }, /* Use this row to check eccgenerate() */
+ { 0x2de45a569ea42c5d, 0x22 }, /* Use this row to check eccgenerate() */
+ { 0x349f707cea72f815, 0xf3 }, /* Use this row to check eccgenerate() */
+ { 0x05b1f74167cffc15, 0xe9 }, /* Use this row to check eccgenerate() */
+ { 0x945d4579f676b34b, 0x63 }, /* Use this row to check eccgenerate() */
+ { 0x519bcf4b1b10585f, 0x47 }, /* Use this row to check eccgenerate() */
+ { 0x1b36961e5adaf31e, 0x25 }, /* Use this row to check eccgenerate() */
+ { 0xf04a076fabc16d6f, 0x20 }, /* Use this row to check eccgenerate() */
+ { 0x9577b3257e80031e, 0xef }, /* Use this row to check eccgenerate() */
+ { 0x4fb1083c24ed9412, 0x97 }, /* Use this row to check eccgenerate() */
+ { 0x3dfc2f62681de831, 0x1f }, /* Use this row to check eccgenerate() */
+ { 0xe7150d114ed56f3f, 0x10 }, /* Use this row to check eccgenerate() */
+ { 0xa2f39f52bfa2717a, 0x40 }, /* Use this row to check eccgenerate() */
+ { 0x1720a55087bd5215, 0xb3 }, /* Use this row to check eccgenerate() */
+ { 0x8253a77601c8db0d, 0x45 }, /* Use this row to check eccgenerate() */
+ { 0x01ecae0412bd9c44, 0x5f }, /* Use this row to check eccgenerate() */
+ { 0xb161c921a39a0d20, 0x51 }, /* Use this row to check eccgenerate() */
+ { 0x8d0d06362ed0095b, 0x94 }, /* Use this row to check eccgenerate() */
+ { 0x969f0671e5003a1e, 0x9b }, /* Use this row to check eccgenerate() */
+ { 0xdb77ed6992befd77, 0x63 }, /* Use this row to check eccgenerate() */
+ { 0xadce55572afd4b6a, 0x3e }, /* Use this row to check eccgenerate() */
+ { 0x84d73f092c13bd35, 0x50 }, /* Use this row to check eccgenerate() */
+ { 0xd7d42a25c804ec75, 0x05 }, /* Use this row to check eccgenerate() */
+ { 0x4685ef1374224778, 0x72 }, /* Use this row to check eccgenerate() */
+ { 0x980fdc0a6d4cde4a, 0x9d }, /* Use this row to check eccgenerate() */
+ { 0xd569c67c9636f84f, 0x81 }, /* Use this row to check eccgenerate() */
+ { 0xe40b680fd60b0c6d, 0x2c }, /* Use this row to check eccgenerate() */
+ { 0x95ae7d67bc7fd30d, 0x72 }, /* Use this row to check eccgenerate() */
+ { 0x433d262386ff0762, 0xf4 }, /* Use this row to check eccgenerate() */
+ { 0x87c7e36facce2238, 0x5a }, /* Use this row to check eccgenerate() */
+ { 0xbf8bbf7cc590cd19, 0xe0 }, /* Use this row to check eccgenerate() */
+ { 0x682bdb3988b39274, 0x4f }, /* Use this row to check eccgenerate() */
+ { 0xb7839c4f70ed881e, 0x6b }, /* Use this row to check eccgenerate() */
+ { 0x55eec23cf538e16f, 0x72 }, /* Use this row to check eccgenerate() */
+ { 0x87f7de674d23a340, 0xb4 }, /* Use this row to check eccgenerate() */
+ { 0x7720ef2a3066b026, 0x7c }, /* Use this row to check eccgenerate() */
+ { 0x5d796d5c34c6343f, 0x5e }, /* Use this row to check eccgenerate() */
+ { 0xfcca2035fbf72e34, 0xc6 }, /* Use this row to check eccgenerate() */
+ { 0x6f1a762c344e9801, 0x87 }, /* Use this row to check eccgenerate() */
+ { 0xa19a764c43501049, 0x35 }, /* Use this row to check eccgenerate() */
+ { 0xd9860819072a5237, 0x6a }, /* Use this row to check eccgenerate() */
+ { 0xdd355e2477043d49, 0x2d }, /* Use this row to check eccgenerate() */
+ { 0x33841057bd927028, 0xaa }, /* Use this row to check eccgenerate() */
+ { 0x4392780a73e4db0b, 0xfa }, /* Use this row to check eccgenerate() */
+ { 0x1fb3fe4377c1367a, 0x47 }, /* Use this row to check eccgenerate() */
+ { 0x3c520414ca595c7a, 0x58 }, /* Use this row to check eccgenerate() */
+ { 0x520def6ede3ebe40, 0xac }, /* Use this row to check eccgenerate() */
+ { 0x4e2c475fa57ddf4d, 0x5c }, /* Use this row to check eccgenerate() */
+ { 0x9ab6c03d09918b3e, 0x95 }, /* Use this row to check eccgenerate() */
+ { 0x56b42e7fa31a0a1c, 0x5d }, /* Use this row to check eccgenerate() */
+ { 0xd480ba4222ae9f25, 0x87 }, /* Use this row to check eccgenerate() */
+ { 0x5674d464cdd41d2a, 0xc7 }, /* Use this row to check eccgenerate() */
+ { 0xc8cc4c5e31fa271f, 0x6e }, /* Use this row to check eccgenerate() */
+ { 0x6548c020533ff519, 0x00 }, /* Use this row to check eccgenerate() */
+ { 0x968f056337e7c20a, 0x0e }, /* Use this row to check eccgenerate() */
+ { 0x3f11154207e3366d, 0xbe }, /* Use this row to check eccgenerate() */
+ { 0x7ee773366f160e7c, 0x53 }, /* Use this row to check eccgenerate() */
+ { 0x2ca97e241c477366, 0x1c }, /* Use this row to check eccgenerate() */
+ { 0x8f2b4f72b16b840d, 0x88 }, /* Use this row to check eccgenerate() */
+ { 0x282dbb076f3bf72e, 0xd0 }, /* Use this row to check eccgenerate() */
+ { 0x39955329afde4d36, 0xc7 }, /* Use this row to check eccgenerate() */
+ { 0x8d1d0c77657fbf1b, 0x22 }, /* Use this row to check eccgenerate() */
+ { 0x0afd9e698ba24218, 0x1a }, /* Use this row to check eccgenerate() */
+ { 0x9533ce56dc495356, 0x2a }, /* Use this row to check eccgenerate() */
+ { 0x7f645d72a4b35f27, 0x80 }, /* Use this row to check eccgenerate() */
+ { 0xc661ff4cebe7fc55, 0xe2 }, /* Use this row to check eccgenerate() */
+ { 0xb9bc1a0053e51735, 0xff }, /* Use this row to check eccgenerate() */
+ { 0x84df3f541dd6d331, 0x54 }, /* Use this row to check eccgenerate() */
+ { 0x7015c94b8189675e, 0x02 }, /* Use this row to check eccgenerate() */
+ { 0xb9702a69ea270075, 0x1f }, /* Use this row to check eccgenerate() */
+ { 0xf10a376206a5ce2e, 0x6f }, /* Use this row to check eccgenerate() */
+ { 0x75bbdc2af8813f2b, 0xb1 }, /* Use this row to check eccgenerate() */
+ { 0x14c9b2116ff2aa18, 0x7a }, /* Use this row to check eccgenerate() */
+ { 0x205e2f26a1645b4f, 0x2b }, /* Use this row to check eccgenerate() */
+ { 0x10a0527ea4f40104, 0xf6 }, /* Use this row to check eccgenerate() */
+ { 0x53d34f3a498bea2d, 0x93 }, /* Use this row to check eccgenerate() */
+ { 0xae0aaa494935a627, 0xbf }, /* Use this row to check eccgenerate() */
+ { 0xd4d7e83fe0f05b31, 0x58 }, /* Use this row to check eccgenerate() */
+ { 0xbc3aaf07b8074933, 0x74 }, /* Use this row to check eccgenerate() */
+ { 0x5cbba85a690bb716, 0xbf }, /* Use this row to check eccgenerate() */
+ { 0x55f3b36c3c9f0c7a, 0x3a }, /* Use this row to check eccgenerate() */
+ { 0x8f84242f231da827, 0x50 }, /* Use this row to check eccgenerate() */
+ { 0x40f37b590eb0ce6c, 0x9c }, /* Use this row to check eccgenerate() */
+ { 0x8f39364b14646403, 0x0b }, /* Use this row to check eccgenerate() */
+ { 0xfe8b6478b0084525, 0x21 }, /* Use this row to check eccgenerate() */
+ { 0xb6ad135448aa6034, 0x1c }, /* Use this row to check eccgenerate() */
+ { 0x402ca05fef969b5a, 0x90 }, /* Use this row to check eccgenerate() */
+ { 0x5e8946732b69f07e, 0xaa }, /* Use this row to check eccgenerate() */
+ { 0xcccd4b4e55f55271, 0xe8 }, /* Use this row to check eccgenerate() */
+ { 0xf9e954757ee77519, 0xf8 }, /* Use this row to check eccgenerate() */
+ { 0xc7726047dc6d9e4c, 0x67 }, /* Use this row to check eccgenerate() */
+ { 0x25a344744cbda42f, 0x77 }, /* Use this row to check eccgenerate() */
+ { 0x2cae0061757d0a11, 0xca }, /* Use this row to check eccgenerate() */
+ { 0x2d855344f97a2d34, 0x9b }, /* Use this row to check eccgenerate() */
+ { 0x6386e44ae9e8af68, 0x6c }, /* Use this row to check eccgenerate() */
+ { 0x2588bc628a40fc1e, 0x4c }, /* Use this row to check eccgenerate() */
+ { 0xad5da446b8799837, 0x31 }, /* Use this row to check eccgenerate() */
+ { 0xc6296724b40ce111, 0xde }, /* Use this row to check eccgenerate() */
+ { 0xc8704515ed502020, 0x72 }, /* Use this row to check eccgenerate() */
+ { 0x9d59654555639d6f, 0x16 }, /* Use this row to check eccgenerate() */
+ { 0x9e0dfe23c6fca90d, 0x37 }, /* Use this row to check eccgenerate() */
+ { 0xb593456853077919, 0xee }, /* Use this row to check eccgenerate() */
+ { 0x7e706918de399e03, 0xe7 }, /* Use this row to check eccgenerate() */
+ { 0x332ff174131d8c5b, 0x34 }, /* Use this row to check eccgenerate() */
+ { 0x920402754a3eb566, 0x2f }, /* Use this row to check eccgenerate() */
+ { 0x26ac53332c19466a, 0x0c }, /* Use this row to check eccgenerate() */
+ { 0x78d6ea195977623c, 0x6f }, /* Use this row to check eccgenerate() */
+ { 0xcff46c4d4b4f9827, 0x20 }, /* Use this row to check eccgenerate() */
+ { 0x44cac55ba584eb7a, 0x5f }, /* Use this row to check eccgenerate() */
+ { 0x8e6d9b63fc79c011, 0xc8 }, /* Use this row to check eccgenerate() */
+ { 0x86babc30a750aa26, 0x20 }, /* Use this row to check eccgenerate() */
+ { 0x5fca425eb3f55746, 0x12 }, /* Use this row to check eccgenerate() */
+ { 0x6702395833186177, 0xaf }, /* Use this row to check eccgenerate() */
+ { 0x2069811725f4a902, 0x87 }, /* Use this row to check eccgenerate() */
+ { 0x7b57477230737e6d, 0xd9 }, /* Use this row to check eccgenerate() */
+ { 0xf66f287bbdc2e65c, 0xfa }, /* Use this row to check eccgenerate() */
+ { 0x10ca5f7619654516, 0x52 }, /* Use this row to check eccgenerate() */
+ { 0xf79ee319ac036e63, 0x58 }, /* Use this row to check eccgenerate() */
+ { 0xbf20fa3e8e3ac90e, 0x82 }, /* Use this row to check eccgenerate() */
+ { 0xd8787e752bced40e, 0x54 }, /* Use this row to check eccgenerate() */
+ { 0x57e71a795125fc33, 0xfe }, /* Use this row to check eccgenerate() */
+ { 0xab9c5e70fe24d228, 0xfc }, /* Use this row to check eccgenerate() */
+ { 0x49746a50d0bd0513, 0x9d }, /* Use this row to check eccgenerate() */
+ { 0x7542f10d7a91cb3d, 0xb9 }, /* Use this row to check eccgenerate() */
+ { 0x760b8c4f8e3e302c, 0x82 }, /* Use this row to check eccgenerate() */
+ { 0x358fda5203b08c71, 0x23 }, /* Use this row to check eccgenerate() */
+ { 0xb6a5e437fdc54800, 0xb6 }, /* Use this row to check eccgenerate() */
+ { 0x30dea97795591d31, 0x7c }, /* Use this row to check eccgenerate() */
+ { 0xba4dc7331da81d10, 0x11 }, /* Use this row to check eccgenerate() */
+ { 0x4d1b9c7d51472b0f, 0x37 }, /* Use this row to check eccgenerate() */
+ { 0x0e0a126c35a50e26, 0xd6 }, /* Use this row to check eccgenerate() */
+ { 0x4e0a543c448bc478, 0x0f }, /* Use this row to check eccgenerate() */
+ { 0xf08e325c1fd47162, 0x6b }, /* Use this row to check eccgenerate() */
+ { 0xad0e3b7146a93756, 0x86 }, /* Use this row to check eccgenerate() */
+ { 0x71770c65afaf2c1b, 0xae }, /* Use this row to check eccgenerate() */
+ { 0x01d5284f8687b966, 0x37 }, /* Use this row to check eccgenerate() */
+ { 0x84ac8b0fc85e275e, 0x86 }, /* Use this row to check eccgenerate() */
+ { 0x981c2d71ac71873f, 0x4e }, /* Use this row to check eccgenerate() */
+ { 0x2603537dce20f65f, 0xb5 }, /* Use this row to check eccgenerate() */
+ { 0x5c5f260c0d5f1e7f, 0x0b }, /* Use this row to check eccgenerate() */
+ { 0x100fab709c0edf4c, 0xc9 }, /* Use this row to check eccgenerate() */
+ { 0x99d4274d91ee005f, 0x83 }, /* Use this row to check eccgenerate() */
+ { 0x26481e10c6b48f28, 0x16 }, /* Use this row to check eccgenerate() */
+ { 0xe45cad38cab2d144, 0x9c }, /* Use this row to check eccgenerate() */
+ { 0x1bfafc53e195e543, 0x8e }, /* Use this row to check eccgenerate() */
+ { 0x163bf46931784936, 0xdc }, /* Use this row to check eccgenerate() */
+ { 0x75030e2f29040f40, 0x48 }, /* Use this row to check eccgenerate() */
+ { 0x48d8802265454826, 0x2a }, /* Use this row to check eccgenerate() */
+ { 0xabee7f7c6592400b, 0x2b }, /* Use this row to check eccgenerate() */
+ { 0x15426d26f6e6bb13, 0x89 }, /* Use this row to check eccgenerate() */
+ { 0x7c6e757a1c668c61, 0x6d }, /* Use this row to check eccgenerate() */
+ { 0xe4c4b33f16179675, 0x74 }, /* Use this row to check eccgenerate() */
+ { 0xc2881d35001b010a, 0xd4 }, /* Use this row to check eccgenerate() */
+ { 0xce3bf7697de1e030, 0x65 }, /* Use this row to check eccgenerate() */
+ { 0x8a40ff2fe88b7032, 0x19 }, /* Use this row to check eccgenerate() */
+ { 0x849a4f7f2a9b1d76, 0x58 }, /* Use this row to check eccgenerate() */
+ { 0xbc891e559b4faa20, 0x4c }, /* Use this row to check eccgenerate() */
+ { 0x61043a491e6f774c, 0x28 }, /* Use this row to check eccgenerate() */
+ { 0xe8214911e2d13c65, 0x9e }, /* Use this row to check eccgenerate() */
+ { 0xc36722294561e701, 0x3d }, /* Use this row to check eccgenerate() */
+ { 0x77d93038031c4665, 0x55 }, /* Use this row to check eccgenerate() */
+ { 0x2c205525daa21613, 0x85 }, /* Use this row to check eccgenerate() */
+ { 0x3fe85e39ecdc3e67, 0x20 }, /* Use this row to check eccgenerate() */
+ { 0x526f7f7275f8d547, 0xa4 }, /* Use this row to check eccgenerate() */
+ { 0x6bdf915bead6de35, 0xac }, /* Use this row to check eccgenerate() */
+ { 0x063d6b1767b1ec18, 0x78 }, /* Use this row to check eccgenerate() */
+ { 0x7dc8820ee74d0756, 0x31 }, /* Use this row to check eccgenerate() */
+ { 0xe7680860ea011f57, 0x3f }, /* Use this row to check eccgenerate() */
+ { 0x67e3ff073f51a043, 0xd6 }, /* Use this row to check eccgenerate() */
+ { 0x27dd1076b6a4ff49, 0x10 }, /* Use this row to check eccgenerate() */
+ { 0xe03f1d40f223ff37, 0xec }, /* Use this row to check eccgenerate() */
+ { 0x8d73a958ab776075, 0x6f }, /* Use this row to check eccgenerate() */
+ { 0xc9e6d7419cc93b15, 0x8f }, /* Use this row to check eccgenerate() */
+ { 0x7f9b787aee77e321, 0xb7 }, /* Use this row to check eccgenerate() */
+ { 0x34d9ca23b1082153, 0xa9 }, /* Use this row to check eccgenerate() */
+ { 0xb424673842039b23, 0xe2 }, /* Use this row to check eccgenerate() */
+ { 0x1ca6b136abb2fb5b, 0xe1 }, /* Use this row to check eccgenerate() */
+ { 0x978f3a43e144bc5d, 0x64 }, /* Use this row to check eccgenerate() */
+ { 0x563d92255b8e1070, 0x14 }, /* Use this row to check eccgenerate() */
+ { 0x4565ef25e9feb935, 0x2d }, /* Use this row to check eccgenerate() */
+ { 0x50b0a64ec11c2401, 0x3c }, /* Use this row to check eccgenerate() */
+ { 0xa86a2b574ba25a3d, 0x8b }, /* Use this row to check eccgenerate() */
+ { 0x36a47914cd78295d, 0xf1 }, /* Use this row to check eccgenerate() */
+ { 0x0ccac9208fd33337, 0xe4 }, /* Use this row to check eccgenerate() */
+ { 0x457833019d87791c, 0xc4 }, /* Use this row to check eccgenerate() */
+ { 0x8fab785433a7da16, 0x0c }, /* Use this row to check eccgenerate() */
+ { 0xdf1e3b0c26b85041, 0x94 }, /* Use this row to check eccgenerate() */
+ { 0xc2818c561c1f222d, 0x9a }, /* Use this row to check eccgenerate() */
+ { 0x0b97054fa805134e, 0xec }, /* Use this row to check eccgenerate() */
+ { 0x5a0e3421411d0551, 0x57 }, /* Use this row to check eccgenerate() */
+ { 0x8420a0743f70d072, 0xa8 }, /* Use this row to check eccgenerate() */
+ { 0xea22cc4e0e339b59, 0x15 }, /* Use this row to check eccgenerate() */
+ { 0xef775737a0c6512b, 0xe7 }, /* Use this row to check eccgenerate() */
+ { 0xfc54621b81b20612, 0x9a }, /* Use this row to check eccgenerate() */
+ { 0x6bb1c04745b5e95c, 0x1e }, /* Use this row to check eccgenerate() */
+ { 0x06d20d5e41ba5141, 0x56 }, /* Use this row to check eccgenerate() */
+ { 0x8d5cac7ebb616716, 0x43 }, /* Use this row to check eccgenerate() */
+ { 0x89da9073ae3c3935, 0xb1 }, /* Use this row to check eccgenerate() */
+ { 0x3e106d6cc3002613, 0xec }, /* Use this row to check eccgenerate() */
+ { 0x60889f2f95a45a14, 0x69 }, /* Use this row to check eccgenerate() */
+ { 0xc94b352b8388a06d, 0x53 }, /* Use this row to check eccgenerate() */
+ { 0xa940f12ef0331804, 0x7a }, /* Use this row to check eccgenerate() */
+
+};
+
+int main(void)
+{
+ int i;
+ uint8_t ret_memcpy;
+ uint8_t ret_verify;
+ uint64_t dst;
+ uint64_t *buf;
+ struct ecc64 *ret_buf;
+
+ /*
+ * Test that eccgenerate() still works, but skip the first 64 because they
+ * have intentional bitflips
+ */
+ printf("Checking eccgenerate()\n");
+ for (i = 64; i < NUM_ECC_ROWS; i++) {
+ if (eccgenerate(be64toh(ecc_data[i].data)) != ecc_data[i].ecc) {
+ ERR("ECC did not generate the correct value, expecting 0x%02x, got 0x%02x\n",
+ ecc_data[i].ecc, eccgenerate(be64toh(ecc_data[i].data)));
+ }
+ }
+
+ /* Test that the ecc code can detect and recover bitflips */
+ printf("Testing bitflip recovery\n");
+ for (i = 0; i < 64; i++) {
+ ret_memcpy = memcpy_from_ecc(&dst, &ecc_data[i], sizeof(dst));
+ if (dst != 0xffffffffffffffff || ret_memcpy) {
+ ERR("ECC code didn't correct bad bit %d in 0x%016lx\n", 63 - i, be64toh(ecc_data[i].data));
+ exit(1);
+ }
+
+ ret_verify = eccverify(be64toh(ecc_data[i].data), ecc_data[i].ecc);
+ if (ret_verify != 63 - i) {
+ ERR("ECC did not catch incorrect bit %d in row 0x%016lx 0x%02x, got 0x%02x\n",
+ i, ecc_data[i].data, ecc_data[i].ecc, ret_verify);
+ exit(1);
+ }
+ }
+
+ buf = malloc(NUM_ECC_ROWS * sizeof(*buf));
+ if (!buf) {
+ ERR("malloc #1 failed during ecc test\n");
+ exit(1);
+ }
+ printf("pass\n");
+
+ /* Test a large memcpy */
+ printf("Testing a large(ish) memcpy_from_ecc()\n");
+ ret_memcpy = memcpy_from_ecc(buf, ecc_data, NUM_ECC_ROWS * sizeof(*buf));
+ if (ret_memcpy) {
+ ERR("ECC Couldn't memcpy entire buffer\n");
+ exit(1);
+ }
+
+ for (i = 0; i < NUM_ECC_ROWS; i++) {
+ /* Large memcpy should have fixed the bitflips */
+ if (i < 64 && buf[i] != 0xffffffffffffffff) {
+ ERR("memcpy_from_ecc got it wrong for uint64_t number %d, got 0x%016lx, expecting 0xffffffffffffffff\n",
+ i, buf[i]);
+ exit(1);
+ }
+
+ /* But not changed any of the correct data */
+ if (i > 63 && buf[i] != ecc_data[i].data) {
+ ERR("memcpy_from_ecc got it wrong for uint64_t number %d, git 0x%016lx, expecting 0x%016lx\n",
+ i, buf[i], ecc_data[i].data);
+ exit(1);
+ }
+ }
+ printf("pass\n");
+
+ /* Test a memcpy to add ecc data */
+ printf("Testing a large(ish) memcpy_to_ecc()\n");
+ ret_buf = malloc(ecc_buffer_size(NUM_ECC_ROWS * sizeof(*buf)));
+ if (!buf) {
+ ERR("malloc #2 failed during ecc test\n");
+ exit(1);
+ }
+
+ ret_memcpy = memcpy_to_ecc(ret_buf, buf, NUM_ECC_ROWS * sizeof(*buf));
+ if (ret_memcpy) {
+ ERR("ECC Couldn't memcpy entire buffer\n");
+ exit(1);
+ }
+
+ for (i = 0; i < NUM_ECC_ROWS; i++) {
+ /* The data should be the same */
+ if (ret_buf[i].data != buf[i]) {
+ ERR("memcpy_to_ecc got it wrong on uint64_t %d, expecting 0x%016lx, got 0x%016lx\n",
+ i, buf[i], ret_buf[i].data);
+ exit(1);
+ }
+
+ /* Check the correctness of ecc bytes */
+ if (ret_buf[i].ecc != ecc_data[i].ecc) {
+ ERR("memcpy_to_ecc got it on the ecc for uint64_t %d, expecting 0x%02x, got 0x%02x\n",
+ i, ecc_data[i].ecc, ret_buf[i].ecc);
+ exit(1);
+ }
+ }
+ printf("ECC tests pass\n");
+
+ printf("ECC test error conditions\n");
+ if (memcpy_to_ecc(ret_buf, buf, 7) == 0) {
+ ERR("memcpy_to_ecc didn't detect bad size 7\n");
+ exit(1);
+ }
+
+ if (memcpy_to_ecc(ret_buf, buf, 15) == 0) {
+ ERR("memcpy_to_ecc didn't detect bad size 15\n");
+ exit(1);
+ }
+ if (memcpy_from_ecc(buf, ret_buf, 7) == 0) {
+ ERR("memcpy_from_ecc didn't detect bad size 7\n");
+ exit(1);
+ }
+ if (memcpy_from_ecc(buf, ret_buf, 15) == 0) {
+ ERR("memcpy_from_ecc didn't detect bad size 15\n");
+ exit(1);
+ }
+ printf("ECC error conditions pass\n");
+
+ free(buf);
+ free(ret_buf);
+
+ /* Check that unaligned address become aligned */
+ if (ecc_buffer_align(0, 5) != 0) {
+ ERR("ecc_buffer_align(0, 5) not 0 -> %ld\n", ecc_buffer_align(0, 5));
+ exit(1);
+ }
+
+ if (ecc_buffer_align(0, 8) != 0) {
+ ERR("ecc_buffer_align(0, 8) not 0 -> %ld\n", ecc_buffer_align(0, 8));
+ exit(1);
+ }
+ if (ecc_buffer_align(0, 9) != 9) {
+ ERR("ecc_buffer_align(0, 9) not 9 -> %ld\n", ecc_buffer_align(0, 9));
+ exit(1);
+ }
+ if (ecc_buffer_align(0, 15) != 9) {
+ ERR("ecc_buffer_align(0, 15) not 9 -> %ld\n", ecc_buffer_align(0, 15));
+ exit(1);
+ }
+ if (ecc_buffer_align(5, 10) != 5) {
+ ERR("ecc_buffer_align(5, 10) not 5 -> %ld\n", ecc_buffer_align(5, 10));
+ exit(1);
+ }
+ if (ecc_buffer_align(5, 18) != 14) {
+ ERR("ecc_buffer_align(5, 18) not 14 -> %ld\n", ecc_buffer_align(5, 18));
+ exit(1);
+ }
+ if (ecc_buffer_align(0, 50) != 45) {
+ ERR("ecc_buffer_align(0, 50) not 45 -> %ld\n", ecc_buffer_align(0, 50));
+ exit(1);
+ }
+ return 0;
+}
diff --git a/roms/skiboot/libflash/test/test-flash.c b/roms/skiboot/libflash/test/test-flash.c
new file mode 100644
index 000000000..3304195f8
--- /dev/null
+++ b/roms/skiboot/libflash/test/test-flash.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2013-2017 IBM Corp. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libflash-priv.h>
+
+#include "../libflash.c"
+#include "../ecc.c"
+
+#define __unused __attribute__((unused))
+
+#define ERR(fmt...) fprintf(stderr, fmt)
+
+/* Flash commands */
+#define CMD_PP 0x02
+#define CMD_READ 0x03
+#define CMD_WRDI 0x04
+#define CMD_RDSR 0x05
+#define CMD_WREN 0x06
+#define CMD_SE 0x20
+#define CMD_RDSCUR 0x2b
+#define CMD_BE32K 0x52
+#define CMD_CE 0x60
+#define CMD_RDID 0x9f
+#define CMD_EN4B 0xb7
+#define CMD_BE 0xd8
+#define CMD_RDDPB 0xe0
+#define CMD_RDSPB 0xe2
+#define CMD_EX4B 0xe9
+
+/* Flash status bits */
+#define STAT_WIP 0x01
+#define STAT_WEN 0x02
+
+static uint8_t *sim_image;
+static uint32_t sim_image_sz = 0x100000;
+static uint32_t sim_index;
+static uint32_t sim_addr;
+static uint32_t sim_er_size;
+static uint8_t sim_sr;
+static bool sim_fl_4b;
+static bool sim_ct_4b;
+
+static enum sim_state {
+ sim_state_idle,
+ sim_state_rdid,
+ sim_state_rdsr,
+ sim_state_read_addr,
+ sim_state_read_data,
+ sim_state_write_addr,
+ sim_state_write_data,
+ sim_state_erase_addr,
+ sim_state_erase_done,
+} sim_state;
+
+/*
+ * Simulated flash & controller
+ */
+static int sim_start_cmd(uint8_t cmd)
+{
+ if (sim_state != sim_state_idle) {
+ ERR("SIM: Command %02x in wrong state %d\n", cmd, sim_state);
+ return -1;
+ }
+
+ sim_index = 0;
+ sim_addr = 0;
+
+ switch(cmd) {
+ case CMD_RDID:
+ sim_state = sim_state_rdid;
+ break;
+ case CMD_RDSR:
+ sim_state = sim_state_rdsr;
+ break;
+ case CMD_EX4B:
+ sim_fl_4b = false;
+ break;
+ case CMD_EN4B:
+ sim_fl_4b = true;
+ break;
+ case CMD_WREN:
+ sim_sr |= STAT_WEN;
+ break;
+ case CMD_READ:
+ sim_state = sim_state_read_addr;
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in READ !\n");
+ break;
+ case CMD_PP:
+ sim_state = sim_state_write_addr;
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in PP !\n");
+ if (!(sim_sr & STAT_WEN))
+ ERR("SIM: PP without WEN, ignoring... \n");
+ break;
+ case CMD_SE:
+ case CMD_BE32K:
+ case CMD_BE:
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in SE/BE !\n");
+ if (!(sim_sr & STAT_WEN))
+ ERR("SIM: SE/BE without WEN, ignoring... \n");
+ sim_state = sim_state_erase_addr;
+ switch(cmd) {
+ case CMD_SE: sim_er_size = 0x1000; break;
+ case CMD_BE32K: sim_er_size = 0x8000; break;
+ case CMD_BE: sim_er_size = 0x10000; break;
+ }
+ break;
+ case CMD_CE:
+ if (!(sim_sr & STAT_WEN)) {
+ ERR("SIM: CE without WEN, ignoring... \n");
+ break;
+ }
+ memset(sim_image, 0xff, sim_image_sz);
+ sim_sr |= STAT_WIP;
+ sim_sr &= ~STAT_WEN;
+ break;
+ default:
+ ERR("SIM: Unsupported command %02x\n", cmd);
+ return -1;
+ }
+ return 0;
+}
+
+static void sim_end_cmd(void)
+{
+ /* For write and sector/block erase, set WIP & clear WEN here */
+ if (sim_state == sim_state_write_data) {
+ sim_sr |= STAT_WIP;
+ sim_sr &= ~STAT_WEN;
+ }
+ sim_state = sim_state_idle;
+}
+
+static bool sim_do_address(const uint8_t **buf, uint32_t *len)
+{
+ uint8_t asize = sim_fl_4b ? 4 : 3;
+ const uint8_t *p = *buf;
+
+ while(*len) {
+ sim_addr = (sim_addr << 8) | *(p++);
+ *buf = p;
+ *len = *len - 1;
+ sim_index++;
+ if (sim_index >= asize)
+ return true;
+ }
+ return false;
+}
+
+static int sim_wbytes(const void *buf, uint32_t len)
+{
+ const uint8_t *b = buf;
+ bool addr_complete;
+
+ again:
+ switch(sim_state) {
+ case sim_state_read_addr:
+ addr_complete = sim_do_address(&b, &len);
+ if (addr_complete) {
+ sim_state = sim_state_read_data;
+ sim_index = 0;
+ if (len)
+ goto again;
+ }
+ break;
+ case sim_state_write_addr:
+ addr_complete = sim_do_address(&b, &len);
+ if (addr_complete) {
+ sim_state = sim_state_write_data;
+ sim_index = 0;
+ if (len)
+ goto again;
+ }
+ break;
+ case sim_state_write_data:
+ if (!(sim_sr & STAT_WEN))
+ break;
+ while(len--) {
+ uint8_t c = *(b++);
+ if (sim_addr >= sim_image_sz) {
+ ERR("SIM: Write past end of flash\n");
+ return -1;
+ }
+ /* Flash write only clears bits */
+ sim_image[sim_addr] &= c;
+ sim_addr = (sim_addr & 0xffffff00) |
+ ((sim_addr + 1) & 0xff);
+ }
+ break;
+ case sim_state_erase_addr:
+ if (!(sim_sr & STAT_WEN))
+ break;
+ addr_complete = sim_do_address(&b, &len);
+ if (addr_complete) {
+ memset(sim_image + sim_addr, 0xff, sim_er_size);
+ sim_sr |= STAT_WIP;
+ sim_sr &= ~STAT_WEN;
+ sim_state = sim_state_erase_done;
+ }
+ break;
+ default:
+ ERR("SIM: Write in wrong state %d\n", sim_state);
+ return -1;
+ }
+ return 0;
+}
+
+static int sim_rbytes(void *buf, uint32_t len)
+{
+ uint8_t *b = buf;
+
+ switch(sim_state) {
+ case sim_state_rdid:
+ while(len--) {
+ switch(sim_index) {
+ case 0:
+ *(b++) = 0x55;
+ break;
+ case 1:
+ *(b++) = 0xaa;
+ break;
+ case 2:
+ *(b++) = 0x55;
+ break;
+ default:
+ ERR("SIM: RDID index %d\n", sim_index);
+ *(b++) = 0;
+ break;
+ }
+ sim_index++;
+ }
+ break;
+ case sim_state_rdsr:
+ while(len--) {
+ *(b++) = sim_sr;
+ if (sim_index > 0)
+ ERR("SIM: RDSR index %d\n", sim_index);
+ sim_index++;
+
+ /* If WIP was 1, clear it, ie, simulate write/erase
+ * completion
+ */
+ sim_sr &= ~STAT_WIP;
+ }
+ break;
+ case sim_state_read_data:
+ while(len--) {
+ if (sim_addr >= sim_image_sz) {
+ ERR("SIM: Read past end of flash\n");
+ return -1;
+ }
+ *(b++) = sim_image[sim_addr++];
+ }
+ break;
+ default:
+ ERR("SIM: Read in wrong state %d\n", sim_state);
+ return -1;
+ }
+ return 0;
+}
+
+static int sim_send_addr(uint32_t addr)
+{
+ const void *ap;
+
+ /* Layout address MSB first in memory */
+ addr = cpu_to_be32(addr);
+
+ /* Send the right amount of bytes */
+ ap = (char *)&addr;
+
+ if (sim_ct_4b)
+ return sim_wbytes(ap, 4);
+ else
+ return sim_wbytes(ap + 1, 3);
+}
+
+static int sim_cmd_rd(struct spi_flash_ctrl *ctrl __unused, uint8_t cmd,
+ bool has_addr, uint32_t addr, void *buffer,
+ uint32_t size)
+{
+ int rc;
+
+ rc = sim_start_cmd(cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = sim_send_addr(addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = sim_rbytes(buffer, size);
+ bail:
+ sim_end_cmd();
+ return rc;
+}
+
+static int sim_cmd_wr(struct spi_flash_ctrl *ctrl __unused, uint8_t cmd,
+ bool has_addr, uint32_t addr, const void *buffer,
+ uint32_t size)
+{
+ int rc;
+
+ rc = sim_start_cmd(cmd);
+ if (rc)
+ goto bail;
+ if (has_addr) {
+ rc = sim_send_addr(addr);
+ if (rc)
+ goto bail;
+ }
+ if (buffer && size)
+ rc = sim_wbytes(buffer, size);
+ bail:
+ sim_end_cmd();
+ return rc;
+}
+
+static int sim_set_4b(struct spi_flash_ctrl *ctrl __unused, bool enable)
+{
+ sim_ct_4b = enable;
+
+ return 0;
+}
+
+static int sim_read(struct spi_flash_ctrl *ctrl __unused, uint32_t pos,
+ void *buf, uint32_t len)
+{
+ if (sim_ct_4b != sim_fl_4b)
+ ERR("SIM: 4b mode mismatch in autoread !\n");
+ if ((pos + len) < pos)
+ return -1;
+ if ((pos + len) > sim_image_sz)
+ return -1;
+ memcpy(buf, sim_image + pos, len);
+ return 0;
+};
+
+struct spi_flash_ctrl sim_ctrl = {
+ .cmd_wr = sim_cmd_wr,
+ .cmd_rd = sim_cmd_rd,
+ .set_4b = sim_set_4b,
+ .read = sim_read,
+};
+
+int main(void)
+{
+ struct blocklevel_device *bl;
+ uint64_t total_size;
+ uint32_t erase_granule;
+ const char *name;
+ uint16_t *test;
+ struct ecc64 *ecc_test;
+ uint64_t *test64;
+ int i, rc;
+
+ sim_image = malloc(sim_image_sz);
+ memset(sim_image, 0xff, sim_image_sz);
+ test = malloc(0x10000 * 2);
+
+ rc = flash_init(&sim_ctrl, &bl, NULL);
+ if (rc) {
+ ERR("flash_init failed with err %d\n", rc);
+ exit(1);
+ }
+ rc = flash_get_info(bl, &name, &total_size, &erase_granule);
+ if (rc) {
+ ERR("flash_get_info failed with err %d\n", rc);
+ exit(1);
+ }
+
+ /* Make up a test pattern */
+ for (i=0; i<0x10000;i++)
+ test[i] = cpu_to_be16(i);
+
+ /* Write 64k of stuff at 0 and at 128k */
+ printf("Writing test patterns...\n");
+ flash_smart_write(bl, 0, test, 0x10000);
+ flash_smart_write(bl, 0x20000, test, 0x10000);
+
+ /* Write "Hello world" straddling the 64k boundary */
+#define HW "Hello World"
+ printf("Writing test string...\n");
+ flash_smart_write(bl, 0xfffc, HW, sizeof(HW));
+
+ /* Check result */
+ if (memcmp(sim_image + 0xfffc, HW, sizeof(HW))) {
+ ERR("Test string mismatch !\n");
+ exit(1);
+ }
+ printf("Test string pass\n");
+ if (memcmp(sim_image, test, 0xfffc)) {
+ ERR("Test pattern mismatch !\n");
+ exit(1);
+ }
+ printf("Test pattern pass\n");
+
+ printf("Test ECC interfaces\n");
+ flash_smart_write_corrected(bl, 0, test, 0x10000, 1);
+ ecc_test = (struct ecc64 *)sim_image;
+ test64 = (uint64_t *)test;
+ for (i = 0; i < 0x10000 / sizeof(*ecc_test); i++) {
+ if (test64[i] != ecc_test[i].data) {
+ ERR("flash_smart_write_corrected() pattern missmatch at %d: 0x%016lx vs 0x%016lx\n",
+ i, test64[i], ecc_test[i].data);
+ exit(1);
+ }
+ if (ecc_test[i].ecc != eccgenerate(be64toh(test64[i]))) {
+ ERR("ECCs don't match 0x%02x vs 0x%02x\n", ecc_test[i].ecc, eccgenerate(test64[i]));
+ exit(1);
+ }
+ }
+ printf("Test ECC interface pass\n");
+
+ printf("Test ECC erase\n");
+ if (flash_erase(bl, 0, 0x10000) != 0) {
+ ERR("flash_erase didn't return 0\n");
+ exit(1);
+ }
+
+ for (i = 0; i < 0x10000 / sizeof(*ecc_test); i++) {
+ uint8_t zero = 0;
+ if (ecc_test[i].data != 0xFFFFFFFFFFFFFFFF) {
+ ERR("Data not properly cleared at %d\n", i);
+ exit(1);
+ }
+ rc = flash_write(bl, i * sizeof(*ecc_test) + 8, &zero, 1, 0);
+ if (rc || ecc_test[i].ecc != 0) {
+ ERR("Cleared data not correctly ECCed: 0x%02x (0x%016lx) expecting 0 at %d\n", ecc_test[i].ecc, ecc_test[i].data, i);
+ exit(1);
+ }
+ }
+ printf("Test ECC erase pass\n");
+
+ flash_exit(bl);
+ free(test);
+
+ return 0;
+}
diff --git a/roms/skiboot/libflash/test/test-ipmi-hiomap.c b/roms/skiboot/libflash/test/test-ipmi-hiomap.c
new file mode 100644
index 000000000..6117e9dd4
--- /dev/null
+++ b/roms/skiboot/libflash/test/test-ipmi-hiomap.c
@@ -0,0 +1,3388 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2018-2019 IBM Corp. */
+
+#include <assert.h>
+#include <ccan/container_of/container_of.h>
+#include <libflash/blocklevel.h>
+#include <lock.h>
+#include <lpc.h>
+#include <hiomap.h>
+#include <ipmi.h>
+#include <opal-api.h>
+#include <platform.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../ipmi-hiomap.h"
+#include "../errors.h"
+
+/* Stub for blocklevel debug macros */
+bool libflash_debug;
+
+const struct bmc_sw_config bmc_sw_hiomap = {
+ .ipmi_oem_hiomap_cmd = IPMI_CODE(0x3a, 0x5a),
+};
+
+const struct bmc_platform _bmc_platform = {
+ .name = "generic:hiomap",
+ .sw = &bmc_sw_hiomap,
+};
+
+enum scenario_event_type {
+ scenario_sentinel = 0,
+ scenario_event_p,
+ scenario_cmd,
+ scenario_sel,
+ scenario_delay,
+};
+
+struct scenario_cmd_data {
+ uint8_t cmd;
+ uint8_t seq;
+ uint8_t args[13];
+} __attribute__((packed));
+
+struct scenario_cmd {
+ struct scenario_cmd_data req;
+ struct scenario_cmd_data resp;
+ uint8_t cc;
+ size_t resp_size;
+};
+
+struct scenario_sel {
+ uint8_t bmc_state;
+};
+
+struct scenario_event {
+ enum scenario_event_type type;
+ union {
+ const struct scenario_event *p;
+ struct scenario_cmd c;
+ struct scenario_sel s;
+ };
+};
+
+#define SCENARIO_SENTINEL { .type = scenario_sentinel }
+
+struct ipmi_sel {
+ void (*fn)(uint8_t data, void *context);
+ void *context;
+};
+
+struct ipmi_msg_ctx {
+ const struct scenario_event *scenario;
+ const struct scenario_event *cursor;
+
+ struct ipmi_sel sel;
+
+ struct ipmi_msg msg;
+};
+
+struct ipmi_msg_ctx ipmi_msg_ctx;
+
+const struct bmc_platform *bmc_platform = &_bmc_platform;
+
+static void scenario_enter(const struct scenario_event *scenario)
+{
+ ipmi_msg_ctx.scenario = scenario;
+ ipmi_msg_ctx.cursor = scenario;
+}
+
+static void scenario_advance(void)
+{
+ struct ipmi_msg_ctx *ctx = &ipmi_msg_ctx;
+
+ assert(ctx->cursor->type == scenario_delay);
+ ctx->cursor++;
+
+ /* Deliver all the undelayed, scheduled SELs */
+ while (ctx->cursor->type == scenario_sel) {
+ ctx->sel.fn(ctx->cursor->s.bmc_state, ctx->sel.context);
+ ctx->cursor++;
+ }
+}
+
+static void scenario_exit(void)
+{
+ if (ipmi_msg_ctx.cursor->type != scenario_sentinel) {
+ ptrdiff_t d = ipmi_msg_ctx.cursor - ipmi_msg_ctx.scenario;
+ printf("%s: Exiting on event %tu with event type %d \n",
+ __func__, d, ipmi_msg_ctx.cursor->type);
+ assert(false);
+ }
+}
+
+void ipmi_init_msg(struct ipmi_msg *msg, int interface __attribute__((unused)),
+ uint32_t code, void (*complete)(struct ipmi_msg *),
+ void *user_data, size_t req_size, size_t resp_size)
+{
+ msg->backend = NULL;
+ msg->cmd = IPMI_CMD(code);
+ msg->netfn = IPMI_NETFN(code) << 2;
+ msg->req_size = req_size;
+ msg->resp_size = resp_size;
+ msg->complete = complete;
+ msg->user_data = user_data;
+}
+
+struct ipmi_msg *ipmi_mkmsg(int interface __attribute__((unused)),
+ uint32_t code, void (*complete)(struct ipmi_msg *),
+ void *user_data, void *req_data, size_t req_size,
+ size_t resp_size)
+{
+ struct ipmi_msg *msg = &ipmi_msg_ctx.msg;
+
+ ipmi_init_msg(msg, 0 /* some bogus value */, code, complete, user_data,
+ req_size, resp_size);
+
+ msg->data = malloc(req_size > resp_size ? req_size : resp_size);
+ if (req_data)
+ memcpy(msg->data, req_data, req_size);
+
+ return msg;
+}
+
+void ipmi_free_msg(struct ipmi_msg *msg __attribute__((unused)))
+{
+ if (msg)
+ free(msg->data);
+}
+
+void ipmi_queue_msg_sync(struct ipmi_msg *msg)
+{
+ struct ipmi_msg_ctx *ctx = container_of(msg, struct ipmi_msg_ctx, msg);
+ const struct scenario_cmd *cmd;
+
+ if (ctx->cursor->type == scenario_cmd) {
+ cmd = &ctx->cursor->c;
+ } else if (ctx->cursor->type == scenario_event_p) {
+ assert(ctx->cursor->p->type == scenario_cmd);
+ cmd = &ctx->cursor->p->c;
+ } else {
+ printf("Got unexpected request:\n");
+ for (ssize_t i = 0; i < msg->req_size; i++)
+ printf("msg->data[%zd]: 0x%02x\n", i, msg->data[i]);
+ assert(false);
+ }
+
+ assert((msg->netfn >> 2) == 0x3a);
+ assert(msg->cmd == 0x5a);
+ assert(msg->req_size >= 2);
+
+ if (memcmp(msg->data, &cmd->req, msg->req_size)) {
+ printf("Comparing received vs expected message\n");
+ for (ssize_t i = 0; i < msg->req_size; i++) {
+ printf("msg->data[%zd]: 0x%02x, cmd->req[%zd]: 0x%02x\n",
+ i, msg->data[i], i, ((uint8_t *)(&cmd->req))[i]);
+ }
+ assert(false);
+ }
+
+ msg->cc = cmd->cc;
+ memcpy(msg->data, &cmd->resp, msg->resp_size);
+
+ if (cmd->resp_size)
+ msg->resp_size = cmd->resp_size;
+
+ msg->complete(msg);
+
+ ctx->cursor++;
+
+ /* Deliver all the scheduled SELs */
+ while (ctx->cursor->type == scenario_sel) {
+ ctx->sel.fn(ctx->cursor->s.bmc_state, ctx->sel.context);
+ ctx->cursor++;
+ }
+}
+
+int ipmi_sel_register(uint8_t oem_cmd __attribute__((unused)),
+ void (*fn)(uint8_t data, void *context),
+ void *context)
+{
+ ipmi_msg_ctx.sel.fn = fn;
+ ipmi_msg_ctx.sel.context = context;
+
+ return 0;
+}
+
+int64_t lpc_write(enum OpalLPCAddressType addr_type __attribute__((unused)),
+ uint32_t addr __attribute__((unused)),
+ uint32_t data __attribute__((unused)),
+ uint32_t sz)
+{
+ assert(sz != 0);
+ return 0;
+}
+
+int64_t lpc_read(enum OpalLPCAddressType addr_type __attribute__((unused)),
+ uint32_t addr __attribute__((unused)), uint32_t *data,
+ uint32_t sz)
+{
+ memset(data, 0xaa, sz);
+
+ return 0;
+}
+
+static bool lpc_read_success(const uint8_t *buf, size_t len)
+{
+ if (len < 64) {
+ while (len--)
+ if (*buf++ != 0xaa)
+ return false;
+ return true;
+ }
+
+ for (int i = 0; i < 64; i++)
+ if (buf[i] != 0xaa)
+ return false;
+
+ return !memcmp(buf, buf + 64, len - 64);
+}
+
+/* Commonly used messages */
+
+static const struct scenario_event hiomap_ack_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 1,
+ .args = {
+ [0] = HIOMAP_E_ACK_MASK,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 1,
+ },
+ },
+};
+
+static const struct scenario_event hiomap_get_info_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 2,
+ .args = {
+ [0] = HIOMAP_V2,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 2,
+ .args = {
+ [0] = HIOMAP_V2,
+ [1] = 12,
+ [2] = 8, [3] = 0,
+ },
+ },
+ },
+};
+
+static const struct scenario_event hiomap_get_flash_info_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 3,
+ .args = {
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 3,
+ .args = {
+ [0] = 0x00, [1] = 0x20,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ },
+};
+
+static const struct scenario_event
+hiomap_create_read_window_qs0l1_rs0l1_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0xff, [1] = 0x0f,
+ [2] = 0x01, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+};
+
+static const struct scenario_event
+hiomap_create_read_window_qs0l2_rs0l1_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x02, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0xff, [1] = 0x0f,
+ [2] = 0x01, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+};
+
+static const struct scenario_event
+hiomap_create_write_window_qs0l1_rs0l1_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0xff, [1] = 0x0f,
+ [2] = 0x01, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+};
+
+static const struct scenario_event hiomap_mark_dirty_qs0l1_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 5,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 5,
+ },
+ },
+};
+
+static const struct scenario_event
+hiomap_create_write_window_qs0l2_rs0l1_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x02, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0xff, [1] = 0x0f,
+ [2] = 0x01, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+};
+
+static const struct scenario_event hiomap_flush_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 6,
+ },
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 6,
+ },
+ },
+};
+
+static const struct scenario_event
+hiomap_create_write_window_qs1l1_rs1l1_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 7,
+ .args = {
+ [0] = 0x01, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 7,
+ .args = {
+ [0] = 0xfe, [1] = 0x0f,
+ [2] = 0x01, [3] = 0x00,
+ [4] = 0x01, [5] = 0x00,
+ },
+ },
+ },
+};
+
+static const struct scenario_event hiomap_erase_qs0l1_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 5,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 5,
+ },
+ },
+};
+
+static const struct scenario_event hiomap_reset_call_seq_4 = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 4,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 4,
+ },
+ },
+};
+
+static const struct scenario_event hiomap_reset_call_seq_5 = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 5,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 5,
+ },
+ },
+};
+
+static const struct scenario_event hiomap_reset_call_seq_6 = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 6,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 6,
+ },
+ },
+};
+
+static const struct scenario_event hiomap_reset_call_seq_7 = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 7,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 7,
+ },
+ },
+};
+
+static const struct scenario_event hiomap_reset_call_seq_9 = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 9,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 9,
+ },
+ },
+};
+
+static const struct scenario_event hiomap_reset_call_seq_a = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 0xa,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 0xa,
+ },
+ },
+};
+
+static const struct scenario_event scenario_hiomap_init[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_4, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_init(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_init);
+ assert(!ipmi_hiomap_init(&bl));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event scenario_hiomap_event_daemon_ready[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_DAEMON_READY } },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_4, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_event_daemon_ready(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+
+ scenario_enter(scenario_hiomap_event_daemon_ready);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ assert(ctx->bmc_state == HIOMAP_E_DAEMON_READY);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event scenario_hiomap_event_daemon_stopped[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_DAEMON_READY } },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_PROTOCOL_RESET } },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_4, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_event_daemon_stopped(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+
+ scenario_enter(scenario_hiomap_event_daemon_stopped);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ assert(ctx->bmc_state == HIOMAP_E_PROTOCOL_RESET);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event scenario_hiomap_event_daemon_restarted[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_DAEMON_READY } },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_PROTOCOL_RESET } },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_DAEMON_READY } },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_4, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_event_daemon_restarted(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+
+ scenario_enter(scenario_hiomap_event_daemon_restarted);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ assert(ctx->bmc_state == (HIOMAP_E_DAEMON_READY | HIOMAP_E_PROTOCOL_RESET));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_event_daemon_lost_flash_control[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_DAEMON_READY } },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = (HIOMAP_E_DAEMON_READY
+ | HIOMAP_E_FLASH_LOST),
+ }
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_event_daemon_lost_flash_control(void)
+{
+ struct blocklevel_device *bl;
+ size_t len = 2 * (1 << 12);
+ void *buf;
+
+ buf = malloc(len);
+ assert(buf);
+
+ scenario_enter(scenario_hiomap_event_daemon_lost_flash_control);
+ assert(!ipmi_hiomap_init(&bl));
+ assert(bl->read(bl, 0, buf, len) == FLASH_ERR_AGAIN);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+
+ free(buf);
+}
+
+static const struct scenario_event
+scenario_hiomap_event_daemon_regained_flash_control_dirty[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_DAEMON_READY } },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x02, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0xfe, [1] = 0x0f,
+ [2] = 0x02, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_delay
+ },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = (HIOMAP_E_DAEMON_READY
+ | HIOMAP_E_FLASH_LOST),
+ }
+ },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = (HIOMAP_E_DAEMON_READY
+ | HIOMAP_E_WINDOW_RESET),
+ }
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 5,
+ .args = { [0] = HIOMAP_E_WINDOW_RESET },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 5,
+ }
+ }
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 6,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x02, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 6,
+ .args = {
+ [0] = 0xfe, [1] = 0x0f,
+ [2] = 0x02, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_7, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_event_daemon_regained_flash_control_dirty(void)
+{
+ struct blocklevel_device *bl;
+ size_t len = 2 * (1 << 12);
+ void *buf;
+
+ buf = malloc(len);
+ assert(buf);
+
+ scenario_enter(scenario_hiomap_event_daemon_regained_flash_control_dirty);
+ assert(!ipmi_hiomap_init(&bl));
+ assert(!bl->read(bl, 0, buf, len));
+ scenario_advance();
+ assert(!bl->read(bl, 0, buf, len));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+
+ free(buf);
+}
+
+static const struct scenario_event scenario_hiomap_protocol_reset_recovery[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_DAEMON_READY } },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x02, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0xfe, [1] = 0x0f,
+ [2] = 0x02, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_delay
+ },
+ {
+ .type = scenario_sel,
+ .s = { .bmc_state = HIOMAP_E_PROTOCOL_RESET, }
+ },
+ {
+ .type = scenario_sel,
+ .s = { .bmc_state = HIOMAP_E_DAEMON_READY, }
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 5,
+ .args = { [0] = HIOMAP_E_PROTOCOL_RESET },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 5,
+ }
+ }
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 6,
+ .args = {
+ [0] = HIOMAP_V2,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 6,
+ .args = {
+ [0] = HIOMAP_V2,
+ [1] = 12,
+ [2] = 8, [3] = 0,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 7,
+ .args = {
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 7,
+ .args = {
+ [0] = 0x00, [1] = 0x20,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 8,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x02, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 8,
+ .args = {
+ [0] = 0xfe, [1] = 0x0f,
+ [2] = 0x02, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_9, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_reset_recovery(void)
+{
+ struct blocklevel_device *bl;
+ size_t len = 2 * (1 << 12);
+ void *buf;
+
+ buf = malloc(len);
+ assert(buf);
+
+ scenario_enter(scenario_hiomap_protocol_reset_recovery);
+ assert(!ipmi_hiomap_init(&bl));
+ assert(!bl->read(bl, 0, buf, len));
+ scenario_advance();
+ assert(!bl->read(bl, 0, buf, len));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+
+ free(buf);
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_read_one_block[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_read_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_read_one_block(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_read_one_block);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->read(bl, 0, buf, len));
+ assert(lpc_read_success(buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static void test_hiomap_protocol_read_one_byte(void)
+{
+ struct blocklevel_device *bl;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_read_one_block);
+ assert(!ipmi_hiomap_init(&bl));
+ len = 1;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->read(bl, 0, buf, len));
+ assert(lpc_read_success(buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_read_two_blocks[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_read_window_qs0l2_rs0l1_call,
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 5,
+ .args = {
+ [0] = 0x01, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 5,
+ .args = {
+ [0] = 0xfe, [1] = 0x0f,
+ [2] = 0x01, [3] = 0x00,
+ [4] = 0x01, [5] = 0x00,
+ },
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_read_two_blocks(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_read_two_blocks);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 2 * (1 << ctx->block_size_shift);
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->read(bl, 0, buf, len));
+ assert(lpc_read_success(buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static void test_hiomap_protocol_read_1block_1byte(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_read_two_blocks);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = (1 << ctx->block_size_shift) + 1;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->read(bl, 0, buf, len));
+ assert(lpc_read_success(buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_read_one_block_twice[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_read_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_read_one_block_twice(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_read_one_block_twice);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->read(bl, 0, buf, len));
+ assert(!bl->read(bl, 0, buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_event_before_action[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = HIOMAP_E_DAEMON_READY |
+ HIOMAP_E_FLASH_LOST,
+ }
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_event_before_read(void)
+{
+ struct blocklevel_device *bl;
+ char buf;
+ int rc;
+
+ scenario_enter(scenario_hiomap_protocol_event_before_action);
+ assert(!ipmi_hiomap_init(&bl));
+ rc = bl->read(bl, 0, &buf, sizeof(buf));
+ assert(rc == FLASH_ERR_AGAIN);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_event_during_read[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_read_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = HIOMAP_E_DAEMON_READY |
+ HIOMAP_E_FLASH_LOST,
+ }
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_event_during_read(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ uint8_t *buf;
+ size_t len;
+ int rc;
+
+ scenario_enter(scenario_hiomap_protocol_event_during_read);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ rc = bl->read(bl, 0, buf, len);
+ assert(rc == FLASH_ERR_AGAIN);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_write_one_block[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_mark_dirty_qs0l1_call, },
+ { .type = scenario_event_p, .p = &hiomap_flush_call, },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_7, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_write_one_block(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_write_one_block);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->write(bl, 0, buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static void test_hiomap_protocol_write_one_byte(void)
+{
+ struct blocklevel_device *bl;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_write_one_block);
+ assert(!ipmi_hiomap_init(&bl));
+ len = 1;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->write(bl, 0, buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_write_two_blocks[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l2_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_mark_dirty_qs0l1_call, },
+ { .type = scenario_event_p, .p = &hiomap_flush_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs1l1_rs1l1_call,
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 8,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 8,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 9,
+ },
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 9,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_a, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_write_two_blocks(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_write_two_blocks);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 2 * (1 << ctx->block_size_shift);
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->write(bl, 0, buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static void test_hiomap_protocol_write_1block_1byte(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_write_two_blocks);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = (1 << ctx->block_size_shift) + 1;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->write(bl, 0, buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_write_one_block_twice[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_mark_dirty_qs0l1_call, },
+ { .type = scenario_event_p, .p = &hiomap_flush_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 7,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 7,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 8,
+ },
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 8,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_9, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_write_one_block_twice(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ uint8_t *buf;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_write_one_block_twice);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(!bl->write(bl, 0, buf, len));
+ assert(!bl->write(bl, 0, buf, len));
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static void test_hiomap_protocol_event_before_write(void)
+{
+ struct blocklevel_device *bl;
+ char buf;
+ int rc;
+
+ scenario_enter(scenario_hiomap_protocol_event_before_action);
+ assert(!ipmi_hiomap_init(&bl));
+ rc = bl->write(bl, 0, &buf, sizeof(buf));
+ assert(rc == FLASH_ERR_AGAIN);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_event_during_write[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = HIOMAP_E_DAEMON_READY |
+ HIOMAP_E_FLASH_LOST,
+ }
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_event_during_write(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ char *buf;
+ int rc;
+
+ scenario_enter(scenario_hiomap_protocol_event_during_write);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ rc = bl->write(bl, 0, buf, len);
+ free(buf);
+ assert(rc == FLASH_ERR_AGAIN);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_erase_one_block[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_erase_qs0l1_call,
+ },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_flush_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_7, },
+ SCENARIO_SENTINEL,
+};
+
+static const struct scenario_event
+scenario_hiomap_protocol_erase_two_blocks[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l2_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_erase_qs0l1_call, },
+ { .type = scenario_event_p, .p = &hiomap_flush_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs1l1_rs1l1_call,
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 8,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 8,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 9,
+ },
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 9,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_a, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_erase_two_blocks(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_erase_two_blocks);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 2 * (1 << ctx->block_size_shift);
+ assert(!bl->erase(bl, 0, len));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_erase_one_block_twice[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_erase_qs0l1_call, },
+ { .type = scenario_event_p, .p = &hiomap_flush_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 7,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 7,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 8,
+ },
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 8,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_9, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_erase_one_block_twice(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_erase_one_block_twice);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ assert(!bl->erase(bl, 0, len));
+ assert(!bl->erase(bl, 0, len));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static void test_hiomap_protocol_erase_one_block(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_erase_one_block);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ assert(!bl->erase(bl, 0, len));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static void test_hiomap_protocol_event_before_erase(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ int rc;
+
+ scenario_enter(scenario_hiomap_protocol_event_before_action);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ rc = bl->erase(bl, 0, len);
+ assert(rc == FLASH_ERR_AGAIN);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_event_during_erase[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = HIOMAP_E_DAEMON_READY |
+ HIOMAP_E_FLASH_LOST,
+ }
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_event_during_erase(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ int rc;
+
+ scenario_enter(scenario_hiomap_protocol_event_during_erase);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ rc = bl->erase(bl, 0, len);
+ assert(rc == FLASH_ERR_AGAIN);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event scenario_hiomap_protocol_bad_sequence[] = {
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 1,
+ .args = {
+ [0] = HIOMAP_E_ACK_MASK,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 0,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_bad_sequence(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_protocol_bad_sequence);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event scenario_hiomap_protocol_action_error[] = {
+ {
+ .type = scenario_cmd,
+ .c = {
+ /* Ack is legitimate, but we'll pretend it's invalid */
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 1,
+ .args = { [0] = 0x3 },
+ },
+ .cc = IPMI_INVALID_COMMAND_ERR,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 1,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_action_error(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_protocol_action_error);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_get_flash_info[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 4,
+ .args = {
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x20,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_get_flash_info(void)
+{
+ struct blocklevel_device *bl;
+ const char *name;
+ uint32_t granule;
+ uint64_t size;
+
+ scenario_enter(scenario_hiomap_protocol_get_flash_info);
+ assert(!ipmi_hiomap_init(&bl));
+ assert(!bl->get_info(bl, &name, &size, &granule));
+ assert(!name);
+ assert(size == (32 * 1024 * 1024));
+ assert(granule == (4 * 1024));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_persistent_error[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ { .type = scenario_sel, .s = { .bmc_state = HIOMAP_E_PROTOCOL_RESET } },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_persistent_error(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ char buf;
+ int rc;
+
+ scenario_enter(scenario_hiomap_protocol_persistent_error);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ assert(ctx->bmc_state == HIOMAP_E_PROTOCOL_RESET);
+ rc = bl->read(bl, 0, &buf, sizeof(buf));
+ assert(rc == FLASH_ERR_DEVICE_GONE);
+ rc = bl->read(bl, 0, &buf, sizeof(buf));
+ assert(rc == FLASH_ERR_DEVICE_GONE);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_get_info_error[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 2,
+ .args = {
+ [0] = HIOMAP_V2,
+ },
+ },
+ .cc = IPMI_INVALID_COMMAND_ERR,
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_get_info_error(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_get_info_error);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_get_flash_info_error[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 3,
+ .args = {
+ [0] = HIOMAP_V2,
+ },
+ },
+ .cc = IPMI_INVALID_COMMAND_ERR,
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_get_flash_info_error(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_get_flash_info_error);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_create_read_window_error[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_INVALID_COMMAND_ERR,
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_create_read_window_error(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_create_read_window_error);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->read(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_create_write_window_error[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_INVALID_COMMAND_ERR,
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_create_write_window_error(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_create_write_window_error);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->write(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event scenario_hiomap_mark_dirty_error[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 5,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_INVALID_COMMAND_ERR,
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_mark_dirty_error(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_mark_dirty_error);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->write(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event scenario_hiomap_flush_error[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_mark_dirty_qs0l1_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 6,
+ },
+ .cc = IPMI_INVALID_COMMAND_ERR,
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_7, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_flush_error(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_flush_error);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->write(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static void test_hiomap_ack_error(void)
+{
+ /* Same thing at the moment */
+ test_hiomap_protocol_action_error();
+}
+
+static const struct scenario_event scenario_hiomap_erase_error[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 5,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_INVALID_COMMAND_ERR,
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_erase_error(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_erase_error);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ assert(bl->erase(bl, 0, len) > 0);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event scenario_hiomap_ack_malformed_small[] = {
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 1,
+ .args = { [0] = 0x3 },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 1
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_ack_malformed_small(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_ack_malformed_small);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event scenario_hiomap_ack_malformed_large[] = {
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 1,
+ .args = { [0] = 0x3 },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 3,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 1,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_ack_malformed_large(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_ack_malformed_large);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_get_info_malformed_small[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 2,
+ .args = { [0] = 0x2 },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 7,
+ .resp = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 2,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_get_info_malformed_small(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_get_info_malformed_small);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_get_info_malformed_large[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 2,
+ .args = { [0] = 0x2 },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 9,
+ .resp = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 2,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_get_info_malformed_large(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_get_info_malformed_large);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_get_flash_info_malformed_small[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 3,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 5,
+ .resp = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 3,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_get_flash_info_malformed_small(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_get_flash_info_malformed_small);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_get_flash_info_malformed_large[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 3,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 7,
+ .resp = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 3,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_get_flash_info_malformed_large(void)
+{
+ struct blocklevel_device *bl;
+
+ scenario_enter(scenario_hiomap_get_flash_info_malformed_large);
+ assert(ipmi_hiomap_init(&bl) > 0);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_create_read_window_malformed_small[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 7,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_create_read_window_malformed_small(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_create_read_window_malformed_small);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->read(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+
+}
+
+static const struct scenario_event
+scenario_hiomap_create_read_window_malformed_large[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 9,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_READ_WINDOW,
+ .seq = 4,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_create_read_window_malformed_large(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_create_read_window_malformed_large);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->read(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_create_write_window_malformed_small[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 7,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 4,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_create_write_window_malformed_small(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_create_write_window_malformed_small);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->write(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+
+}
+
+static const struct scenario_event
+scenario_hiomap_create_write_window_malformed_large[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 4,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp_size = 9,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 4,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_5, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_create_write_window_malformed_large(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_create_write_window_malformed_large);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->write(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_mark_dirty_malformed_small[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 5,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp_size = 1,
+ .resp = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 5,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_mark_dirty_malformed_small(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_mark_dirty_malformed_small);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->write(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+
+}
+
+static const struct scenario_event
+scenario_hiomap_mark_dirty_malformed_large[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 5,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp_size = 3,
+ .resp = {
+ .cmd = HIOMAP_C_MARK_DIRTY,
+ .seq = 5,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_mark_dirty_malformed_large(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_mark_dirty_malformed_large);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->write(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_flush_malformed_small[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_mark_dirty_qs0l1_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 6,
+ },
+ .resp_size = 1,
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 6,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_7, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_flush_malformed_small(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_flush_malformed_small);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->write(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+
+}
+
+static const struct scenario_event
+scenario_hiomap_flush_malformed_large[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_mark_dirty_qs0l1_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 6,
+ },
+ .resp_size = 3,
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 6,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_7, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_flush_malformed_large(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+ void *buf;
+
+ scenario_enter(scenario_hiomap_flush_malformed_large);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ buf = calloc(1, len);
+ assert(buf);
+ assert(bl->write(bl, 0, buf, len) > 0);
+ free(buf);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_erase_malformed_small[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 5,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp_size = 1,
+ .resp = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 5,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_erase_malformed_small(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_erase_malformed_small);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ assert(bl->erase(bl, 0, len) > 0);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_erase_malformed_large[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 5,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp_size = 3,
+ .resp = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 5,
+ },
+ },
+ },
+ { .type = scenario_event_p, .p = &hiomap_reset_call_seq_6, },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_erase_malformed_large(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_erase_malformed_large);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ assert(bl->erase(bl, 0, len) > 0);
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+/* Common recovery calls */
+
+static const struct scenario_event hiomap_recovery_ack_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 7,
+ .args = {
+ [0] = HIOMAP_E_PROTOCOL_RESET,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 7,
+ },
+ },
+};
+
+static const struct scenario_event hiomap_recovery_get_info_call = {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 8,
+ .args = {
+ [0] = HIOMAP_V2,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 8,
+ .args = {
+ [0] = HIOMAP_V2,
+ [1] = 12,
+ [2] = 8, [3] = 0,
+ },
+ },
+ },
+};
+
+static const struct scenario_event
+scenario_hiomap_protocol_recovery_failure_ack[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_erase_qs0l1_call, },
+ { .type = scenario_event_p, .p = &hiomap_flush_call, },
+ { .type = scenario_delay },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = HIOMAP_E_DAEMON_READY |
+ HIOMAP_E_PROTOCOL_RESET
+ }
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 7,
+ .args = {
+ [0] = HIOMAP_E_PROTOCOL_RESET,
+ },
+ },
+ .cc = IPMI_ERR_UNSPECIFIED,
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 8,
+ .args = {
+ [0] = HIOMAP_E_PROTOCOL_RESET,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 8,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 9,
+ .args = {
+ [0] = HIOMAP_V2,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 9,
+ .args = {
+ [0] = HIOMAP_V2,
+ [1] = 12,
+ [2] = 8, [3] = 0,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 10,
+ .args = {
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 10,
+ .args = {
+ [0] = 0x00, [1] = 0x20,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 11,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 11,
+ .args = {
+ [0] = 0xff, [1] = 0x0f,
+ [2] = 0x01, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 12,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 12,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 13,
+ },
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 13,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 14,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 14,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_recovery_failure_ack(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_recovery_failure_ack);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ /*
+ * We're erasing the same block 3 times - it's irrelevant, we're just
+ * trying to manipulate window state
+ */
+ assert(!bl->erase(bl, 0, len));
+ scenario_advance();
+ assert(bl->erase(bl, 0, len) > 0);
+ assert(!bl->erase(bl, 0, len));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_recovery_failure_get_info[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_erase_qs0l1_call, },
+ { .type = scenario_event_p, .p = &hiomap_flush_call, },
+ { .type = scenario_delay },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = HIOMAP_E_DAEMON_READY |
+ HIOMAP_E_PROTOCOL_RESET
+ }
+ },
+ { .type = scenario_event_p, .p = &hiomap_recovery_ack_call, },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 8,
+ .args = {
+ [0] = HIOMAP_V2,
+ },
+ },
+ .cc = IPMI_ERR_UNSPECIFIED,
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 9,
+ .args = {
+ [0] = HIOMAP_E_PROTOCOL_RESET,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 9,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 10,
+ .args = {
+ [0] = HIOMAP_V2,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 10,
+ .args = {
+ [0] = HIOMAP_V2,
+ [1] = 12,
+ [2] = 8, [3] = 0,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 11,
+ .args = {
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 11,
+ .args = {
+ [0] = 0x00, [1] = 0x20,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 12,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 12,
+ .args = {
+ [0] = 0xff, [1] = 0x0f,
+ [2] = 0x01, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 13,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 13,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 14,
+ },
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 14,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 15,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 15,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_recovery_failure_get_info(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_recovery_failure_get_info);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ /*
+ * We're erasing the same block 3 times - it's irrelevant, we're just
+ * trying to manipulate window state
+ */
+ assert(!bl->erase(bl, 0, len));
+ scenario_advance();
+ assert(bl->erase(bl, 0, len) > 0);
+ assert(!bl->erase(bl, 0, len));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+static const struct scenario_event
+scenario_hiomap_protocol_recovery_failure_get_flash_info[] = {
+ { .type = scenario_event_p, .p = &hiomap_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_info_call, },
+ { .type = scenario_event_p, .p = &hiomap_get_flash_info_call, },
+ {
+ .type = scenario_event_p,
+ .p = &hiomap_create_write_window_qs0l1_rs0l1_call,
+ },
+ { .type = scenario_event_p, .p = &hiomap_erase_qs0l1_call, },
+ { .type = scenario_event_p, .p = &hiomap_flush_call, },
+ { .type = scenario_delay },
+ {
+ .type = scenario_sel,
+ .s = {
+ .bmc_state = HIOMAP_E_DAEMON_READY |
+ HIOMAP_E_PROTOCOL_RESET
+ }
+ },
+ { .type = scenario_event_p, .p = &hiomap_recovery_ack_call, },
+ { .type = scenario_event_p, .p = &hiomap_recovery_get_info_call},
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 9,
+ },
+ .cc = IPMI_ERR_UNSPECIFIED,
+ },
+
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 10,
+ .args = {
+ [0] = HIOMAP_E_PROTOCOL_RESET,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_ACK,
+ .seq = 10,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 11,
+ .args = {
+ [0] = HIOMAP_V2,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_INFO,
+ .seq = 11,
+ .args = {
+ [0] = HIOMAP_V2,
+ [1] = 12,
+ [2] = 8, [3] = 0,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 12,
+ .args = {
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_GET_FLASH_INFO,
+ .seq = 12,
+ .args = {
+ [0] = 0x00, [1] = 0x20,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 13,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_CREATE_WRITE_WINDOW,
+ .seq = 13,
+ .args = {
+ [0] = 0xff, [1] = 0x0f,
+ [2] = 0x01, [3] = 0x00,
+ [4] = 0x00, [5] = 0x00,
+ },
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 14,
+ .args = {
+ [0] = 0x00, [1] = 0x00,
+ [2] = 0x01, [3] = 0x00,
+ },
+ },
+ .resp = {
+ .cmd = HIOMAP_C_ERASE,
+ .seq = 14,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 15,
+ },
+ .resp = {
+ .cmd = HIOMAP_C_FLUSH,
+ .seq = 15,
+ },
+ },
+ },
+ {
+ .type = scenario_cmd,
+ .c = {
+ .req = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 16,
+ },
+ .cc = IPMI_CC_NO_ERROR,
+ .resp = {
+ .cmd = HIOMAP_C_RESET,
+ .seq = 16,
+ },
+ },
+ },
+ SCENARIO_SENTINEL,
+};
+
+static void test_hiomap_protocol_recovery_failure_get_flash_info(void)
+{
+ struct blocklevel_device *bl;
+ struct ipmi_hiomap *ctx;
+ size_t len;
+
+ scenario_enter(scenario_hiomap_protocol_recovery_failure_get_flash_info);
+ assert(!ipmi_hiomap_init(&bl));
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ /*
+ * We're erasing the same block 3 times - it's irrelevant, we're just
+ * trying to manipulate window state
+ */
+ assert(!bl->erase(bl, 0, len));
+ scenario_advance();
+ ctx = container_of(bl, struct ipmi_hiomap, bl);
+ len = 1 << ctx->block_size_shift;
+ assert(bl->erase(bl, 0, len) > 0);
+ assert(!bl->erase(bl, 0, len));
+ ipmi_hiomap_exit(bl);
+ scenario_exit();
+}
+
+struct test_case {
+ const char *name;
+ void (*fn)(void);
+};
+
+#define TEST_CASE(x) { #x, x }
+
+struct test_case test_cases[] = {
+ TEST_CASE(test_hiomap_init),
+ TEST_CASE(test_hiomap_event_daemon_ready),
+ TEST_CASE(test_hiomap_event_daemon_stopped),
+ TEST_CASE(test_hiomap_event_daemon_restarted),
+ TEST_CASE(test_hiomap_event_daemon_lost_flash_control),
+ TEST_CASE(test_hiomap_event_daemon_regained_flash_control_dirty),
+ TEST_CASE(test_hiomap_protocol_reset_recovery),
+ TEST_CASE(test_hiomap_protocol_read_one_block),
+ TEST_CASE(test_hiomap_protocol_read_one_byte),
+ TEST_CASE(test_hiomap_protocol_read_two_blocks),
+ TEST_CASE(test_hiomap_protocol_read_1block_1byte),
+ TEST_CASE(test_hiomap_protocol_read_one_block_twice),
+ TEST_CASE(test_hiomap_protocol_event_before_read),
+ TEST_CASE(test_hiomap_protocol_event_during_read),
+ TEST_CASE(test_hiomap_protocol_write_one_block),
+ TEST_CASE(test_hiomap_protocol_write_one_byte),
+ TEST_CASE(test_hiomap_protocol_write_two_blocks),
+ TEST_CASE(test_hiomap_protocol_write_1block_1byte),
+ TEST_CASE(test_hiomap_protocol_write_one_block_twice),
+ TEST_CASE(test_hiomap_protocol_event_before_write),
+ TEST_CASE(test_hiomap_protocol_event_during_write),
+ TEST_CASE(test_hiomap_protocol_erase_one_block),
+ TEST_CASE(test_hiomap_protocol_erase_two_blocks),
+ TEST_CASE(test_hiomap_protocol_erase_one_block_twice),
+ TEST_CASE(test_hiomap_protocol_event_before_erase),
+ TEST_CASE(test_hiomap_protocol_event_during_erase),
+ TEST_CASE(test_hiomap_protocol_bad_sequence),
+ TEST_CASE(test_hiomap_protocol_action_error),
+ TEST_CASE(test_hiomap_protocol_persistent_error),
+ TEST_CASE(test_hiomap_protocol_get_flash_info),
+ TEST_CASE(test_hiomap_get_info_error),
+ TEST_CASE(test_hiomap_get_flash_info_error),
+ TEST_CASE(test_hiomap_create_read_window_error),
+ TEST_CASE(test_hiomap_create_write_window_error),
+ TEST_CASE(test_hiomap_mark_dirty_error),
+ TEST_CASE(test_hiomap_flush_error),
+ TEST_CASE(test_hiomap_ack_error),
+ TEST_CASE(test_hiomap_erase_error),
+ TEST_CASE(test_hiomap_ack_malformed_small),
+ TEST_CASE(test_hiomap_ack_malformed_large),
+ TEST_CASE(test_hiomap_get_info_malformed_small),
+ TEST_CASE(test_hiomap_get_info_malformed_large),
+ TEST_CASE(test_hiomap_get_flash_info_malformed_small),
+ TEST_CASE(test_hiomap_get_flash_info_malformed_large),
+ TEST_CASE(test_hiomap_create_read_window_malformed_small),
+ TEST_CASE(test_hiomap_create_read_window_malformed_large),
+ TEST_CASE(test_hiomap_create_write_window_malformed_small),
+ TEST_CASE(test_hiomap_create_write_window_malformed_large),
+ TEST_CASE(test_hiomap_mark_dirty_malformed_small),
+ TEST_CASE(test_hiomap_mark_dirty_malformed_large),
+ TEST_CASE(test_hiomap_flush_malformed_small),
+ TEST_CASE(test_hiomap_flush_malformed_large),
+ TEST_CASE(test_hiomap_erase_malformed_small),
+ TEST_CASE(test_hiomap_erase_malformed_large),
+ TEST_CASE(test_hiomap_protocol_recovery_failure_ack),
+ TEST_CASE(test_hiomap_protocol_recovery_failure_get_info),
+ TEST_CASE(test_hiomap_protocol_recovery_failure_get_flash_info),
+ { NULL, NULL },
+};
+
+int main(void)
+{
+ struct test_case *tc = &test_cases[0];
+
+ do {
+ printf("%s\n", tc->name);
+ tc->fn();
+ printf("\n");
+ } while ((++tc)->fn);
+
+ return 0;
+}
diff --git a/roms/skiboot/libflash/test/test-mbox.c b/roms/skiboot/libflash/test/test-mbox.c
new file mode 100644
index 000000000..260a3c7d4
--- /dev/null
+++ b/roms/skiboot/libflash/test/test-mbox.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2017-2018 IBM Corp. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <libflash/libflash.h>
+#include <libflash/libflash-priv.h>
+
+#include "stubs.h"
+#include "mbox-server.h"
+
+#define zalloc(n) calloc(1, n)
+#define __unused __attribute__((unused))
+
+#undef pr_fmt
+
+void mbox_init(void)
+{
+}
+
+#include "../libflash.c"
+#include "../mbox-flash.c"
+#include "../ecc.c"
+#include "../blocklevel.c"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "MBOX-PROXY: " fmt
+
+/* client interface */
+
+#include "../../include/lpc-mbox.h"
+
+#define ERR(...) FL_DBG(__VA_ARGS__)
+
+static int run_flash_test(struct blocklevel_device *bl)
+{
+ struct mbox_flash_data *mbox_flash;
+ char hello[] = "Hello World";
+ uint32_t erase_granule;
+ uint64_t total_size;
+ const char *name;
+ uint16_t *test;
+ char *tmp;
+ int i, rc;
+
+ mbox_flash = container_of(bl, struct mbox_flash_data, bl);
+
+ /*
+ * Do something first so that if it has been reset it does that
+ * before we check versions
+ */
+ rc = blocklevel_get_info(bl, &name, &total_size, &erase_granule);
+ if (rc) {
+ ERR("blocklevel_get_info() failed with err %d\n", rc);
+ return 1;
+ }
+ if (total_size != mbox_server_total_size()) {
+ ERR("Total flash size is incorrect: 0x%08lx vs 0x%08x\n",
+ total_size, mbox_server_total_size());
+ return 1;
+ }
+ if (erase_granule != mbox_server_erase_granule()) {
+ ERR("Erase granule is incorrect 0x%08x vs 0x%08x\n",
+ erase_granule, mbox_server_erase_granule());
+ return 1;
+ }
+
+
+ /* Sanity check that mbox_flash has inited correctly */
+ if (mbox_flash->version != mbox_server_version()) {
+ ERR("MBOX Flash didn't agree with the server version\n");
+ return 1;
+ }
+ if (mbox_flash->version == 1 && mbox_flash->shift != 12) {
+ ERR("MBOX Flash version 1 isn't using a 4K shift\n");
+ return 1;
+ }
+
+ mbox_server_memset(0xff);
+
+ test = calloc(erase_granule * 20, 1);
+
+ /* Make up a test pattern */
+ for (i = 0; i < erase_granule * 10; i++)
+ test[i] = i;
+
+ /* Write 64k of stuff at 0 and at 128k */
+ printf("Writing test patterns...\n");
+ rc = blocklevel_write(bl, 0, test, erase_granule * 10);
+ if (rc) {
+ ERR("blocklevel_write(0, erase_granule * 10) failed with err %d\n", rc);
+ return 1;
+ }
+ rc = blocklevel_write(bl, erase_granule * 20, test, erase_granule * 10);
+ if (rc) {
+ ERR("blocklevel_write(0x20000, 0x10000) failed with err %d\n", rc);
+ return 1;
+ }
+
+ if (mbox_server_memcmp(0, test, erase_granule * 10)) {
+ ERR("Test pattern mismatch !\n");
+ return 1;
+ }
+
+ /* Write "Hello world" straddling the 64k boundary */
+ printf("Writing test string...\n");
+ rc = blocklevel_write(bl, (erase_granule * 10) - 8, hello, sizeof(hello));
+ if (rc) {
+ ERR("blocklevel_write(0xfffc, %s, %lu) failed with err %d\n",
+ hello, sizeof(hello), rc);
+ return 1;
+ }
+
+ /* Check result */
+ if (mbox_server_memcmp((erase_granule * 10) - 8, hello, sizeof(hello))) {
+ ERR("Test string mismatch!\n");
+ return 1;
+ }
+
+ /* Erase granule is something but never 0x50, this shouldn't succeed */
+ rc = blocklevel_erase(bl, 0, 0x50);
+ if (!rc) {
+ ERR("blocklevel_erase(0, 0x50) didn't fail!\n");
+ return 1;
+ }
+
+ /* Check it didn't silently erase */
+ if (mbox_server_memcmp(0, test, (erase_granule * 10) - 8)) {
+ ERR("Test pattern mismatch !\n");
+ return 1;
+ }
+
+ /*
+ * For v1 protocol this should NOT call MARK_WRITE_ERASED!
+ * The server MARK_WRITE_ERASED will call exit(1) if it gets a
+ * MARK_WRITE_ERASED and version == 1
+ */
+ rc = blocklevel_erase(bl, 0, erase_granule);
+ if (rc) {
+ ERR("blocklevel_erase(0, erase_granule) failed with err %d\n", rc);
+ return 1;
+ }
+
+ /*
+ * Version 1 doesn't specify that the buffer actually becomes 0xff
+ * It is up to the daemon to do what it wants really - there are
+ * implementations that do nothing but writes to the same region
+ * work fine
+ */
+
+ /* This check is important for v2 */
+ /* Check stuff got erased */
+ tmp = malloc(erase_granule * 2);
+ if (!tmp) {
+ ERR("malloc failed\n");
+ return 1;
+ }
+ if (mbox_server_version() > 1) {
+ memset(tmp, 0xff, erase_granule);
+ if (mbox_server_memcmp(0, tmp, erase_granule)) {
+ ERR("Buffer not erased\n");
+ rc = 1;
+ goto out;
+ }
+ }
+
+ /* Read beyond the end of flash */
+ rc = blocklevel_read(bl, total_size, tmp, 0x1000);
+ if (!rc) {
+ ERR("blocklevel_read(total_size, 0x1000) (read beyond the end) succeeded\n");
+ goto out;
+ }
+
+ /* Test some simple write/read cases, avoid first page */
+ rc = blocklevel_write(bl, erase_granule * 2, test, erase_granule / 2);
+ if (rc) {
+ ERR("blocklevel_write(erase_granule, erase_granule / 2) failed with err %d\n", rc);
+ goto out;
+ }
+ rc = blocklevel_write(bl, erase_granule * 2 + erase_granule / 2, test, erase_granule / 2);
+ if (rc) {
+ ERR("blocklevel_write(erase_granule * 2 + erase_granule / 2, erase_granule) failed with err %d\n", rc);
+ goto out;
+ }
+
+ rc = mbox_server_memcmp(erase_granule * 2, test, erase_granule / 2);
+ if (rc) {
+ ERR("%s:%d mbox_server_memcmp miscompare\n", __FILE__, __LINE__);
+ goto out;
+ }
+ rc = mbox_server_memcmp(erase_granule * 2 + erase_granule / 2, test, erase_granule / 2);
+ if (rc) {
+ ERR("%s:%d mbox_server_memcmp miscompare\n", __FILE__, __LINE__);
+ goto out;
+ }
+
+ /* Great so the writes made it, can we read them back? Do it in
+ * four small reads */
+ for (i = 0; i < 4; i++) {
+ rc = blocklevel_read(bl, erase_granule * 2 + (i * erase_granule / 4), tmp + (i * erase_granule / 4), erase_granule / 4);
+ if (rc) {
+ ERR("blocklevel_read(0x%08x, erase_granule / 4) failed with err %d\n",
+ 2 * erase_granule + (i * erase_granule / 4), rc);
+ goto out;
+ }
+ }
+ rc = memcmp(test, tmp, erase_granule / 2);
+ if (rc) {
+ ERR("%s:%d read back miscompare\n", __FILE__, __LINE__);
+ goto out;
+ }
+ rc = memcmp(test, tmp + erase_granule / 2, erase_granule / 2);
+ if (rc) {
+ ERR("%s:%d read back miscompare\n", __FILE__, __LINE__);
+ goto out;
+ }
+
+ /*
+ * Make sure we didn't corrupt other stuff, also make sure one
+ * blocklevel call will understand how to read from two windows
+ */
+ for (i = 3; i < 9; i = i + 2) {
+ printf("i:%d erase: 0x%08x\n", i, erase_granule);
+ rc = blocklevel_read(bl, i * erase_granule, tmp, 2 * erase_granule);
+ if (rc) {
+ ERR("blocklevel_read(0x%08x, 2 * erase_granule) failed with err: %d\n", i * erase_granule, rc);
+ goto out;
+ }
+ rc = memcmp(((char *)test) + (i * erase_granule), tmp, 2 * erase_granule);
+ if (rc) {
+ ERR("%s:%d read back miscompare (pos: 0x%08x)\n", __FILE__, __LINE__, i * erase_granule);
+ goto out;
+ }
+ }
+
+ srand(1);
+ /*
+ * Try to jump around the place doing a tonne of small reads.
+ * Worth doing the same with writes TODO
+ */
+#ifdef __STRICT_TEST__
+#define TEST_LOOPS 1000
+#else
+#define TEST_LOOPS 100
+#endif
+ for (i = 0; i < TEST_LOOPS; i++) {
+ int r = rand();
+
+ printf("Loop %d of %d\n", i, TEST_LOOPS);
+ /* Avoid reading too far, just skip it */
+ if ((r % erase_granule * 10) + (r % erase_granule * 2) > erase_granule * 10)
+ continue;
+
+ rc = blocklevel_read(bl, erase_granule * 20 + (r % erase_granule * 10), tmp, r % erase_granule * 2);
+ if (rc) {
+ ERR("blocklevel_read(0x%08x, 0x%08x) failed with err %d\n", 0x20000 + (r % 0x100000), r % 0x2000, rc);
+ goto out;
+ }
+ rc = memcmp(((char *)test) + (r % erase_granule * 10), tmp, r % erase_granule * 2);
+ if (rc) {
+ ERR("%s:%d read back miscompare (pos: 0x%08x)\n", __FILE__, __LINE__, 0x20000 + (r % 0x10000));
+ goto out;
+ }
+ }
+out:
+ free(tmp);
+ return rc;
+}
+
+int main(void)
+{
+ struct blocklevel_device *bl;
+ int rc;
+
+ libflash_debug = true;
+
+ mbox_server_init();
+
+#ifdef __STRICT_TEST__
+ printf("Found __STRICT_TEST__, this may take time time.\n");
+#else
+ printf("__STRICT_TEST__ not found, use make strict-check for a more\n");
+ printf("thorough test, it will take significantly longer.\n");
+#endif
+
+ printf("Doing mbox-flash V1 tests\n");
+
+ /* run test */
+ mbox_flash_init(&bl);
+ rc = run_flash_test(bl);
+ if (rc)
+ goto out;
+ /*
+ * Trick mbox-flash into thinking there was a reboot so we can
+ * switch to v2
+ */
+
+ printf("Doing mbox-flash V2 tests\n");
+
+ mbox_server_reset(2, 12);
+
+ /* Do all the tests again */
+ rc = run_flash_test(bl);
+ if (rc)
+ goto out;
+
+ mbox_server_reset(2, 17);
+
+ /* Do all the tests again */
+ rc = run_flash_test(bl);
+ if (rc)
+ goto out;
+
+
+ printf("Doing mbox-flash V3 tests\n");
+
+ mbox_server_reset(3, 20);
+
+ /* Do all the tests again */
+ rc = run_flash_test(bl);
+
+
+out:
+ mbox_flash_exit(bl);
+
+ mbox_server_destroy();
+
+ return rc;
+}