Date: Fri, 21 Aug 2015 18:31:37 GMT From: iateaca@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r290045 - soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve Message-ID: <201508211831.t7LIVboM030210@socsvn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: iateaca Date: Fri Aug 21 18:31:36 2015 New Revision: 290045 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=290045 Log: rename pci_ne2000.c in ne2000.c Added: soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/ne2000.c - copied unchanged from r290042, soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c Deleted: soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c Modified: soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/Makefile Modified: soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/Makefile ============================================================================== --- soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/Makefile Fri Aug 21 18:19:35 2015 (r290044) +++ soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/Makefile Fri Aug 21 18:31:36 2015 (r290045) @@ -20,12 +20,12 @@ mem.c \ mevent.c \ mptbl.c \ + ne2000.c \ pci_ahci.c \ pci_emul.c \ pci_hostbridge.c \ pci_irq.c \ pci_lpc.c \ - pci_ne2000.c \ pci_passthru.c \ pci_virtio_block.c \ pci_virtio_net.c \ Copied: soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/ne2000.c (from r290042, soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/ne2000.c Fri Aug 21 18:31:36 2015 (r290045, copy of r290042, soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c) @@ -0,0 +1,1342 @@ + + +#include <sys/types.h> +#include <machine/vmm.h> +#include <sys/ioctl.h> + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <pthread.h> + +#include <vmmapi.h> +#include <net/ethernet.h> + +#include "pci_emul.h" +#include "inout.h" +#include "mevent.h" +#include "if_edreg.h" + +/* + * NE2000 Debug Log + */ +#define DEBUG_NE2000 1 +#if DEBUG_NE2000 == 1 +static FILE *dbg; +#define DPRINTF(fmt, arg...) \ +do {fprintf(dbg, "%s-%d: " fmt "\n", __func__, __LINE__, ##arg); \ +fflush(dbg); } while (0) +#else +#define DPRINTF(fmt, arg...) +#endif + +/* + * NE2000 defines + */ +#define NE2000_P0 0 +#define NE2000_P1 1 +#define NE2000_P2 2 +#define NE2000_P3 3 +#define NE2000_P0_RO 4 + +#define NE2000_MEM_SIZE 32768 +#define NE2000_PAGE_SIZE 0x10 +#define NE2000_PAGE_COUNT 5 + +#define NE2000_BAR_NIC 0 +#define NE2000_BAR_ASIC 1 + +#define LPC_NE2000_NUM 2 + +#define ED_RTL80X9_CONFIG2 0x05 +#define ED_RTL80X9_CF2_10_T 0x40 +#define ED_RTL80X9_CONFIG3 0x06 +#define ED_RTL80X9_CF3_FUDUP 0x40 +#define ED_RTL80X9_80X9ID0 0x0a +#define ED_RTL80X9_ID0 0x50 +#define ED_RTL80X9_80X9ID1 0x0b +#define ED_RTL8029_ID1 0x43 + +#define MAX_INPUT_LEN 32 + +#define ETHER_MAX_FRAME_LEN (ETHER_MAX_LEN - ETHER_CRC_LEN) +#define ETHER_MIN_FRAME_LEN (ETHER_MIN_LEN - ETHER_CRC_LEN) + +typedef void (*ne2000_intr_func_t)(void *arg); + +/* + * NE2000 data structures + */ +struct ne2000_softc { + /* NIC registers */ + uint8_t nic_regs[NE2000_PAGE_COUNT][NE2000_PAGE_SIZE]; + + /* ASIC registers */ + uint8_t reset; + + /* State Variables */ + int tapfd; + uint8_t lintr; + uint8_t page; + uint8_t remote_read; + uint8_t remote_write; + + /* NIC memory is 32k */ + uint8_t ram[NE2000_MEM_SIZE]; + uint8_t rcv_buf[ETHER_MAX_FRAME_LEN]; + + /* + * one single mutex used to lock the reception flow with + * the read and write register flows + */ + pthread_mutex_t mtx; + + /* Interrupts callbacks (PCI or LPC) */ + ne2000_intr_func_t intr_assert; + ne2000_intr_func_t intr_deassert; + + /* The argument used by the interrupts callbacks */ + void *intr_arg; +}; + +/* + * NE2000 module function declarations + */ +static void +ne2000_set_reg_by_offset(struct ne2000_softc *sc, uint8_t page, + uint8_t offset, uint8_t value); +static uint8_t +ne2000_get_reg_by_offset(struct ne2000_softc *sc, uint8_t page, + uint8_t offset); +static void +ne2000_set_field_by_offset(struct ne2000_softc *sc, uint8_t page, + uint8_t offset, uint8_t mask, uint8_t value); + +static struct ne2000_softc * +ne2000_init(ne2000_intr_func_t intr_assert, ne2000_intr_func_t intr_deassert, + void *intr_arg, const char *opts); + +static void +ne2000_update_intr(struct ne2000_softc *sc); + +static uint16_t +ne2000_read(struct ne2000_softc *sc, uint8_t offset, int size); +static int +ne2000_write(struct ne2000_softc *sc, uint8_t offset, uint16_t value, int size); + +static uint8_t +ne2000_read_nic_locked(struct ne2000_softc *sc, uint8_t offset); +static uint16_t +ne2000_read_asic_locked(struct ne2000_softc *sc, uint8_t offset); + +static int +ne2000_write_nic_locked(struct ne2000_softc *sc, uint8_t offset, uint8_t value); +static int +ne2000_write_asic_locked(struct ne2000_softc *sc, uint8_t offset, uint16_t value); + +static int +ne2000_emul_reg_cr(struct ne2000_softc *sc, uint8_t value); +static int +ne2000_emul_reg_page0(struct ne2000_softc *sc, uint8_t offset, + uint8_t value); +static int +ne2000_emul_reg_page1(struct ne2000_softc *sc, uint8_t offset, + uint8_t value); + +static int ne2000_reset_board(void); +static int ne2000_software_reset(struct ne2000_softc *sc); + +static int +ne2000_tap_init(struct ne2000_softc *sc, char *tap_name); +static int +ne2000_tap_tx(struct ne2000_softc *sc, uint8_t tpsr, uint16_t tbcr); +static int +ne2000_tap_rx(struct ne2000_softc *sc); +static void +ne2000_tap_callback(int fd, enum ev_type type, void *param); + +static int +ne2000_receive_ring_is_valid(struct ne2000_softc *sc); +static int +ne2000_receive_ring_is_full(struct ne2000_softc *sc); + +static int +ne2000_ether_frame_is_valid(struct ne2000_softc *sc); + +static uint32_t +ne2000_ether_crc32_be(const uint8_t *buf, size_t len); + +static int +ne2000_parse_input(const char *opts, char *tap_name, uint8_t *mac); + +/* + * PCI NE2000 function declarations + */ +static int +pci_ne2000_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts); +static void +pci_ne2000_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int baridx, uint64_t offset, int size, uint64_t value); +static uint64_t +pci_ne2000_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int baridx, uint64_t offset, int size); + +static void +pci_ne2000_intr_assert(void *arg); +static void +pci_ne2000_intr_deassert(void *arg); + +/* + * LPC NE2000 function declarations + */ +int +lpc_ne2000_init(struct vmctx *lpc_ctx, uint8_t unit, const char *opts); +static int +lpc_ne2000_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, + uint32_t *eax, void *arg); + +static void +lpc_ne2000_intr_assert(void *arg); +static void +lpc_ne2000_intr_deassert(void *arg); + +/* + * NE2000 global data + */ +static struct lpc_ne2000_softc { + struct ne2000_softc *ne2000_sc; + struct vmctx *lpc_ctx; + int base_addr; + int irq; + const char *name; +} lpc_ne2000_sc[LPC_NE2000_NUM] = { + {NULL, NULL, 0x310, 10, "ne2k0"}, + {NULL, NULL, 0x330, 11, "ne2k1"} +}; + +struct pci_devemu pci_de_ne2000_net = { + .pe_emu = "ne2k", + .pe_init = pci_ne2000_init, + .pe_barwrite = pci_ne2000_write, + .pe_barread = pci_ne2000_read +}; +PCI_EMUL_SET(pci_de_ne2000_net); + +/* + * NE2000 module function definitions + */ +static void +ne2000_set_reg_by_offset(struct ne2000_softc *sc, uint8_t page, + uint8_t offset, uint8_t value) +{ + assert(page < NE2000_PAGE_COUNT); + assert(offset < NE2000_PAGE_SIZE); + + sc->nic_regs[page][offset] = value; +} + +static uint8_t +ne2000_get_reg_by_offset(struct ne2000_softc *sc, uint8_t page, + uint8_t offset) +{ + assert(page < NE2000_PAGE_COUNT); + assert(offset < NE2000_PAGE_SIZE); + + return sc->nic_regs[page][offset]; +} + +static void +ne2000_set_field_by_offset(struct ne2000_softc *sc, uint8_t page, + uint8_t offset, uint8_t mask, uint8_t value) +{ + uint8_t reg_value = 0; + + reg_value = ne2000_get_reg_by_offset(sc, page, offset); + + reg_value &= ~mask; + reg_value |= value; + + ne2000_set_reg_by_offset(sc, page, offset, reg_value); +} + +static int +ne2000_tap_init(struct ne2000_softc *sc, char *tap_name) +{ + int err; + int opt = 1; + struct mevent *evf_read = NULL; + + assert(tap_name != NULL); + + sc->tapfd = open(tap_name, O_RDWR); + assert(sc->tapfd != -1); + + err = ioctl(sc->tapfd, FIONBIO, &opt); + assert(err >= 0); + + evf_read = mevent_add(sc->tapfd, EVF_READ, ne2000_tap_callback, sc); + assert(evf_read != NULL); + + DPRINTF("Tap interface: fd: %d, opt: %d", sc->tapfd, opt); + + return 0; +} + +static int +ne2000_tap_tx(struct ne2000_softc *sc, uint8_t tpsr, uint16_t tbcr) +{ + ssize_t write_len; + + write_len = write(sc->tapfd, sc->ram + tpsr * ED_PAGE_SIZE, tbcr); + assert(write_len > 0 && write_len == tbcr); + + DPRINTF("Transmit Packet: from %d address of %d bytes", + tpsr * ED_PAGE_SIZE, tbcr); + + ne2000_set_field_by_offset(sc, NE2000_P0, ED_P0_ISR, + ED_ISR_PTX, ED_ISR_PTX); + ne2000_update_intr(sc); + + return 0; +} + +static int +ne2000_tap_rx(struct ne2000_softc *sc) +{ + uint8_t pstart = 0; + uint8_t pstop = 0; + uint8_t curr = 0; + uint8_t next_curr = 0; + uint8_t psize = 0; + + uint32_t size = 0; + uint32_t tmp_size = 0; + uint32_t index = 0; + uint32_t start = 0; + uint32_t stop = 0; + + ssize_t read_len = 0; + + struct ed_ring *ed_hdr = NULL; + + memset(sc->rcv_buf, 0, ETHER_MAX_FRAME_LEN); + read_len = read(sc->tapfd, sc->rcv_buf, ETHER_MAX_FRAME_LEN); + assert(read_len > 0); + + DPRINTF("Receive Packet: from tap interface of %zd bytes", read_len); + + /* clear the Receiver Status Register */ + ne2000_set_reg_by_offset(sc, NE2000_P0_RO, ED_P0_RSR, 0x00); + + if (!ne2000_receive_ring_is_valid(sc)) { + DPRINTF("Drop the packet: the ring is not valid"); + return 0; + } + + if (!ne2000_ether_frame_is_valid(sc)) { + DPRINTF("Drop the packet: the ether frame did not match"); + return 0; + } + + if (ne2000_receive_ring_is_full(sc)) { + DPRINTF("Drop the packet: the ring is full"); + return 0; + } + + ne2000_set_field_by_offset(sc, NE2000_P0_RO, ED_P0_RSR, + ED_RSR_PRX, ED_RSR_PRX); + + size = read_len < ETHER_MIN_FRAME_LEN ? ETHER_MIN_FRAME_LEN : read_len; + + /* psize is the number of pages used by the frame and ne2000 header */ + psize = (size + sizeof(struct ed_ring) + (ED_PAGE_SIZE - 1)) / + ED_PAGE_SIZE; + assert(psize <= 6); + + pstart = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTART); + pstop = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTOP); + curr = ne2000_get_reg_by_offset(sc, NE2000_P1, ED_P1_CURR); + + start = pstart * ED_PAGE_SIZE; + stop = pstop * ED_PAGE_SIZE; + index = curr * ED_PAGE_SIZE; + + next_curr = curr + psize; + if (next_curr >= pstop) + next_curr -= (pstop - pstart); + + DPRINTF("Receive Packet: size: %d psize: %d next_curr: %d index: %d", + size, psize, next_curr, index); + + ed_hdr = (struct ed_ring *)(sc->ram + index); + ed_hdr->rsr = ne2000_get_reg_by_offset(sc, NE2000_P0_RO, ED_P0_RSR); + ed_hdr->next_packet = next_curr; + ed_hdr->count = size + sizeof(struct ed_ring); + + index += sizeof(struct ed_ring); + + if (index + size >= stop) { + tmp_size = stop - index; + memcpy(sc->ram + index, sc->rcv_buf, tmp_size); + index = start; + size -= tmp_size; + } + memcpy(sc->ram + index, sc->rcv_buf + tmp_size, size); + + ne2000_set_reg_by_offset(sc, NE2000_P1, ED_P1_CURR, next_curr); + ne2000_set_field_by_offset(sc, NE2000_P0, ED_P0_ISR, + ED_ISR_PRX, ED_ISR_PRX); + + ne2000_update_intr(sc); + + return 0; +} + +static void +ne2000_tap_callback(int fd, enum ev_type type, void *param) +{ + int err; + struct ne2000_softc *sc = (struct ne2000_softc *)param; + assert(sc != NULL); + + err = pthread_mutex_lock(&sc->mtx); + assert(err == 0); + + err = ne2000_tap_rx(sc); + assert(err == 0); + + err = pthread_mutex_unlock(&sc->mtx); + assert(err == 0); + + return; +} + +static int +ne2000_receive_ring_is_valid(struct ne2000_softc *sc) +{ + uint8_t cr = 0; + + uint8_t pstart = 0; + uint8_t pstop = 0; + + uint8_t curr = 0; + uint8_t bnry = 0; + + cr = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_CR); + if (cr & ED_CR_STP) { + DPRINTF("Ring is not valid: the NIC is Stopped"); + return 0; + } + + pstart = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTART); + pstop = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTOP); + + curr = ne2000_get_reg_by_offset(sc, NE2000_P1, ED_P1_CURR); + bnry = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_BNRY); + + if (pstart == 0 || pstop == 0) + return 0; + if (curr < pstart || curr >= pstop) + return 0; + if (bnry < pstart || bnry >= pstop) + return 0; + + return 1; +} + +static int +ne2000_receive_ring_is_full(struct ne2000_softc *sc) +{ + uint32_t avail = 0; + uint32_t start = 0; + uint32_t stop = 0; + uint32_t index = 0; + uint32_t boundary = 0; + + uint8_t pstart = 0; + uint8_t pstop = 0; + uint8_t curr = 0; + uint8_t bnry = 0; + + assert(ne2000_receive_ring_is_valid(sc)); + + pstart = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTART); + pstop = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTOP); + curr = ne2000_get_reg_by_offset(sc, NE2000_P1, ED_P1_CURR); + bnry = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_BNRY); + + index = curr * ED_PAGE_SIZE; + boundary = bnry * ED_PAGE_SIZE; + start = pstart * ED_PAGE_SIZE; + stop = pstop * ED_PAGE_SIZE; + + if (index < boundary) + avail = boundary - index; + else + avail = (stop - start) - (index - boundary); + + if (avail < (ETHER_MAX_FRAME_LEN + sizeof(struct ed_ring))) + return 1; + + return 0; +} + +static int +ne2000_ether_frame_is_valid(struct ne2000_softc *sc) +{ + uint8_t key = 0; + uint8_t mar_offset = 0; + uint8_t mar_reg = 0; + uint8_t rcr = 0; + uint8_t broadcast_addr[] = {[0 ... (ETHER_ADDR_LEN - 1)] = 0xff}; + + rcr = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_RCR); + + if (rcr & ED_RCR_MON) { + DPRINTF("The NIC card is in Monitor Mode"); + return 0; + } + + /* is valid if the destination MAC matches the NIC's address */ + if (sc->rcv_buf[0] == sc->ram[0] && + sc->rcv_buf[1] == sc->ram[2] && + sc->rcv_buf[2] == sc->ram[4] && + sc->rcv_buf[3] == sc->ram[6] && + sc->rcv_buf[4] == sc->ram[8] && + sc->rcv_buf[5] == sc->ram[10]) + return 1; + + /* is valid if the destination MAC is the broadcast address */ + if (rcr & ED_RCR_AB) { + if (memcmp(sc->rcv_buf, broadcast_addr, ETHER_ADDR_LEN) == 0) { + ne2000_set_field_by_offset(sc, NE2000_P0_RO, ED_P0_RSR, + ED_RSR_PHY, ED_RSR_PHY); + return 1; + } + } + + /* is valid if the destination MAC represents a multicast address group */ + if ((rcr & ED_RCR_AM) && (sc->rcv_buf[0] & 0x01)) { + key = ne2000_ether_crc32_be(sc->rcv_buf, ETHER_ADDR_LEN) >> 26; + + mar_offset = ED_P1_MAR0 + (key >> 3); + mar_reg = ne2000_get_reg_by_offset(sc, NE2000_P1, mar_offset); + + if (mar_reg & (1 << (key & 7))) { + ne2000_set_field_by_offset(sc, NE2000_P0_RO, ED_P0_RSR, + ED_RSR_PHY, ED_RSR_PHY); + return 1; + } + } + + /* + * if the physical destination address does not match the station's + * address, accept the frame only in the Promiscuous Physical mode + */ + if (rcr & ED_RCR_PRO) + return 1; + + return 0; +} + +/* + * This function is a copy of the ether_crc32_be function from the net kernel + * module in the FreeBSD tree sources + */ +static uint32_t +ne2000_ether_crc32_be(const uint8_t *buf, size_t len) +{ + size_t i; + uint32_t crc, carry; + int bit; + uint8_t data; + + crc = 0xffffffff; /* initial value */ + + for (i = 0; i < len; i++) { + for (data = *buf++, bit = 0; bit < 8; bit++, data >>= 1) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (data & 0x01); + crc <<= 1; + if (carry) + crc = (crc ^ ETHER_CRC_POLY_BE) | carry; + } + } + + return (crc); +} + +static int +ne2000_parse_input(const char *opts, char *tap_name, uint8_t *mac) +{ + uint8_t len = 0; + char cp_opts[MAX_INPUT_LEN]; + char *delim = NULL; + char *p_mac = NULL; + struct ether_addr *addr = NULL; + + if (opts == NULL) + return 1; + + len = strlen(opts); + if (len >= MAX_INPUT_LEN) { + DPRINTF("The input len should be less than %d", MAX_INPUT_LEN); + return 1; + } + + strncpy(cp_opts, opts, MAX_INPUT_LEN); + + /* search for mac address in the input string */ + delim = strchr(cp_opts, ','); + + if (delim != NULL) { + /* mark the end of the tap name */ + *delim = 0; + + /* point to the start of the mac address */ + p_mac = delim + 1; + + /* parse the mac addres */ + addr = ether_aton(p_mac); + + /* if the mac address is valid overwrite the default one */ + if (addr != NULL) + memcpy(mac, addr, ETHER_ADDR_LEN); + else + DPRINTF("Invalid mac"); + } + + /* copy the tap name */ + strcpy(tap_name, cp_opts); + + return 0; +} + +static int +ne2000_write_nic_locked(struct ne2000_softc *sc, uint8_t offset, uint8_t value) +{ + int err; + + /* the CR register is located always at offset = 0 */ + if (offset == ED_P0_CR || offset == ED_P1_CR || offset == ED_P2_CR) + return ne2000_emul_reg_cr(sc, value); + + if (!(sc->page == NE2000_P0 && offset == ED_P0_ISR)) + ne2000_set_reg_by_offset(sc, sc->page, offset, value); + + if (sc->page == NE2000_P0) { + err = ne2000_emul_reg_page0(sc, offset, value); + assert(err == 0); + } else if (sc->page == NE2000_P1) { + err = ne2000_emul_reg_page1(sc, offset, value); + assert(err == 0); + } else if (sc->page == NE2000_P3) + DPRINTF("The ED driver wrote a register from PAGE3"); + else + assert(0); + + return 0; +} + +static int +ne2000_write_asic_locked(struct ne2000_softc *sc, uint8_t offset, uint16_t value) +{ + uint8_t dcr = 0; + uint8_t rbcr0 = 0; + uint8_t rbcr1 = 0; + uint8_t rsar0 = 0; + uint8_t rsar1 = 0; + + uint16_t rbcr = 0; + uint16_t rsar = 0; + + uint8_t pstart = 0; + uint8_t pstop = 0; + + switch (offset) { + case ED_NOVELL_RESET: + sc->reset = value; + break; + case ED_NOVELL_DATA: + /* Write the value word into the NIC's RAM using the Remote DMA + * protocol + */ + dcr = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_DCR); + if ((dcr & ED_DCR_WTS) != ED_DCR_WTS) { + DPRINTF("The NE2000 card is working only in Word mode"); + sc->remote_write = 0; + break; + } + + assert(sc->remote_write); + + rbcr0 = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_RBCR0); + rbcr1 = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_RBCR1); + rbcr = rbcr0 | (rbcr1 << 8); + + rsar0 = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_RSAR0); + rsar1 = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_RSAR1); + rsar = rsar0 | (rsar1 << 8); + + assert(rsar < NE2000_MEM_SIZE); + + if (ne2000_receive_ring_is_valid(sc)) { + pstart = ne2000_get_reg_by_offset(sc, NE2000_P0, + ED_P0_PSTART); + pstop = ne2000_get_reg_by_offset(sc, NE2000_P0, + ED_P0_PSTOP); + assert(rsar + 1 < pstart * ED_PAGE_SIZE || + rsar >= pstop * ED_PAGE_SIZE); + } + + /* copy the value in LOW - HIGH order */ + sc->ram[rsar] = value; + sc->ram[rsar + 1] = value >> 8; + + rsar += 2; + rbcr -= 2; + + if (rbcr == 0) { + sc->remote_write = 0; + ne2000_set_field_by_offset(sc, NE2000_P0, ED_P0_ISR, + ED_ISR_RDC, ED_ISR_RDC); + } + + ne2000_set_reg_by_offset(sc, NE2000_P0, ED_P0_RSAR0, rsar); + ne2000_set_reg_by_offset(sc, NE2000_P0, ED_P0_RSAR1, rsar >> 8); + + ne2000_set_reg_by_offset(sc, NE2000_P0, ED_P0_RBCR0, rbcr); + ne2000_set_reg_by_offset(sc, NE2000_P0, ED_P0_RBCR1, rbcr >> 8); + + break; + default: + assert(0); + } + + return 0; +} + +static int +ne2000_emul_reg_cr(struct ne2000_softc *sc, uint8_t value) +{ + int err; + uint8_t rbcr0 = 0; + uint8_t rbcr1 = 0; + uint8_t rsar0 = 0; + uint8_t rsar1 = 0; + + uint16_t rbcr = 0; + uint16_t rsar = 0; + + uint8_t tbcr0 = 0; + uint8_t tbcr1 = 0; + + uint16_t tbcr = 0; + uint8_t tpsr = 0; + + uint8_t old_cr = 0; + + /* check is not selected a new page */ + switch (value & (ED_CR_PS0 | ED_CR_PS1)) { + case ED_CR_PAGE_0: + sc->page = NE2000_P0; + break; + case ED_CR_PAGE_1: + sc->page = NE2000_P1; + break; + case ED_CR_PAGE_2: + DPRINTF("The ED driver seleted PAGE2"); + assert(0); + break; + case ED_CR_PAGE_3: + sc->page = NE2000_P3; + break; + } + + old_cr = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_CR); + + /* emulate any command specified in the CR register */ + if (value & ED_CR_STA) { + if ((old_cr & ED_CR_STA) == 0) { + ne2000_set_field_by_offset(sc, NE2000_P0, ED_P0_ISR, + ED_ISR_RST, 0); + } + } + if (value & ED_CR_RD2) + assert(!(sc->remote_read || sc->remote_write)); + if (value & (ED_CR_RD0 | ED_CR_RD1)) { + assert(value & ED_CR_STA); + + if (value & ED_CR_RD0) + sc->remote_read = 1; + else + sc->remote_write = 1; + + rbcr0 = ne2000_get_reg_by_offset(sc, NE2000_P0, + ED_P0_RBCR0); + rbcr1 = ne2000_get_reg_by_offset(sc, NE2000_P0, + ED_P0_RBCR1); + rbcr = rbcr0 | (rbcr1 << 8); + + rsar0 = ne2000_get_reg_by_offset(sc, NE2000_P0, + ED_P0_RSAR0); + rsar1 = ne2000_get_reg_by_offset(sc, NE2000_P0, + ED_P0_RSAR1); + rsar = rsar0 | (rsar1 << 8); + + DPRINTF("Remote DMA %s: from %d address of %d bytes", + sc->remote_read ? "read" : "write", rsar, rbcr); + } + if (value & ED_CR_TXP) { + assert(!(sc->remote_read || sc->remote_write)); + assert(value & ED_CR_STA); + + tpsr = ne2000_get_reg_by_offset(sc, NE2000_P0, + ED_P0_TPSR); + tbcr0 = ne2000_get_reg_by_offset(sc, NE2000_P0, + ED_P0_TBCR0); + tbcr1 = ne2000_get_reg_by_offset(sc, NE2000_P0, + ED_P0_TBCR1); + tbcr = tbcr0 | (tbcr1 << 8); + + err = ne2000_tap_tx(sc, tpsr, tbcr); + assert(err == 0); + } + + /* store the value in the CR register located in the Page0 */ + ne2000_set_reg_by_offset(sc, NE2000_P0, ED_P0_CR, value); + + return 0; +} + +static int +ne2000_emul_reg_page0(struct ne2000_softc *sc, uint8_t offset, + uint8_t value) +{ + uint8_t pstart = 0; + uint8_t pstop = 0; + + assert(offset != ED_P0_CR); + + switch (offset) { + case ED_P0_PSTART: + DPRINTF("Page Start Register: %d", value); + assert(value > 0 && value * ED_PAGE_SIZE < NE2000_MEM_SIZE); + break; + case ED_P0_PSTOP: + DPRINTF("Page Stop Register: %d", value); + assert(value > 0 && value * ED_PAGE_SIZE <= NE2000_MEM_SIZE); + break; + case ED_P0_BNRY: + DPRINTF("Boundary Register: %d", value); + pstart = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTART); + pstop = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTOP); + assert(value >= pstart && value < pstop); + break; + case ED_P0_ISR: + DPRINTF("ISR Register: %d", value); + ne2000_set_field_by_offset(sc, NE2000_P0, ED_P0_ISR, value, 0); + ne2000_update_intr(sc); + break; + case ED_P0_RCR: + DPRINTF("RCR Register: %d", value); + break; + } + + return 0; +} + +static int +ne2000_emul_reg_page1(struct ne2000_softc *sc, uint8_t offset, + uint8_t value) +{ + uint8_t pstart = 0; + uint8_t pstop = 0; + + assert(offset != ED_P1_CR); + + switch (offset) { + case ED_P1_PAR0 ... ED_P1_PAR5: + DPRINTF("PAR[%d]: 0x%x", offset - ED_P1_PAR0, value); + break; + case ED_P1_CURR: + DPRINTF("Current Page Register: %d", value); + pstart = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTART); + pstop = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_PSTOP); + assert(value >= pstart && value < pstop); + break; + case ED_P1_MAR0 ... ED_P1_MAR7: + DPRINTF("MAR[%d]: 0x%x", offset - ED_P1_MAR0, value); + break; + default: + assert(0); + } + + return 0; +} + +static int +ne2000_software_reset(struct ne2000_softc *sc) +{ + DPRINTF("The NIC is in Software Reset State"); + + /* reset the Receive Ring Registers */ + ne2000_set_reg_by_offset(sc, NE2000_P0, ED_P0_PSTART, 0); + ne2000_set_reg_by_offset(sc, NE2000_P0, ED_P0_PSTOP, 0); + ne2000_set_reg_by_offset(sc, NE2000_P1, ED_P1_CURR, 0); + ne2000_set_reg_by_offset(sc, NE2000_P0, ED_P0_BNRY, 0); + + /* disable the interrupts */ + ne2000_set_reg_by_offset(sc, NE2000_P0, ED_P0_IMR, 0); + + /* the NIC enters the reset state */ + ne2000_set_field_by_offset(sc, NE2000_P0, ED_P0_ISR, + ED_ISR_RST, ED_ISR_RST); + + return 0; +} + +static struct ne2000_softc * +ne2000_init(ne2000_intr_func_t intr_assert, ne2000_intr_func_t intr_deassert, + void *intr_arg, const char *opts) +{ + struct ne2000_softc *sc = NULL; + + /* the default mac address is 00:a0:98:4a:0e:ee */ + uint8_t mac[ETHER_ADDR_LEN] = {0x00, 0xa0, 0x98, 0x4a, 0x0e, 0xee}; + char tap_name[MAX_INPUT_LEN]; + int err; + + assert(intr_assert); + assert(intr_deassert); + assert(intr_arg); + +#if DEBUG_NE2000 == 1 + dbg = fopen("/tmp/bhyve_ne2000.log", "w+"); +#endif + + sc = calloc(1, sizeof(struct ne2000_softc)); + + sc->intr_assert = intr_assert; + sc->intr_deassert = intr_deassert; + sc->intr_arg = intr_arg; + + err = ne2000_parse_input(opts, tap_name, mac); + if (err != 0) { + printf("Use input param like: -s x:y,ne2000-net,tap_name[,mac address]"); + free(sc); + return NULL; + } + + err = pthread_mutex_init(&sc->mtx, NULL); + assert(err == 0); + + err = ne2000_tap_init(sc, tap_name); + assert(err == 0); + + /* set network medium type as 10BaseT and full-duplex */ + ne2000_set_reg_by_offset(sc, NE2000_P3, + ED_RTL80X9_CONFIG2, ED_RTL80X9_CF2_10_T); + ne2000_set_reg_by_offset(sc, NE2000_P3, + ED_RTL80X9_CONFIG3, ED_RTL80X9_CF3_FUDUP); + + /* the NE2000 card has his MAC address located in the first 6 words of the RAM memory */ + sc->ram[0] = mac[0]; + sc->ram[2] = mac[1]; + sc->ram[4] = mac[2]; + sc->ram[6] = mac[3]; + sc->ram[8] = mac[4]; + sc->ram[10] = mac[5]; + + return sc; +} + +static void +ne2000_update_intr(struct ne2000_softc *sc) +{ + uint8_t isr = 0; + uint8_t imr = 0; + + isr = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_ISR); + imr = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_IMR); + + if (imr & isr) { + if (!sc->lintr) { + sc->intr_assert(sc->intr_arg); + sc->lintr = 1; + } + } else { + if (sc->lintr) { + sc->intr_deassert(sc->intr_arg); + sc->lintr = 0; + } + } +} + +static uint16_t +ne2000_read(struct ne2000_softc *sc, uint8_t offset, int size) +{ + int err; + uint16_t value = 0; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201508211831.t7LIVboM030210>