SET_FIELD(SIOCSIFFLAGS, siocsifflags_c, ifr.ifr_flags = Int_val(caml_val)) SET_FIELD(SIOCSIFPFLAGS, siocsifpflags_c, ifr.ifr_flags = Int_val(caml_val)) SET_FIELD(SIOCSIFMTU, siocsifmtu_c, ifr.ifr_mtu = Int_val(caml_val)) SET_FIELD(SIOCSIFTXQLEN, siocsiftxqlen_c, ifr.ifr_qlen = Int_val(caml_val)) SET_FIELD(SIOCSIFNAME, siocsifname_c, copyifname(ifr.ifr_newname, String_val(caml_val))) static void set_hwaddr(struct sockaddr *sa, value hwaddr) { /* quick and dirty checks */ if (caml_string_length(hwaddr) != ETHERNET_MAC_LEN) caml_failwith("Expected 6 byte ethernet MAC"); memcpy(sa->sa_data, String_val(hwaddr), ETHERNET_MAC_LEN); return; } SET_FIELD(SIOCSIFHWADDR, siocsifhwaddr_c, set_hwaddr(&ifr.ifr_hwaddr, caml_val)) static void set_ipaddr(struct sockaddr *sa, value ipaddr) { struct sockaddr_in *sin = (struct sockaddr_in *)sa; sin->sin_family = AF_INET; sin->sin_addr.s_addr = Int32_val(ipaddr); return; } SET_FIELD(SIOCSIFADDR, siocsifaddr_c, set_ipaddr(&ifr.ifr_addr, caml_val)) SET_FIELD(SIOCSIFBRDADDR, siocsifbrdaddr_c, set_ipaddr(&ifr.ifr_broadaddr, caml_val)) SET_FIELD(SIOCSIFNETMASK, siocsifnetmask_c, set_ipaddr(&ifr.ifr_netmask, caml_val))
vmxnet3::vmxnet3(hw::PCI_Device& d, const uint16_t mtu) : Link(Link_protocol{{this, &vmxnet3::transmit}, mac()}, bufstore_), m_pcidev(d), m_mtu(mtu), bufstore_{1024, buffer_size_for_mtu(mtu)} { INFO("vmxnet3", "Driver initializing (rev=%#x)", d.rev_id()); assert(d.rev_id() == REVISION_ID); // find and store capabilities d.parse_capabilities(); // find BARs etc. d.probe_resources(); if (d.msix_cap()) { d.init_msix(); uint8_t msix_vectors = d.get_msix_vectors(); INFO2("[x] Device has %u MSI-X vectors", msix_vectors); assert(msix_vectors >= 3); if (msix_vectors > 2 + NUM_RX_QUEUES) msix_vectors = 2 + NUM_RX_QUEUES; for (int i = 0; i < msix_vectors; i++) { auto irq = Events::get().subscribe(nullptr); this->irqs.push_back(irq); d.setup_msix_vector(SMP::cpu_id(), IRQ_BASE + irq); } Events::get().subscribe(irqs[0], {this, &vmxnet3::msix_evt_handler}); Events::get().subscribe(irqs[1], {this, &vmxnet3::msix_xmit_handler}); for (int q = 0; q < NUM_RX_QUEUES; q++) Events::get().subscribe(irqs[2 + q], {this, &vmxnet3::msix_recv_handler}); } else { assert(0 && "This driver does not support legacy IRQs"); } // dma areas this->iobase = d.get_bar(PCI_BAR_VD); assert(this->iobase); this->ptbase = d.get_bar(PCI_BAR_PT); assert(this->ptbase); // verify and select version bool ok = check_version(); assert(ok); // reset device ok = reset(); assert(ok); // get mac address retrieve_hwaddr(); // check link status auto link_spd = check_link(); if (link_spd) { INFO2("Link up at %u Mbps", link_spd); } else { INFO2("LINK DOWN! :("); return; } // set MAC set_hwaddr(this->hw_addr); // initialize DMA areas this->dma = (vmxnet3_dma*) memalign(VMXNET3_DMA_ALIGN, sizeof(vmxnet3_dma)); memset(this->dma, 0, sizeof(vmxnet3_dma)); auto& queues = dma->queues; // setup tx queues queues.tx.cfg.desc_address = (uintptr_t) &dma->tx_desc; queues.tx.cfg.comp_address = (uintptr_t) &dma->tx_comp; queues.tx.cfg.num_desc = vmxnet3::NUM_TX_DESC; queues.tx.cfg.num_comp = VMXNET3_NUM_TX_COMP; queues.tx.cfg.intr_index = 1; // temp rxq buffer storage memset(tx.buffers, 0, sizeof(tx.buffers)); // setup rx queues for (int q = 0; q < NUM_RX_QUEUES; q++) { memset(rx[q].buffers, 0, sizeof(rx[q].buffers)); rx[q].desc0 = &dma->rx0_desc[0]; rx[q].desc1 = &dma->rx1_desc[0]; rx[q].comp = &dma->rx_comp[0]; rx[q].index = q; auto& queue = queues.rx[q]; queue.cfg.desc_address[0] = (uintptr_t) rx[q].desc0; queue.cfg.desc_address[1] = (uintptr_t) rx[q].desc1; queue.cfg.comp_address = (uintptr_t) rx[q].comp; queue.cfg.num_desc[0] = vmxnet3::NUM_RX_DESC; queue.cfg.num_desc[1] = vmxnet3::NUM_RX_DESC; queue.cfg.num_comp = VMXNET3_NUM_RX_COMP; queue.cfg.driver_data_len = sizeof(vmxnet3_rx_desc) + 2 * sizeof(vmxnet3_rx_desc); queue.cfg.intr_index = 2 + q; } auto& shared = dma->shared; // setup shared physical area shared.magic = VMXNET3_REV1_MAGIC; shared.misc.guest_info.arch = (sizeof(void*) == 4) ? GOS_BITS_32_BITS : GOS_BITS_64_BITS; shared.misc.guest_info.type = GOS_TYPE_LINUX; shared.misc.version = VMXNET3_VERSION_MAGIC; shared.misc.version_support = 1; shared.misc.upt_version_support = 1; shared.misc.upt_features = UPT1_F_RXVLAN; shared.misc.driver_data_address = (uintptr_t) &dma; shared.misc.queue_desc_address = (uintptr_t) &dma->queues; shared.misc.driver_data_len = sizeof(vmxnet3_dma); shared.misc.queue_desc_len = sizeof(vmxnet3_queues); shared.misc.mtu = packet_len(); // 60-9000 shared.misc.num_tx_queues = 1; shared.misc.num_rx_queues = NUM_RX_QUEUES; shared.interrupt.mask_mode = VMXNET3_IT_AUTO | (VMXNET3_IMM_AUTO << 2); shared.interrupt.num_intrs = 2 + NUM_RX_QUEUES; shared.interrupt.event_intr_index = 0; memset(shared.interrupt.moderation_level, UPT1_IML_ADAPTIVE, VMXNET3_MAX_INTRS); shared.interrupt.control = 0x1; // disable all shared.rx_filter.mode = VMXNET3_RXM_UCAST | VMXNET3_RXM_BCAST | VMXNET3_RXM_ALL_MULTI; // location of shared area to device uintptr_t shabus = (uintptr_t) &shared; mmio_write32(this->iobase + 0x10, shabus); // shared low mmio_write32(this->iobase + 0x18, 0x0); // shared high // activate device int status = command(VMXNET3_CMD_ACTIVATE_DEV); if (status) { assert(0 && "Failed to activate device"); } // initialize and fill RX queue... for (int q = 0; q < NUM_RX_QUEUES; q++) { refill(rx[q]); } // deferred transmit this->deferred_irq = Events::get().subscribe(handle_deferred); // enable interrupts enable_intr(0); enable_intr(1); for (int q = 0; q < NUM_RX_QUEUES; q++) enable_intr(2 + q); }