All of lore.kernel.org
 help / color / mirror / Atom feed
From: 54weasels <54weasels@gmail.com>
To: qemu-devel@nongnu.org
Cc: laurent@vivier.eu, thuth@redhat.com, 54weasels <54weasels@gmail.com>
Subject: [PATCH 2/7] hw/net/lance: Add Sun-3 Native DMA byte-swapping support
Date: Sat,  2 May 2026 18:57:51 -0700	[thread overview]
Message-ID: <20260503015756.99176-3-54weasels@gmail.com> (raw)
In-Reply-To: <20260503015756.99176-1-54weasels@gmail.com>

The Sun-3 hardware physically byte-swaps the D0-D7 and D8-D15 DMA lanes between the Little-Endian LANCE controller and the Big-Endian Sun-3 memory. This commit intercepts the LANCE DMA reads/writes by injecting a phys_mem override in pcnet.c specifically for the Sun-3 instance, dynamically neutralizing the hardcoded internal initblk swap and properly mapping the payload payloads.

Signed-off-by: 54weasels <54weasels@gmail.com>
---
 hw/net/lance.c         | 111 ++++++++++++++++++++++++++++++++++++++---
 hw/net/meson.build     |   3 +-
 include/hw/net/lance.h |   3 ++
 3 files changed, 109 insertions(+), 8 deletions(-)

diff --git a/hw/net/lance.c b/hw/net/lance.c
index 5d5bf9b961..93944fb35d 100644
--- a/hw/net/lance.c
+++ b/hw/net/lance.c
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 /*
  * QEMU AMD PC-Net II (Am79C970A) emulation
  *
@@ -22,7 +23,8 @@
  * THE SOFTWARE.
  */
 
-/* This software was written to be compatible with the specification:
+/*
+ * This software was written to be compatible with the specification:
  * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
  * AMD Publication# 19436  Rev:E  Amendment/0  Issue Date: June 2000
  */
@@ -44,8 +46,73 @@
 #include "hw/core/qdev-properties.h"
 #include "trace.h"
 #include "system/system.h"
+#include "system/address-spaces.h"
+#include "system/dma.h"
+
+/*
+ * LANCE Native DMA Read Hook
+ *
+ * Sun-3 Hardware intrinsically byte-swaps the D0-D7 and D8-D15 DMA lanes
+ * between the Little-Endian LANCE controller and the Big-Endian Sun-3 memory.
+ * We MUST model this manually. pcnet.c passes CSR_BSWP(s) for payloads
+ * (correctly bypassing the swap if LANCE internally neutralizes it).
+ * However, pcnet.c hardcodes do_bswap=1 for the `initblk` structure
+ * (len 24/28).
+ * We dynamically intercept the `initblk` fetch by cross-referencing CSR_IADR to
+ * enforce the hardware swap reliably. Because this branch is only taken when
+ * `dma_mr` is explicitly provided by the machine, this quirk is safely isolated
+ * to the Sun-3 and does not impact SPARC (which uses `ledma`).
+ */
+static void lance_dma_read(void *dma_opaque, hwaddr addr,
+                           uint8_t *buf, int len, int do_bswap)
+{
+    SysBusPCNetState *d = SYSBUS_PCNET(dma_opaque);
+    PCNetState *s = &d->state;
+
+    dma_memory_read(&d->dma_as, addr, buf, len, MEMTXATTRS_UNSPECIFIED);
+
+    uint32_t bcr_ssize32 = (s->bcr[20] & 0x0100);
+    hwaddr iadr = (s->csr[1] | ((uint32_t)s->csr[2] << 16));
+    if (!bcr_ssize32) {
+        iadr |= ((0xff00 & (uint32_t)s->csr[2]) << 16);
+    }
 
+    int internal_bswap = do_bswap;
+    if (addr == iadr && (len == 24 || len == 28)) {
+        internal_bswap = 0; /* Force hardware swap natively for initblk */
+    }
 
+    if (!internal_bswap) {
+        for (int i = 0; i < (len & ~1); i += 2) {
+            uint8_t tmp = buf[i];
+            buf[i] = buf[i + 1];
+            buf[i + 1] = tmp;
+        }
+    }
+}
+
+/* LANCE Native DMA Write Hook */
+static void lance_dma_write(void *dma_opaque, hwaddr addr,
+                            uint8_t *buf, int len, int do_bswap)
+{
+    SysBusPCNetState *s = SYSBUS_PCNET(dma_opaque);
+
+    if (!do_bswap) {
+        uint8_t *swapped_buf = g_malloc(len);
+        for (int i = 0; i < (len & ~1); i += 2) {
+            swapped_buf[i] = buf[i + 1];
+            swapped_buf[i + 1] = buf[i];
+        }
+        if (len & 1) {
+            swapped_buf[len - 1] = buf[len - 1];
+        }
+        dma_memory_write(&s->dma_as, addr, swapped_buf, len,
+                         MEMTXATTRS_UNSPECIFIED);
+        g_free(swapped_buf);
+    } else {
+        dma_memory_write(&s->dma_as, addr, buf, len, MEMTXATTRS_UNSPECIFIED);
+    }
+}
 static void parent_lance_reset(void *opaque, int irq, int level)
 {
     SysBusPCNetState *d = opaque;
@@ -59,7 +126,15 @@ static void lance_mem_write(void *opaque, hwaddr addr,
     SysBusPCNetState *d = opaque;
 
     trace_lance_mem_writew(addr, val & 0xffff);
-    pcnet_ioport_writew(&d->state, addr, val & 0xffff);
+    if (size == 1) {
+        uint16_t orig = pcnet_ioport_readw(&d->state, addr & ~1);
+        if (addr & 1) { /* LSB in Big Endian */
+            val = (orig & 0xff00) | (val & 0xff);
+        } else { /* MSB in Big Endian */
+            val = (orig & 0x00ff) | ((val & 0xff) << 8);
+        }
+    }
+    pcnet_ioport_writew(&d->state, addr & ~1, val & 0xffff);
 }
 
 static uint64_t lance_mem_read(void *opaque, hwaddr addr,
@@ -68,18 +143,28 @@ static uint64_t lance_mem_read(void *opaque, hwaddr addr,
     SysBusPCNetState *d = opaque;
     uint32_t val;
 
-    val = pcnet_ioport_readw(&d->state, addr);
-    trace_lance_mem_readw(addr, val & 0xffff);
+    val = pcnet_ioport_readw(&d->state, addr & ~1);
+    if (size == 1) {
+        if (addr & 1) {
+            val = val & 0xff;
+        } else {
+            val = (val >> 8) & 0xff;
+        }
+    }
     return val & 0xffff;
 }
 
 static const MemoryRegionOps lance_mem_ops = {
     .read = lance_mem_read,
     .write = lance_mem_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
+    .endianness = DEVICE_BIG_ENDIAN,
     .valid = {
-        .min_access_size = 2,
-        .max_access_size = 2,
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
     },
 };
 
@@ -115,8 +200,18 @@ static void lance_realize(DeviceState *dev, Error **errp)
 
     sysbus_init_irq(sbd, &s->irq);
 
+    if (d->dma_mr) {
+        address_space_init(&d->dma_as, d->dma_mr, "lance-dma");
+        s->phys_mem_read = lance_dma_read;
+        s->phys_mem_write = lance_dma_write;
+        s->dma_opaque = DEVICE(d);
+    } else {
+#if defined(TARGET_SPARC)
     s->phys_mem_read = ledma_memory_read;
     s->phys_mem_write = ledma_memory_write;
+#endif
+    }
+
     pcnet_common_init(dev, s, &net_lance_info);
 }
 
@@ -140,6 +235,8 @@ static void lance_instance_init(Object *obj)
 static const Property lance_properties[] = {
     DEFINE_PROP_LINK("dma", SysBusPCNetState, state.dma_opaque,
                      TYPE_DEVICE, DeviceState *),
+    DEFINE_PROP_LINK("dma_mr", SysBusPCNetState, dma_mr,
+                     TYPE_MEMORY_REGION, MemoryRegion *),
     DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf),
 };
 
diff --git a/hw/net/meson.build b/hw/net/meson.build
index 3102587469..e653a9bc73 100644
--- a/hw/net/meson.build
+++ b/hw/net/meson.build
@@ -31,7 +31,8 @@ system_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.c'))
 
 system_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c'))
 system_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c'))
-system_ss.add(when: 'CONFIG_LANCE', if_true: files('lance.c'))
+# LANCE uses target-specific memory types (e.g., target_ulong) and must be compiled per-target.
+specific_ss.add(when: 'CONFIG_LANCE', if_true: files('lance.c'))
 system_ss.add(when: 'CONFIG_LASI_82596', if_true: files('lasi_i82596.c'))
 system_ss.add(when: 'CONFIG_I82596_COMMON', if_true: files('i82596.c'))
 system_ss.add(when: 'CONFIG_SUNHME', if_true: files('sunhme.c'))
diff --git a/include/hw/net/lance.h b/include/hw/net/lance.h
index be473e2eed..706160d7eb 100644
--- a/include/hw/net/lance.h
+++ b/include/hw/net/lance.h
@@ -31,6 +31,7 @@
 
 #include "net/net.h"
 #include "hw/net/pcnet.h"
+#include "system/memory.h"
 #include "hw/core/sysbus.h"
 #include "qom/object.h"
 
@@ -43,6 +44,8 @@ struct SysBusPCNetState {
     SysBusDevice parent_obj;
 
     PCNetState state;
+    MemoryRegion *dma_mr;
+    AddressSpace dma_as;
 };
 
 #endif
-- 
2.50.1 (Apple Git-155)



  parent reply	other threads:[~2026-05-03  5:59 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-03  1:57 [PATCH 0/7] m68k: Add Sun-3 Machine Emulation 54weasels
2026-05-03  1:57 ` [PATCH 1/7] target/m68k: Implement Physical Bus Error exception handling 54weasels
2026-05-10  5:40   ` Thomas Huth
2026-05-11  6:38     ` Purr Box
2026-05-03  1:57 ` 54weasels [this message]
2026-05-03  1:57 ` [PATCH 3/7] hw/char/escc: Expose diagnostic RS232 I/O routing 54weasels
2026-05-03  1:57 ` [PATCH 4/7] hw/timer: Introduce Intersil 7170 RTC implementation 54weasels
2026-05-03  1:57 ` [PATCH 5/7] hw/m68k: Overhaul Sun-3 MMU and Boot PROM mapping 54weasels
2026-05-03  1:57 ` [PATCH 6/7] tests/qtest: Add Sun-3 hardware interaction tests 54weasels
2026-05-03  1:57 ` [PATCH 7/7] tests/functional: Add Sun-3 firmware boot and diagnostic test 54weasels

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260503015756.99176-3-54weasels@gmail.com \
    --to=54weasels@gmail.com \
    --cc=laurent@vivier.eu \
    --cc=qemu-devel@nongnu.org \
    --cc=thuth@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.