qemu-devel
[Top][All Lists]
Advanced

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

[PATCH 30/30] mac_via: work around QEMU unaligned MMIO access bug


From: Mark Cave-Ayland
Subject: [PATCH 30/30] mac_via: work around QEMU unaligned MMIO access bug
Date: Wed, 24 May 2023 22:11:04 +0100

During the kernel timer calibration routine A/UX performs an unaligned access
across the T1CL and T1CH registers to read the entire 16-bit value in a
single memory access.

This triggers a bug in the QEMU softtlb implementation whereby the 2 separate
accesses are combined incorrectly losing the high byte of the counter (see
https://gitlab.com/qemu-project/qemu/-/issues/360 for more detail). Since
A/UX requires a minimum difference of 0x500 between 2 subsequent reads to
succeed then this causes the timer calibration routine to get stuck in an
infinite loop.

Add a temporary workaround for the QEMU unaligned MMIO access bug whereby
these special accesses are detected and the 8-byte result copied into both
halves of the 16-bit access which allows the existing softtlb implementation
to return the correct result.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
---
 hw/m68k/q800.c            |  1 +
 hw/misc/mac_via.c         | 42 +++++++++++++++++++++++++++++++++++++++
 hw/misc/trace-events      |  1 +
 include/hw/misc/mac_via.h |  4 +++-
 4 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c
index bf4acb5db7..918cc8f695 100644
--- a/hw/m68k/q800.c
+++ b/hw/m68k/q800.c
@@ -443,6 +443,7 @@ static const MemoryRegionOps macio_alias_ops = {
     .valid = {
         .min_access_size = 1,
         .max_access_size = 4,
+        .unaligned = true,     /* For VIA1 via1_unaligned_hack_state() */
     },
 };
 
diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c
index 4ec1ee18dd..45c8dee9f4 100644
--- a/hw/misc/mac_via.c
+++ b/hw/misc/mac_via.c
@@ -1026,12 +1026,47 @@ static void 
via1_timer_calibration_hack(MOS6522Q800VIA1State *v1s, int addr,
     }
 }
 
+static bool via1_unaligned_hack_state(MOS6522Q800VIA1State *v1s, hwaddr addr,
+                                      int size)
+{
+    /*
+     * Workaround for bug in QEMU whereby load_helper() doesn't correctly
+     * handle combining unaligned memory accesses: see QEMU issue
+     * https://gitlab.com/qemu-project/qemu/-/issues/360 for all the
+     * details.
+     *
+     * Its only known use is during the A/UX timer calibration loop which
+     * runs on kernel startup.
+     */
+    switch (v1s->unaligned_hack_state) {
+    case 0:
+        /* First half of unaligned access */
+        if (addr == 0x11fe && size == 2) {
+            v1s->unaligned_hack_state = 1;
+            trace_via1_unaligned_hack_state(v1s->unaligned_hack_state);
+            return true;
+        }
+        return false;
+    case 1:
+        /* Second half of unaligned access */
+        if (addr == 0x1200 && size == 2) {
+            v1s->unaligned_hack_state = 0;
+            trace_via1_unaligned_hack_state(v1s->unaligned_hack_state);
+            return true;
+        }
+        return false;
+    default:
+        g_assert_not_reached();
+    }
+}
+
 static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned 
size)
 {
     MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque);
     MOS6522State *ms = MOS6522(v1s);
     int64_t now;
     uint64_t ret;
+    hwaddr oldaddr = addr;
 
     addr = (addr >> 9) & 0xf;
     ret = mos6522_read(ms, addr, size);
@@ -1059,6 +1094,12 @@ static uint64_t mos6522_q800_via1_read(void *opaque, 
hwaddr addr, unsigned size)
         }
         break;
     }
+
+    if (via1_unaligned_hack_state(v1s, oldaddr, size)) {
+        /* Splat return byte into word to fix unaligned access combine */
+        ret |= ret << 8;
+    }
+
     return ret;
 }
 
@@ -1126,6 +1167,7 @@ static const MemoryRegionOps mos6522_q800_via1_ops = {
     .valid = {
         .min_access_size = 1,
         .max_access_size = 4,
+        .unaligned = true,     /* For VIA1 via1_unaligned_hack_state() */
     },
 };
 
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 7206bd5d93..8867cef356 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -252,6 +252,7 @@ via1_adb_poll(uint8_t data, const char *vadbint, int 
status, int index, int size
 via1_adb_netbsd_enum_hack(void) "using NetBSD enum hack"
 via1_auxmode(int mode) "setting auxmode to %d"
 via1_timer_hack_state(int state) "setting timer_hack_state to %d"
+via1_unaligned_hack_state(int state) "setting unaligned_hack_state to %d"
 
 # grlib_ahb_apb_pnp.c
 grlib_ahb_pnp_read(uint64_t addr, unsigned size, uint32_t value) "AHB PnP read 
addr:0x%03"PRIx64" size:%u data:0x%08x"
diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h
index 63cdcf7c69..0a12737552 100644
--- a/include/hw/misc/mac_via.h
+++ b/include/hw/misc/mac_via.h
@@ -77,8 +77,10 @@ struct MOS6522Q800VIA1State {
 
     /* SETUPTIMEK hack */
     int timer_hack_state;
-};
 
+    /* Unaligned access hack */
+    int unaligned_hack_state;
+};
 
 /* VIA 2 */
 #define VIA2_IRQ_SCSI_DATA_BIT  CA2_INT_BIT
-- 
2.30.2




reply via email to

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