socsvn commit: r288190 - soc2015/iateaca/bhyve-ne2000-head/usr.sbin/bhyve
iateaca at FreeBSD.org
iateaca at FreeBSD.org
Fri Jul 10 14:51:14 UTC 2015
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;
More information about the svn-soc-all
mailing list