All of lore.kernel.org
 help / color / mirror / Atom feed
* [PULL v2 0/8] Hppa 715 cpu nic patches
@ 2026-11-05 22:31 Helge Deller
  2026-11-05 22:31 ` [PULL v2 1/8] target/hppa: Move emulated power button into CPU I/O region Helge Deller
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Helge Deller @ 2026-11-05 22:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Keith Monahan, Richard Henderson, Helge Deller

From: Helge Deller <deller@gmx.de>

The following changes since commit 99e6fa0053ba1b1454b8c4446f857c430547a702:

  Merge tag 'pull-nbd-2026-11-02' of https://gitlab.com/ebblake/qemu into staging (2026-11-03 10:54:31 -0800)

are available in the Git repository at:

  https://github.com/hdeller/qemu-hppa.git tags/hppa-715-cpu-nic-pull-request

for you to fetch changes up to 58952e4a5a2ddc0e450bef0770959b1147c98f2e:

  i82596: Various send- and transmit fixes for 715 (2026-11-05 23:29:19 +0100)

----------------------------------------------------------------
qemu-hppa-715

A last bunch of fixes to allow HP-UX 9.05 to run on the 715 machine.
This includes a fix to handle an invalid use of the blr instruction, a fix
which puts the power button address outside of the ROM, and various fixes for
the i82596 NIC.

Tests were run on HP-UX 9.05, HP-UX 10.20, NetBSD and Linux.

----------------------------------------------------------------

Helge Deller (8):
  target/hppa: Move emulated power button into CPU I/O region
  target/hppa: Add special blr instruction for HP-UX 9.05
  lasi_i82596: Decode the PORT command from bits 3:0, not 7:0.
  i82596: Implement 82596CA big-endian mode (SYSBUS bit 7)
  i82596: Initialize variables at chip reset
  i82596: Drop crc calculation
  i82596: Fix source address inserting
  i82596: Various send- and transmit fixes for 715

 hw/hppa/machine.c        |  13 +-
 hw/net/i82596.c          | 355 ++++++++++++++++++++++++++-------------
 hw/net/i82596.h          |   5 +
 hw/net/lasi_i82596.c     |   4 +-
 target/hppa/cpu.h        |   2 +
 target/hppa/insns.decode |   1 +
 6 files changed, 261 insertions(+), 119 deletions(-)

-- 
2.54.0



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PULL v2 1/8] target/hppa: Move emulated power button into CPU I/O region
  2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
@ 2026-11-05 22:31 ` Helge Deller
  2026-11-05 22:31 ` [PULL v2 2/8] target/hppa: Add special blr instruction for HP-UX 9.05 Helge Deller
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Helge Deller @ 2026-11-05 22:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Keith Monahan, Richard Henderson, Helge Deller

From: Helge Deller <deller@gmx.de>

HP-UX 9.05 crashes when trying to access the emulated power button, which
currently is located by default at the end of the ROM area. It was hackish to
put the button there, so move the virtual power button into the I/O of the
monarch CPU instead.

Noticed-by: Keith Monahan <keith@techtravels.org>
Signed-off-by: Helge Deller <deller@gmx.de>
---
 hw/hppa/machine.c | 13 +++++++++----
 target/hppa/cpu.h |  2 ++
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c
index f23936fc5b..8a3f673daf 100644
--- a/hw/hppa/machine.c
+++ b/hw/hppa/machine.c
@@ -50,7 +50,6 @@ struct HppaMachineState {
 
 #define MIN_SEABIOS_HPPA_VERSION 26 /* require at least this fw version */
 
-#define HPA_POWER_BUTTON        (FIRMWARE_END - 0x10)
 static hwaddr soft_power_reg;
 
 static void hppa_powerdown_req(Notifier *n, void *opaque)
@@ -123,6 +122,7 @@ static time_t rtc_ref;
 
 static uint64_t io_cpu_read(void *opaque, hwaddr addr, unsigned size)
 {
+    HPPACPU *cpu = opaque;
     uint64_t val = 0;
 
     switch (addr) {
@@ -132,6 +132,8 @@ static uint64_t io_cpu_read(void *opaque, hwaddr addr, unsigned size)
         break;
     case 8:             /* DebugOutputPort */
         return 0xe9;    /* readback */
+    case 16:            /* Power switch button */
+        return cpu->env.power_btn;
     }
     return val;
 }
@@ -139,6 +141,7 @@ static uint64_t io_cpu_read(void *opaque, hwaddr addr, unsigned size)
 static void io_cpu_write(void *opaque, hwaddr addr,
                          uint64_t val, unsigned size)
 {
+    HPPACPU *cpu = opaque;
     unsigned char ch;
     Chardev *debugout;
 
@@ -155,6 +158,9 @@ static void io_cpu_write(void *opaque, hwaddr addr,
             fprintf(stderr, "%c", ch);
         }
         break;
+    case 16:
+        cpu->env.power_btn = val;
+        break;
     }
 }
 
@@ -312,8 +318,6 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine,
         ram_max = FIRMWARE_START;  /* 3.75 GB (32-bit CPU) */
     }
 
-    soft_power_reg = translate(NULL, HPA_POWER_BUTTON);
-
     for (unsigned int i = 0; i < smp_cpus; i++) {
         g_autofree char *name = g_strdup_printf("cpu%u-io-eir", i);
         g_autofree char *cflush_name = NULL;
@@ -348,10 +352,11 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine,
     /* RTC and DebugOutputPort on CPU #0 */
     cpu_region = g_new(MemoryRegion, 1);
     memory_region_init_io(cpu_region, OBJECT(cpu[0]), &hppa_io_helper_ops,
-                          cpu[0], "cpu0-io-rtc", 2 * sizeof(uint64_t));
+                          cpu[0], "cpu0-io-rtc", 3 * sizeof(uint64_t));
     memory_region_add_subregion(addr_space,
                                 translate(NULL, cpu[0]->env.cpu_hpa + 16),
                                 cpu_region);
+    soft_power_reg = translate(NULL, cpu[0]->env.cpu_hpa + 16 + 16);
 
     /* Main memory region. */
     if (machine->ram_size > ram_max) {
diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h
index 7b708a564b..22f8a1727d 100644
--- a/target/hppa/cpu.h
+++ b/target/hppa/cpu.h
@@ -269,6 +269,8 @@ typedef struct CPUArchState {
 
     HPPATLBEntry tlb[HPPA_TLB_ENTRIES];
 
+    target_ulong power_btn;    /* virtual power button (0 = reboot) */
+
     /* Fields up to this point are cleared by a CPU reset */
     struct {} end_reset_fields;
 
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PULL v2 2/8] target/hppa: Add special blr instruction for HP-UX 9.05
  2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
  2026-11-05 22:31 ` [PULL v2 1/8] target/hppa: Move emulated power button into CPU I/O region Helge Deller
@ 2026-11-05 22:31 ` Helge Deller
  2026-11-05 22:31 ` [PULL v2 3/8] lasi_i82596: Decode the PORT command from bits 3:0, not 7:0 Helge Deller
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Helge Deller @ 2026-11-05 22:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Keith Monahan, Richard Henderson, Helge Deller

From: Helge Deller <deller@gmx.de>

The HP-UX 9.05 installer's update executable use a branch instruction with
reserved bits set.  Real hardware ignores those bits, but QEMU treated the
instruction as illegal and killed the program.  Add an entry to handle this
specific blr instruction (code 0xe81a4018) to avoid this crash.

Noticed-by: Keith Monahan <keith@techtravels.org>
Signed-off-by: Helge Deller <deller@gmx.de>
---
 target/hppa/insns.decode | 1 +
 1 file changed, 1 insertion(+)

diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode
index f700fe472e..135f563c88 100644
--- a/target/hppa/insns.decode
+++ b/target/hppa/insns.decode
@@ -451,6 +451,7 @@ bl              111010 ..... ..... 101 ........... n:1 .        &BL l=2 \
                 disp=%assemble_22
 b_gate          111010 ..... ..... 001 ........... .   .        @bl
 blr             111010 l:5   x:5   010 00000000000 n:1 0
+blr             111010 l:5   x:5   010 00000000110 n:1 0    # invalid BLR used in HP-UX 9.05 installer (0xe81a4018)
 nopbts          111010 00000 00000 010 0---------1   0 1    # clrbts/popbts
 nopbts          111010 00000 ----- 010 00000000000   0 1    # pushbts/pushnom
 bv              111010 b:5   x:5   110 00000000000 n:1 0
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PULL v2 3/8] lasi_i82596: Decode the PORT command from bits 3:0, not 7:0.
  2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
  2026-11-05 22:31 ` [PULL v2 1/8] target/hppa: Move emulated power button into CPU I/O region Helge Deller
  2026-11-05 22:31 ` [PULL v2 2/8] target/hppa: Add special blr instruction for HP-UX 9.05 Helge Deller
@ 2026-11-05 22:31 ` Helge Deller
  2026-11-05 22:31 ` [PULL v2 4/8] i82596: Implement 82596CA big-endian mode (SYSBUS bit 7) Helge Deller
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Helge Deller @ 2026-11-05 22:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Keith Monahan, Richard Henderson, Helge Deller

From: Helge Deller <deller@gmx.de>

It's sufficient to handle bits 3:0, decoding more breaks HP-UX 9.07.

Noticed-by: Keith Monahan <keith@techtravels.org>
Signed-off-by: Helge Deller <deller@gmx.de>
---
 hw/net/lasi_i82596.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c
index 423a8676a0..5df1a59617 100644
--- a/hw/net/lasi_i82596.c
+++ b/hw/net/lasi_i82596.c
@@ -43,8 +43,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 = v & ~0xf;
+            i82596_ioport_writew(&d->state, d->last_val & 0xf, v);
         }
         d->last_val = val;
         break;
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PULL v2 4/8] i82596: Implement 82596CA big-endian mode (SYSBUS bit 7)
  2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
                   ` (2 preceding siblings ...)
  2026-11-05 22:31 ` [PULL v2 3/8] lasi_i82596: Decode the PORT command from bits 3:0, not 7:0 Helge Deller
@ 2026-11-05 22:31 ` Helge Deller
  2026-11-05 22:31 ` [PULL v2 5/8] i82596: Initialize variables at chip reset Helge Deller
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Helge Deller @ 2026-11-05 22:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Keith Monahan, Richard Henderson, Helge Deller

From: Helge Deller <deller@gmx.de>

NetBSD runs the 82596CA chip in big-endian mode, which means the SYSBUS bit #7
is set, and pointers need to be read as big-endian 32-bit pointer values.
Increment the error counters at offsets 12-32 of the SCB live in memory.

Noticed-by: Keith Monahan <keith@techtravels.org>
Signed-off-by: Helge Deller <deller@gmx.de>
---
 hw/net/i82596.c | 118 +++++++++++++++++++++++++++++-------------------
 hw/net/i82596.h |   3 ++
 2 files changed, 74 insertions(+), 47 deletions(-)

diff --git a/hw/net/i82596.c b/hw/net/i82596.c
index 37ce20efb1..a518aa7696 100644
--- a/hw/net/i82596.c
+++ b/hw/net/i82596.c
@@ -221,39 +221,78 @@ static void set_uint32(uint32_t addr, uint32_t val)
     set_uint16(addr + 2, val >> 16);
 }
 
+/*
+ * Address fields of the SCB, command blocks and RX/TX descriptors.  In
+ * big-endian mode (SYSBUS BE, 82596CA) the chip reads them as 32-bit
+ * big-endian words; otherwise as two 16-bit halves, low half first.  The
+ * ISCP and SCB addresses and the SCB counters are always two halves.
+ */
+#define I596_SYSBUS_BE  0x80
+
+static uint32_t get_addr(I82596State *s, uint32_t addr)
+{
+    if (s->sysbus & I596_SYSBUS_BE) {
+        return ldl_be_phys(&address_space_memory, addr);
+    }
+    return get_uint32(addr);
+}
+
+static void set_addr(I82596State *s, uint32_t addr, uint32_t val)
+{
+    if (s->sysbus & I596_SYSBUS_BE) {
+        stl_be_phys(&address_space_memory, addr, val);
+    } else {
+        set_uint32(addr, val);
+    }
+}
+
+/* SCB error counters (linear mode), counted in memory like the chip does */
+#define SCB_CRC_ERRS        12
+#define SCB_ALIGN_ERRS      16
+#define SCB_RESOURCE_ERRS   20
+#define SCB_OVERRUN_ERRS    24
+#define SCB_RCVCDT_ERRS     28
+#define SCB_SHORT_ERRS      32
+
+static void i82596_count_error(I82596State *s, int offset)
+{
+    if (s->scb) {
+        set_uint32(s->scb + offset, get_uint32(s->scb + offset) + 1);
+    }
+}
+
 /* Centralized error detection and update mechanism */
 static void i82596_record_error(I82596State *s, uint16_t error_type, bool is_tx)
 {
     if (is_tx) {
         if (error_type & TX_ABORTED_ERRORS) {
             s->tx_aborted_errors++;
-            set_uint32(s->scb + 28, s->tx_aborted_errors);
         }
     } else {
         if (error_type & RX_CRC_ERRORS) {
             s->crc_err++;
-            set_uint32(s->scb + 16, s->crc_err);
+            i82596_count_error(s, SCB_CRC_ERRS);
         }
 
         if (error_type & (RX_LENGTH_ERRORS | RX_LENGTH_ERRORS_ALT |
                           RX_FRAME_ERRORS)) {
             s->align_err++;
-            set_uint32(s->scb + 18, s->align_err);
+            i82596_count_error(s, SCB_ALIGN_ERRS);
         }
 
         if (error_type & RFD_STATUS_NOBUFS) {
             s->resource_err++;
-            set_uint32(s->scb + 20, s->resource_err);
+            i82596_count_error(s, SCB_RESOURCE_ERRS);
         }
 
         if (error_type & (RX_OVER_ERRORS | RX_FIFO_ERRORS)) {
             s->over_err++;
-            set_uint32(s->scb + 22, s->over_err);
+            i82596_count_error(s, SCB_OVERRUN_ERRS);
         }
 
         if (error_type & RFD_STATUS_TRUNC) {
             s->short_fr_error++;
-            set_uint32(s->scb + 26, s->short_fr_error);
+            i82596_count_error(s, SCB_SHORT_ERRS);
         }
     }
 }
@@ -439,8 +478,8 @@ static void i82596_tx_tfd_read(I82596State *s, hwaddr addr,
 {
     desc->status_bits = get_uint16(addr + 0);
     desc->command = get_uint16(addr + 2);
-    desc->link_addr = get_uint32(addr + 4);
-    desc->tbd_addr = get_uint32(addr + 8);
+    desc->link_addr = get_addr(s, addr + 4);
+    desc->tbd_addr = get_addr(s, addr + 8);
     desc->tcb_count = get_uint16(addr + 12);
     address_space_read(&address_space_memory, addr + 14,
                       MEMTXATTRS_UNSPECIFIED, desc->dest_addr, 6);
@@ -452,8 +491,8 @@ static void i82596_tx_tfd_write(I82596State *s, hwaddr addr,
 {
     set_uint16(addr + 0, desc->status_bits);
     set_uint16(addr + 2, desc->command);
-    set_uint32(addr + 4, desc->link_addr);
-    set_uint32(addr + 8, desc->tbd_addr);
+    set_addr(s, addr + 4, desc->link_addr);
+    set_addr(s, addr + 8, desc->tbd_addr);
     set_uint16(addr + 12, desc->tcb_count);
     address_space_write(&address_space_memory, addr + 14,
                        MEMTXATTRS_UNSPECIFIED, desc->dest_addr, 6);
@@ -464,8 +503,8 @@ static void i82596_tbd_read(I82596State *s, hwaddr addr,
                             struct i82596_tx_buffer_desc *tbd)
 {
     tbd->size = get_uint16(addr + 0);
-    tbd->link = get_uint32(addr + 4);
-    tbd->buffer = get_uint32(addr + 8);
+    tbd->link = get_addr(s, addr + 4);
+    tbd->buffer = get_addr(s, addr + 8);
 }
 
 static void i82596_rx_rfd_read(I82596State *s, hwaddr addr,
@@ -473,8 +512,8 @@ static void i82596_rx_rfd_read(I82596State *s, hwaddr addr,
 {
     desc->status_bits = get_uint16(addr + 0x0);
     desc->command = get_uint16(addr + 0x2);
-    desc->link = get_uint32(addr + 0x4);
-    desc->rbd_addr = get_uint32(addr + 0x8);
+    desc->link = get_addr(s, addr + 0x4);
+    desc->rbd_addr = get_addr(s, addr + 0x8);
     desc->actual_count = get_uint16(addr + 0xC);
     desc->size = get_uint16(addr + 0xE);
 
@@ -494,8 +533,8 @@ static void i82596_rx_desc_write(I82596State *s, hwaddr addr,
 
     if (write_full) {
         set_uint16(addr + 0x2, desc->command);
-        set_uint32(addr + 0x4, desc->link);
-        set_uint32(addr + 0x8, desc->rbd_addr);
+        set_addr(s, addr + 0x4, desc->link);
+        set_addr(s, addr + 0x8, desc->rbd_addr);
         set_uint16(addr + 0xE, desc->size);
 
         address_space_write(&address_space_memory, addr + 0x10,
@@ -510,8 +549,8 @@ static void i82596_rbd_read(I82596State *s, hwaddr addr,
                             struct i82596_rx_buffer_desc *rbd)
 {
     rbd->actual_count = get_uint16(addr + 0x0);
-    rbd->next_rbd_addr = get_uint32(addr + 0x4);
-    rbd->buffer_addr = get_uint32(addr + 0x8);
+    rbd->next_rbd_addr = get_addr(s, addr + 0x4);
+    rbd->buffer_addr = get_addr(s, addr + 0x8);
     rbd->size = get_uint16(addr + 0xC);
 }
 
@@ -519,8 +558,8 @@ static void i82596_rbd_write(I82596State *s, hwaddr addr,
                              struct i82596_rx_buffer_desc *rbd)
 {
     set_uint16(addr + 0x0, rbd->actual_count);
-    set_uint32(addr + 0x4, rbd->next_rbd_addr);
-    set_uint32(addr + 0x8, rbd->buffer_addr);
+    set_addr(s, addr + 0x4, rbd->next_rbd_addr);
+    set_addr(s, addr + 0x8, rbd->buffer_addr);
     set_uint16(addr + 0xC, rbd->size);
 }
 
@@ -1008,7 +1047,7 @@ static ssize_t i82596_receive_packet(I82596State *s, const uint8_t *buf,
     }
 
 
-    rfd_addr = get_uint32(s->scb + 8);
+    rfd_addr = s->rfd_p;
 
     if (rfd_addr == 0 || rfd_addr == I596_NULL) {
         i82596_update_rx_state(s, RX_NO_RESOURCES);
@@ -1383,23 +1422,19 @@ static void i82596_update_statistics(I82596State *s, bool is_tx,
             s->tx_collisions += collision_count;
             s->collision_events++;
             s->total_collisions += collision_count;
-            set_uint32(s->scb + 32, s->tx_collisions);
         }
         if (error_flags) {
             i82596_record_error(s, error_flags, true);
         }
         if (!(error_flags & (TX_ABORTED_ERRORS | TX_CARRIER_ERRORS))) {
             s->tx_good_frames++;
-            set_uint32(s->scb + 36, s->tx_good_frames);
         }
     } else {
         s->total_frames++;
-        set_uint32(s->scb + 40, s->total_frames);
         if (error_flags) {
             i82596_record_error(s, error_flags, false);
         } else {
             s->total_good_frames++;
-            set_uint32(s->scb + 44, s->total_good_frames);
         }
     }
 }
@@ -1557,7 +1592,7 @@ static void i82596_command_dump(I82596State *s, uint32_t cmd_addr)
     uint16_t cmd = get_uint16(cmd_addr + 2);
     uint16_t status;
 
-    dump_addr = get_uint32(cmd_addr + 8);
+    dump_addr = get_addr(s, cmd_addr + 8);
 
     i82596_init_dump_area(s, dump_buffer);
     address_space_write(&address_space_memory, dump_addr,
@@ -1648,19 +1683,8 @@ static void i82596_update_cu_status(I82596State *s, uint16_t cmd_status,
 static void update_scb_status(I82596State *s)
 {
     s->scb_status = (s->scb_status & 0xf000)
-        | (s->cu_status << 8) | (s->rx_status << 4) | (s->lnkst >> 8);
+        | (s->cu_status << 8) | (s->rx_status << 4);
     set_uint16(s->scb, s->scb_status);
-
-    set_uint32(s->scb + 28, s->tx_aborted_errors);
-    set_uint32(s->scb + 32, s->tx_collisions);
-    set_uint32(s->scb + 36, s->tx_good_frames);
-
-    set_uint32(s->scb + 16, s->crc_err);
-    set_uint32(s->scb + 18, s->align_err);
-    set_uint32(s->scb + 20, s->resource_err);
-    set_uint32(s->scb + 22, s->over_err);
-    set_uint32(s->scb + 24, s->rcvdt_err);
-    set_uint32(s->scb + 26, s->short_fr_error);
 }
 
 static void command_loop(I82596State *s)
@@ -1669,11 +1693,12 @@ static void command_loop(I82596State *s)
            s->cmd_p != 0) {
         uint16_t status = get_uint16(s->cmd_p);
         if (status & (STAT_C | STAT_B)) {
-            uint32_t next = get_uint32(s->cmd_p + 4);
+            uint32_t next = get_addr(s, s->cmd_p + 4);
             if (next == 0 || next == s->cmd_p) {
                 s->cmd_p = I596_NULL;
                 s->cu_status = CU_IDLE;
                 s->scb_status |= SCB_STATUS_CNA;
+                s->send_irq = 1;
                 break;
             }
             s->cmd_p = i82596_translate_address(s, next, false);
@@ -1681,7 +1706,7 @@ static void command_loop(I82596State *s)
         }
         set_uint16(s->cmd_p, STAT_B);
         uint16_t cmd = get_uint16(s->cmd_p + 2);
-        uint32_t next_addr = get_uint32(s->cmd_p + 4);
+        uint32_t next_addr = get_addr(s, s->cmd_p + 4);
         next_addr = (next_addr == 0) ? I596_NULL :
                     i82596_translate_address(s, next_addr, false);
         switch (cmd & CMD_MASK) {
@@ -1784,7 +1809,7 @@ static void examine_scb(I82596State *s)
         break;
 
     case SCB_CUC_START: {
-        uint32_t cmd_ptr = get_uint32(s->scb + 4);
+        uint32_t cmd_ptr = get_addr(s, s->scb + 4);
         s->cmd_p = i82596_translate_address(s, cmd_ptr, false);
         s->cu_status = CU_ACTIVE;
         break;
@@ -1823,7 +1848,7 @@ static void examine_scb(I82596State *s)
         break;
 
     case SCB_RUC_START: {
-        uint32_t rfd_log = get_uint32(s->scb + 8);
+        uint32_t rfd_log = get_addr(s, s->scb + 8);
         hwaddr rfd = i82596_translate_address(s, rfd_log, false);
 
         if (rfd == 0 || rfd == I596_NULL) {
@@ -1862,12 +1887,11 @@ static void examine_scb(I82596State *s)
         if (found) {
             s->current_rx_desc = first_usable_rfd;
             s->last_good_rfa = first_usable_rfd_log;
+            s->rbd_p = i82596_translate_address(s, test_rfd.rbd_addr, false);
+            s->rx_take_el = false;
+            s->rfd_p = first_usable_rfd;
             i82596_update_rx_state(s, RX_READY);
 
-            if (first_usable_rfd != rfd) {
-                set_uint32(s->scb + 8, first_usable_rfd_log);
-            }
-
             if (s->queue_count > 0) {
                 trace_i82596_flush_queue(s->queue_count);
                 i82596_flush_packet_queue(s);
@@ -1909,7 +1933,7 @@ static void examine_scb(I82596State *s)
     }
     if (s->cu_status == CU_ACTIVE) {
         if (s->cmd_p == I596_NULL) {
-            s->cmd_p = get_uint32(s->scb + 4);
+            s->cmd_p = get_addr(s, s->scb + 4);
         }
         update_scb_status(s);
         command_loop(s);
diff --git a/hw/net/i82596.h b/hw/net/i82596.h
index 67b18f957a..ec0b6a75ba 100644
--- a/hw/net/i82596.h
+++ b/hw/net/i82596.h
@@ -75,6 +75,9 @@ struct I82596State_st {
     hwaddr current_tx_desc;
     hwaddr current_rx_desc;
     uint32_t last_good_rfa;
+    uint32_t rbd_p;         /* next free RBD, loaded from the first RFD at RU start */
+    bool rx_take_el;        /* hold timed out: fill the EL RBD like the chip does */
+    uint32_t rfd_p;         /* next RFD, loaded from the SCB at RU start */
     uint8_t packet_queue[PACKET_QUEUE_SIZE][PKT_BUF_SZ];
     size_t packet_queue_len[PACKET_QUEUE_SIZE];
     int queue_head;
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PULL v2 5/8] i82596: Initialize variables at chip reset
  2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
                   ` (3 preceding siblings ...)
  2026-11-05 22:31 ` [PULL v2 4/8] i82596: Implement 82596CA big-endian mode (SYSBUS bit 7) Helge Deller
@ 2026-11-05 22:31 ` Helge Deller
  2026-11-05 22:31 ` [PULL v2 6/8] i82596: Drop crc calculation Helge Deller
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Helge Deller @ 2026-11-05 22:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Keith Monahan, Richard Henderson, Helge Deller

From: Helge Deller <deller@gmx.de>

Do proper initialization of various pointers and register values at chip
reset and add a timer to trigger delivery retry when the RX ring is
full.

Noticed-by: Keith Monahan <keith@techtravels.org>
Signed-off-by: Helge Deller <deller@gmx.de>
---
 hw/net/i82596.c | 7 +++++++
 hw/net/i82596.h | 2 ++
 2 files changed, 9 insertions(+)

diff --git a/hw/net/i82596.c b/hw/net/i82596.c
index a518aa7696..35e0b58330 100644
--- a/hw/net/i82596.c
+++ b/hw/net/i82596.c
@@ -359,6 +359,13 @@ static void i82596_s_reset(I82596State *s)
     s->last_tx_len = 0;
 
     s->last_good_rfa = 0;
+    s->rbd_p = I596_NULL;
+    s->rx_take_el = false;
+    s->rfd_p = I596_NULL;
+    s->rx_hold_start = 0;
+    if (s->rx_retry_timer) {
+        timer_del(s->rx_retry_timer);
+    }
     s->queue_head = 0;
     s->queue_tail = 0;
     s->queue_count = 0;
diff --git a/hw/net/i82596.h b/hw/net/i82596.h
index ec0b6a75ba..15370837cd 100644
--- a/hw/net/i82596.h
+++ b/hw/net/i82596.h
@@ -25,6 +25,7 @@ struct I82596State_st {
     NICState *nic;
     NICConf conf;
     QEMUTimer *flush_queue_timer;
+    QEMUTimer *rx_retry_timer;  /* retries delivery while the RX ring is full */
     uint8_t mode;
 
     QEMUTimer *throttle_timer;
@@ -78,6 +79,7 @@ struct I82596State_st {
     uint32_t rbd_p;         /* next free RBD, loaded from the first RFD at RU start */
     bool rx_take_el;        /* hold timed out: fill the EL RBD like the chip does */
     uint32_t rfd_p;         /* next RFD, loaded from the SCB at RU start */
+    int64_t rx_hold_start;  /* frames held since (virtual ns), 0 if not */
     uint8_t packet_queue[PACKET_QUEUE_SIZE][PKT_BUF_SZ];
     size_t packet_queue_len[PACKET_QUEUE_SIZE];
     int queue_head;
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PULL v2 6/8] i82596: Drop crc calculation
  2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
                   ` (4 preceding siblings ...)
  2026-11-05 22:31 ` [PULL v2 5/8] i82596: Initialize variables at chip reset Helge Deller
@ 2026-11-05 22:31 ` Helge Deller
  2026-11-05 22:31 ` [PULL v2 7/8] i82596: Fix source address inserting Helge Deller
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Helge Deller @ 2026-11-05 22:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Keith Monahan, Richard Henderson, Helge Deller

From: Helge Deller <deller@gmx.de>

No need to calculate and add additional CRCs here.

Noticed-by: Keith Monahan <keith@techtravels.org>
Signed-off-by: Helge Deller <deller@gmx.de>
---
 hw/net/i82596.c | 32 +++-----------------------------
 1 file changed, 3 insertions(+), 29 deletions(-)

diff --git a/hw/net/i82596.c b/hw/net/i82596.c
index 35e0b58330..6bcae73815 100644
--- a/hw/net/i82596.c
+++ b/hw/net/i82596.c
@@ -180,7 +180,6 @@ static void examine_scb(I82596State *s);
 static bool i82596_check_medium_status(I82596State *s);
 static int i82596_csma_backoff(I82596State *s, int retry_count);
 static uint16_t i82596_calculate_crc16(const uint8_t *data, size_t len);
-static size_t i82596_append_crc(I82596State *s, uint8_t *buffer, size_t len);
 static void i82596_bus_throttle_timer(void *opaque);
 static void i82596_flush_queue_timer(void *opaque);
 static int i82596_flush_packet_queue(I82596State *s);
@@ -624,7 +623,7 @@ static int i82596_tx_copy_buffers(I82596State *s, hwaddr tfd_addr,
     return total_len;
 }
 
-static int i82596_tx_process_frame(I82596State *s, bool insert_crc)
+static int i82596_tx_process_frame(I82596State *s)
 {
     uint32_t total_len = s->tx_frame_len;
 
@@ -642,10 +641,6 @@ static int i82596_tx_process_frame(I82596State *s, bool insert_crc)
         total_len = I596_MIN_FRAME_LEN;
     }
 
-    if (insert_crc) {
-        total_len = i82596_append_crc(s, s->tx_buffer, total_len);
-    }
-
     s->tx_frame_len = total_len;
     return total_len;
 }
@@ -710,15 +705,12 @@ static void i82596_transmit(I82596State *s, uint32_t addr)
     int collision_count = 0;
     int frame_len;
     bool success = true;
-    bool insert_crc;
 
     i82596_tx_tfd_read(s, tfd_addr, &tfd);
     trace_i82596_tx_tfd(tfd_addr, tfd.status_bits, tfd.command,
                         tfd.link_addr, tfd.tbd_addr);
 
     s->current_tx_desc = tfd_addr;
-    insert_crc = (I596_NOCRC_INS == 0) && ((tfd.command & 0x10) == 0) &&
-                 !I596_LOOPBACK;
     collision_count = i82596_tx_csma_cd(s, &tx_status);
     if (collision_count < 0) {
         success = false;
@@ -730,7 +722,8 @@ static void i82596_transmit(I82596State *s, uint32_t addr)
         success = false;
         goto tx_complete;
     }
-    frame_len = i82596_tx_process_frame(s, insert_crc);
+    /* QEMU net clients exchange frames without the FCS */
+    frame_len = i82596_tx_process_frame(s);
     if (frame_len <= 0) {
         tx_status |= TX_ABORTED_ERRORS;
         success = false;
@@ -1401,25 +1394,6 @@ static uint16_t i82596_calculate_crc16(const uint8_t *data, size_t len)
     return crc;
 }
 
-static size_t i82596_append_crc(I82596State *s, uint8_t *buffer, size_t len)
-{
-    if (len + 4 > PKT_BUF_SZ) {
-        return len;
-    }
-
-    if (I596_CRC16_32) {
-        uint32_t crc = crc32(~0, buffer, len);
-        crc = cpu_to_be32(crc);
-        memcpy(&buffer[len], &crc, sizeof(crc));
-        return len + sizeof(crc);
-    } else {
-        uint16_t crc = i82596_calculate_crc16(buffer, len);
-        crc = cpu_to_be16(crc);
-        memcpy(&buffer[len], &crc, sizeof(crc));
-        return len + sizeof(crc);
-    }
-}
-
 static void i82596_update_statistics(I82596State *s, bool is_tx,
                                       uint16_t error_flags,
                                       uint16_t collision_count)
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PULL v2 7/8] i82596: Fix source address inserting
  2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
                   ` (5 preceding siblings ...)
  2026-11-05 22:31 ` [PULL v2 6/8] i82596: Drop crc calculation Helge Deller
@ 2026-11-05 22:31 ` Helge Deller
  2026-11-05 22:31 ` [PULL v2 8/8] i82596: Various send- and transmit fixes for 715 Helge Deller
  2026-11-06 16:46 ` [PULL v2 0/8] Hppa 715 cpu nic patches Richard Henderson
  8 siblings, 0 replies; 10+ messages in thread
From: Helge Deller @ 2026-11-05 22:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Keith Monahan, Richard Henderson, Helge Deller

From: Helge Deller <deller@gmx.de>

Insert the source address after the destination instead of overwriting
bytes 6-11.

Noticed-by: Keith Monahan <keith@techtravels.org>
Signed-off-by: Helge Deller <deller@gmx.de>
---
 hw/net/i82596.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/hw/net/i82596.c b/hw/net/i82596.c
index 6bcae73815..19e1bf47db 100644
--- a/hw/net/i82596.c
+++ b/hw/net/i82596.c
@@ -631,8 +631,14 @@ static int i82596_tx_process_frame(I82596State *s)
         return 0;
     }
 
-    if (I596_NO_SRC_ADD_IN == 0 && total_len >= ETH_ALEN * 2) {
+    if (I596_NO_SRC_ADD_IN == 0 && total_len >= ETH_ALEN) {
+        if (total_len + ETH_ALEN > sizeof(s->tx_buffer)) {
+            return 0;
+        }
+        memmove(&s->tx_buffer[ETH_ALEN * 2], &s->tx_buffer[ETH_ALEN],
+                total_len - ETH_ALEN);
         memcpy(&s->tx_buffer[ETH_ALEN], s->conf.macaddr.a, ETH_ALEN);
+        total_len += ETH_ALEN;
     }
 
     if (I596_PADDING && total_len < I596_MIN_FRAME_LEN) {
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PULL v2 8/8] i82596: Various send- and transmit fixes for 715
  2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
                   ` (6 preceding siblings ...)
  2026-11-05 22:31 ` [PULL v2 7/8] i82596: Fix source address inserting Helge Deller
@ 2026-11-05 22:31 ` Helge Deller
  2026-11-06 16:46 ` [PULL v2 0/8] Hppa 715 cpu nic patches Richard Henderson
  8 siblings, 0 replies; 10+ messages in thread
From: Helge Deller @ 2026-11-05 22:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Keith Monahan, Richard Henderson, Helge Deller

From: Helge Deller <deller@gmx.de>

When transmitting, send the TCB data that precedes the TBDs in flexible mode,
and interrupt when the CU leaves the active state.  When receiving, keep the
RBD and RFD pointers in the chip (loaded at RU start; the SCB RFA field is
never written back), report only the RFD's own bytes in its actual count and
report every completed frame.  In the SCB, do not update the RU status with the
link state, as it's not part of it.
This fixes networking with HP-UX 9.05, HP-UX 10.20, Debian and NetBSD 9.4/10.1
on the 715 machine.

Noticed-by: Keith Monahan <keith@techtravels.org>
Signed-off-by: Helge Deller <deller@gmx.de>
---
 hw/net/i82596.c | 190 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 154 insertions(+), 36 deletions(-)

diff --git a/hw/net/i82596.c b/hw/net/i82596.c
index 19e1bf47db..cd7bd0f7fe 100644
--- a/hw/net/i82596.c
+++ b/hw/net/i82596.c
@@ -589,6 +589,17 @@ static int i82596_tx_copy_buffers(I82596State *s, hwaddr tfd_addr,
         total_len = frame_len;
 
     } else {
+        uint16_t tcb_len = desc->tcb_count & SIZE_MASK;
+
+        if (tcb_len > sizeof(s->tx_buffer)) {
+            return -1;
+        }
+        if (tcb_len) {
+            address_space_read(&address_space_memory, tfd_addr + 16,
+                              MEMTXATTRS_UNSPECIFIED, s->tx_buffer, tcb_len);
+            total_len = tcb_len;
+        }
+
         tbd_addr = desc->tbd_addr;
         while (tbd_addr != I596_NULL && tbd_addr != 0) {
             uint16_t buf_size;
@@ -756,6 +767,91 @@ tx_complete:
     }
 }
 
+/*
+ * The RBD the RU fills next, or I596_NULL if none is free.  An RBD with EL
+ * set is the driver's last free buffer: leave it until the driver hands
+ * another one back, which moves EL on.  The chip would fill it and stop
+ * with no resources, which at QEMU speed keeps drivers in their overrun
+ * recovery (NetBSD iee marks every RBD EL there).
+ */
+static uint32_t i82596_next_rbd(I82596State *s)
+{
+    uint32_t rbd = s->rbd_p;
+
+    if (rbd == I596_NULL || rbd == 0) {
+        return I596_NULL;
+    }
+    if (!s->rx_take_el && (get_uint16(rbd + 0xC) & CMD_EOL)) {
+        return I596_NULL;
+    }
+    return rbd;
+}
+
+/* The next RFD (and, unless the RFD holds a whole frame, an RBD) is free. */
+static bool i82596_rx_ring_free(I82596State *s)
+{
+    uint32_t rbd;
+
+    if (s->rfd_p == I596_NULL || s->rfd_p == 0) {
+        return false;
+    }
+    if (get_uint16(s->rfd_p) & (STAT_C | STAT_B)) {
+        return false;           /* the driver has not handed it back yet */
+    }
+    if (!(get_uint16(s->rfd_p + 2) & CMD_FLEX) ||
+        (get_uint16(s->rfd_p + 0xE) & SIZE_MASK) >= PKT_BUF_SZ) {
+        return true;
+    }
+    rbd = i82596_next_rbd(s);
+    return rbd != I596_NULL && rbd != 0;
+}
+
+static bool i82596_rx_buffers_free(I82596State *s)
+{
+    if (i82596_rx_ring_free(s)) {
+        s->rx_hold_start = 0;
+        return true;
+    }
+    return false;
+}
+
+static void i82596_update_rx_state(I82596State *s, int new_state);
+
+/* Hold incoming frames while the ring is full; retry every 500 us. */
+static void i82596_rx_hold(I82596State *s)
+{
+    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+    if (!s->rx_hold_start) {
+        s->rx_hold_start = now;
+    }
+    timer_mod(s->rx_retry_timer, now + 500 * SCALE_US);
+}
+
+static void i82596_rx_retry(void *opaque)
+{
+    I82596State *s = opaque;
+
+    /*
+     * The driver has not handed a buffer back within 20 ms: behave like the
+     * chip.  Fill the last (EL) RBD if that is all that is left, otherwise
+     * report RU out of resources so the driver can rebuild its ring.
+     */
+    if (s->rx_hold_start && s->rx_status == RX_READY &&
+        !i82596_rx_buffers_free(s) &&
+        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->rx_hold_start >
+            20 * SCALE_MS) {
+        s->rx_hold_start = 0;
+        s->rx_take_el = true;
+        if (!i82596_rx_buffers_free(s)) {
+            s->rx_take_el = false;
+            i82596_update_rx_state(s, RX_NO_RESOURCES);
+            i82596_update_scb_irq(s, true);
+        }
+    }
+    qemu_flush_queued_packets(qemu_get_queue(s->nic));
+}
+
 bool i82596_can_receive(NetClientState *nc)
 {
     I82596State *s = qemu_get_nic_opaque(nc);
@@ -769,6 +865,15 @@ bool i82596_can_receive(NetClientState *nc)
         return true;
     }
 
+    /*
+     * A real 82596 cannot be overrun this fast at 10 Mbit/s; hold frames in
+     * the net queue while the driver catches up instead of dropping them.
+     */
+    if (s->rx_status == RX_READY && !i82596_rx_buffers_free(s)) {
+        i82596_rx_hold(s);
+        return false;
+    }
+
     if (!s->throttle_state && !I596_FULL_DUPLEX) {
         return (s->queue_count < PACKET_QUEUE_SIZE);
     }
@@ -1038,6 +1143,8 @@ static ssize_t i82596_receive_packet(I82596State *s, const uint8_t *buf,
     size_t bytes_copied = 0;
     const uint8_t *packet_data = buf;
     bool out_of_resources = false;
+    bool rbds_exhausted = false;
+    size_t rfd_data_len = 0;
     size_t crc_size = i82596_get_crc_size(s);
 
     trace_i82596_receive_packet(buf, size);
@@ -1127,16 +1234,18 @@ static ssize_t i82596_receive_packet(I82596State *s, const uint8_t *buf,
                                     packet_data + bytes_copied,
                                     rfd_frame_size);
                 bytes_copied += rfd_frame_size;
+                rfd_data_len += rfd_frame_size;
             }
 
             if (bytes_copied < payload_size) {
                 size_t remaining = payload_size - bytes_copied;
-                rbd_addr = i82596_translate_address(s, rfd.rbd_addr, false);
+                rbd_addr = i82596_next_rbd(s);
 
                 if (rbd_addr == I596_NULL || rbd_addr == 0) {
                     rx_status |= RFD_STATUS_TRUNC | RFD_STATUS_NOBUFS;
                     i82596_record_error(s, RFD_STATUS_NOBUFS, false);
                     packet_completed = true;
+                    rbds_exhausted = true;
                     break;
                 } else {
                     hwaddr remaining_rbd = I596_NULL;
@@ -1148,22 +1257,18 @@ static ssize_t i82596_receive_packet(I82596State *s, const uint8_t *buf,
                                             &remaining_rbd);
                     bytes_copied += rbd_bytes;
 
-                    uint32_t next_rfd = i82596_translate_address(s, rfd.link,
-                                                                  false);
-                    if (next_rfd != I596_NULL && next_rfd != 0) {
-                        if (remaining_rbd != I596_NULL && remaining_rbd != 0) {
-                            trace_i82596_rx_rfd_update(next_rfd, remaining_rbd);
-                            set_uint32(next_rfd + 8, remaining_rbd);
-                        } else {
-                            set_uint32(next_rfd + 8, I596_NULL);
-                        }
-                    }
+                    /* the RFD points at the first RBD holding its data */
+                    trace_i82596_rx_rfd_update(rfd_addr, rbd_addr);
+                    set_addr(s, rfd_addr + 8, rbd_addr);
+                    s->rbd_p = remaining_rbd;
+                    s->rx_take_el = false;
 
                     if (out_of_resources) {
                         trace_i82596_rx_out_of_rbds();
                         i82596_record_error(s, RFD_STATUS_NOBUFS, false);
                         rx_status |= RFD_STATUS_TRUNC | RFD_STATUS_NOBUFS;
                         packet_completed = true;
+                        rbds_exhausted = true;
                         break;
                     }
 
@@ -1211,30 +1316,31 @@ rx_complete:
     }
 
     rfd.status_bits = rx_status & ~STAT_B;
-    rfd.actual_count = (bytes_copied & 0x3FFF) | 0x4000;
-    if (packet_completed) {
-        rfd.actual_count |= I596_EOF;
+    if (simplified_mode) {
+        rfd.actual_count = (bytes_copied & 0x3FFF) | 0x4000;
+        if (packet_completed) {
+            rfd.actual_count |= I596_EOF;
+        }
+    } else {
+        /* the RBDs carry their own counts and the EOF of a split frame */
+        rfd.actual_count = (rfd_data_len & 0x3FFF) | 0x4000;
+        if (packet_completed && rfd_data_len == bytes_copied) {
+            rfd.actual_count |= I596_EOF;
+        }
     }
 
     i82596_rx_desc_write(s, rfd_addr, &rfd, (simplified_mode || I596_LOOPBACK));
 
-    if (rfd.command & CMD_SUSP) {
-        i82596_update_rx_state(s, RX_SUSPENDED);
-        return size;
+    if (packet_completed) {
+        s->rfd_p = i82596_translate_address(s, rfd.link, false);
+        s->scb_status |= SCB_STATUS_FR;
     }
-
-    if (rfd.command & CMD_EOL) {
+    if (rfd.command & (CMD_SUSP | CMD_EOL)) {
         i82596_update_rx_state(s, RX_SUSPENDED);
-        return size;
+    } else if (rbds_exhausted) {
+        i82596_update_rx_state(s, RX_NO_RESOURCES);
     }
-
-    if (packet_completed && s->rx_status == RX_READY) {
-        uint32_t next_rfd_addr = i82596_translate_address(s, rfd.link, false);
-        if (next_rfd_addr != 0 && next_rfd_addr != I596_NULL) {
-            set_uint32(s->scb + 8, next_rfd_addr);
-        }
-
-        s->scb_status |= SCB_STATUS_FR;
+    if (packet_completed || rbds_exhausted) {
         i82596_update_scb_irq(s, true);
     }
     trace_i82596_rx_complete(s->crc_err, s->align_err, s->resource_err);
@@ -1256,8 +1362,6 @@ ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t size)
     if (s->rx_status != RX_READY) {
         if (s->queue_count >= PACKET_QUEUE_SIZE) {
             trace_i82596_receive_queue_full();
-            s->over_err++;
-            set_uint32(s->scb + 22, s->over_err);
             i82596_record_error(s, RX_OVER_ERRORS, false);
             return size;
         }
@@ -1271,6 +1375,11 @@ queue_packet:
         return size;
     }
 
+    if (!i82596_rx_buffers_free(s)) {
+        i82596_rx_hold(s);
+        return 0;
+    }
+
     return i82596_receive_packet(s, buf, size, false);
 }
 
@@ -1363,11 +1472,6 @@ static bool i82596_check_medium_status(I82596State *s)
         return false;
     }
 
-    if (!I596_LOOPBACK && (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) % 100 < 5)) {
-        s->collision_events++;
-        return false;
-    }
-
     return true;
 }
 
@@ -1740,6 +1844,7 @@ skip_status_update:
         if (cmd & CMD_SUSP) {
             s->cu_status = CU_SUSPENDED;
             s->scb_status |= SCB_STATUS_CNA;
+            s->send_irq = 1;
             stop = true;
         }
 
@@ -1747,6 +1852,7 @@ skip_status_update:
             s->cmd_p = I596_NULL;
             s->cu_status = CU_IDLE;
             s->scb_status |= SCB_STATUS_CNA;
+            s->send_irq = 1;
             stop = true;
         } else if (!stop) {
             if (next_addr == 0 || next_addr == I596_NULL ||
@@ -1754,6 +1860,7 @@ skip_status_update:
                 s->cmd_p = I596_NULL;
                 s->cu_status = CU_IDLE;
                 s->scb_status |= SCB_STATUS_CNA;
+                s->send_irq = 1;
                 stop = true;
             } else {
                 s->cmd_p = next_addr;
@@ -2061,7 +2168,7 @@ void i82596_poll(NetClientState *nc, bool enable)
 
 const VMStateDescription vmstate_i82596 = {
     .name = "i82596",
-    .version_id = 1,
+    .version_id = 2,
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
         VMSTATE_UINT8(mode, I82596State),
@@ -2101,6 +2208,8 @@ const VMStateDescription vmstate_i82596 = {
         VMSTATE_UINT32(total_good_frames, I82596State),
         VMSTATE_BUFFER(rx_buffer, I82596State),
         VMSTATE_UINT16(tx_frame_len, I82596State),
+        VMSTATE_UINT32_V(rbd_p, I82596State, 2),
+        VMSTATE_UINT32_V(rfd_p, I82596State, 2),
         VMSTATE_UINT16(rx_frame_len, I82596State),
         VMSTATE_UINT64(current_tx_desc, I82596State),
         VMSTATE_UINT64(current_rx_desc, I82596State),
@@ -2127,6 +2236,13 @@ static int i82596_flush_packet_queue(I82596State *s)
         int tail = s->queue_tail;
         size_t len = s->packet_queue_len[tail];
 
+        if (s->rx_status == RX_READY && !i82596_rx_buffers_free(s)) {
+            i82596_rx_hold(s);
+            timer_mod(s->flush_queue_timer,
+                      qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500 * SCALE_US);
+            break;
+        }
+
         ssize_t ret = i82596_receive_packet(s, s->packet_queue[tail], len,
                                              true);
 
@@ -2174,6 +2290,8 @@ void i82596_common_init(DeviceState *dev, I82596State *s, NetClientInfo *info)
                 dev->id, &dev->mem_reentrancy_guard, s);
     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
 
+    s->rx_retry_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, i82596_rx_retry, s);
+
     if (USE_TIMER) {
         if (!s->flush_queue_timer) {
             s->flush_queue_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
-- 
2.54.0



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PULL v2 0/8] Hppa 715 cpu nic patches
  2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
                   ` (7 preceding siblings ...)
  2026-11-05 22:31 ` [PULL v2 8/8] i82596: Various send- and transmit fixes for 715 Helge Deller
@ 2026-11-06 16:46 ` Richard Henderson
  8 siblings, 0 replies; 10+ messages in thread
From: Richard Henderson @ 2026-11-06 16:46 UTC (permalink / raw)
  To: Helge Deller, qemu-devel; +Cc: Keith Monahan, Helge Deller

On 11/5/26 14:31, Helge Deller wrote:
> From: Helge Deller<deller@gmx.de>
> 
> The following changes since commit 99e6fa0053ba1b1454b8c4446f857c430547a702:
> 
>    Merge tag 'pull-nbd-2026-11-02' ofhttps://gitlab.com/ebblake/qemu into staging (2026-11-03 10:54:31 -0800)
> 
> are available in the Git repository at:
> 
>    https://github.com/hdeller/qemu-hppa.git tags/hppa-715-cpu-nic-pull-request
> 
> for you to fetch changes up to 58952e4a5a2ddc0e450bef0770959b1147c98f2e:
> 
>    i82596: Various send- and transmit fixes for 715 (2026-11-05 23:29:19 +0100)
> 
> ----------------------------------------------------------------
> qemu-hppa-715
> 
> A last bunch of fixes to allow HP-UX 9.05 to run on the 715 machine.
> This includes a fix to handle an invalid use of the blr instruction, a fix
> which puts the power button address outside of the ROM, and various fixes for
> the i82596 NIC.
> 
> Tests were run on HP-UX 9.05, HP-UX 10.20, NetBSD and Linux.

Applied, thanks.

r~


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-11-06 16:47 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 22:31 [PULL v2 0/8] Hppa 715 cpu nic patches Helge Deller
2026-11-05 22:31 ` [PULL v2 1/8] target/hppa: Move emulated power button into CPU I/O region Helge Deller
2026-11-05 22:31 ` [PULL v2 2/8] target/hppa: Add special blr instruction for HP-UX 9.05 Helge Deller
2026-11-05 22:31 ` [PULL v2 3/8] lasi_i82596: Decode the PORT command from bits 3:0, not 7:0 Helge Deller
2026-11-05 22:31 ` [PULL v2 4/8] i82596: Implement 82596CA big-endian mode (SYSBUS bit 7) Helge Deller
2026-11-05 22:31 ` [PULL v2 5/8] i82596: Initialize variables at chip reset Helge Deller
2026-11-05 22:31 ` [PULL v2 6/8] i82596: Drop crc calculation Helge Deller
2026-11-05 22:31 ` [PULL v2 7/8] i82596: Fix source address inserting Helge Deller
2026-11-05 22:31 ` [PULL v2 8/8] i82596: Various send- and transmit fixes for 715 Helge Deller
2026-11-06 16:46 ` [PULL v2 0/8] Hppa 715 cpu nic patches Richard Henderson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.