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