socsvn commit: r286536 - soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve

iateaca at FreeBSD.org iateaca at FreeBSD.org
Mon Jun 1 19:30:50 UTC 2015


Author: iateaca
Date: Mon Jun  1 19:30:49 2015
New Revision: 286536
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=286536

Log:
  Commit Description:
  implement the NE2000 registers support and an API to access the NIC registers (get and set by offset),
  the initialization phase of the NIC so the ED driver can probe it as a RealTek 8029 device,
  the Remote DMA protocol so the ED driver can store and load from the NIC's RAM memory
  
  M    bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c

Modified:
  soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c

Modified: soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c
==============================================================================
--- soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c	Mon Jun  1 18:49:31 2015	(r286535)
+++ soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c	Mon Jun  1 19:30:49 2015	(r286536)
@@ -1,6 +1,6 @@
 
 #include <stdio.h>
-
+#include <stdlib.h>
 
 #include "pci_emul.h"
 #include "if_edreg.h"
@@ -18,23 +18,152 @@
 #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_MEM_SIZE		32768
+#define NE2000_PAGE_SIZE	0x10
+#define NE2000_PAGE_COUNT	4
+
+#define NE2000_BAR_NIC		0
+#define NE2000_BAR_ASIC		1
+
+#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
+
+
+/*
+ * NE2000 data structures
+ */
+struct pci_ne2000_softc {
+	struct pci_devinst *asc_pi;
+
+	/* NIC registers */
+	uint8_t nic_regs[NE2000_PAGE_COUNT][NE2000_PAGE_SIZE];
+
+	/* ASIC registers */
+	uint8_t reset;
+
+	/* State Variables */
+	uint8_t page;
+
+	/* NIC memory is 16k */
+	uint8_t ram[NE2000_MEM_SIZE];
+};
+
+/*
+ * NE2000 module function declarations
+ */
+static void
+ne2000_set_reg_by_offset(struct pci_ne2000_softc *sc, uint8_t page,
+		uint8_t offset, uint8_t value);
+static int
+ne2000_get_reg_by_offset(struct pci_ne2000_softc *sc, uint8_t page,
+		uint8_t offset);
+static void
+ne2000_set_field_by_offset(struct pci_ne2000_softc *sc, uint8_t page,
+		uint8_t offset, uint8_t mask, uint8_t value);
+
+static uint8_t
+ne2000_read_nic(struct pci_ne2000_softc *sc, uint8_t offset);
+static uint16_t
+ne2000_read_asic(struct pci_ne2000_softc *sc, uint8_t offset);
+
+static int
+ne2000_write_nic(struct pci_ne2000_softc *sc, uint8_t offset, uint8_t value);
+static int
+ne2000_write_asic(struct pci_ne2000_softc *sc, uint8_t offset, uint16_t value);
+
+static int
+ne2000_emul_reg_page0(struct pci_ne2000_softc *sc, uint8_t offset,
+		uint8_t value);
+
+static int ne2000_reset_board(void);
+static int ne2000_software_reset(struct pci_ne2000_softc *sc);
+
+
+/*
+ * NE2000 module function definitions
+ */
+static void
+ne2000_set_reg_by_offset(struct pci_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 int
+ne2000_get_reg_by_offset(struct pci_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 pci_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
 pci_ne2000_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
 {
+	struct pci_ne2000_softc *sc = NULL;
 
 #if DEBUG_NE2000 == 1
 	dbg = fopen("/tmp/bhyve_ne2000.log", "w+");
 #endif
 
+	assert(ctx != NULL);
+	assert(pi != NULL);
+	assert(opts != NULL);
+
+	sc = calloc(1, sizeof(struct pci_ne2000_softc));
+	pi->pi_arg = sc;
+	sc->asc_pi = pi;
+
+	/* probe a RTL8029 PCI card as a generic NE2000 device */
 	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x8029);
 	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10ec);
 
-	pci_emul_alloc_bar(pi, 0, PCIBAR_IO, 16); // nic
-	pci_emul_alloc_bar(pi, 1, PCIBAR_IO, 16); // asic
+	/* allocate two BAR registers for both NIC and ASIC I/O bus address offsets */
+	pci_emul_alloc_bar(pi, 0, PCIBAR_IO, 16);
+	pci_emul_alloc_bar(pi, 1, PCIBAR_IO, 16);
 
+	/* allocate an IRQ pin for our slot */
 	pci_lintr_request(pi);
 
+	/* 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);
+
 	return 0;
 }
 
@@ -42,13 +171,275 @@
 pci_ne2000_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
 		int baridx, uint64_t offset, int size, uint64_t value)
 {
+	struct pci_ne2000_softc *sc = pi->pi_arg;
+	int err;
+
+	assert(sc != NULL);
+	assert(offset <= 0x0f);
+
+	switch (baridx) {
+	case NE2000_BAR_NIC:
+		assert(size == 1);
+		assert(value <= 0xff);
+		err = ne2000_write_nic(sc, offset, value);
+		break;
+	case NE2000_BAR_ASIC:
+		assert(size <= 2);
+		err = ne2000_write_asic(sc, offset, value);
+		break;
+	default:
+		assert(0);
+	}
+
+	assert(err == 0);
+
 	return;
 }
 
+static int
+ne2000_write_nic(struct pci_ne2000_softc *sc, uint8_t offset, uint8_t value)
+{
+	int err;
+
+	/* check is not selected a new page */
+	if (offset == ED_P0_CR) {
+		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;
+		}
+	}
+
+	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_P3)
+		DPRINTF("The ED driver wrote a register from PAGE3");
+	else
+		assert(0);
+
+	return 0;
+}
+
+static int
+ne2000_write_asic(struct pci_ne2000_softc *sc, uint8_t offset, uint16_t value)
+{
+	uint8_t dcr = 0;
+	uint8_t cr = 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;
+
+	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");
+			break;
+		}
+
+		cr = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_CR);
+		assert((cr & (ED_CR_RD1 | ED_CR_STA)) ==
+				(ED_CR_RD1 | ED_CR_STA));
+
+		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);
+
+		/* copy the value in LOW - HIGH order */
+		sc->ram[rsar]     = value;
+		sc->ram[rsar + 1] = value >> 8;
+
+		rsar += 2;
+		rbcr -= 2;
+
+		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_page0(struct pci_ne2000_softc *sc, uint8_t offset,
+		uint8_t value)
+{
+	int err;
+
+	switch (offset) {
+	case ED_P0_CR:
+		if (value & ED_CR_STP) {
+			err = ne2000_software_reset(sc);
+			assert(err == 0);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int
+ne2000_software_reset(struct pci_ne2000_softc *sc)
+{
+	uint8_t mask = 0;
+	uint8_t value = 0;
+
+	mask |= ED_ISR_RST;
+	value |= ED_ISR_RST;
+	ne2000_set_field_by_offset(sc, NE2000_P0, ED_P0_ISR, mask, value);
+
+	return 0;
+}
+
 static uint64_t
 pci_ne2000_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
 		int baridx, uint64_t offset, int size)
 {
+	struct pci_ne2000_softc *sc = pi->pi_arg;
+	uint64_t value = 0;
+
+	assert(sc != NULL);
+	assert(offset <= 0x0f);
+
+	switch (baridx) {
+	case NE2000_BAR_NIC:
+		assert(size == 1);
+		value = ne2000_read_nic(sc, offset);
+		break;
+	case NE2000_BAR_ASIC:
+		assert(size <= 2);
+		value = ne2000_read_asic(sc, offset);
+		break;
+	default:
+		assert(0);
+	}
+
+	return value;
+}
+
+static uint8_t
+ne2000_read_nic(struct pci_ne2000_softc *sc, uint8_t offset)
+{
+	uint8_t value = 0;
+
+	/* check is not a RTL8029 Register Defined in Page0 */
+	if (sc->page == NE2000_P0) {
+		if (offset == ED_RTL80X9_80X9ID0)
+			return ED_RTL80X9_ID0;
+		else if (offset == ED_RTL80X9_80X9ID1)
+			return ED_RTL8029_ID1;
+	}
+
+	/* read a general NE2000 register */
+	value = ne2000_get_reg_by_offset(sc, sc->page, offset);
+
+	return value;
+}
+
+static uint16_t
+ne2000_read_asic(struct pci_ne2000_softc *sc, uint8_t offset)
+{
+	int err;
+	uint8_t dcr = 0;
+	uint8_t cr = 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;
+	uint16_t read_value = 0;
+
+	switch (offset) {
+	case ED_NOVELL_RESET:
+		read_value = sc->reset;
+		err = ne2000_reset_board();
+		assert(err == 0);
+		break;
+	case ED_NOVELL_DATA:
+		/* Read one word from 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");
+			break;
+		}
+
+		cr = ne2000_get_reg_by_offset(sc, NE2000_P0, ED_P0_CR);
+		assert((cr & (ED_CR_RD0 | ED_CR_STA)) ==
+				(ED_CR_RD0 | ED_CR_STA));
+
+		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);
+
+		/* copy the value in LOW - HIGH order */
+		read_value = sc->ram[rsar] | (sc->ram[rsar + 1] << 8);
+
+		rsar += 2;
+		rbcr -= 2;
+
+		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 read_value;
+}
+
+static int ne2000_reset_board(void)
+{
+	DPRINTF("The driver resets the board");
 
 	return 0;
 }


More information about the svn-soc-all mailing list