blob: 39b8378d11971e5e0bfbb639de7d25375be922d0 [file] [log] [blame]
Brandon Kimdab96f12021-02-18 11:21:37 -08001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
William A. Kennington III7d6fa422021-02-08 17:04:02 -080015#include "net_iface_mock.h"
16#include "nic_mock.h"
17#include "platforms/nemora/portable/default_addresses.h"
18#include "platforms/nemora/portable/ncsi.h"
19#include "platforms/nemora/portable/ncsi_fsm.h"
20#include "platforms/nemora/portable/net_types.h"
21
22#include <ncsi_state_machine.h>
23#include <net_config.h>
24#include <net_sockio.h>
25#include <netinet/ether.h>
26#include <netinet/in.h>
27
28#include <gmock/gmock.h>
29
30namespace
31{
32
33constexpr uint32_t ETHER_NCSI = 0x88f8;
34
35class MockConfig : public net::ConfigBase
36{
37 public:
38 int get_mac_addr(mac_addr_t* mac) override
39 {
40 std::memcpy(mac, &mac_addr, sizeof(mac_addr_t));
41
42 return 0;
43 }
44
45 int set_mac_addr(const mac_addr_t& mac) override
46 {
47 mac_addr = mac;
48
49 return 0;
50 }
51
52 int set_nic_hostless(bool is_hostless) override
53 {
54 is_nic_hostless = is_hostless;
55
56 return 0;
57 }
58
59 mac_addr_t mac_addr;
60 bool is_nic_hostless = true;
61};
62
63class NICConnection : public net::SockIO
64{
65 public:
66 int write(const void* buf, size_t len) override
67 {
68 conseq_reads = 0;
69 ++n_writes;
70 std::memcpy(last_write.data, buf, len);
71 last_write.len = len;
72 const auto* hdr = reinterpret_cast<const struct ether_header*>(buf);
73 if (ETHER_NCSI == ntohs(hdr->ether_type))
74 {
75 ++n_handles;
76 next_read.len = nic_mock.handle_request(last_write, &next_read);
77 }
78
79 return len;
80 }
81
82 int recv(void* buf, size_t maxlen) override
83 {
84 ++n_reads;
85 ++conseq_reads;
86
87 if (read_timeout > 0)
88 {
89 if (conseq_reads > read_timeout)
90 {
91 return 0;
92 }
93 }
94
95 if (maxlen < next_read.len)
96 {
97 ++n_read_errs;
98 return 0;
99 }
100
101 std::memcpy(buf, next_read.data, next_read.len);
102
103 return next_read.len;
104 }
105
106 mock::NIC nic_mock{false, 2};
107 int n_writes = 0;
108 int n_reads = 0;
109 int n_handles = 0;
110 int n_read_errs = 0;
111
112 // Max number of consequitive reads without writes.
113 int read_timeout = -1;
114 int conseq_reads = 0;
115
116 ncsi_buf_t last_write = {};
117 ncsi_buf_t next_read = {};
118};
119
120} // namespace
121
122class TestNcsi : public testing::Test
123{
124 public:
125 void SetUp() override
126 {
127 ncsi_sm.set_sockio(&ncsi_sock);
128 ncsi_sm.set_net_config(&net_config_mock);
129 ncsi_sm.set_retest_delay(0);
130 ncsi_sock.nic_mock.set_mac(nic_mac);
131 ncsi_sock.nic_mock.set_hostless(true);
132 ncsi_sock.read_timeout = 10;
133 }
134
135 protected:
136 void ExpectFiltersNotConfigured()
137 {
138 for (uint8_t i = 0; i < ncsi_sock.nic_mock.get_channel_count(); ++i)
139 {
140 EXPECT_FALSE(ncsi_sock.nic_mock.is_filter_configured(i));
141 }
142 }
143
144 void ExpectFiltersConfigured()
145 {
146 // Check that filters are configured on all channels.
147 for (uint8_t i = 0; i < ncsi_sock.nic_mock.get_channel_count(); ++i)
148 {
149 EXPECT_TRUE(ncsi_sock.nic_mock.is_filter_configured(i));
150 const ncsi_oem_filter_t& ch_filter =
151 ncsi_sock.nic_mock.get_filter(i);
152
153 for (unsigned i = 0; i < sizeof(nic_mac.octet); ++i)
154 {
155 EXPECT_EQ(nic_mac.octet[i], ch_filter.mac[i]);
156 }
157
158 EXPECT_EQ(ch_filter.ip, 0);
159 const uint16_t filter_port = ntohs(ch_filter.port);
160 EXPECT_EQ(filter_port, DEFAULT_ADDRESSES_RX_PORT);
161 }
162 }
163
164 MockConfig net_config_mock;
165 NICConnection ncsi_sock;
166 ncsi::StateMachine ncsi_sm;
167 const mac_addr_t nic_mac = {{0xde, 0xca, 0xfb, 0xad, 0x01, 0x02}};
168
169 // Number of states in each state machine
170 static constexpr int l2_num_states = 26;
171 static constexpr int l3l4_num_states = 2;
172 static constexpr int test_num_states = 9;
173
174 // Total number of states in all three state machines.
175 static constexpr int total_num_states =
176 l2_num_states + l3l4_num_states + test_num_states;
177};
178
179TEST_F(TestNcsi, TestMACAddrPropagation)
180{
181 ncsi_sm.run(total_num_states);
182 EXPECT_EQ(ncsi_sock.n_read_errs, 0);
183 EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
184 EXPECT_EQ(0, std::memcmp(nic_mac.octet, net_config_mock.mac_addr.octet,
185 sizeof(nic_mac.octet)));
186
187 // Since network is not configured, the filters should not be configured
188 // either.
189 ExpectFiltersNotConfigured();
190}
191
192TEST_F(TestNcsi, TestFilterConfiguration)
193{
194 ncsi_sm.run(total_num_states);
195 EXPECT_EQ(ncsi_sock.n_read_errs, 0);
196 EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
197
198 ExpectFiltersConfigured();
199}
200
201TEST_F(TestNcsi, TestFilterReset)
202{
203 ncsi_sm.run(total_num_states);
204 EXPECT_EQ(ncsi_sock.n_read_errs, 0);
205 EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
206
207 // Since network is not configured, the filters should not be configured
208 // either.
209 ExpectFiltersNotConfigured();
210
211 ncsi_sm.run(total_num_states);
212
213 ExpectFiltersConfigured();
214}
215
216TEST_F(TestNcsi, TestRetest)
217{
218 ncsi_sm.run(total_num_states + test_num_states);
219
220 // Verify that the test state machine was stepped through twice,
221 // by counting how many times the last command of the state machine
222 // has been executed.
223 const uint8_t last_test_command = NCSI_GET_LINK_STATUS;
224 const auto& cmd_log = ncsi_sock.nic_mock.get_command_log();
225 int num_test_runs = 0;
226 for (const auto& ncsi_frame : cmd_log)
227 {
228 if (ncsi_frame.get_control_packet_type() == last_test_command)
229 {
230 ++num_test_runs;
231 }
232 }
233
234 EXPECT_EQ(num_test_runs, 2);
235}
236
237TEST_F(TestNcsi, TestHostlessSwitch)
238{
239 // By default the NIC is in hostless mode.
240 // Verify that net config flag changes after FSM run.
241 net_config_mock.is_nic_hostless = false;
242 ncsi_sm.run(total_num_states);
243 EXPECT_EQ(ncsi_sock.n_read_errs, 0);
244 EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
245 EXPECT_TRUE(net_config_mock.is_nic_hostless);
246
247 // Now disable the hostless mode and verify that net config
248 // flag changes to false.
249 ncsi_sock.nic_mock.set_hostless(false);
250 ncsi_sm.run(total_num_states);
251 EXPECT_EQ(ncsi_sock.n_read_errs, 0);
252 EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
253 EXPECT_FALSE(net_config_mock.is_nic_hostless);
254}