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