1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2024 Oxide Computer Company
14 */
15
16 #include "ena.h"
17
18 /*
19 * Group/Ring callbacks
20 */
21
22 /*
23 * The ena driver supports only a single mac address: the one assigned
24 * to it by the hypervisor. If mac requests an address besides this
25 * one, then return ENOTSUP. This will prevent VNICs from being
26 * created, as it should.
27 */
28 static int
ena_group_add_mac(void * arg,const uint8_t * mac_addr)29 ena_group_add_mac(void *arg, const uint8_t *mac_addr)
30 {
31 ena_t *ena = arg;
32
33 if (ETHER_IS_MULTICAST(mac_addr)) {
34 return (EINVAL);
35 }
36
37 if (bcmp(ena->ena_mac_addr, mac_addr, ETHERADDRL) == 0) {
38 return (0);
39 }
40
41 return (ENOTSUP);
42 }
43
44 static int
ena_group_rem_mac(void * arg,const uint8_t * mac_addr)45 ena_group_rem_mac(void *arg, const uint8_t *mac_addr)
46 {
47 ena_t *ena = arg;
48
49 if (ETHER_IS_MULTICAST(mac_addr)) {
50 return (EINVAL);
51 }
52
53 if (bcmp(ena->ena_mac_addr, mac_addr, ETHERADDRL) == 0) {
54 return (0);
55 }
56
57 return (ENOTSUP);
58 }
59
60 static int
ena_ring_rx_intr_disable(mac_intr_handle_t mih)61 ena_ring_rx_intr_disable(mac_intr_handle_t mih)
62 {
63 ena_rxq_t *rxq = (ena_rxq_t *)mih;
64 uint32_t intr_ctrl;
65
66 mutex_enter(&rxq->er_lock);
67 intr_ctrl = ena_hw_abs_read32(rxq->er_ena, rxq->er_cq_unmask_addr);
68 ENAHW_REG_INTR_MASK(intr_ctrl);
69 ena_hw_abs_write32(rxq->er_ena, rxq->er_cq_unmask_addr, intr_ctrl);
70 rxq->er_mode = ENA_RXQ_MODE_POLLING;
71 mutex_exit(&rxq->er_lock);
72 return (0);
73 }
74
75 static int
ena_ring_rx_intr_enable(mac_intr_handle_t mih)76 ena_ring_rx_intr_enable(mac_intr_handle_t mih)
77 {
78 ena_rxq_t *rxq = (ena_rxq_t *)mih;
79 uint32_t intr_ctrl;
80
81 mutex_enter(&rxq->er_lock);
82 intr_ctrl = ena_hw_abs_read32(rxq->er_ena, rxq->er_cq_unmask_addr);
83 ENAHW_REG_INTR_UNMASK(intr_ctrl);
84 ena_hw_abs_write32(rxq->er_ena, rxq->er_cq_unmask_addr, intr_ctrl);
85 rxq->er_mode = ENA_RXQ_MODE_INTR;
86 mutex_exit(&rxq->er_lock);
87 return (0);
88 }
89
90 static void
ena_fill_rx_group(void * arg,mac_ring_type_t rtype,const int index,mac_group_info_t * infop,mac_group_handle_t gh)91 ena_fill_rx_group(void *arg, mac_ring_type_t rtype, const int index,
92 mac_group_info_t *infop, mac_group_handle_t gh)
93 {
94 ena_t *ena = arg;
95
96 VERIFY3S(rtype, ==, MAC_RING_TYPE_RX);
97 /*
98 * Typically you pass an Rx group data structure as
99 * mgi_driver, but given we should only ever have one group we
100 * just pass the top-level ena_t.
101 */
102 infop->mgi_driver = (mac_group_driver_t)ena;
103 infop->mgi_start = NULL;
104 infop->mgi_stop = NULL;
105 infop->mgi_addmac = ena_group_add_mac;
106 infop->mgi_remmac = ena_group_rem_mac;
107 infop->mgi_count = ena->ena_num_intrs - 1;
108 }
109
110 static void
ena_fill_tx_ring(void * arg,mac_ring_type_t rtype,const int group_index,const int ring_index,mac_ring_info_t * infop,mac_ring_handle_t rh)111 ena_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
112 const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
113 {
114 ena_t *ena = arg;
115 ena_txq_t *txq = &ena->ena_txqs[ring_index];
116
117 VERIFY3S(rtype, ==, MAC_RING_TYPE_TX);
118 VERIFY3S(ring_index, <, ena->ena_num_txqs);
119 /* Link driver Tx queue to mac ring handle and vice versa. */
120 txq->et_mrh = rh;
121 infop->mri_driver = (mac_ring_driver_t)txq;
122 infop->mri_start = ena_ring_tx_start;
123 infop->mri_stop = ena_ring_tx_stop;
124 infop->mri_tx = ena_ring_tx;
125 infop->mri_stat = ena_ring_tx_stat;
126 }
127
128 static void
ena_fill_rx_ring(void * arg,mac_ring_type_t rtype,const int group_index,const int ring_index,mac_ring_info_t * infop,mac_ring_handle_t rh)129 ena_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
130 const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
131 {
132 ena_t *ena = arg;
133 ena_rxq_t *rxq = &ena->ena_rxqs[ring_index];
134
135 VERIFY3S(rtype, ==, MAC_RING_TYPE_RX);
136 VERIFY3S(ring_index, <, ena->ena_num_rxqs);
137 rxq->er_mrh = rh;
138 infop->mri_driver = (mac_ring_driver_t)rxq;
139 infop->mri_start = ena_ring_rx_start;
140 infop->mri_stop = ena_ring_rx_stop;
141 infop->mri_poll = ena_ring_rx_poll;
142 infop->mri_stat = ena_ring_rx_stat;
143 infop->mri_intr.mi_handle = (mac_intr_handle_t)rxq;
144 infop->mri_intr.mi_enable = ena_ring_rx_intr_enable;
145 infop->mri_intr.mi_disable = ena_ring_rx_intr_disable;
146 infop->mri_intr.mi_ddi_handle =
147 ena->ena_intr_handles[rxq->er_intr_vector];
148 }
149
150 static int
ena_m_start(void * arg)151 ena_m_start(void *arg)
152 {
153 ena_t *ena = arg;
154
155 atomic_or_32(&ena->ena_state, ENA_STATE_STARTED);
156 ena_enable_watchdog(ena);
157
158 return (0);
159 }
160
161 static void
ena_m_stop(void * arg)162 ena_m_stop(void *arg)
163 {
164 ena_t *ena = arg;
165
166 ena_disable_watchdog(ena);
167 atomic_and_32(&ena->ena_state, ~ENA_STATE_STARTED);
168 }
169
170 /*
171 * As discussed in ena_group_add_mac(), ENA only supports a single MAC
172 * address, and therefore we prevent VNICs from being created. That
173 * means there is no chance for promisc to be used as a means for
174 * implementing VNIC support on ENA, as we never allow them to be
175 * created in the first place.
176 *
177 * As for promisc itself, returning success is about the best we can
178 * do. There is no promisc API for an ENA device -- you get only the
179 * exact traffic AWS wants you to see.
180 */
181 static int
ena_m_setpromisc(void * arg,boolean_t on)182 ena_m_setpromisc(void *arg, boolean_t on)
183 {
184 return (0);
185 }
186
187 /*
188 * Similarly to promisc, there is no multicast API for an ENA
189 * device.
190 */
191 static int
ena_m_multicast(void * arg,boolean_t add,const uint8_t * multicast_address)192 ena_m_multicast(void *arg, boolean_t add, const uint8_t *multicast_address)
193 {
194 return (0);
195 }
196
197 static boolean_t
ena_m_getcapab(void * arg,mac_capab_t capab,void * cap_data)198 ena_m_getcapab(void *arg, mac_capab_t capab, void *cap_data)
199 {
200 ena_t *ena = arg;
201 mac_capab_rings_t *cap_rings;
202
203 switch (capab) {
204 case MAC_CAPAB_RINGS:
205 cap_rings = cap_data;
206 cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
207 cap_rings->mr_gaddring = NULL;
208 cap_rings->mr_gremring = NULL;
209 ASSERT3U(ena->ena_num_intrs, >=, 2);
210
211 switch (cap_rings->mr_type) {
212 case MAC_RING_TYPE_TX:
213 /*
214 * We use pseudo Tx groups for now.
215 */
216 cap_rings->mr_gnum = 0;
217 cap_rings->mr_rnum = ena->ena_num_intrs - 1;
218 cap_rings->mr_rget = ena_fill_tx_ring;
219 break;
220 case MAC_RING_TYPE_RX:
221 cap_rings->mr_rnum = ena->ena_num_intrs - 1;
222 cap_rings->mr_rget = ena_fill_rx_ring;
223 /*
224 * The ENA device provides no means to add mac
225 * filters or set promisc mode; it's only
226 * meant to receive its pre-designated unicast
227 * address. However, we still want rings as
228 * the device does provide multiple queues and
229 * RSS.
230 */
231 cap_rings->mr_gnum = 1;
232 cap_rings->mr_gget = ena_fill_rx_group;
233 break;
234 }
235
236 break;
237
238 case MAC_CAPAB_HCKSUM:
239 case MAC_CAPAB_LSO:
240 return (B_FALSE);
241 default:
242 return (B_FALSE);
243 }
244
245 return (B_TRUE);
246 }
247
248 static int
ena_m_setprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,const void * pr_val)249 ena_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
250 uint_t pr_valsize, const void *pr_val)
251 {
252 return (ENOTSUP);
253 }
254
255 static int
ena_m_getprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,void * pr_val)256 ena_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
257 uint_t pr_valsize, void *pr_val)
258 {
259 ena_t *ena = arg;
260 int ret = 0;
261 uint64_t speed;
262 uint8_t *u8;
263
264 mutex_enter(&ena->ena_lock);
265
266 switch (pr_num) {
267 case MAC_PROP_DUPLEX:
268 if (pr_valsize < sizeof (link_duplex_t)) {
269 ret = EOVERFLOW;
270 break;
271 }
272
273 bcopy(&ena->ena_link_duplex, pr_val, sizeof (link_duplex_t));
274 break;
275
276 case MAC_PROP_SPEED:
277 if (pr_valsize < sizeof (uint64_t)) {
278 ret = EOVERFLOW;
279 break;
280 }
281
282 speed = ena->ena_link_speed_mbits * 1000000ULL;
283 bcopy(&speed, pr_val, sizeof (speed));
284 break;
285
286 case MAC_PROP_STATUS:
287 if (pr_valsize < sizeof (link_state_t)) {
288 ret = EOVERFLOW;
289 break;
290 }
291
292 bcopy(&ena->ena_link_state, pr_val, sizeof (link_state_t));
293 break;
294
295 case MAC_PROP_AUTONEG:
296 if (pr_valsize < sizeof (uint8_t)) {
297 ret = EOVERFLOW;
298 break;
299 }
300
301 u8 = pr_val;
302 *u8 = (ena->ena_link_autoneg ? 0 : 1);
303 break;
304
305 case MAC_PROP_MTU:
306 if (pr_valsize < sizeof (uint32_t)) {
307 ret = EOVERFLOW;
308 break;
309 }
310
311 bcopy(&ena->ena_mtu, pr_val, sizeof (uint32_t));
312 break;
313
314 case MAC_PROP_ADV_1000FDX_CAP:
315 case MAC_PROP_EN_1000FDX_CAP:
316 if (pr_valsize < sizeof (uint8_t)) {
317 ret = EOVERFLOW;
318 break;
319 }
320
321 u8 = pr_val;
322 *u8 = (ena->ena_link_speeds & ENAHW_LINK_SPEED_1G) != 0;
323 break;
324
325 case MAC_PROP_ADV_2500FDX_CAP:
326 case MAC_PROP_EN_2500FDX_CAP:
327 if (pr_valsize < sizeof (uint8_t)) {
328 ret = EOVERFLOW;
329 break;
330 }
331
332 u8 = pr_val;
333 *u8 = (ena->ena_link_speeds & ENAHW_LINK_SPEED_2_HALF_G) != 0;
334 break;
335
336 case MAC_PROP_ADV_5000FDX_CAP:
337 case MAC_PROP_EN_5000FDX_CAP:
338 if (pr_valsize < sizeof (uint8_t)) {
339 ret = EOVERFLOW;
340 break;
341 }
342
343 u8 = pr_val;
344 *u8 = (ena->ena_link_speeds & ENAHW_LINK_SPEED_5G) != 0;
345 break;
346
347 case MAC_PROP_ADV_10GFDX_CAP:
348 case MAC_PROP_EN_10GFDX_CAP:
349 if (pr_valsize < sizeof (uint8_t)) {
350 ret = EOVERFLOW;
351 break;
352 }
353
354 u8 = pr_val;
355 *u8 = (ena->ena_link_speeds & ENAHW_LINK_SPEED_10G) != 0;
356 break;
357
358 case MAC_PROP_ADV_25GFDX_CAP:
359 case MAC_PROP_EN_25GFDX_CAP:
360 if (pr_valsize < sizeof (uint8_t)) {
361 ret = EOVERFLOW;
362 break;
363 }
364
365 u8 = pr_val;
366 *u8 = (ena->ena_link_speeds & ENAHW_LINK_SPEED_25G) != 0;
367 break;
368
369 case MAC_PROP_ADV_40GFDX_CAP:
370 case MAC_PROP_EN_40GFDX_CAP:
371 if (pr_valsize < sizeof (uint8_t)) {
372 ret = EOVERFLOW;
373 break;
374 }
375
376 u8 = pr_val;
377 *u8 = (ena->ena_link_speeds & ENAHW_LINK_SPEED_40G) != 0;
378 break;
379
380 case MAC_PROP_ADV_100GFDX_CAP:
381 case MAC_PROP_EN_100GFDX_CAP:
382 if (pr_valsize < sizeof (uint8_t)) {
383 ret = EOVERFLOW;
384 break;
385 }
386
387 u8 = pr_val;
388 *u8 = (ena->ena_link_speeds & ENAHW_LINK_SPEED_100G) != 0;
389 break;
390
391 default:
392 ret = ENOTSUP;
393 break;
394 }
395
396 mutex_exit(&ena->ena_lock);
397 return (ret);
398 }
399
400 static void
ena_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)401 ena_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
402 mac_prop_info_handle_t prh)
403 {
404 }
405
406 static mac_callbacks_t ena_m_callbacks = {
407 .mc_callbacks = MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
408 .mc_getstat = ena_m_stat,
409 .mc_start = ena_m_start,
410 .mc_stop = ena_m_stop,
411 .mc_setpromisc = ena_m_setpromisc,
412 .mc_multicst = ena_m_multicast,
413 .mc_getcapab = ena_m_getcapab,
414 .mc_setprop = ena_m_setprop,
415 .mc_getprop = ena_m_getprop,
416 .mc_propinfo = ena_m_propinfo,
417 };
418
419 int
ena_mac_unregister(ena_t * ena)420 ena_mac_unregister(ena_t *ena)
421 {
422 if (ena->ena_mh == NULL) {
423 return (0);
424 }
425
426 return (mac_unregister(ena->ena_mh));
427 }
428
429 bool
ena_mac_register(ena_t * ena)430 ena_mac_register(ena_t *ena)
431 {
432 int ret;
433 mac_register_t *regp;
434
435 if ((regp = mac_alloc(MAC_VERSION)) == NULL) {
436 ena_err(ena, "failed to allocate MAC handle");
437 return (false);
438 }
439
440 regp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
441 regp->m_driver = ena;
442 regp->m_dip = ena->ena_dip;
443 regp->m_instance = 0;
444 regp->m_src_addr = ena->ena_mac_addr;
445 regp->m_dst_addr = NULL;
446 regp->m_callbacks = &ena_m_callbacks;
447 regp->m_min_sdu = 0;
448 regp->m_max_sdu = ena->ena_mtu;
449 regp->m_pdata = NULL;
450 regp->m_pdata_size = 0;
451 regp->m_priv_props = NULL;
452 regp->m_margin = VLAN_TAGSZ;
453 regp->m_v12n = MAC_VIRT_LEVEL1;
454
455 if ((ret = mac_register(regp, &ena->ena_mh)) != 0) {
456 ena_err(ena, "failed to register ena with mac: %d", ret);
457 }
458
459 mac_free(regp);
460
461 if (ret == 0) {
462 /*
463 * Until we get the first AENQ link change event, we
464 * do not actually know the status of the link.
465 */
466 mac_link_update(ena->ena_mh, LINK_STATE_UNKNOWN);
467 }
468
469 return (ret == 0);
470 }
471