/* * This file is part of the Palacios Virtual Machine Monitor developed * by the V3VEE Project with funding from the United States National * Science Foundation and the Department of Energy. * * The V3VEE Project is a joint project between Northwestern University * and the University of New Mexico. You can find out more at * http://www.v3vee.org * * Copyright (c) 2009, Lei Xia * Copyright (c) 2009, The V3VEE Project * All rights reserved. * * Author: Lei Xia * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "V3VEE_LICENSE". */ /* * Virtual NE2K Network Card */ #include #include #include #include #include #include #include #ifndef CONFIG_DEBUG_NE2K #undef PrintDebug #define PrintDebug(fmts, args...) #endif #define NE2K_DEFAULT_IRQ 11 #define MAX_ETH_FRAME_SIZE 1514 // 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 NIC_DATA_PORT 0xc110 //Data read/write port #define NIC_RESET_PORT 0xc11f //Data read/write port // 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 typedef enum {NIC_READY, NIC_REG_POSTED} nic_state_t; 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_context { struct guest_info * vm; nic_state_t dev_state; // 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)); uint8_t mcast_addr[8]; // multicast mask array uint8_t mac_addr[6]; // MAC Addr uint8_t mem[NE2K_MEM_SIZE]; struct pci_device * pci_dev; struct vm_device * pci_bus; }; #define compare_mac(src, dst) !memcmp(src, dst, 6) #ifdef CONFIG_DEBUG_NE2K static void dump_state(struct vm_device * dev) { struct ne2k_context *nic_state = (struct ne2k_context *)dev->private_data; int i; PrintDebug("====NE2000: Dumping state Begin ==========\n"); PrintDebug("Registers:\n"); // JRL: Dump Registers PrintDebug("Memory:\n"); for(i = 0; i < 32; i++) { PrintDebug("0x%02x ", nic_state->mem[i]); } PrintDebug("\n"); PrintDebug("====NE2000: Dumping state End==========\n"); } #endif static int ne2k_update_irq(struct vm_device *dev) { struct ne2k_context * nic_state = (struct ne2k_context *)dev->private_data; struct pci_device * pci_dev = nic_state->pci_dev; int irq_line = 0; if (pci_dev == NULL){ PrintDebug("Ne2k: Device %p is not attached to any PCI Bus\n", nic_state); irq_line = NE2K_DEFAULT_IRQ; } else { irq_line = pdev->config_header.intr_line; } if (irq_line == 0){ PrintError("Ne2k: IRQ_LINE: %d\n", irq_line); return -1; } PrintDebug("ne2k_update_irq: irq_line: %d\n", irq_line); // The top bit of the ISR/IMR is reserved and does not indicate and irq event // We mask the bit out of the irq pending check if ((nic_state->isr.val & nic_state->imr.val) & 0x7f) { v3_raise_irq(nic_state->vm, irq_line); PrintDebug("Ne2k: RaiseIrq: isr: 0x%02x imr: 0x%02x\n", nic_state->isr.val, nic_state->imr.val); } return 0; } static void ne2k_init_state(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_state->vm = dev->vm; nic_state->isr.reset = 1; nic_state->imr.val = 0x00; nic_state->cmd.val = 0x22; for (i = 0; i < 5; i++) { nic_state->mac_addr[i] = mac[i]; } memset(nic_state->mcast_addr, 0xff, sizeof(nic_state->mcast_addr)); // Not sure what this is about.... memset(nic_state->mem, 0xff, 32); memcpy(nic_state->mem, nic_state->mac_addr, 6); nic_state->mem[14] = 0x57; nic_state->mem[15] = 0x57; #ifdef CONFIG_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"); PrintError("Implement Send Packet interface\n"); return -1; } static int ne2k_rxbuf_full(struct vm_device *dev) { int empty; int index; int boundary; struct ne2k_context * nic_state = (struct ne2k_context *)dev->private_data; index = nic_state->curpag << 8; boundary = nic_state->boundary << 8; if (index < boundary) { empty = boundary - index; } else { empty = ((nic_state->pgstop - nic_state->pgstart) << 8) - (index - boundary); } if (empty < (MAX_ETH_FRAME_SIZE + 4)) { return 1; } return 0; } #define MIN_BUF_SIZE 60 // This needs to be completely redone... 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; uchar_t * p; uint32_t total_len; uint32_t next; uint32_t len; uint32_t index; uint32_t empty; uchar_t buf[60]; uint32_t start; uint32_t stop; start = nic_state->pgstart << 8; stop = nic_state->pgstop << 8; if (nic_state->cmd.stop) { return; } if (ne2k_rxbuf_full(dev)) { PrintError("Ne2k: received buffer overflow\n"); return; } //packet too small, expand it if (length < MIN_BUF_SIZE) { memcpy(buf, pkt, length); memset(buf + length, 0, MIN_BUF_SIZE - length); pkt = buf; length = MIN_BUF_SIZE; } index = nic_state->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); } p = nic_state->mem + index; nic_state->rsr.val = 0; nic_state->rsr.rx_pkt_ok = 1; if (pkt[0] & 0x01) { nic_state->rsr.phy = 1; } p[0] = nic_state->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; } nic_state->curpag = next >> 8; nic_state->isr.pkt_rx = 1; ne2k_update_irq(dev); } static int netif_input(uchar_t *pkt, uint_t size) { struct ne2k_context * nic_state; static const uchar_t brocast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int i; #ifdef CONFIG_DEBUG_NE2K PrintDebug("\nNe2k: Packet Received:\nSource:"); for (i = 6; i < 12; i++) { PrintDebug("%x ", pkt[i]); } PrintDebug("\n"); for(i = 0; i < size; i++) { PrintDebug("%x ", pkt[i]); } PrintDebug("\n"); #endif if (nic_state->rcr.prom_phys_enable == 1) { //promiscuous mode ne2k_receive(ne2ks[i], pkt, size); } else if (compare_mac(pkt, brocast_mac) && (nic_state->rcr.bcast_ok)) { //broadcast packet ne2k_receive(ne2ks[i], pkt, size); } else if ((pkt[0] & 0x01) && (nic_state->rcr.mcast_ok)) { //TODO: multicast packet ne2k_receive(ne2ks[i], pkt, size); } else if (compare_mac(pkt, nic_state->mac_addr)) { ne2k_receive(ne2ks[i], pkt, size); } return 0; } static void ne2k_mem_writeb(struct ne2k_context * nic_state, uint32_t addr, uint32_t val) { uchar_t tmp; tmp = (uchar_t) (val & 0x000000ff); 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); } PrintDebug("wmem addr: %x val: %x\n", 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); } PrintDebug("wmem addr: %x val: %x\n", addr, val); } static uchar_t ne2k_mem_readb(struct ne2k_context *nic_state, uint32_t addr) { PrintDebug("rmem addr: %x\n", addr); 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)); } else { return 0xffff; } } static uint32_t ne2k_mem_readl(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 (uint32_t)le32_to_cpu((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->rsar += len; // wrap if (nic_state->rsar == nic_state->pgstop) { nic_state->rsar = nic_state->pgstart; } if (nic_state->rbcr <= len) { nic_state->rbcr = 0; nic_state->isr.rem_dma_done = 1; ne2k_update_irq(dev); } else { nic_state->rbcr -= len; } } //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->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); memcpy(dst, &val, length); PrintDebug("ne2k_read: port:0x%x (%u bytes): 0x%x", 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; uint32_t addr = nic_state->rsar; if (nic_state->rbcr == 0) { return length; } memcpy(&val, src, length); 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); return length; } static int ne2k_reset_device(struct vm_device * dev) { PrintDebug("vnic: reset device\n"); init_ne2k_context(dev); return 0; } //for 0xc11f port static int ne2k_reset_port_read(ushort_t port, void * dst, uint_t length, struct vm_device * dev) { PrintDebug("ne2k_read: port:0x%x (%u bytes): 0x%x\n", port, length, val); memset(dst, 0, length); ne2k_reset_device(dev); return length; } static int ne2k_reset_port_write(ushort_t port, void * src, uint_t length, struct vm_device * dev) { PrintDebug("ne2k_write: port:0x%x (%u bytes): 0x%x\n", port, length, val); return length; } static int ne2k_cmd_write(uint16_t port, void * src, uint_t length, struct vm_device * dev) { struct ne2k_context * nic_state = (struct ne2k_context *)dev->private_data; if (length != 1) { PrintError("Invalid write length to ne2k Command register\n"); return -1; } nic_state->cmd.val = *(uint8_t *)src; if (!(nic_state->cmd.stop)) { nic_state->isr.reset = 0; // if ((send pkt) && (dma byte count == 0)) if ((nic_state.rem_dma_cmd & 0x3) && (nic_state->rbcr == 0)) { nic_state->isr.rem_dma_done = 1; ne2k_update_irq(dev); } if (nic_state->cmd.tx_pkt) { int offset = (nic_state->tpsr << 8); if (offset >= NE2K_PMEM_END) { offset -= NE2K_PMEM_SIZE; } if (offset + nic_state->tbcr <= NE2K_PMEM_END) { ne2k_send_packet(dev, nic_state->mem + offset, nic_state->tbcr); } nic_state->tsr.val = 0; // clear the tx status reg nic_state->tsr.pkt_tx_ok = 1; // indicate successful tx nic_state->isr.pkt_tx = 1; // irq due to pkt tx nic_state->cmd.tx_pkt = 0; // reset cmd bit ne2k_update_irq(dev); } } else { // stop the controller } return length; } static int ne2k_cmd_read(uint16_t port, void * src, uint_t length, struct vm_device * dev) { *(uint8_t *)dst = nic_state->cmd.val; PrintDebug("ne2k_read: port:0x%x val: 0x%x\n", port, *(uint8_t *)dst); return length; } static int ne2k_std_write(uint16_t port, void * src, uint_t length, struct vm_device * dev) { struct ne2k_context * nic_state = (struct ne2k_context *)dev->private_data; int index = port & 0x1f; uint8_t page = nic_state->cmd.pg_sel; if (length != 1 PrintError("ne2k_write error: length %d\n", length); return -1; } PrintDebug("ne2k_write: port:0x%x val: 0x%x\n", port, (int)val); if (page == 0) { switch (port) { case EN0_STARTPG: nic_state->pgstart = val; break; case EN0_STOPPG: nic_state->pgstop = val; break; case EN0_BOUNDARY: nic_state->boundary = val; break; case EN0_TPSR: nic_state->tpsr = val; break; case EN0_TCNTLO: nic_state->tbcr0 = val; break; case EN0_TCNTHI: nic_state->tbcr1 = val; break; case EN0_ISR: nic_state->isr.val &= ~(val & 0x7f); ne2k_update_irq(dev); break; case EN0_RSARLO: nic_state->rsar0 = val; break; case EN0_RSARHI: nic_state->rsar1 = val; break; case EN0_RCNTLO: nic_state->rbcr0 = val; break; case EN0_RCNTHI: nic_state->rbcr1 = val; break; case EN0_RXCR: nic_state->rcr.val = val; break; case EN0_TXCR: nic_state->tcr.val = val; break; case EN0_DCFG: nic_state->dcr.val = val; break; case EN0_IMR: nic_state->imr.val = 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); return -1; } } else if (page == 1) { switch (port) { case EN1_PHYS ... EN1_PHYS + 5: nic_state->mac_addr[port - EN1_PHYS] = val; break; case EN1_CURPAG: nic_state->curpag = val; break; case EN1_MULT ... EN1_MULT + 7: // PrintError("ne2k_write error: write EN_MULT:0x%x\n", (int)val); nic_state->mcast_addr[port - EN1_MULT] = val; break; default: PrintError("ne2k_write error: invalid port:0x%x\n", port); return -1; } } else if (page == 2) { switch (port) { case EN2_LDMA0: nic_state->clda0 = val; break; case EN2_LDMA1: nic_state->clda1 = val; break; case EN2_RNPR: nic_state->rnpp = val; break; case EN2_LNRP: nic_state->lnpp = val; break; case EN2_ACNT0: nic_state->addcnt0 = val; break; case EN2_ACNT1: nic_state->addcnt1 = val; break; default: PrintError("ne2k_write error: invalid port:0x%x\n", port); return -1; } } else { PrintError("Invalid Register Page Value\n"); return -1; } return length; } static int ne2k_std_read(uint16_t port, void * dst, uint_t length, struct vm_device *dev) { struct ne2k_context * nic_state = (struct ne2k_context *)(dev->private_data); uint16_t index = port & 0x1f; uint8_t page = nic_state->cmd.pg_sel; if (length > 1) { PrintError("ne2k_read error: length %d\n", length); return length; } if (page == 0) { switch (index) { case EN0_CLDALO: *(uint8_t *)dst = nic_state->clda0; break; case EN0_CLDAHI: *(uint8_t *)dst = nic_state->clda1; break; case EN0_BOUNDARY: *(uint8_t *)dst = nic_state->boundary; break; case EN0_TSR: *(uint8_t *)dst = nic_state->tsr.val; break; case EN0_NCR: *(uint8_t *)dst = nic_state->ncr; break; case EN0_FIFO: *(uint8_t *)dst = nic_state->fifo; break; case EN0_ISR: *(uint8_t *)dst = nic_state->isr.val; ne2k_update_irq(dev); break; case EN0_CRDALO: *(uint8_t *)dst = nic_state->crda0; break; case EN0_CRDAHI: *(uint8_t *)dst = nic_state->crda1; break; case EN0_RSR: *(uint8_t *)dst = nic_state->rsr.val; break; case EN0_COUNTER0: *(uint8_t *)dst = nic_state->cntr0; break; case EN0_COUNTER1: *(uint8_t *)dst = nic_state->cntr1; break; case EN0_COUNTER2: *(uint8_t *)dst = nic_state->cntr2; break; default: PrintError("ne2k_read error: invalid port:0x%x\n", port); return -1; } } else if (page == 1) { switch (index) { case EN1_PHYS ... EN1_PHYS + 5: *(uint8_t *)dst = nic_state->mac_addr[index - EN1_PHYS]; break; case EN1_CURPAG: *(uint8_t *)dst = nic_state->curpag; break; case EN1_MULT ... EN1_MULT + 7: *(uint8_t *)dst = nic_state->mcast_addr[index - EN1_MULT]; break; default: PrintError("ne2k_read error: invalid port:0x%x\n", port); return -1; } } else if (page == 2) { switch (index) { case EN2_STARTPG: *(uint8_t *)dst = nic_state->pgstart; break; case EN2_STOPPG: *(uint8_t *)dst = nic_state->pgstop; break; case EN2_RNPR: *(uint8_t *)dst = nic_state->rnpp; break; case EN2_LNRP: *(uint8_t *)dst = nic_state->lnpp; break; case EN2_TPSR: *(uint8_t *)dst = nic_state->tpsr; break; case EN2_ACNT0: *(uint8_t *)dst = nic_state->addcnt0; break; case EN2_ACNT1: *(uint8_t *)dst = nic_state->addcnt1; break; case EN2_RCR: *(uint8_t *)dst = nic_state->rcr.val; break; case EN2_TCR: *(uint8_t *)dst = nic_state->tcr.val; break; case EN2_DCR: *(uint8_t *)dst = nic_state->dcr.val; break; case EN2_IMR: *(uint8_t *)dst = nic_state->imr.val; break; default: PrintError("ne2k_read error: invalid port:0x%x\n", port); return -1; } } else { PrintError("Invalid Register Page Value\n"); return -1; } PrintDebug("ne2k_read: port:0x%x val: 0x%x\n", port, *(uint8_t *)dst); return length; } static int ne2k_start_device(struct vm_device * dev) { PrintDebug("vnic: start device\n"); return 0; } static int ne2k_stop_device(struct vm_device * dev) { PrintDebug("vnic: stop device\n"); return 0; } static int ne2k_init_device(struct vm_device * dev) { struct ne2k_context * nic_state = (struct ne2k_context *)(dev->private_data); PrintDebug("Initializing NE2K\n"); init_ne2k_context(dev); if (nic_state->pci_bus == NULL) { PrintDebug("NE2k: Not attached to pci\n"); v3_dev_hook_io(dev, NIC_REG_BASE_PORT , &ne2k_cmd_read, &ne2k_cmd_write); for (i = 1; i < 16; i++){ v3_dev_hook_io(dev, NIC_REG_BASE_PORT + i, &ne2k_std_read, &ne2k_std_write); } v3_dev_hook_io(dev, NIC_DATA_PORT, &ne2k_data_read, &ne2k_data_write); v3_dev_hook_io(dev, NIC_RESET_PORT, &ne2k_reset_read, &ne2k_reset_write); } else { struct v3_pci_bar bars[6]; struct pci_device * pci_dev = NULL; int i; PrintDebug("NE2k: PCI Enabled\n"); for (i = 0; i < 6; i++) { bars[i].type = PCI_BAR_NONE; } bars[0].type = PCI_BAR_IO; bars[0].default_base_port = NIC_REG_BASE_PORT; bars[0].num_ports = 256; bars[0].io_read = ne2k_pci_read; bars[0].io_write = ne2k_pci_write; pci_dev = v3_pci_register_device(nic_state->pci_bus, PCI_STD_DEVICE, 0, -1, 0, "NE2000", bars, pci_config_update, NULL, NULL, dev); if (pci_dev == NULL) { PrintError("Failed to register NE2K with PCI\n"); return -1; } pci_dev->config_header.vendor_id = 0x10ec; pci_dev->config_header.device_id = 0x8029; pci_dev->config_header.revision = 0x00; 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; } #ifdef CONFIG_DEBUG_NE2K dump_state(dev); #endif return 0; } static int ne2k_deinit_device(struct vm_device *dev) { int i; for (i = 0; i < 16; i++){ v3_dev_unhook_io(dev, NIC_REG_BASE_PORT + i); } v3_dev_unhook_io(dev, NIC_DATA_PORT); v3_dev_unhook_io(dev, NIC_RESET_PORT); return 0; } static struct v3_device_ops dev_ops = { .init = ne2k_init_device, .deinit = ne2k_deinit_device, .reset = ne2k_reset_device, .start = ne2k_start_device, .stop = ne2k_stop_device, }; struct vm_device * v3_create_ne2k(struct vm_device * pci) { struct ne2k_context * nic_state = V3_Malloc(sizeof(struct ne2k_context)); memset(nic_state, 0, sizeof(struct ne2k_context)); nic_state->pci_bus = pci; struct vm_device * device = v3_create_device("NE2K", &dev_ops, nic_state); return device; }