2 * Copyright (c) 2018 Jakub Jermar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "virtio-net.h"
35 #include <ddf/driver.h>
36 #include <ddf/interrupt.h>
39 #include <pci_dev_iface.h>
44 #include <virtio-pci.h>
46 #define NAME "virtio-net"
48 #define VIRTIO_NET_NUM_QUEUES 3
54 #define BUFFER_SIZE 2048
55 #define RX_BUF_SIZE BUFFER_SIZE
56 #define TX_BUF_SIZE BUFFER_SIZE
57 #define CT_BUF_SIZE BUFFER_SIZE
59 static ddf_dev_ops_t virtio_net_dev_ops
;
61 static errno_t
virtio_net_dev_add(ddf_dev_t
*dev
);
63 static driver_ops_t virtio_net_driver_ops
= {
64 .dev_add
= virtio_net_dev_add
67 static driver_t virtio_net_driver
= {
69 .driver_ops
= &virtio_net_driver_ops
72 /** VirtIO net IRQ handler.
74 * @param icall IRQ event notification
75 * @param arg Argument (nic_t *)
77 static void virtio_net_irq_handler(ipc_call_t
*icall
, void *arg
)
79 nic_t
*nic
= (nic_t
*)arg
;
80 virtio_net_t
*virtio_net
= nic_get_specific(nic
);
81 virtio_dev_t
*vdev
= &virtio_net
->virtio_dev
;
85 while (virtio_virtq_consume_used(vdev
, RX_QUEUE_1
, &descno
, &len
)) {
86 virtio_net_hdr_t
*hdr
=
87 (virtio_net_hdr_t
*) virtio_net
->rx_buf
[descno
];
88 if (len
<= sizeof(*hdr
)) {
90 "RX data length too short, packet dropped");
91 virtio_virtq_produce_available(vdev
, RX_QUEUE_1
,
96 nic_frame_t
*frame
= nic_alloc_frame(nic
, len
- sizeof(*hdr
));
98 memcpy(frame
->data
, &hdr
[1], len
- sizeof(*hdr
));
99 nic_received_frame(nic
, frame
);
102 "Cannot allocate RX frame, packet dropped");
105 virtio_virtq_produce_available(vdev
, RX_QUEUE_1
, descno
);
108 while (virtio_virtq_consume_used(vdev
, TX_QUEUE_1
, &descno
, &len
)) {
109 virtio_free_desc(vdev
, TX_QUEUE_1
, &virtio_net
->tx_free_head
,
112 while (virtio_virtq_consume_used(vdev
, CT_QUEUE_1
, &descno
, &len
)) {
113 virtio_free_desc(vdev
, CT_QUEUE_1
, &virtio_net
->ct_free_head
,
118 static errno_t
virtio_net_register_interrupt(ddf_dev_t
*dev
)
120 nic_t
*nic
= ddf_dev_data_get(dev
);
121 virtio_net_t
*virtio_net
= nic_get_specific(nic
);
122 virtio_dev_t
*vdev
= &virtio_net
->virtio_dev
;
124 hw_res_list_parsed_t res
;
125 hw_res_list_parsed_init(&res
);
127 errno_t rc
= nic_get_resources(nic
, &res
);
131 if (res
.irqs
.count
< 1) {
132 hw_res_list_parsed_clean(&res
);
137 virtio_net
->irq
= res
.irqs
.irqs
[0];
138 hw_res_list_parsed_clean(&res
);
140 irq_pio_range_t pio_ranges
[] = {
142 .base
= vdev
->isr_phys
,
143 .size
= sizeof(vdev
->isr_phys
),
147 irq_cmd_t irq_commands
[] = {
149 .cmd
= CMD_PIO_READ_8
,
150 .addr
= (void *) vdev
->isr_phys
,
154 .cmd
= CMD_PREDICATE
,
163 irq_code_t irq_code
= {
164 .rangecount
= sizeof(pio_ranges
) / sizeof(irq_pio_range_t
),
165 .ranges
= pio_ranges
,
166 .cmdcount
= sizeof(irq_commands
) / sizeof(irq_cmd_t
),
170 return register_interrupt_handler(dev
, virtio_net
->irq
,
171 virtio_net_irq_handler
, (void *)nic
, &irq_code
,
172 &virtio_net
->irq_handle
);
175 static errno_t
virtio_net_initialize(ddf_dev_t
*dev
)
177 nic_t
*nic
= nic_create_and_bind(dev
);
181 virtio_net_t
*virtio_net
= calloc(1, sizeof(virtio_net_t
));
183 nic_unbind_and_destroy(dev
);
187 nic_set_specific(nic
, virtio_net
);
189 errno_t rc
= virtio_pci_dev_initialize(dev
, &virtio_net
->virtio_dev
);
193 virtio_dev_t
*vdev
= &virtio_net
->virtio_dev
;
194 virtio_pci_common_cfg_t
*cfg
= virtio_net
->virtio_dev
.common_cfg
;
195 virtio_net_cfg_t
*netcfg
= virtio_net
->virtio_dev
.device_cfg
;
200 rc
= virtio_net_register_interrupt(dev
);
204 /* Reset the device and negotiate the feature bits */
205 rc
= virtio_device_setup_start(vdev
,
206 VIRTIO_NET_F_MAC
| VIRTIO_NET_F_CTRL_VQ
);
210 /* Perform device-specific setup */
213 * Discover and configure the virtqueues
215 uint16_t num_queues
= pio_read_le16(&cfg
->num_queues
);
216 if (num_queues
!= VIRTIO_NET_NUM_QUEUES
) {
217 ddf_msg(LVL_NOTE
, "Unsupported number of virtqueues: %u",
223 vdev
->queues
= calloc(sizeof(virtq_t
), num_queues
);
229 rc
= virtio_virtq_setup(vdev
, RX_QUEUE_1
, RX_BUFFERS
);
232 rc
= virtio_virtq_setup(vdev
, TX_QUEUE_1
, TX_BUFFERS
);
235 rc
= virtio_virtq_setup(vdev
, CT_QUEUE_1
, CT_BUFFERS
);
242 rc
= virtio_setup_dma_bufs(RX_BUFFERS
, RX_BUF_SIZE
, false,
243 virtio_net
->rx_buf
, virtio_net
->rx_buf_p
);
246 rc
= virtio_setup_dma_bufs(TX_BUFFERS
, TX_BUF_SIZE
, true,
247 virtio_net
->tx_buf
, virtio_net
->tx_buf_p
);
250 rc
= virtio_setup_dma_bufs(CT_BUFFERS
, CT_BUF_SIZE
, true,
251 virtio_net
->ct_buf
, virtio_net
->ct_buf_p
);
256 * Give all RX buffers to the NIC
258 for (unsigned i
= 0; i
< RX_BUFFERS
; i
++) {
260 * Associtate the buffer with the descriptor, set length and
263 virtio_virtq_desc_set(vdev
, RX_QUEUE_1
, i
,
264 virtio_net
->rx_buf_p
[i
], RX_BUF_SIZE
, VIRTQ_DESC_F_WRITE
,
267 * Put the set descriptor into the available ring of the RX
270 virtio_virtq_produce_available(vdev
, RX_QUEUE_1
, i
);
274 * Put all TX and CT buffers on a free list
276 virtio_create_desc_free_list(vdev
, TX_QUEUE_1
, TX_BUFFERS
,
277 &virtio_net
->tx_free_head
);
278 virtio_create_desc_free_list(vdev
, CT_QUEUE_1
, CT_BUFFERS
,
279 &virtio_net
->ct_free_head
);
282 * Read the MAC address
284 nic_address_t nic_addr
;
285 for (unsigned i
= 0; i
< ETH_ADDR
; i
++)
286 nic_addr
.address
[i
] = pio_read_8(&netcfg
->mac
[i
]);
287 rc
= nic_report_address(nic
, &nic_addr
);
291 ddf_msg(LVL_NOTE
, "MAC address: " PRIMAC
, ARGSMAC(nic_addr
.address
));
296 rc
= hw_res_enable_interrupt(ddf_dev_parent_sess_get(dev
),
299 ddf_msg(LVL_NOTE
, "Failed to enable interrupt");
303 ddf_msg(LVL_NOTE
, "Registered IRQ %d", virtio_net
->irq
);
306 virtio_device_setup_finalize(vdev
);
311 virtio_teardown_dma_bufs(virtio_net
->rx_buf
);
312 virtio_teardown_dma_bufs(virtio_net
->tx_buf
);
313 virtio_teardown_dma_bufs(virtio_net
->ct_buf
);
315 virtio_device_setup_fail(vdev
);
316 virtio_pci_dev_cleanup(vdev
);
320 static void virtio_net_uninitialize(ddf_dev_t
*dev
)
322 nic_t
*nic
= ddf_dev_data_get(dev
);
323 virtio_net_t
*virtio_net
= (virtio_net_t
*) nic_get_specific(nic
);
325 virtio_teardown_dma_bufs(virtio_net
->rx_buf
);
326 virtio_teardown_dma_bufs(virtio_net
->tx_buf
);
327 virtio_teardown_dma_bufs(virtio_net
->ct_buf
);
329 virtio_device_setup_fail(&virtio_net
->virtio_dev
);
330 virtio_pci_dev_cleanup(&virtio_net
->virtio_dev
);
333 static void virtio_net_send(nic_t
*nic
, void *data
, size_t size
)
335 virtio_net_t
*virtio_net
= nic_get_specific(nic
);
336 virtio_dev_t
*vdev
= &virtio_net
->virtio_dev
;
338 if (size
> sizeof(virtio_net
) + TX_BUF_SIZE
) {
339 ddf_msg(LVL_WARN
, "TX data too big, frame dropped");
343 uint16_t descno
= virtio_alloc_desc(vdev
, TX_QUEUE_1
,
344 &virtio_net
->tx_free_head
);
345 if (descno
== (uint16_t) -1U) {
346 ddf_msg(LVL_WARN
, "No TX buffers available, frame dropped");
349 assert(descno
< TX_BUFFERS
);
351 /* Setup the packet header */
352 virtio_net_hdr_t
*hdr
= (virtio_net_hdr_t
*) virtio_net
->tx_buf
[descno
];
353 memset(hdr
, 0, sizeof(virtio_net_hdr_t
));
354 hdr
->gso_type
= VIRTIO_NET_HDR_GSO_NONE
;
355 hdr
->num_buffers
= 0;
357 /* Copy packet data into the buffer just past the header */
358 memcpy(&hdr
[1], data
, size
);
361 * Set the descriptor, put it into the virtqueue and notify the device
363 virtio_virtq_desc_set(vdev
, TX_QUEUE_1
, descno
,
364 virtio_net
->tx_buf_p
[descno
], sizeof(virtio_net_hdr_t
) + size
, 0, 0);
365 virtio_virtq_produce_available(vdev
, TX_QUEUE_1
, descno
);
368 static errno_t
virtio_net_on_multicast_mode_change(nic_t
*nic
,
369 nic_multicast_mode_t new_mode
, const nic_address_t
*address_list
,
370 size_t address_count
)
373 case NIC_MULTICAST_BLOCKED
:
374 nic_report_hw_filtering(nic
, -1, 0, -1);
376 case NIC_MULTICAST_LIST
:
377 nic_report_hw_filtering(nic
, -1, 0, -1);
379 case NIC_MULTICAST_PROMISC
:
380 nic_report_hw_filtering(nic
, -1, 0, -1);
388 static errno_t
virtio_net_on_broadcast_mode_change(nic_t
*nic
,
389 nic_broadcast_mode_t new_mode
)
392 case NIC_BROADCAST_BLOCKED
:
394 case NIC_BROADCAST_ACCEPTED
:
401 static errno_t
virtio_net_dev_add(ddf_dev_t
*dev
)
403 ddf_msg(LVL_NOTE
, "%s %s (handle = %zu)", __func__
,
404 ddf_dev_get_name(dev
), ddf_dev_get_handle(dev
));
406 errno_t rc
= virtio_net_initialize(dev
);
410 ddf_fun_t
*fun
= ddf_fun_create(dev
, fun_exposed
, "port0");
415 nic_t
*nic
= ddf_dev_data_get(dev
);
416 nic_set_ddf_fun(nic
, fun
);
417 ddf_fun_set_ops(fun
, &virtio_net_dev_ops
);
419 nic_set_send_frame_handler(nic
, virtio_net_send
);
420 nic_set_filtering_change_handlers(nic
, NULL
,
421 virtio_net_on_multicast_mode_change
,
422 virtio_net_on_broadcast_mode_change
, NULL
, NULL
);
424 rc
= ddf_fun_bind(fun
);
426 ddf_msg(LVL_ERROR
, "Failed binding device function");
430 rc
= ddf_fun_add_to_category(fun
, DEVICE_CATEGORY_NIC
);
432 ddf_msg(LVL_ERROR
, "Failed adding function to category");
436 ddf_msg(LVL_NOTE
, "The %s device has been successfully initialized.",
437 ddf_dev_get_name(dev
));
444 ddf_fun_destroy(fun
);
446 virtio_net_uninitialize(dev
);
450 static errno_t
virtio_net_get_device_info(ddf_fun_t
*fun
,
451 nic_device_info_t
*info
)
453 nic_t
*nic
= nic_get_from_ddf_fun(fun
);
457 str_cpy(info
->vendor_name
, sizeof(info
->vendor_name
), "Red Hat, Inc.");
458 str_cpy(info
->model_name
, sizeof(info
->model_name
),
459 "Virtio network device");
464 static errno_t
virtio_net_get_cable_state(ddf_fun_t
*fun
,
465 nic_cable_state_t
*state
)
467 *state
= NIC_CS_PLUGGED
;
471 static errno_t
virtio_net_get_operation_mode(ddf_fun_t
*fun
, int *speed
,
472 nic_channel_mode_t
*duplex
, nic_role_t
*role
)
475 *duplex
= NIC_CM_FULL_DUPLEX
;
476 *role
= NIC_ROLE_UNKNOWN
;
480 static nic_iface_t virtio_net_nic_iface
= {
481 .get_device_info
= virtio_net_get_device_info
,
482 .get_cable_state
= virtio_net_get_cable_state
,
483 .get_operation_mode
= virtio_net_get_operation_mode
,
488 printf("%s: HelenOS virtio-net driver\n", NAME
);
490 if (nic_driver_init(NAME
) != EOK
)
493 nic_driver_implement(&virtio_net_driver_ops
, &virtio_net_dev_ops
,
494 &virtio_net_nic_iface
);
496 (void) ddf_log_init(NAME
);
497 return ddf_driver_main(&virtio_net_driver
);