* redistribute, and modify it as specified in the file "V3VEE_LICENSE".
*/
-/*
-* Virtual NE2K Network Card
-*/
-
#include <devices/pci.h>
-#include <devices/ne2k.h>
#include <palacios/vmm.h>
-#include <palacios/vmm_types.h>
#include <palacios/vmm_io.h>
#include <palacios/vmm_debug.h>
#include <palacios/vmm_string.h>
+#include <palacios/vmm_dev_mgr.h>
+#include <palacios/vmm_intr.h>
+#include <palacios/vmm_ethernet.h>
+#include <palacios/vm_guest.h>
+#include <palacios/vmm_sprintf.h>
-#ifndef DEBUG_NE2K
+#ifndef V3_CONFIG_DEBUG_NE2K
#undef PrintDebug
#define PrintDebug(fmts, args...)
#endif
-//#define TEST_PERFORMANCE 0
-typedef enum {NIC_READY, NIC_REG_POSTED} nic_state_t;
+#define NE2K_DEFAULT_IRQ 11
+
+// What the hell is this crap?
+#define NE2K_PMEM_SIZE (32 * 1024)
+#define NE2K_PMEM_START (16 * 1024)
+#define NE2K_PMEM_END (NE2K_PMEM_SIZE + NE2K_PMEM_START)
+#define NE2K_MEM_SIZE NE2K_PMEM_END
+
+#define NIC_REG_BASE_PORT 0xc100 /* Command register (for all pages) */
+
+#define NE2K_CMD_OFFSET 0x00
+#define NE2K_DATA_OFFSET 0x10
+#define NE2K_RESET_OFFSET 0x1f
+
+/* Page 0 registers */
+#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
+#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
+#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
+#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
+#define EN0_TSR 0x04 /* Transmit status reg RD */
+#define EN0_TPSR 0x04 /* Transmit starting page WR */
+#define EN0_NCR 0x05 /* Number of collision reg RD */
+#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
+#define EN0_FIFO 0x06 /* FIFO RD */
+#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
+#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
+#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
+#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
+#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
+#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
+#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
+#define EN0_RTL8029ID0 0x0a /* Realtek ID byte #1 RD */
+#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
+#define EN0_RTL8029ID1 0x0b /* Realtek ID byte #2 RD */
+#define EN0_RSR 0x0c /* rx status reg RD */
+#define EN0_RXCR 0x0c /* RX configuration reg WR */
+#define EN0_TXCR 0x0d /* TX configuration reg WR */
+#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
+#define EN0_DCFG 0x0e /* Data configuration reg WR */
+#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
+#define EN0_IMR 0x0f /* Interrupt mask reg WR */
+#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
+
+/* Page 1 registers */
+#define EN1_PHYS 0x01
+#define EN1_CURPAG 0x07
+#define EN1_MULT 0x08
+
+/* Page 2 registers */
+#define EN2_STARTPG 0x01 /* Starting page of ring bfr RD */
+#define EN2_STOPPG 0x02 /* Ending page +1 of ring bfr RD */
+#define EN2_LDMA0 0x01 /* Current Local DMA Address 0 WR */
+#define EN2_LDMA1 0x02 /* Current Local DMA Address 1 WR */
+#define EN2_RNPR 0x03 /* Remote Next Packet Pointer RD WR */
+#define EN2_TPSR 0x04 /* Transmit Page Start Address RD */
+#define EN2_LNRP 0x05 /* Local Next Packet Pointer RD WR */
+#define EN2_ACNT0 0x06 /* Address Counter Upper WR */
+#define EN2_ACNT1 0x07 /* Address Counter Lower WR */
+#define EN2_RCR 0x0c /* Receive Configuration Register RD */
+#define EN2_TCR 0x0d /* Transmit Configuration Register RD */
+#define EN2_DCR 0x0e /* Data Configuration Register RD */
+#define EN2_IMR 0x0f /* Interrupt Mask Register RD */
+
+/* Page 3 registers */
+#define EN3_CONFIG0 0x03
+#define EN3_CONFIG1 0x04
+#define EN3_CONFIG2 0x05
+#define EN3_CONFIG3 0x06
+
+
+struct cmd_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t stop : 1;
+ uint8_t start : 1;
+ uint8_t tx_pkt : 1;
+ uint8_t rem_dma_cmd : 3; /* 0=Not allowed, 1=Read, 2=Write, 3=Send Pkt, 4=Abort/Complete DMA */
+ uint8_t pg_sel : 2;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct intr_status_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t pkt_rx : 1;
+ uint8_t pkt_tx : 1;
+ uint8_t rx_err : 1;
+ uint8_t tx_err : 1;
+ uint8_t overwrite_warn : 1;
+ uint8_t cnt_overflow : 1;
+ uint8_t rem_dma_done : 1;
+ uint8_t reset_status : 1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct intr_mask_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t pkt_rx : 1;
+ uint8_t pkt_tx : 1;
+ uint8_t rx_err : 1;
+ uint8_t tx_err : 1;
+ uint8_t overwrite_warn : 1;
+ uint8_t cnt_overflow : 1;
+ uint8_t rem_dma_done : 1;
+ uint8_t rsvd : 1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct data_cfg_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t word_trans_sel : 1;
+ uint8_t byte_order_sel : 1;
+ uint8_t long_addr_sel : 1;
+ uint8_t loopback_sel : 1;
+ uint8_t auto_init_rem : 1;
+ uint8_t fifo_thresh_sel : 2;
+ uint8_t rsvd : 1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct tx_cfg_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t inhibit_crc : 1;
+ uint8_t enc_loop_ctrl : 2;
+ uint8_t auto_tx_disable : 1;
+ uint8_t coll_offset_en : 1;
+ uint8_t rsvd : 3;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+struct tx_status_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t pkt_tx_ok : 1;
+ uint8_t rsvd : 1;
+ uint8_t tx_collision : 1;
+ uint8_t tx_aborted : 1;
+ uint8_t carrier_lost : 1;
+ uint8_t fifo_underrun : 1;
+ uint8_t cd_heartbeat : 1;
+ uint8_t oow_collision : 1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+struct rx_cfg_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t save_pkt_errs : 1;
+ uint8_t runt_pkt_ok : 1;
+ uint8_t bcast_ok : 1;
+ uint8_t mcast_ok : 1;
+ uint8_t prom_phys_enable : 1;
+ uint8_t mon_mode : 1;
+ uint8_t rsvd : 2;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct rx_status_reg {
+ union {
+ uint8_t val;
+ struct {
+ uint8_t pkt_rx_ok : 1;
+ uint8_t crc_err : 1;
+ uint8_t frame_align_err : 1;
+ uint8_t fifo_overrun : 1;
+ uint8_t missed_pkt : 1;
+ uint8_t phy_match : 1; /* 0=Physical Addr Match, 1=MCAST/BCAST Addr Match */
+ uint8_t rx_disabled : 1;
+ uint8_t deferring : 1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct ne2k_registers {
+ struct cmd_reg cmd;
+ struct intr_status_reg isr;
+ struct intr_mask_reg imr;
+ struct data_cfg_reg dcr;
+ struct tx_cfg_reg tcr;
+ struct tx_status_reg tsr;
+ struct rx_cfg_reg rcr;
+ struct rx_status_reg rsr;
+
+ uint8_t pgstart; /* page start reg */
+ uint8_t pgstop; /* page stop reg */
+ uint8_t boundary; /* boundary ptr */
+ uint8_t tpsr; /* tx page start addr */
+ uint8_t ncr; /* number of collisions */
+ uint8_t fifo; /* FIFO... */
+
+ uint8_t curpag; /* current page */
+ uint8_t rnpp; /* rem next pkt ptr */
+ uint8_t lnpp; /* local next pkt ptr */
+
+ uint8_t cntr0; /* counter 0 (frame alignment errors) */
+ uint8_t cntr1; /* counter 1 (CRC Errors) */
+ uint8_t cntr2; /* counter 2 (missed pkt errors) */
+
+ union { /* current local DMA Addr */
+ uint16_t clda;
+ struct {
+ uint8_t clda0;
+ uint8_t clda1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+
+
+ union { /* current remote DMA addr */
+ uint16_t crda;
+ struct {
+ uint8_t crda0;
+ uint8_t crda1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+
+
+ union { /* Remote Start Addr Reg */
+ uint16_t rsar;
+ struct {
+ uint8_t rsar0;
+ uint8_t rsar1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+
+
+ union { /* TX Byte count Reg */
+ uint16_t tbcr;
+ struct {
+ uint8_t tbcr0;
+ uint8_t tbcr1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+
+ union { /* Remote Byte count Reg */
+ uint16_t rbcr;
+ struct {
+ uint8_t rbcr0;
+ uint8_t rbcr1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+
+ union { /* Address counter? */
+ uint16_t addcnt;
+ struct {
+ uint8_t addcnt0;
+ uint8_t addcnt1;
+ } __attribute__((packed));
+ } __attribute__((packed));
+};
-struct ne2k_context{
- struct guest_info *vm;
- nic_state_t dev_state;
- struct ne2k_regs regs;
- uchar_t mac[6];
- uchar_t mem[NE2K_MEM_SIZE];
- struct pci_device *pci_dev;
- struct vm_device *pci;
-};
+struct ne2k_state {
+ struct v3_vm_info * vm;
+ struct pci_device * pci_dev;
+ struct vm_device * pci_bus;
+ struct vm_device * dev;
-#define NUM_NE2K 10
+ struct ne2k_registers context;
+ uint8_t mem[NE2K_MEM_SIZE];
-struct vm_device *ne2ks[NUM_NE2K]; //the array of virtual network cards
+ uint8_t mcast_addr[8];
+ uint8_t mac[ETH_ALEN];
-static int nic_no = 0;
+ struct nic_statistics statistics;
-#if TEST_PERFORMANCE
-static uint32_t exit_num = 0;
-static uint32_t int_num = 0;
-#endif
+ struct v3_dev_net_ops *net_ops;
+ void * backend_data;
+};
-#define compare_mac(src, dst) ({ \
- ((src[0] == dst[0]) && \
- (src[1] == dst[1]) && \
- (src[2] == dst[2]) && \
- (src[3] == dst[3]) && \
- (src[4] == dst[4]) && \
- (src[5] == dst[5]))? 1:0; \
- })
+static int ne2k_update_irq(struct ne2k_state * nic_state) {
+ struct pci_device * pci_dev = nic_state->pci_dev;
-extern int V3_Send_pkt(uchar_t *buf, int length);
-extern int V3_Register_pkt_event(int (*netif_input)(uchar_t * pkt, uint_t size));
+ if ((nic_state->context.isr.val & nic_state->context.imr.val) & 0x7f) {
+ if (pci_dev == NULL){
+ v3_raise_virq(&(nic_state->vm->cores[0]), NE2K_DEFAULT_IRQ);
+ } else {
+ v3_pci_raise_irq(nic_state->pci_bus, nic_state->pci_dev, 0);
+ }
-#ifdef DEBUG_NE2K
-static void dump_state(struct vm_device *dev)
-{
- int i;
- uchar_t *p;
- struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data;
+ nic_state->statistics.rx_interrupts ++;
- PrintDebug("====NE2000: Dumping state Begin ==========\n");
- PrintDebug("Registers:\n");
+ PrintDebug(VM_NONE, VCORE_NONE, "NE2000: Raise IRQ\n");
+ }
- p = (uchar_t *)&nic_state->regs;
- for(i = 0; i < sizeof(struct ne2k_regs); i++)
- PrintDebug("Regs[%d] = 0x%2x\n", i, (int)p[i]);
-
- PrintDebug("Memory:\n");
- for(i = 0; i < 32; i++)
- PrintDebug("0x%02x ", nic_state->mem[i]);
- PrintDebug("\n");
- PrintDebug("====NE2000: Dumping state End==========\n");
+ return 0;
}
-#endif
-#if 0
-//no-pci version
-static void ne2k_update_irq(struct vm_device *dev)
-{
- int isr;
- struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data;
- struct guest_info *guest = dev->vm;
+static int tx_one_pkt(struct ne2k_state * nic_state, uchar_t *pkt, uint32_t length) {
- isr = ((nic_state->regs.isr & nic_state->regs.imr) & 0x7f);
+#ifdef V3_CONFIG_DEBUG_NE2K
+ PrintDebug(VM_NONE, VCORE_NONE, "NE2000: Send Packet:\n");
+ v3_hexdump(pkt, length, NULL, 0);
+#endif
- if ((isr & 0x7f) != 0x0) {
- v3_raise_irq(guest, NIC_DEF_IRQ);
- PrintDebug("Ne2k: RaiseIrq: isr: 0x%02x imr: 0x%02x\n", nic_state->regs.isr, nic_state->regs.imr);
- }
-}
-
-#endif
+ if(nic_state->net_ops->send(pkt, length, nic_state->backend_data) >= 0){
+ nic_state->statistics.tx_pkts ++;
+ nic_state->statistics.tx_bytes += length;
-#if 1
-//pci version
-static void ne2k_update_irq(struct vm_device *dev)
-{
- int isr;
- struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data;
- struct pci_device *pdev = nic_state->pci_dev;
- int irqline = 0;
-
- if (pdev == NULL){
- PrintDebug("Ne2k: Device %p is not attached to any PCI Bus\n", nic_state);
- irqline = NE2K_DEF_IRQ;
- } else {
- irqline = pdev->config_header.intr_line;
+ return 0;
}
- isr = ((nic_state->regs.isr & nic_state->regs.imr) & 0x7f);
-
- if (irqline == 0){
- PrintError("Ne2k: IRQ_LINE: %d\n", irqline);
- return;
- }
-
- PrintDebug("Ne2k: RaiseIrq: isr: 0x%02x imr: 0x%02x\n", nic_state->regs.isr, nic_state->regs.imr);
- PrintDebug("ne2k_update_irq: irq_line: %d\n", irqline);
+ nic_state->statistics.tx_dropped ++;
- if ((isr & 0x7f) != 0x0) {
- v3_raise_irq(nic_state->vm, irqline);
- PrintDebug("Ne2k: RaiseIrq: isr: 0x%02x imr: 0x%02x\n", nic_state->regs.isr, nic_state->regs.imr);
-
- #if TEST_PERFORMANCE
- if ((++int_num) % 50 == 0)
- PrintError("Ne2k: Total Exit: %d, INT: %d\n", (int)exit_num, int_num);
- #endif
- }
+ return -1;
}
-#endif
-
-static void init_ne2k_context(struct vm_device *dev)
-{
- struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data;
- int i;
- uchar_t mac[6] = {0x52, 0x54, 0x0, 0x12, 0x34, (0x60 + nic_no)};
-
- nic_state->vm = dev->vm;
-
- nic_state->regs.isr = ENISR_RESET;
- nic_state->regs.imr = 0x00;
- nic_state->regs.cmd = 0x22;
-
- for (i = 0; i < 5; i++)
- nic_state->regs.macaddr[i] = nic_state->mac[i] = mac[i];
-
- nic_state->regs.macaddr[5] = nic_state->mac[5] = mac[5] + nic_no;
+static int ne2k_rxbuf_full(struct ne2k_registers * regs) {
+ int empty;
+ int index;
+ int boundary;
- for (i = 0; i < 8; i++)
- nic_state->regs.mult[i] = 0xff;
+ index = regs->curpag << 8;
+ boundary = regs->boundary << 8;
- for(i = 0; i < 32; i++) {
- nic_state->mem[i] = 0xff;
+ if (index < boundary) {
+ empty = boundary - index;
+ } else {
+ empty = ((regs->pgstop - regs->pgstart) << 8) - (index - boundary);
}
- memcpy(nic_state->mem, nic_state->mac, 6);
- nic_state->mem[14] = 0x57;
- nic_state->mem[15] = 0x57;
-
-#ifdef DEBUG_NE2K
- dump_state(dev);
-#endif
-
-}
-
-static int ne2k_send_packet(struct vm_device *dev, uchar_t *pkt, int length)
-{
- int i;
-
- PrintDebug("\nNe2k: Sending Packet\n");
-
- for (i = 0; i<length; i++)
- PrintDebug("%x ",pkt[i]);
- PrintDebug("\n");
-
- return V3_Send_pkt(pkt, length);
-}
-
-//TODO: Support Broadcast and Multicast
-static struct vm_device * get_rx_dev(uchar_t *dst_mac)
-{
- struct ne2k_context *nic_state;
- struct ne2k_regs *nregs;
- int i;
-
- static const uchar_t brocast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
- for (i = 0; i < NUM_NE2K; i++){
- if (ne2ks[i] != NULL) {
- nic_state = (struct ne2k_context *)ne2ks[i]->private_data;
- nregs = &(nic_state->regs);
-
- if (nregs->rcr & 0x10) {//broadcast packets, not correct here
- return ne2ks[i];
- } else {
- if (compare_mac(dst_mac, brocast_mac) && (nregs->rcr & 0x04)) //broadcast address
- return ne2ks[i];
- if (dst_mac[0] & 0x01) {
- // multicast packet, not done here
- // ==========
- if (nregs->rcr & 0x08)
- return ne2ks[i];
- }
- if (compare_mac(dst_mac, nic_state->mac))
- return ne2ks[i];
- }
- }
+ if (empty < (ETHERNET_PACKET_LEN + 4)) {
+ return 1;
}
- return NULL;
-}
-
-static int ne2k_rxbuf_full(struct vm_device *dev)
-{
- int empty, index, boundary;
- struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data;
-
- index = nic_state->regs.curpag << 8;
- boundary = nic_state->regs.boundary << 8;
- if (index < boundary)
- empty = boundary - index;
- else
- empty = ((nic_state->regs.pgstop - nic_state->regs.pgstart) << 8) - (index - boundary);
-
- if (empty < (MAX_ETH_FRAME_SIZE + 4))
- return 1;
-
return 0;
}
#define MIN_BUF_SIZE 60
-static void ne2k_receive(struct vm_device *dev, const uchar_t *pkt, int length)
-{
- struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data;
- struct ne2k_regs *nregs = &(nic_state->regs);
- uchar_t *p;
- uint32_t total_len, next, len, index, empty;
- uchar_t buf[60];
- uint32_t start, stop;
-
- start = nregs->pgstart << 8;
- stop = nregs->pgstop << 8;
+
+// This needs to be completely redone...
+static int rx_one_pkt(struct ne2k_state * nic_state, const uchar_t * pkt, uint32_t length) {
+ struct ne2k_registers * regs = (struct ne2k_registers *)&(nic_state->context);
+ uchar_t * p;
+ uint32_t total_len;
+ uint32_t next;
+ uint32_t len;
+ uint32_t index;
+ uint32_t empty;
+ uint32_t start;
+ uint32_t stop;
+
+ start = regs->pgstart << 8;
+ stop = regs->pgstop << 8;
- if (nregs->cmd & NE2K_STOP)
- return;
+ if (regs->cmd.stop) {
+ return -1;
+ }
- if (ne2k_rxbuf_full(dev)){
- PrintError("Ne2k: received buffer overflow\n");
- return;
+ if (ne2k_rxbuf_full(regs)) {
+ PrintError(VM_NONE, VCORE_NONE, "Ne2k: received buffer overflow\n");
+ return -1;
}
//packet too small, expand it
if (length < MIN_BUF_SIZE) {
+ uchar_t buf[MIN_BUF_SIZE];
+
memcpy(buf, pkt, length);
memset(buf + length, 0, MIN_BUF_SIZE - length);
pkt = buf;
length = MIN_BUF_SIZE;
}
- index = nregs->curpag << 8;
+ index = regs->curpag << 8;
+
//header, 4 bytes
total_len = length + 4;
+
//address for next packet (4 bytes for CRC)
next = index + ((total_len + 4 + 255) & ~0xff);
- if (next >= stop)
- next -= stop - start;
+
+ if (next >= stop) {
+ next -= (stop - start);
+ }
p = nic_state->mem + index;
- nregs->rsr = ENRSR_RXOK;
+ regs->rsr.val = 0;
+ regs->rsr.pkt_rx_ok = 1;
- if (pkt[0] & 0x01)
- nregs->rsr |= ENRSR_PHY;
-
- p[0] = nregs->rsr;
+ if (pkt[0] & 0x01) {
+ regs->rsr.phy_match = 1; /* TODO: Check this back */
+ }
+
+ p[0] = regs->rsr.val;
p[1] = next >> 8;
p[2] = total_len;
p[3] = total_len >> 8;
index += 4;
while (length > 0) {
- if (index <= stop)
- empty = stop - index;
- else
- empty = 0;
- len = length;
- if (len > empty)
- len = empty;
- memcpy(nic_state->mem + index, pkt, len);
- pkt += len;
- index += len;
- if (index == stop)
- index = start;
- length -= len;
- }
- nregs->curpag = next >> 8;
-
- nregs->isr |= ENISR_RX;
- ne2k_update_irq(dev);
-}
-
-static int ne2k_hook_iospace(struct vm_device *vmdev, addr_t base_addr, int size, int type, void *data);
-
-static struct pci_device * pci_ne2k_init(struct vm_device *vmdev,
- struct vm_device *pci,
- int bus_no,
- int dev_num,
- int fn_num,
- int (*io_read)(ushort_t port, void * dst, uint_t length, struct vm_device * dev),
- int (*io_write)(ushort_t port, void * src, uint_t length, struct vm_device * dev))
-{
- uchar_t *pci_conf;
- struct pci_device *pdev;
- struct v3_pci_bar ne2k_bar;
-
- ne2k_bar.type = PCI_BAR_IO;
- ne2k_bar.num_ports = 0x100;
- ne2k_bar.default_base_port = 0xc100;
- ne2k_bar.io_read = io_read;
- ne2k_bar.io_write = io_write;
-
- pdev = v3_pci_register_device(vmdev,
- PCI_STD_DEVICE,
- bus_no,
- dev_num,
- fn_num,
- "NE2000",
- &ne2k_bar,
- NULL, NULL, NULL, vmdev);
-
- if (pdev == NULL){
- PrintError("NIC: Register to PCI bus failed\n");
- return NULL;
- }
-
- pci_conf = pdev->config_space;
-
- pci_conf[0x00] = 0xec; // Realtek 8029
- pci_conf[0x01] = 0x10;
- pci_conf[0x02] = 0x29;
- pci_conf[0x03] = 0x80;
- pci_conf[0x0a] = 0x00; // ethernet network controller
- pci_conf[0x0b] = 0x02;
- pci_conf[0x0e] = 0x00; // header_type
- pci_conf[0x3d] = 1; // interrupt pin 0
- pci_conf[0x3c] = 11;
-
- pdev->vm_dev = vmdev;
-
- return pdev;
-}
-
-static int netif_input(uchar_t * pkt, uint_t size)
-{
- uint_t i;
- struct vm_device *dev;
-
- PrintDebug("\nNe2k: Packet Received:\nSource:");
- for (i = 6; i < 12; i++) {
- PrintDebug("%x ", pkt[i]);
- }
+ if (index <= stop) {
+ empty = stop - index;
+ } else {
+ empty = 0;
+ }
- dev = get_rx_dev(pkt); //TODO: handle multiple destination packets
+ len = length;
- if (dev == NULL)
- return 0;
+ if (len > empty) {
+ len = empty;
+ }
- PrintDebug("\n");
- for(i= 0; i<size; i++)
- PrintDebug("%x ", pkt[i]);
-
- ne2k_receive(dev, pkt, size);
+ memcpy(nic_state->mem + index, pkt, len);
+ pkt += len;
+ index += len;
- return 0;
-}
+ if (index == stop) {
+ index = start;
+ }
+ length -= len;
+ }
-static inline uint16_t cpu2le16(uint16_t val)
-{
- uint16_t p;
- uchar_t *p1 = (uchar_t *)&p;
+ regs->curpag = next >> 8;
- p1[0] = val;
- p1[1] = val >> 8;
+ regs->isr.pkt_rx = 1;
+ ne2k_update_irq(nic_state);
- return p;
+ return 0;
}
-static inline uint32_t cpu2le32(uint32_t val)
-{
- uint32_t p;
- uchar_t *p1 = (uchar_t *)&p;
-
- p1[0] = val;
- p1[1] = val >> 8;
- p1[2] = val >> 16;
- p1[3] = val >> 24;
-
- return p;
-}
+static int ne2k_rx(uint8_t * buf, uint32_t size, void * private_data){
+ struct ne2k_state * nic_state = (struct ne2k_state *)private_data;
+
+#ifdef V3_CONFIG_DEBUG_NE2K
+ PrintDebug(VM_NONE, VCORE_NONE, "\nNe2k: Packet Received:\n");
+ v3_hexdump(buf, size, NULL, 0);
+#endif
+
+ if(!rx_one_pkt(nic_state, buf, size)){
+ nic_state->statistics.rx_pkts ++;
+ nic_state->statistics.rx_bytes += size;
+
+ return 0;
+ }
-static inline uint16_t le16_to_cpu(const uint16_t *p)
-{
- const uchar_t *p1 = (const uchar_t *)p;
- return p1[0] | (p1[1] << 8);
+ nic_state->statistics.rx_dropped ++;
+
+ return -1;
}
-static inline uint32_t le32_to_cpu(const uint32_t *p)
-{
- const uchar_t *p1 = (const uchar_t *)p;
- return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24);
-}
-static void
-ne2k_mem_writeb(struct ne2k_context *nic_state,
- uint32_t addr,
- uint32_t val)
-{
- uchar_t tmp;
+static inline void mem_writeb(struct ne2k_state * nic_state,
+ uint32_t addr,
+ uint32_t val) {
+ uchar_t tmp = (uchar_t) (val & 0x000000ff);
- tmp = (uchar_t) (val & 0x000000ff);
- if (addr < 32 || (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
+ if ((addr < 32) || (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
nic_state->mem[addr] = tmp;
}
-
- PrintDebug("wmem addr: %x val: %x\n", addr, val);
}
-static void
-ne2k_mem_writew(struct ne2k_context *nic_state,
- uint32_t addr,
- uint32_t val)
-{
- addr &= ~1; //XXX: check exact behaviour if not even
- if (addr < 32 ||
- (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
- *(ushort_t *)(nic_state->mem + addr) = cpu2le16(val);
- }
+static inline void mem_writew(struct ne2k_state * nic_state,
+ uint32_t addr,
+ uint32_t val) {
+ addr &= ~1;
- PrintDebug("wmem addr: %x val: %x\n", addr, val);
+ if ((addr < 32) || (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
+ *(uint16_t *)(nic_state->mem + addr) = val;
+ }
}
-static void
-ne2k_mem_writel(struct ne2k_context *nic_state,
- uint32_t addr,
- uint32_t val)
-{
- addr &= ~1; // XXX: check exact behaviour if not even
- if (addr < 32 ||
- (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
- *(uint32_t *)(nic_state->mem + addr) = cpu2le32(val);
- }
+static inline void mem_writel(struct ne2k_state * nic_state,
+ uint32_t addr,
+ uint32_t val) {
+ addr &= ~1;
- PrintDebug("wmem addr: %x val: %x\n", addr, val);
+ if ((addr < 32) || (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
+ *(uint32_t *)(nic_state->mem + addr) = val;
+ }
}
-static uchar_t
-ne2k_mem_readb(struct ne2k_context *nic_state, uint32_t addr)
-{
- PrintDebug("rmem addr: %x\n", addr);
+static inline uint8_t mem_readb(struct ne2k_state * nic_state, uint32_t addr) {
- if (addr < 32 ||
- (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
+ if ((addr < 32) || (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
return nic_state->mem[addr];
} else {
return 0xff;
}
}
-static ushort_t
-ne2k_mem_readw(struct ne2k_context *nic_state, uint32_t addr)
-{
- PrintDebug("rmem addr: %x\n", addr);
-
- addr &= ~1; //XXX: check exact behaviour if not even
- if (addr < 32 ||
- (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
- return (ushort_t)le16_to_cpu((ushort_t *)(nic_state->mem + addr));
+static inline uint16_t mem_readw(struct ne2k_state * nic_state, uint32_t addr) {
+ addr &= ~1;
+
+ if ((addr < 32) || (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
+ return *(uint16_t *)(nic_state->mem + addr);
} else {
return 0xffff;
}
}
-static uint32_t
-ne2k_mem_readl(struct ne2k_context *nic_state, uint32_t addr)
-{
- PrintDebug("rmem addr: %x\n", addr);
+static uint32_t mem_readl(struct ne2k_state * nic_state, uint32_t addr) {
+ addr &= ~1;
- addr &= ~1; //XXX: check exact behaviour if not even
- if (addr < 32 ||
- (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
- return (uint32_t)le32_to_cpu((uint32_t *)(nic_state->mem + addr));
+ if ((addr < 32) || (addr >= NE2K_PMEM_START && addr < NE2K_MEM_SIZE)) {
+ return *(uint32_t *)(nic_state->mem + addr);
} else {
return 0xffffffff;
}
}
-static void
-ne2k_dma_update(struct vm_device *dev, int len)
-{
- struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data;
-
- nic_state->regs.rsar += len;
+
+static void dma_update( struct ne2k_state * nic_state, int len) {
+ struct ne2k_registers * regs = (struct ne2k_registers *)&(nic_state->context);
+
+ regs->rsar += len;
+
// wrap
- if (nic_state->regs.rsar == nic_state->regs.pgstop)
- nic_state->regs.rsar = nic_state->regs.pgstart;
+ if (regs->rsar == regs->pgstop) {
+ regs->rsar = regs->pgstart;
+ }
- if (nic_state->regs.rbcr <= len) {
- nic_state->regs.rbcr = 0;
- nic_state->regs.isr |= ENISR_RDC;
- ne2k_update_irq(dev);
+ if (regs->rbcr <= len) {
+ regs->rbcr = 0;
+ regs->isr.rem_dma_done = 1;
+ ne2k_update_irq(nic_state);
} else {
- nic_state->regs.rbcr -= len;
+ regs->rbcr -= len;
}
}
+static int ne2k_data_read(struct guest_info * core,
+ uint16_t port,
+ void * dst,
+ uint_t length,
+ void * private_data) {
+ struct ne2k_state * nic_state = (struct ne2k_state *)private_data;
+ uint32_t val;
+ struct ne2k_registers * regs = (struct ne2k_registers *)&(nic_state->context);
+
+ // current dma address
+ uint32_t addr = regs->rsar;
+
+ switch (length){
+ case 1:
+ val = mem_readb(nic_state, addr);
+ break;
+ case 2:
+ val = mem_readw(nic_state, addr);
+ break;
+ case 4:
+ val = mem_readl(nic_state, addr);
+ break;
+ default:
+ PrintError(VM_NONE, VCORE_NONE, "ne2k_data_read error: invalid length %d\n", length);
+ val = 0x0;
+ }
+
+ dma_update(nic_state, length);
+ memcpy(dst, &val, length);
-//for data port read/write
-static int ne2k_data_read(ushort_t port,
- void * dst,
- uint_t length,
- struct vm_device *dev)
-{
- uint32_t val;
- struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data;
-
- // current dma address
- uint32_t addr = nic_state->regs.rsar;
-
- switch(length){
- case 1:
- val = ne2k_mem_readb(nic_state, addr);
- break;
- case 2:
- val = ne2k_mem_readw(nic_state, addr);
- break;
- case 4:
- val = ne2k_mem_readl(nic_state, addr);
- break;
- default:
- PrintError("ne2k_data_read error: invalid length %d\n", length);
- val = 0x0;
- }
-
- ne2k_dma_update(dev, length);
+ PrintDebug(VM_NONE, VCORE_NONE, "NE2000 read: port:0x%x (%u bytes): 0x%x", port & 0x1f, length, val);
- memcpy(dst, &val, length);
+ return length;
+}
- PrintDebug("ne2k_read: port:0x%x (%u bytes): 0x%x", port & 0x1f,length, val);
+static int ne2k_data_write(struct guest_info * core,
+ uint16_t port,
+ void * src,
+ uint_t length,
+ void * private_data) {
+ struct ne2k_state * nic_state = (struct ne2k_state *)private_data;
+ uint32_t val;
+ struct ne2k_registers * regs = (struct ne2k_registers *)&(nic_state->context);
+
+ uint32_t addr = regs->rsar;
+
+ if (regs->rbcr == 0) {
+ return length;
+ }
- #if TEST_PERFORMANCE
- if ((++exit_num) % 50 == 0)
- PrintError("Ne2k-Ne2k: Total Exit: %d, INT: %d\n", (int)exit_num, int_num);
- #endif
+ memcpy(&val, src, length);
+
+ switch (length) {
+ case 1:
+ mem_writeb(nic_state, addr, val);
+ break;
+ case 2:
+ mem_writew(nic_state, addr, val);
+ break;
+ case 4:
+ mem_writel(nic_state, addr, val);
+ break;
+ default:
+ PrintError(VM_NONE, VCORE_NONE, "NE2000 port write error: invalid length %d\n", length);
+ }
+
+ dma_update(nic_state, length);
- return length;
+ PrintDebug(VM_NONE, VCORE_NONE, "NE2000: Write port:0x%x (%u bytes): 0x%x\n", port & 0x1f, length, val);
+
+ return length;
}
-static int ne2k_data_write(ushort_t port,
- void * src,
- uint_t length,
- struct vm_device *dev)
-{
- uint32_t val;
- struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data;
- if (nic_state->regs.rbcr == 0)
- return length;
+static void ne2k_init_state(struct ne2k_state * nic_state) {
- memcpy(&val, src, length);
- uint32_t addr = nic_state->regs.rsar;
+ /* Not sure what this is about.... */
+ memset(nic_state->mem, 0xff, 32);
- switch (length){
- case 1:
- ne2k_mem_writeb(nic_state, addr, val);
- break;
- case 2:
- ne2k_mem_writew(nic_state, addr, val);
- break;
- case 4:
- ne2k_mem_writel(nic_state, addr, val);
- break;
- default:
- PrintError("nic_data_write error: invalid length %d\n", length);
- }
-
- ne2k_dma_update(dev, length);
-
- PrintDebug("ne2k_write: port:0x%x (%u bytes): 0x%x\n", port & 0x1f,length, val);
+ memcpy(nic_state->mem, nic_state->mac, ETH_ALEN);
+ memset(nic_state->mcast_addr, 0xff, sizeof(nic_state->mcast_addr));
+ nic_state->mem[14] = 0x57;
+ nic_state->mem[15] = 0x57;
- #if TEST_PERFORMANCE
- if ((++exit_num) % 50 == 0)
- PrintError("Ne2k-Ne2k: Total Exit: %d, INT: %d\n", (int)exit_num, int_num);
- #endif
-
- return length;
+ /* initiate registers */
+ nic_state->context.isr.reset_status = 1;
+ nic_state->context.imr.val = 0x00;
+ nic_state->context.cmd.val = 0x22;
}
-static int ne2k_reset_device(struct vm_device * dev)
-{
-
- PrintDebug("vnic: reset device\n");
+static int reset_device(struct ne2k_state * nic_state) {
+ ne2k_init_state(nic_state);
- init_ne2k_context(dev);
+ PrintDebug(VM_NONE, VCORE_NONE, "NE2000: Reset device\n");
- return 0;
+ return 0;
}
-
//for 0xc11f port
-static int ne2k_reset_port_read(ushort_t port,
- void * dst,
- uint_t length,
- struct vm_device *dev)
-{
- uint32_t val = 0x0;
+static int ne2k_reset_port_read(struct guest_info * core,
+ uint16_t port,
+ void * dst,
+ uint_t length,
+ void * private_data) {
+ struct ne2k_state * nic_state = (struct ne2k_state *)private_data;
- memcpy(dst, &val, length);
+ memset(dst, 0, length);
+ reset_device(nic_state);
- PrintDebug("ne2k_read: port:0x%x (%u bytes): 0x%x\n", port,length, val);
-
- ne2k_reset_device(dev);
+ return length;
+}
- #if TEST_PERFORMANCE
- if ((++exit_num) % 50 == 0)
- PrintError("Ne2k-Ne2k: Total Exit: %d, INT: %d\n", (int)exit_num, int_num);
- #endif
+static int ne2k_reset_port_write(struct guest_info * core,
+ uint16_t port,
+ void * src,
+ uint_t length,
+ void * private_data) {
- return length;
+ return length;
}
-static int ne2k_reset_port_write(ushort_t port,
- void * src,
- uint_t length,
- struct vm_device *dev)
-{
- uint32_t val;
- memcpy(&val, src, length);
- PrintDebug("ne2k_write: port:0x%x (%u bytes): 0x%x\n", port,length, val);
+static int ne2k_cmd_write(struct guest_info * core,
+ uint16_t port,
+ void * src,
+ uint_t length,
+ void * private_data) {
+ struct ne2k_state * nic_state = (struct ne2k_state *)private_data;
+ struct ne2k_registers * regs = (struct ne2k_registers *)&(nic_state->context);
- #if TEST_PERFORMANCE
- if ((++exit_num) % 50 == 0)
- PrintError("Ne2k-Ne2k: Total Exit: %d, INT: %d\n", (int)exit_num, int_num);
- #endif
-
- return length;
-}
+ if (length != 1) {
+ PrintError(core->vm_info, core, "Invalid write length to NE2000 Command register\n");
+ return -1;
+ }
-static int ne2k_ioport_write(ushort_t port,
- void * src,
- uint_t length,
- struct vm_device *dev)
-{
- uchar_t page;
- struct ne2k_context *nic_state = (struct ne2k_context* )dev->private_data;
- uchar_t val;
- int index;
+ regs->cmd.val = *(uint8_t *)src;
+
+ if (!(regs->cmd.stop)) {
+ regs->isr.reset_status = 0;
+
+ // if ((send pkt) && (dma byte count == 0))
+ if ((regs->cmd.rem_dma_cmd & 0x3) && (regs->rbcr == 0)) {
+ regs->isr.rem_dma_done = 1;
+ ne2k_update_irq(nic_state);
+ }
- if (length == 1) {
- memcpy(&val, src, 1);
+ if (regs->cmd.tx_pkt) {
+ int offset = (regs->tpsr << 8);
+
+ if (offset >= NE2K_PMEM_END) {
+ offset -= NE2K_PMEM_SIZE;
+ }
+
+ if (offset + regs->tbcr <= NE2K_PMEM_END) {
+ tx_one_pkt(nic_state, nic_state->mem + offset, regs->tbcr);
+ }
+
+ regs->tsr.val = 0; /* clear the tx status reg */
+ regs->tsr.pkt_tx_ok = 1; /* indicate successful tx */
+
+ regs->isr.pkt_tx = 1; /* irq due to pkt tx */
+ regs->cmd.tx_pkt = 0; /* reset cmd bit */
+
+ ne2k_update_irq(nic_state);
+ }
} else {
- PrintError("ne2k_write error: length %d\n", length);
- return length;
+ /* stop the controller */
}
- PrintDebug("ne2k_write: port:0x%x val: 0x%x\n", port, (int)val);
-
- port &= 0x1f;
+ return length;
+}
- if (port == 0x10)
- return ne2k_data_write(port, src, length, dev);
+static int ne2k_cmd_read(struct guest_info * core,
+ uint16_t port,
+ void * dst,
+ uint_t length,
+ void * private_data) {
+ struct ne2k_state * nic_state = (struct ne2k_state *)private_data;
- if (port == 0x1f)
- return ne2k_reset_port_write(port, src, length, dev);
+ if (length != 1) {
+ PrintError(core->vm_info, core, "Invalid read length to NE2000 Command register\n");
+ return -1;
+ }
+
+ *(uint8_t *)dst = nic_state->context.cmd.val;
+
+ PrintDebug(core->vm_info, core, "ne2k_read: port:0x%x val: 0x%x\n", port, *(uint8_t *)dst);
+ return length;
+}
+
+static int ne2k_std_write(struct guest_info * core,
+ uint16_t port,
+ void * src,
+ uint_t length,
+ void * private_data) {
+ struct ne2k_state * nic_state = (struct ne2k_state *)private_data;
+ struct ne2k_registers * regs = (struct ne2k_registers *)&(nic_state->context);
+ int idx = port & 0x1f;
+ uint8_t page = regs->cmd.pg_sel;
+
+ if (length != 1){
+ PrintError(core->vm_info, core, "NE2000 port write error: length %d port 0x%xnot equal to 1\n", length, port);
+ return -1;
+ }
+
+ uint8_t val = *(uint8_t *)src;
+
+ PrintDebug(core->vm_info, core, "NE2000: write port:0x%x val: 0x%x\n", port, (uint8_t)val);
+
+ if (page == 0) {
+ switch (idx) {
+ case EN0_STARTPG:
+ regs->pgstart = val;
+ break;
+ case EN0_STOPPG:
+ regs->pgstop = val;
+ break;
+ case EN0_BOUNDARY:
+ regs->boundary = val;
+ break;
+ case EN0_TPSR:
+ regs->tpsr = val;
+ break;
+ case EN0_TCNTLO:
+ regs->tbcr0 = val;
+ break;
+ case EN0_TCNTHI:
+ regs->tbcr1 = val;
+ break;
+ case EN0_ISR:
+ regs->isr.val &= ~(val & 0x7f);
+ ne2k_update_irq(nic_state);
+ break;
+ case EN0_RSARLO:
+ regs->rsar0 = val;
+ break;
+ case EN0_RSARHI:
+ regs->rsar1 = val;
+ break;
+ case EN0_RCNTLO:
+ regs->rbcr0 = val;
+ break;
+ case EN0_RCNTHI:
+ regs->rbcr1 = val;
+ break;
+ case EN0_RXCR:
+ regs->rcr.val = val;
+ break;
+ case EN0_TXCR:
+ regs->tcr.val = val;
+ break;
+ case EN0_DCFG:
+ regs->dcr.val = val;
+ break;
+ case EN0_IMR:
+ regs->imr.val = val;
+ ne2k_update_irq(nic_state);
+ break;
+
+ default:
+ PrintError(core->vm_info, core, "NE2000 port write error: invalid port:0x%x\n", port);
+ return -1;
+ }
+ } else if (page == 1) {
+ switch (idx) {
+ case EN1_PHYS ... EN1_PHYS + ETH_ALEN -1:
+ nic_state->mac[port - EN1_PHYS] = val;
+ break;
+ case EN1_CURPAG:
+ regs->curpag = val;
+ break;
+ case EN1_MULT ... EN1_MULT + 7:
+ nic_state->mcast_addr[port - EN1_MULT] = val;
+ break;
- if (port == EN0_COMMAND) {
- nic_state->regs.cmd = val;
- if (!(val & NE2K_STOP)) {
- nic_state->regs.isr &= ~ENISR_RESET;
- if ((val & (NE2K_DMAREAD | NE2K_DMAWRITE)) &&
- nic_state->regs.rbcr == 0) {
- nic_state->regs.isr |= ENISR_RDC;
- ne2k_update_irq(dev);
- }
- if (val & NE2K_TRANSMIT) {
- index = (nic_state->regs.tpsr << 8);
- if (index >= NE2K_PMEM_END)
- index -= NE2K_PMEM_SIZE;
- if (index + nic_state->regs.tbcr <= NE2K_PMEM_END) {
- ne2k_send_packet(dev, nic_state->mem + index, nic_state->regs.tbcr);
- }
- nic_state->regs.tsr = ENTSR_PTX;
- nic_state->regs.isr |= ENISR_TX;
- nic_state->regs.cmd &= ~NE2K_TRANSMIT;
- ne2k_update_irq(dev);
- }
- }
+ default:
+ PrintError(core->vm_info, core, "NE2000 write port error: invalid port:0x%x\n", port);
+ return -1;
+ }
+ } else if (page == 2) {
+ switch (idx) {
+ case EN2_LDMA0:
+ regs->clda0 = val;
+ break;
+ case EN2_LDMA1:
+ regs->clda1 = val;
+ break;
+ case EN2_RNPR:
+ regs->rnpp = val;
+ break;
+ case EN2_LNRP:
+ regs->lnpp = val;
+ break;
+ case EN2_ACNT0:
+ regs->addcnt0 = val;
+ break;
+ case EN2_ACNT1:
+ regs->addcnt1 = val;
+ break;
+
+ default:
+ PrintError(core->vm_info, core, "NE2000 write port error: invalid port:0x%x\n", port);
+ return -1;
+ }
} else {
- page = nic_state->regs.cmd >> 6;
- if(page == 0){
- switch(port) {
- case EN0_STARTPG:
- nic_state->regs.pgstart = val;
- break;
- case EN0_STOPPG:
- nic_state->regs.pgstop = val;
- break;
- case EN0_BOUNDARY:
- nic_state->regs.boundary = val;
- break;
- case EN0_TPSR:
- nic_state->regs.tpsr = val;
- break;
- case EN0_TCNTLO:
- nic_state->regs.tbcr = (nic_state->regs.tbcr & 0xff00) | val;
- break;
- case EN0_TCNTHI:
- nic_state->regs.tbcr = (nic_state->regs.tbcr & 0x00ff) | (val << 8);
- break;
- case EN0_ISR:
- nic_state->regs.isr &= ~(val & 0x7f);
- ne2k_update_irq(dev);
- break;
- case EN0_RSARLO:
- nic_state->regs.rsar = (nic_state->regs.rsar & 0xff00) | val;
- break;
- case EN0_RSARHI:
- nic_state->regs.rsar = (nic_state->regs.rsar & 0x00ff) | (val << 8);
- break;
- case EN0_RCNTLO:
- nic_state->regs.rbcr = (nic_state->regs.rbcr & 0xff00) | val;
- break;
- case EN0_RCNTHI:
- nic_state->regs.rbcr = (nic_state->regs.rbcr & 0x00ff) | (val << 8);
- break;
- case EN0_RXCR:
- nic_state->regs.rcr = val;
- break;
- case EN0_TXCR:
- nic_state->regs.tcr = val;
- case EN0_DCFG:
- nic_state->regs.dcr = val;
- break;
- case EN0_IMR:
- nic_state->regs.imr = val;
- //PrintError("ne2k_write error: write IMR:0x%x\n", (int)val);
- ne2k_update_irq(dev);
- break;
- default:
- PrintError("ne2k_write error: invalid port:0x%x\n", port);
- break;
- }
- }
- if(page == 1){
- switch(port) {
- case EN1_PHYS ... EN1_PHYS + 5:
- nic_state->regs.phys[port - EN1_PHYS] = val;
- break;
- case EN1_CURPAG:
- nic_state->regs.curpag = val;
- break;
- case EN1_MULT ... EN1_MULT + 7:
- // PrintError("ne2k_write error: write EN_MULT:0x%x\n", (int)val);
- nic_state->regs.mult[port - EN1_MULT] = val;
- break;
- default:
- PrintError("ne2k_write error: invalid port:0x%x\n", port);
- break;
- }
- }
- if(page == 2){
- switch(port) {
- case EN2_LDMA0:
- nic_state->regs.clda = (nic_state->regs.clda & 0xff00) | val;
- break;
- case EN2_LDMA1:
- nic_state->regs.clda = (nic_state->regs.clda & 0x00ff) | (val << 8);
- break;
- case EN2_RNPR:
- nic_state->regs.rnpp = val;
- break;
- case EN2_LNRP:
- nic_state->regs.lnpp = val;
- break;
- case EN2_ACNT0:
- nic_state->regs.addcnt = (nic_state->regs.addcnt & 0xff00) | val;
- break;
- case EN2_ACNT1:
- nic_state->regs.addcnt = (nic_state->regs.addcnt & 0x00ff) | (val << 8);
- break;
- default:
- PrintError("ne2k_write error: invalid port:0x%x\n", port);
- break;
- }
- }
+ PrintError(core->vm_info, core, "NE2000: Invalid Register Page Value\n");
+ return -1;
}
- #if TEST_PERFORMANCE
- if ((++exit_num) % 50 == 0)
- PrintError("Ne2k-Ne2k: Total Exit: %d, INT: %d\n", (int)exit_num, int_num);
- #endif
return length;
}
-static int ne2k_ioport_read(ushort_t port,
- void * dst,
- uint_t length,
- struct vm_device *dev)
-{
- uchar_t page, ret, offset;
-
- struct ne2k_context *nic_state = (struct ne2k_context* )dev->private_data;
+static int ne2k_std_read(struct guest_info * core,
+ uint16_t port,
+ void * dst,
+ uint_t length,
+ void * private_data) {
+ struct ne2k_state * nic_state = (struct ne2k_state *)private_data;
+ struct ne2k_registers * regs = (struct ne2k_registers *)&(nic_state->context);
+ uint16_t index = port & 0x1f;
+ uint8_t page = regs->cmd.pg_sel;
if (length > 1) {
- PrintError("ne2k_read error: length %d\n", length);
- return length;
+ PrintError(core->vm_info, core, "ne2k_read error: length %d\n", length);
+ return length;
}
- offset = port;
- port &= 0x1f;
+ if (page == 0) {
+ switch (index) {
+ case EN0_CLDALO:
+ *(uint8_t *)dst = regs->clda0;
+ break;
+ case EN0_CLDAHI:
+ *(uint8_t *)dst = regs->clda1;
+ break;
+ case EN0_BOUNDARY:
+ *(uint8_t *)dst = regs->boundary;
+ break;
+ case EN0_TSR:
+ *(uint8_t *)dst = regs->tsr.val;
+ break;
+ case EN0_NCR:
+ *(uint8_t *)dst = regs->ncr;
+ break;
+ case EN0_FIFO:
+ *(uint8_t *)dst = regs->fifo;
+ break;
+ case EN0_ISR:
+ *(uint8_t *)dst = regs->isr.val;
+ ne2k_update_irq(nic_state);
+ break;
+ case EN0_CRDALO:
+ *(uint8_t *)dst = regs->crda0;
+ break;
+ case EN0_CRDAHI:
+ *(uint8_t *)dst = regs->crda1;
+ break;
+ case EN0_RSR:
+ *(uint8_t *)dst = regs->rsr.val;
+ break;
+ case EN0_COUNTER0:
+ *(uint8_t *)dst = regs->cntr0;
+ break;
+ case EN0_COUNTER1:
+ *(uint8_t *)dst = regs->cntr1;
+ break;
+ case EN0_COUNTER2:
+ *(uint8_t *)dst = regs->cntr2;
+ break;
+
+ default:
+ PrintError(core->vm_info, core, "NE2000 port read error: invalid port:0x%x\n", port);
+ return -1;
+ }
+ } else if (page == 1) {
+ switch (index) {
+ case EN1_PHYS ... EN1_PHYS + ETH_ALEN -1:
+ *(uint8_t *)dst = nic_state->mac[index - EN1_PHYS];
+ break;
+ case EN1_CURPAG:
+ *(uint8_t *)dst = regs->curpag;
+ break;
+ case EN1_MULT ... EN1_MULT + 7:
+ *(uint8_t *)dst = nic_state->mcast_addr[index - EN1_MULT];
+ break;
+
+ default:
+ PrintError(core->vm_info, core, "ne2k_read error: invalid port:0x%x\n", port);
+ return -1;
+ }
+ } else if (page == 2) {
+ switch (index) {
+ case EN2_STARTPG:
+ *(uint8_t *)dst = regs->pgstart;
+ break;
+ case EN2_STOPPG:
+ *(uint8_t *)dst = regs->pgstop;
+ break;
+ case EN2_RNPR:
+ *(uint8_t *)dst = regs->rnpp;
+ break;
+ case EN2_LNRP:
+ *(uint8_t *)dst = regs->lnpp;
+ break;
+ case EN2_TPSR:
+ *(uint8_t *)dst = regs->tpsr;
+ break;
+ case EN2_ACNT0:
+ *(uint8_t *)dst = regs->addcnt0;
+ break;
+ case EN2_ACNT1:
+ *(uint8_t *)dst = regs->addcnt1;
+ break;
+ case EN2_RCR:
+ *(uint8_t *)dst = regs->rcr.val;
+ break;
+ case EN2_TCR:
+ *(uint8_t *)dst = regs->tcr.val;
+ break;
+ case EN2_DCR:
+ *(uint8_t *)dst = regs->dcr.val;
+ break;
+ case EN2_IMR:
+ *(uint8_t *)dst = regs->imr.val;
+ break;
+ default:
+ PrintError(core->vm_info, core, "NE2000 port read error: invalid port:0x%x\n", port);
+ return -1;
+ }
+ } else {
+ PrintError(core->vm_info, core, "NE2000 port read: Invalid Register Page Value\n");
+ return -1;
+ }
- if (port == 0x10)
- return ne2k_data_read(port, dst, length, dev);
+ PrintDebug(core->vm_info, core, "NE2000 port read: port:0x%x val: 0x%x\n", port, *(uint8_t *)dst);
- if (port == 0x1f)
- return ne2k_reset_port_read(port, dst, length, dev);
+ return length;
+}
- if (port == EN0_COMMAND) {
- ret = nic_state->regs.cmd;
- } else {
- page = nic_state->regs.cmd >> 6;
- if (page == 0){
- switch(port) {
- case EN0_CLDALO:
- ret = nic_state->regs.clda & 0x00ff;
- break;
- case EN0_CLDAHI:
- ret = (nic_state->regs.clda & 0xff00) >> 8;
- break;
- case EN0_BOUNDARY:
- ret = nic_state->regs.boundary;
- break;
- case EN0_TSR:
- ret = nic_state->regs.tsr;
- break;
- case EN0_NCR:
- ret = nic_state->regs.ncr;
- break;
- case EN0_FIFO:
- ret = nic_state->regs.fifo;
- break;
- case EN0_ISR:
- ret = nic_state->regs.isr;
- ne2k_update_irq(dev);
- break;
- case EN0_CRDALO:
- ret = nic_state->regs.crda & 0x00ff;
- break;
- case EN0_CRDAHI:
- ret = (nic_state->regs.crda & 0xff00) >> 8;
- break;
- case EN0_RSR:
- ret = nic_state->regs.rsr;
- break;
- case EN0_COUNTER0:
- ret = nic_state->regs.cntr & 0x000000ff;
- break;
- case EN0_COUNTER1:
- ret = (nic_state->regs.cntr & 0x0000ff00) >> 8;
- break;
- case EN0_COUNTER2:
- ret = (nic_state->regs.cntr & 0x00ff0000) >> 16;
- break;
- default:
- PrintError("ne2k_read error: invalid port:0x%x\n", port);
- ret = 0x00;
- break;
- }
- }
- if (page == 1){
- switch(port) {
- case EN1_PHYS ... EN1_PHYS + 5:
- ret = nic_state->regs.phys[port - EN1_PHYS];
- break;
- case EN1_CURPAG:
- ret = nic_state->regs.curpag;
- break;
- case EN1_MULT ... EN1_MULT + 7:
- ret = nic_state->regs.mult[port - EN1_MULT];
- break;
- default:
- PrintError("ne2k_read error: invalid port:0x%x\n", port);
- ret = 0x00;
- break;
- }
- }
- if (page == 2){
- switch(port) {
- case EN2_STARTPG:
- ret = nic_state->regs.pgstart;
- break;
- case EN2_STOPPG:
- ret = nic_state->regs.pgstop;
- break;
- case EN2_RNPR:
- ret = nic_state->regs.rnpp;
- break;
- case EN2_LNRP:
- ret = nic_state->regs.lnpp;
- break;
- case EN2_TPSR:
- ret = nic_state->regs.tpsr;
- break;
- case EN2_ACNT0:
- ret = nic_state->regs.addcnt & 0x00ff;
- break;
- case EN2_ACNT1:
- ret = (nic_state->regs.addcnt & 0xff00) >> 8;
- break;
- case EN2_RCR:
- ret = nic_state->regs.rcr;
- break;
- case EN2_TCR:
- ret = nic_state->regs.tcr;
- break;
- case EN2_DCR:
- ret = nic_state->regs.dcr;
- break;
- case EN2_IMR:
- ret = nic_state->regs.imr;
- break;
- default:
- PrintError("ne2k_read error: invalid port:0x%x\n", port);
- ret = 0x00;
- break;
- }
- }
+
+
+static int ne2k_pci_write(struct guest_info * core,
+ uint16_t port,
+ void * src,
+ uint_t length,
+ void * private_data) {
+ uint16_t idx = port & 0x1f;
+ int ret;
+
+ switch (idx) {
+ case NE2K_CMD_OFFSET:
+ ret = ne2k_cmd_write(core, port, src, length, private_data);
+ break;
+ case NE2K_CMD_OFFSET+1 ... NE2K_CMD_OFFSET+15:
+ ret = ne2k_std_write(core, port, src, length, private_data);
+ break;
+ case NE2K_DATA_OFFSET:
+ ret = ne2k_data_write(core, port, src, length, private_data);
+ break;
+ case NE2K_RESET_OFFSET:
+ ret = ne2k_reset_port_write(core, port, src, length, private_data);
+ break;
+
+ default:
+ PrintError(core->vm_info, core, "NE2000 port write error: invalid port:0x%x\n", port);
+ return -1;
}
- memcpy(dst, &ret, 1);
+ return ret;
+}
- PrintDebug("ne2k_read: port:0x%x val: 0x%x\n", offset, (int)ret);
+static int ne2k_pci_read(struct guest_info * core,
+ uint16_t port,
+ void * dst,
+ uint_t length,
+ void * private_data) {
+ uint16_t idx = port & 0x1f;
+ int ret;
+
+ switch (idx) {
+ case NE2K_CMD_OFFSET:
+ ret = ne2k_cmd_read(core, port, dst, length, private_data);
+ break;
+ case NE2K_CMD_OFFSET+1 ... NE2K_CMD_OFFSET+15:
+ ret = ne2k_std_read(core, port, dst, length, private_data);
+ break;
+ case NE2K_DATA_OFFSET:
+ ret = ne2k_data_read(core, port, dst, length, private_data);
+ break;
+ case NE2K_RESET_OFFSET:
+ ret = ne2k_reset_port_read(core, port, dst, length, private_data);
+ break;
+
+ default:
+ PrintError(core->vm_info, core, "NE2000 port read error: invalid port:0x%x\n", port);
+ return -1;
+ }
- #if TEST_PERFORMANCE
- if ((++exit_num) % 50 == 0)
- PrintError("Ne2k-Ne2k: Total Exit: %d, INT: %d\n", (int)exit_num, int_num);
- #endif
+ return ret;
- return length;
}
+static int pci_config_update(struct pci_device * pci_dev,
+ uint32_t reg_num,
+ void * src,
+ uint_t length,
+ void * private_data) {
+ PrintDebug(VM_NONE, VCORE_NONE, "PCI Config Update\n");
-static int ne2k_start_device(struct vm_device *dev)
-{
- PrintDebug("vnic: start device\n");
-
- return 0;
+ /* Do we need this? */
+
+ return 0;
}
-static int ne2k_stop_device(struct vm_device *dev)
+static int register_dev(struct ne2k_state * nic_state)
{
- PrintDebug("vnic: stop device\n");
-
- return 0;
-}
+ int i;
-static void init_global_setting()
-{
- int i;
-
- V3_Register_pkt_event(&netif_input);
-
- for (i = 0; i < NUM_NE2K; i++)
- ne2ks[i] = NULL;
+ if (nic_state->pci_bus != NULL) {
+ struct v3_pci_bar bars[6];
+ struct pci_device * pci_dev = NULL;
-}
+ PrintDebug(VM_NONE, VCORE_NONE, "NE2000: PCI Enabled\n");
-static int ne2k_hook_iospace(struct vm_device *vmdev, addr_t base_addr, int size, int type, void *data)
-{
- int i;
+ for (i = 0; i < 6; i++) {
+ bars[i].type = PCI_BAR_NONE;
+ }
- if (base_addr <= 0)
- return -1;
+ bars[0].type = PCI_BAR_IO;
+ bars[0].default_base_port = -1;
+ bars[0].num_ports = 256;
- PrintDebug("In NIC%d: Hook IO space starting from %x\n", nic_no, (int) base_addr);
+ bars[0].io_read = ne2k_pci_read;
+ bars[0].io_write = ne2k_pci_write;
+ bars[0].private_data = nic_state;
- for (i = 0; i < 16; i++){
- v3_dev_hook_io(vmdev, base_addr + i, &ne2k_ioport_read, &ne2k_ioport_write);
- }
- v3_dev_hook_io(vmdev, base_addr + NIC_DATA_PORT, &ne2k_data_read, &ne2k_data_write);
- v3_dev_hook_io(vmdev, base_addr + NIC_RESET_PORT, &ne2k_reset_port_read, &ne2k_reset_port_write);
+ pci_dev = v3_pci_register_device(nic_state->pci_bus, PCI_STD_DEVICE, 0, -1, 0,
+ "NE2000", bars,
+ pci_config_update, NULL, NULL, NULL, nic_state);
- return 0;
-}
+ if (pci_dev == NULL) {
+ PrintError(VM_NONE, VCORE_NONE, "NE2000: Could not register PCI Device\n");
+ return -1;
+ }
+
+ pci_dev->config_header.vendor_id = 0x10ec;
+ pci_dev->config_header.device_id = 0x8029;
+ pci_dev->config_header.revision = 0x00;
-static int ne2k_init_device(struct vm_device * vmdev)
-{
- struct vm_device *pci = NULL;
- struct pci_device *pdev = NULL;
- struct ne2k_context *nic_state = (struct ne2k_context *)vmdev->private_data;
+ pci_dev->config_header.subclass = 0x00;
+ pci_dev->config_header.class = 0x02;
+ pci_dev->config_header.header_type = 0x00;
+
+ pci_dev->config_header.intr_line = 11;
+ pci_dev->config_header.intr_pin = 1;
+
+ nic_state->pci_dev = pci_dev;
+ }else {
+ PrintDebug(VM_NONE, VCORE_NONE, "NE2000: Not attached to PCI\n");
- PrintDebug("ne2k%d: init_device\n", nic_no);
+ v3_dev_hook_io(nic_state->dev, NIC_REG_BASE_PORT , &ne2k_cmd_read, &ne2k_cmd_write);
- if (nic_no == 0) // only initiate once
- init_global_setting();
+ for (i = 1; i < 16; i++){
+ v3_dev_hook_io(nic_state->dev, NIC_REG_BASE_PORT + i, &ne2k_std_read, &ne2k_std_write);
+ }
- init_ne2k_context(vmdev);
+ v3_dev_hook_io(nic_state->dev, NIC_REG_BASE_PORT + NE2K_DATA_OFFSET, &ne2k_data_read, &ne2k_data_write);
+ v3_dev_hook_io(nic_state->dev, NIC_REG_BASE_PORT + NE2K_RESET_OFFSET, &ne2k_reset_port_read, &ne2k_reset_port_write);
+ }
- pci = nic_state->pci;
- if (pci != NULL) {
- PrintDebug("Ne2k: attach ne2k to the pci %p\n", pci);
- pdev = pci_ne2k_init(vmdev, pci, 0, -1, 0, &ne2k_ioport_read, &ne2k_ioport_write);
- if (pdev == NULL)
- PrintError("Ne2k: initiate failure, failure to attach ne2k to the bus %p\n", pci);
- } else {
- PrintError("Ne2k: Failure to initiate Ne2k , no pci\n");
- ne2k_hook_iospace(vmdev, NE2K_DEF_BASE_ADDR, 100, 0, NULL);
- }
+ return 0;
+}
- nic_state->pci_dev = pdev;
- ne2ks[nic_no ++] = vmdev;
+static int connect_fn(struct v3_vm_info * info,
+ void * frontend_data,
+ struct v3_dev_net_ops * ops,
+ v3_cfg_tree_t * cfg,
+ void * private_data) {
+ struct ne2k_state * nic_state = (struct ne2k_state *)frontend_data;
- #ifdef DEBUG_NE2K
- dump_state(vmdev);
- #endif
+ ne2k_init_state(nic_state);
+ register_dev(nic_state);
- return 0;
+ nic_state->net_ops = ops;
+ nic_state->backend_data = private_data;
+
+ ops->recv = ne2k_rx;
+ ops->poll = NULL;
+ ops->config.frontend_data = nic_state;
+ ops->config.fnt_mac = nic_state->mac;
+
+ return 0;
}
+static int ne2k_free(struct ne2k_state * nic_state) {
+ int i;
-static int ne2k_deinit_device(struct vm_device *vmdev)
-{
- int i;
-
- for (i = 0; i<16; i++){
- v3_dev_unhook_io(vmdev, NE2K_DEF_BASE_ADDR + i);
- }
+ /* dettached from backend */
- v3_dev_unhook_io(vmdev, NE2K_DEF_BASE_ADDR + NIC_DATA_PORT);
- v3_dev_unhook_io(vmdev, NE2K_DEF_BASE_ADDR + NIC_RESET_PORT);
-
- return 0;
+ if(nic_state->pci_bus == NULL){
+ for (i = 0; i < 16; i++){
+ v3_dev_unhook_io(nic_state->dev, NIC_REG_BASE_PORT + i);
+ }
+
+ v3_dev_unhook_io(nic_state->dev, NIC_REG_BASE_PORT + NE2K_DATA_OFFSET);
+ v3_dev_unhook_io(nic_state->dev, NIC_REG_BASE_PORT + NE2K_RESET_OFFSET);
+ }else {
+ /* unregistered from PCI? */
+ }
+
+ V3_Free(nic_state);
+
+ return 0;
}
-static struct vm_device_ops dev_ops = {
- .init = ne2k_init_device,
- .deinit = ne2k_deinit_device,
- .reset = ne2k_reset_device,
- .start = ne2k_start_device,
- .stop = ne2k_stop_device,
+static struct v3_device_ops dev_ops = {
+ .free = (int (*)(void *))ne2k_free,
};
-struct vm_device *v3_create_ne2k(struct vm_device *pci)
-{
- struct ne2k_context * nic_state = V3_Malloc(sizeof(struct ne2k_context));
+static int ne2k_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
+ struct vm_device * pci_bus = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
+ struct ne2k_state * nic_state = NULL;
+ char * dev_id = v3_cfg_val(cfg, "ID");
+ char * macstr = v3_cfg_val(cfg, "mac");
- memset(nic_state, 0, sizeof(struct ne2k_context));
+ nic_state = (struct ne2k_state *)V3_Malloc(sizeof(struct ne2k_state));
- PrintDebug("NE2K internal at %p\n", (void *)nic_state);
+ if (!nic_state) {
+ PrintError(vm, VCORE_NONE, "Cannot allocate in init\n");
+ return -1;
+ }
- nic_state->pci = pci;
+ memset(nic_state, 0, sizeof(struct ne2k_state));
- struct vm_device *device = v3_create_device("NE2K", &dev_ops, nic_state);
+ nic_state->pci_bus = pci_bus;
+ nic_state->vm = vm;
- return device;
+ if (macstr != NULL && !str2mac(macstr, nic_state->mac)) {
+ PrintDebug(vm, VCORE_NONE, "NE2000: Mac specified %s\n", macstr);
+ }else {
+ PrintDebug(vm, VCORE_NONE, "NE2000: MAC not specified\n");
+ random_ethaddr(nic_state->mac);
+ }
+
+ struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, nic_state);
+
+ if (dev == NULL) {
+ PrintError(vm, VCORE_NONE, "NE2000: Could not attach device %s\n", dev_id);
+ V3_Free(nic_state);
+ return -1;
+ }
+
+ nic_state->dev = dev;
+
+ if (v3_dev_add_net_frontend(vm, dev_id, connect_fn, (void *)nic_state) == -1) {
+ PrintError(vm, VCORE_NONE, "NE2000: Could not register %s as net frontend\n", dev_id);
+ v3_remove_device(dev);
+ V3_Free(nic_state);
+ return -1;
+ }
+
+ return 0;
}
+device_register("NE2000", ne2k_init)