ncsid: Import from gBMC
This is the initial code drop from gBMC.
Google-Bug-Id: 179618516
Upstream: 1e71af914bc8c54d8b91d0a1cf377e2696713c2f
Change-Id: Ic653e8271dacd205e04f2bc713071ef2ec5936a4
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/ncsid/test/ncsi_test.cpp b/ncsid/test/ncsi_test.cpp
new file mode 100644
index 0000000..9808da6
--- /dev/null
+++ b/ncsid/test/ncsi_test.cpp
@@ -0,0 +1,240 @@
+#include "net_iface_mock.h"
+#include "nic_mock.h"
+#include "platforms/nemora/portable/default_addresses.h"
+#include "platforms/nemora/portable/ncsi.h"
+#include "platforms/nemora/portable/ncsi_fsm.h"
+#include "platforms/nemora/portable/net_types.h"
+
+#include <ncsi_state_machine.h>
+#include <net_config.h>
+#include <net_sockio.h>
+#include <netinet/ether.h>
+#include <netinet/in.h>
+
+#include <gmock/gmock.h>
+
+namespace
+{
+
+constexpr uint32_t ETHER_NCSI = 0x88f8;
+
+class MockConfig : public net::ConfigBase
+{
+ public:
+ int get_mac_addr(mac_addr_t* mac) override
+ {
+ std::memcpy(mac, &mac_addr, sizeof(mac_addr_t));
+
+ return 0;
+ }
+
+ int set_mac_addr(const mac_addr_t& mac) override
+ {
+ mac_addr = mac;
+
+ return 0;
+ }
+
+ int set_nic_hostless(bool is_hostless) override
+ {
+ is_nic_hostless = is_hostless;
+
+ return 0;
+ }
+
+ mac_addr_t mac_addr;
+ bool is_nic_hostless = true;
+};
+
+class NICConnection : public net::SockIO
+{
+ public:
+ int write(const void* buf, size_t len) override
+ {
+ conseq_reads = 0;
+ ++n_writes;
+ std::memcpy(last_write.data, buf, len);
+ last_write.len = len;
+ const auto* hdr = reinterpret_cast<const struct ether_header*>(buf);
+ if (ETHER_NCSI == ntohs(hdr->ether_type))
+ {
+ ++n_handles;
+ next_read.len = nic_mock.handle_request(last_write, &next_read);
+ }
+
+ return len;
+ }
+
+ int recv(void* buf, size_t maxlen) override
+ {
+ ++n_reads;
+ ++conseq_reads;
+
+ if (read_timeout > 0)
+ {
+ if (conseq_reads > read_timeout)
+ {
+ return 0;
+ }
+ }
+
+ if (maxlen < next_read.len)
+ {
+ ++n_read_errs;
+ return 0;
+ }
+
+ std::memcpy(buf, next_read.data, next_read.len);
+
+ return next_read.len;
+ }
+
+ mock::NIC nic_mock{false, 2};
+ int n_writes = 0;
+ int n_reads = 0;
+ int n_handles = 0;
+ int n_read_errs = 0;
+
+ // Max number of consequitive reads without writes.
+ int read_timeout = -1;
+ int conseq_reads = 0;
+
+ ncsi_buf_t last_write = {};
+ ncsi_buf_t next_read = {};
+};
+
+} // namespace
+
+class TestNcsi : public testing::Test
+{
+ public:
+ void SetUp() override
+ {
+ ncsi_sm.set_sockio(&ncsi_sock);
+ ncsi_sm.set_net_config(&net_config_mock);
+ ncsi_sm.set_retest_delay(0);
+ ncsi_sock.nic_mock.set_mac(nic_mac);
+ ncsi_sock.nic_mock.set_hostless(true);
+ ncsi_sock.read_timeout = 10;
+ }
+
+ protected:
+ void ExpectFiltersNotConfigured()
+ {
+ for (uint8_t i = 0; i < ncsi_sock.nic_mock.get_channel_count(); ++i)
+ {
+ EXPECT_FALSE(ncsi_sock.nic_mock.is_filter_configured(i));
+ }
+ }
+
+ void ExpectFiltersConfigured()
+ {
+ // Check that filters are configured on all channels.
+ for (uint8_t i = 0; i < ncsi_sock.nic_mock.get_channel_count(); ++i)
+ {
+ EXPECT_TRUE(ncsi_sock.nic_mock.is_filter_configured(i));
+ const ncsi_oem_filter_t& ch_filter =
+ ncsi_sock.nic_mock.get_filter(i);
+
+ for (unsigned i = 0; i < sizeof(nic_mac.octet); ++i)
+ {
+ EXPECT_EQ(nic_mac.octet[i], ch_filter.mac[i]);
+ }
+
+ EXPECT_EQ(ch_filter.ip, 0);
+ const uint16_t filter_port = ntohs(ch_filter.port);
+ EXPECT_EQ(filter_port, DEFAULT_ADDRESSES_RX_PORT);
+ }
+ }
+
+ MockConfig net_config_mock;
+ NICConnection ncsi_sock;
+ ncsi::StateMachine ncsi_sm;
+ const mac_addr_t nic_mac = {{0xde, 0xca, 0xfb, 0xad, 0x01, 0x02}};
+
+ // Number of states in each state machine
+ static constexpr int l2_num_states = 26;
+ static constexpr int l3l4_num_states = 2;
+ static constexpr int test_num_states = 9;
+
+ // Total number of states in all three state machines.
+ static constexpr int total_num_states =
+ l2_num_states + l3l4_num_states + test_num_states;
+};
+
+TEST_F(TestNcsi, TestMACAddrPropagation)
+{
+ ncsi_sm.run(total_num_states);
+ EXPECT_EQ(ncsi_sock.n_read_errs, 0);
+ EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
+ EXPECT_EQ(0, std::memcmp(nic_mac.octet, net_config_mock.mac_addr.octet,
+ sizeof(nic_mac.octet)));
+
+ // Since network is not configured, the filters should not be configured
+ // either.
+ ExpectFiltersNotConfigured();
+}
+
+TEST_F(TestNcsi, TestFilterConfiguration)
+{
+ ncsi_sm.run(total_num_states);
+ EXPECT_EQ(ncsi_sock.n_read_errs, 0);
+ EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
+
+ ExpectFiltersConfigured();
+}
+
+TEST_F(TestNcsi, TestFilterReset)
+{
+ ncsi_sm.run(total_num_states);
+ EXPECT_EQ(ncsi_sock.n_read_errs, 0);
+ EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
+
+ // Since network is not configured, the filters should not be configured
+ // either.
+ ExpectFiltersNotConfigured();
+
+ ncsi_sm.run(total_num_states);
+
+ ExpectFiltersConfigured();
+}
+
+TEST_F(TestNcsi, TestRetest)
+{
+ ncsi_sm.run(total_num_states + test_num_states);
+
+ // Verify that the test state machine was stepped through twice,
+ // by counting how many times the last command of the state machine
+ // has been executed.
+ const uint8_t last_test_command = NCSI_GET_LINK_STATUS;
+ const auto& cmd_log = ncsi_sock.nic_mock.get_command_log();
+ int num_test_runs = 0;
+ for (const auto& ncsi_frame : cmd_log)
+ {
+ if (ncsi_frame.get_control_packet_type() == last_test_command)
+ {
+ ++num_test_runs;
+ }
+ }
+
+ EXPECT_EQ(num_test_runs, 2);
+}
+
+TEST_F(TestNcsi, TestHostlessSwitch)
+{
+ // By default the NIC is in hostless mode.
+ // Verify that net config flag changes after FSM run.
+ net_config_mock.is_nic_hostless = false;
+ ncsi_sm.run(total_num_states);
+ EXPECT_EQ(ncsi_sock.n_read_errs, 0);
+ EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
+ EXPECT_TRUE(net_config_mock.is_nic_hostless);
+
+ // Now disable the hostless mode and verify that net config
+ // flag changes to false.
+ ncsi_sock.nic_mock.set_hostless(false);
+ ncsi_sm.run(total_num_states);
+ EXPECT_EQ(ncsi_sock.n_read_errs, 0);
+ EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
+ EXPECT_FALSE(net_config_mock.is_nic_hostless);
+}