1 /*
2 * Copyright 2021 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <string.h>
18
19 #include <netinet/in.h>
20
21 #include "platforms/nemora/portable/ncsi.h"
22 #include "platforms/nemora/portable/ncsi_client.h"
23 #include "platforms/nemora/portable/ncsi_fsm.h"
24
25 #define GO_TO_STATE(variable, state) do { *variable = state; } while (0)
26 #define GO_TO_NEXT_STATE(variable) do { (*variable)++; } while (0)
27
28 // TODO - This state machine needs to be rewritten, now that we have a
29 // better idea of the states and transitions involved.
30 // The NC-SI related states of the state machine are currently organized in
31 // request/response pairs. However when I added support for the second channel
32 // this resulted in more hard-coded pairs which worked okay for X
33 // (despite some ugliness, see ch_under_test below) but broke down for X
34 // since it only supports 1 channel. For now just add a little more ugliness
35 // by stepping by 1 or 3 when going from a pair to the next depending on whether
36 // the second channel is supported (1) or not (3 - skip over the second channel
37 // pair).
38 #define GO_TO_NEXT_CHANNEL(variable, ncsi_state)\
39 do { *variable += (ncsi_state->channel_count == 1)\
40 ? 3 : 1; } while (0)
41
ncsi_fsm_clear_state(ncsi_state_t * ncsi_state)42 static void ncsi_fsm_clear_state(ncsi_state_t* ncsi_state) {
43 // This implicitly resets:
44 // l2_config_state to NCSI_STATE_L2_CONFIG_BEGIN
45 // l3l4_config_state to NCSI_STATE_L3L4_CONFIG_BEGIN
46 // test_state to NCSI_STATE_TEST_BEGIN
47 memset(ncsi_state, 0, sizeof(ncsi_state_t));
48 }
49
ncsi_fsm_fail(ncsi_state_t * ncsi_state,network_debug_t * network_debug)50 static void ncsi_fsm_fail(ncsi_state_t* ncsi_state,
51 network_debug_t* network_debug) {
52 network_debug->ncsi.fail_count++;
53 memcpy(&network_debug->ncsi.state_that_failed, ncsi_state,
54 sizeof(network_debug->ncsi.state_that_failed));
55 ncsi_fsm_clear_state(ncsi_state);
56 }
57
ncsi_fsm_connection_state(const ncsi_state_t * ncsi_state,const network_debug_t * network_debug)58 ncsi_connection_state_t ncsi_fsm_connection_state(
59 const ncsi_state_t* ncsi_state, const network_debug_t* network_debug) {
60 if (!network_debug->ncsi.enabled) {
61 return NCSI_CONNECTION_DISABLED;
62 }
63 if (ncsi_state->l2_config_state != NCSI_STATE_L2_CONFIG_END) {
64 if (network_debug->ncsi.loopback) {
65 return NCSI_CONNECTION_LOOPBACK;
66 } else {
67 return NCSI_CONNECTION_DOWN;
68 }
69 }
70 if (ncsi_state->l3l4_config_state != NCSI_STATE_L3L4_CONFIG_END) {
71 return NCSI_CONNECTION_UP;
72 }
73 return NCSI_CONNECTION_UP_AND_CONFIGURED;
74 }
75
ncsi_fsm_poll_l2_config(ncsi_state_t * ncsi_state,network_debug_t * network_debug,ncsi_buf_t * ncsi_buf,mac_addr_t * mac)76 ncsi_response_type_t ncsi_fsm_poll_l2_config(ncsi_state_t* ncsi_state,
77 network_debug_t* network_debug,
78 ncsi_buf_t* ncsi_buf,
79 mac_addr_t* mac) {
80 ncsi_l2_config_state_t* const state_variable = &ncsi_state->l2_config_state;
81 ncsi_response_type_t ncsi_response_type = NCSI_RESPONSE_NONE;
82 uint32_t len = 0;
83
84 switch(*state_variable) {
85 case NCSI_STATE_RESTART:
86 if (++ncsi_state->restart_delay_count >= NCSI_FSM_RESTART_DELAY_COUNT) {
87 network_debug->ncsi.pending_restart = false;
88 GO_TO_NEXT_STATE(state_variable);
89 ncsi_state->restart_delay_count = 0;
90 }
91 break;
92 case NCSI_STATE_CLEAR_0: // necessary to get mac
93 len = ncsi_cmd_clear_initial_state(ncsi_buf->data, CHANNEL_0_ID);
94 GO_TO_NEXT_STATE(state_variable);
95 break;
96 case NCSI_STATE_CLEAR_0_RESPONSE:
97 {
98 bool loopback = false;
99 ncsi_response_type = ncsi_validate_std_response(
100 ncsi_buf->data, ncsi_buf->len, NCSI_CLEAR_INITIAL_STATE);
101 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
102 GO_TO_NEXT_STATE(state_variable);
103 } else {
104 // If we did not receive a response but we did receive something,
105 // then maybe there is a physical loopback, so check that we received
106 // exactly what we sent
107 if (ncsi_buf->len >= sizeof(ncsi_simple_command_t)) {
108 ncsi_simple_command_t expected_loopback_data;
109 (void)ncsi_cmd_clear_initial_state((uint8_t*)&expected_loopback_data,
110 CHANNEL_0_ID);
111 if (0 == memcmp((uint8_t*)&expected_loopback_data,
112 ncsi_buf->data, sizeof(expected_loopback_data))) {
113 loopback = true;
114 }
115 }
116 ncsi_fsm_fail(ncsi_state, network_debug);
117 }
118 network_debug->ncsi.loopback = loopback;
119 }
120 break;
121 case NCSI_STATE_GET_VERSION:
122 len = ncsi_cmd_get_version(ncsi_buf->data, CHANNEL_0_ID);
123 GO_TO_NEXT_STATE(state_variable);
124 break;
125 case NCSI_STATE_GET_VERSION_RESPONSE:
126 ncsi_response_type = ncsi_validate_std_response(
127 ncsi_buf->data, ncsi_buf->len, NCSI_GET_VERSION_ID);
128 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
129 ncsi_version_id_response_t* get_version_response =
130 (ncsi_version_id_response_t*)ncsi_buf->data;
131 // TODO - Add check for this being actually X
132 network_debug->ncsi.mlx_legacy =
133 ((ntohl(get_version_response->version.firmware_version) >> 24) ==
134 0x08);
135 GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
136 } else {
137 ncsi_fsm_fail(ncsi_state, network_debug);
138 }
139 break;
140 case NCSI_STATE_GET_CAPABILITIES:
141 len = ncsi_cmd_get_capabilities(ncsi_buf->data, CHANNEL_0_ID);
142 GO_TO_NEXT_STATE(state_variable);
143 break;
144 case NCSI_STATE_GET_CAPABILITIES_RESPONSE:
145 ncsi_response_type = ncsi_validate_std_response(
146 ncsi_buf->data, ncsi_buf->len, NCSI_GET_CAPABILITIES);
147 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
148 const ncsi_capabilities_response_t* get_capabilities_response =
149 (ncsi_capabilities_response_t*) ncsi_buf->data;
150 if (1 != get_capabilities_response->channel_count &&
151 2 != get_capabilities_response->channel_count) {
152 /* TODO: Return Error
153 CPRINT("[NCSI Unsupported channel count {}]\n",
154 get_capabilities_response->channel_count);
155 */
156 ncsi_fsm_fail(ncsi_state, network_debug);
157 } else {
158 ncsi_state->channel_count =
159 get_capabilities_response->channel_count;
160 GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
161 }
162 } else{
163 ncsi_fsm_fail(ncsi_state, network_debug);
164 }
165 break;
166 case NCSI_STATE_CLEAR_1:
167 len = ncsi_cmd_clear_initial_state(ncsi_buf->data, CHANNEL_1_ID);
168 GO_TO_NEXT_STATE(state_variable);
169 break;
170 case NCSI_STATE_CLEAR_1_RESPONSE:
171 ncsi_response_type = ncsi_validate_std_response(
172 ncsi_buf->data, ncsi_buf->len, NCSI_CLEAR_INITIAL_STATE);
173 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
174 GO_TO_NEXT_STATE(state_variable);
175 } else {
176 ncsi_fsm_fail(ncsi_state, network_debug);
177 }
178 break;
179 case NCSI_STATE_RESET_CHANNEL_0:
180 if (network_debug->ncsi.pending_stop) {
181 len = ncsi_cmd_reset_channel(ncsi_buf->data, CHANNEL_0_ID);
182 GO_TO_NEXT_STATE(state_variable);
183 } else {
184 // skip resetting channels
185 GO_TO_STATE(state_variable, NCSI_STATE_GET_MAC);
186 }
187 break;
188 case NCSI_STATE_RESET_CHANNEL_0_RESPONSE:
189 ncsi_response_type = ncsi_validate_std_response(
190 ncsi_buf->data, ncsi_buf->len, NCSI_RESET_CHANNEL);
191 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
192 GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
193 } else {
194 ncsi_fsm_fail(ncsi_state, network_debug);
195 }
196 break;
197 case NCSI_STATE_RESET_CHANNEL_1:
198 len = ncsi_cmd_reset_channel(ncsi_buf->data, CHANNEL_1_ID);
199 GO_TO_NEXT_STATE(state_variable);
200 break;
201 case NCSI_STATE_RESET_CHANNEL_1_RESPONSE:
202 ncsi_response_type = ncsi_validate_std_response(
203 ncsi_buf->data, ncsi_buf->len, NCSI_RESET_CHANNEL);
204 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
205 GO_TO_NEXT_STATE(state_variable);
206 } else {
207 ncsi_fsm_fail(ncsi_state, network_debug);
208 }
209 break;
210 case NCSI_STATE_STOPPED:
211 network_debug->ncsi.pending_stop = false;
212 // Reset the L2 config state machine through fail(). This state machine
213 // will not be executed again so long as 'enabled' is false.
214 network_debug->ncsi.enabled = false;
215 ncsi_fsm_fail(ncsi_state, network_debug);
216 break;
217 // TODO: Add check for MFG ID and firmware version before trying
218 // any OEM commands.
219 case NCSI_STATE_GET_MAC:
220 // Only get MAC from channel 0, because that's the one that identifies the
221 // host machine (for both MDB and DHCP).
222 len = ncsi_oem_cmd_get_host_mac(ncsi_buf->data, CHANNEL_0_ID);
223 GO_TO_NEXT_STATE(state_variable);
224 break;
225 case NCSI_STATE_GET_MAC_RESPONSE:
226 ncsi_response_type = ncsi_validate_oem_response(
227 ncsi_buf->data, ncsi_buf->len, NCSI_OEM_COMMAND_GET_HOST_MAC);
228 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
229 ncsi_host_mac_response_t* get_mac_response =
230 (ncsi_host_mac_response_t*) ncsi_buf->data;
231 memcpy(mac->octet, get_mac_response->mac, sizeof(mac_addr_t));
232 GO_TO_NEXT_STATE(state_variable);
233 } else {
234 ncsi_fsm_fail(ncsi_state, network_debug);
235 }
236 break;
237 case NCSI_STATE_SET_MAC_FILTER_0:
238 len = ncsi_cmd_set_mac(ncsi_buf->data, CHANNEL_0_ID, mac);
239 GO_TO_NEXT_STATE(state_variable);
240 break;
241 case NCSI_STATE_SET_MAC_FILTER_0_RESPONSE:
242 ncsi_response_type = ncsi_validate_std_response(
243 ncsi_buf->data, ncsi_buf->len, NCSI_SET_MAC_ADDRESS);
244 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
245 GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
246 } else{
247 ncsi_fsm_fail(ncsi_state, network_debug);
248 }
249 break;
250 case NCSI_STATE_SET_MAC_FILTER_1:
251 len = ncsi_cmd_set_mac(ncsi_buf->data, CHANNEL_1_ID, mac);
252 GO_TO_NEXT_STATE(state_variable);
253 break;
254 case NCSI_STATE_SET_MAC_FILTER_1_RESPONSE:
255 ncsi_response_type = ncsi_validate_std_response(
256 ncsi_buf->data, ncsi_buf->len, NCSI_SET_MAC_ADDRESS);
257 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
258 GO_TO_NEXT_STATE(state_variable);
259 } else{
260 ncsi_fsm_fail(ncsi_state, network_debug);
261 }
262 break;
263 case NCSI_STATE_ENABLE_CHANNEL_0:
264 len = ncsi_cmd_enable_channel(ncsi_buf->data, CHANNEL_0_ID);
265 GO_TO_NEXT_STATE(state_variable);
266 break;
267 case NCSI_STATE_ENABLE_CHANNEL_0_RESPONSE:
268 ncsi_response_type = ncsi_validate_std_response(
269 ncsi_buf->data, ncsi_buf->len, NCSI_ENABLE_CHANNEL);
270 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
271 GO_TO_NEXT_CHANNEL(state_variable, ncsi_state);
272 } else{
273 ncsi_fsm_fail(ncsi_state, network_debug);
274 }
275 break;
276 case NCSI_STATE_ENABLE_CHANNEL_1:
277 len = ncsi_cmd_enable_channel(ncsi_buf->data, CHANNEL_1_ID);
278 GO_TO_NEXT_STATE(state_variable);
279 break;
280 case NCSI_STATE_ENABLE_CHANNEL_1_RESPONSE:
281 ncsi_response_type = ncsi_validate_std_response(
282 ncsi_buf->data, ncsi_buf->len, NCSI_ENABLE_CHANNEL);
283 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
284 GO_TO_NEXT_STATE(state_variable);
285 } else{
286 ncsi_fsm_fail(ncsi_state, network_debug);
287 }
288 break;
289 // TODO: Enable broadcast filter to block ARP.
290 case NCSI_STATE_ENABLE_TX:
291 // The NIC FW transmits all passthrough TX on the lowest enabled channel,
292 // so there is no point in enabling TX on the second channel.
293 // TODO: - In the future we may add a check for link status,
294 // in which case we may want to intelligently disable ch.0
295 // (if down) and enable ch.1
296 len = ncsi_cmd_enable_tx(ncsi_buf->data, CHANNEL_0_ID);
297 GO_TO_NEXT_STATE(state_variable);
298 break;
299 case NCSI_STATE_ENABLE_TX_RESPONSE:
300 ncsi_response_type = ncsi_validate_std_response(
301 ncsi_buf->data, ncsi_buf->len, NCSI_ENABLE_CHANNEL_NETWORK_TX);
302 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
303 GO_TO_NEXT_STATE(state_variable);
304 } else{
305 ncsi_fsm_fail(ncsi_state, network_debug);
306 }
307 break;
308 case NCSI_STATE_L2_CONFIG_END:
309 // Done
310 break;
311 default:
312 ncsi_fsm_fail(ncsi_state, network_debug);
313 break;
314 }
315
316 ncsi_buf->len = len;
317 return ncsi_response_type;
318 }
319
write_ncsi_oem_config_filter(uint8_t * buffer,uint8_t channel,network_debug_t * network_debug,mac_addr_t * mac,uint32_t ipv4_addr,uint16_t rx_port)320 static uint32_t write_ncsi_oem_config_filter(uint8_t* buffer, uint8_t channel,
321 network_debug_t* network_debug,
322 mac_addr_t* mac,
323 uint32_t ipv4_addr,
324 uint16_t rx_port) {
325 uint32_t len;
326 (void)ipv4_addr;
327 if (network_debug->ncsi.oem_filter_disable) {
328 mac_addr_t zero_mac = {.octet = {0,}};
329 len = ncsi_oem_cmd_set_filter(buffer, channel, &zero_mac, 0, 0, 0);
330
331 } else {
332 len = ncsi_oem_cmd_set_filter(buffer, channel, mac, 0, rx_port, 1);
333 }
334 return len;
335 }
336
ncsi_fsm_poll_l3l4_config(ncsi_state_t * ncsi_state,network_debug_t * network_debug,ncsi_buf_t * ncsi_buf,mac_addr_t * mac,uint32_t ipv4_addr,uint16_t rx_port)337 ncsi_response_type_t ncsi_fsm_poll_l3l4_config(ncsi_state_t* ncsi_state,
338 network_debug_t* network_debug,
339 ncsi_buf_t* ncsi_buf,
340 mac_addr_t* mac,
341 uint32_t ipv4_addr,
342 uint16_t rx_port) {
343 uint32_t len = 0;
344 ncsi_response_type_t ncsi_response_type = NCSI_RESPONSE_NONE;
345
346 if (ncsi_state->l3l4_config_state == NCSI_STATE_L3L4_CONFIG_BEGIN) {
347 ncsi_state->l3l4_channel = 0;
348 ncsi_state->l3l4_waiting_response = false;
349 ncsi_state->l3l4_config_state = NCSI_STATE_CONFIG_FILTERS;
350 }
351
352 /* Go through every state with every channel. */
353 if (ncsi_state->l3l4_waiting_response) {
354 ncsi_response_type = ncsi_validate_oem_response(
355 ncsi_buf->data, ncsi_buf->len, ncsi_state->l3l4_command);
356 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
357 /* Current channel ACK'ed, go to the next one. */
358 ncsi_state->l3l4_channel++;
359 if (ncsi_state->l3l4_channel >= ncsi_state->channel_count) {
360 /* All channels done, reset channel number and go to the next state.
361 * NOTE: This assumes that state numbers are sequential.*/
362 ncsi_state->l3l4_config_state += 1;
363 ncsi_state->l3l4_channel = 0;
364 }
365 } else {
366 ncsi_fsm_fail(ncsi_state, network_debug);
367 }
368
369 ncsi_state->l3l4_waiting_response = false;
370 } else {
371 // Send appropriate command.
372 switch(ncsi_state->l3l4_config_state) {
373 case NCSI_STATE_CONFIG_FILTERS:
374 len =
375 write_ncsi_oem_config_filter(ncsi_buf->data, ncsi_state->l3l4_channel,
376 network_debug, mac, ipv4_addr, rx_port);
377 ncsi_state->l3l4_command = NCSI_OEM_COMMAND_SET_FILTER;
378 ncsi_state->l3l4_waiting_response = true;
379 break;
380 default:
381 ncsi_fsm_fail(ncsi_state, network_debug);
382 break;
383 }
384 }
385
386 ncsi_buf->len = len;
387 return ncsi_response_type;
388 }
389
390 /*
391 * Start a sub-section of the state machine that runs health checks.
392 * This is dependent on the NC-SI configuration being completed
393 * (e.g. ncsi_channel_count must be known).
394 */
ncsi_fsm_start_test(network_debug_t * network_debug,uint8_t channel_count)395 static bool ncsi_fsm_start_test(network_debug_t* network_debug,
396 uint8_t channel_count) {
397 if (network_debug->ncsi.test.max_tries > 0) {
398 network_debug->ncsi.test.runs++;
399 if (2 == channel_count) {
400 network_debug->ncsi.test.ch_under_test ^= 1;
401 } else {
402 network_debug->ncsi.test.ch_under_test = 0;
403 }
404 return true;
405 }
406 return false;
407 }
408
409 /*
410 * Allow for a limited number of retries for the NC-SI test because
411 * it can fail under heavy TCP/IP load (since NC-SI responses share
412 * the RX buffers in chip/$(CHIP)/net.c with TCP/IP incoming traffic).
413 */
ncsi_fsm_retry_test(network_debug_t * network_debug)414 static bool ncsi_fsm_retry_test(network_debug_t* network_debug) {
415 const uint8_t max_tries = network_debug->ncsi.test.max_tries;
416 if (max_tries) {
417 uint8_t remaining_tries = max_tries - 1 - network_debug->ncsi.test.tries;
418 if (remaining_tries > 0) {
419 network_debug->ncsi.test.tries++;
420 return true;
421 }
422 }
423 network_debug->ncsi.test.tries = 0;
424 return false;
425 }
426
427 /*
428 * Returns true if we have executed an NC-SI Get OEM Filter command for all
429 * channels and the flags indicate that it is running in hostless mode.
430 * This means that we can DHCP/ARP if needed.
431 * Otherwise returns false.
432 *
433 * NOTE: We default to false, if we cannot complete the L2 config state
434 * machine or the test sequence.
435 */
ncsi_fsm_is_nic_hostless(const ncsi_state_t * ncsi_state)436 bool ncsi_fsm_is_nic_hostless(const ncsi_state_t* ncsi_state) {
437 uint8_t flags = ncsi_state->flowsteering[0].flags;
438 if (ncsi_state->channel_count > 1) {
439 flags &= ncsi_state->flowsteering[1].flags;
440 }
441 return flags & NCSI_OEM_FILTER_FLAGS_HOSTLESS;
442 }
443
ncsi_fsm_update_passthrough_stats(const ncsi_passthrough_stats_t * increment,network_debug_t * network_debug)444 static void ncsi_fsm_update_passthrough_stats(
445 const ncsi_passthrough_stats_t* increment, network_debug_t* network_debug) {
446 ncsi_passthrough_stats_t* accumulated =
447 &network_debug->ncsi.pt_stats_be[network_debug->ncsi.test.ch_under_test];
448 #define ACCUMULATE_PT_STATS(stat) accumulated->stat += increment->stat;
449 ACCUMULATE_PT_STATS(tx_packets_received_hi);
450 ACCUMULATE_PT_STATS(tx_packets_received_lo);
451 ACCUMULATE_PT_STATS(tx_packets_dropped);
452 ACCUMULATE_PT_STATS(tx_channel_errors);
453 ACCUMULATE_PT_STATS(tx_undersized_errors);
454 ACCUMULATE_PT_STATS(tx_oversized_errors);
455 ACCUMULATE_PT_STATS(rx_packets_received);
456 ACCUMULATE_PT_STATS(rx_packets_dropped);
457 ACCUMULATE_PT_STATS(rx_channel_errors);
458 ACCUMULATE_PT_STATS(rx_undersized_errors);
459 ACCUMULATE_PT_STATS(rx_oversized_errors);
460 #undef ACCUMULATE_PT_STATS
461 }
462
ncsi_fsm_update_passthrough_stats_legacy(const ncsi_passthrough_stats_legacy_t * read,network_debug_t * network_debug)463 static void ncsi_fsm_update_passthrough_stats_legacy(
464 const ncsi_passthrough_stats_legacy_t* read,
465 network_debug_t* network_debug) {
466 // Legacy MLX response does not include tx_packets_received_hi and also MLX
467 // counters
468 // are not reset on read (i.e. we cannot accumulate them).
469 ncsi_passthrough_stats_t* accumulated =
470 &network_debug->ncsi.pt_stats_be[network_debug->ncsi.test.ch_under_test];
471 accumulated->tx_packets_received_hi = 0;
472 accumulated->tx_packets_received_lo = read->tx_packets_received;
473 #define COPY_PT_STATS(stat) accumulated->stat = read->stat;
474 COPY_PT_STATS(tx_packets_dropped);
475 COPY_PT_STATS(tx_channel_errors);
476 COPY_PT_STATS(tx_undersized_errors);
477 COPY_PT_STATS(tx_oversized_errors);
478 COPY_PT_STATS(rx_packets_received);
479 COPY_PT_STATS(rx_packets_dropped);
480 COPY_PT_STATS(rx_channel_errors);
481 COPY_PT_STATS(rx_undersized_errors);
482 COPY_PT_STATS(rx_oversized_errors);
483 #undef COPY_PT_STATS
484 }
485
ncsi_fsm_poll_test(ncsi_state_t * ncsi_state,network_debug_t * network_debug,ncsi_buf_t * ncsi_buf,mac_addr_t * mac,uint32_t ipv4_addr,uint16_t rx_port)486 ncsi_response_type_t ncsi_fsm_poll_test(ncsi_state_t* ncsi_state,
487 network_debug_t* network_debug,
488 ncsi_buf_t* ncsi_buf, mac_addr_t* mac,
489 uint32_t ipv4_addr, uint16_t rx_port) {
490 ncsi_test_state_t* const state_variable =
491 &ncsi_state->test_state;
492 ncsi_response_type_t ncsi_response_type = NCSI_RESPONSE_NONE;
493 uint32_t len = 0;
494
495 switch(*state_variable) {
496 case NCSI_STATE_TEST_PARAMS:
497 if (ncsi_fsm_start_test(network_debug, ncsi_state->channel_count)) {
498 GO_TO_NEXT_STATE(state_variable);
499 } else {
500 // debugging only - skip test by setting max_tries to 0
501 GO_TO_STATE(state_variable, NCSI_STATE_TEST_END);
502 }
503 break;
504 case NCSI_STATE_ECHO:
505 len = ncsi_oem_cmd_echo(ncsi_buf->data,
506 network_debug->ncsi.test.ch_under_test,
507 network_debug->ncsi.test.ping.tx);
508 network_debug->ncsi.test.ping.tx_count++;
509 GO_TO_NEXT_STATE(state_variable);
510 break;
511 case NCSI_STATE_ECHO_RESPONSE:
512 ncsi_response_type = ncsi_validate_oem_response(
513 ncsi_buf->data, ncsi_buf->len, NCSI_OEM_COMMAND_ECHO);
514 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
515 network_debug->ncsi.test.ping.rx_count++;
516 ncsi_oem_echo_response_t* echo_response =
517 (ncsi_oem_echo_response_t*) ncsi_buf->data;
518 if (0 == memcmp(echo_response->pattern,
519 network_debug->ncsi.test.ping.tx,
520 sizeof(echo_response->pattern))) {
521 GO_TO_NEXT_STATE(state_variable);
522 break;
523 } else {
524 network_debug->ncsi.test.ping.bad_rx_count++;
525 memcpy(network_debug->ncsi.test.ping.last_bad_rx,
526 echo_response->pattern,
527 sizeof(network_debug->ncsi.test.ping.last_bad_rx));
528 }
529 }
530 if (ncsi_fsm_retry_test(network_debug)) {
531 GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
532 } else {
533 ncsi_fsm_fail(ncsi_state, network_debug);
534 }
535 break;
536 case NCSI_STATE_CHECK_FILTERS:
537 len = ncsi_oem_cmd_get_filter(ncsi_buf->data,
538 network_debug->ncsi.test.ch_under_test);
539 GO_TO_NEXT_STATE(state_variable);
540 break;
541 case NCSI_STATE_CHECK_FILTERS_RESPONSE:
542 ncsi_response_type = ncsi_validate_oem_response(
543 ncsi_buf->data, ncsi_buf->len, NCSI_OEM_COMMAND_GET_FILTER);
544 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
545 ncsi_oem_get_filter_response_t* get_filter_response =
546 (ncsi_oem_get_filter_response_t*) ncsi_buf->data;
547 // Stash away response because it contains information about NIC mode
548 memcpy((void*)ncsi_state->flowsteering[
549 network_debug->ncsi.test.ch_under_test].regid,
550 (void*)get_filter_response->filter.regid,
551 sizeof(ncsi_state->flowsteering[0].regid));
552 ncsi_state->flowsteering[
553 network_debug->ncsi.test.ch_under_test].flags =
554 get_filter_response->filter.flags;
555 // Test filter parameters only if we know that we configured the NIC,
556 // and if the NIC is in host-based mode (it appears to return all zeros's
557 // in hostless mode!).
558 if (NCSI_STATE_L3L4_CONFIG_END != ncsi_state->l3l4_config_state ||
559 ncsi_fsm_is_nic_hostless(ncsi_state)) {
560 GO_TO_NEXT_STATE(state_variable);
561 break;
562 }
563 ncsi_oem_set_filter_cmd_t expected;
564 (void)write_ncsi_oem_config_filter(
565 (uint8_t*)&expected, network_debug->ncsi.test.ch_under_test,
566 network_debug, mac, ipv4_addr, rx_port);
567 /* TODO: handle these responses in error reporting routine */
568 if (0 != memcmp((void*)&get_filter_response->filter.mac,
569 (void*)&expected.filter.mac,
570 sizeof(expected.filter.mac))) {
571 ncsi_response_type = NCSI_RESPONSE_UNEXPECTED_PARAMS;
572 } else if (get_filter_response->filter.ip != expected.filter.ip ||
573 get_filter_response->filter.port != expected.filter.port) {
574 ncsi_response_type = NCSI_RESPONSE_UNEXPECTED_PARAMS;
575 } else {
576 GO_TO_NEXT_STATE(state_variable);
577 break;
578 }
579 }
580 if (ncsi_fsm_retry_test(network_debug)) {
581 GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
582 } else {
583 ncsi_fsm_fail(ncsi_state, network_debug);
584 }
585 break;
586 case NCSI_STATE_GET_PT_STATS:
587 len = ncsi_cmd_get_passthrough_stats(
588 ncsi_buf->data, network_debug->ncsi.test.ch_under_test);
589 GO_TO_NEXT_STATE(state_variable);
590 break;
591 case NCSI_STATE_GET_PT_STATS_RESPONSE:
592 if (!network_debug->ncsi.mlx_legacy) {
593 ncsi_response_type = ncsi_validate_std_response(
594 ncsi_buf->data, ncsi_buf->len, NCSI_GET_PASSTHROUGH_STATISTICS);
595 if (ncsi_response_type == NCSI_RESPONSE_ACK) {
596 const ncsi_passthrough_stats_response_t* response =
597 (const ncsi_passthrough_stats_response_t*)ncsi_buf->data;
598 ncsi_fsm_update_passthrough_stats(&response->stats, network_debug);
599 GO_TO_NEXT_STATE(state_variable);
600 break;
601 }
602 } else {
603 uint32_t response_size =
604 ncsi_get_response_size(NCSI_GET_PASSTHROUGH_STATISTICS) -
605 sizeof(uint32_t);
606 ncsi_response_type = ncsi_validate_response(
607 ncsi_buf->data, ncsi_buf->len, NCSI_GET_PASSTHROUGH_STATISTICS, false,
608 response_size);
609 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
610 const ncsi_passthrough_stats_legacy_response_t* legacy_response =
611 (const ncsi_passthrough_stats_legacy_response_t*)ncsi_buf->data;
612 ncsi_fsm_update_passthrough_stats_legacy(&legacy_response->stats,
613 network_debug);
614 GO_TO_NEXT_STATE(state_variable);
615 break;
616 }
617 }
618 if (ncsi_fsm_retry_test(network_debug)) {
619 GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
620 } else {
621 ncsi_fsm_fail(ncsi_state, network_debug);
622 }
623 break;
624 case NCSI_STATE_GET_LINK_STATUS:
625 // We only care about ch.0 link status because that's the only one we use
626 // to transmit.
627 len = ncsi_cmd_get_link_status(ncsi_buf->data, 0);
628 GO_TO_NEXT_STATE(state_variable);
629 break;
630 case NCSI_STATE_GET_LINK_STATUS_RESPONSE:
631 ncsi_response_type = ncsi_validate_std_response(
632 ncsi_buf->data, ncsi_buf->len, NCSI_GET_LINK_STATUS);
633 if (NCSI_RESPONSE_ACK == ncsi_response_type) {
634 const ncsi_link_status_response_t* response =
635 (const ncsi_link_status_response_t*)ncsi_buf->data;
636 const uint32_t link_status = ntohl(response->link_status.link_status);
637 if (link_status & NCSI_LINK_STATUS_UP) {
638 GO_TO_NEXT_STATE(state_variable);
639 break;
640 }
641 // TODO: report this error.
642 // CPRINT("[NCSI Link Status down {:#08x}]\n", link_status);
643 }
644 if (ncsi_fsm_retry_test(network_debug)) {
645 GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
646 } else {
647 ncsi_fsm_fail(ncsi_state, network_debug);
648 }
649 break;
650 case NCSI_STATE_TEST_END:
651 network_debug->ncsi.test.tries = 0;
652 if (network_debug->ncsi.pending_restart) {
653 ncsi_fsm_fail(ncsi_state, network_debug); // (Ab)use fail to restart.
654 }
655 if (++ncsi_state->retest_delay_count >= NCSI_FSM_RETEST_DELAY_COUNT) {
656 GO_TO_STATE(state_variable, NCSI_STATE_TEST_BEGIN);
657 ncsi_state->retest_delay_count = 0;
658 }
659 break;
660 default:
661 ncsi_fsm_fail(ncsi_state, network_debug);
662 break;
663 }
664
665 ncsi_buf->len = len;
666 return ncsi_response_type;
667 }
668