blob: 6eabf5d0998c34a60078c5ddee6365edd37fc82a [file] [log] [blame]
William A. Kennington III7d6fa422021-02-08 17:04:02 -08001#include "ncsi_state_machine.h"
2
3#include "common_defs.h"
4#include "platforms/nemora/portable/default_addresses.h"
5#include "platforms/nemora/portable/ncsi_fsm.h"
6
7#include <arpa/inet.h>
8#include <netinet/ether.h>
9#include <unistd.h>
10
11#include <chrono>
12#include <cstdint>
13#include <cstdlib>
14#include <cstring>
15#include <iostream>
16#include <thread>
17
18#define ETHER_NCSI 0x88f8
19
20#define CPRINTF(...) fprintf(stderr, __VA_ARGS__)
21
22#ifdef NCSID_VERBOSE_LOGGING
23#define DEBUG_PRINTF printf
24#else
25#define DEBUG_PRINTF(...)
26#endif
27
28namespace ncsi
29{
30
31namespace
32{
33
34const char kStateFormat[] = "l2_config=%d/%d l3l4_config=%d/%d test=%d/%d";
35// This assumes that the number of states is < 100, so each number
36// in the format above does not take more than two characters to represent,
37// thus %d (two characters) is substituted for the number which is also
38// two characters max.
39constexpr auto kStateFormatLen = sizeof(kStateFormat);
40
41void snprintf_state(char* buffer, int size, const ncsi_state_t* state)
42{
43 (void)snprintf(buffer, size, kStateFormat, state->l2_config_state,
44 NCSI_STATE_L2_CONFIG_END, state->l3l4_config_state,
45 NCSI_STATE_L3L4_CONFIG_END, state->test_state,
46 NCSI_STATE_TEST_END);
47}
48
49void print_state(const ncsi_state_t& state)
50{
51 (void)state;
52 DEBUG_PRINTF(kStateFormat, state.l2_config_state, NCSI_STATE_L2_CONFIG_END,
53 state.l3l4_config_state, NCSI_STATE_L3L4_CONFIG_END,
54 state.test_state, NCSI_STATE_TEST_END);
55 DEBUG_PRINTF(" restart_delay_count=%d\n", state.restart_delay_count);
56}
57
58const uint8_t echo_pattern[NCSI_OEM_ECHO_PATTERN_SIZE] = {
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
60 0xFF, 0xFF, 0xFF, 0xFF, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A,
61 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0x12, 0x34, 0x56, 0x78,
62 0x9A, 0xBC, 0xDE, 0xF0, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
63
64} // namespace
65
66void StateMachine::reset()
67{
68 std::memset(&ncsi_state_, 0, sizeof(ncsi_state_));
69 ncsi_state_.restart_delay_count = NCSI_FSM_RESTART_DELAY_COUNT - 1;
70 network_debug_.ncsi.test.max_tries = MAX_TRIES;
71 // This needs to be initialized in the firmware.
72 network_debug_.ncsi.test.ch_under_test = 0;
73 network_debug_.ncsi.oem_filter_disable = false;
74
75 network_debug_.ncsi.pending_stop = false;
76 network_debug_.ncsi.enabled = true;
77 network_debug_.ncsi.loopback = false;
78}
79
80StateMachine::StateMachine()
81{
82 reset();
83 network_debug_.ncsi.pending_restart = true;
84 std::memcpy(network_debug_.ncsi.test.ping.tx, echo_pattern,
85 sizeof(echo_pattern));
86}
87
88size_t StateMachine::poll_l2_config()
89{
90 size_t len = 0;
91 mac_addr_t mac;
92 net_config_->get_mac_addr(&mac);
93 ncsi_response_type_t response_type = ncsi_fsm_poll_l2_config(
94 &ncsi_state_, &network_debug_, &ncsi_buf_, &mac);
95
96 auto* response = reinterpret_cast<ncsi_simple_response_t*>(ncsi_buf_.data);
97
98 if (response_type == NCSI_RESPONSE_ACK)
99 {
100 /* If the response is MAC response, some extra handling needed. */
101 if ((NCSI_RESPONSE | NCSI_OEM_COMMAND) ==
102 response->hdr.control_packet_type)
103 {
104 auto* oem_response =
105 reinterpret_cast<ncsi_oem_simple_response_t*>(ncsi_buf_.data);
106 if (oem_response->oem_header.oem_cmd ==
107 NCSI_OEM_COMMAND_GET_HOST_MAC)
108 {
109 net_config_->set_mac_addr(mac);
110 }
111 }
112 }
113 else if (NCSI_RESPONSE_NONE == response_type)
114 {
115 /* Buffer is ready to be sent. */
116 len = ncsi_buf_.len;
117 ncsi_buf_.len = 0;
118 }
119 else
120 {
121 report_ncsi_error(response_type);
122 }
123
124 return len;
125}
126
127size_t StateMachine::poll_simple(ncsi_simple_poll_f poll_func)
128{
129 mac_addr_t mac;
130 net_config_->get_mac_addr(&mac);
131 const uint16_t rx_port = DEFAULT_ADDRESSES_RX_PORT;
132
133 ncsi_response_type_t response_type =
134 poll_func(&ncsi_state_, &network_debug_, &ncsi_buf_, &mac, 0, rx_port);
135
136 auto* response = reinterpret_cast<ncsi_simple_response_t*>(ncsi_buf_.data);
137
138 size_t len = 0;
139 if (response_type == NCSI_RESPONSE_NONE)
140 {
141 /* Buffer is ready to be sent, or we are done. */
142 len = ncsi_buf_.len;
143 ncsi_buf_.len = 0;
144 }
145 else if (response->hdr.control_packet_type ==
146 (NCSI_RESPONSE | NCSI_GET_LINK_STATUS))
147 {
148 auto status_response =
149 reinterpret_cast<ncsi_link_status_response_t*>(response);
150 bool new_link_up = ntohl(status_response->link_status.link_status) &
151 NCSI_LINK_STATUS_UP;
152 if (!link_up_ || new_link_up != *link_up_)
153 {
154 CPRINTF("[NCSI link %s]\n", new_link_up ? "up" : "down");
155 link_up_ = new_link_up;
156 }
157 }
158 else if (response->hdr.control_packet_type ==
159 (NCSI_RESPONSE | NCSI_OEM_COMMAND))
160 {
161 auto* oem_response =
162 reinterpret_cast<ncsi_oem_simple_response_t*>(ncsi_buf_.data);
163 if (oem_response->oem_header.oem_cmd == NCSI_OEM_COMMAND_GET_FILTER)
164 {
165 bool new_hostless = ncsi_fsm_is_nic_hostless(&ncsi_state_);
166 if (!hostless_ || new_hostless != *hostless_)
167 {
168 CPRINTF("[NCSI nic %s]\n",
169 new_hostless ? "hostless" : "hostfull");
170 net_config_->set_nic_hostless(new_hostless);
171 hostless_ = new_hostless;
172 }
173 }
174 }
175 else if (response_type != NCSI_RESPONSE_ACK)
176 {
177 report_ncsi_error(response_type);
178 }
179
180 return len;
181}
182
183void StateMachine::report_ncsi_error(ncsi_response_type_t response_type)
184{
185 char state_string[kStateFormatLen];
186 snprintf_state(state_string, sizeof(state_string), &ncsi_state_);
187 auto* response =
188 reinterpret_cast<const ncsi_simple_response_t*>(ncsi_buf_.data);
189 switch (response_type)
190 {
191 case NCSI_RESPONSE_UNDERSIZED:
192 if (!ncsi_buf_.len)
193 {
194 network_debug_.ncsi.rx_error.timeout_count++;
195 CPRINTF("[NCSI timeout in state %s]\n", state_string);
196 }
197 else
198 {
199 network_debug_.ncsi.rx_error.undersized_count++;
200 CPRINTF("[NCSI undersized response in state %s]\n",
201 state_string);
202 }
203 break;
204 case NCSI_RESPONSE_NACK:
205 network_debug_.ncsi.rx_error.nack_count++;
206 CPRINTF(
207 "[NCSI nack in state %s. Response: 0x%04x Reason: 0x%04x]\n",
208 state_string, ntohs(response->response_code),
209 ntohs(response->reason_code));
210 break;
211 case NCSI_RESPONSE_UNEXPECTED_TYPE:
212 network_debug_.ncsi.rx_error.unexpected_type_count++;
213 CPRINTF("[NCSI unexpected response in state %s. Response type: "
214 "0x%02x]\n",
215 state_string, response->hdr.control_packet_type);
216 break;
217 case NCSI_RESPONSE_UNEXPECTED_SIZE:
218 {
219 int expected_size;
220 if ((NCSI_RESPONSE | NCSI_OEM_COMMAND) ==
221 response->hdr.control_packet_type)
222 {
223 auto* oem_response =
224 reinterpret_cast<ncsi_oem_simple_response_t*>(
225 ncsi_buf_.data);
226 expected_size = ncsi_oem_get_response_size(
227 oem_response->oem_header.oem_cmd);
228 }
229 else
230 {
231 expected_size = ncsi_get_response_size(
232 response->hdr.control_packet_type & (~NCSI_RESPONSE));
233 }
234 network_debug_.ncsi.rx_error.unexpected_size_count++;
235 CPRINTF("[NCSI unexpected response size in state %s."
236 " Expected %d]\n",
237 state_string, expected_size);
238 }
239 break;
240 case NCSI_RESPONSE_OEM_FORMAT_ERROR:
241 network_debug_.ncsi.rx_error.unexpected_type_count++;
242 CPRINTF("[NCSI OEM format error]\n");
243 break;
244 case NCSI_RESPONSE_UNEXPECTED_PARAMS:
245 CPRINTF("[NCSI OEM Filter MAC or TCP/IP Config Mismatch]\n");
246 break;
247 default:
248 /* NCSI_RESPONSE_ACK and NCSI_RESPONSE_NONE are not errors and need
249 * not be handled here, so this branch is just to complete the
250 * switch.
251 */
252 CPRINTF("[NCSI OK]\n");
253 break;
254 }
255}
256
257int StateMachine::receive_ncsi()
258{
259 int len;
260 do
261 {
262 // Return value of zero means timeout
263 len = sock_io_->recv(ncsi_buf_.data, sizeof(ncsi_buf_.data));
264 if (len > 0)
265 {
266 auto* hdr = reinterpret_cast<struct ether_header*>(ncsi_buf_.data);
267 if (ETHER_NCSI == ntohs(hdr->ether_type))
268 {
269 ncsi_buf_.len = len;
270 break;
271 }
272 }
273
274 ncsi_buf_.len = 0;
275 } while (len > 0);
276
277 return ncsi_buf_.len;
278}
279
280void StateMachine::run_test_fsm(size_t* tx_len)
281{
282 // Sleep and restart when test FSM finishes.
283 if (is_test_done())
284 {
285 std::this_thread::sleep_for(std::chrono::seconds(retest_delay_s_));
286 // Skip over busy wait in state machine - already waited.
287 ncsi_state_.retest_delay_count = NCSI_FSM_RESTART_DELAY_COUNT;
288 }
289 // until NCSI_STATE_TEST_END
290 *tx_len = poll_simple(ncsi_fsm_poll_test);
291}
292
293void StateMachine::run(int max_rounds)
294{
295 if (!net_config_ || !sock_io_)
296 {
297 CPRINTF("StateMachine configuration incomplete: "
298 "net_config_: <%p>, sock_io_: <%p>",
299 reinterpret_cast<void*>(net_config_),
300 reinterpret_cast<void*>(sock_io_));
301 return;
302 }
303
304 bool infinite_loop = (max_rounds <= 0);
305 while (infinite_loop || --max_rounds >= 0)
306 {
307 receive_ncsi();
308
309 size_t tx_len = 0;
310 switch (ncsi_fsm_connection_state(&ncsi_state_, &network_debug_))
311 {
312 case NCSI_CONNECTION_DOWN:
313 case NCSI_CONNECTION_LOOPBACK:
314 tx_len = poll_l2_config();
315 break;
316 case NCSI_CONNECTION_UP:
317 if (!is_test_done() || ncsi_fsm_is_nic_hostless(&ncsi_state_))
318 {
319 run_test_fsm(&tx_len);
320 }
321 else
322 {
323 // - Only start L3/L4 config when test is finished
324 // (it will last until success
325 // (i.e. NCSI_CONNECTION_UP_AND_CONFIGURED) or fail.
326 tx_len = poll_simple(ncsi_fsm_poll_l3l4_config);
327 }
328 break;
329 case NCSI_CONNECTION_UP_AND_CONFIGURED:
330 run_test_fsm(&tx_len);
331 break;
332 case NCSI_CONNECTION_DISABLED:
333 if (network_debug_.ncsi.pending_restart)
334 {
335 network_debug_.ncsi.enabled = true;
336 }
337 break;
338 default:
339 fail();
340 }
341
342 if (tx_len)
343 {
344 print_state(ncsi_state_);
345
346 sock_io_->write(ncsi_buf_.data, tx_len);
347 }
348 }
349}
350
351void StateMachine::clear_state()
352{
353 // This implicitly resets:
354 // l2_config_state to NCSI_STATE_L2_CONFIG_BEGIN
355 // l3l4_config_state to NCSI_STATE_L3L4_CONFIG_BEGIN
356 // test_state to NCSI_STATE_TEST_BEGIN
357 std::memset(&ncsi_state_, 0, sizeof(ncsi_state_));
358}
359
360void StateMachine::fail()
361{
362 network_debug_.ncsi.fail_count++;
363 clear_state();
364}
365
366void StateMachine::set_sockio(net::SockIO* sock_io)
367{
368 sock_io_ = sock_io;
369}
370
371void StateMachine::set_net_config(net::ConfigBase* net_config)
372{
373 net_config_ = net_config;
374}
375
376void StateMachine::set_retest_delay(unsigned delay)
377{
378 retest_delay_s_ = delay;
379}
380
381} // namespace ncsi