[Skiboot] [PATCH 3/4] libflash/ipmi-hiomap: Add support for unit tests

Andrew Jeffery andrew at aj.id.au
Wed Nov 7 17:55:51 AEDT 2018


Lay the ground work for unit testing the ipmi-hiomap implementation. The
design hooks a subset of the IPMI interface to move through a
data-driven "scenario" of IPMI message exchanges. Two basic tests are
added exercising the initialsation path of the protocol implementation.

Signed-off-by: Andrew Jeffery <andrew at aj.id.au>
---
 libflash/ipmi-hiomap.c           |  32 +---
 libflash/ipmi-hiomap.h           |  31 ++++
 libflash/test/Makefile.check     |   6 +
 libflash/test/stubs.c            |  19 ++
 libflash/test/stubs.h            |   5 +
 libflash/test/test-ipmi-hiomap.c | 310 +++++++++++++++++++++++++++++++
 6 files changed, 372 insertions(+), 31 deletions(-)
 create mode 100644 libflash/test/test-ipmi-hiomap.c

diff --git a/libflash/ipmi-hiomap.c b/libflash/ipmi-hiomap.c
index ae4b3102bea8..88e9edaca484 100644
--- a/libflash/ipmi-hiomap.c
+++ b/libflash/ipmi-hiomap.c
@@ -19,8 +19,8 @@
 #include <hiomap.h>
 #include <inttypes.h>
 #include <ipmi.h>
-#include <lock.h>
 #include <lpc.h>
+#include <mem_region-malloc.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
@@ -32,36 +32,6 @@
 
 #define CMD_OP_HIOMAP_EVENT	0x0f
 
-enum lpc_window_state { closed_window, read_window, write_window };
-
-struct lpc_window {
-	uint32_t lpc_addr; /* Offset into LPC space */
-	uint32_t cur_pos;  /* Current position of the window in the flash */
-	uint32_t size;     /* Size of the window into the flash */
-};
-
-struct ipmi_hiomap {
-	/* Members protected by the blocklevel lock */
-	uint8_t seq;
-	uint8_t version;
-	uint8_t block_size_shift;
-	uint16_t timeout;
-	struct blocklevel_device bl;
-	uint32_t total_size;
-	uint32_t erase_granule;
-	struct lpc_window current;
-
-	/*
-	 * update, bmc_state and window_state can be accessed by both calls
-	 * through read/write/erase functions and the IPMI SEL handler. All
-	 * three variables are protected by lock to avoid conflict.
-	 */
-	struct lock lock;
-	bool update;
-	uint8_t bmc_state;
-	enum lpc_window_state window_state;
-};
-
 struct ipmi_hiomap_result {
 	struct ipmi_hiomap *ctx;
 	int16_t cc;
diff --git a/libflash/ipmi-hiomap.h b/libflash/ipmi-hiomap.h
index 4742b7d80b16..f60f725d2ef0 100644
--- a/libflash/ipmi-hiomap.h
+++ b/libflash/ipmi-hiomap.h
@@ -17,11 +17,42 @@
 #ifndef __LIBFLASH_IPMI_HIOMAP_H
 #define __LIBFLASH_IPMI_HIOMAP_H
 
+#include <lock.h>
 #include <stdbool.h>
 #include <stdint.h>
 
 #include "blocklevel.h"
 
+enum lpc_window_state { closed_window, read_window, write_window };
+
+struct lpc_window {
+	uint32_t lpc_addr; /* Offset into LPC space */
+	uint32_t cur_pos;  /* Current position of the window in the flash */
+	uint32_t size;     /* Size of the window into the flash */
+};
+
+struct ipmi_hiomap {
+	/* Members protected by the blocklevel lock */
+	uint8_t seq;
+	uint8_t version;
+	uint8_t block_size_shift;
+	uint16_t timeout;
+	struct blocklevel_device bl;
+	uint32_t total_size;
+	uint32_t erase_granule;
+	struct lpc_window current;
+
+	/*
+	 * update, bmc_state and window_state can be accessed by both calls
+	 * through read/write/erase functions and the IPMI SEL handler. All
+	 * three variables are protected by lock to avoid conflict.
+	 */
+	struct lock lock;
+	bool update;
+	uint8_t bmc_state;
+	enum lpc_window_state window_state;
+};
+
 int ipmi_hiomap_init(struct blocklevel_device **bl);
 void ipmi_hiomap_exit(struct blocklevel_device *bl);
 
diff --git a/libflash/test/Makefile.check b/libflash/test/Makefile.check
index da458841ab8d..e0d55fa03d5b 100644
--- a/libflash/test/Makefile.check
+++ b/libflash/test/Makefile.check
@@ -1,4 +1,9 @@
 # -*-Makefile-*-
+libflash_test_test_ipmi_hiomap_SOURCES = \
+	libflash/test/test-ipmi-hiomap.c \
+	libflash/test/stubs.c \
+	libflash/ipmi-hiomap.c
+
 libflash_test_test_flash_SOURCES = \
 	libflash/test/test-flash.c \
 	libflash/test/stubs.c \
@@ -15,6 +20,7 @@ libflash_test_test_mbox_SOURCES = \
 	libflash/test/mbox-server.c
 
 check_PROGRAMS = \
+	libflash/test/test-ipmi-hiomap \
 	libflash/test/test-flash \
 	libflash/test/test-ecc \
 	libflash/test/test-mbox
diff --git a/libflash/test/stubs.c b/libflash/test/stubs.c
index e09c8f5735dc..f1130679b29b 100644
--- a/libflash/test/stubs.c
+++ b/libflash/test/stubs.c
@@ -90,3 +90,22 @@ u16 bmc_get_u16(struct bmc_mbox_msg *msg, int offset)
 
 	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/libflash/test/stubs.h b/libflash/test/stubs.h
index bc6d3f380986..bb87dba6183a 100644
--- a/libflash/test/stubs.h
+++ b/libflash/test/stubs.h
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <lock.h>
 #include <stdint.h>
 
 #include "../../include/lpc-mbox.h"
@@ -25,3 +26,7 @@ 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/libflash/test/test-ipmi-hiomap.c b/libflash/test/test-ipmi-hiomap.c
new file mode 100644
index 000000000000..04472e71a760
--- /dev/null
+++ b/libflash/test/test-ipmi-hiomap.c
@@ -0,0 +1,310 @@
+#include <assert.h>
+#include <ccan/container_of/container_of.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"
+
+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
+};
+
+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;
+};
+
+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;
+	};
+};
+
+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_exit(void)
+{
+	assert(ipmi_msg_ctx.cursor->type == scenario_sentinel);
+}
+
+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 {
+		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);
+	}
+	memcpy(msg->data, &cmd->resp, msg->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 __attribute__((unused)))
+{
+	return 0;
+}
+
+int64_t lpc_read(enum OpalLPCAddressType addr_type __attribute__((unused)),
+		 uint32_t addr __attribute__((unused)),
+		 uint32_t *data __attribute__((unused)),
+		 uint32_t sz __attribute__((unused)))
+{
+	return 0;
+}
+
+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 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, },
+	{ },
+};
+
+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 } },
+	{ },
+};
+
+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 == 0x80);
+	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),
+	{ },
+};
+
+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;
+}
-- 
2.19.1



More information about the Skiboot mailing list