1 #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 28 namespace ncsi 29 { 30 31 namespace 32 { 33 34 const 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. 39 constexpr auto kStateFormatLen = sizeof(kStateFormat); 40 41 void 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 49 void 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 58 const 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 66 void 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 80 StateMachine::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 88 size_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 127 size_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 183 void 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 257 int 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 280 void 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 293 void 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 351 void 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 360 void StateMachine::fail() 361 { 362 network_debug_.ncsi.fail_count++; 363 clear_state(); 364 } 365 366 void StateMachine::set_sockio(net::SockIO* sock_io) 367 { 368 sock_io_ = sock_io; 369 } 370 371 void StateMachine::set_net_config(net::ConfigBase* net_config) 372 { 373 net_config_ = net_config; 374 } 375 376 void StateMachine::set_retest_delay(unsigned delay) 377 { 378 retest_delay_s_ = delay; 379 } 380 381 } // namespace ncsi 382