OK, solved. This motherboard (or bios) gives the io apic an id of 0, same as the boot processor's local apic. The unexpected aliasing causes the lapic (virtual) address to be overwritten by the ioapic address, so the lapic timer code is looking in the wrong place for its registers.
This was actually corrected on sources by a change to mp.c on 17 May (by making two separate tables for local apics and io apics). Unfortunately the /386/9pc on sources, although also dated 17 May, doesn't include the fix, so I didn't realise it had been fixed till I tried rebuilding the kernel from a fresh replica/pull. Erik's kernel has also had a similar fix, presumably made some time ago - there's a 9fans post about a "wierd lapic/ioapic configuration" which I should have spotted as a clue ...