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