blob: 6486548085b1ff20c93029bdb64482b8ba67b5ab [file] [log] [blame]
Brandon Kimdab96f12021-02-18 11:21:37 -08001// 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
William A. Kennington III7d6fa422021-02-08 17:04:02 -080015#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
42namespace ncsi
43{
44
45namespace
46{
47
48const 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.
53constexpr auto kStateFormatLen = sizeof(kStateFormat);
54
55void 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
63void 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
72const 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
80void 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
94StateMachine::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
102size_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
141size_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
197void 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
271int 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
294void 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
307void 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
365void 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
374void StateMachine::fail()
375{
376 network_debug_.ncsi.fail_count++;
377 clear_state();
378}
379
380void StateMachine::set_sockio(net::SockIO* sock_io)
381{
382 sock_io_ = sock_io;
383}
384
385void StateMachine::set_net_config(net::ConfigBase* net_config)
386{
387 net_config_ = net_config;
388}
389
390void StateMachine::set_retest_delay(unsigned delay)
391{
392 retest_delay_s_ = delay;
393}
394
395} // namespace ncsi