Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 10 Jul 2015 14:51:13 GMT
From:      iateaca@FreeBSD.org
To:        svn-soc-all@FreeBSD.org
Subject:   socsvn commit: r288190 - soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve
Message-ID:  <201507101451.t6AEpDxH027534@socsvn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: iateaca
Date: Fri Jul 10 14:51:13 2015
New Revision: 288190
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=288190

Log:
  implement the reception protocol of the NE2000 nic card

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	Fri Jul 10 13:54:03 2015	(r288189)
+++ soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve/pci_ne2000.c	Fri Jul 10 14:51:13 2015	(r288190)
@@ -53,6 +53,9 @@
 
 #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)
+
 /*
  * NE2000 data structures
  */
@@ -74,6 +77,7 @@
 
 	/* NIC memory is 16k */
 	uint8_t ram[NE2000_MEM_SIZE];
+	uint8_t rcv_buf[ETHER_MAX_FRAME_LEN];
 };
 
 /*
@@ -132,6 +136,11 @@
 
 static int
 ne2000_receive_ring_is_valid(struct pci_ne2000_softc *sc);
+static int
+ne2000_receive_ring_is_full(struct pci_ne2000_softc *sc);
+
+static int
+ne2000_ether_frame_is_valid(struct pci_ne2000_softc *sc);
 
 static int
 ne2000_parse_input(char *opts, char *tap_name, uint8_t *mac);
@@ -239,13 +248,86 @@
 static int
 ne2000_tap_rx(struct pci_ne2000_softc *sc)
 {
-	uint8_t dummybuf[2048];
-	ssize_t read_len;
+	uint8_t pstart = 0;
+	uint8_t pstop = 0;
+	uint8_t curr = 0;
+	uint8_t next_curr = 0;
+	uint8_t psize = 0;
 
-	read_len = read(sc->tapfd, dummybuf, sizeof(dummybuf));
+	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);
 
+	if (!ne2000_ether_frame_is_valid(sc))
+		return -1;
+
+	if (!ne2000_receive_ring_is_valid(sc)) {
+		DPRINTF("Drop the packet since the ring is not valid");
+		return 0;
+	}
+
+	if (ne2000_receive_ring_is_full(sc)) {
+		DPRINTF("Drop the packet since the ring is full");
+		return 0;
+	}
+
+	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);
+
+	ne2000_set_field_by_offset(sc, NE2000_P0, ED_P0_RSR, 0xff, ED_RSR_PRX);
+
+	ed_hdr = (struct ed_ring *)(sc->ram + index);
+	ed_hdr->rsr = ne2000_get_reg_by_offset(sc, NE2000_P0, 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);
+
+	pci_ne2000_update_intr(sc);
+
 	return 0;
 }
 
@@ -288,6 +370,70 @@
 }
 
 static int
+ne2000_receive_ring_is_full(struct pci_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;
+
+	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 pci_ne2000_softc *sc)
+{
+	/*
+	 * TODO implement a better validation taking in consideration the
+	 * Receiver Configuration Register (RCR) and maybe the frame's CRC
+	 */
+
+	/* 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 (sc->rcv_buf[0] == 0xff &&
+	    sc->rcv_buf[1] == 0xff &&
+	    sc->rcv_buf[2] == 0xff &&
+	    sc->rcv_buf[3] == 0xff &&
+	    sc->rcv_buf[4] == 0xff &&
+	    sc->rcv_buf[5] == 0xff)
+		return 1;
+
+	return 0;
+}
+
+static int
 ne2000_parse_input(char *opts, char *tap_name, uint8_t *mac)
 {
 	uint8_t len = 0;
@@ -442,7 +588,8 @@
 		}
 	}
 
-	ne2000_set_reg_by_offset(sc, sc->page, offset, 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);
@@ -620,6 +767,7 @@
 		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);
 		pci_ne2000_update_intr(sc);
 		break;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201507101451.t6AEpDxH027534>