[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