xref: /openbmc/google-misc/acpi-power-state-daemon/subprojects/ncsid/src/ncsi_state_machine.cpp (revision dab96f131fb3a46d93f1093feccc9095d8589ece)
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*