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 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 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 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 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 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 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 */ 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 */ 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 */ 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 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 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 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