1/* 2 * Copyright 2020, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the GNU General Public License version 2. Note that NO WARRANTY is provided. 8 * See "LICENSE_GPLv2.txt" for details. 9 * 10 * @TAG(DATA61_GPL) 11 */ 12 13#include <autoconf.h> 14#include <stdbool.h> 15 16#include <camkes/dma.h> 17#include <camkes/dataport.h> 18#include <camkes/io.h> 19#include <camkes/irq.h> 20#include <camkes/virtqueue.h> 21 22#include <platsupport/io.h> 23#include <platsupport/irq.h> 24#include <platsupport/interface_registration.h> 25#include <ethdrivers/raw.h> 26#include <ethdrivers/intel.h> 27#include <sel4utils/sel4_zf_logif.h> 28#include <virtqueue.h> 29#include <picotcp-ethernet-async.h> 30 31typedef struct data { 32 ps_io_ops_t *io_ops; 33 virtqueue_device_t tx_virtqueue; 34 virtqueue_device_t rx_virtqueue; 35 bool action; 36 bool blocked_tx; 37 bool no_rx_bufs; 38 uint8_t hw_mac[6]; 39 struct eth_driver *eth_driver; 40} server_data_t; 41 42#define BUF_SIZE 2048 43 44static void eth_tx_complete(void *iface, void *cookie) 45{ 46 server_data_t *state = iface; 47 48 virtqueue_ring_object_t handle; 49 handle.first = (uint32_t)(uintptr_t)cookie; 50 handle.cur = (uint32_t)(uintptr_t)cookie; 51 if (!virtqueue_add_used_buf(&state->tx_virtqueue, &handle, BUF_SIZE)) { 52 ZF_LOGF("eth_tx_complete: Error while enqueuing used buffer, queue full"); 53 } 54 if (state->blocked_tx) { 55 state->action = true; 56 } 57} 58 59static uintptr_t eth_allocate_rx_buf(void *iface, size_t buf_size, void **cookie) 60{ 61 if (buf_size > BUF_SIZE) { 62 return 0; 63 } 64 server_data_t *state = iface; 65 66 virtqueue_ring_object_t handle; 67 68 if (virtqueue_get_available_buf(&state->rx_virtqueue, &handle) == 0) { 69 // No buffer available to fill RX ring with. 70 state->no_rx_bufs = true; 71 return 0; 72 } 73 state->no_rx_bufs = false; 74 void *buf; 75 unsigned len; 76 vq_flags_t flag; 77 int more = virtqueue_gather_available(&state->rx_virtqueue, &handle, &buf, &len, &flag); 78 if (more == 0) { 79 ZF_LOGF("eth_allocate_rx_buf: Invalid virtqueue ring entry"); 80 } 81 82 uintptr_t phys = ps_dma_pin(&state->io_ops->dma_manager, DECODE_DMA_ADDRESS(buf), BUF_SIZE); 83 *cookie = (void *)(uintptr_t) handle.first; 84 return phys; 85} 86 87static void eth_rx_complete(void *iface, unsigned int num_bufs, void **cookies, unsigned int *lens) 88{ 89 server_data_t *state = iface; 90 if (num_bufs != 1) { 91 ZF_LOGE("Dropping packets because num_received didn't match descriptor"); 92 for (int i = 0; i < num_bufs; i++) { 93 virtqueue_ring_object_t handle; 94 handle.first = (uintptr_t)cookies[i]; 95 handle.cur = (uintptr_t)cookies[i]; 96 if (!virtqueue_add_used_buf(&state->rx_virtqueue, &handle, 0)) { 97 ZF_LOGF("eth_rx_complete: Error while enqueuing used buffer, queue full"); 98 } 99 } 100 state->action = true; 101 return; 102 103 } 104 virtqueue_ring_object_t handle; 105 handle.first = (uintptr_t)cookies[0]; 106 handle.cur = (uintptr_t)cookies[0]; 107 if (!virtqueue_add_used_buf(&state->rx_virtqueue, &handle, lens[0])) { 108 ZF_LOGF("eth_rx_complete: Error while enqueuing used buffer, queue full"); 109 } 110 state->action = true; 111 return; 112} 113 114static struct raw_iface_callbacks ethdriver_callbacks = { 115 .tx_complete = eth_tx_complete, 116 .rx_complete = eth_rx_complete, 117 .allocate_rx_buf = eth_allocate_rx_buf 118}; 119 120 121 122static void client_get_mac(uint8_t *b1, uint8_t *b2, uint8_t *b3, uint8_t *b4, uint8_t *b5, uint8_t *b6, void *cookie) 123{ 124 server_data_t *state = cookie; 125 *b1 = state->hw_mac[0]; 126 *b2 = state->hw_mac[1]; 127 *b3 = state->hw_mac[2]; 128 *b4 = state->hw_mac[3]; 129 *b5 = state->hw_mac[4]; 130 *b6 = state->hw_mac[5]; 131} 132 133static void virt_queue_handle_irq(seL4_Word badge, void *cookie) 134{ 135 server_data_t *state = cookie; 136 if (state->no_rx_bufs) { 137 state->eth_driver->i_fn.raw_poll(state->eth_driver); 138 } 139 while (1) { 140 141 virtqueue_ring_object_t handle; 142 143 unsigned next = (state->tx_virtqueue.a_ring_last_seen + 1) & (state->tx_virtqueue.queue_len - 1); 144 145 if (next == state->tx_virtqueue.avail_ring->idx) { 146 break; 147 } 148 handle.first = state->tx_virtqueue.avail_ring->ring[next]; 149 handle.cur = handle.first; 150 151 void *buf; 152 unsigned len; 153 vq_flags_t flag; 154 int more = virtqueue_gather_available(&state->tx_virtqueue, &handle, &buf, &len, &flag); 155 if (more == 0) { 156 ZF_LOGF("tx_queue_handle_irq: Invalid virtqueue ring entry"); 157 } 158 159 uintptr_t phys = ps_dma_pin(&state->io_ops->dma_manager, DECODE_DMA_ADDRESS(buf), BUF_SIZE); 160 int err = state->eth_driver->i_fn.raw_tx(state->eth_driver, 1, (uintptr_t *) &phys, (unsigned int *)&len, 161 (void *)(uintptr_t)handle.first); 162 if (err != ETHIF_TX_ENQUEUED) { 163 state->blocked_tx = true; 164 break; 165 } else { 166 state->blocked_tx = false; 167 virtqueue_get_available_buf(&state->tx_virtqueue, &handle); 168 } 169 } 170 171} 172 173 174static void notify_client(UNUSED seL4_Word badge, void *cookie) 175{ 176 server_data_t *state = cookie; 177 if (state->action) { 178 if (state->blocked_tx) { 179 virt_queue_handle_irq(badge, cookie); 180 } 181 state->action = false; 182 state->tx_virtqueue.notify(); 183 } 184} 185 186 187static int hardware_interface_searcher(void *cookie, void *interface_instance, char **properties) 188{ 189 190 server_data_t *state = cookie; 191 state->eth_driver = interface_instance; 192 return PS_INTERFACE_FOUND_MATCH; 193} 194 195int picotcp_ethernet_async_server_init(ps_io_ops_t *io_ops, const char *tx_virtqueue, const char *rx_virtqueue, 196 register_callback_handler_fn_t register_handler, register_get_mac_server_fn register_get_mac_fn) 197{ 198 199 server_data_t *data; 200 int error = ps_calloc(&io_ops->malloc_ops, 1, sizeof(*data), (void **)&data); 201 data->io_ops = io_ops; 202 203 204 error = ps_interface_find(&io_ops->interface_registration_ops, 205 PS_ETHERNET_INTERFACE, hardware_interface_searcher, data); 206 if (error) { 207 ZF_LOGF("Unable to find an ethernet device"); 208 } 209 210 data->eth_driver->cb_cookie = data; 211 data->eth_driver->i_cb = ethdriver_callbacks; 212 213 seL4_Word tx_badge; 214 seL4_Word rx_badge; 215 216 /* Initialise read virtqueue */ 217 error = camkes_virtqueue_device_init_with_recv(&data->tx_virtqueue, camkes_virtqueue_get_id_from_name(tx_virtqueue), 218 NULL, &tx_badge); 219 if (error) { 220 ZF_LOGE("Unable to initialise serial server read virtqueue"); 221 } 222 /* Initialise write virtqueue */ 223 error = camkes_virtqueue_device_init_with_recv(&data->rx_virtqueue, camkes_virtqueue_get_id_from_name(rx_virtqueue), 224 NULL, &rx_badge); 225 if (error) { 226 ZF_LOGE("Unable to initialise serial server write virtqueue"); 227 } 228 229 error = register_handler(tx_badge, "tx_event", virt_queue_handle_irq, data); 230 if (error) { 231 ZF_LOGE("Unable to register handler"); 232 } 233 error = register_handler(rx_badge, "rx_event", virt_queue_handle_irq, data); 234 if (error) { 235 ZF_LOGE("Unable to register handler"); 236 } 237 238 error = register_handler(0, "notify_client", notify_client, data); 239 if (error) { 240 ZF_LOGE("Unable to register handler"); 241 } 242 243 244 data->eth_driver->i_fn.get_mac(data->eth_driver, data->hw_mac); 245 data->eth_driver->i_fn.raw_poll(data->eth_driver); 246 247 register_get_mac_fn(client_get_mac, data); 248 return 0; 249} 250