--- /dev/null
+/*
+ * Copyright (c) 2017 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+#ifndef E10K_DEVIF_H_
+#define E10K_DEVIF_H_ 1
+
+struct e10k_queue;
+typedef void (*e10k_event_cb_t)(void* q);
+
+errval_t e10k_queue_create(struct e10k_queue** q, e10k_event_cb_t cb,
+ bool use_vf, bool interrupts);
+errval_t e10k_queue_destroy(struct e10k_queue* q);
+#endif
--- /dev/null
+--------------------------------------------------------------------------
+-- Copyright (c) 2017, ETH Zurich.
+-- All rights reserved.
+--
+-- This file is distributed under the terms in the attached LICENSE file.
+-- If you do not find this file, copies can be found by writing to:
+-- ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+--
+-- Hakefile for lib/backend/net/e10k/
+--
+-- Intel e10k backend implementation
+--
+--------------------------------------------------------------------------
+
+ [
+ build library { target = "devif_backend_e10k",
+ cFiles = [ "devif_backend_e10k.c", "e10k_devif_vf.c",
+ "helper.c", "sleep.c"],
+ flounderBindings = [ "e10k_vf"],
+ flounderExtraBindings = [ ("e10k_vf", ["rpcclient"])],
+ flounderDefs = [ "e10k_vf" ],
+ flounderExtraDefs = [ ("e10k_vf",["rpcclient"]) ],
+ mackerelDevices = [ "e10k_vf", "e10k"],
+ addLibraries = libDeps ["pci", "skb", "acpi_client"]
+ }
+ ]
+
+
--- /dev/null
+/*
+ * Copyright (c) 2017 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+//#define DEBUG_E10K_VF 1
+//#define DEBUG_E10K_QUEUE 1
+
+/*****************************************************************
+ * Debug printer:
+ *****************************************************************/
+
+#if defined(DEBUG_E10K_VF)
+#define DEBUG_VF(x...) do { printf("e10k_vf:%s.%d:%s:%d: ", \
+ disp_name(), disp_get_core_id(), __func__, __LINE__); \
+ printf(x);\
+ } while (0)
+
+#else
+#define DEBUG_VF(x...) ((void)0)
+#endif
+
+#if defined(DEBUG_E10K_QUEUE)
+#define DEBUG_QUEUE(x...) do { printf("e10k_queue:%s.%d:%s:%d: ", \
+ disp_name(), disp_get_core_id(), __func__, __LINE__); \
+ printf(x);\
+ } while (0)
+#else
+#define DEBUG_QUEUE(x...) ((void)0)
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007-2011, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/nameservice_client.h>
+#include <devif/backends/net/e10k_devif.h>
+#include <devif/queue_interface.h>
+#include <dev/e10k_q_dev.h>
+#include <dev/e10k_dev.h>
+#include <dev/e10k_vf_dev.h>
+#include <skb/skb.h>
+
+
+#include <if/e10k_vf_defs.h>
+#include <if/e10k_vf_rpcclient_defs.h>
+
+#include "e10k_devif_vf.h"
+#include "helper.h"
+#include "e10k_queue.h"
+#include "debug.h"
+
+#define NUM_TX_DESC 2048
+#define NUM_RX_DESC 2048
+
+#define ETHHDR_LEN 14
+#define IPHDR_LEN 20
+#define UDPHDR_LEN 8
+
+/******************************************************************************/
+/* Misc functions */
+
+
+static inline bool buf_use_tcpxsm(uint64_t flags)
+{
+ return (flags & NETIF_TXFLAG_TCPCHECKSUM);
+}
+
+static inline bool buf_use_udpxsm(uint64_t flags)
+{
+ return (flags & NETIF_TXFLAG_UDPCHECKSUM);
+}
+
+static inline bool buf_use_ipxsm(uint64_t flags)
+{
+ return (flags & NETIF_TXFLAG_IPCHECKSUM) ||
+ buf_use_tcpxsm(flags) || buf_use_udpxsm(flags);
+}
+
+static inline uint8_t buf_tcphdrlen(uint64_t flags)
+{
+ return ((flags & NETIF_TXFLAG_TCPHDRLEN_MASK) >>
+ NETIF_TXFLAG_TCPHDRLEN_SHIFT) * 4;
+}
+
+static errval_t update_txtail(struct e10k_queue* q, size_t tail)
+{
+ assert(q->d != NULL);
+
+ if (q->use_vf) {
+ e10k_vf_vftdt_wr(q->d, q->id, tail);
+ } else {
+ e10k_tdt_wr(q->d, q->id, tail);
+ }
+ return SYS_ERR_OK;
+}
+
+static errval_t update_rxtail(struct e10k_queue* q, size_t tail)
+{
+ assert(q->d != NULL);
+
+ if (q->use_vf) {
+ e10k_vf_vfrdt_wr(q->d, q->id, tail);
+ } else {
+ e10k_rdt_1_wr(q->d, q->id, tail);
+ }
+ return SYS_ERR_OK;
+}
+
+
+static struct region_entry* get_region(struct e10k_queue* q, regionid_t rid)
+{
+ struct region_entry* entry = q->regions;
+ while (entry != NULL) {
+ if (entry->rid == rid) {
+ return entry;
+ }
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+
+static errval_t enqueue_tx_buf(struct e10k_queue* q, regionid_t rid,
+ bufferid_t bid, lpaddr_t base, size_t len,
+ uint64_t flags)
+{
+ DEBUG_QUEUE("Enqueueing TX buf \n");
+
+ if (e10k_queue_free_txslots(q) == 0) {
+ DEBUG_QUEUE("e10k_%d: Not enough space in TX ring, not adding buffer\n",
+ q->id);
+ // TODO better error
+ return SFN_ERR_ENQUEUE;
+ }
+
+ bool last = flags & NETIF_TXFLAG_LAST;
+ bool first = flags & NETIF_TXFLAG_FIRST;
+ // Prepare checksum offload
+ if (buf_use_ipxsm(flags)) {
+ e10k_q_l4_type_t l4t = 0;
+ uint8_t l4len = 0;
+
+ if (buf_use_tcpxsm(flags)) {
+ l4t = e10k_q_tcp;
+ l4len = buf_tcphdrlen(flags);
+ } else if (buf_use_udpxsm(flags)) {
+ l4t = e10k_q_udp;
+ l4len = UDPHDR_LEN;
+ }
+ e10k_queue_add_txcontext(q, 0, ETHHDR_LEN, IPHDR_LEN, l4len, l4t);
+
+ if (q->use_vtd) {
+ // get virtual address of buffer
+ struct region_entry* entry = get_region(q, rid);
+ assert(entry != NULL);
+
+ lpaddr_t addr = 0;
+ addr = (lpaddr_t) entry->virt + (base-entry->phys);
+ e10k_queue_add_txbuf_ctx(q, rid, bid, addr, len, flags,
+ first, last, len, 0, true, l4len !=0);
+ } else {
+
+ e10k_queue_add_txbuf_ctx(q, rid, bid, base, len, flags,
+ first, last, len, 0, true, l4len != 0);
+ }
+ } else {
+ if (q->use_vtd) {
+ // get virtual address of buffer
+ struct region_entry* entry = get_region(q, rid);
+ assert(entry != NULL);
+
+ lpaddr_t addr = 0;
+ addr = (lpaddr_t) entry->virt + (base-entry->phys);
+ e10k_queue_add_txbuf(q, rid, bid, addr, len, flags,
+ first, last, len);
+ } else {
+ e10k_queue_add_txbuf(q, rid, bid, base, len, flags, first, last, len);
+ }
+ }
+ e10k_queue_bump_txtail(q);
+ return SYS_ERR_OK;
+}
+
+
+static errval_t enqueue_rx_buf(struct e10k_queue* q, regionid_t rid,
+ bufferid_t bid, lpaddr_t base, size_t len,
+ uint64_t flags)
+{
+ DEBUG_QUEUE("Enqueueing RX buf \n");
+ // check if there is space
+ if (e10k_queue_free_rxslots(q) == 0) {
+ DEBUG_QUEUE("e10k_%d: Not enough space in RX ring, not adding buffer\n",
+ q->id);
+ // TODO better error
+ return SFN_ERR_ENQUEUE;
+ }
+
+ if (q->use_vtd) {
+ // get virtual address of buffer
+ struct region_entry* entry = get_region(q, rid);
+ assert(entry != NULL);
+
+ lpaddr_t addr = 0;
+ addr = (lpaddr_t) entry->virt + (base-entry->phys);
+ e10k_queue_add_rxbuf(q, rid, bid, addr, len, flags);
+ } else {
+ e10k_queue_add_rxbuf(q, rid, bid, base, len, flags);
+ }
+
+ DEBUG_QUEUE("before bump tail\n");
+ e10k_queue_bump_rxtail(q);
+ DEBUG_QUEUE("Enqueueing RX buf: terminated\n");
+ return SYS_ERR_OK;
+}
+
+/******************************************************************************/
+/* Queue functions */
+
+static errval_t e10k_enqueue(struct devq* q, regionid_t rid, bufferid_t bid,
+ lpaddr_t base, size_t len, uint64_t flags)
+{
+ errval_t err;
+
+
+ struct e10k_queue* queue = (struct e10k_queue*) q;
+ if (flags & NETIF_RXFLAG) {
+ /* can not enqueue receive buffer larger than 2048 bytes */
+ assert(len <= 2048);
+
+ err = enqueue_rx_buf(queue, rid, bid, base, len, flags);
+ if (err_is_fail(err)) {
+ return err;
+ }
+ } else if (flags & NETIF_TXFLAG) {
+
+ assert(len <= 2048);
+
+ err = enqueue_tx_buf(queue, rid, bid, base, len, flags);
+ if (err_is_fail(err)) {
+ return err;
+ }
+ }
+
+ return SYS_ERR_OK;
+}
+
+
+static errval_t e10k_dequeue(struct devq* q, regionid_t* rid, bufferid_t* bid,
+ lpaddr_t* base, size_t* len, uint64_t* flags)
+{
+ struct e10k_queue* que = (struct e10k_queue*) q;
+ int last;
+ errval_t err = SYS_ERR_OK;
+
+ if (!e10k_queue_get_rxbuf(que, rid, bid, base, len, flags, &last)) {
+ err = DEVQ_ERR_RX_EMPTY;
+ } else {
+ return SYS_ERR_OK;
+ }
+
+ if (!e10k_queue_get_txbuf(que, rid, bid, base, len, flags)) {
+ err = DEVQ_ERR_RX_EMPTY;
+ } else {
+ return SYS_ERR_OK;
+ }
+
+ return err;
+}
+
+static errval_t e10k_register(struct devq* q, struct capref cap, regionid_t rid)
+{
+ errval_t err;
+ struct e10k_queue* queue = (struct e10k_queue*) q;
+
+ struct frame_identity id;
+ err = invoke_frame_identify(cap, &id);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ struct capref cr;
+ err = slot_alloc(&cr);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ err = cap_copy(cr, cap);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ void* va;
+ err = vspace_map_one_frame_attr(&va, id.bytes, cr,
+ VREGION_FLAGS_READ_WRITE_NOCACHE,
+ NULL, NULL);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ // keep track of regions since we need the virtual address ...
+ struct region_entry* entry = malloc(sizeof(struct region_entry));
+ entry->rid = rid;
+ entry->cap = cap;
+ entry->phys = id.base;
+ entry->virt = (lvaddr_t)va;
+ entry->size = id.bytes;
+ entry->next = NULL;
+
+ // linked list of regions
+ struct region_entry* cur = queue->regions;
+ if (cur == NULL) {
+ queue->regions = entry;
+ return SYS_ERR_OK;
+ }
+
+ while (cur->next != NULL) {
+ cur = cur->next;
+ }
+
+ cur->next = entry;
+
+ return SYS_ERR_OK;
+}
+
+static errval_t e10k_deregister(struct devq* q, regionid_t rid)
+{
+ return SYS_ERR_OK;
+}
+
+static errval_t e10k_control(struct devq* q, uint64_t cmd, uint64_t value)
+{
+ return SYS_ERR_OK;
+}
+
+
+static errval_t e10k_notify(struct devq* q)
+{
+ return SYS_ERR_OK;
+}
+
+/******************************************************************
+ * Management functions
+ *
+ */
+
+static void bind_cb(void *st, errval_t err, struct e10k_vf_binding *b)
+{
+ struct e10k_queue* q = (struct e10k_queue*) st;
+ assert(err_is_ok(err));
+
+ DEBUG_QUEUE("Sucessfully connected to management interface\n");
+
+ struct e10k_vf_rpc_client *r = malloc(sizeof(*r));
+ assert(r != NULL);
+ err = e10k_vf_rpc_client_init(r, b);
+ assert(err_is_ok(err));
+ b->st = q;
+ q->binding = r;
+ q->bound = true;
+
+}
+
+/** Connect to the management interface */
+static void connect_to_mngif(struct e10k_queue* q)
+{
+ errval_t r;
+ iref_t iref;
+
+ q->bound = false;
+ char name[strlen("e10k_vf") + 2];
+
+ // Build label for interal management service
+ sprintf(name, "%s%u", "e10k_vf", q->pci_function);
+
+ // Connect to service
+ DEBUG_QUEUE("Looking up management interface (%s)\n", name);
+ r = nameservice_blocking_lookup(name, &iref);
+ assert(err_is_ok(r));
+
+ DEBUG_QUEUE("Binding to management interface\n");
+ r = e10k_vf_bind(iref, bind_cb, q, get_default_waitset(),
+ IDC_BIND_FLAGS_DEFAULT);
+ assert(err_is_ok(r));
+
+ while (!q->bound) {
+ event_dispatch(get_default_waitset());
+ }
+}
+
+/*********************************************************
+ * Queue creation and destruction
+ */
+
+
+errval_t e10k_queue_destroy(struct e10k_queue* queue)
+{
+ //TODO: do the cleanup
+ return SYS_ERR_OK;
+}
+
+static errval_t map_device_memory(struct e10k_queue* q,
+ struct capref regs)
+{
+
+ struct frame_identity id = {.base = 0, .bytes = 0};
+ errval_t err;
+
+ err = invoke_frame_identify(regs, &id);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ void* va;
+ err = vspace_map_one_frame_attr(&va, id.bytes, regs,
+ VREGION_FLAGS_READ_WRITE_NOCACHE,
+ NULL, NULL);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+
+ DEBUG_QUEUE("mapped %zu bytes at address %p\n", id.bytes, va);
+ q->d = malloc(sizeof(e10k_t));
+ assert(q->d != NULL);
+ e10k_initialize(q->d, va);
+ return SYS_ERR_OK;
+}
+// TODO mostly cleanup when fail
+errval_t e10k_queue_create(struct e10k_queue** queue, e10k_event_cb_t cb,
+ bool use_vf, bool interrupts)
+{
+
+ errval_t err;
+ struct e10k_queue* q;
+ // start VF driver
+
+ q = malloc(sizeof(struct e10k_queue));
+ q->pci_function = 0; // TODO allow also function 1
+
+ if (use_vf) {
+ USER_PANIC("NOT YET WORKING \n");
+ // Start VF
+ if (!e10k_vf_started()) {
+ err = e10k_init_vf_driver(q->pci_function, interrupts);
+ if (err_is_fail(err)) {
+ return err;
+ }
+ }
+
+ // If i can not create any more queues -> start new VF
+ if (!e10k_vf_can_create_queue()) {
+ err = e10k_init_vf_driver(q->pci_function, interrupts);
+ if (err_is_fail(err)) {
+ return err;
+ }
+ }
+
+ // TODO: VF queues only work with VT-d enabled?
+ err = skb_client_connect();
+ assert(err_is_ok(err));
+ err = skb_execute_query("vtd_enabled(0,_).");
+ if (err_is_fail(err)) {
+ DEBUG_QUEUE("Assume disabled VT-d \n");
+ //q->use_vtd = false;
+ } else {
+ DEBUG_QUEUE("Assume enabled VT-d \n");
+ //q->use_vtd = true;
+ }
+
+ } else {
+ q->use_vtd = false;
+ // need to set up communicaton to PF
+ connect_to_mngif(q);
+ }
+
+ // allocate memory for RX/TX rings
+ struct capref tx_frame;
+ size_t tx_size = e10k_q_tdesc_legacy_size*NUM_TX_DESC;
+ void* tx_virt = alloc_map_frame(VREGION_FLAGS_READ_WRITE, tx_size, &tx_frame);
+ if (tx_virt == NULL) {
+ return DEVQ_ERR_INIT_QUEUE;
+ }
+
+
+ struct capref rx_frame;
+ size_t rx_size = e10k_q_tdesc_legacy_size*NUM_TX_DESC;
+ void* rx_virt = alloc_map_frame(VREGION_FLAGS_READ_WRITE, rx_size, &rx_frame);
+ if (rx_virt == NULL) {
+ return DEVQ_ERR_INIT_QUEUE;
+ }
+
+ struct e10k_queue_ops ops = {
+ .update_txtail = update_txtail,
+ .update_rxtail = update_rxtail,
+ };
+
+ struct capref txhwb_frame = NULL_CAP;
+ void* txhwb_virt = NULL;
+#if 0
+ if (use_txhwb) {
+ txhwb_virt = alloc_map_frame(VREGION_FLAGS_READ_WRITE, BASE_PAGE_SIZE,
+ &txhwb_frame);
+ if (txhwb_virt == NULL) {
+ return DEVQ_ERR_INIT_QUEUE;
+ }
+ memset(txhwb_virt, 0, sizeof(uint32_t));
+ }
+#endif
+ e10k_queue_init(q, tx_virt, NUM_TX_DESC, txhwb_virt,
+ rx_virt, NUM_RX_DESC, &ops);
+
+ DEBUG_QUEUE("Local queue init done\n");
+
+ q->use_vf = use_vf;
+ q->rx_frame = rx_frame;
+ q->tx_frame = tx_frame;
+ q->txhwb_frame = txhwb_frame;
+ q->use_irq = interrupts;
+
+ // XXX:disable by default for now
+ q->use_txhwb = false;
+ q->use_rsc = false;
+
+ if (q->use_vf) {
+ err = e10k_vf_init_queue_hw(q);
+ if (err_is_fail(err)) {
+ return err;
+ }
+ } else {
+ struct capref regs;
+
+ // Inform card driver about new queue and get the registers/queue id
+ err = slot_alloc(®s);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ if (q->use_irq) {
+ /*
+ err = pci_setup_inthandler(interrupt_handler, NULL, &vector);
+ assert(err_is_ok(err));
+ core = disp_get_core_id();
+ */
+ // TODO setup MSI-X interrupts
+ }
+
+ int q_id;
+ err = q->binding->vtbl.create_queue(q->binding, tx_frame, txhwb_frame,
+ rx_frame, 2048, q->msix_intvec,
+ q->msix_intdest, false, false, &q_id,
+ ®s);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ assert(q_id >= 0);
+ q->id = (uint16_t)q_id;
+
+ err = map_device_memory(q, regs);
+ if (err_is_fail(err)) {
+ return err;
+ }
+ }
+
+ err = devq_init(&q->q, false);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ q->q.f.enq = e10k_enqueue;
+ q->q.f.deq = e10k_dequeue;
+ q->q.f.reg = e10k_register;
+ q->q.f.dereg = e10k_deregister;
+ q->q.f.ctrl = e10k_control;
+ q->q.f.notify = e10k_notify;
+
+
+ *queue = q;
+
+ return SYS_ERR_OK;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2013, 2014, University of Washington, 2017 ETH Zürich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <net_device_manager/net_device_manager.h>
+#include <pci/pci.h>
+#include <barrelfish/nameservice_client.h>
+#include <barrelfish/debug.h>
+#include <ipv4/lwip/inet.h>
+#include <acpi_client/acpi_client.h>
+
+
+#include <if/e10k_defs.h>
+#include <if/e10k_vf_defs.h>
+#include <if/e10k_vf_rpcclient_defs.h>
+#include <dev/e10k_vf_dev.h>
+#include <dev/e10k_q_dev.h>
+
+#include "sleep.h"
+#include "helper.h"
+#include "e10k_devif_vf.h"
+#include "e10k_queue.h"
+#include "debug.h"
+
+
+#define POOL_SIZE 2
+
+
+#define E10K_PCI_DEVID 0x10ED
+
+
+#define QUEUE_INTRX 0
+#define QUEUE_INTTX 1
+
+
+struct vf_state {
+ uint8_t vf_num;
+ // if we have 64 vfs then pool
+ // size is only 2 but the maximum is 8
+ struct e10k_queue* qs[POOL_SIZE];
+ bool q_enabled[8];
+
+ uint64_t vf_mac;
+
+ // device info
+ int initialized;
+ e10k_vf_t *d;
+ struct capref *regframe;
+ uint64_t d_mac;
+ uint32_t pci_function;
+
+
+ // interrupt related
+ bool msix;
+ struct bmallocator msix_alloc;
+ size_t vdriver_msix;
+ uint8_t vdriver_vector;
+ bool use_interrupts;
+
+ //
+ struct e10k_vf_rpc_client *binding;
+ bool bound;
+};
+
+static struct vf_state* vf;
+
+
+#define prnonz(x) \
+ uint32_t x = e10k_vf_##x##_rd(vf->d); \
+ snprintf(str[cnt++], 32, #x "=%x \n", x); \
+
+static void stats_dump(void)
+{
+ char str[256][32];
+ int cnt = 0;
+ memset(str, 0, 256 * 32);
+
+ prnonz(vfctrl);
+ prnonz(vfstatus);
+ prnonz(vflinks);
+ prnonz(vfrxmemwrap);
+ prnonz(vfeicr);
+ prnonz(vfeics);
+ prnonz(vfeims);
+ prnonz(vfpsrtype);
+
+ if(cnt > 0) {
+ for(int i = 0; i < cnt; i++) {
+ printf("%s ", str[i]);
+ }
+ printf("\n");
+ }
+}
+
+static void setup_interrupt(size_t *msix_index, uint8_t core, uint8_t vector)
+{
+ bool res;
+ errval_t err;
+ uint8_t dest;
+
+ res = bmallocator_alloc(&vf->msix_alloc, msix_index);
+ assert(res);
+
+ err = get_apicid_from_core(core, &dest);
+ assert(err_is_ok(err));
+
+ err = pci_msix_vector_init(*msix_index, dest, vector);
+ assert(err_is_ok(err));
+
+ DEBUG_VF("e10k: MSI-X vector setup index=%"PRIx64", core=%d apic=%d swvec=%x\n",
+ *msix_index, core, dest, vector);
+}
+
+
+static void interrupt_handler_msix(void* arg)
+{
+ struct e10k_queue* q = (struct e10k_queue*)arg;
+ DEBUG_VF("MSI-X management interrupt\n");
+ e10k_vf_vfeicr_t eicr = e10k_vf_vfeicr_rd(vf->d);
+
+ eicr &= ~(1 << q->msix_index);
+
+ // Ensure management MSI-X vector is cleared
+ e10k_vf_vfeicr_wr(vf->d, 1 << q->msix_index);
+
+ // Reenable interrupt
+ e10k_vf_vfeimc_msix_wrf(vf->d, 1 << (q->msix_index % 32));
+ // TODO check for packets?
+}
+
+/** Stop whole device. */
+static void stop_device(struct vf_state* v)
+{
+ DEBUG_VF("Stopping device\n");
+
+ // Disable interrupts
+ e10k_vf_vfeimc_msix_wrf(v->d, 7);
+ e10k_vf_vfeicr_rd(v->d);
+
+ // Disable each RX and TX queue
+ for(int i = 0; i < 4; i++) {
+ e10k_vf_vftxdctl_wr(v->d, i, e10k_vf_vftxdctl_swflsh_insert(0x0, 1));
+ }
+ for(int i = 0; i < 8; i++) {
+ e10k_vf_vfrxdctl_wr(v->d, i, 0x0);
+ }
+
+ // From BSD driver (not in spec)
+ milli_sleep(2);
+}
+
+/**
+ * Initialize hardware registers.
+ * Is also called after a reset of the device.
+ */
+static void device_init(void)
+{
+
+ int i;
+ errval_t err;
+ bool initialized_before = vf->initialized;
+
+ vf->initialized = 0;
+
+ stats_dump();
+
+ stop_device(vf);
+
+ assert(!initialized_before);
+
+ // Issue Global reset
+ e10k_vf_vfctrl_rst_wrf(vf->d, 1);
+ // Spec says 10, fbsd driver 50
+ milli_sleep(5000);
+ DEBUG_VF("Global reset done\n");
+
+ // Disable interrupts
+ e10k_vf_vfeimc_msix_wrf(vf->d, 7);
+ e10k_vf_vfeicr_rd(vf->d);
+
+ // Wait for link to come up
+ DEBUG_VF("Waiting for Link\n");
+ stats_dump();
+ //while (e10k_vf_vflinks_lnk_up_rdf(vf->d) == 0); // TODO: Timeout and Uncomment
+ DEBUG_VF("Link Up\n");
+ milli_sleep(50);
+
+ // Initialize interrupts
+ e10k_vf_vfeicr_wr(vf->d, 7);
+ e10k_vf_vfeitr_wr(vf->d, 0, 0x0);
+ e10k_vf_vfeitr_wr(vf->d, 1, 0x0);
+
+ if (vf->msix) {
+ // Allocate msix vector for cdriver and set up handler
+ if (vf->vdriver_msix == -1) {
+ err = pci_setup_inthandler(interrupt_handler_msix, NULL,
+ &vf->vdriver_vector);
+ assert(err_is_ok(err));
+
+ setup_interrupt(&vf->vdriver_msix, disp_get_core_id(),
+ vf->vdriver_vector);
+ }
+
+ // Map management interrupts to our vector
+ e10k_vf_vfivar_misc_i_alloc0_wrf(vf->d, vf->vdriver_msix);
+ e10k_vf_vfivar_misc_i_allocval0_wrf(vf->d, 1);
+
+ // Enable interrupt
+ e10k_vf_vfeitr_wr(vf->d, vf->vdriver_msix / 32, (1 << (vf->vdriver_msix % 32)));
+ } else {
+ // Enable all interrupts
+ e10k_vf_vfeimc_wr(vf->d, e10k_vf_vfeims_rd(vf->d));
+ e10k_vf_vfeims_msix_wrf(vf->d, 7);
+ }
+
+ // Other stuff
+ e10k_vf_vfpsrtype_wr(vf->d, 0);
+
+ // disable relaxed ordering
+ for (i = 0; i < 8; i++) {
+ e10k_vf_vfdca_txctrl_txdesc_wbro_wrf(vf->d, i, 0);
+ e10k_vf_vfdca_rxctrl_rxhdr_ro_wrf(vf->d, i, 0);
+ e10k_vf_vfdca_rxctrl_rxdata_wrro_wrf(vf->d, i, 0);
+ }
+
+ // enable all queues
+ for (i = 0; i < POOL_SIZE; i++) {
+ e10k_vf_vftxdctl_enable_wrf(vf->d, i, 1);
+ }
+
+ vf->initialized = 1;
+}
+
+/** Initialize hardware queue n. */
+static void queue_hw_init(struct e10k_queue* q)
+{
+ errval_t r;
+ struct frame_identity frameid = { .base = 0, .bytes = 0 };
+ uint64_t tx_phys, txhwb_phys, rx_phys;
+ size_t tx_size, rx_size;
+ uint8_t n = q->id;
+ e10k_vf_t* d = q->d;
+
+ // Get physical addresses for rx/tx rings
+ r = invoke_frame_identify(q->tx_frame, &frameid);
+ assert(err_is_ok(r));
+ tx_phys = frameid.base;
+ tx_size = frameid.bytes;
+
+ r = invoke_frame_identify(q->rx_frame, &frameid);
+ assert(err_is_ok(r));
+ rx_phys = frameid.base;
+ rx_size = frameid.bytes;
+
+
+ DEBUG_VF("tx.phys=%"PRIx64" tx.size=%"PRIu64"\n", tx_phys, tx_size);
+ DEBUG_VF("rx.phys=%"PRIx64" rx.size=%"PRIu64"\n", rx_phys, rx_size);
+
+
+ // Initialize RX queue in HW
+ e10k_vf_vfrdbal_wr(d, n, rx_phys);
+ e10k_vf_vfrdbah_wr(d, n, rx_phys >> 32);
+ e10k_vf_vfrdlen_wr(d, n, rx_size);
+
+ e10k_vf_vfsrrctl_bsz_pkt_wrf(d, n, q->rxbufsz / 1024);
+ e10k_vf_vfsrrctl_bsz_hdr_wrf(d, n, 128 / 64); // TODO: Do 128 bytes suffice in
+ // all cases?
+ e10k_vf_vfsrrctl_desctype_wrf(d, n, e10k_vf_adv_1buf);
+ e10k_vf_vfsrrctl_drop_en_wrf(d, n, 1);
+
+ // Initialize queue pointers (empty)
+ e10k_vf_vfrdt_wr(d, n, q->rx_head);
+ e10k_vf_vfrdh_wr(d, n, q->rx_head);
+
+ e10k_vf_vfrxdctl_enable_wrf(d, n, 1);
+ while (e10k_vf_vfrxdctl_enable_rdf(d, n) == 0); // TODO: Timeout
+ DEBUG_VF("[%x] RX queue enabled\n", n);
+
+ // Setup Interrupts for this queue
+ if (q->use_irq) {
+ DEBUG_VF("[%x] Setting up interrupts\n", n);
+ uint8_t rxv, txv;
+ // Look for interrupt vector
+ if (q->msix_intvec != 0) {
+ if (q->msix_index == -1) {
+ setup_interrupt(&q->msix_index, q->msix_intdest,
+ q->msix_intvec);
+ }
+ rxv = txv = q->msix_index;
+ }
+
+ // XXX. Please double check, this is against the original intention
+ // Map rxv/txv to eicr bits that we can recognize
+ rxv = QUEUE_INTRX;
+ txv = QUEUE_INTTX;
+
+ // DEBUG_VF("rxv=%d txv=%d\n", rxv, txv);
+
+ // Setup mapping queue Rx/Tx -> interrupt
+ uint8_t i = n / 2;
+ if ((n % 2) == 0) {
+ e10k_vf_vfivar_i_alloc0_wrf(d, i, rxv);
+ e10k_vf_vfivar_i_allocval0_wrf(d, i, 1);
+ e10k_vf_vfivar_i_alloc1_wrf(d, i, txv);
+ e10k_vf_vfivar_i_allocval1_wrf(d, i, 1);
+ } else {
+ e10k_vf_vfivar_i_alloc2_wrf(d, i, rxv);
+ e10k_vf_vfivar_i_allocval2_wrf(d, i, 1);
+ e10k_vf_vfivar_i_alloc3_wrf(d, i, txv);
+ e10k_vf_vfivar_i_allocval3_wrf(d, i, 1);
+ }
+ if (q->msix_intvec != 0) {
+ // Enable interrupt
+ e10k_vf_vfeitr_wr(d, rxv / 32, (1 << (rxv % 32)));
+ }
+ if (rxv < 16) {
+ // Make sure interrupt is cleared
+ e10k_vf_vfeicr_wr(d, 1 << rxv);
+ }
+ }
+
+ // Initialize TX queue in HW
+ if (q->use_vtd) {
+ e10k_vf_vftdbal_wr(d, n, (lvaddr_t) q->tx_ring);
+ e10k_vf_vftdbah_wr(d, n, ((lvaddr_t)q->tx_ring) >> 32);
+ } else {
+ e10k_vf_vftdbal_wr(d, n, tx_phys);
+ e10k_vf_vftdbah_wr(d, n, tx_phys >> 32);
+ }
+
+ e10k_vf_vftdlen_wr(d, n, tx_size);
+
+ // Initialize TX head index write back
+ if (!capref_is_null(q->txhwb_frame)) {
+ r = invoke_frame_identify(q->txhwb_frame, &frameid);
+ assert(err_is_ok(r));
+ txhwb_phys = frameid.base;
+
+ if (q->use_vtd) {
+ e10k_vf_vftdwbal_headwb_low_wrf(d, n, ((lvaddr_t)q->tx_hwb) >> 2);
+ e10k_vf_vftdwbah_headwb_high_wrf(d, n, ((lvaddr_t)q->tx_hwb) >> 32);
+ } else {
+ e10k_vf_vftdwbal_headwb_low_wrf(d, n, txhwb_phys >> 2);
+ e10k_vf_vftdwbah_headwb_high_wrf(d, n, txhwb_phys >> 32);
+ }
+ e10k_vf_vftdwbal_headwb_en_wrf(d, n, 1);
+ }
+
+ // Initialized by queue driver to avoid race conditions
+ // Initialize queue pointers
+ assert(q->tx_head == 0);
+ e10k_vf_vftdh_wr(d, n, q->tx_head);
+ e10k_vf_vftdt_wr(d, n, q->tx_head);
+
+ // Configure prefetch and writeback threshhold
+ e10k_vf_vftxdctl_pthresh_wrf(d, n, 8); // FIXME: Figure out what the right number
+ // is here.
+ e10k_vf_vftxdctl_hthresh_wrf(d, n, 0);
+ e10k_vf_vftxdctl_wthresh_wrf(d, n, 0); // Needs to be 0 for TXHWB
+
+ e10k_vf_vftxdctl_enable_wrf(d, n, 1);
+
+ while (e10k_vf_vftxdctl_enable_rdf(d, n) == 0); // TODO: Timeout
+ DEBUG_VF("[%x] TX queue enabled\n", n);
+
+ // Some initialization stuff from BSD driver
+ e10k_vf_vfdca_txctrl_txdesc_wbro_wrf(d, n, 0);
+}
+
+#if 0
+/** Stop queue. */
+static void queue_hw_stop(uint8_t n)
+{
+#if 0
+ // This process is described in 4.6.7.1.2
+
+ // Disable TX for this queue
+ e10k_txdctl_enable_wrf(d, n, 0);
+
+ // TODO: Flush packet buffers
+ // TODO: Remove all filters
+ // TODO: With RSC we have to wait here (see spec), not used atm
+
+ // Disable RX for this queue
+ e10k_rxdctl_1_enable_wrf(d, n, 0);
+ while (e10k_rxdctl_1_enable_rdf(d, n) != 0); // TODO: Timeout
+
+ // A bit too much, but make sure memory is not used anymore
+ milli_sleep(1);
+#else
+ assert(!"NYI");
+#endif
+}
+#endif
+
+/** Here are the global interrupts handled. */
+static void interrupt_handler(void* arg)
+{
+ e10k_vf_vfeicr_t eicr = e10k_vf_vfeicr_rd(vf->d);
+
+ DEBUG_VF("interrupt vf (eicr=%x)\n", eicr);
+
+ if (eicr & ((1 << QUEUE_INTRX) | (1 << QUEUE_INTTX))) {
+ e10k_vf_vfeicr_wr(vf->d, eicr);
+ /*qd_interrupt(!!(eicr & (1 << QUEUE_INTRX)),
+ !!(eicr & (1 << QUEUE_INTTX)));*/
+ }
+}
+
+/******************************************************************************/
+/* Initialization code for driver */
+
+/** Callback from pci to initialize a specific PCI device. */
+static void pci_init_card(struct device_mem* bar_info, int bar_count)
+{
+
+ DEBUG_VF("pci init card\n");
+ errval_t err;
+ bool res;
+
+ assert(!vf->initialized);
+
+ vf->d = malloc(sizeof(*(vf->d)));
+
+ // Map first BAR for register access
+ assert(bar_count >= 1);
+ map_device(&bar_info[0]);
+ vf->regframe = bar_info[0].frame_cap;
+ DEBUG_VF("BAR[0] mapped (v=%llx p=%llx l=%llx)\n",
+ (unsigned long long) bar_info[0].vaddr,
+ (unsigned long long) bar_info[0].paddr,
+ (unsigned long long) bar_info[0].bytes);
+
+ // Initialize Mackerel binding
+ e10k_vf_initialize(vf->d, (void*) bar_info[0].vaddr);
+
+ // Initialize manager for MSI-X vectors
+ if (vf->msix) {
+ DEBUG_VF("Enabling MSI-X interrupts\n");
+ uint16_t msix_count = 0;
+ err = pci_msix_enable(&msix_count);
+ assert(err_is_ok(err));
+ assert(msix_count > 0);
+ DEBUG_VF("MSI-X #vecs=%d\n", msix_count);
+
+ res = bmallocator_init(&vf->msix_alloc, msix_count);
+ assert(res);
+ } else {
+ DEBUG_VF("Using legacy interrupts\n");
+ }
+
+ DEBUG_VF("STATUS = %x\n", e10k_vf_vfstatus_rd(vf->d));
+
+ // Initialize hardware registers etc.
+ DEBUG_VF("Initializing hardware\n");
+ device_init();
+
+ assert(vf->initialized);
+
+ // Tell PF driver
+ err = vf->binding->vtbl.init_done(vf->binding, vf->vf_num);
+ assert(err_is_ok(err));
+
+}
+
+
+/** Register with PCI */
+static errval_t pci_register(void)
+{
+ errval_t r;
+
+ r = pci_client_connect();
+ if (err_is_fail(r)) {
+ return r;
+ }
+
+ interrupt_handler_fn inthandler;
+
+ if(vf->use_interrupts) {
+ inthandler = interrupt_handler;
+ } else {
+ inthandler = NULL;
+ }
+
+ r = pci_register_driver_noirq(pci_init_card, PCI_CLASS_ETHERNET,
+ PCI_DONT_CARE, PCI_DONT_CARE,
+ PCI_VENDOR_INTEL, E10K_PCI_DEVID,
+ 7, 16, vf->pci_function);
+
+ DEBUG_VF("pci registered\n");
+ if (err_is_fail(r)) {
+ DEBUG_VF("err\n");
+ return r;
+ }
+ return SYS_ERR_OK;
+}
+
+
+static void vf_bind_cont(void *st, errval_t err, struct e10k_vf_binding *b)
+{
+ assert(err_is_ok(err));
+
+ struct e10k_vf_rpc_client *r = malloc(sizeof(*r));
+ assert(r != NULL);
+ err = e10k_vf_rpc_client_init(r, b);
+ if (err_is_ok(err)) {
+ vf->binding = r;
+ } else {
+ free(r);
+ }
+}
+
+static errval_t e10k_vf_client_connect(int pci_function)
+{
+ iref_t iref;
+ errval_t err, err2 = SYS_ERR_OK;
+ char name[256];
+
+ snprintf(name, 256, "e10k_vf%u", pci_function);
+
+ /* Connect to the pci server */
+ err = nameservice_blocking_lookup(name, &iref);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ assert(iref != 0);
+
+ /* Setup flounder connection with pci server */
+ err = e10k_vf_bind(iref, vf_bind_cont, &err, get_default_waitset(),
+ IDC_BIND_FLAG_RPC_CAP_TRANSFER);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ /* XXX: Wait for connection establishment */
+ while (vf->binding == NULL && err2 == SYS_ERR_OK) {
+ messages_wait_and_handle_next();
+ }
+
+ return err2;
+}
+
+errval_t e10k_vf_init_queue_hw(struct e10k_queue* q)
+{
+ DEBUG_VF("VF queue init\n");
+ assert(vf->initialized);
+ assert(e10k_vf_can_create_queue());
+ assert(q != NULL);
+
+ uint8_t q_idx = 0;
+ // check which queue to initalize
+ for (int i = 0; i < 2; i++) {
+ if (!vf->q_enabled[i]) {
+ q_idx = i;
+ vf->q_enabled[i] = true;
+ break;
+ }
+ }
+
+ q->d = vf->d;
+ DEBUG_VF("Enabled queue %d of VF in hardware\n", q_idx);
+ // initialize queue in hardware
+ queue_hw_init(q);
+
+ return SYS_ERR_OK;
+}
+
+errval_t e10k_init_vf_driver(uint8_t pci_function,
+ bool interrupts)
+{
+
+ errval_t err, err2;
+ // crate vtd domain for VF driver
+ // XXX: might not be the best idea to do it here
+ err = connect_to_acpi();
+ assert(err_is_ok(err));
+ err = vtd_create_domain(cap_vroot);
+ assert(err_is_ok(err));
+ err = vtd_domain_add_device(0, 7, 16, 0, cap_vroot);
+ assert(err_is_ok(err));
+
+ DEBUG_VF("VF driver started\n");
+ vf = malloc(sizeof(struct vf_state));
+ vf->use_interrupts = interrupts;
+
+ DEBUG_VF("Connecting to PF driver...\n");
+ err = e10k_vf_client_connect(pci_function);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ DEBUG_VF("Requesting VF number from PF...\n");
+ err = vf->binding->vtbl.request_vf_number(vf->binding, (uint8_t*) &vf->vf_num,
+ &err2);
+ if (err_is_fail(err) || err_is_fail(err2)) {
+ return err_is_fail(err) ? err: err2;
+ }
+
+ DEBUG_VF("Requesting MAC from PF...\n");
+ err = vf->binding->vtbl.get_mac_address(vf->binding, vf->vf_num, &vf->d_mac);
+ assert(err_is_ok(err));
+
+ DEBUG_VF("VF num %d initalize...\n", vf->vf_num);
+ err = pci_register();
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ while (!vf->initialized) {
+ event_dispatch(get_default_waitset());
+ }
+
+ DEBUG_VF("VF init done\n");
+ return SYS_ERR_OK;
+}
+
+bool e10k_vf_started(void)
+{
+ return !(vf == NULL);
+}
+
+// Check if VF queue pool has still queues that it can enable
+bool e10k_vf_can_create_queue(void)
+{
+ for (int i = 0; i < POOL_SIZE; i++) {
+ if (!vf->q_enabled[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
--- /dev/null
+/** \file
+ * \brief Virtual Function management for e10k
+ */
+
+/*
+ * Copyright (c) 2017 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef __E10K_DEVIF_VF_H__
+#define __E10K_DEVIF_VF_H__
+
+struct vf_state;
+struct e10k_queue;
+
+
+ /*
+ * The library is not fully stateless. During the initalization
+ * call of a function there is a lot of state that can only
+ * stored in global variables
+ */
+
+ /**
+ * @brief Initializes a new virtual function of the 10k.
+ *
+ * @param pci_function From which physical function this virtual
+ * function should be initalized
+ * @param interrupts Enable interrupts
+ *
+ * @returns error on failure or SYS_ERR_OK on success
+ */
+errval_t e10k_init_vf_driver(uint8_t pci_function,
+ bool interrupts);
+
+ /**
+ * @brief Checks if the state of the library initalized
+ *
+ * @returns true if it is otherwise false
+ */
+bool e10k_vf_started(void);
+
+ /**
+ * @brief Checks if current active VF can still initalize another queue.
+ * The general assumption is that each VF can allocate 2 queues.
+ * Other configurations have to be specified in the PF driver.
+ *
+ * @returns true if it is otherwise false
+ */
+bool e10k_vf_can_create_queue(void);
+
+
+ /**
+ * @brief initalized queue on VF in hardware
+ *
+ * @param queue e10k queue struct with information about the mapped
+ * memory that is used for DMA
+ *
+ * @returns SYS_ERR_OK on success otherwise failter
+ */
+errval_t e10k_vf_init_queue_hw(struct e10k_queue* q);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2007-2011, 2017, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef E10K_QUEUE_H_
+#define E10K_QUEUE_H_
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <arch/x86/barrelfish_kpi/asm_inlines_arch.h>
+#include <net_interfaces/flags.h>
+
+#include <devif/queue_interface.h>
+#include "../../../queue_interface_internal.h"
+#include <dev/e10k_q_dev.h>
+
+
+struct e10k_queue_ops {
+ errval_t (*update_txtail)(struct e10k_queue*, size_t);
+ errval_t (*update_rxtail)(struct e10k_queue*, size_t);
+};
+
+/**
+ * Context structure for RX descriptors. This is needed to implement RSC, since
+ * we need to be able to chain buffers together. */
+struct e10k_queue_rxctx {
+ struct devq_buf buf;
+ struct e10k_queue_rxctx *previous;
+ bool used;
+};
+
+
+struct region_entry {
+ uint32_t rid;
+ lpaddr_t phys;
+ lvaddr_t virt;
+ size_t size;
+ struct capref cap;
+ struct region_entry* next;
+};
+
+struct e10k_queue {
+ struct devq q;
+
+ // queue info
+ void* d;
+ bool enabled;
+ uint16_t id;
+ uint32_t rsbufsz;
+ bool use_vf; // use VF for this queue
+ bool use_rsc; // Receive Side Coalescing
+ bool use_vtd; // Virtual addressing (required for VF)
+ bool use_rxctx; //
+ bool use_txhwb;
+ size_t rxbufsz;
+ uint8_t pci_function;
+
+ // registered regions
+ struct region_entry* regions;
+
+ // interrupt
+ bool use_irq;
+ uint8_t msix_intvec;
+ uint8_t msix_intdest;
+ size_t msix_index;
+
+
+ // memory caps
+ struct capref rx_frame;
+ struct capref tx_frame;
+ struct capref txhwb_frame;
+
+ struct capref regs;
+ // vf state
+ struct vf_state* vf;
+
+ // Communicatio to PF
+ struct e10k_vf_rpc_client *binding;
+ bool bound;
+
+ // FIXME: Look for appropriate type for the _head/tail/size fields
+ e10k_q_tdesc_adv_wb_array_t* tx_ring;
+ struct devq_buf* tx_bufs;
+ bool* tx_isctx;
+ size_t tx_head;
+ size_t tx_tail, tx_lasttail;
+ size_t tx_size;
+ void* tx_hwb;
+
+ e10k_q_rdesc_adv_wb_array_t* rx_ring;
+ struct e10k_queue_rxctx* rx_context;
+ size_t rx_head;
+ size_t rx_tail;
+ size_t rx_size;
+
+ struct e10k_queue_ops ops;
+ void* opaque;
+
+};
+
+typedef struct e10k_queue e10k_queue_t;
+
+// Does not initalize the queue struct itself
+static inline void e10k_queue_init(struct e10k_queue* q, void* tx, size_t tx_size,
+ uint32_t* tx_hwb, void* rx, size_t rx_size,
+ struct e10k_queue_ops* ops)
+{
+ q->tx_ring = tx;
+ q->tx_bufs = calloc(tx_size, sizeof(struct devq_buf));
+ q->tx_isctx = calloc(tx_size, sizeof(bool));
+ q->tx_head = 0;
+ q->tx_tail = q->tx_lasttail = 0;
+ q->tx_size = tx_size;
+ q->tx_hwb = tx_hwb;
+
+ q->rx_ring = rx;
+ q->rx_context = calloc(rx_size, sizeof(*q->rx_context));
+ q->rx_head = 0;
+ q->rx_tail = 0;
+ q->rx_size = rx_size;
+
+ q->ops = *ops;
+
+ // Initialize ring memory with zero
+ memset(tx, 0, tx_size * e10k_q_tdesc_adv_wb_size);
+ memset(rx, 0, rx_size * e10k_q_rdesc_adv_wb_size);
+}
+
+static inline int e10k_queue_add_txcontext(e10k_queue_t* q, uint8_t idx,
+ uint8_t maclen, uint16_t iplen,
+ uint8_t l4len, e10k_q_l4_type_t l4t)
+{
+ e10k_q_tdesc_adv_ctx_t d;
+ size_t tail = q->tx_tail;
+
+ memset(q->tx_ring[tail], 0, e10k_q_tdesc_adv_wb_size);
+
+ // TODO: Check if there is room in the queue
+ q->tx_isctx[tail] = true;
+ d = q->tx_ring[tail];
+
+ e10k_q_tdesc_adv_rd_dtyp_insert(d, e10k_q_adv_ctx);
+ e10k_q_tdesc_adv_rd_dext_insert(d, 1);
+
+ /* e10k_q_tdesc_adv_ctx_bcntlen_insert(d, 0x3f); */
+ e10k_q_tdesc_adv_ctx_idx_insert(d, idx);
+ e10k_q_tdesc_adv_ctx_maclen_insert(d, maclen);
+ e10k_q_tdesc_adv_ctx_iplen_insert(d, iplen);
+ e10k_q_tdesc_adv_ctx_ipv4_insert(d, 1);
+ e10k_q_tdesc_adv_ctx_l4len_insert(d, l4len);
+ e10k_q_tdesc_adv_ctx_l4t_insert(d, l4t);
+
+ q->tx_lasttail = q->tx_tail;
+ q->tx_tail = (tail + 1) % q->tx_size;
+ return 0;
+}
+
+
+static inline int e10k_queue_add_txbuf_ctx(e10k_queue_t* q, regionid_t rid,
+ bufferid_t bid, lpaddr_t base,
+ size_t totallen, uint64_t flags,
+ bool first, bool last,
+ size_t len, uint8_t ctx,
+ bool ixsm, bool txsm)
+{
+ e10k_q_tdesc_adv_rd_t d;
+ size_t tail = q->tx_tail;
+
+ memset(q->tx_ring[tail], 0, e10k_q_tdesc_adv_wb_size);
+
+ // TODO: Check if there is room in the queue
+ q->tx_isctx[tail] = false;
+ struct devq_buf* buf = &q->tx_bufs[tail];
+ buf->addr = base;
+ buf->bid = bid;
+ buf->rid = rid;
+ buf->flags = flags;
+ buf->len = totallen;
+ d = q->tx_ring[tail];
+
+ e10k_q_tdesc_adv_rd_buffer_insert(d, base);
+ e10k_q_tdesc_adv_rd_dtalen_insert(d, len);
+ if (first) {
+ e10k_q_tdesc_adv_rd_paylen_insert(d, totallen);
+ }
+ e10k_q_tdesc_adv_rd_dtyp_insert(d, e10k_q_adv_data);
+ e10k_q_tdesc_adv_rd_dext_insert(d, 1);
+ e10k_q_tdesc_adv_rd_rs_insert(d, (last == 1));
+ e10k_q_tdesc_adv_rd_ifcs_insert(d, 1);
+ e10k_q_tdesc_adv_rd_eop_insert(d, last);
+
+ if (ctx != (uint8_t)-1) {
+ e10k_q_tdesc_adv_rd_idx_insert(d, ctx);
+ e10k_q_tdesc_adv_rd_cc_insert(d, 1);
+ e10k_q_tdesc_adv_rd_ixsm_insert(d, ixsm);
+ e10k_q_tdesc_adv_rd_txsm_insert(d, txsm);
+ }
+
+ q->tx_lasttail = q->tx_tail;
+ q->tx_tail = (tail + 1) % q->tx_size;
+ return 0;
+
+}
+
+static inline int e10k_queue_add_txbuf(e10k_queue_t* q, regionid_t rid,
+ bufferid_t bid, lpaddr_t base,
+ size_t totallen, uint64_t flags,
+ bool first, bool last,
+ size_t len)
+{
+ return e10k_queue_add_txbuf_ctx(q, rid, bid, base, totallen,
+ flags, first, last,
+ len, -1, false, false);
+}
+
+/*
+ * Reclaim 1 packet from the TX queue once it's handled by the
+ * card. Call multiple times to reclaim more packets.
+ *
+ * \param q Queue to check
+ * \param opaque Contains opaque data of reclaimed packet, if any
+ *
+ * \return true if packet can be reclaimed otherwise false
+ */
+static inline bool e10k_queue_get_txbuf(e10k_queue_t* q, regionid_t* rid,
+ bufferid_t* bid, lpaddr_t* base,
+ size_t* len, uint64_t* flags)
+{
+ /* e10k_q_tdesc_adv_wb_t d; */
+ size_t head = q->tx_head;
+ bool result = false;
+
+ // If HWB is enabled, we can skip reading the descriptor if nothing happened
+ if (q->tx_hwb && *((uint32_t*)q->tx_hwb) == head) {
+ return false;
+ }
+
+ if(!q->tx_hwb) {
+ size_t idx = head;
+
+ // Skip over context and non-EOP descriptors
+ while(idx != q->tx_tail && q->tx_isctx[idx] &&
+ !e10k_q_tdesc_adv_wb_dd_extract(q->tx_ring[idx])) {
+ idx = (idx + 1) % q->tx_size;
+ }
+
+ if(idx == q->tx_tail) {
+ return false;
+ }
+ }
+
+ // That last packet got written out, now go reclaim from the head pointer.
+ if (!q->tx_isctx[head]) {
+ //*opaque = q->tx_opaque[head];
+ *rid = q->tx_bufs[head].rid;
+ *bid = q->tx_bufs[head].bid;
+ *base = q->tx_bufs[head].addr;
+ *len = q->tx_bufs[head].len;
+ *flags = q->tx_bufs[head].flags;
+
+ result = true;
+ }
+
+ /* memset(q->tx_ring[head], 0, e10k_q_tdesc_adv_wb_size); */
+ q->tx_head = (head + 1) % q->tx_size;
+ return result;
+}
+
+static inline errval_t e10k_queue_bump_txtail(e10k_queue_t* q)
+{
+ return q->ops.update_txtail(q, q->tx_tail);
+}
+
+static inline size_t e10k_queue_free_txslots(e10k_queue_t* q)
+{
+ size_t head = q->tx_head;
+ size_t tail = q->tx_tail;
+ size_t size = q->tx_size;
+
+ if (tail >= head) {
+ return size - (tail - head) - 1; // TODO: could this be off by 1?
+ } else {
+ return size - (tail + size - head) - 1; // TODO: off by 1?
+ }
+
+}
+
+static inline int e10k_queue_add_rxbuf(e10k_queue_t* q,
+ regionid_t rid,
+ bufferid_t bid,
+ lpaddr_t base,
+ size_t len,
+ uint64_t flags)
+{
+ e10k_q_rdesc_adv_rd_t d;
+ size_t tail = q->rx_tail;
+ struct e10k_queue_rxctx *ctx;
+
+ ctx = q->rx_context + tail;
+ if (ctx->used) {
+ printf("e10k: Already used!\n");
+ return 1;
+ }
+
+ // TODO: Check if there is room in the queue
+ ctx->buf.rid = rid;
+ ctx->buf.bid = bid;
+ ctx->buf.len = len;
+ ctx->buf.addr = base;
+ ctx->buf.flags = flags;
+ ctx->used = true;
+ d = (e10k_q_rdesc_adv_rd_t) q->rx_ring[tail];
+
+ e10k_q_rdesc_adv_rd_buffer_insert(d, base);
+ // TODO: Does this make sense for RSC?
+ e10k_q_rdesc_adv_rd_hdr_buffer_insert(d, 0);
+
+ q->rx_tail = (tail + 1) % q->rx_size;
+
+ return 0;
+}
+
+static inline uint64_t e10k_queue_convert_rxflags(e10k_q_rdesc_adv_wb_t d)
+{
+ uint64_t flags = 0;
+
+ // IP checksum
+ if (e10k_q_rdesc_adv_wb_ipcs_extract(d)) {
+ flags |= NETIF_RXFLAG_IPCHECKSUM;
+ if (!e10k_q_rdesc_adv_wb_ipe_extract(d)) {
+ flags |= NETIF_RXFLAG_IPCHECKSUM_GOOD;
+ }
+ }
+
+ // L4 checksum
+ if (e10k_q_rdesc_adv_wb_l4i_extract(d)) {
+ flags |= NETIF_RXFLAG_L4CHECKSUM;
+ if (!e10k_q_rdesc_adv_wb_l4e_extract(d)) {
+ flags |= NETIF_RXFLAG_L4CHECKSUM_GOOD;
+ }
+ }
+
+ // Packet type
+ if (e10k_q_rdesc_adv_wb_pt_ipv4_extract(d)) {
+ flags |= NETIF_RXFLAG_TYPE_IPV4;
+ }
+ if (e10k_q_rdesc_adv_wb_pt_tcp_extract(d)) {
+ flags |= NETIF_RXFLAG_TYPE_TCP;
+ }
+ if (e10k_q_rdesc_adv_wb_pt_udp_extract(d)) {
+ flags |= NETIF_RXFLAG_TYPE_UDP;
+ }
+
+ return flags;
+}
+
+static inline bool e10k_queue_get_rxbuf(e10k_queue_t* q, regionid_t* rid,
+ bufferid_t* bid, lpaddr_t* base,
+ size_t* len, uint64_t* flags, int* last)
+{
+ e10k_q_rdesc_adv_wb_t d;
+ size_t head = q->rx_head;
+ struct e10k_queue_rxctx *ctx;
+
+ d = q->rx_ring[head];
+ ctx = q->rx_context + head;
+
+ if (!e10k_q_rdesc_adv_wb_dd_extract(d)) {
+ return false;
+ }
+
+ // Barrier needed according to linux driver to make sure nothing else is
+ // read before the dd bit TODO: make sure
+ lfence();
+
+ // TODO add code for RSC
+
+ *flags = ctx->buf.flags;
+ // Set flags if it this is a descriptor with EOP
+ // TODO: with multi-part packets, we want these flags on the first packet
+ if (e10k_q_rdesc_adv_wb_eop_extract(d)) {
+ *flags = *flags | e10k_queue_convert_rxflags(d);
+ }
+
+ // TODO: Extract status (okay/error)
+ *last = e10k_q_rdesc_adv_wb_eop_extract(d);
+ *len = e10k_q_rdesc_adv_wb_pkt_len_extract(d);
+ *rid = ctx->buf.rid;
+ *bid = ctx->buf.bid;
+ *base = ctx->buf.addr;
+
+ ctx->used = false;
+ memset(d, 0, e10k_q_rdesc_adv_wb_size);
+
+ q->rx_head = (head + 1) % q->rx_size;
+ return true;
+}
+
+static inline errval_t e10k_queue_bump_rxtail(e10k_queue_t* q)
+{
+ return q->ops.update_rxtail(q, q->rx_tail);
+}
+
+static inline size_t e10k_queue_free_rxslots(e10k_queue_t* q)
+{
+ size_t head = q->rx_head;
+ size_t tail = q->rx_tail;
+ size_t size = q->rx_size;
+
+ if (tail >= head) {
+ return size - (tail - head) - 1; // TODO: could this be off by 1?
+ } else {
+ return size - (tail + size - head) - 1; // TODO: off by 1?
+ }
+}
+
+
+#endif // ndef E10K_QUEUE_H_
--- /dev/null
+/*
+ * Copyright (c) 2007-2011, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include "helper.h"
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include <skb/skb.h>
+
+
+/* Dump bytes of memory region to stdout */
+void debug_dumpmem(void* buf, size_t len)
+{
+ uint8_t* b = buf;
+
+ while (len--)
+ printf("0x%02x ", *b++);
+ printf("\n");
+}
+
+/* allocate a single frame, mapping it into our vspace with given attributes */
+void* alloc_map_frame(vregion_flags_t attr, size_t size, struct capref *retcap)
+{
+ struct capref frame;
+ errval_t r;
+
+ r = frame_alloc(&frame, size, NULL);
+ assert(err_is_ok(r));
+ void *va;
+ r = vspace_map_one_frame_attr(&va, size, frame, attr,
+ NULL, NULL);
+ if (err_is_fail(r)) {
+ DEBUG_ERR(r, "vspace_map_one_frame failed");
+ return NULL;
+ }
+
+ if (retcap != NULL) {
+ *retcap = frame;
+ }
+
+ return va;
+}
+
+/* Get APIC id for specified core */
+errval_t get_apicid_from_core(coreid_t cid, uint8_t *apicid)
+{
+ static bool connected = false;
+ errval_t err;
+ unsigned int i;
+
+ if (!connected) {
+ err = skb_client_connect();
+ if (err_is_fail(err)) {
+ return err;
+ }
+ connected = true;
+ }
+
+ err = skb_execute_query("corename(%d,_,apic(ApicID)),write(ApicID).",
+ cid);
+ if (err_is_fail(err)) {
+ return err;
+ }
+ err = skb_read_output("%u.", &i);
+ *apicid = i;
+ return err;
+}
+
+
+/*****************************************************************************/
+/* Bitmap based allocator */
+
+
+/** Init allocator for n objects. */
+bool bmallocator_init(struct bmallocator *alloc, size_t n)
+{
+ alloc->count = n;
+ alloc->bitmap = calloc((n + BMALLOCATOR_BITS - 1) / BMALLOCATOR_BITS,
+ BMALLOCATOR_BITS / 8);
+ return alloc->bitmap != NULL;
+}
+
+/** Release memory associated with allocator. */
+void bmallocator_destroy(struct bmallocator *alloc)
+{
+ free(alloc->bitmap);
+ alloc->bitmap = NULL;
+}
+
+/** Allocate object, return index in *n if successful (return true). */
+bool bmallocator_alloc(struct bmallocator *alloc, size_t *n)
+{
+ size_t i;
+ BMALLOCATOR_TYPE bit;
+ size_t idx;
+
+ // This could be improved
+ for (i = 0; i < alloc->count; i++) {
+ bit = 1 << (i % BMALLOCATOR_BITS);
+ idx = i / BMALLOCATOR_BITS;
+
+ if (!(alloc->bitmap[idx] & bit)) {
+ alloc->bitmap[idx] |= bit;
+ *n = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+/** Free object n, return value indicates if it was allocated before. */
+bool bmallocator_free(struct bmallocator *alloc, size_t n)
+{
+ bool result;
+ BMALLOCATOR_TYPE bit;
+ size_t idx;
+
+ if (n >= alloc->count) {
+ return false;
+ }
+
+ bit = (1 << (n % BMALLOCATOR_BITS));
+ idx = n / BMALLOCATOR_BITS;
+
+ result = alloc->bitmap[idx] & bit;
+ alloc->bitmap[idx] &= ~bit;
+
+ return result;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2011, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef HELPER_H_
+#define HELPER_H_
+
+#include <errors/errno.h>
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/vregion.h>
+
+void debug_dumpmem(void* buf, size_t len);
+void* alloc_map_frame(vregion_flags_t attr, size_t size, struct capref *retcap);
+errval_t get_apicid_from_core(coreid_t cid, uint8_t *apicid);
+
+/* Simple bitmap-based allocator */
+#define BMALLOCATOR_BITS 8
+#define BMALLOCATOR_TYPE uint8_t
+struct bmallocator {
+ BMALLOCATOR_TYPE *bitmap;
+ size_t count;
+};
+
+/** Init allocator for n objects. */
+bool bmallocator_init(struct bmallocator *alloc, size_t n);
+/** Release memory associated with allocator. */
+void bmallocator_destroy(struct bmallocator *alloc);
+/** Allocate object, return index in *n if successful (return true). */
+bool bmallocator_alloc(struct bmallocator *alloc, size_t *n);
+/** Free object n, return value indicates if it was allocated before. */
+bool bmallocator_free(struct bmallocator *alloc, size_t n);
+
+#endif // ndef HELPER_H_
--- /dev/null
+/** \file
+ * \brief Simple sleep call
+ */
+
+/*
+ * Copyright (c) 2010-2011, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/sys_debug.h>
+
+#include <bench/bench.h>
+
+#include "sleep.h"
+
+#if defined(__x86_64__)
+static uint64_t tscperms;
+#endif
+
+static bool initialised = false;
+
+void sleep_init(void)
+{
+ if (!initialised) {
+ bench_init();
+#if defined(__x86_64__)
+ errval_t err = sys_debug_get_tsc_per_ms(&tscperms);
+ assert(err_is_ok(err));
+#endif
+ initialised = true;
+ }
+}
+
+
+void cycle_sleep(uint64_t cycles)
+{
+ if (!initialised) {
+ sleep_init();
+ }
+
+ uint64_t start = bench_tsc();
+ uint64_t stop = bench_tsc();
+ while ((stop - start) < cycles) {
+ // sys_yield(CPTR_NULL);
+ thread_yield_dispatcher(NULL_CAP);
+ stop = bench_tsc();
+ }
+}
+
+// FIXME: this should be determined at runtime from the kernel...
+// #define TSC_PER_MS 2513385
+
+void milli_sleep(uint64_t ms)
+{
+#if defined(__x86_64__)
+ uint64_t cycles = ms * tscperms;
+ cycle_sleep(cycles);
+#else
+ USER_PANIC("milli_sleep NYI for non-x86_64");
+#endif
+}
+
--- /dev/null
+/** \file
+ * \brief Simple sleep call
+ */
+
+/*
+ * Copyright (c) 2010-2011, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef __SLEEP_H__
+#define __SLEEP_H__
+
+void sleep_init(void);
+void cycle_sleep(uint64_t cycles);
+void milli_sleep(uint64_t ms);
+
+#endif