/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ #if HAVE_CONFIG_H #include "config.h" #endif #include "compiler.h" #include "range.h" #include "libmctp-log.h" #include "libmctp-i2c.h" #include "libmctp-sizes.h" #include "libmctp-alloc.h" /* For access to mctp_bninding_i2c internals */ #include "i2c-internal.h" #ifdef NDEBUG #undef NDEBUG #endif #include #include #include #include #include #include #include #include struct mctp_binding_serial_pipe { int ingress; int egress; struct mctp_binding_serial *serial; }; // Sized to test fragmentation and >8 bit length #define TEST_MSG_LEN 300 static uint8_t mctp_msg_src[TEST_MSG_LEN]; struct i2c_test { struct mctp_binding_i2c *i2c; struct mctp *mctp; uint8_t rx_msg[TEST_MSG_LEN]; size_t rx_len; /* Physical addresses. These get set regardless of whether the packet * is dropped by the stack (no match etc) */ uint8_t last_rx_i2c_src; uint8_t last_tx_i2c_dst; }; static const uint8_t I2C_ADDR_A = 0x20; static const uint8_t I2C_ADDR_B = 0x21; static const uint8_t EID_A = 50; static const uint8_t EID_B = 51; static int test_i2c_tx(const void *buf, size_t len, void *ctx) { struct i2c_test *test_pair = ctx; struct i2c_test *tx_test = &test_pair[0]; struct i2c_test *rx_test = &test_pair[1]; mctp_prdebug("test_i2c_tx len %zu", len); const struct mctp_i2c_hdr *hdr = buf; tx_test->last_tx_i2c_dst = hdr->dest >> 1; rx_test->last_rx_i2c_src = hdr->source >> 1; mctp_i2c_rx(rx_test->i2c, buf, len); return 0; } static void test_i2c_rxmsg(uint8_t src_eid, bool tag_owner, uint8_t msg_tag, void *ctx, void *msg, size_t len) { struct i2c_test *test_pair = ctx; // struct i2c_test *tx_test = &test_pair[0]; struct i2c_test *rx_test = &test_pair[1]; mctp_prdebug("test_i2c_rx src %d len %zu tag %d owner %d", src_eid, len, msg_tag, tag_owner); // Must be cleared by previous test runs assert(rx_test->rx_len == 0); memcpy(rx_test->rx_msg, msg, len); rx_test->rx_len = len; } /* Transmits a MCTP message and checks the received message matches */ static void run_tx_test(struct i2c_test *tx_test, uint8_t dest_eid, size_t tx_len, struct i2c_test *rx_test) { int rc; const uint8_t msg_tag = 2; const bool tag_owner = false; assert(tx_len <= sizeof(mctp_msg_src)); rc = mctp_message_tx(tx_test->mctp, dest_eid, tag_owner, msg_tag, mctp_msg_src, tx_len); assert(rc == 0); while (!mctp_is_tx_ready(tx_test->mctp, dest_eid)) { mctp_i2c_tx_poll(tx_test->i2c); } assert(rx_test->rx_len == tx_len); assert(memcmp(rx_test->rx_msg, mctp_msg_src, tx_len) == 0); rx_test->rx_len = 0; } static void test_neigh_expiry(struct i2c_test *tx_test, struct i2c_test *rx_test) { const uint8_t msg_tag = 2; const bool tag_owner = true; const size_t msg_len = 5; int rc; (void)rx_test; /* Clear the tx neighbour table */ memset(tx_test->i2c->neigh, 0x0, sizeof(tx_test->i2c->neigh)); /* Check that all EIDs fail */ rx_test->rx_len = 0; for (size_t eid = 8; eid < 254; eid++) { mctp_message_tx(tx_test->mctp, eid, tag_owner, msg_tag, mctp_msg_src, msg_len); /* Not received */ assert(rx_test->rx_len == 0); } /* Add one entry */ rc = mctp_i2c_set_neighbour(tx_test->i2c, EID_B, rx_test->i2c->own_addr); assert(rc == 0); rx_test->rx_len = 0; mctp_message_tx(tx_test->mctp, EID_B, tag_owner, msg_tag, mctp_msg_src, msg_len); assert(rx_test->rx_len == msg_len); assert(tx_test->last_tx_i2c_dst == rx_test->i2c->own_addr); /* Replace the entry */ rx_test->i2c->own_addr++; rc = mctp_i2c_set_neighbour(tx_test->i2c, EID_B, rx_test->i2c->own_addr); assert(rc == 0); rx_test->rx_len = 0; mctp_message_tx(tx_test->mctp, EID_B, tag_owner, msg_tag, mctp_msg_src, msg_len); assert(rc == 0); assert(rx_test->rx_len == msg_len); assert(tx_test->last_tx_i2c_dst == rx_test->i2c->own_addr); /* Check only one entry is set */ size_t count = 0; for (size_t i = 0; i < MCTP_I2C_NEIGH_COUNT; i++) { struct mctp_i2c_neigh *n = &tx_test->i2c->neigh[i]; if (n->used) { assert(n->eid == EID_B); count++; } } assert(count == 1); /* Ensure we can iterate without overflow. * If MCTP_I2C_NEIGH_COUNT increases too large this test would need rethinking * (and eviction may become impossible) */ assert((int)EID_B + MCTP_I2C_NEIGH_COUNT < 254); assert((int)I2C_ADDR_B + MCTP_I2C_NEIGH_COUNT < 0x7f); /* Fill entries. -1 because one was already filled. */ for (size_t i = 0; i < MCTP_I2C_NEIGH_COUNT - 1; i++) { /* Unused addresses */ uint8_t addr = rx_test->i2c->own_addr + i + 1; uint8_t eid = EID_B + i + 1; rc = mctp_i2c_set_neighbour(tx_test->i2c, eid, addr); assert(rc == 0); } /* Check all are used */ for (size_t i = 0; i < MCTP_I2C_NEIGH_COUNT; i++) { struct mctp_i2c_neigh *n = &tx_test->i2c->neigh[i]; assert(n->used); } /* Test eviction */ { uint8_t addr = rx_test->i2c->own_addr + MCTP_I2C_NEIGH_COUNT + 1; uint8_t eid = EID_B + MCTP_I2C_NEIGH_COUNT + 1; rc = mctp_i2c_set_neighbour(tx_test->i2c, eid, addr); assert(rc == 0); /* EID_B got evicted, send should fail */ rx_test->rx_len = 0; mctp_message_tx(tx_test->mctp, EID_B, tag_owner, msg_tag, mctp_msg_src, msg_len); /* Not received */ assert(rx_test->rx_len == 0); } /* Add EID_B again */ rc = mctp_i2c_set_neighbour(tx_test->i2c, EID_B, rx_test->i2c->own_addr); assert(rc == 0); rx_test->rx_len = 0; mctp_message_tx(tx_test->mctp, EID_B, tag_owner, msg_tag, mctp_msg_src, msg_len); /* Is received */ assert(rx_test->rx_len == msg_len); } int main(void) { struct i2c_test scenario[2]; struct i2c_test *tx_test = &scenario[0]; struct i2c_test *rx_test = &scenario[1]; mctp_set_log_stdio(MCTP_LOG_DEBUG); memset(scenario, 0x0, sizeof(scenario)); /* Setup a source buffer */ for (size_t i = 0; i < sizeof(mctp_msg_src); i++) { mctp_msg_src[i] = i & 0xff; } tx_test->mctp = mctp_init(); assert(tx_test->mctp); tx_test->i2c = malloc(MCTP_SIZEOF_BINDING_I2C); assert(tx_test->i2c); rx_test->mctp = mctp_init(); assert(rx_test->mctp); rx_test->i2c = malloc(MCTP_SIZEOF_BINDING_I2C); assert(rx_test->i2c); /* TX side */ mctp_i2c_setup(tx_test->i2c, I2C_ADDR_A, test_i2c_tx, scenario); mctp_register_bus(tx_test->mctp, mctp_binding_i2c_core(tx_test->i2c), EID_A); mctp_set_rx_all(tx_test->mctp, NULL, NULL); mctp_i2c_set_neighbour(tx_test->i2c, EID_B, I2C_ADDR_B); /* RX side */ mctp_i2c_setup(rx_test->i2c, I2C_ADDR_B, NULL, NULL); mctp_register_bus(rx_test->mctp, mctp_binding_i2c_core(rx_test->i2c), EID_B); mctp_set_rx_all(rx_test->mctp, test_i2c_rxmsg, scenario); // mctp_i2c_set_neighbour(rx_test->i2c, EID_A, I2C_ADDR_A); /* Try all message sizes */ for (size_t i = 1; i < sizeof(mctp_msg_src); i++) { run_tx_test(tx_test, EID_B, i, rx_test); } test_neigh_expiry(tx_test, rx_test); return 0; }