blob: 6117e9dd401c55c119246a442cedcd0a939dad04 [file] [log] [blame]
// 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;
}