#include #include #include #include #include #include #include #include #include #include #include #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; 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 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, }, 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 } }, 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 } }, 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 } }, 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), } }, 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, }, }, }, }, 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, }, }, }, }, 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, }, 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 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, }, }, }, }, 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 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, }, 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, } }, 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, } }, 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, }, 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 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, }, }, }, 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 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, }, }, }, 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, } }, 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, }, 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, }, }, }, 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, }, }, }, 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, } }, 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, }, }, }, }, 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 } }, 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, }, }, 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, }, }, 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, }, }, 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, }, }, 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, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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, }, }, }, 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_two_blocks), 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_two_blocks), 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; }