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-ayl...@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