qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PULL v3 02/11] hppa: Add support for LASI chip with i82596 NIC


From: Helge Deller
Subject: Re: [PULL v3 02/11] hppa: Add support for LASI chip with i82596 NIC
Date: Mon, 2 Mar 2020 21:34:25 +0100

* Peter Maydell <address@hidden>:
> On Mon, 2 Mar 2020 at 19:23, Helge Deller <address@hidden> wrote:
> >
> > On 17.02.20 18:56, Peter Maydell wrote:
> > > On Fri, 24 Jan 2020 at 23:20, Richard Henderson
> > > <address@hidden> wrote:
> > >>
> > >> From: Helge Deller <address@hidden>
> > >>
> > >> LASI is a built-in multi-I/O chip which supports serial, parallel,
> > >> network (Intel i82596 Apricot), sound and other functionalities.
> > >> LASI has been used in many HP PARISC machines.
> > >> This patch adds the necessary parts to allow Linux and HP-UX to detect
> > >> LASI and the network card.
>
> > > So we could be reading off the end of the buffer.
> > >
> > > I don't know whether the buffer should be 4 bytes
> > > larger to allow for the checksum, or if the len calculation
> > > is wrong.
> >
> > I'm working on a bigger patch which will improve this driver.
> > It still has some issues with the emulation in Linux and HP-UX.
> > With the patch I will take try to fix those out-of-bounds
> > accesses too.
> > It will take some time though, until I will send the patch.
>
> As this is a bug fix and in particular a fix for a buffer
> overrun, I think it would be better if you could send a
> patch that just fixes this.

Of course I usually agree on that, but in this special case the initial
patch was more or less the basic functionality that the NIC gets
detected. The driver is nevertheless full of bugs, so even if I'd try to
get this specific buffer overrun fixed, it will not give you a working
NIC emulation. Such a patch would just hide the buffer overrun without
any real benefit.

> If you include it in a larger general-improvements patch we'll
> probably just ask you to split it out when we get to code-review of
> that patchset, and doing it that way will mean we have the buggy code
> in QEMU for longer.

I've done big progress with my fixes. Below you will find the current
patch against qemu git head which does work with a Linux guest. It's not
perfect yet either, but the Linux guest gets an IP address, apt-get
update works, same as pings and such. It's still work-in-progress, so
please don't look too deep into it yet. I'm aware of the rough edges
which I plan to clean up in the next few days, but I attached it here
for your reference in the hope that you can see that there is much more
work than just trying to fix some static code analysis output.

That said, I would be happy to further clean it up, split it into
logically seperate patches and send them upstream in a few days for
review if you agree. I think with this approach we gain more than trying
to "fix" the old buggy code.

Thoughts?

Helge


diff --git a/hw/net/i82596.c b/hw/net/i82596.c
index fe9f2390a9..f8d55601bc 100644
--- a/hw/net/i82596.c
+++ b/hw/net/i82596.c
@@ -27,29 +27,29 @@
 #define DBG(x)          do { } while (0)
 #endif

-#define USE_TIMER       0
-
 #define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)

-#define PKT_BUF_SZ      1536
 #define MAX_MC_CNT      64

 #define ISCP_BUSY       0x0001

 #define I596_NULL       ((uint32_t)0xffffffff)

-#define SCB_STATUS_CX   0x8000 /* CU finished command with I bit */
-#define SCB_STATUS_FR   0x4000 /* RU finished receiving a frame */
-#define SCB_STATUS_CNA  0x2000 /* CU left active state */
-#define SCB_STATUS_RNR  0x1000 /* RU left active state */
+#define SCB_STAT_CX     0x8000 /* CU finished command with I bit */
+#define SCB_STAT_FR     0x4000 /* RU finished receiving a frame */
+#define SCB_STAT_CNA    0x2000 /* CU left active state */
+#define SCB_STAT_RNR    0x1000 /* RU left active state */

-#define CU_IDLE         0
+#define CU_IDLE         0       /* CUS values */
 #define CU_SUSPENDED    1
 #define CU_ACTIVE       2

-#define RX_IDLE         0
+#define RX_IDLE         0       /* RUS values */
 #define RX_SUSPENDED    1
+#define RX_NO_RESOURCES 2
 #define RX_READY        4
+#define RX_NO_RESO_RBD  (8 + RX_NO_RESOURCES)
+#define RX_NO_MORE_RBD  (8 + RX_READY)

 #define CMD_EOL         0x8000  /* The last command of the list, stop. */
 #define CMD_SUSP        0x4000  /* Suspend after doing cmd. */
@@ -76,10 +76,13 @@ enum commands {

 /* various flags in the chip config registers */
 #define I596_PREFETCH   (s->config[0] & 0x80)
+#define I596_NO_SRC_ADD_IN (s->config[3] & 0x08) /* if 1, do not insert MAC in 
Tx Packet */
+#define I596_LOOPBACK   (s->config[3] >> 6)     /* loopback mode, 3 = external 
loopback */
 #define I596_PROMISC    (s->config[8] & 0x01)
-#define I596_BC_DISABLE (s->config[8] & 0x02) /* broadcast disable */
-#define I596_NOCRC_INS  (s->config[8] & 0x08)
-#define I596_CRCINM     (s->config[11] & 0x04) /* CRC appended */
+#define I596_BC_DISABLE (s->config[8] & 0x02)   /* broadcast disable */
+#define I596_NOCRC_INS  (s->config[8] & 0x08)   /* do not append CRC to Tx 
frame */
+#define I596_CRC16_32   (s->config[8] & 0x10)   /* CRC-16 or CRC-32 */
+#define I596_CRCINM     (s->config[11] & 0x04)  /* Rx CRC appended in memory */
 #define I596_MC_ALL     (s->config[11] & 0x20)
 #define I596_MULTIIA    (s->config[13] & 0x40)

@@ -134,9 +137,14 @@ struct qemu_ether_header {
 static void i82596_transmit(I82596State *s, uint32_t addr)
 {
     uint32_t tdb_p; /* Transmit Buffer Descriptor */
+    uint16_t cmd;
+    int insert_crc;

-    /* TODO: Check flexible mode */
+    cmd = get_uint16(addr + 2);
+    assert(cmd & 8);    /* check flexible mode */
     tdb_p = get_uint32(addr + 8);
+    /* check NC bit and possibly insert CRC */
+    insert_crc = (I596_NOCRC_INS == 0) && ((cmd & 0x10) == 0) && 
!I596_LOOPBACK;
     while (tdb_p != I596_NULL) {
         uint16_t size, len;
         uint32_t tba;
@@ -147,16 +155,43 @@ static void i82596_transmit(I82596State *s, uint32_t addr)
         trace_i82596_transmit(len, tba);

         if (s->nic && len) {
-            assert(len <= sizeof(s->tx_buffer));
+            uint16_t new_len;
+            new_len = len + 4;
+            assert(new_len <= sizeof(s->tx_buffer));
             address_space_read(&address_space_memory, tba,
                                MEMTXATTRS_UNSPECIFIED, s->tx_buffer, len);
+
+            if (I596_NO_SRC_ADD_IN == 0) {
+                /* insert MAC in Tx Packet */
+                memcpy(&s->tx_buffer[ETH_ALEN], s->conf.macaddr.a, ETH_ALEN);
+            }
+
+            DBG(printf("i82596_transmit: insert_crc = %d  insert SRC = %d\n",
+                        insert_crc, I596_NO_SRC_ADD_IN == 0));
+            if (insert_crc) {
+                uint32_t crc = crc32(~0, s->tx_buffer, len);
+                crc = cpu_to_be32(crc);
+                memcpy(&s->tx_buffer[len], &crc, sizeof(crc));
+                len += sizeof(crc);
+            }
+
             DBG(PRINT_PKTHDR("Send", &s->tx_buffer));
-            DBG(printf("Sending %d bytes\n", len));
-            qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, len);
+            DBG(printf("Sending %d bytes (crc_inserted=%d)\n", len, 
insert_crc));
+            switch (I596_LOOPBACK) {
+            case 0:     /* no loopback, send packet */
+                qemu_send_packet_raw(qemu_get_queue(s->nic), s->tx_buffer, 
len);
+                break;
+            case 1:     /* external loopback enabled */
+                i82596_receive(qemu_get_queue(s->nic), s->tx_buffer, len);
+                break;
+            default:    /* all other loopback modes: ignore! */
+                break;
+            }
         }

         /* was this the last package? */
         if (size & I596_EOF) {
+            qemu_flush_queued_packets(qemu_get_queue(s->nic));
             break;
         }

@@ -175,6 +210,7 @@ static void set_individual_address(I82596State *s, uint32_t 
addr)
     address_space_read(&address_space_memory, addr + 8,
                        MEMTXATTRS_UNSPECIFIED, m, ETH_ALEN);
     qemu_format_nic_info_str(nc, m);
+    DBG(printf("MAC addr set to %s\n", nc->info_str));
     trace_i82596_new_mac(nc->info_str);
 }

@@ -188,6 +224,7 @@ static void set_multicast_list(I82596State *s, uint32_t 
addr)
     if (mc_count > MAX_MC_CNT) {
         mc_count = MAX_MC_CNT;
     }
+    DBG(printf("Add %d multicast entries.\n", mc_count));
     for (i = 0; i < mc_count; i++) {
         uint8_t multicast_addr[ETH_ALEN];
         address_space_read(&address_space_memory, addr + i * ETH_ALEN,
@@ -212,7 +249,9 @@ void i82596_set_link_status(NetClientState *nc)
 static void update_scb_status(I82596State *s)
 {
     s->scb_status = (s->scb_status & 0xf000)
-        | (s->cu_status << 8) | (s->rx_status << 4);
+        | (s->CUS << 8) | (s->RUS << 4)
+        | 8 /* 8: bus throttle timers loaded */;
+    DBG(printf("update_scb_status 0x%04x CUS: %d, RUS: %d\n", s->scb_status, 
s->CUS, s->RUS));
     set_uint16(s->scb, s->scb_status);
 }

@@ -220,13 +259,13 @@ static void update_scb_status(I82596State *s)
 static void i82596_s_reset(I82596State *s)
 {
     trace_i82596_s_reset(s);
-    s->scp = 0;
+    DBG(printf("i82596_s_reset()\n"));
+    s->scp = 0x00FFFFF4;
     s->scb_status = 0;
-    s->cu_status = CU_IDLE;
-    s->rx_status = RX_SUSPENDED;
+    s->CUS = CU_IDLE;
+    s->RUS = RX_IDLE;
     s->cmd_p = I596_NULL;
     s->lnkst = 0x8000; /* initial link state: up */
-    s->ca = s->ca_active = 0;
     s->send_irq = 0;
 }

@@ -237,7 +276,7 @@ static void command_loop(I82596State *s)
     uint16_t status;
     uint8_t byte_cnt;

-    DBG(printf("STARTING COMMAND LOOP cmd_p=%08x\n", s->cmd_p));
+    DBG(printf("STARTING COMMAND LOOP cmd_p=0x%08x\n", s->cmd_p));

     while (s->cmd_p != I596_NULL) {
         /* set status */
@@ -246,7 +285,8 @@ static void command_loop(I82596State *s)
         status = STAT_C | STAT_OK; /* update, but write later */

         cmd = get_uint16(s->cmd_p + 2);
-        DBG(printf("Running command %04x at %08x\n", cmd, s->cmd_p));
+        DBG(printf("Running command 0x%04x (cmd %d) at 0x%08x\n",
+                cmd, cmd & 7, s->cmd_p));

         switch (cmd & 0x07) {
         case CmdNOp:
@@ -264,11 +304,17 @@ static void command_loop(I82596State *s)
             /* config byte according to page 35ff */
             s->config[2] &= 0x82; /* mask valid bits */
             s->config[2] |= 0x40;
+            DBG(printf("I596_CONFIG3 = 0x%02x  LOOPBACK 0x%x\n", s->config[3], 
I596_LOOPBACK));
+            if (I596_NO_SRC_ADD_IN == 0) {
+                assert((s->config[3] & 0x07) == ETH_ALEN);
+            }
             s->config[7]  &= 0xf7; /* clear zero bit */
-            assert(I596_NOCRC_INS == 0); /* do CRC insertion */
+            assert(I596_CRC16_32 == 0); /* only CRC-32 implemented */
+            DBG(printf("I596_CRCINM = %d\n\n", I596_CRCINM));
             s->config[10] = MAX(s->config[10], 5); /* min frame length */
             s->config[12] &= 0x40; /* only full duplex field valid */
             s->config[13] |= 0x3f; /* set ones in byte 13 */
+            s->scb_status |= SCB_STAT_CX;
             break;
         case CmdTDR:
             /* get signal LINK */
@@ -290,7 +336,7 @@ static void command_loop(I82596State *s)
         set_uint16(s->cmd_p, status);

         s->cmd_p = get_uint32(s->cmd_p + 4); /* get link address */
-        DBG(printf("NEXT addr would be %08x\n", s->cmd_p));
+        DBG(printf("NEXT loop addr is 0x%08x\n", s->cmd_p));
         if (s->cmd_p == 0) {
             s->cmd_p = I596_NULL;
         }
@@ -301,71 +347,51 @@ static void command_loop(I82596State *s)
         }
         /* Suspend after doing cmd? */
         if (cmd & CMD_SUSP) {
-            s->cu_status = CU_SUSPENDED;
-            printf("FIXME SUSPEND !!\n");
+            s->CUS = CU_SUSPENDED;
+            printf("FIXME SUSPEND ?\n");
         }
-        /* Interrupt after doing cmd? */
-        if (cmd & CMD_INTR) {
-            s->scb_status |= SCB_STATUS_CX;
-        } else {
-            s->scb_status &= ~SCB_STATUS_CX;
-        }
-        update_scb_status(s);

         /* Interrupt after doing cmd? */
         if (cmd & CMD_INTR) {
+            s->scb_status |= SCB_STAT_CX;
             s->send_irq = 1;
         }

-        if (s->cu_status != CU_ACTIVE) {
+        if (s->CUS == CU_SUSPENDED) {
             break;
         }
     }
     DBG(printf("FINISHED COMMAND LOOP\n"));
-    qemu_flush_queued_packets(qemu_get_queue(s->nic));
-}
-
-static void i82596_flush_queue_timer(void *opaque)
-{
-    I82596State *s = opaque;
-    if (0) {
-        timer_del(s->flush_queue_timer);
-        qemu_flush_queued_packets(qemu_get_queue(s->nic));
-        timer_mod(s->flush_queue_timer,
-              qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
-    }
 }

 static void examine_scb(I82596State *s)
 {
-    uint16_t command, cuc, ruc;
+    uint16_t command, cuc, ruc, c;

     /* get the scb command word */
     command = get_uint16(s->scb + 2);
+    DBG(printf("COMMAND = 0x%04x\n", command));
     cuc = (command >> 8) & 0x7;
     ruc = (command >> 4) & 0x7;
-    DBG(printf("MAIN COMMAND %04x  cuc %02x ruc %02x\n", command, cuc, ruc));
-    /* and clear the scb command word */
-    set_uint16(s->scb + 2, 0);
+    DBG(printf("MAIN CU COMMAND 0x%04x: stat 0x%02x cuc 0x%02x ruc 0x%02x\n",
+            command, command >> 12,  cuc, ruc));

-    if (command & BIT(31))      /* ACK-CX */
-        s->scb_status &= ~SCB_STATUS_CX;
-    if (command & BIT(30))      /*ACK-FR */
-        s->scb_status &= ~SCB_STATUS_FR;
-    if (command & BIT(29))      /*ACK-CNA */
-        s->scb_status &= ~SCB_STATUS_CNA;
-    if (command & BIT(28))      /*ACK-RNR */
-        s->scb_status &= ~SCB_STATUS_RNR;
+    /* toggle the STAT flags in SCB status word */
+    c = command & (SCB_STAT_CX | SCB_STAT_FR | SCB_STAT_CNA | SCB_STAT_RNR);
+    s->scb_status &= ~c;

     switch (cuc) {
     case 0:     /* no change */
+    case 5:
+    case 6:
         break;
     case 1:     /* CUC_START */
-        s->cu_status = CU_ACTIVE;
+        s->CUS = CU_ACTIVE;
         break;
     case 4:     /* CUC_ABORT */
-        s->cu_status = CU_SUSPENDED;
-        s->scb_status |= SCB_STATUS_CNA; /* CU left active state */
+        s->CUS = CU_IDLE;
+        s->scb_status |= SCB_STAT_CNA; /* CU left active state */
+        s->send_irq = 1;
         break;
     default:
         printf("WARNING: Unknown CUC %d!\n", cuc);
@@ -376,16 +402,17 @@ static void examine_scb(I82596State *s)
         break;
     case 1:     /* RX_START */
     case 2:     /* RX_RESUME */
-        s->rx_status = RX_IDLE;
-        if (USE_TIMER) {
-            timer_mod(s->flush_queue_timer, qemu_clock_get_ms(
-                                QEMU_CLOCK_VIRTUAL) + 1000);
-        }
+        s->RUS = RX_READY;
         break;
     case 3:     /* RX_SUSPEND */
+        s->RUS = RX_SUSPENDED;
+        s->scb_status |= SCB_STAT_RNR; /* RU left active state */
+        s->send_irq = 1;
+        break;
     case 4:     /* RX_ABORT */
-        s->rx_status = RX_SUSPENDED;
-        s->scb_status |= SCB_STATUS_RNR; /* RU left active state */
+        s->RUS = RX_IDLE;
+        s->scb_status |= SCB_STAT_RNR; /* RU left active state */
+        s->send_irq = 1;
         break;
     default:
         printf("WARNING: Unknown RUC %d!\n", ruc);
@@ -396,66 +423,92 @@ static void examine_scb(I82596State *s)
     }

     /* execute commands from SCBL */
-    if (s->cu_status != CU_SUSPENDED) {
+    if (s->CUS == CU_ACTIVE) {
         if (s->cmd_p == I596_NULL) {
             s->cmd_p = get_uint32(s->scb + 4);
         }
+        command_loop(s);
+        s->CUS = CU_IDLE;
+        s->send_irq = 1;
     }

-    /* update scb status */
-    update_scb_status(s);
-
-    command_loop(s);
+    qemu_flush_queued_packets(qemu_get_queue(s->nic));
 }

 static void signal_ca(I82596State *s)
 {
-    uint32_t iscp = 0;
+    DBG(printf("-- CA start\n"));

     /* trace_i82596_channel_attention(s); */
     if (s->scp) {
+        uint32_t iscp;
+        uint8_t sysbus;
+        uint8_t mode;       /* MODE_82586 or MODE_LINEAR */
+
         /* CA after reset -> do init with new scp. */
-        s->sysbus = get_byte(s->scp + 3); /* big endian */
-        DBG(printf("SYSBUS = %08x\n", s->sysbus));
-        if (((s->sysbus >> 1) & 0x03) != 2) {
-            printf("WARNING: NO LINEAR MODE !!\n");
-        }
-        if ((s->sysbus >> 7)) {
+        sysbus = get_byte(s->scp + 3); /* big endian */
+        DBG(printf("SYSBUS = %08x\n", sysbus));
+        mode = (sysbus >> 1) & 0x03;
+        /* Only MODE_LINEAR is currently implemented. */
+        assert(mode == MODE_LINEAR);
+        if ((sysbus >> 7)) {
             printf("WARNING: 32BIT LINMODE IN B-STEPPING NOT SUPPORTED !!\n");
         }
         iscp = get_uint32(s->scp + 8);
         s->scb = get_uint32(iscp + 4);
+        DBG(printf("ISCP = 0x%08x, SCB = 0x%08x\n", iscp,s->scb));
+        /* set_uint32(iscp + 4, 0); NOT: clear SCB pointer */
         set_byte(iscp + 1, 0); /* clear BUSY flag in iscp */
+        /* sets CX and CNR to equal 1 in the SCB, clears the SCB command word,
+         * sends an interrupt to the CPU, and awaits another CA signal */
+        s->scb_status = SCB_STAT_CX | SCB_STAT_CNA;
+        s->CUS = CU_IDLE;
+        s->RUS = RX_IDLE;
         s->scp = 0;
+        s->send_irq = 1;
+        goto _cont;
     }

-    s->ca++;    /* count ca() */
-    if (!s->ca_active) {
-        s->ca_active = 1;
-        while (s->ca)   {
-            examine_scb(s);
-            s->ca--;
-        }
-        s->ca_active = 0;
-    }
+    examine_scb(s);
+
+_cont:
+    /* update scb status */
+    update_scb_status(s);
+
+    /* and clear the scb command word */
+    set_uint16(s->scb + 2, 0);

     if (s->send_irq) {
         s->send_irq = 0;
+        DBG(printf("Send IRQ\n"));
         qemu_set_irq(s->irq, 1);
     }
+    DBG(printf("-- CA end\n"));
 }

 void i82596_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
 {
     I82596State *s = opaque;
-    /* printf("i82596_ioport_writew addr=0x%08x val=0x%04x\n", addr, val); */
-    switch (addr) {
+    uint32_t res, tmp;
+    DBG(printf("i82596_ioport_writew addr=0x%08x val=0x%04x\n", addr, val));
+    switch (addr & PORT_BYTEMASK) {
     case PORT_RESET: /* Reset */
         i82596_s_reset(s);
         break;
+    case PORT_SELFTEST:
+        res = val + sizeof(uint32_t);
+        tmp = get_uint32(res); /* should be -1 */
+        DBG(printf("i82596 SELFTEST at 0x%04x val 0x%04x requested.\n", res, 
tmp));
+        assert(tmp == I596_NULL);
+        set_uint32(res, 0); /* set to zero */
+        break;
     case PORT_ALTSCP:
+        DBG(printf("i82596 ALTSCP requested.\n"));
         s->scp = val;
         break;
+    case PORT_ALTDUMP:
+        printf("i82596 PORT_ALTDUMP not implemented yet.\n");
+        break;
     case PORT_CA:
         signal_ca(s);
         break;
@@ -478,18 +531,15 @@ int i82596_can_receive(NetClientState *nc)
 {
     I82596State *s = qemu_get_nic_opaque(nc);

-    if (s->rx_status == RX_SUSPENDED) {
-        return 0;
+    if (s->RUS != RX_READY) {
+        /* return 0; */
     }

+    /* Link down? */
     if (!s->lnkst) {
         return 0;
     }

-    if (USE_TIMER && !timer_pending(s->flush_queue_timer)) {
-        return 1;
-    }
-
     return 1;
 }

@@ -500,7 +550,7 @@ ssize_t i82596_receive(NetClientState *nc, const uint8_t 
*buf, size_t sz)
     I82596State *s = qemu_get_nic_opaque(nc);
     uint32_t rfd_p;
     uint32_t rbd;
-    uint16_t is_broadcast = 0;
+    uint16_t status, is_broadcast = 0;
     size_t len = sz;
     uint32_t crc;
     uint8_t *crc_ptr;
@@ -508,14 +558,10 @@ ssize_t i82596_receive(NetClientState *nc, const uint8_t 
*buf, size_t sz)
     static const uint8_t broadcast_macaddr[6] = {
                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

-    DBG(printf("i82596_receive() start\n"));
-
-    if (USE_TIMER && timer_pending(s->flush_queue_timer)) {
-        return 0;
-    }
+    DBG(printf("i82596_receive() start, sz = %lu\n", sz));

     /* first check if receiver is enabled */
-    if (s->rx_status == RX_SUSPENDED) {
+    if (s->RUS == RX_SUSPENDED) {
         trace_i82596_receive_analysis(">>> Receiving suspended");
         return -1;
     }
@@ -527,14 +573,14 @@ ssize_t i82596_receive(NetClientState *nc, const uint8_t 
*buf, size_t sz)

     /* Received frame smaller than configured "min frame len"? */
     if (sz < s->config[10]) {
-        printf("Received frame too small, %zu vs. %u bytes\n",
-               sz, s->config[10]);
-        return -1;
+        if (0) printf("Received frame too small, %lu vs. %u bytes\n",
+            sz, s->config[10]);
+        sz = 60; /* return -1; */
     }

     DBG(printf("Received %lu bytes\n", sz));

-    if (I596_PROMISC) {
+    if (I596_PROMISC || I596_LOOPBACK) {

         /* promiscuous: receive all */
         trace_i82596_receive_analysis(
@@ -598,69 +644,90 @@ ssize_t i82596_receive(NetClientState *nc, const uint8_t 
*buf, size_t sz)
     }

     /* Calculate the ethernet checksum (4 bytes) */
-    len += 4;
-    crc = cpu_to_be32(crc32(~0, buf, sz));
-    crc_ptr = (uint8_t *) &crc;
-
-    rfd_p = get_uint32(s->scb + 8); /* get Receive Frame Descriptor */
-    assert(rfd_p && rfd_p != I596_NULL);
+    if (I596_CRCINM && !I596_LOOPBACK) {
+        len += 4;
+        crc = crc32(~0, buf, sz);
+        crc = cpu_to_be32(crc);
+        crc_ptr = (uint8_t *) &crc;
+    }

-    /* get first Receive Buffer Descriptor Address */
-    rbd = get_uint32(rfd_p + 8);
-    assert(rbd && rbd != I596_NULL);
+    rfd_p = get_uint32(s->scb + 8); /* get initial Receive Frame Descriptor */
+    do {
+        assert(rfd_p && rfd_p != I596_NULL);
+        status = get_uint16(rfd_p+0);
+        /* if rfd is filled, get next one from link addr */
+        if (status & STAT_OK)
+            rfd_p = get_uint32(rfd_p+4);
+    } while (status & STAT_OK);

     trace_i82596_receive_packet(len);
-    /* PRINT_PKTHDR("Receive", buf); */
+    DBG(PRINT_PKTHDR("Receive", buf));

     while (len) {
-        uint16_t command, status;
+        uint16_t command;
         uint32_t next_rfd;
+        uint32_t rba;
+        uint16_t rba_size;
+        uint32_t actual_count_ptr;

+        DBG(printf("Receive: rfd is 0x%08x, len = %lu\n", rfd_p, len));
         command = get_uint16(rfd_p + 2);
         assert(command & CMD_FLEX); /* assert Flex Mode */
-        /* get first Receive Buffer Descriptor Address */
+
+        /* get first Receive Buffer Descriptor address */
         rbd = get_uint32(rfd_p + 8);
-        assert(get_uint16(rfd_p + 14) == 0);
+        assert(rbd && rbd != I596_NULL);

-        /* printf("Receive: rfd is %08x\n", rfd_p); */
+        /* possibly store first bytes in rfd */
+        rba = rfd_p + 16;       /* 30 ?? data is behind the length field */
+        rba_size = get_uint16(rfd_p + 14); /* count of additional bytes in rfd 
*/
+        actual_count_ptr = rfd_p + 12;

         while (len) {
-            uint16_t buffer_size, num;
-            uint32_t rba;
+            uint16_t num, actual_count;

-            /* printf("Receive: rbd is %08x\n", rbd); */
-            buffer_size = get_uint16(rbd + 12);
-            /* printf("buffer_size is 0x%x\n", buffer_size); */
-            assert(buffer_size != 0);
+            DBG(printf("rba is at 0x%x, rba_size = %d, cnt_ptr 0x%08x\n", rba, 
rba_size, actual_count_ptr));

-            num = buffer_size & SIZE_MASK;
+            /* store number of received bytes first */
+            num = rba_size & SIZE_MASK;
             if (num > len) {
                 num = len;
             }
-            rba = get_uint32(rbd + 8);
-            /* printf("rba is 0x%x\n", rba); */
-            address_space_write(&address_space_memory, rba,
-                                MEMTXATTRS_UNSPECIFIED, buf, num);
+            actual_count = num;
+            if (num == len) {
+                actual_count |= I596_EOF; /* set EOF BIT */
+            }
+
+            if (num) {
+                actual_count |= 0x4000; /* set F BIT */
+                set_uint16(actual_count_ptr, actual_count); /* write actual 
count with flags */
+
+                address_space_write(&address_space_memory, rba,
+                                    MEMTXATTRS_UNSPECIFIED, buf, num);
+            }
             rba += num;
             buf += num;
             len -= num;
-            if (len == 0) { /* copy crc */
+            if (len == 0 && I596_CRCINM && !I596_LOOPBACK) { /* copy crc */
                 address_space_write(&address_space_memory, rba - 4,
                                     MEMTXATTRS_UNSPECIFIED, crc_ptr, 4);
             }

-            num |= 0x4000; /* set F BIT */
-            if (len == 0) {
-                num |= I596_EOF; /* set EOF BIT */
+            if (len == 0) { // do not get next rbd
+                break;
             }
-            set_uint16(rbd + 0, num); /* write actual count with flags */
+
+            if (rba_size & I596_EOF) /* last entry */
+                break;
+
+            DBG(printf("Receive: rbd is 0x%08x\n", rbd));
+            rba_size = get_uint16(rbd + 12);
+            rba = get_uint32(rbd + 8);
+            actual_count_ptr = rbd + 0;
+            assert(rba_size != 0);

             /* get next rbd */
             rbd = get_uint32(rbd + 4);
-            /* printf("Next Receive: rbd is %08x\n", rbd); */
-
-            if (buffer_size & I596_EOF) /* last entry */
-                break;
         }

         /* Housekeeping, see pg. 18 */
@@ -671,8 +738,8 @@ ssize_t i82596_receive(NetClientState *nc, const uint8_t 
*buf, size_t sz)
         set_uint16(rfd_p, status);

         if (command & CMD_SUSP) {  /* suspend after command? */
-            s->rx_status = RX_SUSPENDED;
-            s->scb_status |= SCB_STATUS_RNR; /* RU left active state */
+            s->RUS = RX_SUSPENDED;
+            s->scb_status |= SCB_STAT_RNR; /* RU left active state */
             break;
         }
         if (command & CMD_EOL) /* was it last Frame Descriptor? */
@@ -683,24 +750,28 @@ ssize_t i82596_receive(NetClientState *nc, const uint8_t 
*buf, size_t sz)

     assert(len == 0);

-    s->scb_status |= SCB_STATUS_FR; /* set "RU finished receiving frame" bit. 
*/
+    s->scb_status |= SCB_STAT_FR; /* set "RU finished receiving frame" bit. */
     update_scb_status(s);

     /* send IRQ that we received data */
-    qemu_set_irq(s->irq, 1);
-    /* s->send_irq = 1; */
+    if (I596_LOOPBACK) {
+        s->send_irq = 1;
+    } else {
+        qemu_set_irq(s->irq, 1);
+    }

     if (0) {
         DBG(printf("Checking:\n"));
         rfd_p = get_uint32(s->scb + 8); /* get Receive Frame Descriptor */
-        DBG(printf("Next Receive: rfd is %08x\n", rfd_p));
+        DBG(printf("Next Receive: rfd is 0x%08x\n", rfd_p));
         rfd_p = get_uint32(rfd_p + 4); /* get Next Receive Frame Descriptor */
-        DBG(printf("Next Receive: rfd is %08x\n", rfd_p));
+        DBG(printf("Next Receive: rfd is 0x%08x\n", rfd_p));
         /* get first Receive Buffer Descriptor Address */
         rbd = get_uint32(rfd_p + 8);
-        DBG(printf("Next Receive: rbd is %08x\n", rbd));
+        DBG(printf("Next Receive: rbd is 0x%08x\n", rbd));
     }

+    DBG(printf("i82596_receive() end sz = %lu\n", sz));
     return sz;
 }

@@ -711,7 +782,6 @@ const VMStateDescription vmstate_i82596 = {
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
         VMSTATE_UINT16(lnkst, I82596State),
-        VMSTATE_TIMER_PTR(flush_queue_timer, I82596State),
         VMSTATE_END_OF_LIST()
     }
 };
@@ -725,9 +795,5 @@ void i82596_common_init(DeviceState *dev, I82596State *s, 
NetClientInfo *info)
                 dev->id, s);
     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);

-    if (USE_TIMER) {
-        s->flush_queue_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
-                                    i82596_flush_queue_timer, s);
-    }
     s->lnkst = 0x8000; /* initial link state: up */
 }
diff --git a/hw/net/i82596.h b/hw/net/i82596.h
index 1238ac11f8..0b1c0ce2aa 100644
--- a/hw/net/i82596.h
+++ b/hw/net/i82596.h
@@ -10,7 +10,14 @@
 #define PORT_SELFTEST           0x01    /* selftest */
 #define PORT_ALTSCP             0x02    /* alternate SCB address */
 #define PORT_ALTDUMP            0x03    /* Alternate DUMP address */
-#define PORT_CA                 0x10    /* QEMU-internal CA signal */
+#define PORT_CA                 0x04    /* QEMU-internal CA signal */
+#define PORT_BYTEMASK           0x0f    /* all valid bits */
+
+/* modes in which the 82596 can operate */
+#define MODE_82586              0       /* 24 bit address space */
+#define MODE_32BIT_SEGMENTED    1
+#define MODE_LINEAR             2       /* 32 bit address space */
+#define MODE_UNKNOWN            3

 typedef struct I82596State_st I82596State;

@@ -23,22 +30,19 @@ struct I82596State_st {
     QEMUTimer *flush_queue_timer;

     hwaddr scp;         /* pointer to SCP */
-    uint8_t sysbus;
     uint32_t scb;       /* SCB */
     uint16_t scb_status;
-    uint8_t cu_status, rx_status;
+    uint8_t  send_irq;
+    uint8_t  CUS:3;     /* Command Unit status word in SCB */
+    uint8_t  RUS:4;     /* Receive Unit status word in SCB */
     uint16_t lnkst;
-
     uint32_t cmd_p;     /* addr of current command */
-    int ca;
-    int ca_active;
-    int send_irq;

     /* Hash register (multicast mask array, multiple individual addresses). */
     uint8_t mult[8];
     uint8_t config[14]; /* config bytes from CONFIGURE command */

-    uint8_t tx_buffer[0x4000];
+    uint8_t tx_buffer[1540];
 };

 void i82596_h_reset(void *opaque);
diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c
index 52637a562d..dc37591ad8 100644
--- a/hw/net/lasi_i82596.c
+++ b/hw/net/lasi_i82596.c
@@ -42,8 +42,8 @@ static void lasi_82596_mem_write(void *opaque, hwaddr addr,
         d->val_index++;
         if (d->val_index == 0) {
             uint32_t v = d->last_val | (val << 16);
-            v = v & ~0xff;
-            i82596_ioport_writew(&d->state, d->last_val & 0xff, v);
+            v &= ~PORT_BYTEMASK;
+            i82596_ioport_writew(&d->state, d->last_val & PORT_BYTEMASK, v);
         }
         d->last_val = val;
         break;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]