blob: 8996125976dfabe738fd0e9e5e93fec348100639 [file] [log] [blame]
William A. Kennington III7d6fa422021-02-08 17:04:02 -08001#include "nic_mock.h"
2
3#include "platforms/nemora/portable/ncsi.h"
4
5#include <algorithm>
6#include <cstddef>
7#include <stdexcept>
8
9namespace mock
10{
11
12bool NCSIFrame::parse_ethernet_frame(const ncsi_buf_t& ncsi_buf)
13{
14 std::memcpy(&dst_mac_, ncsi_buf.data, sizeof(dst_mac_));
15 std::memcpy(&src_mac_, ncsi_buf.data + sizeof(dst_mac_), sizeof(src_mac_));
16 // The constant defined in a way that assumes big-endian platform, so we are
17 // just going to calculate it here properly.
18 const uint8_t et_hi = *(ncsi_buf.data + 2 * sizeof(mac_addr_t));
19 const uint8_t et_lo = *(ncsi_buf.data + 2 * sizeof(mac_addr_t) + 1);
20 ethertype_ = (et_hi << 8) + et_lo;
21
22 if (ethertype_ != NCSI_ETHERTYPE)
23 {
24 return false;
25 }
26
27 // This code parses the NC-SI command, according to spec and
28 // as defined in platforms/nemora/portable/ncsi.h
29 // It takes some shortcuts to only retrieve the data we are interested in,
30 // such as using offsetof ot get to a particular field.
31 control_packet_type_ =
32 *(ncsi_buf.data + offsetof(ncsi_header_t, control_packet_type));
33 channel_id_ = *(ncsi_buf.data + offsetof(ncsi_header_t, channel_id));
34
35 size_t payload_offset = sizeof(ncsi_header_t);
36 if (control_packet_type_ & NCSI_RESPONSE)
37 {
38 is_response_ = true;
39 control_packet_type_ &= ~NCSI_RESPONSE;
40 std::memcpy(&response_code_, ncsi_buf.data + payload_offset,
41 sizeof(response_code_));
42 response_code_ = ntohs(response_code_);
43 std::memcpy(&reason_code_,
44 ncsi_buf.data + payload_offset + sizeof(reason_code_),
45 sizeof(reason_code_));
46 reason_code_ = ntohs(reason_code_);
47 payload_offset += sizeof(reason_code_) + sizeof(response_code_);
48 }
49
50 if (control_packet_type_ == NCSI_OEM_COMMAND)
51 {
52 std::memcpy(&manufacturer_id_, ncsi_buf.data + payload_offset,
53 sizeof(manufacturer_id_));
54 manufacturer_id_ = ntohl(manufacturer_id_);
55 // Number of reserved bytes after manufacturer_id_ = 3
56 oem_command_ =
57 *(ncsi_buf.data + payload_offset + sizeof(manufacturer_id_) + 3);
58 payload_offset += sizeof(ncsi_oem_extension_header_t);
59 }
60
61 packet_raw_ =
62 std::vector<uint8_t>(ncsi_buf.data, ncsi_buf.data + ncsi_buf.len);
63 // TODO: Verify payload length.
64
65 return true;
66}
67
68uint32_t NIC::handle_request(const ncsi_buf_t& request_buf,
69 ncsi_buf_t* response_buf)
70{
71 const ncsi_header_t* ncsi_header =
72 reinterpret_cast<const ncsi_header_t*>(request_buf.data);
73
74 NCSIFrame request_frame;
75 request_frame.parse_ethernet_frame(request_buf);
76 save_frame_to_log(request_frame);
77
78 uint32_t response_size;
79 if (is_loopback_)
80 {
81 std::memcpy(response_buf, &request_buf, sizeof(request_buf));
82 response_size = request_buf.len;
83 }
84 else if (std::find(simple_commands_.begin(), simple_commands_.end(),
85 ncsi_header->control_packet_type) !=
86 simple_commands_.end())
87 {
88 // Simple Response
89 response_size =
90 ncsi_build_simple_ack(request_buf.data, response_buf->data);
91 }
92 else
93 {
94 // Not-so-Simple Response
95 switch (ncsi_header->control_packet_type)
96 {
97 case NCSI_GET_VERSION_ID:
98 response_size = ncsi_build_version_id_ack(
99 request_buf.data, response_buf->data, &version_);
100 break;
101 case NCSI_GET_CAPABILITIES:
102 response_size = sizeof(ncsi_capabilities_response_t);
103 {
104 ncsi_capabilities_response_t response;
105 ncsi_build_response_header(
106 request_buf.data, reinterpret_cast<uint8_t*>(&response),
107 0, 0, response_size - sizeof(ncsi_header_t));
108 response.channel_count = channel_count_;
109 std::memcpy(response_buf->data, &response,
110 sizeof(response));
111 }
112 break;
113 case NCSI_GET_PASSTHROUGH_STATISTICS:
114 if (is_legacy_)
115 {
116 response_size = ncsi_build_pt_stats_legacy_ack(
117 request_buf.data, response_buf->data, &stats_legacy_);
118 }
119 else
120 {
121 response_size = ncsi_build_pt_stats_ack(
122 request_buf.data, response_buf->data, &stats_);
123 }
124 break;
125 case NCSI_GET_LINK_STATUS:
126 response_size = ncsi_build_link_status_ack(
127 request_buf.data, response_buf->data, &link_status_);
128 break;
129 case NCSI_OEM_COMMAND:
130 response_size = handle_oem_request(request_buf, response_buf);
131 break;
132 default:
133 response_size = ncsi_build_simple_nack(
134 request_buf.data, response_buf->data, 1, 1);
135 break;
136 }
137 }
138
139 response_buf->len = response_size;
140
141 return response_size;
142}
143
144uint32_t NIC::handle_oem_request(const ncsi_buf_t& request_buf,
145 ncsi_buf_t* response_buf)
146{
147 const ncsi_oem_simple_cmd_t* oem_cmd =
148 reinterpret_cast<const ncsi_oem_simple_cmd_t*>(request_buf.data);
149 uint32_t response_size;
150 switch (oem_cmd->oem_header.oem_cmd)
151 {
152 case NCSI_OEM_COMMAND_GET_HOST_MAC:
153 response_size = ncsi_build_oem_get_mac_ack(
154 request_buf.data, response_buf->data, &mac_);
155 break;
156 case NCSI_OEM_COMMAND_SET_FILTER:
157 {
158 const ncsi_oem_set_filter_cmd_t* cmd =
159 reinterpret_cast<const ncsi_oem_set_filter_cmd_t*>(
160 request_buf.data);
161 if (set_filter(cmd->hdr.channel_id, cmd->filter))
162 {
163 response_size = ncsi_build_oem_simple_ack(request_buf.data,
164 response_buf->data);
165 }
166 else
167 {
168 response_size = ncsi_build_simple_nack(
169 request_buf.data, response_buf->data, 3, 4);
170 }
171 }
172 break;
173 case NCSI_OEM_COMMAND_ECHO:
174 response_size =
175 ncsi_build_oem_echo_ack(request_buf.data, response_buf->data);
176 break;
177 case NCSI_OEM_COMMAND_GET_FILTER:
178 {
179 const ncsi_simple_command_t* cmd =
180 reinterpret_cast<const ncsi_simple_command_t*>(
181 request_buf.data);
182 if (cmd->hdr.channel_id == 0)
183 {
184 response_size = ncsi_build_oem_get_filter_ack(
185 request_buf.data, response_buf->data, &ch0_filter_);
186 }
187 else if (cmd->hdr.channel_id == 1)
188 {
189 response_size = ncsi_build_oem_get_filter_ack(
190 request_buf.data, response_buf->data, &ch1_filter_);
191 }
192 else
193 {
194 response_size = ncsi_build_simple_nack(
195 request_buf.data, response_buf->data, 3, 4);
196 }
197 }
198 break;
199 default:
200 response_size = ncsi_build_simple_nack(request_buf.data,
201 response_buf->data, 1, 2);
202 break;
203 }
204
205 return response_size;
206}
207
208bool NIC::is_filter_configured(uint8_t channel) const
209{
210 if (channel == 0)
211 {
212 return is_ch0_filter_configured_;
213 }
214 else if (channel == 1)
215 {
216 return is_ch1_filter_configured_;
217 }
218
219 throw std::invalid_argument("Unsupported channel");
220}
221
222bool NIC::set_filter(uint8_t channel, const ncsi_oem_filter_t& filter)
223{
224 ncsi_oem_filter_t* nic_filter;
225 if (channel == 0)
226 {
227 nic_filter = &ch0_filter_;
228 is_ch0_filter_configured_ = true;
229 }
230 else if (channel == 1)
231 {
232 nic_filter = &ch1_filter_;
233 is_ch1_filter_configured_ = true;
234 }
235 else
236 {
237 throw std::invalid_argument("Unsupported channel");
238 }
239
240 std::memcpy(nic_filter->mac, filter.mac, MAC_ADDR_SIZE);
241 nic_filter->ip = 0;
242 nic_filter->port = filter.port;
243 return true;
244}
245
246const ncsi_oem_filter_t& NIC::get_filter(uint8_t channel) const
247{
248 if (channel == 0)
249 {
250 return ch0_filter_;
251 }
252 else if (channel == 1)
253 {
254 return ch1_filter_;
255 }
256
257 throw std::invalid_argument("Unsupported channel");
258}
259
260void NIC::set_hostless(bool is_hostless)
261{
262 auto set_flag_op = [](uint8_t lhs, uint8_t rhs) -> auto
263 {
264 return lhs | rhs;
265 };
266
267 auto clear_flag_op = [](uint8_t lhs, uint8_t rhs) -> auto
268 {
269 return lhs & ~rhs;
270 };
271
272 auto flag_op = is_hostless ? set_flag_op : clear_flag_op;
273
274 if (channel_count_ > 0)
275 {
276 ch0_filter_.flags =
277 flag_op(ch0_filter_.flags, NCSI_OEM_FILTER_FLAGS_HOSTLESS);
278 }
279
280 if (channel_count_ > 1)
281 {
282 ch1_filter_.flags =
283 flag_op(ch1_filter_.flags, NCSI_OEM_FILTER_FLAGS_HOSTLESS);
284 }
285}
286
287void NIC::toggle_hostless()
288{
289 if (channel_count_ > 0)
290 {
291 ch0_filter_.flags ^= NCSI_OEM_FILTER_FLAGS_HOSTLESS;
292 }
293
294 if (channel_count_ > 1)
295 {
296 ch1_filter_.flags ^= NCSI_OEM_FILTER_FLAGS_HOSTLESS;
297 }
298}
299
300bool NIC::is_hostless()
301{
302 return ch0_filter_.flags & NCSI_OEM_FILTER_FLAGS_HOSTLESS;
303}
304
305void NIC::save_frame_to_log(const NCSIFrame& frame)
306{
307 if (cmd_log_.size() >= max_log_size_)
308 {
309 cmd_log_.erase(cmd_log_.begin());
310 }
311
312 cmd_log_.push_back(frame);
313}
314
315const std::vector<uint8_t> NIC::simple_commands_ = {
316 NCSI_CLEAR_INITIAL_STATE,
317 NCSI_SELECT_PACKAGE,
318 NCSI_DESELECT_PACKAGE,
319 NCSI_ENABLE_CHANNEL,
320 NCSI_DISABLE_CHANNEL,
321 NCSI_RESET_CHANNEL,
322 NCSI_ENABLE_CHANNEL_NETWORK_TX,
323 NCSI_DISABLE_CHANNEL_NETWORK_TX,
324 NCSI_AEN_ENABLE,
325 NCSI_SET_LINK,
326 NCSI_SET_VLAN_FILTER,
327 NCSI_ENABLE_VLAN,
328 NCSI_DISABLE_VLAN,
329 NCSI_SET_MAC_ADDRESS,
330 NCSI_ENABLE_BROADCAST_FILTER,
331 NCSI_DISABLE_BROADCAST_FILTER,
332 NCSI_ENABLE_GLOBAL_MULTICAST_FILTER,
333 NCSI_DISABLE_GLOBAL_MULTICAST_FILTER,
334 NCSI_SET_NCSI_FLOW_CONTROL,
335};
336
337} // namespace mock