summaryrefslogtreecommitdiffstats
path: root/qemu/hw/mips
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/hw/mips')
-rw-r--r--qemu/hw/mips/Makefile.objs5
-rw-r--r--qemu/hw/mips/addr.c39
-rw-r--r--qemu/hw/mips/cputimer.c157
-rw-r--r--qemu/hw/mips/gt64xxx_pci.c1259
-rw-r--r--qemu/hw/mips/mips_fulong2e.c406
-rw-r--r--qemu/hw/mips/mips_int.c78
-rw-r--r--qemu/hw/mips/mips_jazz.c383
-rw-r--r--qemu/hw/mips/mips_malta.c1238
-rw-r--r--qemu/hw/mips/mips_mipssim.c245
-rw-r--r--qemu/hw/mips/mips_r4k.c314
10 files changed, 4124 insertions, 0 deletions
diff --git a/qemu/hw/mips/Makefile.objs b/qemu/hw/mips/Makefile.objs
new file mode 100644
index 000000000..9633f3a57
--- /dev/null
+++ b/qemu/hw/mips/Makefile.objs
@@ -0,0 +1,5 @@
+obj-y += mips_r4k.o mips_malta.o mips_mipssim.o
+obj-y += addr.o cputimer.o mips_int.o
+obj-$(CONFIG_JAZZ) += mips_jazz.o
+obj-$(CONFIG_FULONG) += mips_fulong2e.o
+obj-y += gt64xxx_pci.o
diff --git a/qemu/hw/mips/addr.c b/qemu/hw/mips/addr.c
new file mode 100644
index 000000000..ff3b95260
--- /dev/null
+++ b/qemu/hw/mips/addr.c
@@ -0,0 +1,39 @@
+/*
+ * QEMU MIPS address translation support
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/mips/cpudevs.h"
+
+uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr)
+{
+ return addr & 0x1fffffffll;
+}
+
+uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr)
+{
+ return addr | ~0x7fffffffll;
+}
+
+uint64_t cpu_mips_kvm_um_phys_to_kseg0(void *opaque, uint64_t addr)
+{
+ return addr | 0x40000000ll;
+}
diff --git a/qemu/hw/mips/cputimer.c b/qemu/hw/mips/cputimer.c
new file mode 100644
index 000000000..577c9aeab
--- /dev/null
+++ b/qemu/hw/mips/cputimer.c
@@ -0,0 +1,157 @@
+/*
+ * QEMU MIPS timer support
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/mips/cpudevs.h"
+#include "qemu/timer.h"
+#include "sysemu/kvm.h"
+
+#define TIMER_FREQ 100 * 1000 * 1000
+
+/* XXX: do not use a global */
+uint32_t cpu_mips_get_random (CPUMIPSState *env)
+{
+ static uint32_t lfsr = 1;
+ static uint32_t prev_idx = 0;
+ uint32_t idx;
+ /* Don't return same value twice, so get another value */
+ do {
+ lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u);
+ idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired;
+ } while (idx == prev_idx);
+ prev_idx = idx;
+ return idx;
+}
+
+/* MIPS R4K timer */
+static void cpu_mips_timer_update(CPUMIPSState *env)
+{
+ uint64_t now, next;
+ uint32_t wait;
+
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ wait = env->CP0_Compare - env->CP0_Count -
+ (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
+ next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
+ timer_mod(env->timer, next);
+}
+
+/* Expire the timer. */
+static void cpu_mips_timer_expire(CPUMIPSState *env)
+{
+ cpu_mips_timer_update(env);
+ if (env->insn_flags & ISA_MIPS32R2) {
+ env->CP0_Cause |= 1 << CP0Ca_TI;
+ }
+ qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
+}
+
+uint32_t cpu_mips_get_count (CPUMIPSState *env)
+{
+ if (env->CP0_Cause & (1 << CP0Ca_DC)) {
+ return env->CP0_Count;
+ } else {
+ uint64_t now;
+
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ if (timer_pending(env->timer)
+ && timer_expired(env->timer, now)) {
+ /* The timer has already expired. */
+ cpu_mips_timer_expire(env);
+ }
+
+ return env->CP0_Count +
+ (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
+ }
+}
+
+void cpu_mips_store_count (CPUMIPSState *env, uint32_t count)
+{
+ /*
+ * This gets called from cpu_state_reset(), potentially before timer init.
+ * So env->timer may be NULL, which is also the case with KVM enabled so
+ * treat timer as disabled in that case.
+ */
+ if (env->CP0_Cause & (1 << CP0Ca_DC) || !env->timer)
+ env->CP0_Count = count;
+ else {
+ /* Store new count register */
+ env->CP0_Count =
+ count - (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ TIMER_FREQ, get_ticks_per_sec());
+ /* Update timer timer */
+ cpu_mips_timer_update(env);
+ }
+}
+
+void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value)
+{
+ env->CP0_Compare = value;
+ if (!(env->CP0_Cause & (1 << CP0Ca_DC)))
+ cpu_mips_timer_update(env);
+ if (env->insn_flags & ISA_MIPS32R2)
+ env->CP0_Cause &= ~(1 << CP0Ca_TI);
+ qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
+}
+
+void cpu_mips_start_count(CPUMIPSState *env)
+{
+ cpu_mips_store_count(env, env->CP0_Count);
+}
+
+void cpu_mips_stop_count(CPUMIPSState *env)
+{
+ /* Store the current value */
+ env->CP0_Count += (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ TIMER_FREQ, get_ticks_per_sec());
+}
+
+static void mips_timer_cb (void *opaque)
+{
+ CPUMIPSState *env;
+
+ env = opaque;
+#if 0
+ qemu_log("%s\n", __func__);
+#endif
+
+ if (env->CP0_Cause & (1 << CP0Ca_DC))
+ return;
+
+ /* ??? This callback should occur when the counter is exactly equal to
+ the comparator value. Offset the count by one to avoid immediately
+ retriggering the callback before any virtual time has passed. */
+ env->CP0_Count++;
+ cpu_mips_timer_expire(env);
+ env->CP0_Count--;
+}
+
+void cpu_mips_clock_init (CPUMIPSState *env)
+{
+ /*
+ * If we're in KVM mode, don't create the periodic timer, that is handled in
+ * kernel.
+ */
+ if (!kvm_enabled()) {
+ env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb, env);
+ }
+}
diff --git a/qemu/hw/mips/gt64xxx_pci.c b/qemu/hw/mips/gt64xxx_pci.c
new file mode 100644
index 000000000..10fcca33f
--- /dev/null
+++ b/qemu/hw/mips/gt64xxx_pci.c
@@ -0,0 +1,1259 @@
+/*
+ * QEMU GT64120 PCI host
+ *
+ * Copyright (c) 2006,2007 Aurelien Jarno
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/mips/mips.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+#include "hw/i386/pc.h"
+#include "exec/address-spaces.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define GT_REGS (0x1000 >> 2)
+
+/* CPU Configuration */
+#define GT_CPU (0x000 >> 2)
+#define GT_MULTI (0x120 >> 2)
+
+/* CPU Address Decode */
+#define GT_SCS10LD (0x008 >> 2)
+#define GT_SCS10HD (0x010 >> 2)
+#define GT_SCS32LD (0x018 >> 2)
+#define GT_SCS32HD (0x020 >> 2)
+#define GT_CS20LD (0x028 >> 2)
+#define GT_CS20HD (0x030 >> 2)
+#define GT_CS3BOOTLD (0x038 >> 2)
+#define GT_CS3BOOTHD (0x040 >> 2)
+#define GT_PCI0IOLD (0x048 >> 2)
+#define GT_PCI0IOHD (0x050 >> 2)
+#define GT_PCI0M0LD (0x058 >> 2)
+#define GT_PCI0M0HD (0x060 >> 2)
+#define GT_PCI0M1LD (0x080 >> 2)
+#define GT_PCI0M1HD (0x088 >> 2)
+#define GT_PCI1IOLD (0x090 >> 2)
+#define GT_PCI1IOHD (0x098 >> 2)
+#define GT_PCI1M0LD (0x0a0 >> 2)
+#define GT_PCI1M0HD (0x0a8 >> 2)
+#define GT_PCI1M1LD (0x0b0 >> 2)
+#define GT_PCI1M1HD (0x0b8 >> 2)
+#define GT_ISD (0x068 >> 2)
+
+#define GT_SCS10AR (0x0d0 >> 2)
+#define GT_SCS32AR (0x0d8 >> 2)
+#define GT_CS20R (0x0e0 >> 2)
+#define GT_CS3BOOTR (0x0e8 >> 2)
+
+#define GT_PCI0IOREMAP (0x0f0 >> 2)
+#define GT_PCI0M0REMAP (0x0f8 >> 2)
+#define GT_PCI0M1REMAP (0x100 >> 2)
+#define GT_PCI1IOREMAP (0x108 >> 2)
+#define GT_PCI1M0REMAP (0x110 >> 2)
+#define GT_PCI1M1REMAP (0x118 >> 2)
+
+/* CPU Error Report */
+#define GT_CPUERR_ADDRLO (0x070 >> 2)
+#define GT_CPUERR_ADDRHI (0x078 >> 2)
+#define GT_CPUERR_DATALO (0x128 >> 2) /* GT-64120A only */
+#define GT_CPUERR_DATAHI (0x130 >> 2) /* GT-64120A only */
+#define GT_CPUERR_PARITY (0x138 >> 2) /* GT-64120A only */
+
+/* CPU Sync Barrier */
+#define GT_PCI0SYNC (0x0c0 >> 2)
+#define GT_PCI1SYNC (0x0c8 >> 2)
+
+/* SDRAM and Device Address Decode */
+#define GT_SCS0LD (0x400 >> 2)
+#define GT_SCS0HD (0x404 >> 2)
+#define GT_SCS1LD (0x408 >> 2)
+#define GT_SCS1HD (0x40c >> 2)
+#define GT_SCS2LD (0x410 >> 2)
+#define GT_SCS2HD (0x414 >> 2)
+#define GT_SCS3LD (0x418 >> 2)
+#define GT_SCS3HD (0x41c >> 2)
+#define GT_CS0LD (0x420 >> 2)
+#define GT_CS0HD (0x424 >> 2)
+#define GT_CS1LD (0x428 >> 2)
+#define GT_CS1HD (0x42c >> 2)
+#define GT_CS2LD (0x430 >> 2)
+#define GT_CS2HD (0x434 >> 2)
+#define GT_CS3LD (0x438 >> 2)
+#define GT_CS3HD (0x43c >> 2)
+#define GT_BOOTLD (0x440 >> 2)
+#define GT_BOOTHD (0x444 >> 2)
+#define GT_ADERR (0x470 >> 2)
+
+/* SDRAM Configuration */
+#define GT_SDRAM_CFG (0x448 >> 2)
+#define GT_SDRAM_OPMODE (0x474 >> 2)
+#define GT_SDRAM_BM (0x478 >> 2)
+#define GT_SDRAM_ADDRDECODE (0x47c >> 2)
+
+/* SDRAM Parameters */
+#define GT_SDRAM_B0 (0x44c >> 2)
+#define GT_SDRAM_B1 (0x450 >> 2)
+#define GT_SDRAM_B2 (0x454 >> 2)
+#define GT_SDRAM_B3 (0x458 >> 2)
+
+/* Device Parameters */
+#define GT_DEV_B0 (0x45c >> 2)
+#define GT_DEV_B1 (0x460 >> 2)
+#define GT_DEV_B2 (0x464 >> 2)
+#define GT_DEV_B3 (0x468 >> 2)
+#define GT_DEV_BOOT (0x46c >> 2)
+
+/* ECC */
+#define GT_ECC_ERRDATALO (0x480 >> 2) /* GT-64120A only */
+#define GT_ECC_ERRDATAHI (0x484 >> 2) /* GT-64120A only */
+#define GT_ECC_MEM (0x488 >> 2) /* GT-64120A only */
+#define GT_ECC_CALC (0x48c >> 2) /* GT-64120A only */
+#define GT_ECC_ERRADDR (0x490 >> 2) /* GT-64120A only */
+
+/* DMA Record */
+#define GT_DMA0_CNT (0x800 >> 2)
+#define GT_DMA1_CNT (0x804 >> 2)
+#define GT_DMA2_CNT (0x808 >> 2)
+#define GT_DMA3_CNT (0x80c >> 2)
+#define GT_DMA0_SA (0x810 >> 2)
+#define GT_DMA1_SA (0x814 >> 2)
+#define GT_DMA2_SA (0x818 >> 2)
+#define GT_DMA3_SA (0x81c >> 2)
+#define GT_DMA0_DA (0x820 >> 2)
+#define GT_DMA1_DA (0x824 >> 2)
+#define GT_DMA2_DA (0x828 >> 2)
+#define GT_DMA3_DA (0x82c >> 2)
+#define GT_DMA0_NEXT (0x830 >> 2)
+#define GT_DMA1_NEXT (0x834 >> 2)
+#define GT_DMA2_NEXT (0x838 >> 2)
+#define GT_DMA3_NEXT (0x83c >> 2)
+#define GT_DMA0_CUR (0x870 >> 2)
+#define GT_DMA1_CUR (0x874 >> 2)
+#define GT_DMA2_CUR (0x878 >> 2)
+#define GT_DMA3_CUR (0x87c >> 2)
+
+/* DMA Channel Control */
+#define GT_DMA0_CTRL (0x840 >> 2)
+#define GT_DMA1_CTRL (0x844 >> 2)
+#define GT_DMA2_CTRL (0x848 >> 2)
+#define GT_DMA3_CTRL (0x84c >> 2)
+
+/* DMA Arbiter */
+#define GT_DMA_ARB (0x860 >> 2)
+
+/* Timer/Counter */
+#define GT_TC0 (0x850 >> 2)
+#define GT_TC1 (0x854 >> 2)
+#define GT_TC2 (0x858 >> 2)
+#define GT_TC3 (0x85c >> 2)
+#define GT_TC_CONTROL (0x864 >> 2)
+
+/* PCI Internal */
+#define GT_PCI0_CMD (0xc00 >> 2)
+#define GT_PCI0_TOR (0xc04 >> 2)
+#define GT_PCI0_BS_SCS10 (0xc08 >> 2)
+#define GT_PCI0_BS_SCS32 (0xc0c >> 2)
+#define GT_PCI0_BS_CS20 (0xc10 >> 2)
+#define GT_PCI0_BS_CS3BT (0xc14 >> 2)
+#define GT_PCI1_IACK (0xc30 >> 2)
+#define GT_PCI0_IACK (0xc34 >> 2)
+#define GT_PCI0_BARE (0xc3c >> 2)
+#define GT_PCI0_PREFMBR (0xc40 >> 2)
+#define GT_PCI0_SCS10_BAR (0xc48 >> 2)
+#define GT_PCI0_SCS32_BAR (0xc4c >> 2)
+#define GT_PCI0_CS20_BAR (0xc50 >> 2)
+#define GT_PCI0_CS3BT_BAR (0xc54 >> 2)
+#define GT_PCI0_SSCS10_BAR (0xc58 >> 2)
+#define GT_PCI0_SSCS32_BAR (0xc5c >> 2)
+#define GT_PCI0_SCS3BT_BAR (0xc64 >> 2)
+#define GT_PCI1_CMD (0xc80 >> 2)
+#define GT_PCI1_TOR (0xc84 >> 2)
+#define GT_PCI1_BS_SCS10 (0xc88 >> 2)
+#define GT_PCI1_BS_SCS32 (0xc8c >> 2)
+#define GT_PCI1_BS_CS20 (0xc90 >> 2)
+#define GT_PCI1_BS_CS3BT (0xc94 >> 2)
+#define GT_PCI1_BARE (0xcbc >> 2)
+#define GT_PCI1_PREFMBR (0xcc0 >> 2)
+#define GT_PCI1_SCS10_BAR (0xcc8 >> 2)
+#define GT_PCI1_SCS32_BAR (0xccc >> 2)
+#define GT_PCI1_CS20_BAR (0xcd0 >> 2)
+#define GT_PCI1_CS3BT_BAR (0xcd4 >> 2)
+#define GT_PCI1_SSCS10_BAR (0xcd8 >> 2)
+#define GT_PCI1_SSCS32_BAR (0xcdc >> 2)
+#define GT_PCI1_SCS3BT_BAR (0xce4 >> 2)
+#define GT_PCI1_CFGADDR (0xcf0 >> 2)
+#define GT_PCI1_CFGDATA (0xcf4 >> 2)
+#define GT_PCI0_CFGADDR (0xcf8 >> 2)
+#define GT_PCI0_CFGDATA (0xcfc >> 2)
+
+/* Interrupts */
+#define GT_INTRCAUSE (0xc18 >> 2)
+#define GT_INTRMASK (0xc1c >> 2)
+#define GT_PCI0_ICMASK (0xc24 >> 2)
+#define GT_PCI0_SERR0MASK (0xc28 >> 2)
+#define GT_CPU_INTSEL (0xc70 >> 2)
+#define GT_PCI0_INTSEL (0xc74 >> 2)
+#define GT_HINTRCAUSE (0xc98 >> 2)
+#define GT_HINTRMASK (0xc9c >> 2)
+#define GT_PCI0_HICMASK (0xca4 >> 2)
+#define GT_PCI1_SERR1MASK (0xca8 >> 2)
+
+#define PCI_MAPPING_ENTRY(regname) \
+ hwaddr regname ##_start; \
+ hwaddr regname ##_length; \
+ MemoryRegion regname ##_mem
+
+#define TYPE_GT64120_PCI_HOST_BRIDGE "gt64120"
+
+#define GT64120_PCI_HOST_BRIDGE(obj) \
+ OBJECT_CHECK(GT64120State, (obj), TYPE_GT64120_PCI_HOST_BRIDGE)
+
+typedef struct GT64120State {
+ PCIHostState parent_obj;
+
+ uint32_t regs[GT_REGS];
+ PCI_MAPPING_ENTRY(PCI0IO);
+ PCI_MAPPING_ENTRY(PCI0M0);
+ PCI_MAPPING_ENTRY(PCI0M1);
+ PCI_MAPPING_ENTRY(ISD);
+ MemoryRegion pci0_mem;
+ AddressSpace pci0_mem_as;
+} GT64120State;
+
+/* Adjust range to avoid touching space which isn't mappable via PCI */
+/* XXX: Hardcoded values for Malta: 0x1e000000 - 0x1f100000
+ 0x1fc00000 - 0x1fd00000 */
+static void check_reserved_space (hwaddr *start,
+ hwaddr *length)
+{
+ hwaddr begin = *start;
+ hwaddr end = *start + *length;
+
+ if (end >= 0x1e000000LL && end < 0x1f100000LL)
+ end = 0x1e000000LL;
+ if (begin >= 0x1e000000LL && begin < 0x1f100000LL)
+ begin = 0x1f100000LL;
+ if (end >= 0x1fc00000LL && end < 0x1fd00000LL)
+ end = 0x1fc00000LL;
+ if (begin >= 0x1fc00000LL && begin < 0x1fd00000LL)
+ begin = 0x1fd00000LL;
+ /* XXX: This is broken when a reserved range splits the requested range */
+ if (end >= 0x1f100000LL && begin < 0x1e000000LL)
+ end = 0x1e000000LL;
+ if (end >= 0x1fd00000LL && begin < 0x1fc00000LL)
+ end = 0x1fc00000LL;
+
+ *start = begin;
+ *length = end - begin;
+}
+
+static void gt64120_isd_mapping(GT64120State *s)
+{
+ hwaddr start = s->regs[GT_ISD] << 21;
+ hwaddr length = 0x1000;
+
+ if (s->ISD_length) {
+ memory_region_del_subregion(get_system_memory(), &s->ISD_mem);
+ }
+ check_reserved_space(&start, &length);
+ length = 0x1000;
+ /* Map new address */
+ DPRINTF("ISD: "TARGET_FMT_plx"@"TARGET_FMT_plx
+ " -> "TARGET_FMT_plx"@"TARGET_FMT_plx"\n",
+ s->ISD_length, s->ISD_start, length, start);
+ s->ISD_start = start;
+ s->ISD_length = length;
+ memory_region_add_subregion(get_system_memory(), s->ISD_start, &s->ISD_mem);
+}
+
+static void gt64120_pci_mapping(GT64120State *s)
+{
+ /* Update PCI0IO mapping */
+ if ((s->regs[GT_PCI0IOLD] & 0x7f) <= s->regs[GT_PCI0IOHD]) {
+ /* Unmap old IO address */
+ if (s->PCI0IO_length) {
+ memory_region_del_subregion(get_system_memory(), &s->PCI0IO_mem);
+ object_unparent(OBJECT(&s->PCI0IO_mem));
+ }
+ /* Map new IO address */
+ s->PCI0IO_start = s->regs[GT_PCI0IOLD] << 21;
+ s->PCI0IO_length = ((s->regs[GT_PCI0IOHD] + 1) -
+ (s->regs[GT_PCI0IOLD] & 0x7f)) << 21;
+ if (s->PCI0IO_length) {
+ memory_region_init_alias(&s->PCI0IO_mem, OBJECT(s), "pci0-io",
+ get_system_io(), 0, s->PCI0IO_length);
+ memory_region_add_subregion(get_system_memory(), s->PCI0IO_start,
+ &s->PCI0IO_mem);
+ }
+ }
+
+ /* Update PCI0M0 mapping */
+ if ((s->regs[GT_PCI0M0LD] & 0x7f) <= s->regs[GT_PCI0M0HD]) {
+ /* Unmap old MEM address */
+ if (s->PCI0M0_length) {
+ memory_region_del_subregion(get_system_memory(), &s->PCI0M0_mem);
+ object_unparent(OBJECT(&s->PCI0M0_mem));
+ }
+ /* Map new mem address */
+ s->PCI0M0_start = s->regs[GT_PCI0M0LD] << 21;
+ s->PCI0M0_length = ((s->regs[GT_PCI0M0HD] + 1) -
+ (s->regs[GT_PCI0M0LD] & 0x7f)) << 21;
+ if (s->PCI0M0_length) {
+ memory_region_init_alias(&s->PCI0M0_mem, OBJECT(s), "pci0-mem0",
+ &s->pci0_mem, s->PCI0M0_start,
+ s->PCI0M0_length);
+ memory_region_add_subregion(get_system_memory(), s->PCI0M0_start,
+ &s->PCI0M0_mem);
+ }
+ }
+
+ /* Update PCI0M1 mapping */
+ if ((s->regs[GT_PCI0M1LD] & 0x7f) <= s->regs[GT_PCI0M1HD]) {
+ /* Unmap old MEM address */
+ if (s->PCI0M1_length) {
+ memory_region_del_subregion(get_system_memory(), &s->PCI0M1_mem);
+ object_unparent(OBJECT(&s->PCI0M1_mem));
+ }
+ /* Map new mem address */
+ s->PCI0M1_start = s->regs[GT_PCI0M1LD] << 21;
+ s->PCI0M1_length = ((s->regs[GT_PCI0M1HD] + 1) -
+ (s->regs[GT_PCI0M1LD] & 0x7f)) << 21;
+ if (s->PCI0M1_length) {
+ memory_region_init_alias(&s->PCI0M1_mem, OBJECT(s), "pci0-mem1",
+ &s->pci0_mem, s->PCI0M1_start,
+ s->PCI0M1_length);
+ memory_region_add_subregion(get_system_memory(), s->PCI0M1_start,
+ &s->PCI0M1_mem);
+ }
+ }
+}
+
+static int gt64120_post_load(void *opaque, int version_id)
+{
+ GT64120State *s = opaque;
+
+ gt64120_isd_mapping(s);
+ gt64120_pci_mapping(s);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_gt64120 = {
+ .name = "gt64120",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = gt64120_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, GT64120State, GT_REGS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void gt64120_writel (void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ GT64120State *s = opaque;
+ PCIHostState *phb = PCI_HOST_BRIDGE(s);
+ uint32_t saddr;
+
+ if (!(s->regs[GT_CPU] & 0x00001000))
+ val = bswap32(val);
+
+ saddr = (addr & 0xfff) >> 2;
+ switch (saddr) {
+
+ /* CPU Configuration */
+ case GT_CPU:
+ s->regs[GT_CPU] = val;
+ break;
+ case GT_MULTI:
+ /* Read-only register as only one GT64xxx is present on the CPU bus */
+ break;
+
+ /* CPU Address Decode */
+ case GT_PCI0IOLD:
+ s->regs[GT_PCI0IOLD] = val & 0x00007fff;
+ s->regs[GT_PCI0IOREMAP] = val & 0x000007ff;
+ gt64120_pci_mapping(s);
+ break;
+ case GT_PCI0M0LD:
+ s->regs[GT_PCI0M0LD] = val & 0x00007fff;
+ s->regs[GT_PCI0M0REMAP] = val & 0x000007ff;
+ gt64120_pci_mapping(s);
+ break;
+ case GT_PCI0M1LD:
+ s->regs[GT_PCI0M1LD] = val & 0x00007fff;
+ s->regs[GT_PCI0M1REMAP] = val & 0x000007ff;
+ gt64120_pci_mapping(s);
+ break;
+ case GT_PCI1IOLD:
+ s->regs[GT_PCI1IOLD] = val & 0x00007fff;
+ s->regs[GT_PCI1IOREMAP] = val & 0x000007ff;
+ break;
+ case GT_PCI1M0LD:
+ s->regs[GT_PCI1M0LD] = val & 0x00007fff;
+ s->regs[GT_PCI1M0REMAP] = val & 0x000007ff;
+ break;
+ case GT_PCI1M1LD:
+ s->regs[GT_PCI1M1LD] = val & 0x00007fff;
+ s->regs[GT_PCI1M1REMAP] = val & 0x000007ff;
+ break;
+ case GT_PCI0M0HD:
+ case GT_PCI0M1HD:
+ case GT_PCI0IOHD:
+ s->regs[saddr] = val & 0x0000007f;
+ gt64120_pci_mapping(s);
+ break;
+ case GT_PCI1IOHD:
+ case GT_PCI1M0HD:
+ case GT_PCI1M1HD:
+ s->regs[saddr] = val & 0x0000007f;
+ break;
+ case GT_ISD:
+ s->regs[saddr] = val & 0x00007fff;
+ gt64120_isd_mapping(s);
+ break;
+
+ case GT_PCI0IOREMAP:
+ case GT_PCI0M0REMAP:
+ case GT_PCI0M1REMAP:
+ case GT_PCI1IOREMAP:
+ case GT_PCI1M0REMAP:
+ case GT_PCI1M1REMAP:
+ s->regs[saddr] = val & 0x000007ff;
+ break;
+
+ /* CPU Error Report */
+ case GT_CPUERR_ADDRLO:
+ case GT_CPUERR_ADDRHI:
+ case GT_CPUERR_DATALO:
+ case GT_CPUERR_DATAHI:
+ case GT_CPUERR_PARITY:
+ /* Read-only registers, do nothing */
+ break;
+
+ /* CPU Sync Barrier */
+ case GT_PCI0SYNC:
+ case GT_PCI1SYNC:
+ /* Read-only registers, do nothing */
+ break;
+
+ /* SDRAM and Device Address Decode */
+ case GT_SCS0LD:
+ case GT_SCS0HD:
+ case GT_SCS1LD:
+ case GT_SCS1HD:
+ case GT_SCS2LD:
+ case GT_SCS2HD:
+ case GT_SCS3LD:
+ case GT_SCS3HD:
+ case GT_CS0LD:
+ case GT_CS0HD:
+ case GT_CS1LD:
+ case GT_CS1HD:
+ case GT_CS2LD:
+ case GT_CS2HD:
+ case GT_CS3LD:
+ case GT_CS3HD:
+ case GT_BOOTLD:
+ case GT_BOOTHD:
+ case GT_ADERR:
+ /* SDRAM Configuration */
+ case GT_SDRAM_CFG:
+ case GT_SDRAM_OPMODE:
+ case GT_SDRAM_BM:
+ case GT_SDRAM_ADDRDECODE:
+ /* Accept and ignore SDRAM interleave configuration */
+ s->regs[saddr] = val;
+ break;
+
+ /* Device Parameters */
+ case GT_DEV_B0:
+ case GT_DEV_B1:
+ case GT_DEV_B2:
+ case GT_DEV_B3:
+ case GT_DEV_BOOT:
+ /* Not implemented */
+ DPRINTF ("Unimplemented device register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* ECC */
+ case GT_ECC_ERRDATALO:
+ case GT_ECC_ERRDATAHI:
+ case GT_ECC_MEM:
+ case GT_ECC_CALC:
+ case GT_ECC_ERRADDR:
+ /* Read-only registers, do nothing */
+ break;
+
+ /* DMA Record */
+ case GT_DMA0_CNT:
+ case GT_DMA1_CNT:
+ case GT_DMA2_CNT:
+ case GT_DMA3_CNT:
+ case GT_DMA0_SA:
+ case GT_DMA1_SA:
+ case GT_DMA2_SA:
+ case GT_DMA3_SA:
+ case GT_DMA0_DA:
+ case GT_DMA1_DA:
+ case GT_DMA2_DA:
+ case GT_DMA3_DA:
+ case GT_DMA0_NEXT:
+ case GT_DMA1_NEXT:
+ case GT_DMA2_NEXT:
+ case GT_DMA3_NEXT:
+ case GT_DMA0_CUR:
+ case GT_DMA1_CUR:
+ case GT_DMA2_CUR:
+ case GT_DMA3_CUR:
+ /* Not implemented */
+ DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* DMA Channel Control */
+ case GT_DMA0_CTRL:
+ case GT_DMA1_CTRL:
+ case GT_DMA2_CTRL:
+ case GT_DMA3_CTRL:
+ /* Not implemented */
+ DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* DMA Arbiter */
+ case GT_DMA_ARB:
+ /* Not implemented */
+ DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* Timer/Counter */
+ case GT_TC0:
+ case GT_TC1:
+ case GT_TC2:
+ case GT_TC3:
+ case GT_TC_CONTROL:
+ /* Not implemented */
+ DPRINTF ("Unimplemented timer register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* PCI Internal */
+ case GT_PCI0_CMD:
+ case GT_PCI1_CMD:
+ s->regs[saddr] = val & 0x0401fc0f;
+ break;
+ case GT_PCI0_TOR:
+ case GT_PCI0_BS_SCS10:
+ case GT_PCI0_BS_SCS32:
+ case GT_PCI0_BS_CS20:
+ case GT_PCI0_BS_CS3BT:
+ case GT_PCI1_IACK:
+ case GT_PCI0_IACK:
+ case GT_PCI0_BARE:
+ case GT_PCI0_PREFMBR:
+ case GT_PCI0_SCS10_BAR:
+ case GT_PCI0_SCS32_BAR:
+ case GT_PCI0_CS20_BAR:
+ case GT_PCI0_CS3BT_BAR:
+ case GT_PCI0_SSCS10_BAR:
+ case GT_PCI0_SSCS32_BAR:
+ case GT_PCI0_SCS3BT_BAR:
+ case GT_PCI1_TOR:
+ case GT_PCI1_BS_SCS10:
+ case GT_PCI1_BS_SCS32:
+ case GT_PCI1_BS_CS20:
+ case GT_PCI1_BS_CS3BT:
+ case GT_PCI1_BARE:
+ case GT_PCI1_PREFMBR:
+ case GT_PCI1_SCS10_BAR:
+ case GT_PCI1_SCS32_BAR:
+ case GT_PCI1_CS20_BAR:
+ case GT_PCI1_CS3BT_BAR:
+ case GT_PCI1_SSCS10_BAR:
+ case GT_PCI1_SSCS32_BAR:
+ case GT_PCI1_SCS3BT_BAR:
+ case GT_PCI1_CFGADDR:
+ case GT_PCI1_CFGDATA:
+ /* not implemented */
+ break;
+ case GT_PCI0_CFGADDR:
+ phb->config_reg = val & 0x80fffffc;
+ break;
+ case GT_PCI0_CFGDATA:
+ if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) {
+ val = bswap32(val);
+ }
+ if (phb->config_reg & (1u << 31)) {
+ pci_data_write(phb->bus, phb->config_reg, val, 4);
+ }
+ break;
+
+ /* Interrupts */
+ case GT_INTRCAUSE:
+ /* not really implemented */
+ s->regs[saddr] = ~(~(s->regs[saddr]) | ~(val & 0xfffffffe));
+ s->regs[saddr] |= !!(s->regs[saddr] & 0xfffffffe);
+ DPRINTF("INTRCAUSE %" PRIx64 "\n", val);
+ break;
+ case GT_INTRMASK:
+ s->regs[saddr] = val & 0x3c3ffffe;
+ DPRINTF("INTRMASK %" PRIx64 "\n", val);
+ break;
+ case GT_PCI0_ICMASK:
+ s->regs[saddr] = val & 0x03fffffe;
+ DPRINTF("ICMASK %" PRIx64 "\n", val);
+ break;
+ case GT_PCI0_SERR0MASK:
+ s->regs[saddr] = val & 0x0000003f;
+ DPRINTF("SERR0MASK %" PRIx64 "\n", val);
+ break;
+
+ /* Reserved when only PCI_0 is configured. */
+ case GT_HINTRCAUSE:
+ case GT_CPU_INTSEL:
+ case GT_PCI0_INTSEL:
+ case GT_HINTRMASK:
+ case GT_PCI0_HICMASK:
+ case GT_PCI1_SERR1MASK:
+ /* not implemented */
+ break;
+
+ /* SDRAM Parameters */
+ case GT_SDRAM_B0:
+ case GT_SDRAM_B1:
+ case GT_SDRAM_B2:
+ case GT_SDRAM_B3:
+ /* We don't simulate electrical parameters of the SDRAM.
+ Accept, but ignore the values. */
+ s->regs[saddr] = val;
+ break;
+
+ default:
+ DPRINTF ("Bad register offset 0x%x\n", (int)addr);
+ break;
+ }
+}
+
+static uint64_t gt64120_readl (void *opaque,
+ hwaddr addr, unsigned size)
+{
+ GT64120State *s = opaque;
+ PCIHostState *phb = PCI_HOST_BRIDGE(s);
+ uint32_t val;
+ uint32_t saddr;
+
+ saddr = (addr & 0xfff) >> 2;
+ switch (saddr) {
+
+ /* CPU Configuration */
+ case GT_MULTI:
+ /* Only one GT64xxx is present on the CPU bus, return
+ the initial value */
+ val = s->regs[saddr];
+ break;
+
+ /* CPU Error Report */
+ case GT_CPUERR_ADDRLO:
+ case GT_CPUERR_ADDRHI:
+ case GT_CPUERR_DATALO:
+ case GT_CPUERR_DATAHI:
+ case GT_CPUERR_PARITY:
+ /* Emulated memory has no error, always return the initial
+ values */
+ val = s->regs[saddr];
+ break;
+
+ /* CPU Sync Barrier */
+ case GT_PCI0SYNC:
+ case GT_PCI1SYNC:
+ /* Reading those register should empty all FIFO on the PCI
+ bus, which are not emulated. The return value should be
+ a random value that should be ignored. */
+ val = 0xc000ffee;
+ break;
+
+ /* ECC */
+ case GT_ECC_ERRDATALO:
+ case GT_ECC_ERRDATAHI:
+ case GT_ECC_MEM:
+ case GT_ECC_CALC:
+ case GT_ECC_ERRADDR:
+ /* Emulated memory has no error, always return the initial
+ values */
+ val = s->regs[saddr];
+ break;
+
+ case GT_CPU:
+ case GT_SCS10LD:
+ case GT_SCS10HD:
+ case GT_SCS32LD:
+ case GT_SCS32HD:
+ case GT_CS20LD:
+ case GT_CS20HD:
+ case GT_CS3BOOTLD:
+ case GT_CS3BOOTHD:
+ case GT_SCS10AR:
+ case GT_SCS32AR:
+ case GT_CS20R:
+ case GT_CS3BOOTR:
+ case GT_PCI0IOLD:
+ case GT_PCI0M0LD:
+ case GT_PCI0M1LD:
+ case GT_PCI1IOLD:
+ case GT_PCI1M0LD:
+ case GT_PCI1M1LD:
+ case GT_PCI0IOHD:
+ case GT_PCI0M0HD:
+ case GT_PCI0M1HD:
+ case GT_PCI1IOHD:
+ case GT_PCI1M0HD:
+ case GT_PCI1M1HD:
+ case GT_PCI0IOREMAP:
+ case GT_PCI0M0REMAP:
+ case GT_PCI0M1REMAP:
+ case GT_PCI1IOREMAP:
+ case GT_PCI1M0REMAP:
+ case GT_PCI1M1REMAP:
+ case GT_ISD:
+ val = s->regs[saddr];
+ break;
+ case GT_PCI0_IACK:
+ /* Read the IRQ number */
+ val = pic_read_irq(isa_pic);
+ break;
+
+ /* SDRAM and Device Address Decode */
+ case GT_SCS0LD:
+ case GT_SCS0HD:
+ case GT_SCS1LD:
+ case GT_SCS1HD:
+ case GT_SCS2LD:
+ case GT_SCS2HD:
+ case GT_SCS3LD:
+ case GT_SCS3HD:
+ case GT_CS0LD:
+ case GT_CS0HD:
+ case GT_CS1LD:
+ case GT_CS1HD:
+ case GT_CS2LD:
+ case GT_CS2HD:
+ case GT_CS3LD:
+ case GT_CS3HD:
+ case GT_BOOTLD:
+ case GT_BOOTHD:
+ case GT_ADERR:
+ val = s->regs[saddr];
+ break;
+
+ /* SDRAM Configuration */
+ case GT_SDRAM_CFG:
+ case GT_SDRAM_OPMODE:
+ case GT_SDRAM_BM:
+ case GT_SDRAM_ADDRDECODE:
+ val = s->regs[saddr];
+ break;
+
+ /* SDRAM Parameters */
+ case GT_SDRAM_B0:
+ case GT_SDRAM_B1:
+ case GT_SDRAM_B2:
+ case GT_SDRAM_B3:
+ /* We don't simulate electrical parameters of the SDRAM.
+ Just return the last written value. */
+ val = s->regs[saddr];
+ break;
+
+ /* Device Parameters */
+ case GT_DEV_B0:
+ case GT_DEV_B1:
+ case GT_DEV_B2:
+ case GT_DEV_B3:
+ case GT_DEV_BOOT:
+ val = s->regs[saddr];
+ break;
+
+ /* DMA Record */
+ case GT_DMA0_CNT:
+ case GT_DMA1_CNT:
+ case GT_DMA2_CNT:
+ case GT_DMA3_CNT:
+ case GT_DMA0_SA:
+ case GT_DMA1_SA:
+ case GT_DMA2_SA:
+ case GT_DMA3_SA:
+ case GT_DMA0_DA:
+ case GT_DMA1_DA:
+ case GT_DMA2_DA:
+ case GT_DMA3_DA:
+ case GT_DMA0_NEXT:
+ case GT_DMA1_NEXT:
+ case GT_DMA2_NEXT:
+ case GT_DMA3_NEXT:
+ case GT_DMA0_CUR:
+ case GT_DMA1_CUR:
+ case GT_DMA2_CUR:
+ case GT_DMA3_CUR:
+ val = s->regs[saddr];
+ break;
+
+ /* DMA Channel Control */
+ case GT_DMA0_CTRL:
+ case GT_DMA1_CTRL:
+ case GT_DMA2_CTRL:
+ case GT_DMA3_CTRL:
+ val = s->regs[saddr];
+ break;
+
+ /* DMA Arbiter */
+ case GT_DMA_ARB:
+ val = s->regs[saddr];
+ break;
+
+ /* Timer/Counter */
+ case GT_TC0:
+ case GT_TC1:
+ case GT_TC2:
+ case GT_TC3:
+ case GT_TC_CONTROL:
+ val = s->regs[saddr];
+ break;
+
+ /* PCI Internal */
+ case GT_PCI0_CFGADDR:
+ val = phb->config_reg;
+ break;
+ case GT_PCI0_CFGDATA:
+ if (!(phb->config_reg & (1 << 31))) {
+ val = 0xffffffff;
+ } else {
+ val = pci_data_read(phb->bus, phb->config_reg, 4);
+ }
+ if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) {
+ val = bswap32(val);
+ }
+ break;
+
+ case GT_PCI0_CMD:
+ case GT_PCI0_TOR:
+ case GT_PCI0_BS_SCS10:
+ case GT_PCI0_BS_SCS32:
+ case GT_PCI0_BS_CS20:
+ case GT_PCI0_BS_CS3BT:
+ case GT_PCI1_IACK:
+ case GT_PCI0_BARE:
+ case GT_PCI0_PREFMBR:
+ case GT_PCI0_SCS10_BAR:
+ case GT_PCI0_SCS32_BAR:
+ case GT_PCI0_CS20_BAR:
+ case GT_PCI0_CS3BT_BAR:
+ case GT_PCI0_SSCS10_BAR:
+ case GT_PCI0_SSCS32_BAR:
+ case GT_PCI0_SCS3BT_BAR:
+ case GT_PCI1_CMD:
+ case GT_PCI1_TOR:
+ case GT_PCI1_BS_SCS10:
+ case GT_PCI1_BS_SCS32:
+ case GT_PCI1_BS_CS20:
+ case GT_PCI1_BS_CS3BT:
+ case GT_PCI1_BARE:
+ case GT_PCI1_PREFMBR:
+ case GT_PCI1_SCS10_BAR:
+ case GT_PCI1_SCS32_BAR:
+ case GT_PCI1_CS20_BAR:
+ case GT_PCI1_CS3BT_BAR:
+ case GT_PCI1_SSCS10_BAR:
+ case GT_PCI1_SSCS32_BAR:
+ case GT_PCI1_SCS3BT_BAR:
+ case GT_PCI1_CFGADDR:
+ case GT_PCI1_CFGDATA:
+ val = s->regs[saddr];
+ break;
+
+ /* Interrupts */
+ case GT_INTRCAUSE:
+ val = s->regs[saddr];
+ DPRINTF("INTRCAUSE %x\n", val);
+ break;
+ case GT_INTRMASK:
+ val = s->regs[saddr];
+ DPRINTF("INTRMASK %x\n", val);
+ break;
+ case GT_PCI0_ICMASK:
+ val = s->regs[saddr];
+ DPRINTF("ICMASK %x\n", val);
+ break;
+ case GT_PCI0_SERR0MASK:
+ val = s->regs[saddr];
+ DPRINTF("SERR0MASK %x\n", val);
+ break;
+
+ /* Reserved when only PCI_0 is configured. */
+ case GT_HINTRCAUSE:
+ case GT_CPU_INTSEL:
+ case GT_PCI0_INTSEL:
+ case GT_HINTRMASK:
+ case GT_PCI0_HICMASK:
+ case GT_PCI1_SERR1MASK:
+ val = s->regs[saddr];
+ break;
+
+ default:
+ val = s->regs[saddr];
+ DPRINTF ("Bad register offset 0x%x\n", (int)addr);
+ break;
+ }
+
+ if (!(s->regs[GT_CPU] & 0x00001000))
+ val = bswap32(val);
+
+ return val;
+}
+
+static const MemoryRegionOps isd_mem_ops = {
+ .read = gt64120_readl,
+ .write = gt64120_writel,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot;
+
+ slot = (pci_dev->devfn >> 3);
+
+ switch (slot) {
+ /* PIIX4 USB */
+ case 10:
+ return 3;
+ /* AMD 79C973 Ethernet */
+ case 11:
+ return 1;
+ /* Crystal 4281 Sound */
+ case 12:
+ return 2;
+ /* PCI slot 1 to 4 */
+ case 18 ... 21:
+ return ((slot - 18) + irq_num) & 0x03;
+ /* Unknown device, don't do any translation */
+ default:
+ return irq_num;
+ }
+}
+
+static int pci_irq_levels[4];
+
+static void gt64120_pci_set_irq(void *opaque, int irq_num, int level)
+{
+ int i, pic_irq, pic_level;
+ qemu_irq *pic = opaque;
+
+ pci_irq_levels[irq_num] = level;
+
+ /* now we change the pic irq level according to the piix irq mappings */
+ /* XXX: optimize */
+ pic_irq = piix4_dev->config[0x60 + irq_num];
+ if (pic_irq < 16) {
+ /* The pic level is the logical OR of all the PCI irqs mapped
+ to it */
+ pic_level = 0;
+ for (i = 0; i < 4; i++) {
+ if (pic_irq == piix4_dev->config[0x60 + i])
+ pic_level |= pci_irq_levels[i];
+ }
+ qemu_set_irq(pic[pic_irq], pic_level);
+ }
+}
+
+
+static void gt64120_reset(void *opaque)
+{
+ GT64120State *s = opaque;
+
+ /* FIXME: Malta specific hw assumptions ahead */
+
+ /* CPU Configuration */
+#ifdef TARGET_WORDS_BIGENDIAN
+ s->regs[GT_CPU] = 0x00000000;
+#else
+ s->regs[GT_CPU] = 0x00001000;
+#endif
+ s->regs[GT_MULTI] = 0x00000003;
+
+ /* CPU Address decode */
+ s->regs[GT_SCS10LD] = 0x00000000;
+ s->regs[GT_SCS10HD] = 0x00000007;
+ s->regs[GT_SCS32LD] = 0x00000008;
+ s->regs[GT_SCS32HD] = 0x0000000f;
+ s->regs[GT_CS20LD] = 0x000000e0;
+ s->regs[GT_CS20HD] = 0x00000070;
+ s->regs[GT_CS3BOOTLD] = 0x000000f8;
+ s->regs[GT_CS3BOOTHD] = 0x0000007f;
+
+ s->regs[GT_PCI0IOLD] = 0x00000080;
+ s->regs[GT_PCI0IOHD] = 0x0000000f;
+ s->regs[GT_PCI0M0LD] = 0x00000090;
+ s->regs[GT_PCI0M0HD] = 0x0000001f;
+ s->regs[GT_ISD] = 0x000000a0;
+ s->regs[GT_PCI0M1LD] = 0x00000790;
+ s->regs[GT_PCI0M1HD] = 0x0000001f;
+ s->regs[GT_PCI1IOLD] = 0x00000100;
+ s->regs[GT_PCI1IOHD] = 0x0000000f;
+ s->regs[GT_PCI1M0LD] = 0x00000110;
+ s->regs[GT_PCI1M0HD] = 0x0000001f;
+ s->regs[GT_PCI1M1LD] = 0x00000120;
+ s->regs[GT_PCI1M1HD] = 0x0000002f;
+
+ s->regs[GT_SCS10AR] = 0x00000000;
+ s->regs[GT_SCS32AR] = 0x00000008;
+ s->regs[GT_CS20R] = 0x000000e0;
+ s->regs[GT_CS3BOOTR] = 0x000000f8;
+
+ s->regs[GT_PCI0IOREMAP] = 0x00000080;
+ s->regs[GT_PCI0M0REMAP] = 0x00000090;
+ s->regs[GT_PCI0M1REMAP] = 0x00000790;
+ s->regs[GT_PCI1IOREMAP] = 0x00000100;
+ s->regs[GT_PCI1M0REMAP] = 0x00000110;
+ s->regs[GT_PCI1M1REMAP] = 0x00000120;
+
+ /* CPU Error Report */
+ s->regs[GT_CPUERR_ADDRLO] = 0x00000000;
+ s->regs[GT_CPUERR_ADDRHI] = 0x00000000;
+ s->regs[GT_CPUERR_DATALO] = 0xffffffff;
+ s->regs[GT_CPUERR_DATAHI] = 0xffffffff;
+ s->regs[GT_CPUERR_PARITY] = 0x000000ff;
+
+ /* CPU Sync Barrier */
+ s->regs[GT_PCI0SYNC] = 0x00000000;
+ s->regs[GT_PCI1SYNC] = 0x00000000;
+
+ /* SDRAM and Device Address Decode */
+ s->regs[GT_SCS0LD] = 0x00000000;
+ s->regs[GT_SCS0HD] = 0x00000007;
+ s->regs[GT_SCS1LD] = 0x00000008;
+ s->regs[GT_SCS1HD] = 0x0000000f;
+ s->regs[GT_SCS2LD] = 0x00000010;
+ s->regs[GT_SCS2HD] = 0x00000017;
+ s->regs[GT_SCS3LD] = 0x00000018;
+ s->regs[GT_SCS3HD] = 0x0000001f;
+ s->regs[GT_CS0LD] = 0x000000c0;
+ s->regs[GT_CS0HD] = 0x000000c7;
+ s->regs[GT_CS1LD] = 0x000000c8;
+ s->regs[GT_CS1HD] = 0x000000cf;
+ s->regs[GT_CS2LD] = 0x000000d0;
+ s->regs[GT_CS2HD] = 0x000000df;
+ s->regs[GT_CS3LD] = 0x000000f0;
+ s->regs[GT_CS3HD] = 0x000000fb;
+ s->regs[GT_BOOTLD] = 0x000000fc;
+ s->regs[GT_BOOTHD] = 0x000000ff;
+ s->regs[GT_ADERR] = 0xffffffff;
+
+ /* SDRAM Configuration */
+ s->regs[GT_SDRAM_CFG] = 0x00000200;
+ s->regs[GT_SDRAM_OPMODE] = 0x00000000;
+ s->regs[GT_SDRAM_BM] = 0x00000007;
+ s->regs[GT_SDRAM_ADDRDECODE] = 0x00000002;
+
+ /* SDRAM Parameters */
+ s->regs[GT_SDRAM_B0] = 0x00000005;
+ s->regs[GT_SDRAM_B1] = 0x00000005;
+ s->regs[GT_SDRAM_B2] = 0x00000005;
+ s->regs[GT_SDRAM_B3] = 0x00000005;
+
+ /* ECC */
+ s->regs[GT_ECC_ERRDATALO] = 0x00000000;
+ s->regs[GT_ECC_ERRDATAHI] = 0x00000000;
+ s->regs[GT_ECC_MEM] = 0x00000000;
+ s->regs[GT_ECC_CALC] = 0x00000000;
+ s->regs[GT_ECC_ERRADDR] = 0x00000000;
+
+ /* Device Parameters */
+ s->regs[GT_DEV_B0] = 0x386fffff;
+ s->regs[GT_DEV_B1] = 0x386fffff;
+ s->regs[GT_DEV_B2] = 0x386fffff;
+ s->regs[GT_DEV_B3] = 0x386fffff;
+ s->regs[GT_DEV_BOOT] = 0x146fffff;
+
+ /* DMA registers are all zeroed at reset */
+
+ /* Timer/Counter */
+ s->regs[GT_TC0] = 0xffffffff;
+ s->regs[GT_TC1] = 0x00ffffff;
+ s->regs[GT_TC2] = 0x00ffffff;
+ s->regs[GT_TC3] = 0x00ffffff;
+ s->regs[GT_TC_CONTROL] = 0x00000000;
+
+ /* PCI Internal */
+#ifdef TARGET_WORDS_BIGENDIAN
+ s->regs[GT_PCI0_CMD] = 0x00000000;
+#else
+ s->regs[GT_PCI0_CMD] = 0x00010001;
+#endif
+ s->regs[GT_PCI0_TOR] = 0x0000070f;
+ s->regs[GT_PCI0_BS_SCS10] = 0x00fff000;
+ s->regs[GT_PCI0_BS_SCS32] = 0x00fff000;
+ s->regs[GT_PCI0_BS_CS20] = 0x01fff000;
+ s->regs[GT_PCI0_BS_CS3BT] = 0x00fff000;
+ s->regs[GT_PCI1_IACK] = 0x00000000;
+ s->regs[GT_PCI0_IACK] = 0x00000000;
+ s->regs[GT_PCI0_BARE] = 0x0000000f;
+ s->regs[GT_PCI0_PREFMBR] = 0x00000040;
+ s->regs[GT_PCI0_SCS10_BAR] = 0x00000000;
+ s->regs[GT_PCI0_SCS32_BAR] = 0x01000000;
+ s->regs[GT_PCI0_CS20_BAR] = 0x1c000000;
+ s->regs[GT_PCI0_CS3BT_BAR] = 0x1f000000;
+ s->regs[GT_PCI0_SSCS10_BAR] = 0x00000000;
+ s->regs[GT_PCI0_SSCS32_BAR] = 0x01000000;
+ s->regs[GT_PCI0_SCS3BT_BAR] = 0x1f000000;
+#ifdef TARGET_WORDS_BIGENDIAN
+ s->regs[GT_PCI1_CMD] = 0x00000000;
+#else
+ s->regs[GT_PCI1_CMD] = 0x00010001;
+#endif
+ s->regs[GT_PCI1_TOR] = 0x0000070f;
+ s->regs[GT_PCI1_BS_SCS10] = 0x00fff000;
+ s->regs[GT_PCI1_BS_SCS32] = 0x00fff000;
+ s->regs[GT_PCI1_BS_CS20] = 0x01fff000;
+ s->regs[GT_PCI1_BS_CS3BT] = 0x00fff000;
+ s->regs[GT_PCI1_BARE] = 0x0000000f;
+ s->regs[GT_PCI1_PREFMBR] = 0x00000040;
+ s->regs[GT_PCI1_SCS10_BAR] = 0x00000000;
+ s->regs[GT_PCI1_SCS32_BAR] = 0x01000000;
+ s->regs[GT_PCI1_CS20_BAR] = 0x1c000000;
+ s->regs[GT_PCI1_CS3BT_BAR] = 0x1f000000;
+ s->regs[GT_PCI1_SSCS10_BAR] = 0x00000000;
+ s->regs[GT_PCI1_SSCS32_BAR] = 0x01000000;
+ s->regs[GT_PCI1_SCS3BT_BAR] = 0x1f000000;
+ s->regs[GT_PCI1_CFGADDR] = 0x00000000;
+ s->regs[GT_PCI1_CFGDATA] = 0x00000000;
+ s->regs[GT_PCI0_CFGADDR] = 0x00000000;
+
+ /* Interrupt registers are all zeroed at reset */
+
+ gt64120_isd_mapping(s);
+ gt64120_pci_mapping(s);
+}
+
+PCIBus *gt64120_register(qemu_irq *pic)
+{
+ GT64120State *d;
+ PCIHostState *phb;
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, TYPE_GT64120_PCI_HOST_BRIDGE);
+ qdev_init_nofail(dev);
+ d = GT64120_PCI_HOST_BRIDGE(dev);
+ phb = PCI_HOST_BRIDGE(dev);
+ memory_region_init(&d->pci0_mem, OBJECT(dev), "pci0-mem", UINT32_MAX);
+ address_space_init(&d->pci0_mem_as, &d->pci0_mem, "pci0-mem");
+ phb->bus = pci_register_bus(dev, "pci",
+ gt64120_pci_set_irq, gt64120_pci_map_irq,
+ pic,
+ &d->pci0_mem,
+ get_system_io(),
+ PCI_DEVFN(18, 0), 4, TYPE_PCI_BUS);
+ memory_region_init_io(&d->ISD_mem, OBJECT(dev), &isd_mem_ops, d, "isd-mem", 0x1000);
+
+ pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci");
+ return phb->bus;
+}
+
+static int gt64120_init(SysBusDevice *dev)
+{
+ GT64120State *s;
+
+ s = GT64120_PCI_HOST_BRIDGE(dev);
+
+ qemu_register_reset(gt64120_reset, s);
+ return 0;
+}
+
+static int gt64120_pci_init(PCIDevice *d)
+{
+ /* FIXME: Malta specific hw assumptions ahead */
+ pci_set_word(d->config + PCI_COMMAND, 0);
+ pci_set_word(d->config + PCI_STATUS,
+ PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
+ pci_config_set_prog_interface(d->config, 0);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001);
+ pci_set_byte(d->config + 0x3d, 0x01);
+
+ return 0;
+}
+
+static void gt64120_pci_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ k->init = gt64120_pci_init;
+ k->vendor_id = PCI_VENDOR_ID_MARVELL;
+ k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X;
+ k->revision = 0x10;
+ k->class_id = PCI_CLASS_BRIDGE_HOST;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
+}
+
+static const TypeInfo gt64120_pci_info = {
+ .name = "gt64120_pci",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIDevice),
+ .class_init = gt64120_pci_class_init,
+};
+
+static void gt64120_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = gt64120_init;
+ dc->vmsd = &vmstate_gt64120;
+}
+
+static const TypeInfo gt64120_info = {
+ .name = TYPE_GT64120_PCI_HOST_BRIDGE,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .instance_size = sizeof(GT64120State),
+ .class_init = gt64120_class_init,
+};
+
+static void gt64120_pci_register_types(void)
+{
+ type_register_static(&gt64120_info);
+ type_register_static(&gt64120_pci_info);
+}
+
+type_init(gt64120_pci_register_types)
diff --git a/qemu/hw/mips/mips_fulong2e.c b/qemu/hw/mips/mips_fulong2e.c
new file mode 100644
index 000000000..dea941ad8
--- /dev/null
+++ b/qemu/hw/mips/mips_fulong2e.c
@@ -0,0 +1,406 @@
+/*
+ * QEMU fulong 2e mini pc support
+ *
+ * Copyright (c) 2008 yajin (yajin@vm-kernel.org)
+ * Copyright (c) 2009 chenming (chenming@rdc.faw.com.cn)
+ * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com)
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+/*
+ * Fulong 2e mini pc is based on ICT/ST Loongson 2e CPU (MIPS III like, 800MHz)
+ * http://www.linux-mips.org/wiki/Fulong
+ *
+ * Loongson 2e user manual:
+ * http://www.loongsondeveloper.com/doc/Loongson2EUserGuide.pdf
+ */
+
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/char/serial.h"
+#include "hw/block/fdc.h"
+#include "net/net.h"
+#include "hw/boards.h"
+#include "hw/i2c/smbus.h"
+#include "sysemu/block-backend.h"
+#include "hw/block/flash.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/pci/pci.h"
+#include "sysemu/char.h"
+#include "sysemu/sysemu.h"
+#include "audio/audio.h"
+#include "qemu/log.h"
+#include "hw/loader.h"
+#include "hw/mips/bios.h"
+#include "hw/ide.h"
+#include "elf.h"
+#include "hw/isa/vt82c686.h"
+#include "hw/timer/mc146818rtc.h"
+#include "hw/timer/i8254.h"
+#include "sysemu/blockdev.h"
+#include "exec/address-spaces.h"
+#include "sysemu/qtest.h"
+#include "qemu/error-report.h"
+
+#define DEBUG_FULONG2E_INIT
+
+#define ENVP_ADDR 0x80002000l
+#define ENVP_NB_ENTRIES 16
+#define ENVP_ENTRY_SIZE 256
+
+#define MAX_IDE_BUS 2
+
+/*
+ * PMON is not part of qemu and released with BSD license, anyone
+ * who want to build a pmon binary please first git-clone the source
+ * from the git repository at:
+ * http://www.loongson.cn/support/git/pmon
+ * Then follow the "Compile Guide" available at:
+ * http://dev.lemote.com/code/pmon
+ *
+ * Notes:
+ * 1, don't use the source at http://dev.lemote.com/http_git/pmon.git
+ * 2, use "Bonito2edev" to replace "dir_corresponding_to_your_target_hardware"
+ * in the "Compile Guide".
+ */
+#define FULONG_BIOSNAME "pmon_fulong2e.bin"
+
+/* PCI SLOT in fulong 2e */
+#define FULONG2E_VIA_SLOT 5
+#define FULONG2E_ATI_SLOT 6
+#define FULONG2E_RTL8139_SLOT 7
+
+static ISADevice *pit;
+
+static struct _loaderparams {
+ int ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+} loaderparams;
+
+static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index,
+ const char *string, ...)
+{
+ va_list ap;
+ int32_t table_addr;
+
+ if (index >= ENVP_NB_ENTRIES)
+ return;
+
+ if (string == NULL) {
+ prom_buf[index] = 0;
+ return;
+ }
+
+ table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
+ prom_buf[index] = tswap32(ENVP_ADDR + table_addr);
+
+ va_start(ap, string);
+ vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
+ va_end(ap);
+}
+
+static int64_t load_kernel (CPUMIPSState *env)
+{
+ int64_t kernel_entry, kernel_low, kernel_high;
+ int index = 0;
+ long initrd_size;
+ ram_addr_t initrd_offset;
+ uint32_t *prom_buf;
+ long prom_size;
+
+ if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
+ (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low,
+ (uint64_t *)&kernel_high, 0, ELF_MACHINE, 1) < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ loaderparams.kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size (loaderparams.initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK;
+ if (initrd_offset + initrd_size > ram_size) {
+ fprintf(stderr,
+ "qemu: memory too small for initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset, ram_size - initrd_offset);
+ }
+ if (initrd_size == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* Setup prom parameters. */
+ prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE);
+ prom_buf = g_malloc(prom_size);
+
+ prom_set(prom_buf, index++, "%s", loaderparams.kernel_filename);
+ if (initrd_size > 0) {
+ prom_set(prom_buf, index++, "rd_start=0x%" PRIx64 " rd_size=%li %s",
+ cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size,
+ loaderparams.kernel_cmdline);
+ } else {
+ prom_set(prom_buf, index++, "%s", loaderparams.kernel_cmdline);
+ }
+
+ /* Setup minimum environment variables */
+ prom_set(prom_buf, index++, "busclock=33000000");
+ prom_set(prom_buf, index++, "cpuclock=100000000");
+ prom_set(prom_buf, index++, "memsize=%i", loaderparams.ram_size/1024/1024);
+ prom_set(prom_buf, index++, "modetty0=38400n8r");
+ prom_set(prom_buf, index++, NULL);
+
+ rom_add_blob_fixed("prom", prom_buf, prom_size,
+ cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
+
+ g_free(prom_buf);
+ return kernel_entry;
+}
+
+static void write_bootloader (CPUMIPSState *env, uint8_t *base, int64_t kernel_addr)
+{
+ uint32_t *p;
+
+ /* Small bootloader */
+ p = (uint32_t *) base;
+
+ stl_p(p++, 0x0bf00010); /* j 0x1fc00040 */
+ stl_p(p++, 0x00000000); /* nop */
+
+ /* Second part of the bootloader */
+ p = (uint32_t *) (base + 0x040);
+
+ stl_p(p++, 0x3c040000); /* lui a0, 0 */
+ stl_p(p++, 0x34840002); /* ori a0, a0, 2 */
+ stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */
+ stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a0, low(ENVP_ADDR) */
+ stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */
+ stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */
+ stl_p(p++, 0x3c070000 | (loaderparams.ram_size >> 16)); /* lui a3, high(env->ram_size) */
+ stl_p(p++, 0x34e70000 | (loaderparams.ram_size & 0xffff)); /* ori a3, a3, low(env->ram_size) */
+ stl_p(p++, 0x3c1f0000 | ((kernel_addr >> 16) & 0xffff)); /* lui ra, high(kernel_addr) */;
+ stl_p(p++, 0x37ff0000 | (kernel_addr & 0xffff)); /* ori ra, ra, low(kernel_addr) */
+ stl_p(p++, 0x03e00008); /* jr ra */
+ stl_p(p++, 0x00000000); /* nop */
+}
+
+
+static void main_cpu_reset(void *opaque)
+{
+ MIPSCPU *cpu = opaque;
+ CPUMIPSState *env = &cpu->env;
+
+ cpu_reset(CPU(cpu));
+ /* TODO: 2E reset stuff */
+ if (loaderparams.kernel_filename) {
+ env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL));
+ }
+}
+
+static const uint8_t eeprom_spd[0x80] = {
+ 0x80,0x08,0x07,0x0d,0x09,0x02,0x40,0x00,0x04,0x70,
+ 0x70,0x00,0x82,0x10,0x00,0x01,0x0e,0x04,0x0c,0x01,
+ 0x02,0x20,0x80,0x75,0x70,0x00,0x00,0x50,0x3c,0x50,
+ 0x2d,0x20,0xb0,0xb0,0x50,0x50,0x00,0x00,0x00,0x00,
+ 0x00,0x41,0x48,0x3c,0x32,0x75,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x9c,0x7b,0x07,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x48,0x42,0x35,0x34,0x41,0x32,
+ 0x35,0x36,0x38,0x4b,0x4e,0x2d,0x41,0x37,0x35,0x42,
+ 0x20,0x30,0x20
+};
+
+/* Audio support */
+static void audio_init (PCIBus *pci_bus)
+{
+ vt82c686b_ac97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 5));
+ vt82c686b_mc97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 6));
+}
+
+/* Network support */
+static void network_init (PCIBus *pci_bus)
+{
+ int i;
+
+ for(i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+ const char *default_devaddr = NULL;
+
+ if (i == 0 && (!nd->model || strcmp(nd->model, "rtl8139") == 0)) {
+ /* The fulong board has a RTL8139 card using PCI SLOT 7 */
+ default_devaddr = "07";
+ }
+
+ pci_nic_init_nofail(nd, pci_bus, "rtl8139", default_devaddr);
+ }
+}
+
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+ CPUState *cpu = current_cpu;
+
+ if (cpu && level) {
+ cpu_exit(cpu);
+ }
+}
+
+static void mips_fulong2e_init(MachineState *machine)
+{
+ ram_addr_t ram_size = machine->ram_size;
+ const char *cpu_model = machine->cpu_model;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *initrd_filename = machine->initrd_filename;
+ char *filename;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *bios = g_new(MemoryRegion, 1);
+ long bios_size;
+ int64_t kernel_entry;
+ qemu_irq *i8259;
+ qemu_irq *cpu_exit_irq;
+ PCIBus *pci_bus;
+ ISABus *isa_bus;
+ I2CBus *smbus;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ MIPSCPU *cpu;
+ CPUMIPSState *env;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "Loongson-2E";
+ }
+ cpu = cpu_mips_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ env = &cpu->env;
+
+ qemu_register_reset(main_cpu_reset, cpu);
+
+ /* fulong 2e has 256M ram. */
+ ram_size = 256 * 1024 * 1024;
+
+ /* fulong 2e has a 1M flash.Winbond W39L040AP70Z */
+ bios_size = 1024 * 1024;
+
+ /* allocate RAM */
+ memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size);
+ memory_region_init_ram(bios, NULL, "fulong2e.bios", bios_size,
+ &error_abort);
+ vmstate_register_ram_global(bios);
+ memory_region_set_readonly(bios, true);
+
+ memory_region_add_subregion(address_space_mem, 0, ram);
+ memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios);
+
+ /* We do not support flash operation, just loading pmon.bin as raw BIOS.
+ * Please use -L to set the BIOS path and -bios to set bios name. */
+
+ if (kernel_filename) {
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ kernel_entry = load_kernel (env);
+ write_bootloader(env, memory_region_get_ram_ptr(bios), kernel_entry);
+ } else {
+ if (bios_name == NULL) {
+ bios_name = FULONG_BIOSNAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image_targphys(filename, 0x1fc00000LL,
+ BIOS_SIZE);
+ g_free(filename);
+ } else {
+ bios_size = -1;
+ }
+
+ if ((bios_size < 0 || bios_size > BIOS_SIZE) &&
+ !kernel_filename && !qtest_enabled()) {
+ error_report("Could not load MIPS bios '%s'", bios_name);
+ exit(1);
+ }
+ }
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /* North bridge, Bonito --> IP2 */
+ pci_bus = bonito_init((qemu_irq *)&(env->irq[2]));
+
+ /* South bridge */
+ ide_drive_get(hd, ARRAY_SIZE(hd));
+
+ isa_bus = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0));
+ if (!isa_bus) {
+ fprintf(stderr, "vt82c686b_init error\n");
+ exit(1);
+ }
+
+ /* Interrupt controller */
+ /* The 8259 -> IP5 */
+ i8259 = i8259_init(isa_bus, env->irq[5]);
+ isa_bus_irqs(isa_bus, i8259);
+
+ vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1));
+ pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2),
+ "vt82c686b-usb-uhci");
+ pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3),
+ "vt82c686b-usb-uhci");
+
+ smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4),
+ 0xeee1, NULL);
+ /* TODO: Populate SPD eeprom data. */
+ smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd));
+
+ /* init other devices */
+ pit = pit_init(isa_bus, 0x40, 0, NULL);
+ cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+ DMA_init(0, cpu_exit_irq);
+
+ /* Super I/O */
+ isa_create_simple(isa_bus, "i8042");
+
+ rtc_init(isa_bus, 2000, NULL);
+
+ serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS);
+ parallel_hds_isa_init(isa_bus, 1);
+
+ /* Sound card */
+ audio_init(pci_bus);
+ /* Network card */
+ network_init(pci_bus);
+}
+
+static QEMUMachine mips_fulong2e_machine = {
+ .name = "fulong2e",
+ .desc = "Fulong 2e mini pc",
+ .init = mips_fulong2e_init,
+};
+
+static void mips_fulong2e_machine_init(void)
+{
+ qemu_register_machine(&mips_fulong2e_machine);
+}
+
+machine_init(mips_fulong2e_machine_init);
diff --git a/qemu/hw/mips/mips_int.c b/qemu/hw/mips/mips_int.c
new file mode 100644
index 000000000..d740046ba
--- /dev/null
+++ b/qemu/hw/mips/mips_int.c
@@ -0,0 +1,78 @@
+/*
+ * QEMU MIPS interrupt support
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/mips/cpudevs.h"
+#include "cpu.h"
+#include "sysemu/kvm.h"
+#include "kvm_mips.h"
+
+static void cpu_mips_irq_request(void *opaque, int irq, int level)
+{
+ MIPSCPU *cpu = opaque;
+ CPUMIPSState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
+
+ if (irq < 0 || irq > 7)
+ return;
+
+ if (level) {
+ env->CP0_Cause |= 1 << (irq + CP0Ca_IP);
+
+ if (kvm_enabled() && irq == 2) {
+ kvm_mips_set_interrupt(cpu, irq, level);
+ }
+
+ } else {
+ env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP));
+
+ if (kvm_enabled() && irq == 2) {
+ kvm_mips_set_interrupt(cpu, irq, level);
+ }
+ }
+
+ if (env->CP0_Cause & CP0Ca_IP_mask) {
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+}
+
+void cpu_mips_irq_init_cpu(CPUMIPSState *env)
+{
+ qemu_irq *qi;
+ int i;
+
+ qi = qemu_allocate_irqs(cpu_mips_irq_request, mips_env_get_cpu(env), 8);
+ for (i = 0; i < 8; i++) {
+ env->irq[i] = qi[i];
+ }
+}
+
+void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level)
+{
+ if (irq < 0 || irq > 2) {
+ return;
+ }
+
+ qemu_set_irq(env->irq[irq], level);
+}
diff --git a/qemu/hw/mips/mips_jazz.c b/qemu/hw/mips/mips_jazz.c
new file mode 100644
index 000000000..9d60633ef
--- /dev/null
+++ b/qemu/hw/mips/mips_jazz.c
@@ -0,0 +1,383 @@
+/*
+ * QEMU MIPS Jazz support
+ *
+ * Copyright (c) 2007-2008 Hervé Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/i386/pc.h"
+#include "hw/char/serial.h"
+#include "hw/isa/isa.h"
+#include "hw/block/fdc.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/arch_init.h"
+#include "hw/boards.h"
+#include "net/net.h"
+#include "hw/scsi/esp.h"
+#include "hw/mips/bios.h"
+#include "hw/loader.h"
+#include "hw/timer/mc146818rtc.h"
+#include "hw/timer/i8254.h"
+#include "hw/audio/pcspk.h"
+#include "sysemu/block-backend.h"
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+#include "sysemu/qtest.h"
+#include "qemu/error-report.h"
+
+enum jazz_model_e
+{
+ JAZZ_MAGNUM,
+ JAZZ_PICA61,
+};
+
+static void main_cpu_reset(void *opaque)
+{
+ MIPSCPU *cpu = opaque;
+
+ cpu_reset(CPU(cpu));
+}
+
+static uint64_t rtc_read(void *opaque, hwaddr addr, unsigned size)
+{
+ uint8_t val;
+ address_space_read(&address_space_memory, 0x90000071,
+ MEMTXATTRS_UNSPECIFIED, &val, 1);
+ return val;
+}
+
+static void rtc_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ uint8_t buf = val & 0xff;
+ address_space_write(&address_space_memory, 0x90000071,
+ MEMTXATTRS_UNSPECIFIED, &buf, 1);
+}
+
+static const MemoryRegionOps rtc_ops = {
+ .read = rtc_read,
+ .write = rtc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint64_t dma_dummy_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ /* Nothing to do. That is only to ensure that
+ * the current DMA acknowledge cycle is completed. */
+ return 0xff;
+}
+
+static void dma_dummy_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ /* Nothing to do. That is only to ensure that
+ * the current DMA acknowledge cycle is completed. */
+}
+
+static const MemoryRegionOps dma_dummy_ops = {
+ .read = dma_dummy_read,
+ .write = dma_dummy_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+#define MAGNUM_BIOS_SIZE_MAX 0x7e000
+#define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX)
+
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+ CPUState *cpu = current_cpu;
+
+ if (cpu && level) {
+ cpu_exit(cpu);
+ }
+}
+
+static CPUUnassignedAccess real_do_unassigned_access;
+static void mips_jazz_do_unassigned_access(CPUState *cpu, hwaddr addr,
+ bool is_write, bool is_exec,
+ int opaque, unsigned size)
+{
+ if (!is_exec) {
+ /* ignore invalid access (ie do not raise exception) */
+ return;
+ }
+ (*real_do_unassigned_access)(cpu, addr, is_write, is_exec, opaque, size);
+}
+
+static void mips_jazz_init(MachineState *machine,
+ enum jazz_model_e jazz_model)
+{
+ MemoryRegion *address_space = get_system_memory();
+ const char *cpu_model = machine->cpu_model;
+ char *filename;
+ int bios_size, n;
+ MIPSCPU *cpu;
+ CPUClass *cc;
+ CPUMIPSState *env;
+ qemu_irq *i8259;
+ rc4030_dma *dmas;
+ MemoryRegion *rc4030_dma_mr;
+ MemoryRegion *isa_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *isa_io = g_new(MemoryRegion, 1);
+ MemoryRegion *rtc = g_new(MemoryRegion, 1);
+ MemoryRegion *i8042 = g_new(MemoryRegion, 1);
+ MemoryRegion *dma_dummy = g_new(MemoryRegion, 1);
+ NICInfo *nd;
+ DeviceState *dev, *rc4030;
+ SysBusDevice *sysbus;
+ ISABus *isa_bus;
+ ISADevice *pit;
+ DriveInfo *fds[MAX_FD];
+ qemu_irq esp_reset, dma_enable;
+ qemu_irq *cpu_exit_irq;
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *bios = g_new(MemoryRegion, 1);
+ MemoryRegion *bios2 = g_new(MemoryRegion, 1);
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "R4000";
+ }
+ cpu = cpu_mips_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ env = &cpu->env;
+ qemu_register_reset(main_cpu_reset, cpu);
+
+ /* Chipset returns 0 in invalid reads and do not raise data exceptions.
+ * However, we can't simply add a global memory region to catch
+ * everything, as memory core directly call unassigned_mem_read/write
+ * on some invalid accesses, which call do_unassigned_access on the
+ * CPU, which raise an exception.
+ * Handle that case by hijacking the do_unassigned_access method on
+ * the CPU, and do not raise exceptions for data access. */
+ cc = CPU_GET_CLASS(cpu);
+ real_do_unassigned_access = cc->do_unassigned_access;
+ cc->do_unassigned_access = mips_jazz_do_unassigned_access;
+
+ /* allocate RAM */
+ memory_region_allocate_system_memory(ram, NULL, "mips_jazz.ram",
+ machine->ram_size);
+ memory_region_add_subregion(address_space, 0, ram);
+
+ memory_region_init_ram(bios, NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE,
+ &error_abort);
+ vmstate_register_ram_global(bios);
+ memory_region_set_readonly(bios, true);
+ memory_region_init_alias(bios2, NULL, "mips_jazz.bios", bios,
+ 0, MAGNUM_BIOS_SIZE);
+ memory_region_add_subregion(address_space, 0x1fc00000LL, bios);
+ memory_region_add_subregion(address_space, 0xfff00000LL, bios2);
+
+ /* load the BIOS image. */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image_targphys(filename, 0xfff00000LL,
+ MAGNUM_BIOS_SIZE);
+ g_free(filename);
+ } else {
+ bios_size = -1;
+ }
+ if ((bios_size < 0 || bios_size > MAGNUM_BIOS_SIZE) && !qtest_enabled()) {
+ error_report("Could not load MIPS bios '%s'", bios_name);
+ exit(1);
+ }
+
+ /* Init CPU internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /* Chipset */
+ rc4030 = rc4030_init(&dmas, &rc4030_dma_mr);
+ sysbus = SYS_BUS_DEVICE(rc4030);
+ sysbus_connect_irq(sysbus, 0, env->irq[6]);
+ sysbus_connect_irq(sysbus, 1, env->irq[3]);
+ memory_region_add_subregion(address_space, 0x80000000,
+ sysbus_mmio_get_region(sysbus, 0));
+ memory_region_add_subregion(address_space, 0xf0000000,
+ sysbus_mmio_get_region(sysbus, 1));
+ memory_region_init_io(dma_dummy, NULL, &dma_dummy_ops, NULL, "dummy_dma", 0x1000);
+ memory_region_add_subregion(address_space, 0x8000d000, dma_dummy);
+
+ /* ISA bus: IO space at 0x90000000, mem space at 0x91000000 */
+ memory_region_init(isa_io, NULL, "isa-io", 0x00010000);
+ memory_region_init(isa_mem, NULL, "isa-mem", 0x01000000);
+ memory_region_add_subregion(address_space, 0x90000000, isa_io);
+ memory_region_add_subregion(address_space, 0x91000000, isa_mem);
+ isa_bus = isa_bus_new(NULL, isa_mem, isa_io);
+
+ /* ISA devices */
+ i8259 = i8259_init(isa_bus, env->irq[4]);
+ isa_bus_irqs(isa_bus, i8259);
+ cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+ DMA_init(0, cpu_exit_irq);
+ pit = pit_init(isa_bus, 0x40, 0, NULL);
+ pcspk_init(isa_bus, pit);
+
+ /* Video card */
+ switch (jazz_model) {
+ case JAZZ_MAGNUM:
+ dev = qdev_create(NULL, "sysbus-g364");
+ qdev_init_nofail(dev);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(sysbus, 0, 0x60080000);
+ sysbus_mmio_map(sysbus, 1, 0x40000000);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 3));
+ {
+ /* Simple ROM, so user doesn't have to provide one */
+ MemoryRegion *rom_mr = g_new(MemoryRegion, 1);
+ memory_region_init_ram(rom_mr, NULL, "g364fb.rom", 0x80000,
+ &error_abort);
+ vmstate_register_ram_global(rom_mr);
+ memory_region_set_readonly(rom_mr, true);
+ uint8_t *rom = memory_region_get_ram_ptr(rom_mr);
+ memory_region_add_subregion(address_space, 0x60000000, rom_mr);
+ rom[0] = 0x10; /* Mips G364 */
+ }
+ break;
+ case JAZZ_PICA61:
+ isa_vga_mm_init(0x40000000, 0x60000000, 0, get_system_memory());
+ break;
+ default:
+ break;
+ }
+
+ /* Network controller */
+ for (n = 0; n < nb_nics; n++) {
+ nd = &nd_table[n];
+ if (!nd->model)
+ nd->model = g_strdup("dp83932");
+ if (strcmp(nd->model, "dp83932") == 0) {
+ qemu_check_nic_model(nd, "dp83932");
+
+ dev = qdev_create(NULL, "dp8393x");
+ qdev_set_nic_properties(dev, nd);
+ qdev_prop_set_uint8(dev, "it_shift", 2);
+ qdev_prop_set_ptr(dev, "dma_mr", rc4030_dma_mr);
+ qdev_init_nofail(dev);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(sysbus, 0, 0x80001000);
+ sysbus_mmio_map(sysbus, 1, 0x8000b000);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4));
+ break;
+ } else if (is_help_option(nd->model)) {
+ fprintf(stderr, "qemu: Supported NICs: dp83932\n");
+ exit(1);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
+ exit(1);
+ }
+ }
+
+ /* SCSI adapter */
+ esp_init(0x80002000, 0,
+ rc4030_dma_read, rc4030_dma_write, dmas[0],
+ qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable);
+
+ /* Floppy */
+ if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) {
+ fprintf(stderr, "qemu: too many floppy drives\n");
+ exit(1);
+ }
+ for (n = 0; n < MAX_FD; n++) {
+ fds[n] = drive_get(IF_FLOPPY, 0, n);
+ }
+ fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0, 0x80003000, fds);
+
+ /* Real time clock */
+ rtc_init(isa_bus, 1980, NULL);
+ memory_region_init_io(rtc, NULL, &rtc_ops, NULL, "rtc", 0x1000);
+ memory_region_add_subregion(address_space, 0x80004000, rtc);
+
+ /* Keyboard (i8042) */
+ i8042_mm_init(qdev_get_gpio_in(rc4030, 6), qdev_get_gpio_in(rc4030, 7),
+ i8042, 0x1000, 0x1);
+ memory_region_add_subregion(address_space, 0x80005000, i8042);
+
+ /* Serial ports */
+ if (serial_hds[0]) {
+ serial_mm_init(address_space, 0x80006000, 0,
+ qdev_get_gpio_in(rc4030, 8), 8000000/16,
+ serial_hds[0], DEVICE_NATIVE_ENDIAN);
+ }
+ if (serial_hds[1]) {
+ serial_mm_init(address_space, 0x80007000, 0,
+ qdev_get_gpio_in(rc4030, 9), 8000000/16,
+ serial_hds[1], DEVICE_NATIVE_ENDIAN);
+ }
+
+ /* Parallel port */
+ if (parallel_hds[0])
+ parallel_mm_init(address_space, 0x80008000, 0,
+ qdev_get_gpio_in(rc4030, 0), parallel_hds[0]);
+
+ /* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */
+
+ /* NVRAM */
+ dev = qdev_create(NULL, "ds1225y");
+ qdev_init_nofail(dev);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(sysbus, 0, 0x80009000);
+
+ /* LED indicator */
+ sysbus_create_simple("jazz-led", 0x8000f000, NULL);
+}
+
+static
+void mips_magnum_init(MachineState *machine)
+{
+ mips_jazz_init(machine, JAZZ_MAGNUM);
+}
+
+static
+void mips_pica61_init(MachineState *machine)
+{
+ mips_jazz_init(machine, JAZZ_PICA61);
+}
+
+static QEMUMachine mips_magnum_machine = {
+ .name = "magnum",
+ .desc = "MIPS Magnum",
+ .init = mips_magnum_init,
+ .block_default_type = IF_SCSI,
+};
+
+static QEMUMachine mips_pica61_machine = {
+ .name = "pica61",
+ .desc = "Acer Pica 61",
+ .init = mips_pica61_init,
+ .block_default_type = IF_SCSI,
+};
+
+static void mips_jazz_machine_init(void)
+{
+ qemu_register_machine(&mips_magnum_machine);
+ qemu_register_machine(&mips_pica61_machine);
+}
+
+machine_init(mips_jazz_machine_init);
diff --git a/qemu/hw/mips/mips_malta.c b/qemu/hw/mips/mips_malta.c
new file mode 100644
index 000000000..3082e7534
--- /dev/null
+++ b/qemu/hw/mips/mips_malta.c
@@ -0,0 +1,1238 @@
+/*
+ * QEMU Malta board support
+ *
+ * Copyright (c) 2006 Aurelien Jarno
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/char/serial.h"
+#include "hw/block/fdc.h"
+#include "net/net.h"
+#include "hw/boards.h"
+#include "hw/i2c/smbus.h"
+#include "sysemu/block-backend.h"
+#include "hw/block/flash.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/pci/pci.h"
+#include "sysemu/char.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/arch_init.h"
+#include "qemu/log.h"
+#include "hw/mips/bios.h"
+#include "hw/ide.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "hw/timer/mc146818rtc.h"
+#include "hw/timer/i8254.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/blockdev.h"
+#include "exec/address-spaces.h"
+#include "hw/sysbus.h" /* SysBusDevice */
+#include "qemu/host-utils.h"
+#include "sysemu/qtest.h"
+#include "qemu/error-report.h"
+#include "hw/empty_slot.h"
+#include "sysemu/kvm.h"
+#include "exec/semihost.h"
+
+//#define DEBUG_BOARD_INIT
+
+#define ENVP_ADDR 0x80002000l
+#define ENVP_NB_ENTRIES 16
+#define ENVP_ENTRY_SIZE 256
+
+/* Hardware addresses */
+#define FLASH_ADDRESS 0x1e000000ULL
+#define FPGA_ADDRESS 0x1f000000ULL
+#define RESET_ADDRESS 0x1fc00000ULL
+
+#define FLASH_SIZE 0x400000
+
+#define MAX_IDE_BUS 2
+
+typedef struct {
+ MemoryRegion iomem;
+ MemoryRegion iomem_lo; /* 0 - 0x900 */
+ MemoryRegion iomem_hi; /* 0xa00 - 0x100000 */
+ uint32_t leds;
+ uint32_t brk;
+ uint32_t gpout;
+ uint32_t i2cin;
+ uint32_t i2coe;
+ uint32_t i2cout;
+ uint32_t i2csel;
+ CharDriverState *display;
+ char display_text[9];
+ SerialState *uart;
+} MaltaFPGAState;
+
+#define TYPE_MIPS_MALTA "mips-malta"
+#define MIPS_MALTA(obj) OBJECT_CHECK(MaltaState, (obj), TYPE_MIPS_MALTA)
+
+typedef struct {
+ SysBusDevice parent_obj;
+
+ qemu_irq *i8259;
+} MaltaState;
+
+static ISADevice *pit;
+
+static struct _loaderparams {
+ int ram_size, ram_low_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+} loaderparams;
+
+/* Malta FPGA */
+static void malta_fpga_update_display(void *opaque)
+{
+ char leds_text[9];
+ int i;
+ MaltaFPGAState *s = opaque;
+
+ for (i = 7 ; i >= 0 ; i--) {
+ if (s->leds & (1 << i))
+ leds_text[i] = '#';
+ else
+ leds_text[i] = ' ';
+ }
+ leds_text[8] = '\0';
+
+ qemu_chr_fe_printf(s->display, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n", leds_text);
+ qemu_chr_fe_printf(s->display, "\n\n\n\n|\e[31m%-8.8s\e[00m|", s->display_text);
+}
+
+/*
+ * EEPROM 24C01 / 24C02 emulation.
+ *
+ * Emulation for serial EEPROMs:
+ * 24C01 - 1024 bit (128 x 8)
+ * 24C02 - 2048 bit (256 x 8)
+ *
+ * Typical device names include Microchip 24C02SC or SGS Thomson ST24C02.
+ */
+
+//~ #define DEBUG
+
+#if defined(DEBUG)
+# define logout(fmt, ...) fprintf(stderr, "MALTA\t%-24s" fmt, __func__, ## __VA_ARGS__)
+#else
+# define logout(fmt, ...) ((void)0)
+#endif
+
+struct _eeprom24c0x_t {
+ uint8_t tick;
+ uint8_t address;
+ uint8_t command;
+ uint8_t ack;
+ uint8_t scl;
+ uint8_t sda;
+ uint8_t data;
+ //~ uint16_t size;
+ uint8_t contents[256];
+};
+
+typedef struct _eeprom24c0x_t eeprom24c0x_t;
+
+static eeprom24c0x_t spd_eeprom = {
+ .contents = {
+ /* 00000000: */ 0x80,0x08,0xFF,0x0D,0x0A,0xFF,0x40,0x00,
+ /* 00000008: */ 0x01,0x75,0x54,0x00,0x82,0x08,0x00,0x01,
+ /* 00000010: */ 0x8F,0x04,0x02,0x01,0x01,0x00,0x00,0x00,
+ /* 00000018: */ 0x00,0x00,0x00,0x14,0x0F,0x14,0x2D,0xFF,
+ /* 00000020: */ 0x15,0x08,0x15,0x08,0x00,0x00,0x00,0x00,
+ /* 00000028: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000030: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000038: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xD0,
+ /* 00000040: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000048: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000050: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000058: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000060: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000068: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000070: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000078: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x64,0xF4,
+ },
+};
+
+static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size)
+{
+ enum { SDR = 0x4, DDR2 = 0x8 } type;
+ uint8_t *spd = spd_eeprom.contents;
+ uint8_t nbanks = 0;
+ uint16_t density = 0;
+ int i;
+
+ /* work in terms of MB */
+ ram_size >>= 20;
+
+ while ((ram_size >= 4) && (nbanks <= 2)) {
+ int sz_log2 = MIN(31 - clz32(ram_size), 14);
+ nbanks++;
+ density |= 1 << (sz_log2 - 2);
+ ram_size -= 1 << sz_log2;
+ }
+
+ /* split to 2 banks if possible */
+ if ((nbanks == 1) && (density > 1)) {
+ nbanks++;
+ density >>= 1;
+ }
+
+ if (density & 0xff00) {
+ density = (density & 0xe0) | ((density >> 8) & 0x1f);
+ type = DDR2;
+ } else if (!(density & 0x1f)) {
+ type = DDR2;
+ } else {
+ type = SDR;
+ }
+
+ if (ram_size) {
+ fprintf(stderr, "Warning: SPD cannot represent final %dMB"
+ " of SDRAM\n", (int)ram_size);
+ }
+
+ /* fill in SPD memory information */
+ spd[2] = type;
+ spd[5] = nbanks;
+ spd[31] = density;
+
+ /* checksum */
+ spd[63] = 0;
+ for (i = 0; i < 63; i++) {
+ spd[63] += spd[i];
+ }
+
+ /* copy for SMBUS */
+ memcpy(eeprom, spd, sizeof(spd_eeprom.contents));
+}
+
+static void generate_eeprom_serial(uint8_t *eeprom)
+{
+ int i, pos = 0;
+ uint8_t mac[6] = { 0x00 };
+ uint8_t sn[5] = { 0x01, 0x23, 0x45, 0x67, 0x89 };
+
+ /* version */
+ eeprom[pos++] = 0x01;
+
+ /* count */
+ eeprom[pos++] = 0x02;
+
+ /* MAC address */
+ eeprom[pos++] = 0x01; /* MAC */
+ eeprom[pos++] = 0x06; /* length */
+ memcpy(&eeprom[pos], mac, sizeof(mac));
+ pos += sizeof(mac);
+
+ /* serial number */
+ eeprom[pos++] = 0x02; /* serial */
+ eeprom[pos++] = 0x05; /* length */
+ memcpy(&eeprom[pos], sn, sizeof(sn));
+ pos += sizeof(sn);
+
+ /* checksum */
+ eeprom[pos] = 0;
+ for (i = 0; i < pos; i++) {
+ eeprom[pos] += eeprom[i];
+ }
+}
+
+static uint8_t eeprom24c0x_read(eeprom24c0x_t *eeprom)
+{
+ logout("%u: scl = %u, sda = %u, data = 0x%02x\n",
+ eeprom->tick, eeprom->scl, eeprom->sda, eeprom->data);
+ return eeprom->sda;
+}
+
+static void eeprom24c0x_write(eeprom24c0x_t *eeprom, int scl, int sda)
+{
+ if (eeprom->scl && scl && (eeprom->sda != sda)) {
+ logout("%u: scl = %u->%u, sda = %u->%u i2c %s\n",
+ eeprom->tick, eeprom->scl, scl, eeprom->sda, sda,
+ sda ? "stop" : "start");
+ if (!sda) {
+ eeprom->tick = 1;
+ eeprom->command = 0;
+ }
+ } else if (eeprom->tick == 0 && !eeprom->ack) {
+ /* Waiting for start. */
+ logout("%u: scl = %u->%u, sda = %u->%u wait for i2c start\n",
+ eeprom->tick, eeprom->scl, scl, eeprom->sda, sda);
+ } else if (!eeprom->scl && scl) {
+ logout("%u: scl = %u->%u, sda = %u->%u trigger bit\n",
+ eeprom->tick, eeprom->scl, scl, eeprom->sda, sda);
+ if (eeprom->ack) {
+ logout("\ti2c ack bit = 0\n");
+ sda = 0;
+ eeprom->ack = 0;
+ } else if (eeprom->sda == sda) {
+ uint8_t bit = (sda != 0);
+ logout("\ti2c bit = %d\n", bit);
+ if (eeprom->tick < 9) {
+ eeprom->command <<= 1;
+ eeprom->command += bit;
+ eeprom->tick++;
+ if (eeprom->tick == 9) {
+ logout("\tcommand 0x%04x, %s\n", eeprom->command,
+ bit ? "read" : "write");
+ eeprom->ack = 1;
+ }
+ } else if (eeprom->tick < 17) {
+ if (eeprom->command & 1) {
+ sda = ((eeprom->data & 0x80) != 0);
+ }
+ eeprom->address <<= 1;
+ eeprom->address += bit;
+ eeprom->tick++;
+ eeprom->data <<= 1;
+ if (eeprom->tick == 17) {
+ eeprom->data = eeprom->contents[eeprom->address];
+ logout("\taddress 0x%04x, data 0x%02x\n",
+ eeprom->address, eeprom->data);
+ eeprom->ack = 1;
+ eeprom->tick = 0;
+ }
+ } else if (eeprom->tick >= 17) {
+ sda = 0;
+ }
+ } else {
+ logout("\tsda changed with raising scl\n");
+ }
+ } else {
+ logout("%u: scl = %u->%u, sda = %u->%u\n", eeprom->tick, eeprom->scl,
+ scl, eeprom->sda, sda);
+ }
+ eeprom->scl = scl;
+ eeprom->sda = sda;
+}
+
+static uint64_t malta_fpga_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ MaltaFPGAState *s = opaque;
+ uint32_t val = 0;
+ uint32_t saddr;
+
+ saddr = (addr & 0xfffff);
+
+ switch (saddr) {
+
+ /* SWITCH Register */
+ case 0x00200:
+ val = 0x00000000; /* All switches closed */
+ break;
+
+ /* STATUS Register */
+ case 0x00208:
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = 0x00000012;
+#else
+ val = 0x00000010;
+#endif
+ break;
+
+ /* JMPRS Register */
+ case 0x00210:
+ val = 0x00;
+ break;
+
+ /* LEDBAR Register */
+ case 0x00408:
+ val = s->leds;
+ break;
+
+ /* BRKRES Register */
+ case 0x00508:
+ val = s->brk;
+ break;
+
+ /* UART Registers are handled directly by the serial device */
+
+ /* GPOUT Register */
+ case 0x00a00:
+ val = s->gpout;
+ break;
+
+ /* XXX: implement a real I2C controller */
+
+ /* GPINP Register */
+ case 0x00a08:
+ /* IN = OUT until a real I2C control is implemented */
+ if (s->i2csel)
+ val = s->i2cout;
+ else
+ val = 0x00;
+ break;
+
+ /* I2CINP Register */
+ case 0x00b00:
+ val = ((s->i2cin & ~1) | eeprom24c0x_read(&spd_eeprom));
+ break;
+
+ /* I2COE Register */
+ case 0x00b08:
+ val = s->i2coe;
+ break;
+
+ /* I2COUT Register */
+ case 0x00b10:
+ val = s->i2cout;
+ break;
+
+ /* I2CSEL Register */
+ case 0x00b18:
+ val = s->i2csel;
+ break;
+
+ default:
+#if 0
+ printf ("malta_fpga_read: Bad register offset 0x" TARGET_FMT_lx "\n",
+ addr);
+#endif
+ break;
+ }
+ return val;
+}
+
+static void malta_fpga_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ MaltaFPGAState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & 0xfffff);
+
+ switch (saddr) {
+
+ /* SWITCH Register */
+ case 0x00200:
+ break;
+
+ /* JMPRS Register */
+ case 0x00210:
+ break;
+
+ /* LEDBAR Register */
+ case 0x00408:
+ s->leds = val & 0xff;
+ malta_fpga_update_display(s);
+ break;
+
+ /* ASCIIWORD Register */
+ case 0x00410:
+ snprintf(s->display_text, 9, "%08X", (uint32_t)val);
+ malta_fpga_update_display(s);
+ break;
+
+ /* ASCIIPOS0 to ASCIIPOS7 Registers */
+ case 0x00418:
+ case 0x00420:
+ case 0x00428:
+ case 0x00430:
+ case 0x00438:
+ case 0x00440:
+ case 0x00448:
+ case 0x00450:
+ s->display_text[(saddr - 0x00418) >> 3] = (char) val;
+ malta_fpga_update_display(s);
+ break;
+
+ /* SOFTRES Register */
+ case 0x00500:
+ if (val == 0x42)
+ qemu_system_reset_request ();
+ break;
+
+ /* BRKRES Register */
+ case 0x00508:
+ s->brk = val & 0xff;
+ break;
+
+ /* UART Registers are handled directly by the serial device */
+
+ /* GPOUT Register */
+ case 0x00a00:
+ s->gpout = val & 0xff;
+ break;
+
+ /* I2COE Register */
+ case 0x00b08:
+ s->i2coe = val & 0x03;
+ break;
+
+ /* I2COUT Register */
+ case 0x00b10:
+ eeprom24c0x_write(&spd_eeprom, val & 0x02, val & 0x01);
+ s->i2cout = val;
+ break;
+
+ /* I2CSEL Register */
+ case 0x00b18:
+ s->i2csel = val & 0x01;
+ break;
+
+ default:
+#if 0
+ printf ("malta_fpga_write: Bad register offset 0x" TARGET_FMT_lx "\n",
+ addr);
+#endif
+ break;
+ }
+}
+
+static const MemoryRegionOps malta_fpga_ops = {
+ .read = malta_fpga_read,
+ .write = malta_fpga_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void malta_fpga_reset(void *opaque)
+{
+ MaltaFPGAState *s = opaque;
+
+ s->leds = 0x00;
+ s->brk = 0x0a;
+ s->gpout = 0x00;
+ s->i2cin = 0x3;
+ s->i2coe = 0x0;
+ s->i2cout = 0x3;
+ s->i2csel = 0x1;
+
+ s->display_text[8] = '\0';
+ snprintf(s->display_text, 9, " ");
+}
+
+static void malta_fpga_led_init(CharDriverState *chr)
+{
+ qemu_chr_fe_printf(chr, "\e[HMalta LEDBAR\r\n");
+ qemu_chr_fe_printf(chr, "+--------+\r\n");
+ qemu_chr_fe_printf(chr, "+ +\r\n");
+ qemu_chr_fe_printf(chr, "+--------+\r\n");
+ qemu_chr_fe_printf(chr, "\n");
+ qemu_chr_fe_printf(chr, "Malta ASCII\r\n");
+ qemu_chr_fe_printf(chr, "+--------+\r\n");
+ qemu_chr_fe_printf(chr, "+ +\r\n");
+ qemu_chr_fe_printf(chr, "+--------+\r\n");
+}
+
+static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
+ hwaddr base, qemu_irq uart_irq, CharDriverState *uart_chr)
+{
+ MaltaFPGAState *s;
+
+ s = (MaltaFPGAState *)g_malloc0(sizeof(MaltaFPGAState));
+
+ memory_region_init_io(&s->iomem, NULL, &malta_fpga_ops, s,
+ "malta-fpga", 0x100000);
+ memory_region_init_alias(&s->iomem_lo, NULL, "malta-fpga",
+ &s->iomem, 0, 0x900);
+ memory_region_init_alias(&s->iomem_hi, NULL, "malta-fpga",
+ &s->iomem, 0xa00, 0x10000-0xa00);
+
+ memory_region_add_subregion(address_space, base, &s->iomem_lo);
+ memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi);
+
+ s->display = qemu_chr_new("fpga", "vc:320x200", malta_fpga_led_init);
+
+ s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq,
+ 230400, uart_chr, DEVICE_NATIVE_ENDIAN);
+
+ malta_fpga_reset(s);
+ qemu_register_reset(malta_fpga_reset, s);
+
+ return s;
+}
+
+/* Network support */
+static void network_init(PCIBus *pci_bus)
+{
+ int i;
+
+ for(i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+ const char *default_devaddr = NULL;
+
+ if (i == 0 && (!nd->model || strcmp(nd->model, "pcnet") == 0))
+ /* The malta board has a PCNet card using PCI SLOT 11 */
+ default_devaddr = "0b";
+
+ pci_nic_init_nofail(nd, pci_bus, "pcnet", default_devaddr);
+ }
+}
+
+/* ROM and pseudo bootloader
+
+ The following code implements a very very simple bootloader. It first
+ loads the registers a0 to a3 to the values expected by the OS, and
+ then jump at the kernel address.
+
+ The bootloader should pass the locations of the kernel arguments and
+ environment variables tables. Those tables contain the 32-bit address
+ of NULL terminated strings. The environment variables table should be
+ terminated by a NULL address.
+
+ For a simpler implementation, the number of kernel arguments is fixed
+ to two (the name of the kernel and the command line), and the two
+ tables are actually the same one.
+
+ The registers a0 to a3 should contain the following values:
+ a0 - number of kernel arguments
+ a1 - 32-bit address of the kernel arguments table
+ a2 - 32-bit address of the environment variables table
+ a3 - RAM size in bytes
+*/
+
+static void write_bootloader (CPUMIPSState *env, uint8_t *base,
+ int64_t run_addr, int64_t kernel_entry)
+{
+ uint32_t *p;
+
+ /* Small bootloader */
+ p = (uint32_t *)base;
+
+ stl_p(p++, 0x08000000 | /* j 0x1fc00580 */
+ ((run_addr + 0x580) & 0x0fffffff) >> 2);
+ stl_p(p++, 0x00000000); /* nop */
+
+ /* YAMON service vector */
+ stl_p(base + 0x500, run_addr + 0x0580); /* start: */
+ stl_p(base + 0x504, run_addr + 0x083c); /* print_count: */
+ stl_p(base + 0x520, run_addr + 0x0580); /* start: */
+ stl_p(base + 0x52c, run_addr + 0x0800); /* flush_cache: */
+ stl_p(base + 0x534, run_addr + 0x0808); /* print: */
+ stl_p(base + 0x538, run_addr + 0x0800); /* reg_cpu_isr: */
+ stl_p(base + 0x53c, run_addr + 0x0800); /* unred_cpu_isr: */
+ stl_p(base + 0x540, run_addr + 0x0800); /* reg_ic_isr: */
+ stl_p(base + 0x544, run_addr + 0x0800); /* unred_ic_isr: */
+ stl_p(base + 0x548, run_addr + 0x0800); /* reg_esr: */
+ stl_p(base + 0x54c, run_addr + 0x0800); /* unreg_esr: */
+ stl_p(base + 0x550, run_addr + 0x0800); /* getchar: */
+ stl_p(base + 0x554, run_addr + 0x0800); /* syscon_read: */
+
+
+ /* Second part of the bootloader */
+ p = (uint32_t *) (base + 0x580);
+
+ if (semihosting_get_argc()) {
+ /* Preserve a0 content as arguments have been passed */
+ stl_p(p++, 0x00000000); /* nop */
+ } else {
+ stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */
+ }
+ stl_p(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff)); /* lui sp, high(ENVP_ADDR) */
+ stl_p(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff)); /* ori sp, sp, low(ENVP_ADDR) */
+ stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */
+ stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a1, low(ENVP_ADDR) */
+ stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */
+ stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */
+ stl_p(p++, 0x3c070000 | (loaderparams.ram_low_size >> 16)); /* lui a3, high(ram_low_size) */
+ stl_p(p++, 0x34e70000 | (loaderparams.ram_low_size & 0xffff)); /* ori a3, a3, low(ram_low_size) */
+
+ /* Load BAR registers as done by YAMON */
+ stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_p(p++, 0x3c08df00); /* lui t0, 0xdf00 */
+#else
+ stl_p(p++, 0x340800df); /* ori t0, r0, 0x00df */
+#endif
+ stl_p(p++, 0xad280068); /* sw t0, 0x0068(t1) */
+
+ stl_p(p++, 0x3c09bbe0); /* lui t1, 0xbbe0 */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_p(p++, 0x3c08c000); /* lui t0, 0xc000 */
+#else
+ stl_p(p++, 0x340800c0); /* ori t0, r0, 0x00c0 */
+#endif
+ stl_p(p++, 0xad280048); /* sw t0, 0x0048(t1) */
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_p(p++, 0x3c084000); /* lui t0, 0x4000 */
+#else
+ stl_p(p++, 0x34080040); /* ori t0, r0, 0x0040 */
+#endif
+ stl_p(p++, 0xad280050); /* sw t0, 0x0050(t1) */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_p(p++, 0x3c088000); /* lui t0, 0x8000 */
+#else
+ stl_p(p++, 0x34080080); /* ori t0, r0, 0x0080 */
+#endif
+ stl_p(p++, 0xad280058); /* sw t0, 0x0058(t1) */
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_p(p++, 0x3c083f00); /* lui t0, 0x3f00 */
+#else
+ stl_p(p++, 0x3408003f); /* ori t0, r0, 0x003f */
+#endif
+ stl_p(p++, 0xad280060); /* sw t0, 0x0060(t1) */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_p(p++, 0x3c08c100); /* lui t0, 0xc100 */
+#else
+ stl_p(p++, 0x340800c1); /* ori t0, r0, 0x00c1 */
+#endif
+ stl_p(p++, 0xad280080); /* sw t0, 0x0080(t1) */
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_p(p++, 0x3c085e00); /* lui t0, 0x5e00 */
+#else
+ stl_p(p++, 0x3408005e); /* ori t0, r0, 0x005e */
+#endif
+ stl_p(p++, 0xad280088); /* sw t0, 0x0088(t1) */
+
+ /* Jump to kernel code */
+ stl_p(p++, 0x3c1f0000 | ((kernel_entry >> 16) & 0xffff)); /* lui ra, high(kernel_entry) */
+ stl_p(p++, 0x37ff0000 | (kernel_entry & 0xffff)); /* ori ra, ra, low(kernel_entry) */
+ stl_p(p++, 0x03e00009); /* jalr ra */
+ stl_p(p++, 0x00000000); /* nop */
+
+ /* YAMON subroutines */
+ p = (uint32_t *) (base + 0x800);
+ stl_p(p++, 0x03e00009); /* jalr ra */
+ stl_p(p++, 0x24020000); /* li v0,0 */
+ /* 808 YAMON print */
+ stl_p(p++, 0x03e06821); /* move t5,ra */
+ stl_p(p++, 0x00805821); /* move t3,a0 */
+ stl_p(p++, 0x00a05021); /* move t2,a1 */
+ stl_p(p++, 0x91440000); /* lbu a0,0(t2) */
+ stl_p(p++, 0x254a0001); /* addiu t2,t2,1 */
+ stl_p(p++, 0x10800005); /* beqz a0,834 */
+ stl_p(p++, 0x00000000); /* nop */
+ stl_p(p++, 0x0ff0021c); /* jal 870 */
+ stl_p(p++, 0x00000000); /* nop */
+ stl_p(p++, 0x08000205); /* j 814 */
+ stl_p(p++, 0x00000000); /* nop */
+ stl_p(p++, 0x01a00009); /* jalr t5 */
+ stl_p(p++, 0x01602021); /* move a0,t3 */
+ /* 0x83c YAMON print_count */
+ stl_p(p++, 0x03e06821); /* move t5,ra */
+ stl_p(p++, 0x00805821); /* move t3,a0 */
+ stl_p(p++, 0x00a05021); /* move t2,a1 */
+ stl_p(p++, 0x00c06021); /* move t4,a2 */
+ stl_p(p++, 0x91440000); /* lbu a0,0(t2) */
+ stl_p(p++, 0x0ff0021c); /* jal 870 */
+ stl_p(p++, 0x00000000); /* nop */
+ stl_p(p++, 0x254a0001); /* addiu t2,t2,1 */
+ stl_p(p++, 0x258cffff); /* addiu t4,t4,-1 */
+ stl_p(p++, 0x1580fffa); /* bnez t4,84c */
+ stl_p(p++, 0x00000000); /* nop */
+ stl_p(p++, 0x01a00009); /* jalr t5 */
+ stl_p(p++, 0x01602021); /* move a0,t3 */
+ /* 0x870 */
+ stl_p(p++, 0x3c08b800); /* lui t0,0xb400 */
+ stl_p(p++, 0x350803f8); /* ori t0,t0,0x3f8 */
+ stl_p(p++, 0x91090005); /* lbu t1,5(t0) */
+ stl_p(p++, 0x00000000); /* nop */
+ stl_p(p++, 0x31290040); /* andi t1,t1,0x40 */
+ stl_p(p++, 0x1120fffc); /* beqz t1,878 <outch+0x8> */
+ stl_p(p++, 0x00000000); /* nop */
+ stl_p(p++, 0x03e00009); /* jalr ra */
+ stl_p(p++, 0xa1040000); /* sb a0,0(t0) */
+
+}
+
+static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index,
+ const char *string, ...)
+{
+ va_list ap;
+ int32_t table_addr;
+
+ if (index >= ENVP_NB_ENTRIES)
+ return;
+
+ if (string == NULL) {
+ prom_buf[index] = 0;
+ return;
+ }
+
+ table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
+ prom_buf[index] = tswap32(ENVP_ADDR + table_addr);
+
+ va_start(ap, string);
+ vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
+ va_end(ap);
+}
+
+/* Kernel */
+static int64_t load_kernel (void)
+{
+ int64_t kernel_entry, kernel_high;
+ long initrd_size;
+ ram_addr_t initrd_offset;
+ int big_endian;
+ uint32_t *prom_buf;
+ long prom_size;
+ int prom_index = 0;
+ uint64_t (*xlate_to_kseg0) (void *opaque, uint64_t addr);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+
+ if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
+ (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high,
+ big_endian, ELF_MACHINE, 1) < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ loaderparams.kernel_filename);
+ exit(1);
+ }
+
+ /* Sanity check where the kernel has been linked */
+ if (kvm_enabled()) {
+ if (kernel_entry & 0x80000000ll) {
+ error_report("KVM guest kernels must be linked in useg. "
+ "Did you forget to enable CONFIG_KVM_GUEST?");
+ exit(1);
+ }
+
+ xlate_to_kseg0 = cpu_mips_kvm_um_phys_to_kseg0;
+ } else {
+ if (!(kernel_entry & 0x80000000ll)) {
+ error_report("KVM guest kernels aren't supported with TCG. "
+ "Did you unintentionally enable CONFIG_KVM_GUEST?");
+ exit(1);
+ }
+
+ xlate_to_kseg0 = cpu_mips_phys_to_kseg0;
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size (loaderparams.initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK;
+ if (initrd_offset + initrd_size > ram_size) {
+ fprintf(stderr,
+ "qemu: memory too small for initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset,
+ ram_size - initrd_offset);
+ }
+ if (initrd_size == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* Setup prom parameters. */
+ prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE);
+ prom_buf = g_malloc(prom_size);
+
+ prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_filename);
+ if (initrd_size > 0) {
+ prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%li %s",
+ xlate_to_kseg0(NULL, initrd_offset), initrd_size,
+ loaderparams.kernel_cmdline);
+ } else {
+ prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_cmdline);
+ }
+
+ prom_set(prom_buf, prom_index++, "memsize");
+ prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_low_size);
+
+ prom_set(prom_buf, prom_index++, "ememsize");
+ prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_size);
+
+ prom_set(prom_buf, prom_index++, "modetty0");
+ prom_set(prom_buf, prom_index++, "38400n8r");
+ prom_set(prom_buf, prom_index++, NULL);
+
+ rom_add_blob_fixed("prom", prom_buf, prom_size,
+ cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
+
+ g_free(prom_buf);
+ return kernel_entry;
+}
+
+static void malta_mips_config(MIPSCPU *cpu)
+{
+ CPUMIPSState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
+
+ env->mvp->CP0_MVPConf0 |= ((smp_cpus - 1) << CP0MVPC0_PVPE) |
+ ((smp_cpus * cs->nr_threads - 1) << CP0MVPC0_PTC);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ MIPSCPU *cpu = opaque;
+ CPUMIPSState *env = &cpu->env;
+
+ cpu_reset(CPU(cpu));
+
+ /* The bootloader does not need to be rewritten as it is located in a
+ read only location. The kernel location and the arguments table
+ location does not change. */
+ if (loaderparams.kernel_filename) {
+ env->CP0_Status &= ~(1 << CP0St_ERL);
+ }
+
+ malta_mips_config(cpu);
+
+ if (kvm_enabled()) {
+ /* Start running from the bootloader we wrote to end of RAM */
+ env->active_tc.PC = 0x40000000 + loaderparams.ram_size;
+ }
+}
+
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+ CPUState *cpu = current_cpu;
+
+ if (cpu && level) {
+ cpu_exit(cpu);
+ }
+}
+
+static
+void mips_malta_init(MachineState *machine)
+{
+ ram_addr_t ram_size = machine->ram_size;
+ ram_addr_t ram_low_size;
+ const char *cpu_model = machine->cpu_model;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *initrd_filename = machine->initrd_filename;
+ char *filename;
+ pflash_t *fl;
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *ram_high = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_low_preio = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_low_postio;
+ MemoryRegion *bios, *bios_copy = g_new(MemoryRegion, 1);
+ target_long bios_size = FLASH_SIZE;
+ const size_t smbus_eeprom_size = 8 * 256;
+ uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size);
+ int64_t kernel_entry, bootloader_run_addr;
+ PCIBus *pci_bus;
+ ISABus *isa_bus;
+ MIPSCPU *cpu;
+ CPUMIPSState *env;
+ qemu_irq *isa_irq;
+ qemu_irq *cpu_exit_irq;
+ int piix4_devfn;
+ I2CBus *smbus;
+ int i;
+ DriveInfo *dinfo;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ DriveInfo *fd[MAX_FD];
+ int fl_idx = 0;
+ int fl_sectors = bios_size >> 16;
+ int be;
+
+ DeviceState *dev = qdev_create(NULL, TYPE_MIPS_MALTA);
+ MaltaState *s = MIPS_MALTA(dev);
+
+ /* The whole address space decoded by the GT-64120A doesn't generate
+ exception when accessing invalid memory. Create an empty slot to
+ emulate this feature. */
+ empty_slot_init(0, 0x20000000);
+
+ qdev_init_nofail(dev);
+
+ /* Make sure the first 3 serial ports are associated with a device. */
+ for(i = 0; i < 3; i++) {
+ if (!serial_hds[i]) {
+ char label[32];
+ snprintf(label, sizeof(label), "serial%d", i);
+ serial_hds[i] = qemu_chr_new(label, "null", NULL);
+ }
+ }
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+ cpu_model = "20Kc";
+#else
+ cpu_model = "24Kf";
+#endif
+ }
+
+ for (i = 0; i < smp_cpus; i++) {
+ cpu = cpu_mips_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ env = &cpu->env;
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+ qemu_register_reset(main_cpu_reset, cpu);
+ }
+ cpu = MIPS_CPU(first_cpu);
+ env = &cpu->env;
+
+ /* allocate RAM */
+ if (ram_size > (2048u << 20)) {
+ fprintf(stderr,
+ "qemu: Too much memory for this machine: %d MB, maximum 2048 MB\n",
+ ((unsigned int)ram_size / (1 << 20)));
+ exit(1);
+ }
+
+ /* register RAM at high address where it is undisturbed by IO */
+ memory_region_allocate_system_memory(ram_high, NULL, "mips_malta.ram",
+ ram_size);
+ memory_region_add_subregion(system_memory, 0x80000000, ram_high);
+
+ /* alias for pre IO hole access */
+ memory_region_init_alias(ram_low_preio, NULL, "mips_malta_low_preio.ram",
+ ram_high, 0, MIN(ram_size, (256 << 20)));
+ memory_region_add_subregion(system_memory, 0, ram_low_preio);
+
+ /* alias for post IO hole access, if there is enough RAM */
+ if (ram_size > (512 << 20)) {
+ ram_low_postio = g_new(MemoryRegion, 1);
+ memory_region_init_alias(ram_low_postio, NULL,
+ "mips_malta_low_postio.ram",
+ ram_high, 512 << 20,
+ ram_size - (512 << 20));
+ memory_region_add_subregion(system_memory, 512 << 20, ram_low_postio);
+ }
+
+ /* generate SPD EEPROM data */
+ generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size);
+ generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ /* FPGA */
+ /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */
+ malta_fpga_init(system_memory, FPGA_ADDRESS, env->irq[4], serial_hds[2]);
+
+ /* Load firmware in flash / BIOS. */
+ dinfo = drive_get(IF_PFLASH, 0, fl_idx);
+#ifdef DEBUG_BOARD_INIT
+ if (dinfo) {
+ printf("Register parallel flash %d size " TARGET_FMT_lx " at "
+ "addr %08llx '%s' %x\n",
+ fl_idx, bios_size, FLASH_ADDRESS,
+ blk_name(dinfo->bdrv), fl_sectors);
+ }
+#endif
+ fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios",
+ BIOS_SIZE,
+ dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+ 65536, fl_sectors,
+ 4, 0x0000, 0x0000, 0x0000, 0x0000, be);
+ bios = pflash_cfi01_get_memory(fl);
+ fl_idx++;
+ if (kernel_filename) {
+ ram_low_size = MIN(ram_size, 256 << 20);
+ /* For KVM we reserve 1MB of RAM for running bootloader */
+ if (kvm_enabled()) {
+ ram_low_size -= 0x100000;
+ bootloader_run_addr = 0x40000000 + ram_low_size;
+ } else {
+ bootloader_run_addr = 0xbfc00000;
+ }
+
+ /* Write a small bootloader to the flash location. */
+ loaderparams.ram_size = ram_size;
+ loaderparams.ram_low_size = ram_low_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ kernel_entry = load_kernel();
+
+ write_bootloader(env, memory_region_get_ram_ptr(bios),
+ bootloader_run_addr, kernel_entry);
+ if (kvm_enabled()) {
+ /* Write the bootloader code @ the end of RAM, 1MB reserved */
+ write_bootloader(env, memory_region_get_ram_ptr(ram_low_preio) +
+ ram_low_size,
+ bootloader_run_addr, kernel_entry);
+ }
+ } else {
+ /* The flash region isn't executable from a KVM guest */
+ if (kvm_enabled()) {
+ error_report("KVM enabled but no -kernel argument was specified. "
+ "Booting from flash is not supported with KVM.");
+ exit(1);
+ }
+ /* Load firmware from flash. */
+ if (!dinfo) {
+ /* Load a BIOS image. */
+ if (bios_name == NULL) {
+ bios_name = BIOS_FILENAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image_targphys(filename, FLASH_ADDRESS,
+ BIOS_SIZE);
+ g_free(filename);
+ } else {
+ bios_size = -1;
+ }
+ if ((bios_size < 0 || bios_size > BIOS_SIZE) &&
+ !kernel_filename && !qtest_enabled()) {
+ error_report("Could not load MIPS bios '%s', and no "
+ "-kernel argument was specified", bios_name);
+ exit(1);
+ }
+ }
+ /* In little endian mode the 32bit words in the bios are swapped,
+ a neat trick which allows bi-endian firmware. */
+#ifndef TARGET_WORDS_BIGENDIAN
+ {
+ uint32_t *end, *addr = rom_ptr(FLASH_ADDRESS);
+ if (!addr) {
+ addr = memory_region_get_ram_ptr(bios);
+ }
+ end = (void *)addr + MIN(bios_size, 0x3e0000);
+ while (addr < end) {
+ bswap32s(addr);
+ addr++;
+ }
+ }
+#endif
+ }
+
+ /*
+ * Map the BIOS at a 2nd physical location, as on the real board.
+ * Copy it so that we can patch in the MIPS revision, which cannot be
+ * handled by an overlapping region as the resulting ROM code subpage
+ * regions are not executable.
+ */
+ memory_region_init_ram(bios_copy, NULL, "bios.1fc", BIOS_SIZE,
+ &error_abort);
+ if (!rom_copy(memory_region_get_ram_ptr(bios_copy),
+ FLASH_ADDRESS, BIOS_SIZE)) {
+ memcpy(memory_region_get_ram_ptr(bios_copy),
+ memory_region_get_ram_ptr(bios), BIOS_SIZE);
+ }
+ memory_region_set_readonly(bios_copy, true);
+ memory_region_add_subregion(system_memory, RESET_ADDRESS, bios_copy);
+
+ /* Board ID = 0x420 (Malta Board with CoreLV) */
+ stl_p(memory_region_get_ram_ptr(bios_copy) + 0x10, 0x00000420);
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /*
+ * We have a circular dependency problem: pci_bus depends on isa_irq,
+ * isa_irq is provided by i8259, i8259 depends on ISA, ISA depends
+ * on piix4, and piix4 depends on pci_bus. To stop the cycle we have
+ * qemu_irq_proxy() adds an extra bit of indirection, allowing us
+ * to resolve the isa_irq -> i8259 dependency after i8259 is initialized.
+ */
+ isa_irq = qemu_irq_proxy(&s->i8259, 16);
+
+ /* Northbridge */
+ pci_bus = gt64120_register(isa_irq);
+
+ /* Southbridge */
+ ide_drive_get(hd, ARRAY_SIZE(hd));
+
+ piix4_devfn = piix4_init(pci_bus, &isa_bus, 80);
+
+ /* Interrupt controller */
+ /* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */
+ s->i8259 = i8259_init(isa_bus, env->irq[2]);
+
+ isa_bus_irqs(isa_bus, s->i8259);
+ pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);
+ pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci");
+ smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100,
+ isa_get_irq(NULL, 9), NULL, 0, NULL);
+ smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size);
+ g_free(smbus_eeprom_buf);
+ pit = pit_init(isa_bus, 0x40, 0, NULL);
+ cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+ DMA_init(0, cpu_exit_irq);
+
+ /* Super I/O */
+ isa_create_simple(isa_bus, "i8042");
+
+ rtc_init(isa_bus, 2000, NULL);
+ serial_hds_isa_init(isa_bus, 2);
+ parallel_hds_isa_init(isa_bus, 1);
+
+ for(i = 0; i < MAX_FD; i++) {
+ fd[i] = drive_get(IF_FLOPPY, 0, i);
+ }
+ fdctrl_init_isa(isa_bus, fd);
+
+ /* Network card */
+ network_init(pci_bus);
+
+ /* Optional PCI video card */
+ pci_vga_init(pci_bus);
+}
+
+static int mips_malta_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+ return 0;
+}
+
+static void mips_malta_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = mips_malta_sysbus_device_init;
+}
+
+static const TypeInfo mips_malta_device = {
+ .name = TYPE_MIPS_MALTA,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MaltaState),
+ .class_init = mips_malta_class_init,
+};
+
+static QEMUMachine mips_malta_machine = {
+ .name = "malta",
+ .desc = "MIPS Malta Core LV",
+ .init = mips_malta_init,
+ .max_cpus = 16,
+ .is_default = 1,
+};
+
+static void mips_malta_register_types(void)
+{
+ type_register_static(&mips_malta_device);
+}
+
+static void mips_malta_machine_init(void)
+{
+ qemu_register_machine(&mips_malta_machine);
+}
+
+type_init(mips_malta_register_types)
+machine_init(mips_malta_machine_init);
diff --git a/qemu/hw/mips/mips_mipssim.c b/qemu/hw/mips/mips_mipssim.c
new file mode 100644
index 000000000..61f74a631
--- /dev/null
+++ b/qemu/hw/mips/mips_mipssim.c
@@ -0,0 +1,245 @@
+/*
+ * QEMU/mipssim emulation
+ *
+ * Emulates a very simple machine model similar to the one used by the
+ * proprietary MIPS emulator.
+ *
+ * Copyright (c) 2007 Thiemo Seufer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/char/serial.h"
+#include "hw/isa/isa.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "hw/mips/bios.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "hw/sysbus.h"
+#include "exec/address-spaces.h"
+#include "qemu/error-report.h"
+#include "sysemu/qtest.h"
+
+static struct _loaderparams {
+ int ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+} loaderparams;
+
+typedef struct ResetData {
+ MIPSCPU *cpu;
+ uint64_t vector;
+} ResetData;
+
+static int64_t load_kernel(void)
+{
+ int64_t entry, kernel_high;
+ long kernel_size;
+ long initrd_size;
+ ram_addr_t initrd_offset;
+ int big_endian;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+
+ kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
+ NULL, (uint64_t *)&entry, NULL,
+ (uint64_t *)&kernel_high, big_endian,
+ ELF_MACHINE, 1);
+ if (kernel_size >= 0) {
+ if ((entry & ~0x7fffffffULL) == 0x80000000)
+ entry = (int32_t)entry;
+ } else {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ loaderparams.kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size (loaderparams.initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK;
+ if (initrd_offset + initrd_size > loaderparams.ram_size) {
+ fprintf(stderr,
+ "qemu: memory too small for initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset, loaderparams.ram_size - initrd_offset);
+ }
+ if (initrd_size == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+ return entry;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+ CPUMIPSState *env = &s->cpu->env;
+
+ cpu_reset(CPU(s->cpu));
+ env->active_tc.PC = s->vector & ~(target_ulong)1;
+ if (s->vector & 1) {
+ env->hflags |= MIPS_HFLAG_M16;
+ }
+}
+
+static void mipsnet_init(int base, qemu_irq irq, NICInfo *nd)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "mipsnet");
+ qdev_set_nic_properties(dev, nd);
+ qdev_init_nofail(dev);
+
+ s = SYS_BUS_DEVICE(dev);
+ sysbus_connect_irq(s, 0, irq);
+ memory_region_add_subregion(get_system_io(),
+ base,
+ sysbus_mmio_get_region(s, 0));
+}
+
+static void
+mips_mipssim_init(MachineState *machine)
+{
+ ram_addr_t ram_size = machine->ram_size;
+ const char *cpu_model = machine->cpu_model;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *initrd_filename = machine->initrd_filename;
+ char *filename;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *isa = g_new(MemoryRegion, 1);
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *bios = g_new(MemoryRegion, 1);
+ MIPSCPU *cpu;
+ CPUMIPSState *env;
+ ResetData *reset_info;
+ int bios_size;
+
+ /* Init CPUs. */
+ if (cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+ cpu_model = "5Kf";
+#else
+ cpu_model = "24Kf";
+#endif
+ }
+ cpu = cpu_mips_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ env = &cpu->env;
+
+ reset_info = g_malloc0(sizeof(ResetData));
+ reset_info->cpu = cpu;
+ reset_info->vector = env->active_tc.PC;
+ qemu_register_reset(main_cpu_reset, reset_info);
+
+ /* Allocate RAM. */
+ memory_region_allocate_system_memory(ram, NULL, "mips_mipssim.ram",
+ ram_size);
+ memory_region_init_ram(bios, NULL, "mips_mipssim.bios", BIOS_SIZE,
+ &error_abort);
+ vmstate_register_ram_global(bios);
+ memory_region_set_readonly(bios, true);
+
+ memory_region_add_subregion(address_space_mem, 0, ram);
+
+ /* Map the BIOS / boot exception handler. */
+ memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios);
+ /* Load a BIOS / boot exception handler image. */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image_targphys(filename, 0x1fc00000LL, BIOS_SIZE);
+ g_free(filename);
+ } else {
+ bios_size = -1;
+ }
+ if ((bios_size < 0 || bios_size > BIOS_SIZE) &&
+ !kernel_filename && !qtest_enabled()) {
+ /* Bail out if we have neither a kernel image nor boot vector code. */
+ error_report("Could not load MIPS bios '%s', and no "
+ "-kernel argument was specified", bios_name);
+ exit(1);
+ } else {
+ /* We have a boot vector start address. */
+ env->active_tc.PC = (target_long)(int32_t)0xbfc00000;
+ }
+
+ if (kernel_filename) {
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ reset_info->vector = load_kernel();
+ }
+
+ /* Init CPU internal devices. */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /* Register 64 KB of ISA IO space at 0x1fd00000. */
+ memory_region_init_alias(isa, NULL, "isa_mmio",
+ get_system_io(), 0, 0x00010000);
+ memory_region_add_subregion(get_system_memory(), 0x1fd00000, isa);
+
+ /* A single 16450 sits at offset 0x3f8. It is attached to
+ MIPS CPU INT2, which is interrupt 4. */
+ if (serial_hds[0])
+ serial_init(0x3f8, env->irq[4], 115200, serial_hds[0],
+ get_system_io());
+
+ if (nd_table[0].used)
+ /* MIPSnet uses the MIPS CPU INT0, which is interrupt 2. */
+ mipsnet_init(0x4200, env->irq[2], &nd_table[0]);
+}
+
+static QEMUMachine mips_mipssim_machine = {
+ .name = "mipssim",
+ .desc = "MIPS MIPSsim platform",
+ .init = mips_mipssim_init,
+};
+
+static void mips_mipssim_machine_init(void)
+{
+ qemu_register_machine(&mips_mipssim_machine);
+}
+
+machine_init(mips_mipssim_machine_init);
diff --git a/qemu/hw/mips/mips_r4k.c b/qemu/hw/mips/mips_r4k.c
new file mode 100644
index 000000000..f4dcacd86
--- /dev/null
+++ b/qemu/hw/mips/mips_r4k.c
@@ -0,0 +1,314 @@
+/*
+ * QEMU/MIPS pseudo-board
+ *
+ * emulates a simple machine with ISA-like bus.
+ * ISA IO space mapped to the 0x14000000 (PHYS) and
+ * ISA memory at the 0x10000000 (PHYS, 16Mb in size).
+ * All peripherial devices are attached to this "bus" with
+ * the standard PC ISA addresses.
+*/
+#include "hw/hw.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/i386/pc.h"
+#include "hw/char/serial.h"
+#include "hw/isa/isa.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "hw/block/flash.h"
+#include "qemu/log.h"
+#include "hw/mips/bios.h"
+#include "hw/ide.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "hw/timer/mc146818rtc.h"
+#include "hw/timer/i8254.h"
+#include "sysemu/block-backend.h"
+#include "exec/address-spaces.h"
+#include "sysemu/qtest.h"
+
+#define MAX_IDE_BUS 2
+
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 14, 15 };
+
+static ISADevice *pit; /* PIT i8254 */
+
+/* i8254 PIT is attached to the IRQ0 at PIC i8259 */
+
+static struct _loaderparams {
+ int ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+} loaderparams;
+
+static void mips_qemu_write (void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ if ((addr & 0xffff) == 0 && val == 42)
+ qemu_system_reset_request ();
+ else if ((addr & 0xffff) == 4 && val == 42)
+ qemu_system_shutdown_request ();
+}
+
+static uint64_t mips_qemu_read (void *opaque, hwaddr addr,
+ unsigned size)
+{
+ return 0;
+}
+
+static const MemoryRegionOps mips_qemu_ops = {
+ .read = mips_qemu_read,
+ .write = mips_qemu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+typedef struct ResetData {
+ MIPSCPU *cpu;
+ uint64_t vector;
+} ResetData;
+
+static int64_t load_kernel(void)
+{
+ int64_t entry, kernel_high;
+ long kernel_size, initrd_size, params_size;
+ ram_addr_t initrd_offset;
+ uint32_t *params_buf;
+ int big_endian;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+ kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
+ NULL, (uint64_t *)&entry, NULL,
+ (uint64_t *)&kernel_high, big_endian,
+ ELF_MACHINE, 1);
+ if (kernel_size >= 0) {
+ if ((entry & ~0x7fffffffULL) == 0x80000000)
+ entry = (int32_t)entry;
+ } else {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ loaderparams.kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size (loaderparams.initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK;
+ if (initrd_offset + initrd_size > ram_size) {
+ fprintf(stderr,
+ "qemu: memory too small for initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset,
+ ram_size - initrd_offset);
+ }
+ if (initrd_size == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* Store command line. */
+ params_size = 264;
+ params_buf = g_malloc(params_size);
+
+ params_buf[0] = tswap32(ram_size);
+ params_buf[1] = tswap32(0x12345678);
+
+ if (initrd_size > 0) {
+ snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%li %s",
+ cpu_mips_phys_to_kseg0(NULL, initrd_offset),
+ initrd_size, loaderparams.kernel_cmdline);
+ } else {
+ snprintf((char *)params_buf + 8, 256, "%s", loaderparams.kernel_cmdline);
+ }
+
+ rom_add_blob_fixed("params", params_buf, params_size,
+ (16 << 20) - 264);
+
+ g_free(params_buf);
+ return entry;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+ CPUMIPSState *env = &s->cpu->env;
+
+ cpu_reset(CPU(s->cpu));
+ env->active_tc.PC = s->vector;
+}
+
+static const int sector_len = 32 * 1024;
+static
+void mips_r4k_init(MachineState *machine)
+{
+ ram_addr_t ram_size = machine->ram_size;
+ const char *cpu_model = machine->cpu_model;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *initrd_filename = machine->initrd_filename;
+ char *filename;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *bios;
+ MemoryRegion *iomem = g_new(MemoryRegion, 1);
+ MemoryRegion *isa_io = g_new(MemoryRegion, 1);
+ MemoryRegion *isa_mem = g_new(MemoryRegion, 1);
+ int bios_size;
+ MIPSCPU *cpu;
+ CPUMIPSState *env;
+ ResetData *reset_info;
+ int i;
+ qemu_irq *i8259;
+ ISABus *isa_bus;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ DriveInfo *dinfo;
+ int be;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+ cpu_model = "R4000";
+#else
+ cpu_model = "24Kf";
+#endif
+ }
+ cpu = cpu_mips_init(cpu_model);
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ env = &cpu->env;
+
+ reset_info = g_malloc0(sizeof(ResetData));
+ reset_info->cpu = cpu;
+ reset_info->vector = env->active_tc.PC;
+ qemu_register_reset(main_cpu_reset, reset_info);
+
+ /* allocate RAM */
+ if (ram_size > (256 << 20)) {
+ fprintf(stderr,
+ "qemu: Too much memory for this machine: %d MB, maximum 256 MB\n",
+ ((unsigned int)ram_size / (1 << 20)));
+ exit(1);
+ }
+ memory_region_allocate_system_memory(ram, NULL, "mips_r4k.ram", ram_size);
+
+ memory_region_add_subregion(address_space_mem, 0, ram);
+
+ memory_region_init_io(iomem, NULL, &mips_qemu_ops, NULL, "mips-qemu", 0x10000);
+ memory_region_add_subregion(address_space_mem, 0x1fbf0000, iomem);
+
+ /* Try to load a BIOS image. If this fails, we continue regardless,
+ but initialize the hardware ourselves. When a kernel gets
+ preloaded we also initialize the hardware, since the BIOS wasn't
+ run. */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = get_image_size(filename);
+ } else {
+ bios_size = -1;
+ }
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) {
+ bios = g_new(MemoryRegion, 1);
+ memory_region_init_ram(bios, NULL, "mips_r4k.bios", BIOS_SIZE,
+ &error_abort);
+ vmstate_register_ram_global(bios);
+ memory_region_set_readonly(bios, true);
+ memory_region_add_subregion(get_system_memory(), 0x1fc00000, bios);
+
+ load_image_targphys(filename, 0x1fc00000, BIOS_SIZE);
+ } else if ((dinfo = drive_get(IF_PFLASH, 0, 0)) != NULL) {
+ uint32_t mips_rom = 0x00400000;
+ if (!pflash_cfi01_register(0x1fc00000, NULL, "mips_r4k.bios", mips_rom,
+ blk_by_legacy_dinfo(dinfo),
+ sector_len, mips_rom / sector_len,
+ 4, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ }
+ } else if (!qtest_enabled()) {
+ /* not fatal */
+ fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n",
+ bios_name);
+ }
+ if (filename) {
+ g_free(filename);
+ }
+
+ if (kernel_filename) {
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ reset_info->vector = load_kernel();
+ }
+
+ /* Init CPU internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /* ISA bus: IO space at 0x14000000, mem space at 0x10000000 */
+ memory_region_init_alias(isa_io, NULL, "isa-io",
+ get_system_io(), 0, 0x00010000);
+ memory_region_init(isa_mem, NULL, "isa-mem", 0x01000000);
+ memory_region_add_subregion(get_system_memory(), 0x14000000, isa_io);
+ memory_region_add_subregion(get_system_memory(), 0x10000000, isa_mem);
+ isa_bus = isa_bus_new(NULL, isa_mem, get_system_io());
+
+ /* The PIC is attached to the MIPS CPU INT0 pin */
+ i8259 = i8259_init(isa_bus, env->irq[2]);
+ isa_bus_irqs(isa_bus, i8259);
+
+ rtc_init(isa_bus, 2000, NULL);
+
+ pit = pit_init(isa_bus, 0x40, 0, NULL);
+
+ serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS);
+
+ isa_vga_init(isa_bus);
+
+ if (nd_table[0].used)
+ isa_ne2000_init(isa_bus, 0x300, 9, &nd_table[0]);
+
+ ide_drive_get(hd, ARRAY_SIZE(hd));
+ for(i = 0; i < MAX_IDE_BUS; i++)
+ isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i],
+ hd[MAX_IDE_DEVS * i],
+ hd[MAX_IDE_DEVS * i + 1]);
+
+ isa_create_simple(isa_bus, "i8042");
+}
+
+static QEMUMachine mips_machine = {
+ .name = "mips",
+ .desc = "mips r4k platform",
+ .init = mips_r4k_init,
+};
+
+static void mips_machine_init(void)
+{
+ qemu_register_machine(&mips_machine);
+}
+
+machine_init(mips_machine_init);