aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/libflash/test/test-ipmi-hiomap.c
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/test-ipmi-hiomap.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/libflash/test/test-ipmi-hiomap.c')
-rw-r--r--roms/skiboot/libflash/test/test-ipmi-hiomap.c3388
1 files changed, 3388 insertions, 0 deletions
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;
+}