Port-mips archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Several Malta patches developed using QEMU
I need to run a mips port of NetBSD under unpatched QEMU. Unfortunately,
this excludes the possibility of using the newer "mipssim" target, but
recently (well, OK, within the past few years) QEMU's Malta machine type
has had a bunch of attention, particularly to byte-order issues, and seems
to work pretty well, under Linux at least.
The patches below address a number of issues I found while trying to ensure
that all 4 combinations of byte order and word size worked properly with
our MALTA/MALTA64 kernels and QEMU's malta system emulation. THEY HAVE NOT
BEEN TESTED ON REAL HARDWARE - if anyone still has any, which someone likely
does.
Most of the issues center around either endianness issues with the Gallileo
host bridge, or the lack of initialization that would be performed by the
YAMON firmware on real hardware, particularly when using QEMU's "-kernel"
option to directly load the kernel. I don't think these patches contain
anything that would break real hardware, but, if anything, it might be
the PCI IRQ initialization.
I'd like to commit these in the next few days. Comments welcome and if
anyone has a real Malta board and can test big-endian operation in particular
I'd love to know whether it works.
On QEMU, I can run a stock MALTA or MALTA64 kernel now but with added
options YAMON_IRQ_MAP_BAD and PCI_NETBSD_CONFIGURE. I think these should
be harmless on real hardware but don't want to change the in-tree MALTA
config unless someone can confirm.
---
Summary of changes
---
1. The GT-64120 host bridge _does_ byte-swap access to other PCI targets,
but _does not_ byte-swap access to itself (bus 0, device 0). QEMU
evidently used to get this wrong, but, I confirmed with the databook.
This means we need to manually byte-swap a bunch of access to the
bridge's own regs, or when running big-endian, PCI just basically does
not work (no devices found; interrupt handling not working anyway).
2. Remove a hardcoded #undef YAMON_IRQ_MAP_BAD and change the PCI IRQ
mappings we then install to better match QEMU. This allows use of
options YAMON_IRQ_MAP_BAD and PCI_NETBSD_CONFIGURE to ensure interrupts
are mapped properly when we are booted with qemu -kernel instead of
from a disk image. This also involved a small change to Malta's copy
of the pcib driver, which I should note includes a hardcoded constant
0x60 for the Intel PIIX's PIRQRC register. This must be defined in some
header file, right? I can't find it.
3. Malta's bus space does not define CHIP_LITTLE_ENDIAN. If the QEMU
behavior is correct (DEVICE_LITTLE_ENDIAN: automatically swap multibyte
access) then without this I can't see how it possibly ever worked in
big endian mode either on QEMU or physical hardware. QEMU seems to
have relatively recently fixed this, so if it worked on QEMU before, it
is likely because QEMU was broken.
4. QEMU's YAMON emulation sets a bogus timer frequency. This fix is easy:
just always run the timer calibration.
5. Make the PCI_NETBSD_CONFIGURE code work properly on Malta by conforming
it to the Galileo databook and the observed behavior of QEMU and (per
comments in other people's code) real YAMON: BAR allocation should start
at MALTA_PCIMEM_2_BASE, not MALTA_PCIMEM1_BASE.
---
Index: arch/evbmips/malta/machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/evbmips/malta/machdep.c,v
retrieving revision 1.48
diff -u -r1.48 machdep.c
--- arch/evbmips/malta/machdep.c 20 Dec 2025 10:51:02 -0000 1.48
+++ arch/evbmips/malta/machdep.c 29 Mar 2026 21:37:28 -0000
@@ -149,7 +149,6 @@
uint8_t * const brkres = (uint8_t *)MIPS_PHYS_TO_KSEG1(MALTA_BRKRES);
bus_space_handle_t sh;
void *kernend;
- int freqok;
extern char edata[], end[];
@@ -186,9 +185,9 @@
physmem = btoc(memsize);
/*
- * Use YAMON's CPU frequency if available.
+ * Start with YAMON's CPU frequency, which may or may not be reliable.
*/
- freqok = yamon_setcpufreq(1);
+ (void)yamon_setcpufreq(1);
gt_pci_init(&mcp->mc_pc, &mcp->mc_gt);
malta_bus_io_init(&mcp->mc_iot, mcp);
@@ -196,13 +195,13 @@
malta_dma_init(mcp);
/*
- * Calibrate the timer if YAMON failed to tell us.
+ * Always calibrate the timer - YAMON may be right on physical
+ * hardware, but has been observed to be wrong on emulators.
*/
- if (!freqok) {
- bus_space_map(&mcp->mc_iot, MALTA_RTCADR, 2, 0, &sh);
- malta_cal_timer(&mcp->mc_iot, sh);
- bus_space_unmap(&mcp->mc_iot, sh, 2);
- }
+ bus_space_map(&mcp->mc_iot, MALTA_RTCADR, 2, 0, &sh);
+ malta_cal_timer(&mcp->mc_iot, sh);
+ bus_space_unmap(&mcp->mc_iot, sh, 2);
+
#if NCOM > 0
/*
Index: arch/evbmips/malta/malta_bus_io.c
===================================================================
RCS file: /cvsroot/src/sys/arch/evbmips/malta/malta_bus_io.c,v
retrieving revision 1.6
diff -u -r1.6 malta_bus_io.c
--- arch/evbmips/malta/malta_bus_io.c 28 Apr 2008 20:23:17 -0000 1.6
+++ arch/evbmips/malta/malta_bus_io.c 29 Mar 2026 21:37:28 -0000
@@ -44,6 +44,15 @@
#define CHIP malta
#define CHIP_IO /* defined */
+/*
+ * This platform's PCI I/O region is DEVICE_LITTLE_ENDIAN (tested on
+ * QEMU 10, which has recently had what purports to be a thorough going
+ * over to conform it to the actual hardware) so we need CHIP_LITTLE_ENDIAN
+ * here to accommodate big-endian kernels.
+ *
+ */
+#define CHIP_LITTLE_ENDIAN
+
#define CHIP_EX_MALLOC_SAFE(v) (((struct malta_config *)(v))->mc_mallocsafe)
#define CHIP_EXTENT(v) (((struct malta_config *)(v))->mc_io_ex)
Index: arch/evbmips/malta/malta_bus_mem.c
===================================================================
RCS file: /cvsroot/src/sys/arch/evbmips/malta/malta_bus_mem.c,v
retrieving revision 1.7
diff -u -r1.7 malta_bus_mem.c
--- arch/evbmips/malta/malta_bus_mem.c 28 Apr 2008 20:23:17 -0000 1.7
+++ arch/evbmips/malta/malta_bus_mem.c 29 Mar 2026 21:37:28 -0000
@@ -44,6 +44,15 @@
#define CHIP malta
#define CHIP_MEM /* defined */
+/*
+ * This platform's PCI memory region is DEVICE_LITTLE_ENDIAN (tested on
+ * QEMU 10, which has recently had what purports to be a thorough going
+ * over to conform it to the actual hardware) so we need CHIP_LITTLE_ENDIAN
+ * here to accommodate big-endian kernels.
+ *
+ */
+#define CHIP_LITTLE_ENDIAN
+
#define CHIP_EX_MALLOC_SAFE(v) (((struct malta_config *)(v))->mc_mallocsafe)
#define CHIP_EXTENT(v) (((struct malta_config *)(v))->mc_mem_ex)
Index: arch/evbmips/malta/malta_intr.c
===================================================================
RCS file: /cvsroot/src/sys/arch/evbmips/malta/malta_intr.c,v
retrieving revision 1.27
diff -u -r1.27 malta_intr.c
--- arch/evbmips/malta/malta_intr.c 21 Nov 2020 15:36:36 -0000 1.27
+++ arch/evbmips/malta/malta_intr.c 29 Mar 2026 21:37:28 -0000
@@ -271,10 +271,13 @@
}
/*
- * YAMON configures pa_intrline correctly (so far), so we trust it to DTRT
- * in the future...
+ * On real hardware, YAMON configures pa_intrline correctly (so far), but
+ * on QEMU, the firmware doesn't program the PIIX's IRQ routing registers
+ * when directly loading a kernel (or possibly ever), so we do it ourselves,
+ * mapping all PIRQ lines to IRQ 10, which matches QEMU's Malta emulation
+ * and is hopefully safe on real hardware, which must still exist somewhere.
*/
-#undef YAMON_IRQ_MAP_BAD
+#define YAMON_IRQ_MAP_BAD
/*
* PCI interrupt support
@@ -284,18 +287,18 @@
{
#ifdef YAMON_IRQ_MAP_BAD
static const int pciirqmap[12/*device*/][4/*pin*/] = {
- { -1, -1, -1, 11 }, /* 10: USB */
+ { -1, -1, -1, 10 }, /* 10: USB */
{ 10, -1, -1, -1 }, /* 11: Ethernet */
- { 11, -1, -1, -1 }, /* 12: Audio */
+ { 10, -1, -1, -1 }, /* 12: Audio */
{ -1, -1, -1, -1 }, /* 13: not used */
{ -1, -1, -1, -1 }, /* 14: not used */
{ -1, -1, -1, -1 }, /* 15: not used */
{ -1, -1, -1, -1 }, /* 16: not used */
{ -1, -1, -1, -1 }, /* 17: Core card(?) */
- { 10, 10, 11, 11 }, /* 18: PCI Slot 1 */
- { 10, 11, 11, 10 }, /* 19: PCI Slot 2 */
- { 11, 11, 10, 10 }, /* 20: PCI Slot 3 */
- { 11, 10, 10, 11 }, /* 21: PCI Slot 4 */
+ { 10, 10, 10, 10 }, /* 18: PCI Slot 1 */
+ { 10, 10, 10, 10 }, /* 19: PCI Slot 2 */
+ { 10, 10, 10, 10 }, /* 20: PCI Slot 3 */
+ { 10, 10, 10, 10 }, /* 21: PCI Slot 4 */
};
int buspin, device, irq;
#else /* !YAMON_IRQ_MAP_BAD */
Index: arch/evbmips/malta/dev/gt.c
===================================================================
RCS file: /cvsroot/src/sys/arch/evbmips/malta/dev/gt.c,v
retrieving revision 1.17
diff -u -r1.17 gt.c
--- arch/evbmips/malta/dev/gt.c 7 Aug 2021 16:18:51 -0000 1.17
+++ arch/evbmips/malta/dev/gt.c 29 Mar 2026 21:37:28 -0000
@@ -41,6 +41,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
+#include <sys/endian.h>
#include <dev/pci/pcivar.h>
@@ -158,6 +159,12 @@
*fp = (tag >> 8) & 0x7;
}
+/*
+ * Careful - On big-endian systems, the GT-64120 swaps PCI configuration
+ * data for external targets, but not for itself (bus 0, device 0)
+ * with the endearing result that we must conditionally byteswap here.
+ * Observed on QEMU 10 and confirmed with the databook.
+ */
static pcireg_t
gt_conf_read(void *v, pcitag_t tag, int offset)
{
@@ -182,11 +189,15 @@
/* Clear cause register bits. */
GT_REGVAL(GT_INTR_CAUSE) = 0;
- GT_REGVAL(GT_PCI0_CFG_ADDR) = (1 << 31) | tag | offset;
+ GT_REGVAL(GT_PCI0_CFG_ADDR) = htole32((1 << 31) | tag | offset);
data = GT_REGVAL(GT_PCI0_CFG_DATA);
+ if (bus == 0 && dev == 0) {
+ data = le32toh(data);
+ }
/* Check for master abort. */
- if (GT_REGVAL(GT_INTR_CAUSE) & (GTIC_MASABORT0 | GTIC_TARABORT0))
+ if (le32toh(GT_REGVAL(GT_INTR_CAUSE)) &
+ (GTIC_MASABORT0 | GTIC_TARABORT0))
data = (pcireg_t) -1;
PCI_CONF_UNLOCK(s);
@@ -217,8 +228,11 @@
/* Clear cause register bits. */
GT_REGVAL(GT_INTR_CAUSE) = 0;
- GT_REGVAL(GT_PCI0_CFG_ADDR) = (1 << 31) | tag | offset;
- GT_REGVAL(GT_PCI0_CFG_DATA) = data;
+ GT_REGVAL(GT_PCI0_CFG_ADDR) = htole32((1 << 31) | tag | offset);
+ if (bus == 0 && dev == 0)
+ GT_REGVAL(GT_PCI0_CFG_DATA) = htole32(data);
+ else
+ GT_REGVAL(GT_PCI0_CFG_DATA) = data;
PCI_CONF_UNLOCK(s);
}
Index: arch/evbmips/malta/dev/mainbus.c
===================================================================
RCS file: /cvsroot/src/sys/arch/evbmips/malta/dev/mainbus.c,v
retrieving revision 1.18
diff -u -r1.18 mainbus.c
--- arch/evbmips/malta/dev/mainbus.c 20 Dec 2023 14:12:25 -0000 1.18
+++ arch/evbmips/malta/dev/mainbus.c 29 Mar 2026 21:37:28 -0000
@@ -93,8 +93,8 @@
#define PCI_IO_END 0x0000efff
#define PCI_IO_SIZE ((PCI_IO_END - PCI_IO_START) + 1)
-#define PCI_MEM_START MALTA_PCIMEM1_BASE
-#define PCI_MEM_SIZE MALTA_PCIMEM1_SIZE
+#define PCI_MEM_START MALTA_PCIMEM2_BASE
+#define PCI_MEM_SIZE MALTA_PCIMEM2_SIZE
static int
mainbus_match(device_t parent, cfdata_t match, void *aux)
@@ -150,6 +150,20 @@
pcitag_t idetag = pci_make_tag(pc, 0, 10, 1);
pci_conf_write(pc, idetag, PIIX_IDETIM, idetim);
#endif
+
+#if defined(PCI_NETBSD_CONFIGURE)
+ /*
+ * When booting without firmware (as for direct kernel load
+ * under QEMU) we need to configure the PIIX4 (pcib)'s interrupt
+ * routing ourselves. For simplicitly, we rely on our
+ * change to the Gallileo code which uses IRQ 10 for everything
+ * if YAMON_IRQ_MAP_BAD.
+ */
+ pcitag_t isabtag = pci_make_tag(pc, 0, 10, 0);
+ pcireg_t pirqrc = 10 | (10 << 8) | (10 << 16) | (10 << 24);
+ pci_conf_write(pc, isabtag, 0x60, pirqc); /* 0x60 is PIRQRC */
+#endif
+
for (md = mainbusdevs; md->md_name != NULL; md++) {
ma.ma_name = md->md_name;
ma.ma_addr = md->md_addr;
Index: arch/evbmips/malta/pci/pcib.c
===================================================================
RCS file: /cvsroot/src/sys/arch/evbmips/malta/pci/pcib.c,v
retrieving revision 1.26
diff -u -r1.26 pcib.c
--- arch/evbmips/malta/pci/pcib.c 19 Oct 2025 20:35:02 -0000 1.26
+++ arch/evbmips/malta/pci/pcib.c 29 Mar 2026 21:37:28 -0000
@@ -42,6 +42,7 @@
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
+#include <sys/endian.h>
#include <sys/kmem.h>
#include <uvm/uvm_extern.h>
@@ -420,7 +421,7 @@
}
#else
/* XXX - should be a function call to gt.c? */
- irq = GT_REGVAL(GT_PCI0_INTR_ACK) & 0xff;
+ irq = le32toh(GT_REGVAL(GT_PCI0_INTR_ACK)) & 0xff;
/*
* From YAMON source code:
Home |
Main Index |
Thread Index |
Old Index