Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libveth / veth.c
diff --git a/qemu/roms/SLOF/lib/libveth/veth.c b/qemu/roms/SLOF/lib/libveth/veth.c
new file mode 100644 (file)
index 0000000..7487308
--- /dev/null
@@ -0,0 +1,273 @@
+/******************************************************************************
+ * Copyright (c) 2011, 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ *     IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <helpers.h>
+#include "veth.h"
+#include "libhvcall.h"
+
+#undef VETH_DEBUG
+//#define VETH_DEBUG
+#ifdef VETH_DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+/* *** WARNING: We pass our addresses as-is as DMA addresses,
+ *     we -do- rely on the forth code to have enabled TCE bypass
+ *     on our device !
+ */
+#define vaddr_to_dma(vaddr)    ((uint64_t)vaddr)
+
+struct ibmveth_buf_desc_fields {
+       uint32_t flags_len;
+#define IBMVETH_BUF_VALID      0x80000000
+#define IBMVETH_BUF_TOGGLE     0x40000000
+#define IBMVETH_BUF_NO_CSUM    0x02000000
+#define IBMVETH_BUF_CSUM_GOOD  0x01000000
+#define IBMVETH_BUF_LEN_MASK   0x00FFFFFF
+       uint32_t address;
+};
+
+union ibmveth_buf_desc {
+       uint64_t desc;
+       struct ibmveth_buf_desc_fields fields;
+};
+
+struct ibmveth_rx_q_entry {
+       uint32_t flags_off;
+#define IBMVETH_RXQ_TOGGLE             0x80000000
+#define IBMVETH_RXQ_TOGGLE_SHIFT       31
+#define IBMVETH_RXQ_VALID              0x40000000
+#define IBMVETH_RXQ_NO_CSUM            0x02000000
+#define IBMVETH_RXQ_CSUM_GOOD          0x01000000
+#define IBMVETH_RXQ_OFF_MASK           0x0000FFFF
+
+       uint32_t length;
+       uint64_t correlator;
+};
+
+static void *buffer_list; 
+static void *filter_list; 
+static uint64_t *rx_bufs;
+static uint64_t *rx_bufs_aligned;
+static uint32_t cur_rx_toggle;
+static uint32_t cur_rx_index;
+
+#define RX_QUEUE_SIZE  256
+#define RX_BUF_SIZE    2048
+#define RX_BUF_MULT    (RX_BUF_SIZE >> 3)
+
+static struct ibmveth_rx_q_entry *rx_queue;
+
+static inline uint64_t *veth_get_rx_buf(unsigned int i)
+{
+       return &rx_bufs_aligned[i * RX_BUF_MULT];
+}
+
+static int veth_init(net_driver_t *driver)
+{
+       char *mac_addr;
+       union ibmveth_buf_desc rxq_desc;
+       unsigned long rx_queue_len = sizeof(struct ibmveth_rx_q_entry) *
+               RX_QUEUE_SIZE;
+       unsigned int i;
+       long rc;
+
+       if (!driver)
+               return -1;
+
+       dprintf("veth_init(%02x:%02x:%02x:%02x:%02x:%02x)\n",
+               mac_addr[0], mac_addr[1], mac_addr[2],
+               mac_addr[3], mac_addr[4], mac_addr[5]);
+
+       if (driver->running != 0)
+               return 0;
+
+       mac_addr = (char *)driver->mac_addr;
+       cur_rx_toggle = IBMVETH_RXQ_TOGGLE;
+       cur_rx_index = 0;
+       buffer_list = SLOF_alloc_mem_aligned(8192, 4096);
+       filter_list = buffer_list + 4096;
+       rx_queue = SLOF_alloc_mem_aligned(rx_queue_len, 16);
+       rx_bufs = SLOF_alloc_mem(2048 * RX_QUEUE_SIZE + 4);
+       if (!buffer_list || !filter_list || !rx_queue || !rx_bufs) {
+               printf("veth: Failed to allocate memory !\n");
+               goto fail;
+       }
+       rx_bufs_aligned = (uint64_t *)(((uint64_t)rx_bufs | 3) + 1);
+       rxq_desc.fields.address = vaddr_to_dma(rx_queue);
+       rxq_desc.fields.flags_len = IBMVETH_BUF_VALID | rx_queue_len;
+
+       rc = h_register_logical_lan(driver->reg,
+                                   vaddr_to_dma(buffer_list),
+                                   rxq_desc.desc,
+                                   vaddr_to_dma(filter_list),
+                                   (*(uint64_t *)mac_addr) >> 16);
+       if (rc != H_SUCCESS) {
+               printf("veth: Error %ld registering interface !\n", rc);
+               goto fail;
+       }
+       for (i = 0; i < RX_QUEUE_SIZE; i++) {
+               uint64_t *buf = veth_get_rx_buf(i);
+               union ibmveth_buf_desc desc;
+               *buf = (uint64_t)buf;
+               desc.fields.address = vaddr_to_dma(buf);
+               desc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE;
+               h_add_logical_lan_buffer(driver->reg, desc.desc);
+       }
+
+       driver->running = 1;
+
+       return 0;
+ fail:
+       if (buffer_list)
+               SLOF_free_mem(buffer_list, 8192);
+       if (rx_queue)
+               SLOF_free_mem(rx_queue, rx_queue_len);
+       if (rx_bufs)
+               SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4);
+       return -1;
+}
+
+static int veth_term(net_driver_t *driver)
+{
+       dprintf("veth_term()\n");
+
+       if (driver->running == 0)
+               return 0;
+
+       h_free_logical_lan(driver->reg);
+
+       if (buffer_list)
+               SLOF_free_mem(buffer_list, 8192);
+       if (rx_queue)
+               SLOF_free_mem(rx_queue, sizeof(struct ibmveth_rx_q_entry) * RX_QUEUE_SIZE);
+       if (rx_bufs)
+               SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4);
+
+       driver->running = 0;
+
+       return 0;
+}
+
+static int veth_receive(char *f_buffer_pc, int f_len_i, net_driver_t *driver)
+{
+       int packet = 0;
+
+       dprintf("veth_receive()\n");
+
+       while(!packet) {
+               struct ibmveth_rx_q_entry *desc = &rx_queue[cur_rx_index];
+               union ibmveth_buf_desc bdesc;
+               void *buf;
+
+               buf = (void *)desc->correlator;
+
+               if ((desc->flags_off & IBMVETH_RXQ_TOGGLE) != cur_rx_toggle)
+                       break;
+
+               if (!(desc->flags_off & IBMVETH_RXQ_VALID))
+                       goto recycle;
+               if (desc->length > f_len_i) {
+                       printf("veth: Dropping too big packet [%d bytes]\n",
+                              desc->length);
+                       goto recycle;
+               }
+
+               packet = desc->length;
+               memcpy(f_buffer_pc,
+                      buf + (desc->flags_off & IBMVETH_RXQ_OFF_MASK), packet);
+       recycle:
+               bdesc.fields.address = vaddr_to_dma(buf);
+               bdesc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE;
+               h_add_logical_lan_buffer(driver->reg, bdesc.desc);
+
+               cur_rx_index = (cur_rx_index + 1) % RX_QUEUE_SIZE;
+               if (cur_rx_index == 0)
+                       cur_rx_toggle ^= IBMVETH_RXQ_TOGGLE;
+       }
+
+       return packet;
+}
+
+static int veth_xmit(char *f_buffer_pc, int f_len_i, net_driver_t *driver)
+{
+       union ibmveth_buf_desc tx_desc;
+       long rc;
+
+       dprintf("veth_xmit(packet at %p, %d bytes)\n", f_buffer_pc, f_len_i);
+
+       tx_desc.fields.address = vaddr_to_dma(f_buffer_pc);
+       tx_desc.fields.flags_len = IBMVETH_BUF_VALID | f_len_i;
+
+       rc = hv_send_logical_lan(driver->reg, tx_desc.desc, 0, 0, 0, 0, 0);
+       if (rc != H_SUCCESS) {
+               printf("veth: Error %ld sending packet !\n", rc);
+               return -1;
+       }
+
+       return f_len_i;
+}
+
+net_driver_t *libveth_open(char *mac_addr, int mac_len, char *reg, int reg_len)
+{
+       net_driver_t *driver;
+
+       driver = SLOF_alloc_mem(sizeof(*driver));
+       if (!driver) {
+               printf("Unable to allocate veth driver\n");
+               return NULL;
+       }
+
+       /* veth uses a 8-byte wide property instead of 6-byte wide MACs */
+       if ((mac_len == 8) && (mac_addr[0] == 0) && mac_addr[1] == 0)
+               mac_addr += 2;
+       memcpy(driver->mac_addr, mac_addr, 6);
+       driver->reg = *(uint32_t *)reg;
+       driver->running = 0;
+
+       if (veth_init(driver)) {
+               SLOF_free_mem(driver, sizeof(*driver));
+               return NULL;
+       }
+
+       return driver;
+}
+
+void libveth_close(net_driver_t *driver)
+{
+       if (driver) {
+               veth_term(driver);
+               SLOF_free_mem(driver, sizeof(*driver));
+       }
+}
+
+int libveth_read(char *buf, int len, net_driver_t *driver)
+{
+       if (buf)
+               return veth_receive(buf, len, driver);
+
+       return -1;
+}
+
+int libveth_write(char *buf, int len, net_driver_t *driver)
+{
+       if (buf)
+               return veth_xmit(buf, len, driver);
+
+       return -1;
+}