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 static void virtio_net_irq_handler(ipc_call_t
*icall
, ddf_dev_t
*dev
)
74 nic_t
*nic
= ddf_dev_data_get(dev
);
75 virtio_net_t
*virtio_net
= nic_get_specific(nic
);
76 virtio_dev_t
*vdev
= &virtio_net
->virtio_dev
;
80 while (virtio_virtq_consume_used(vdev
, RX_QUEUE_1
, &descno
, &len
)) {
81 virtio_net_hdr_t
*hdr
=
82 (virtio_net_hdr_t
*) virtio_net
->rx_buf
[descno
];
83 if (len
<= sizeof(*hdr
)) {
85 "RX data length too short, packet dropped");
86 virtio_virtq_produce_available(vdev
, RX_QUEUE_1
,
91 nic_frame_t
*frame
= nic_alloc_frame(nic
, len
- sizeof(*hdr
));
93 memcpy(frame
->data
, &hdr
[1], len
- sizeof(*hdr
));
94 nic_received_frame(nic
, frame
);
97 "Cannot allocate RX frame, packet dropped");
100 virtio_virtq_produce_available(vdev
, RX_QUEUE_1
, descno
);
103 while (virtio_virtq_consume_used(vdev
, TX_QUEUE_1
, &descno
, &len
)) {
104 virtio_free_desc(vdev
, TX_QUEUE_1
, &virtio_net
->tx_free_head
,
107 while (virtio_virtq_consume_used(vdev
, CT_QUEUE_1
, &descno
, &len
)) {
108 virtio_free_desc(vdev
, CT_QUEUE_1
, &virtio_net
->ct_free_head
,
113 static errno_t
virtio_net_register_interrupt(ddf_dev_t
*dev
)
115 nic_t
*nic
= ddf_dev_data_get(dev
);
116 virtio_net_t
*virtio_net
= nic_get_specific(nic
);
117 virtio_dev_t
*vdev
= &virtio_net
->virtio_dev
;
119 hw_res_list_parsed_t res
;
120 hw_res_list_parsed_init(&res
);
122 errno_t rc
= nic_get_resources(nic
, &res
);
126 if (res
.irqs
.count
< 1) {
127 hw_res_list_parsed_clean(&res
);
132 virtio_net
->irq
= res
.irqs
.irqs
[0];
133 hw_res_list_parsed_clean(&res
);
135 irq_pio_range_t pio_ranges
[] = {
137 .base
= vdev
->isr_phys
,
138 .size
= sizeof(vdev
->isr_phys
),
142 irq_cmd_t irq_commands
[] = {
144 .cmd
= CMD_PIO_READ_8
,
145 .addr
= (void *) vdev
->isr_phys
,
149 .cmd
= CMD_PREDICATE
,
158 irq_code_t irq_code
= {
159 .rangecount
= sizeof(pio_ranges
) / sizeof(irq_pio_range_t
),
160 .ranges
= pio_ranges
,
161 .cmdcount
= sizeof(irq_commands
) / sizeof(irq_cmd_t
),
165 return register_interrupt_handler(dev
, virtio_net
->irq
,
166 virtio_net_irq_handler
, &irq_code
, &virtio_net
->irq_handle
);
169 static errno_t
virtio_net_initialize(ddf_dev_t
*dev
)
171 nic_t
*nic
= nic_create_and_bind(dev
);
175 virtio_net_t
*virtio_net
= calloc(1, sizeof(virtio_net_t
));
177 nic_unbind_and_destroy(dev
);
181 nic_set_specific(nic
, virtio_net
);
183 errno_t rc
= virtio_pci_dev_initialize(dev
, &virtio_net
->virtio_dev
);
187 virtio_dev_t
*vdev
= &virtio_net
->virtio_dev
;
188 virtio_pci_common_cfg_t
*cfg
= virtio_net
->virtio_dev
.common_cfg
;
189 virtio_net_cfg_t
*netcfg
= virtio_net
->virtio_dev
.device_cfg
;
194 rc
= virtio_net_register_interrupt(dev
);
198 /* Reset the device and negotiate the feature bits */
199 rc
= virtio_device_setup_start(vdev
,
200 VIRTIO_NET_F_MAC
| VIRTIO_NET_F_CTRL_VQ
);
204 /* Perform device-specific setup */
207 * Discover and configure the virtqueues
209 uint16_t num_queues
= pio_read_le16(&cfg
->num_queues
);
210 if (num_queues
!= VIRTIO_NET_NUM_QUEUES
) {
211 ddf_msg(LVL_NOTE
, "Unsupported number of virtqueues: %u",
216 vdev
->queues
= calloc(sizeof(virtq_t
), num_queues
);
222 rc
= virtio_virtq_setup(vdev
, RX_QUEUE_1
, RX_BUFFERS
);
225 rc
= virtio_virtq_setup(vdev
, TX_QUEUE_1
, TX_BUFFERS
);
228 rc
= virtio_virtq_setup(vdev
, CT_QUEUE_1
, CT_BUFFERS
);
235 rc
= virtio_setup_dma_bufs(RX_BUFFERS
, RX_BUF_SIZE
, false,
236 virtio_net
->rx_buf
, virtio_net
->rx_buf_p
);
239 rc
= virtio_setup_dma_bufs(TX_BUFFERS
, TX_BUF_SIZE
, true,
240 virtio_net
->tx_buf
, virtio_net
->tx_buf_p
);
243 rc
= virtio_setup_dma_bufs(CT_BUFFERS
, CT_BUF_SIZE
, true,
244 virtio_net
->ct_buf
, virtio_net
->ct_buf_p
);
249 * Give all RX buffers to the NIC
251 for (unsigned i
= 0; i
< RX_BUFFERS
; i
++) {
253 * Associtate the buffer with the descriptor, set length and
256 virtio_virtq_desc_set(vdev
, RX_QUEUE_1
, i
,
257 virtio_net
->rx_buf_p
[i
], RX_BUF_SIZE
, VIRTQ_DESC_F_WRITE
,
260 * Put the set descriptor into the available ring of the RX
263 virtio_virtq_produce_available(vdev
, RX_QUEUE_1
, i
);
267 * Put all TX and CT buffers on a free list
269 virtio_create_desc_free_list(vdev
, TX_QUEUE_1
, TX_BUFFERS
,
270 &virtio_net
->tx_free_head
);
271 virtio_create_desc_free_list(vdev
, CT_QUEUE_1
, CT_BUFFERS
,
272 &virtio_net
->ct_free_head
);
275 * Read the MAC address
277 nic_address_t nic_addr
;
278 for (unsigned i
= 0; i
< ETH_ADDR
; i
++)
279 nic_addr
.address
[i
] = pio_read_8(&netcfg
->mac
[i
]);
280 rc
= nic_report_address(nic
, &nic_addr
);
284 ddf_msg(LVL_NOTE
, "MAC address: " PRIMAC
, ARGSMAC(nic_addr
.address
));
289 rc
= hw_res_enable_interrupt(ddf_dev_parent_sess_get(dev
),
292 ddf_msg(LVL_NOTE
, "Failed to enable interrupt");
296 ddf_msg(LVL_NOTE
, "Registered IRQ %d", virtio_net
->irq
);
299 virtio_device_setup_finalize(vdev
);
304 virtio_teardown_dma_bufs(virtio_net
->rx_buf
);
305 virtio_teardown_dma_bufs(virtio_net
->tx_buf
);
306 virtio_teardown_dma_bufs(virtio_net
->ct_buf
);
308 virtio_device_setup_fail(vdev
);
309 virtio_pci_dev_cleanup(vdev
);
313 static void virtio_net_uninitialize(ddf_dev_t
*dev
)
315 nic_t
*nic
= ddf_dev_data_get(dev
);
316 virtio_net_t
*virtio_net
= (virtio_net_t
*) nic_get_specific(nic
);
318 virtio_teardown_dma_bufs(virtio_net
->rx_buf
);
319 virtio_teardown_dma_bufs(virtio_net
->tx_buf
);
320 virtio_teardown_dma_bufs(virtio_net
->ct_buf
);
322 virtio_device_setup_fail(&virtio_net
->virtio_dev
);
323 virtio_pci_dev_cleanup(&virtio_net
->virtio_dev
);
326 static void virtio_net_send(nic_t
*nic
, void *data
, size_t size
)
328 virtio_net_t
*virtio_net
= nic_get_specific(nic
);
329 virtio_dev_t
*vdev
= &virtio_net
->virtio_dev
;
331 if (size
> sizeof(virtio_net
) + TX_BUF_SIZE
) {
332 ddf_msg(LVL_WARN
, "TX data too big, frame dropped");
336 uint16_t descno
= virtio_alloc_desc(vdev
, TX_QUEUE_1
,
337 &virtio_net
->tx_free_head
);
338 if (descno
== (uint16_t) -1U) {
339 ddf_msg(LVL_WARN
, "No TX buffers available, frame dropped");
342 assert(descno
< TX_BUFFERS
);
344 /* Setup the packet header */
345 virtio_net_hdr_t
*hdr
= (virtio_net_hdr_t
*) virtio_net
->tx_buf
[descno
];
346 memset(hdr
, 0, sizeof(virtio_net_hdr_t
));
347 hdr
->gso_type
= VIRTIO_NET_HDR_GSO_NONE
;
348 hdr
->num_buffers
= 0;
350 /* Copy packet data into the buffer just past the header */
351 memcpy(&hdr
[1], data
, size
);
354 * Set the descriptor, put it into the virtqueue and notify the device
356 virtio_virtq_desc_set(vdev
, TX_QUEUE_1
, descno
,
357 virtio_net
->tx_buf_p
[descno
], sizeof(virtio_net_hdr_t
) + size
, 0, 0);
358 virtio_virtq_produce_available(vdev
, TX_QUEUE_1
, descno
);
361 static errno_t
virtio_net_on_multicast_mode_change(nic_t
*nic
,
362 nic_multicast_mode_t new_mode
, const nic_address_t
*address_list
,
363 size_t address_count
)
366 case NIC_MULTICAST_BLOCKED
:
367 nic_report_hw_filtering(nic
, -1, 0, -1);
369 case NIC_MULTICAST_LIST
:
370 nic_report_hw_filtering(nic
, -1, 0, -1);
372 case NIC_MULTICAST_PROMISC
:
373 nic_report_hw_filtering(nic
, -1, 0, -1);
381 static errno_t
virtio_net_on_broadcast_mode_change(nic_t
*nic
,
382 nic_broadcast_mode_t new_mode
)
385 case NIC_BROADCAST_BLOCKED
:
387 case NIC_BROADCAST_ACCEPTED
:
394 static errno_t
virtio_net_dev_add(ddf_dev_t
*dev
)
396 ddf_msg(LVL_NOTE
, "%s %s (handle = %zu)", __func__
,
397 ddf_dev_get_name(dev
), ddf_dev_get_handle(dev
));
399 errno_t rc
= virtio_net_initialize(dev
);
403 ddf_fun_t
*fun
= ddf_fun_create(dev
, fun_exposed
, "port0");
408 nic_t
*nic
= ddf_dev_data_get(dev
);
409 nic_set_ddf_fun(nic
, fun
);
410 ddf_fun_set_ops(fun
, &virtio_net_dev_ops
);
412 nic_set_send_frame_handler(nic
, virtio_net_send
);
413 nic_set_filtering_change_handlers(nic
, NULL
,
414 virtio_net_on_multicast_mode_change
,
415 virtio_net_on_broadcast_mode_change
, NULL
, NULL
);
417 rc
= ddf_fun_bind(fun
);
419 ddf_msg(LVL_ERROR
, "Failed binding device function");
423 rc
= ddf_fun_add_to_category(fun
, DEVICE_CATEGORY_NIC
);
425 ddf_msg(LVL_ERROR
, "Failed adding function to category");
429 ddf_msg(LVL_NOTE
, "The %s device has been successfully initialized.",
430 ddf_dev_get_name(dev
));
437 virtio_net_uninitialize(dev
);
442 static errno_t
virtio_net_get_device_info(ddf_fun_t
*fun
,
443 nic_device_info_t
*info
)
445 nic_t
*nic
= nic_get_from_ddf_fun(fun
);
449 str_cpy(info
->vendor_name
, sizeof(info
->vendor_name
), "Red Hat, Inc.");
450 str_cpy(info
->model_name
, sizeof(info
->model_name
),
451 "Virtio network device");
456 static errno_t
virtio_net_get_cable_state(ddf_fun_t
*fun
,
457 nic_cable_state_t
*state
)
459 *state
= NIC_CS_PLUGGED
;
463 static errno_t
virtio_net_get_operation_mode(ddf_fun_t
*fun
, int *speed
,
464 nic_channel_mode_t
*duplex
, nic_role_t
*role
)
467 *duplex
= NIC_CM_FULL_DUPLEX
;
468 *role
= NIC_ROLE_UNKNOWN
;
472 static nic_iface_t virtio_net_nic_iface
= {
473 .get_device_info
= virtio_net_get_device_info
,
474 .get_cable_state
= virtio_net_get_cable_state
,
475 .get_operation_mode
= virtio_net_get_operation_mode
,
480 printf("%s: HelenOS virtio-net driver\n", NAME
);
482 if (nic_driver_init(NAME
) != EOK
)
485 nic_driver_implement(&virtio_net_driver_ops
, &virtio_net_dev_ops
,
486 &virtio_net_nic_iface
);
488 (void) ddf_log_init(NAME
);
489 return ddf_driver_main(&virtio_net_driver
);