aboutsummaryrefslogtreecommitdiffstats
path: root/hw/mips
diff options
context:
space:
mode:
authorTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2023-10-10 11:40:56 +0000
committerTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2023-10-10 11:40:56 +0000
commite02cda008591317b1625707ff8e115a4841aa889 (patch)
treeaee302e3cf8b59ec2d32ec481be3d1afddfc8968 /hw/mips
parentcc668e6b7e0ffd8c9d130513d12053cf5eda1d3b (diff)
Introduce Virtio-loopback epsilon release:
Epsilon release introduces a new compatibility layer which make virtio-loopback design to work with QEMU and rust-vmm vhost-user backend without require any changes. Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> Change-Id: I52e57563e08a7d0bdc002f8e928ee61ba0c53dd9
Diffstat (limited to 'hw/mips')
-rw-r--r--hw/mips/Kconfig61
-rw-r--r--hw/mips/bootloader.c204
-rw-r--r--hw/mips/boston.c835
-rw-r--r--hw/mips/cps.c210
-rw-r--r--hw/mips/fuloong2e.c350
-rw-r--r--hw/mips/fw_cfg.c35
-rw-r--r--hw/mips/fw_cfg.h19
-rw-r--r--hw/mips/gt64xxx_pci.c1300
-rw-r--r--hw/mips/jazz.c441
-rw-r--r--hw/mips/loongson3_bootp.c151
-rw-r--r--hw/mips/loongson3_bootp.h236
-rw-r--r--hw/mips/loongson3_virt.c634
-rw-r--r--hw/mips/malta.c1464
-rw-r--r--hw/mips/meson.build15
-rw-r--r--hw/mips/mips_int.c88
-rw-r--r--hw/mips/mipssim.c246
-rw-r--r--hw/mips/trace-events6
-rw-r--r--hw/mips/trace.h1
18 files changed, 6296 insertions, 0 deletions
diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig
new file mode 100644
index 000000000..b4c5549ce
--- /dev/null
+++ b/hw/mips/Kconfig
@@ -0,0 +1,61 @@
+config MALTA
+ bool
+ select ISA_SUPERIO
+
+config MIPSSIM
+ bool
+ select ISA_BUS
+ select SERIAL_ISA
+ select MIPSNET
+
+config JAZZ
+ bool
+ select ISA_BUS
+ select RC4030
+ select I8259
+ select I8254
+ select I8257
+ select PCSPK
+ select VGA_ISA_MM
+ select G364FB
+ select DP8393X
+ select ESP
+ select FDC_SYSBUS
+ select MC146818RTC
+ select PCKBD
+ select SERIAL
+ select PARALLEL
+ select DS1225Y
+ select JAZZ_LED
+
+config FULOONG
+ bool
+ select PCI_BONITO
+
+config LOONGSON3V
+ bool
+ imply VIRTIO_VGA
+ imply QXL if SPICE
+ select SERIAL
+ select GOLDFISH_RTC
+ select LOONGSON_LIOINTC
+ select PCI_DEVICES
+ select PCI_EXPRESS_GENERIC_BRIDGE
+ select MSI_NONBROKEN
+ select FW_CFG_MIPS
+
+config MIPS_CPS
+ bool
+ select PTIMER
+ select MIPS_ITU
+
+config MIPS_BOSTON
+ bool
+ select FITLOADER
+ select MIPS_CPS
+ select PCI_EXPRESS_XILINX
+ select AHCI_ICH9
+ select SERIAL
+
+config FW_CFG_MIPS
+ bool
diff --git a/hw/mips/bootloader.c b/hw/mips/bootloader.c
new file mode 100644
index 000000000..99991f8b2
--- /dev/null
+++ b/hw/mips/bootloader.c
@@ -0,0 +1,204 @@
+/*
+ * Utility for QEMU MIPS to generate it's simple bootloader
+ *
+ * Instructions used here are carefully selected to keep compatibility with
+ * MIPS Release 6.
+ *
+ * Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "cpu.h"
+#include "hw/mips/bootloader.h"
+
+typedef enum bl_reg {
+ BL_REG_ZERO = 0,
+ BL_REG_AT = 1,
+ BL_REG_V0 = 2,
+ BL_REG_V1 = 3,
+ BL_REG_A0 = 4,
+ BL_REG_A1 = 5,
+ BL_REG_A2 = 6,
+ BL_REG_A3 = 7,
+ BL_REG_T0 = 8,
+ BL_REG_T1 = 9,
+ BL_REG_T2 = 10,
+ BL_REG_T3 = 11,
+ BL_REG_T4 = 12,
+ BL_REG_T5 = 13,
+ BL_REG_T6 = 14,
+ BL_REG_T7 = 15,
+ BL_REG_S0 = 16,
+ BL_REG_S1 = 17,
+ BL_REG_S2 = 18,
+ BL_REG_S3 = 19,
+ BL_REG_S4 = 20,
+ BL_REG_S5 = 21,
+ BL_REG_S6 = 22,
+ BL_REG_S7 = 23,
+ BL_REG_T8 = 24,
+ BL_REG_T9 = 25,
+ BL_REG_K0 = 26,
+ BL_REG_K1 = 27,
+ BL_REG_GP = 28,
+ BL_REG_SP = 29,
+ BL_REG_FP = 30,
+ BL_REG_RA = 31,
+} bl_reg;
+
+static bool bootcpu_supports_isa(uint64_t isa_mask)
+{
+ return cpu_supports_isa(&MIPS_CPU(first_cpu)->env, isa_mask);
+}
+
+/* Base types */
+static void bl_gen_nop(uint32_t **p)
+{
+ stl_p(*p, 0);
+ *p = *p + 1;
+}
+
+static void bl_gen_r_type(uint32_t **p, uint8_t opcode,
+ bl_reg rs, bl_reg rt, bl_reg rd,
+ uint8_t shift, uint8_t funct)
+{
+ uint32_t insn = 0;
+
+ insn = deposit32(insn, 26, 6, opcode);
+ insn = deposit32(insn, 21, 5, rs);
+ insn = deposit32(insn, 16, 5, rt);
+ insn = deposit32(insn, 11, 5, rd);
+ insn = deposit32(insn, 6, 5, shift);
+ insn = deposit32(insn, 0, 6, funct);
+
+ stl_p(*p, insn);
+ *p = *p + 1;
+}
+
+static void bl_gen_i_type(uint32_t **p, uint8_t opcode,
+ bl_reg rs, bl_reg rt, uint16_t imm)
+{
+ uint32_t insn = 0;
+
+ insn = deposit32(insn, 26, 6, opcode);
+ insn = deposit32(insn, 21, 5, rs);
+ insn = deposit32(insn, 16, 5, rt);
+ insn = deposit32(insn, 0, 16, imm);
+
+ stl_p(*p, insn);
+ *p = *p + 1;
+}
+
+/* Single instructions */
+static void bl_gen_dsll(uint32_t **p, bl_reg rd, bl_reg rt, uint8_t sa)
+{
+ if (bootcpu_supports_isa(ISA_MIPS3)) {
+ bl_gen_r_type(p, 0, 0, rt, rd, sa, 0x38);
+ } else {
+ g_assert_not_reached(); /* unsupported */
+ }
+}
+
+static void bl_gen_jalr(uint32_t **p, bl_reg rs)
+{
+ bl_gen_r_type(p, 0, rs, 0, BL_REG_RA, 0, 0x09);
+}
+
+static void bl_gen_lui(uint32_t **p, bl_reg rt, uint16_t imm)
+{
+ /* R6: It's a alias of AUI with RS = 0 */
+ bl_gen_i_type(p, 0x0f, 0, rt, imm);
+}
+
+static void bl_gen_ori(uint32_t **p, bl_reg rt, bl_reg rs, uint16_t imm)
+{
+ bl_gen_i_type(p, 0x0d, rs, rt, imm);
+}
+
+static void bl_gen_sw(uint32_t **p, bl_reg rt, uint8_t base, uint16_t offset)
+{
+ bl_gen_i_type(p, 0x2b, base, rt, offset);
+}
+
+static void bl_gen_sd(uint32_t **p, bl_reg rt, uint8_t base, uint16_t offset)
+{
+ if (bootcpu_supports_isa(ISA_MIPS3)) {
+ bl_gen_i_type(p, 0x3f, base, rt, offset);
+ } else {
+ g_assert_not_reached(); /* unsupported */
+ }
+}
+
+/* Pseudo instructions */
+static void bl_gen_li(uint32_t **p, bl_reg rt, uint32_t imm)
+{
+ bl_gen_lui(p, rt, extract32(imm, 16, 16));
+ bl_gen_ori(p, rt, rt, extract32(imm, 0, 16));
+}
+
+static void bl_gen_dli(uint32_t **p, bl_reg rt, uint64_t imm)
+{
+ bl_gen_li(p, rt, extract64(imm, 32, 32));
+ bl_gen_dsll(p, rt, rt, 16);
+ bl_gen_ori(p, rt, rt, extract64(imm, 16, 16));
+ bl_gen_dsll(p, rt, rt, 16);
+ bl_gen_ori(p, rt, rt, extract64(imm, 0, 16));
+}
+
+static void bl_gen_load_ulong(uint32_t **p, bl_reg rt, target_ulong imm)
+{
+ if (bootcpu_supports_isa(ISA_MIPS3)) {
+ bl_gen_dli(p, rt, imm); /* 64bit */
+ } else {
+ bl_gen_li(p, rt, imm); /* 32bit */
+ }
+}
+
+/* Helpers */
+void bl_gen_jump_to(uint32_t **p, target_ulong jump_addr)
+{
+ bl_gen_load_ulong(p, BL_REG_T9, jump_addr);
+ bl_gen_jalr(p, BL_REG_T9);
+ bl_gen_nop(p); /* delay slot */
+}
+
+void bl_gen_jump_kernel(uint32_t **p, target_ulong sp, target_ulong a0,
+ target_ulong a1, target_ulong a2, target_ulong a3,
+ target_ulong kernel_addr)
+{
+ bl_gen_load_ulong(p, BL_REG_SP, sp);
+ bl_gen_load_ulong(p, BL_REG_A0, a0);
+ bl_gen_load_ulong(p, BL_REG_A1, a1);
+ bl_gen_load_ulong(p, BL_REG_A2, a2);
+ bl_gen_load_ulong(p, BL_REG_A3, a3);
+
+ bl_gen_jump_to(p, kernel_addr);
+}
+
+void bl_gen_write_ulong(uint32_t **p, target_ulong addr, target_ulong val)
+{
+ bl_gen_load_ulong(p, BL_REG_K0, val);
+ bl_gen_load_ulong(p, BL_REG_K1, addr);
+ if (bootcpu_supports_isa(ISA_MIPS3)) {
+ bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0);
+ } else {
+ bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0);
+ }
+}
+
+void bl_gen_write_u32(uint32_t **p, target_ulong addr, uint32_t val)
+{
+ bl_gen_li(p, BL_REG_K0, val);
+ bl_gen_load_ulong(p, BL_REG_K1, addr);
+ bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0);
+}
+
+void bl_gen_write_u64(uint32_t **p, target_ulong addr, uint64_t val)
+{
+ bl_gen_dli(p, BL_REG_K0, val);
+ bl_gen_load_ulong(p, BL_REG_K1, addr);
+ bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0);
+}
diff --git a/hw/mips/boston.c b/hw/mips/boston.c
new file mode 100644
index 000000000..59ca08b93
--- /dev/null
+++ b/hw/mips/boston.c
@@ -0,0 +1,835 @@
+/*
+ * MIPS Boston development board emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+
+#include "elf.h"
+#include "hw/boards.h"
+#include "hw/char/serial.h"
+#include "hw/ide/pci.h"
+#include "hw/ide/ahci.h"
+#include "hw/loader.h"
+#include "hw/loader-fit.h"
+#include "hw/mips/bootloader.h"
+#include "hw/mips/cps.h"
+#include "hw/pci-host/xilinx-pcie.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "chardev/char.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "sysemu/runstate.h"
+
+#include <libfdt.h>
+#include "qom/object.h"
+
+#define TYPE_BOSTON "mips-boston"
+typedef struct BostonState BostonState;
+DECLARE_INSTANCE_CHECKER(BostonState, BOSTON,
+ TYPE_BOSTON)
+
+#define FDT_IRQ_TYPE_NONE 0
+#define FDT_IRQ_TYPE_LEVEL_HIGH 4
+#define FDT_GIC_SHARED 0
+#define FDT_GIC_LOCAL 1
+#define FDT_BOSTON_CLK_SYS 1
+#define FDT_BOSTON_CLK_CPU 2
+#define FDT_PCI_IRQ_MAP_PINS 4
+#define FDT_PCI_IRQ_MAP_DESCS 6
+
+struct BostonState {
+ SysBusDevice parent_obj;
+
+ MachineState *mach;
+ MIPSCPSState cps;
+ SerialMM *uart;
+ Clock *cpuclk;
+
+ CharBackend lcd_display;
+ char lcd_content[8];
+ bool lcd_inited;
+
+ hwaddr kernel_entry;
+ hwaddr fdt_base;
+};
+
+enum {
+ BOSTON_LOWDDR,
+ BOSTON_PCIE0,
+ BOSTON_PCIE1,
+ BOSTON_PCIE2,
+ BOSTON_PCIE2_MMIO,
+ BOSTON_CM,
+ BOSTON_GIC,
+ BOSTON_CDMM,
+ BOSTON_CPC,
+ BOSTON_PLATREG,
+ BOSTON_UART,
+ BOSTON_LCD,
+ BOSTON_FLASH,
+ BOSTON_PCIE1_MMIO,
+ BOSTON_PCIE0_MMIO,
+ BOSTON_HIGHDDR,
+};
+
+static const MemMapEntry boston_memmap[] = {
+ [BOSTON_LOWDDR] = { 0x0, 0x10000000 },
+ [BOSTON_PCIE0] = { 0x10000000, 0x2000000 },
+ [BOSTON_PCIE1] = { 0x12000000, 0x2000000 },
+ [BOSTON_PCIE2] = { 0x14000000, 0x2000000 },
+ [BOSTON_PCIE2_MMIO] = { 0x16000000, 0x100000 },
+ [BOSTON_CM] = { 0x16100000, 0x20000 },
+ [BOSTON_GIC] = { 0x16120000, 0x20000 },
+ [BOSTON_CDMM] = { 0x16140000, 0x8000 },
+ [BOSTON_CPC] = { 0x16200000, 0x8000 },
+ [BOSTON_PLATREG] = { 0x17ffd000, 0x1000 },
+ [BOSTON_UART] = { 0x17ffe000, 0x20 },
+ [BOSTON_LCD] = { 0x17fff000, 0x8 },
+ [BOSTON_FLASH] = { 0x18000000, 0x8000000 },
+ [BOSTON_PCIE1_MMIO] = { 0x20000000, 0x20000000 },
+ [BOSTON_PCIE0_MMIO] = { 0x40000000, 0x40000000 },
+ [BOSTON_HIGHDDR] = { 0x80000000, 0x0 },
+};
+
+enum boston_plat_reg {
+ PLAT_FPGA_BUILD = 0x00,
+ PLAT_CORE_CL = 0x04,
+ PLAT_WRAPPER_CL = 0x08,
+ PLAT_SYSCLK_STATUS = 0x0c,
+ PLAT_SOFTRST_CTL = 0x10,
+#define PLAT_SOFTRST_CTL_SYSRESET (1 << 4)
+ PLAT_DDR3_STATUS = 0x14,
+#define PLAT_DDR3_STATUS_LOCKED (1 << 0)
+#define PLAT_DDR3_STATUS_CALIBRATED (1 << 2)
+ PLAT_PCIE_STATUS = 0x18,
+#define PLAT_PCIE_STATUS_PCIE0_LOCKED (1 << 0)
+#define PLAT_PCIE_STATUS_PCIE1_LOCKED (1 << 8)
+#define PLAT_PCIE_STATUS_PCIE2_LOCKED (1 << 16)
+ PLAT_FLASH_CTL = 0x1c,
+ PLAT_SPARE0 = 0x20,
+ PLAT_SPARE1 = 0x24,
+ PLAT_SPARE2 = 0x28,
+ PLAT_SPARE3 = 0x2c,
+ PLAT_MMCM_DIV = 0x30,
+#define PLAT_MMCM_DIV_CLK0DIV_SHIFT 0
+#define PLAT_MMCM_DIV_INPUT_SHIFT 8
+#define PLAT_MMCM_DIV_MUL_SHIFT 16
+#define PLAT_MMCM_DIV_CLK1DIV_SHIFT 24
+ PLAT_BUILD_CFG = 0x34,
+#define PLAT_BUILD_CFG_IOCU_EN (1 << 0)
+#define PLAT_BUILD_CFG_PCIE0_EN (1 << 1)
+#define PLAT_BUILD_CFG_PCIE1_EN (1 << 2)
+#define PLAT_BUILD_CFG_PCIE2_EN (1 << 3)
+ PLAT_DDR_CFG = 0x38,
+#define PLAT_DDR_CFG_SIZE (0xf << 0)
+#define PLAT_DDR_CFG_MHZ (0xfff << 4)
+ PLAT_NOC_PCIE0_ADDR = 0x3c,
+ PLAT_NOC_PCIE1_ADDR = 0x40,
+ PLAT_NOC_PCIE2_ADDR = 0x44,
+ PLAT_SYS_CTL = 0x48,
+};
+
+static void boston_lcd_event(void *opaque, QEMUChrEvent event)
+{
+ BostonState *s = opaque;
+ if (event == CHR_EVENT_OPENED && !s->lcd_inited) {
+ qemu_chr_fe_printf(&s->lcd_display, " ");
+ s->lcd_inited = true;
+ }
+}
+
+static uint64_t boston_lcd_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ BostonState *s = opaque;
+ uint64_t val = 0;
+
+ switch (size) {
+ case 8:
+ val |= (uint64_t)s->lcd_content[(addr + 7) & 0x7] << 56;
+ val |= (uint64_t)s->lcd_content[(addr + 6) & 0x7] << 48;
+ val |= (uint64_t)s->lcd_content[(addr + 5) & 0x7] << 40;
+ val |= (uint64_t)s->lcd_content[(addr + 4) & 0x7] << 32;
+ /* fall through */
+ case 4:
+ val |= (uint64_t)s->lcd_content[(addr + 3) & 0x7] << 24;
+ val |= (uint64_t)s->lcd_content[(addr + 2) & 0x7] << 16;
+ /* fall through */
+ case 2:
+ val |= (uint64_t)s->lcd_content[(addr + 1) & 0x7] << 8;
+ /* fall through */
+ case 1:
+ val |= (uint64_t)s->lcd_content[(addr + 0) & 0x7];
+ break;
+ }
+
+ return val;
+}
+
+static void boston_lcd_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ BostonState *s = opaque;
+
+ switch (size) {
+ case 8:
+ s->lcd_content[(addr + 7) & 0x7] = val >> 56;
+ s->lcd_content[(addr + 6) & 0x7] = val >> 48;
+ s->lcd_content[(addr + 5) & 0x7] = val >> 40;
+ s->lcd_content[(addr + 4) & 0x7] = val >> 32;
+ /* fall through */
+ case 4:
+ s->lcd_content[(addr + 3) & 0x7] = val >> 24;
+ s->lcd_content[(addr + 2) & 0x7] = val >> 16;
+ /* fall through */
+ case 2:
+ s->lcd_content[(addr + 1) & 0x7] = val >> 8;
+ /* fall through */
+ case 1:
+ s->lcd_content[(addr + 0) & 0x7] = val;
+ break;
+ }
+
+ qemu_chr_fe_printf(&s->lcd_display,
+ "\r%-8.8s", s->lcd_content);
+}
+
+static const MemoryRegionOps boston_lcd_ops = {
+ .read = boston_lcd_read,
+ .write = boston_lcd_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint64_t boston_platreg_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ BostonState *s = opaque;
+ uint32_t gic_freq, val;
+
+ if (size != 4) {
+ qemu_log_mask(LOG_UNIMP, "%uB platform register read\n", size);
+ return 0;
+ }
+
+ switch (addr & 0xffff) {
+ case PLAT_FPGA_BUILD:
+ case PLAT_CORE_CL:
+ case PLAT_WRAPPER_CL:
+ return 0;
+ case PLAT_DDR3_STATUS:
+ return PLAT_DDR3_STATUS_LOCKED | PLAT_DDR3_STATUS_CALIBRATED;
+ case PLAT_MMCM_DIV:
+ gic_freq = mips_gictimer_get_freq(s->cps.gic.gic_timer) / 1000000;
+ val = gic_freq << PLAT_MMCM_DIV_INPUT_SHIFT;
+ val |= 1 << PLAT_MMCM_DIV_MUL_SHIFT;
+ val |= 1 << PLAT_MMCM_DIV_CLK0DIV_SHIFT;
+ val |= 1 << PLAT_MMCM_DIV_CLK1DIV_SHIFT;
+ return val;
+ case PLAT_BUILD_CFG:
+ val = PLAT_BUILD_CFG_PCIE0_EN;
+ val |= PLAT_BUILD_CFG_PCIE1_EN;
+ val |= PLAT_BUILD_CFG_PCIE2_EN;
+ return val;
+ case PLAT_DDR_CFG:
+ val = s->mach->ram_size / GiB;
+ assert(!(val & ~PLAT_DDR_CFG_SIZE));
+ val |= PLAT_DDR_CFG_MHZ;
+ return val;
+ default:
+ qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx "\n",
+ addr & 0xffff);
+ return 0;
+ }
+}
+
+static void boston_platreg_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ if (size != 4) {
+ qemu_log_mask(LOG_UNIMP, "%uB platform register write\n", size);
+ return;
+ }
+
+ switch (addr & 0xffff) {
+ case PLAT_FPGA_BUILD:
+ case PLAT_CORE_CL:
+ case PLAT_WRAPPER_CL:
+ case PLAT_DDR3_STATUS:
+ case PLAT_PCIE_STATUS:
+ case PLAT_MMCM_DIV:
+ case PLAT_BUILD_CFG:
+ case PLAT_DDR_CFG:
+ /* read only */
+ break;
+ case PLAT_SOFTRST_CTL:
+ if (val & PLAT_SOFTRST_CTL_SYSRESET) {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "Write platform register 0x%" HWADDR_PRIx
+ " = 0x%" PRIx64 "\n", addr & 0xffff, val);
+ break;
+ }
+}
+
+static const MemoryRegionOps boston_platreg_ops = {
+ .read = boston_platreg_read,
+ .write = boston_platreg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void mips_boston_instance_init(Object *obj)
+{
+ BostonState *s = BOSTON(obj);
+
+ s->cpuclk = qdev_init_clock_out(DEVICE(obj), "cpu-refclk");
+ clock_set_hz(s->cpuclk, 1000000000); /* 1 GHz */
+}
+
+static const TypeInfo boston_device = {
+ .name = TYPE_BOSTON,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BostonState),
+ .instance_init = mips_boston_instance_init,
+};
+
+static void boston_register_types(void)
+{
+ type_register_static(&boston_device);
+}
+type_init(boston_register_types)
+
+static void gen_firmware(uint32_t *p, hwaddr kernel_entry, hwaddr fdt_addr)
+{
+ uint64_t regaddr;
+
+ /* Move CM GCRs */
+ regaddr = cpu_mips_phys_to_kseg1(NULL, GCR_BASE_ADDR + GCR_BASE_OFS),
+ bl_gen_write_ulong(&p, regaddr,
+ boston_memmap[BOSTON_CM].base);
+
+ /* Move & enable GIC GCRs */
+ regaddr = cpu_mips_phys_to_kseg1(NULL, boston_memmap[BOSTON_CM].base
+ + GCR_GIC_BASE_OFS),
+ bl_gen_write_ulong(&p, regaddr,
+ boston_memmap[BOSTON_GIC].base | GCR_GIC_BASE_GICEN_MSK);
+
+ /* Move & enable CPC GCRs */
+ regaddr = cpu_mips_phys_to_kseg1(NULL, boston_memmap[BOSTON_CM].base
+ + GCR_CPC_BASE_OFS),
+ bl_gen_write_ulong(&p, regaddr,
+ boston_memmap[BOSTON_CPC].base | GCR_CPC_BASE_CPCEN_MSK);
+
+ /*
+ * Setup argument registers to follow the UHI boot protocol:
+ *
+ * a0/$4 = -2
+ * a1/$5 = virtual address of FDT
+ * a2/$6 = 0
+ * a3/$7 = 0
+ */
+ bl_gen_jump_kernel(&p, 0, (int32_t)-2, fdt_addr, 0, 0, kernel_entry);
+}
+
+static const void *boston_fdt_filter(void *opaque, const void *fdt_orig,
+ const void *match_data, hwaddr *load_addr)
+{
+ BostonState *s = BOSTON(opaque);
+ MachineState *machine = s->mach;
+ const char *cmdline;
+ int err;
+ size_t ram_low_sz, ram_high_sz;
+ size_t fdt_sz = fdt_totalsize(fdt_orig) * 2;
+ g_autofree void *fdt = g_malloc0(fdt_sz);
+
+ err = fdt_open_into(fdt_orig, fdt, fdt_sz);
+ if (err) {
+ fprintf(stderr, "unable to open FDT\n");
+ return NULL;
+ }
+
+ cmdline = (machine->kernel_cmdline && machine->kernel_cmdline[0])
+ ? machine->kernel_cmdline : " ";
+ err = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ if (err < 0) {
+ fprintf(stderr, "couldn't set /chosen/bootargs\n");
+ return NULL;
+ }
+
+ ram_low_sz = MIN(256 * MiB, machine->ram_size);
+ ram_high_sz = machine->ram_size - ram_low_sz;
+ qemu_fdt_setprop_sized_cells(fdt, "/memory@0", "reg",
+ 1, boston_memmap[BOSTON_LOWDDR].base, 1, ram_low_sz,
+ 1, boston_memmap[BOSTON_HIGHDDR].base + ram_low_sz,
+ 1, ram_high_sz);
+
+ fdt = g_realloc(fdt, fdt_totalsize(fdt));
+ qemu_fdt_dumpdtb(fdt, fdt_sz);
+
+ s->fdt_base = *load_addr;
+
+ return g_steal_pointer(&fdt);
+}
+
+static const void *boston_kernel_filter(void *opaque, const void *kernel,
+ hwaddr *load_addr, hwaddr *entry_addr)
+{
+ BostonState *s = BOSTON(opaque);
+
+ s->kernel_entry = *entry_addr;
+
+ return kernel;
+}
+
+static const struct fit_loader_match boston_matches[] = {
+ { "img,boston" },
+ { NULL },
+};
+
+static const struct fit_loader boston_fit_loader = {
+ .matches = boston_matches,
+ .addr_to_phys = cpu_mips_kseg0_to_phys,
+ .fdt_filter = boston_fdt_filter,
+ .kernel_filter = boston_kernel_filter,
+};
+
+static inline XilinxPCIEHost *
+xilinx_pcie_init(MemoryRegion *sys_mem, uint32_t bus_nr,
+ hwaddr cfg_base, uint64_t cfg_size,
+ hwaddr mmio_base, uint64_t mmio_size,
+ qemu_irq irq, bool link_up)
+{
+ DeviceState *dev;
+ MemoryRegion *cfg, *mmio;
+
+ dev = qdev_new(TYPE_XILINX_PCIE_HOST);
+
+ qdev_prop_set_uint32(dev, "bus_nr", bus_nr);
+ qdev_prop_set_uint64(dev, "cfg_base", cfg_base);
+ qdev_prop_set_uint64(dev, "cfg_size", cfg_size);
+ qdev_prop_set_uint64(dev, "mmio_base", mmio_base);
+ qdev_prop_set_uint64(dev, "mmio_size", mmio_size);
+ qdev_prop_set_bit(dev, "link_up", link_up);
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ cfg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+ memory_region_add_subregion_overlap(sys_mem, cfg_base, cfg, 0);
+
+ mmio = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
+ memory_region_add_subregion_overlap(sys_mem, 0, mmio, 0);
+
+ qdev_connect_gpio_out_named(dev, "interrupt_out", 0, irq);
+
+ return XILINX_PCIE_HOST(dev);
+}
+
+
+static void fdt_create_pcie(void *fdt, int gic_ph, int irq, hwaddr reg_base,
+ hwaddr reg_size, hwaddr mmio_base, hwaddr mmio_size)
+{
+ int i;
+ char *name, *intc_name;
+ uint32_t intc_ph;
+ uint32_t interrupt_map[FDT_PCI_IRQ_MAP_PINS][FDT_PCI_IRQ_MAP_DESCS];
+
+ intc_ph = qemu_fdt_alloc_phandle(fdt);
+ name = g_strdup_printf("/soc/pci@%" HWADDR_PRIx, reg_base);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible",
+ "xlnx,axi-pcie-host-1.00.a");
+ qemu_fdt_setprop_string(fdt, name, "device_type", "pci");
+ qemu_fdt_setprop_cells(fdt, name, "reg", reg_base, reg_size);
+
+ qemu_fdt_setprop_cell(fdt, name, "#address-cells", 3);
+ qemu_fdt_setprop_cell(fdt, name, "#size-cells", 2);
+ qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 1);
+
+ qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", gic_ph);
+ qemu_fdt_setprop_cells(fdt, name, "interrupts", FDT_GIC_SHARED, irq,
+ FDT_IRQ_TYPE_LEVEL_HIGH);
+
+ qemu_fdt_setprop_cells(fdt, name, "ranges", 0x02000000, 0, mmio_base,
+ mmio_base, 0, mmio_size);
+ qemu_fdt_setprop_cells(fdt, name, "bus-range", 0x00, 0xff);
+
+
+
+ intc_name = g_strdup_printf("%s/interrupt-controller", name);
+ qemu_fdt_add_subnode(fdt, intc_name);
+ qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(fdt, intc_name, "#address-cells", 0);
+ qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
+ qemu_fdt_setprop_cell(fdt, intc_name, "phandle", intc_ph);
+
+ qemu_fdt_setprop_cells(fdt, name, "interrupt-map-mask", 0, 0, 0, 7);
+ for (i = 0; i < FDT_PCI_IRQ_MAP_PINS; i++) {
+ uint32_t *irqmap = interrupt_map[i];
+
+ irqmap[0] = cpu_to_be32(0);
+ irqmap[1] = cpu_to_be32(0);
+ irqmap[2] = cpu_to_be32(0);
+ irqmap[3] = cpu_to_be32(i + 1);
+ irqmap[4] = cpu_to_be32(intc_ph);
+ irqmap[5] = cpu_to_be32(i + 1);
+ }
+ qemu_fdt_setprop(fdt, name, "interrupt-map",
+ &interrupt_map, sizeof(interrupt_map));
+
+ g_free(intc_name);
+ g_free(name);
+}
+
+static const void *create_fdt(BostonState *s,
+ const MemMapEntry *memmap, int *dt_size)
+{
+ void *fdt;
+ int cpu;
+ MachineState *mc = s->mach;
+ uint32_t platreg_ph, gic_ph, clk_ph;
+ char *name, *gic_name, *platreg_name, *stdout_name;
+ static const char * const syscon_compat[2] = {
+ "img,boston-platform-regs", "syscon"
+ };
+
+ fdt = create_device_tree(dt_size);
+ if (!fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+
+ platreg_ph = qemu_fdt_alloc_phandle(fdt);
+ gic_ph = qemu_fdt_alloc_phandle(fdt);
+ clk_ph = qemu_fdt_alloc_phandle(fdt);
+
+ qemu_fdt_setprop_string(fdt, "/", "model", "img,boston");
+ qemu_fdt_setprop_string(fdt, "/", "compatible", "img,boston");
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1);
+ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x1);
+
+
+ qemu_fdt_add_subnode(fdt, "/cpus");
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+
+ for (cpu = 0; cpu < mc->smp.cpus; cpu++) {
+ name = g_strdup_printf("/cpus/cpu@%d", cpu);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "img,mips");
+ qemu_fdt_setprop_string(fdt, name, "status", "okay");
+ qemu_fdt_setprop_cell(fdt, name, "reg", cpu);
+ qemu_fdt_setprop_string(fdt, name, "device_type", "cpu");
+ qemu_fdt_setprop_cells(fdt, name, "clocks", clk_ph, FDT_BOSTON_CLK_CPU);
+ g_free(name);
+ }
+
+ qemu_fdt_add_subnode(fdt, "/soc");
+ qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+ qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
+ qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1);
+ qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x1);
+
+ fdt_create_pcie(fdt, gic_ph, 2,
+ memmap[BOSTON_PCIE0].base, memmap[BOSTON_PCIE0].size,
+ memmap[BOSTON_PCIE0_MMIO].base, memmap[BOSTON_PCIE0_MMIO].size);
+
+ fdt_create_pcie(fdt, gic_ph, 1,
+ memmap[BOSTON_PCIE1].base, memmap[BOSTON_PCIE1].size,
+ memmap[BOSTON_PCIE1_MMIO].base, memmap[BOSTON_PCIE1_MMIO].size);
+
+ fdt_create_pcie(fdt, gic_ph, 0,
+ memmap[BOSTON_PCIE2].base, memmap[BOSTON_PCIE2].size,
+ memmap[BOSTON_PCIE2_MMIO].base, memmap[BOSTON_PCIE2_MMIO].size);
+
+ /* GIC with it's timer node */
+ gic_name = g_strdup_printf("/soc/interrupt-controller@%" HWADDR_PRIx,
+ memmap[BOSTON_GIC].base);
+ qemu_fdt_add_subnode(fdt, gic_name);
+ qemu_fdt_setprop_string(fdt, gic_name, "compatible", "mti,gic");
+ qemu_fdt_setprop_cells(fdt, gic_name, "reg", memmap[BOSTON_GIC].base,
+ memmap[BOSTON_GIC].size);
+ qemu_fdt_setprop(fdt, gic_name, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(fdt, gic_name, "#interrupt-cells", 3);
+ qemu_fdt_setprop_cell(fdt, gic_name, "phandle", gic_ph);
+
+ name = g_strdup_printf("%s/timer", gic_name);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "mti,gic-timer");
+ qemu_fdt_setprop_cells(fdt, name, "interrupts", FDT_GIC_LOCAL, 1,
+ FDT_IRQ_TYPE_NONE);
+ qemu_fdt_setprop_cells(fdt, name, "clocks", clk_ph, FDT_BOSTON_CLK_CPU);
+ g_free(name);
+ g_free(gic_name);
+
+ /* CDMM node */
+ name = g_strdup_printf("/soc/cdmm@%" HWADDR_PRIx, memmap[BOSTON_CDMM].base);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "mti,mips-cdmm");
+ qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_CDMM].base,
+ memmap[BOSTON_CDMM].size);
+ g_free(name);
+
+ /* CPC node */
+ name = g_strdup_printf("/soc/cpc@%" HWADDR_PRIx, memmap[BOSTON_CPC].base);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "mti,mips-cpc");
+ qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_CPC].base,
+ memmap[BOSTON_CPC].size);
+ g_free(name);
+
+ /* platreg and it's clk node */
+ platreg_name = g_strdup_printf("/soc/system-controller@%" HWADDR_PRIx,
+ memmap[BOSTON_PLATREG].base);
+ qemu_fdt_add_subnode(fdt, platreg_name);
+ qemu_fdt_setprop_string_array(fdt, platreg_name, "compatible",
+ (char **)&syscon_compat,
+ ARRAY_SIZE(syscon_compat));
+ qemu_fdt_setprop_cells(fdt, platreg_name, "reg",
+ memmap[BOSTON_PLATREG].base,
+ memmap[BOSTON_PLATREG].size);
+ qemu_fdt_setprop_cell(fdt, platreg_name, "phandle", platreg_ph);
+
+ name = g_strdup_printf("%s/clock", platreg_name);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "img,boston-clock");
+ qemu_fdt_setprop_cell(fdt, name, "#clock-cells", 1);
+ qemu_fdt_setprop_cell(fdt, name, "phandle", clk_ph);
+ g_free(name);
+ g_free(platreg_name);
+
+ /* reboot node */
+ name = g_strdup_printf("/soc/reboot");
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-reboot");
+ qemu_fdt_setprop_cell(fdt, name, "regmap", platreg_ph);
+ qemu_fdt_setprop_cell(fdt, name, "offset", 0x10);
+ qemu_fdt_setprop_cell(fdt, name, "mask", 0x10);
+ g_free(name);
+
+ /* uart node */
+ name = g_strdup_printf("/soc/uart@%" HWADDR_PRIx, memmap[BOSTON_UART].base);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a");
+ qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_UART].base,
+ memmap[BOSTON_UART].size);
+ qemu_fdt_setprop_cell(fdt, name, "reg-shift", 0x2);
+ qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", gic_ph);
+ qemu_fdt_setprop_cells(fdt, name, "interrupts", FDT_GIC_SHARED, 3,
+ FDT_IRQ_TYPE_LEVEL_HIGH);
+ qemu_fdt_setprop_cells(fdt, name, "clocks", clk_ph, FDT_BOSTON_CLK_SYS);
+
+ qemu_fdt_add_subnode(fdt, "/chosen");
+ stdout_name = g_strdup_printf("%s:115200", name);
+ qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", stdout_name);
+ g_free(stdout_name);
+ g_free(name);
+
+ /* lcd node */
+ name = g_strdup_printf("/soc/lcd@%" HWADDR_PRIx, memmap[BOSTON_LCD].base);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "img,boston-lcd");
+ qemu_fdt_setprop_cells(fdt, name, "reg", memmap[BOSTON_LCD].base,
+ memmap[BOSTON_LCD].size);
+ g_free(name);
+
+ name = g_strdup_printf("/memory@0");
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "device_type", "memory");
+ g_free(name);
+
+ return fdt;
+}
+
+static void boston_mach_init(MachineState *machine)
+{
+ DeviceState *dev;
+ BostonState *s;
+ MemoryRegion *flash, *ddr_low_alias, *lcd, *platreg;
+ MemoryRegion *sys_mem = get_system_memory();
+ XilinxPCIEHost *pcie2;
+ PCIDevice *ahci;
+ DriveInfo *hd[6];
+ Chardev *chr;
+ int fw_size, fit_err;
+
+ if ((machine->ram_size % GiB) ||
+ (machine->ram_size > (2 * GiB))) {
+ error_report("Memory size must be 1GB or 2GB");
+ exit(1);
+ }
+
+ dev = qdev_new(TYPE_BOSTON);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ s = BOSTON(dev);
+ s->mach = machine;
+
+ if (!cpu_type_supports_cps_smp(machine->cpu_type)) {
+ error_report("Boston requires CPUs which support CPS");
+ exit(1);
+ }
+
+ object_initialize_child(OBJECT(machine), "cps", &s->cps, TYPE_MIPS_CPS);
+ object_property_set_str(OBJECT(&s->cps), "cpu-type", machine->cpu_type,
+ &error_fatal);
+ object_property_set_int(OBJECT(&s->cps), "num-vp", machine->smp.cpus,
+ &error_fatal);
+ qdev_connect_clock_in(DEVICE(&s->cps), "clk-in",
+ qdev_get_clock_out(dev, "cpu-refclk"));
+ sysbus_realize(SYS_BUS_DEVICE(&s->cps), &error_fatal);
+
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->cps), 0, 0, 1);
+
+ flash = g_new(MemoryRegion, 1);
+ memory_region_init_rom(flash, NULL, "boston.flash",
+ boston_memmap[BOSTON_FLASH].size, &error_fatal);
+ memory_region_add_subregion_overlap(sys_mem,
+ boston_memmap[BOSTON_FLASH].base,
+ flash, 0);
+
+ memory_region_add_subregion_overlap(sys_mem,
+ boston_memmap[BOSTON_HIGHDDR].base,
+ machine->ram, 0);
+
+ ddr_low_alias = g_new(MemoryRegion, 1);
+ memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr",
+ machine->ram, 0,
+ MIN(machine->ram_size, (256 * MiB)));
+ memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0);
+
+ xilinx_pcie_init(sys_mem, 0,
+ boston_memmap[BOSTON_PCIE0].base,
+ boston_memmap[BOSTON_PCIE0].size,
+ boston_memmap[BOSTON_PCIE0_MMIO].base,
+ boston_memmap[BOSTON_PCIE0_MMIO].size,
+ get_cps_irq(&s->cps, 2), false);
+
+ xilinx_pcie_init(sys_mem, 1,
+ boston_memmap[BOSTON_PCIE1].base,
+ boston_memmap[BOSTON_PCIE1].size,
+ boston_memmap[BOSTON_PCIE1_MMIO].base,
+ boston_memmap[BOSTON_PCIE1_MMIO].size,
+ get_cps_irq(&s->cps, 1), false);
+
+ pcie2 = xilinx_pcie_init(sys_mem, 2,
+ boston_memmap[BOSTON_PCIE2].base,
+ boston_memmap[BOSTON_PCIE2].size,
+ boston_memmap[BOSTON_PCIE2_MMIO].base,
+ boston_memmap[BOSTON_PCIE2_MMIO].size,
+ get_cps_irq(&s->cps, 0), true);
+
+ platreg = g_new(MemoryRegion, 1);
+ memory_region_init_io(platreg, NULL, &boston_platreg_ops, s,
+ "boston-platregs",
+ boston_memmap[BOSTON_PLATREG].size);
+ memory_region_add_subregion_overlap(sys_mem,
+ boston_memmap[BOSTON_PLATREG].base, platreg, 0);
+
+ s->uart = serial_mm_init(sys_mem, boston_memmap[BOSTON_UART].base, 2,
+ get_cps_irq(&s->cps, 3), 10000000,
+ serial_hd(0), DEVICE_NATIVE_ENDIAN);
+
+ lcd = g_new(MemoryRegion, 1);
+ memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8);
+ memory_region_add_subregion_overlap(sys_mem,
+ boston_memmap[BOSTON_LCD].base, lcd, 0);
+
+ chr = qemu_chr_new("lcd", "vc:320x240", NULL);
+ qemu_chr_fe_init(&s->lcd_display, chr, NULL);
+ qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL,
+ boston_lcd_event, NULL, s, NULL, true);
+
+ ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus,
+ PCI_DEVFN(0, 0),
+ true, TYPE_ICH9_AHCI);
+ g_assert(ARRAY_SIZE(hd) == ahci_get_num_ports(ahci));
+ ide_drive_get(hd, ahci_get_num_ports(ahci));
+ ahci_ide_create_devs(ahci, hd);
+
+ if (machine->firmware) {
+ fw_size = load_image_targphys(machine->firmware,
+ 0x1fc00000, 4 * MiB);
+ if (fw_size == -1) {
+ error_report("unable to load firmware image '%s'",
+ machine->firmware);
+ exit(1);
+ }
+ } else if (machine->kernel_filename) {
+ uint64_t kernel_entry, kernel_high;
+ ssize_t kernel_size;
+
+ kernel_size = load_elf(machine->kernel_filename, NULL,
+ cpu_mips_kseg0_to_phys, NULL,
+ &kernel_entry, NULL, &kernel_high,
+ NULL, 0, EM_MIPS, 1, 0);
+
+ if (kernel_size > 0) {
+ int dt_size;
+ g_autofree const void *dtb_file_data, *dtb_load_data;
+ hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB);
+ hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr);
+
+ s->kernel_entry = kernel_entry;
+ if (machine->dtb) {
+ dtb_file_data = load_device_tree(machine->dtb, &dt_size);
+ } else {
+ dtb_file_data = create_fdt(s, boston_memmap, &dt_size);
+ }
+
+ dtb_load_data = boston_fdt_filter(s, dtb_file_data,
+ NULL, &dtb_vaddr);
+
+ /* Calculate real fdt size after filter */
+ dt_size = fdt_totalsize(dtb_load_data);
+ rom_add_blob_fixed("dtb", dtb_load_data, dt_size, dtb_paddr);
+ } else {
+ /* Try to load file as FIT */
+ fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s);
+ if (fit_err) {
+ error_report("unable to load kernel image");
+ exit(1);
+ }
+ }
+
+ gen_firmware(memory_region_get_ram_ptr(flash) + 0x7c00000,
+ s->kernel_entry, s->fdt_base);
+ } else if (!qtest_enabled()) {
+ error_report("Please provide either a -kernel or -bios argument");
+ exit(1);
+ }
+}
+
+static void boston_mach_class_init(MachineClass *mc)
+{
+ mc->desc = "MIPS Boston";
+ mc->init = boston_mach_init;
+ mc->block_default_type = IF_IDE;
+ mc->default_ram_size = 1 * GiB;
+ mc->default_ram_id = "boston.ddr";
+ mc->max_cpus = 16;
+ mc->default_cpu_type = MIPS_CPU_TYPE_NAME("I6400");
+}
+
+DEFINE_MACHINE("boston", boston_mach_class_init)
diff --git a/hw/mips/cps.c b/hw/mips/cps.c
new file mode 100644
index 000000000..2b436700c
--- /dev/null
+++ b/hw/mips/cps.c
@@ -0,0 +1,210 @@
+/*
+ * Coherent Processing System emulation.
+ *
+ * Copyright (c) 2016 Imagination Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "hw/mips/cps.h"
+#include "hw/mips/mips.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "hw/mips/cpudevs.h"
+#include "sysemu/kvm.h"
+#include "sysemu/reset.h"
+
+qemu_irq get_cps_irq(MIPSCPSState *s, int pin_number)
+{
+ assert(pin_number < s->num_irq);
+ return s->gic.irq_state[pin_number].irq;
+}
+
+static void mips_cps_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MIPSCPSState *s = MIPS_CPS(obj);
+
+ s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL, 0);
+ /*
+ * Cover entire address space as there do not seem to be any
+ * constraints for the base address of CPC and GIC.
+ */
+ memory_region_init(&s->container, obj, "mips-cps-container", UINT64_MAX);
+ sysbus_init_mmio(sbd, &s->container);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ MIPSCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+
+ cpu_reset(cs);
+}
+
+static bool cpu_mips_itu_supported(CPUMIPSState *env)
+{
+ bool is_mt = (env->CP0_Config5 & (1 << CP0C5_VP)) || ase_mt_available(env);
+
+ return is_mt && !kvm_enabled();
+}
+
+static void mips_cps_realize(DeviceState *dev, Error **errp)
+{
+ MIPSCPSState *s = MIPS_CPS(dev);
+ CPUMIPSState *env;
+ MIPSCPU *cpu;
+ int i;
+ target_ulong gcr_base;
+ bool itu_present = false;
+ bool saar_present = false;
+
+ if (!clock_get(s->clock)) {
+ error_setg(errp, "CPS input clock is not connected to an output clock");
+ return;
+ }
+
+ for (i = 0; i < s->num_vp; i++) {
+ cpu = MIPS_CPU(object_new(s->cpu_type));
+
+ /* All VPs are halted on reset. Leave powering up to CPC. */
+ if (!object_property_set_bool(OBJECT(cpu), "start-powered-off", true,
+ errp)) {
+ return;
+ }
+ /* All cores use the same clock tree */
+ qdev_connect_clock_in(DEVICE(cpu), "clk-in", s->clock);
+
+ if (!qdev_realize_and_unref(DEVICE(cpu), NULL, errp)) {
+ return;
+ }
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(cpu);
+ cpu_mips_clock_init(cpu);
+
+ env = &cpu->env;
+ if (cpu_mips_itu_supported(env)) {
+ itu_present = true;
+ /* Attach ITC Tag to the VP */
+ env->itc_tag = mips_itu_get_tag_region(&s->itu);
+ env->itu = &s->itu;
+ }
+ qemu_register_reset(main_cpu_reset, cpu);
+ }
+
+ cpu = MIPS_CPU(first_cpu);
+ env = &cpu->env;
+ saar_present = (bool)env->saarp;
+
+ /* Inter-Thread Communication Unit */
+ if (itu_present) {
+ object_initialize_child(OBJECT(dev), "itu", &s->itu, TYPE_MIPS_ITU);
+ object_property_set_int(OBJECT(&s->itu), "num-fifo", 16,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->itu), "num-semaphores", 16,
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->itu), "saar-present", saar_present,
+ &error_abort);
+ if (saar_present) {
+ s->itu.saar = &env->CP0_SAAR;
+ }
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->itu), errp)) {
+ return;
+ }
+
+ memory_region_add_subregion(&s->container, 0,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->itu), 0));
+ }
+
+ /* Cluster Power Controller */
+ object_initialize_child(OBJECT(dev), "cpc", &s->cpc, TYPE_MIPS_CPC);
+ object_property_set_int(OBJECT(&s->cpc), "num-vp", s->num_vp,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->cpc), "vp-start-running", 1,
+ &error_abort);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpc), errp)) {
+ return;
+ }
+
+ memory_region_add_subregion(&s->container, 0,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpc), 0));
+
+ /* Global Interrupt Controller */
+ object_initialize_child(OBJECT(dev), "gic", &s->gic, TYPE_MIPS_GIC);
+ object_property_set_int(OBJECT(&s->gic), "num-vp", s->num_vp,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->gic), "num-irq", 128,
+ &error_abort);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gic), errp)) {
+ return;
+ }
+
+ memory_region_add_subregion(&s->container, 0,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gic), 0));
+
+ /* Global Configuration Registers */
+ gcr_base = env->CP0_CMGCRBase << 4;
+
+ object_initialize_child(OBJECT(dev), "gcr", &s->gcr, TYPE_MIPS_GCR);
+ object_property_set_int(OBJECT(&s->gcr), "num-vp", s->num_vp,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->gcr), "gcr-rev", 0x800,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->gcr), "gcr-base", gcr_base,
+ &error_abort);
+ object_property_set_link(OBJECT(&s->gcr), "gic", OBJECT(&s->gic.mr),
+ &error_abort);
+ object_property_set_link(OBJECT(&s->gcr), "cpc", OBJECT(&s->cpc.mr),
+ &error_abort);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) {
+ return;
+ }
+
+ memory_region_add_subregion(&s->container, gcr_base,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gcr), 0));
+}
+
+static Property mips_cps_properties[] = {
+ DEFINE_PROP_UINT32("num-vp", MIPSCPSState, num_vp, 1),
+ DEFINE_PROP_UINT32("num-irq", MIPSCPSState, num_irq, 256),
+ DEFINE_PROP_STRING("cpu-type", MIPSCPSState, cpu_type),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void mips_cps_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = mips_cps_realize;
+ device_class_set_props(dc, mips_cps_properties);
+}
+
+static const TypeInfo mips_cps_info = {
+ .name = TYPE_MIPS_CPS,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MIPSCPSState),
+ .instance_init = mips_cps_init,
+ .class_init = mips_cps_class_init,
+};
+
+static void mips_cps_register_types(void)
+{
+ type_register_static(&mips_cps_info);
+}
+
+type_init(mips_cps_register_types)
diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c
new file mode 100644
index 000000000..c1b8066a1
--- /dev/null
+++ b/hw/mips/fuloong2e.c
@@ -0,0 +1,350 @@
+/*
+ * QEMU fuloong 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.
+ */
+
+/*
+ * Fuloong 2e mini pc is based on ICT/ST Loongson 2e CPU (MIPS III like, 800MHz)
+ * https://www.linux-mips.org/wiki/Fuloong_2E
+ *
+ * Loongson 2e manuals:
+ * https://github.com/loongson-community/docs/tree/master/2E
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/datadir.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "hw/clock.h"
+#include "net/net.h"
+#include "hw/boards.h"
+#include "hw/i2c/smbus_eeprom.h"
+#include "hw/block/flash.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/bootloader.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/pci/pci.h"
+#include "hw/loader.h"
+#include "hw/ide/pci.h"
+#include "hw/qdev-properties.h"
+#include "elf.h"
+#include "hw/isa/vt82c686.h"
+#include "sysemu/qtest.h"
+#include "sysemu/reset.h"
+#include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
+
+#define ENVP_PADDR 0x2000
+#define ENVP_VADDR cpu_mips_phys_to_kseg0(NULL, ENVP_PADDR)
+#define ENVP_NB_ENTRIES 16
+#define ENVP_ENTRY_SIZE 256
+
+/* Fuloong 2e has a 512k flash: Winbond W39L040AP70Z */
+#define BIOS_SIZE (512 * KiB)
+#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:
+ * https://github.com/loongson-community/pmon
+ */
+#define FULOONG_BIOSNAME "pmon_2e.bin"
+
+/* PCI SLOT in Fuloong 2e */
+#define FULOONG2E_VIA_SLOT 5
+#define FULOONG2E_ATI_SLOT 6
+#define FULOONG2E_RTL8139_SLOT 7
+
+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_VADDR + table_addr);
+
+ va_start(ap, string);
+ vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
+ va_end(ap);
+}
+
+static uint64_t load_kernel(MIPSCPU *cpu)
+{
+ uint64_t kernel_entry, kernel_high, initrd_size;
+ int index = 0;
+ long kernel_size;
+ ram_addr_t initrd_offset;
+ uint32_t *prom_buf;
+ long prom_size;
+
+ kernel_size = load_elf(loaderparams.kernel_filename, NULL,
+ cpu_mips_kseg0_to_phys, NULL,
+ &kernel_entry, NULL,
+ &kernel_high, NULL,
+ 0, EM_MIPS, 1, 0);
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s': %s",
+ loaderparams.kernel_filename,
+ load_elf_strerror(kernel_size));
+ 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 = ROUND_UP(kernel_high, INITRD_PAGE_SIZE);
+ if (initrd_offset + initrd_size > loaderparams.ram_size) {
+ error_report("memory too small for initial ram disk '%s'",
+ 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) {
+ error_report("could not load initial ram disk '%s'",
+ 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=%" PRId64 " %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=%u", clock_get_hz(cpu->clock));
+ prom_set(prom_buf, index++, "memsize=%"PRIi64, loaderparams.ram_size / MiB);
+ prom_set(prom_buf, index++, NULL);
+
+ rom_add_blob_fixed("prom", prom_buf, prom_size, ENVP_PADDR);
+
+ g_free(prom_buf);
+ return kernel_entry;
+}
+
+static void write_bootloader(CPUMIPSState *env, uint8_t *base,
+ uint64_t kernel_addr)
+{
+ uint32_t *p;
+
+ /* Small bootloader */
+ p = (uint32_t *)base;
+
+ /* j 0x1fc00040 */
+ stl_p(p++, 0x0bf00010);
+ /* nop */
+ stl_p(p++, 0x00000000);
+
+ /* Second part of the bootloader */
+ p = (uint32_t *)(base + 0x040);
+
+ bl_gen_jump_kernel(&p, ENVP_VADDR - 64, 2, ENVP_VADDR, ENVP_VADDR + 8,
+ loaderparams.ram_size, kernel_addr);
+}
+
+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 void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc,
+ I2CBus **i2c_bus)
+{
+ PCIDevice *dev;
+
+ dev = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(slot, 0), true,
+ TYPE_VT82C686B_ISA);
+ qdev_connect_gpio_out(DEVICE(dev), 0, intc);
+
+ dev = pci_create_simple(pci_bus, PCI_DEVFN(slot, 1), "via-ide");
+ pci_ide_create_devs(dev);
+
+ pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci");
+ pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci");
+
+ dev = pci_create_simple(pci_bus, PCI_DEVFN(slot, 4), TYPE_VT82C686B_PM);
+ *i2c_bus = I2C_BUS(qdev_get_child_bus(DEVICE(dev), "i2c"));
+
+ /* Audio support */
+ pci_create_simple(pci_bus, PCI_DEVFN(slot, 5), TYPE_VIA_AC97);
+ pci_create_simple(pci_bus, PCI_DEVFN(slot, 6), TYPE_VIA_MC97);
+}
+
+/* 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 Fuloong board has a RTL8139 card using PCI SLOT 7 */
+ default_devaddr = "07";
+ }
+
+ pci_nic_init_nofail(nd, pci_bus, "rtl8139", default_devaddr);
+ }
+}
+
+static void mips_fuloong2e_init(MachineState *machine)
+{
+ 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 *bios = g_new(MemoryRegion, 1);
+ long bios_size;
+ uint8_t *spd_data;
+ uint64_t kernel_entry;
+ PCIDevice *pci_dev;
+ PCIBus *pci_bus;
+ I2CBus *smbus;
+ Clock *cpuclk;
+ MIPSCPU *cpu;
+ CPUMIPSState *env;
+ DeviceState *dev;
+
+ cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
+ clock_set_hz(cpuclk, 533080000); /* ~533 MHz */
+
+ /* init CPUs */
+ cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
+ env = &cpu->env;
+
+ qemu_register_reset(main_cpu_reset, cpu);
+
+ /* TODO: support more than 256M RAM as highmem */
+ if (machine->ram_size != 256 * MiB) {
+ error_report("Invalid RAM size, should be 256MB");
+ exit(EXIT_FAILURE);
+ }
+ memory_region_add_subregion(address_space_mem, 0, machine->ram);
+
+ /* Boot ROM */
+ memory_region_init_rom(bios, NULL, "fuloong2e.bios", BIOS_SIZE,
+ &error_fatal);
+ 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 = machine->ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ kernel_entry = load_kernel(cpu);
+ write_bootloader(env, memory_region_get_ram_ptr(bios), kernel_entry);
+ } else {
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
+ machine->firmware ?: FULOONG_BIOSNAME);
+ 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) &&
+ machine->firmware && !qtest_enabled()) {
+ error_report("Could not load MIPS bios '%s'", machine->firmware);
+ exit(1);
+ }
+ }
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(cpu);
+ cpu_mips_clock_init(cpu);
+
+ /* North bridge, Bonito --> IP2 */
+ pci_bus = bonito_init((qemu_irq *)&(env->irq[2]));
+
+ /* South bridge -> IP5 */
+ vt82c686b_southbridge_init(pci_bus, FULOONG2E_VIA_SLOT, env->irq[5],
+ &smbus);
+
+ /* GPU */
+ if (vga_interface_type != VGA_NONE) {
+ pci_dev = pci_new(-1, "ati-vga");
+ dev = DEVICE(pci_dev);
+ qdev_prop_set_uint32(dev, "vgamem_mb", 16);
+ qdev_prop_set_uint16(dev, "x-device-id", 0x5159);
+ pci_realize_and_unref(pci_dev, pci_bus, &error_fatal);
+ }
+
+ /* Populate SPD eeprom data */
+ spd_data = spd_data_generate(DDR, machine->ram_size);
+ smbus_eeprom_init_one(smbus, 0x50, spd_data);
+
+ /* Network card: RTL8139D */
+ network_init(pci_bus);
+}
+
+static void mips_fuloong2e_machine_init(MachineClass *mc)
+{
+ mc->desc = "Fuloong 2e mini pc";
+ mc->init = mips_fuloong2e_init;
+ mc->block_default_type = IF_IDE;
+ mc->default_cpu_type = MIPS_CPU_TYPE_NAME("Loongson-2E");
+ mc->default_ram_size = 256 * MiB;
+ mc->default_ram_id = "fuloong2e.ram";
+ mc->minimum_page_bits = 14;
+}
+
+DEFINE_MACHINE("fuloong2e", mips_fuloong2e_machine_init)
diff --git a/hw/mips/fw_cfg.c b/hw/mips/fw_cfg.c
new file mode 100644
index 000000000..67c4a74f4
--- /dev/null
+++ b/hw/mips/fw_cfg.c
@@ -0,0 +1,35 @@
+/*
+ * QEMU fw_cfg helpers (MIPS specific)
+ *
+ * Copyright (c) 2020 Lemote, Inc.
+ *
+ * Author:
+ * Huacai Chen (chenhc@lemote.com)
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/mips/fw_cfg.h"
+#include "hw/nvram/fw_cfg.h"
+
+const char *fw_cfg_arch_key_name(uint16_t key)
+{
+ static const struct {
+ uint16_t key;
+ const char *name;
+ } fw_cfg_arch_wellknown_keys[] = {
+ {FW_CFG_MACHINE_VERSION, "machine_version"},
+ {FW_CFG_CPU_FREQ, "cpu_frequency"},
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(fw_cfg_arch_wellknown_keys); i++) {
+ if (fw_cfg_arch_wellknown_keys[i].key == key) {
+ return fw_cfg_arch_wellknown_keys[i].name;
+ }
+ }
+ return NULL;
+}
diff --git a/hw/mips/fw_cfg.h b/hw/mips/fw_cfg.h
new file mode 100644
index 000000000..e317d5b9a
--- /dev/null
+++ b/hw/mips/fw_cfg.h
@@ -0,0 +1,19 @@
+/*
+ * QEMU fw_cfg helpers (MIPS specific)
+ *
+ * Copyright (c) 2020 Huacai Chen
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef HW_MIPS_FW_CFG_H
+#define HW_MIPS_FW_CFG_H
+
+#include "hw/boards.h"
+#include "hw/nvram/fw_cfg.h"
+
+/* Data for BIOS to identify machine */
+#define FW_CFG_MACHINE_VERSION (FW_CFG_ARCH_LOCAL + 0)
+#define FW_CFG_CPU_FREQ (FW_CFG_ARCH_LOCAL + 1)
+
+#endif
diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c
new file mode 100644
index 000000000..c7480bd01
--- /dev/null
+++ b/hw/mips/gt64xxx_pci.c
@@ -0,0 +1,1300 @@
+/*
+ * 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/units.h"
+#include "qemu/log.h"
+#include "hw/mips/mips.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+#include "hw/southbridge/piix.h"
+#include "migration/vmstate.h"
+#include "hw/intc/i8259.h"
+#include "hw/irq.h"
+#include "trace.h"
+#include "qom/object.h"
+
+#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"
+
+OBJECT_DECLARE_SIMPLE_TYPE(GT64120State, GT64120_PCI_HOST_BRIDGE)
+
+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;
+};
+
+/* 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)
+{
+ /* Bits 14:0 of ISD map to bits 35:21 of the start address. */
+ hwaddr start = ((hwaddr)s->regs[GT_ISD] << 21) & 0xFFFE00000ull;
+ 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 */
+ trace_gt64120_isd_remap(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 = addr >> 2;
+
+ trace_gt64120_write(addr, val);
+ if (!(s->regs[GT_CPU] & 0x00001000)) {
+ val = bswap32(val);
+ }
+
+ 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 */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gt64120: Read-only register write "
+ "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
+ saddr << 2, size, size << 1, val);
+ break;
+
+ /* CPU Sync Barrier */
+ case GT_PCI0SYNC:
+ case GT_PCI1SYNC:
+ /* Read-only registers, do nothing */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gt64120: Read-only register write "
+ "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
+ saddr << 2, size, size << 1, val);
+ 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 */
+ qemu_log_mask(LOG_UNIMP,
+ "gt64120: Unimplemented device register write "
+ "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
+ saddr << 2, size, size << 1, val);
+ 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 */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gt64120: Read-only register write "
+ "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
+ saddr << 2, size, size << 1, val);
+ 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:
+
+ /* DMA Channel Control */
+ case GT_DMA0_CTRL:
+ case GT_DMA1_CTRL:
+ case GT_DMA2_CTRL:
+ case GT_DMA3_CTRL:
+
+ /* DMA Arbiter */
+ case GT_DMA_ARB:
+ /* Not implemented */
+ qemu_log_mask(LOG_UNIMP,
+ "gt64120: Unimplemented DMA register write "
+ "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
+ saddr << 2, size, size << 1, val);
+ break;
+
+ /* Timer/Counter */
+ case GT_TC0:
+ case GT_TC1:
+ case GT_TC2:
+ case GT_TC3:
+ case GT_TC_CONTROL:
+ /* Not implemented */
+ qemu_log_mask(LOG_UNIMP,
+ "gt64120: Unimplemented timer register write "
+ "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
+ saddr << 2, size, size << 1, val);
+ 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 */
+ qemu_log_mask(LOG_UNIMP,
+ "gt64120: Unimplemented PCI register write "
+ "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
+ saddr << 2, size, size << 1, val);
+ 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);
+ trace_gt64120_write_intreg("INTRCAUSE", size, val);
+ break;
+ case GT_INTRMASK:
+ s->regs[saddr] = val & 0x3c3ffffe;
+ trace_gt64120_write_intreg("INTRMASK", size, val);
+ break;
+ case GT_PCI0_ICMASK:
+ s->regs[saddr] = val & 0x03fffffe;
+ trace_gt64120_write_intreg("ICMASK", size, val);
+ break;
+ case GT_PCI0_SERR0MASK:
+ s->regs[saddr] = val & 0x0000003f;
+ trace_gt64120_write_intreg("SERR0MASK", size, 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:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gt64120: Illegal register write "
+ "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
+ saddr << 2, size, size << 1, val);
+ 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 = addr >> 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];
+ trace_gt64120_read_intreg("INTRCAUSE", size, val);
+ break;
+ case GT_INTRMASK:
+ val = s->regs[saddr];
+ trace_gt64120_read_intreg("INTRMASK", size, val);
+ break;
+ case GT_PCI0_ICMASK:
+ val = s->regs[saddr];
+ trace_gt64120_read_intreg("ICMASK", size, val);
+ break;
+ case GT_PCI0_SERR0MASK:
+ val = s->regs[saddr];
+ trace_gt64120_read_intreg("SERR0MASK", size, 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];
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gt64120: Illegal register read "
+ "reg:0x%03x size:%u value:0x%0*x\n",
+ saddr << 2, size, size << 1, val);
+ break;
+ }
+
+ if (!(s->regs[GT_CPU] & 0x00001000)) {
+ val = bswap32(val);
+ }
+ trace_gt64120_read(addr, val);
+
+ return val;
+}
+
+static const MemoryRegionOps isd_mem_ops = {
+ .read = gt64120_readl,
+ .write = gt64120_writel,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot;
+
+ slot = PCI_SLOT(pci_dev->devfn);
+
+ 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[PIIX_PIRQCA + 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[PIIX_PIRQCA + i]) {
+ pic_level |= pci_irq_levels[i];
+ }
+ }
+ qemu_set_irq(pic[pic_irq], pic_level);
+ }
+}
+
+
+static void gt64120_reset(DeviceState *dev)
+{
+ GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev);
+
+ /* 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);
+}
+
+static void gt64120_realize(DeviceState *dev, Error **errp)
+{
+ GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev);
+
+ memory_region_init_io(&s->ISD_mem, OBJECT(dev), &isd_mem_ops, s,
+ "gt64120-isd", 0x1000);
+}
+
+PCIBus *gt64120_register(qemu_irq *pic)
+{
+ GT64120State *d;
+ PCIHostState *phb;
+ DeviceState *dev;
+
+ dev = qdev_new(TYPE_GT64120_PCI_HOST_BRIDGE);
+ d = GT64120_PCI_HOST_BRIDGE(dev);
+ phb = PCI_HOST_BRIDGE(dev);
+ memory_region_init(&d->pci0_mem, OBJECT(dev), "pci0-mem", 4 * GiB);
+ address_space_init(&d->pci0_mem_as, &d->pci0_mem, "pci0-mem");
+ phb->bus = pci_register_root_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);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci");
+ return phb->bus;
+}
+
+static void gt64120_pci_realize(PCIDevice *d, Error **errp)
+{
+ /* 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);
+}
+
+static void gt64120_pci_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ k->realize = gt64120_pci_realize;
+ 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->user_creatable = false;
+}
+
+static const TypeInfo gt64120_pci_info = {
+ .name = "gt64120_pci",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIDevice),
+ .class_init = gt64120_pci_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void gt64120_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->realize = gt64120_realize;
+ dc->reset = gt64120_reset;
+ 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/hw/mips/jazz.c b/hw/mips/jazz.c
new file mode 100644
index 000000000..f5a26e174
--- /dev/null
+++ b/hw/mips/jazz.c
@@ -0,0 +1,441 @@
+/*
+ * 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 "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/datadir.h"
+#include "hw/clock.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/intc/i8259.h"
+#include "hw/dma/i8257.h"
+#include "hw/char/serial.h"
+#include "hw/char/parallel.h"
+#include "hw/isa/isa.h"
+#include "hw/block/fdc.h"
+#include "sysemu/sysemu.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/rtc/mc146818rtc.h"
+#include "hw/timer/i8254.h"
+#include "hw/display/vga.h"
+#include "hw/audio/pcspk.h"
+#include "hw/input/i8042.h"
+#include "hw/sysbus.h"
+#include "sysemu/qtest.h"
+#include "sysemu/reset.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/help_option.h"
+#ifdef CONFIG_TCG
+#include "hw/core/tcg-cpu-ops.h"
+#endif /* CONFIG_TCG */
+
+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)
+
+#define SONIC_PROM_SIZE 0x1000
+
+static void mips_jazz_init(MachineState *machine,
+ enum jazz_model_e jazz_model)
+{
+ MemoryRegion *address_space = get_system_memory();
+ char *filename;
+ int bios_size, n, big_endian;
+ Clock *cpuclk;
+ MIPSCPU *cpu;
+ MIPSCPUClass *mcc;
+ CPUMIPSState *env;
+ qemu_irq *i8259;
+ rc4030_dma *dmas;
+ IOMMUMemoryRegion *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);
+ MemoryRegion *dp8393x_prom = g_new(MemoryRegion, 1);
+ NICInfo *nd;
+ DeviceState *dev, *rc4030;
+ SysBusDevice *sysbus;
+ ISABus *isa_bus;
+ ISADevice *pit;
+ DriveInfo *fds[MAX_FD];
+ MemoryRegion *bios = g_new(MemoryRegion, 1);
+ MemoryRegion *bios2 = g_new(MemoryRegion, 1);
+ SysBusESPState *sysbus_esp;
+ ESPState *esp;
+ static const struct {
+ unsigned freq_hz;
+ unsigned pll_mult;
+ } ext_clk[] = {
+ [JAZZ_MAGNUM] = {50000000, 2},
+ [JAZZ_PICA61] = {33333333, 4},
+ };
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+
+ if (machine->ram_size > 256 * MiB) {
+ error_report("RAM size more than 256Mb is not supported");
+ exit(EXIT_FAILURE);
+ }
+
+ cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
+ clock_set_hz(cpuclk, ext_clk[jazz_model].freq_hz
+ * ext_clk[jazz_model].pll_mult);
+
+ /* init CPUs */
+ cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
+ 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 this would make all accesses including instruction
+ * accesses be ignored and not raise exceptions.
+ *
+ * NOTE: this behaviour of raising exceptions for bad instruction
+ * fetches but not bad data accesses was added in commit 54e755588cf1e9
+ * to restore behaviour broken by c658b94f6e8c206, but it is not clear
+ * whether the real hardware behaves this way. It is possible that
+ * real hardware ignores bad instruction fetches as well -- if so then
+ * we could replace this hijacking of CPU methods with a simple global
+ * memory region that catches all memory accesses, as we do on Malta.
+ */
+ mcc = MIPS_CPU_GET_CLASS(cpu);
+ mcc->no_data_aborts = true;
+
+ /* allocate RAM */
+ memory_region_add_subregion(address_space, 0, machine->ram);
+
+ memory_region_init_rom(bios, NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE,
+ &error_fatal);
+ 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. */
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware ?: BIOS_FILENAME);
+ 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)
+ && machine->firmware && !qtest_enabled()) {
+ error_report("Could not load MIPS bios '%s'", machine->firmware);
+ exit(1);
+ }
+
+ /* Init CPU internal devices */
+ cpu_mips_irq_init_cpu(cpu);
+ cpu_mips_clock_init(cpu);
+
+ /* 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);
+
+ memory_region_init_rom(dp8393x_prom, NULL, "dp8393x-jazz.prom",
+ SONIC_PROM_SIZE, &error_fatal);
+ memory_region_add_subregion(address_space, 0x8000b000, dp8393x_prom);
+
+ /* 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, &error_abort);
+
+ /* ISA devices */
+ i8259 = i8259_init(isa_bus, env->irq[4]);
+ isa_bus_irqs(isa_bus, i8259);
+ i8257_dma_init(isa_bus, 0);
+ pit = i8254_pit_init(isa_bus, 0x40, 0, NULL);
+ pcspk_init(isa_new(TYPE_PC_SPEAKER), isa_bus, pit);
+
+ /* Video card */
+ switch (jazz_model) {
+ case JAZZ_MAGNUM:
+ dev = qdev_new("sysbus-g364");
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ 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_rom(rom_mr, NULL, "g364fb.rom", 0x80000,
+ &error_fatal);
+ 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) {
+ int checksum, i;
+ uint8_t *prom;
+
+ qemu_check_nic_model(nd, "dp83932");
+
+ dev = qdev_new("dp8393x");
+ qdev_set_nic_properties(dev, nd);
+ qdev_prop_set_uint8(dev, "it_shift", 2);
+ qdev_prop_set_bit(dev, "big_endian", big_endian > 0);
+ object_property_set_link(OBJECT(dev), "dma_mr",
+ OBJECT(rc4030_dma_mr), &error_abort);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, 0x80001000);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4));
+
+ /* Add MAC address with valid checksum to PROM */
+ prom = memory_region_get_ram_ptr(dp8393x_prom);
+ checksum = 0;
+ for (i = 0; i < 6; i++) {
+ prom[i] = nd->macaddr.a[i];
+ checksum += prom[i];
+ if (checksum > 0xff) {
+ checksum = (checksum + 1) & 0xff;
+ }
+ }
+ prom[7] = 0xff - checksum;
+ break;
+ } else if (is_help_option(nd->model)) {
+ error_report("Supported NICs: dp83932");
+ exit(1);
+ } else {
+ error_report("Unsupported NIC: %s", nd->model);
+ exit(1);
+ }
+ }
+
+ /* SCSI adapter */
+ dev = qdev_new(TYPE_SYSBUS_ESP);
+ sysbus_esp = SYSBUS_ESP(dev);
+ esp = &sysbus_esp->esp;
+ esp->dma_memory_read = rc4030_dma_read;
+ esp->dma_memory_write = rc4030_dma_write;
+ esp->dma_opaque = dmas[0];
+ sysbus_esp->it_shift = 0;
+ /* XXX for now until rc4030 has been changed to use DMA enable signal */
+ esp->dma_enabled = 1;
+
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 5));
+ sysbus_mmio_map(sysbus, 0, 0x80002000);
+
+ scsi_bus_legacy_handle_cmdline(&esp->bus);
+
+ /* Floppy */
+ for (n = 0; n < MAX_FD; n++) {
+ fds[n] = drive_get(IF_FLOPPY, 0, n);
+ }
+ /* FIXME: we should enable DMA with a custom IsaDma device */
+ fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), -1, 0x80003000, fds);
+
+ /* Real time clock */
+ mc146818_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 */
+ serial_mm_init(address_space, 0x80006000, 0,
+ qdev_get_gpio_in(rc4030, 8), 8000000 / 16,
+ serial_hd(0), DEVICE_NATIVE_ENDIAN);
+ serial_mm_init(address_space, 0x80007000, 0,
+ qdev_get_gpio_in(rc4030, 9), 8000000 / 16,
+ serial_hd(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_new("ds1225y");
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, 0x80009000);
+
+ /* LED indicator */
+ sysbus_create_simple("jazz-led", 0x8000f000, NULL);
+
+ g_free(dmas);
+}
+
+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 void mips_magnum_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "MIPS Magnum";
+ mc->init = mips_magnum_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000");
+ mc->default_ram_id = "mips_jazz.ram";
+}
+
+static const TypeInfo mips_magnum_type = {
+ .name = MACHINE_TYPE_NAME("magnum"),
+ .parent = TYPE_MACHINE,
+ .class_init = mips_magnum_class_init,
+};
+
+static void mips_pica61_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Acer Pica 61";
+ mc->init = mips_pica61_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_cpu_type = MIPS_CPU_TYPE_NAME("R4000");
+ mc->default_ram_id = "mips_jazz.ram";
+}
+
+static const TypeInfo mips_pica61_type = {
+ .name = MACHINE_TYPE_NAME("pica61"),
+ .parent = TYPE_MACHINE,
+ .class_init = mips_pica61_class_init,
+};
+
+static void mips_jazz_machine_init(void)
+{
+ type_register_static(&mips_magnum_type);
+ type_register_static(&mips_pica61_type);
+}
+
+type_init(mips_jazz_machine_init)
diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c
new file mode 100644
index 000000000..f99af2293
--- /dev/null
+++ b/hw/mips/loongson3_bootp.c
@@ -0,0 +1,151 @@
+/*
+ * LEFI (a UEFI-like interface for BIOS-Kernel boot parameters) helpers
+ *
+ * Copyright (c) 2018-2020 Huacai Chen (chenhc@lemote.com)
+ * Copyright (c) 2018-2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/cutils.h"
+#include "cpu.h"
+#include "hw/boards.h"
+#include "hw/mips/loongson3_bootp.h"
+
+#define LOONGSON3_CORE_PER_NODE 4
+
+static void init_cpu_info(void *g_cpuinfo, uint64_t cpu_freq)
+{
+ struct efi_cpuinfo_loongson *c = g_cpuinfo;
+
+ c->cputype = cpu_to_le32(Loongson_3A);
+ c->processor_id = cpu_to_le32(MIPS_CPU(first_cpu)->env.CP0_PRid);
+ if (cpu_freq > UINT_MAX) {
+ c->cpu_clock_freq = cpu_to_le32(UINT_MAX);
+ } else {
+ c->cpu_clock_freq = cpu_to_le32(cpu_freq);
+ }
+
+ c->cpu_startup_core_id = cpu_to_le16(0);
+ c->nr_cpus = cpu_to_le32(current_machine->smp.cpus);
+ c->total_node = cpu_to_le32(DIV_ROUND_UP(current_machine->smp.cpus,
+ LOONGSON3_CORE_PER_NODE));
+}
+
+static void init_memory_map(void *g_map, uint64_t ram_size)
+{
+ struct efi_memory_map_loongson *emap = g_map;
+
+ emap->nr_map = cpu_to_le32(2);
+ emap->mem_freq = cpu_to_le32(300000000);
+
+ emap->map[0].node_id = cpu_to_le32(0);
+ emap->map[0].mem_type = cpu_to_le32(1);
+ emap->map[0].mem_start = cpu_to_le64(0x0);
+ emap->map[0].mem_size = cpu_to_le32(240);
+
+ emap->map[1].node_id = cpu_to_le32(0);
+ emap->map[1].mem_type = cpu_to_le32(2);
+ emap->map[1].mem_start = cpu_to_le64(0x90000000);
+ emap->map[1].mem_size = cpu_to_le32((ram_size / MiB) - 256);
+}
+
+static void init_system_loongson(void *g_system)
+{
+ struct system_loongson *s = g_system;
+
+ s->ccnuma_smp = cpu_to_le32(0);
+ s->sing_double_channel = cpu_to_le32(1);
+ s->nr_uarts = cpu_to_le32(1);
+ s->uarts[0].iotype = cpu_to_le32(2);
+ s->uarts[0].int_offset = cpu_to_le32(2);
+ s->uarts[0].uartclk = cpu_to_le32(25000000); /* Random value */
+ s->uarts[0].uart_base = cpu_to_le64(virt_memmap[VIRT_UART].base);
+}
+
+static void init_irq_source(void *g_irq_source)
+{
+ struct irq_source_routing_table *irq_info = g_irq_source;
+
+ irq_info->node_id = cpu_to_le32(0);
+ irq_info->PIC_type = cpu_to_le32(0);
+ irq_info->dma_mask_bits = cpu_to_le16(64);
+ irq_info->pci_mem_start_addr = cpu_to_le64(virt_memmap[VIRT_PCIE_MMIO].base);
+ irq_info->pci_mem_end_addr = cpu_to_le64(virt_memmap[VIRT_PCIE_MMIO].base +
+ virt_memmap[VIRT_PCIE_MMIO].size - 1);
+ irq_info->pci_io_start_addr = cpu_to_le64(virt_memmap[VIRT_PCIE_PIO].base);
+}
+
+static void init_interface_info(void *g_interface)
+{
+ struct interface_info *interface = g_interface;
+
+ interface->vers = cpu_to_le16(0x01);
+ strpadcpy(interface->description, 64, "UEFI_Version_v1.0", '\0');
+}
+
+static void board_devices_info(void *g_board)
+{
+ struct board_devices *bd = g_board;
+
+ strpadcpy(bd->name, 64, "Loongson-3A-VIRT-1w-V1.00-demo", '\0');
+}
+
+static void init_special_info(void *g_special)
+{
+ struct loongson_special_attribute *special = g_special;
+
+ strpadcpy(special->special_name, 64, "2018-05-01", '\0');
+}
+
+void init_loongson_params(struct loongson_params *lp, void *p,
+ uint64_t cpu_freq, uint64_t ram_size)
+{
+ init_cpu_info(p, cpu_freq);
+ lp->cpu_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
+ p += ROUND_UP(sizeof(struct efi_cpuinfo_loongson), 64);
+
+ init_memory_map(p, ram_size);
+ lp->memory_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
+ p += ROUND_UP(sizeof(struct efi_memory_map_loongson), 64);
+
+ init_system_loongson(p);
+ lp->system_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
+ p += ROUND_UP(sizeof(struct system_loongson), 64);
+
+ init_irq_source(p);
+ lp->irq_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
+ p += ROUND_UP(sizeof(struct irq_source_routing_table), 64);
+
+ init_interface_info(p);
+ lp->interface_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
+ p += ROUND_UP(sizeof(struct interface_info), 64);
+
+ board_devices_info(p);
+ lp->boarddev_table_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
+ p += ROUND_UP(sizeof(struct board_devices), 64);
+
+ init_special_info(p);
+ lp->special_offset = cpu_to_le64((uintptr_t)p - (uintptr_t)lp);
+ p += ROUND_UP(sizeof(struct loongson_special_attribute), 64);
+}
+
+void init_reset_system(struct efi_reset_system_t *reset)
+{
+ reset->Shutdown = cpu_to_le64(0xffffffffbfc000a8);
+ reset->ResetCold = cpu_to_le64(0xffffffffbfc00080);
+ reset->ResetWarm = cpu_to_le64(0xffffffffbfc00080);
+}
diff --git a/hw/mips/loongson3_bootp.h b/hw/mips/loongson3_bootp.h
new file mode 100644
index 000000000..d525ab745
--- /dev/null
+++ b/hw/mips/loongson3_bootp.h
@@ -0,0 +1,236 @@
+/*
+ * LEFI (a UEFI-like interface for BIOS-Kernel boot parameters) data structures
+ * defined at arch/mips/include/asm/mach-loongson64/boot_param.h in Linux kernel
+ *
+ * Copyright (c) 2017-2020 Huacai Chen (chenhc@lemote.com)
+ * Copyright (c) 2017-2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_MIPS_LOONGSON3_BOOTP_H
+#define HW_MIPS_LOONGSON3_BOOTP_H
+
+struct efi_memory_map_loongson {
+ uint16_t vers; /* version of efi_memory_map */
+ uint32_t nr_map; /* number of memory_maps */
+ uint32_t mem_freq; /* memory frequence */
+ struct mem_map {
+ uint32_t node_id; /* node_id which memory attached to */
+ uint32_t mem_type; /* system memory, pci memory, pci io, etc. */
+ uint64_t mem_start; /* memory map start address */
+ uint32_t mem_size; /* each memory_map size, not the total size */
+ } map[128];
+} QEMU_PACKED;
+
+enum loongson_cpu_type {
+ Legacy_2E = 0x0,
+ Legacy_2F = 0x1,
+ Legacy_3A = 0x2,
+ Legacy_3B = 0x3,
+ Legacy_1A = 0x4,
+ Legacy_1B = 0x5,
+ Legacy_2G = 0x6,
+ Legacy_2H = 0x7,
+ Loongson_1A = 0x100,
+ Loongson_1B = 0x101,
+ Loongson_2E = 0x200,
+ Loongson_2F = 0x201,
+ Loongson_2G = 0x202,
+ Loongson_2H = 0x203,
+ Loongson_3A = 0x300,
+ Loongson_3B = 0x301
+};
+
+/*
+ * Capability and feature descriptor structure for MIPS CPU
+ */
+struct efi_cpuinfo_loongson {
+ uint16_t vers; /* version of efi_cpuinfo_loongson */
+ uint32_t processor_id; /* PRID, e.g. 6305, 6306 */
+ uint32_t cputype; /* Loongson_3A/3B, etc. */
+ uint32_t total_node; /* num of total numa nodes */
+ uint16_t cpu_startup_core_id; /* Boot core id */
+ uint16_t reserved_cores_mask;
+ uint32_t cpu_clock_freq; /* cpu_clock */
+ uint32_t nr_cpus;
+ char cpuname[64];
+} QEMU_PACKED;
+
+#define MAX_UARTS 64
+struct uart_device {
+ uint32_t iotype;
+ uint32_t uartclk;
+ uint32_t int_offset;
+ uint64_t uart_base;
+} QEMU_PACKED;
+
+#define MAX_SENSORS 64
+#define SENSOR_TEMPER 0x00000001
+#define SENSOR_VOLTAGE 0x00000002
+#define SENSOR_FAN 0x00000004
+struct sensor_device {
+ char name[32]; /* a formal name */
+ char label[64]; /* a flexible description */
+ uint32_t type; /* SENSOR_* */
+ uint32_t id; /* instance id of a sensor-class */
+ uint32_t fan_policy; /* step speed or constant speed */
+ uint32_t fan_percent;/* only for constant speed policy */
+ uint64_t base_addr; /* base address of device registers */
+} QEMU_PACKED;
+
+struct system_loongson {
+ uint16_t vers; /* version of system_loongson */
+ uint32_t ccnuma_smp; /* 0: no numa; 1: has numa */
+ uint32_t sing_double_channel;/* 1: single; 2: double */
+ uint32_t nr_uarts;
+ struct uart_device uarts[MAX_UARTS];
+ uint32_t nr_sensors;
+ struct sensor_device sensors[MAX_SENSORS];
+ char has_ec;
+ char ec_name[32];
+ uint64_t ec_base_addr;
+ char has_tcm;
+ char tcm_name[32];
+ uint64_t tcm_base_addr;
+ uint64_t workarounds;
+ uint64_t of_dtb_addr; /* NULL if not support */
+} QEMU_PACKED;
+
+struct irq_source_routing_table {
+ uint16_t vers;
+ uint16_t size;
+ uint16_t rtr_bus;
+ uint16_t rtr_devfn;
+ uint32_t vendor;
+ uint32_t device;
+ uint32_t PIC_type; /* conform use HT or PCI to route to CPU-PIC */
+ uint64_t ht_int_bit; /* 3A: 1<<24; 3B: 1<<16 */
+ uint64_t ht_enable; /* irqs used in this PIC */
+ uint32_t node_id; /* node id: 0x0-0; 0x1-1; 0x10-2; 0x11-3 */
+ uint64_t pci_mem_start_addr;
+ uint64_t pci_mem_end_addr;
+ uint64_t pci_io_start_addr;
+ uint64_t pci_io_end_addr;
+ uint64_t pci_config_addr;
+ uint16_t dma_mask_bits;
+ uint16_t dma_noncoherent;
+} QEMU_PACKED;
+
+struct interface_info {
+ uint16_t vers; /* version of the specificition */
+ uint16_t size;
+ uint8_t flag;
+ char description[64];
+} QEMU_PACKED;
+
+#define MAX_RESOURCE_NUMBER 128
+struct resource_loongson {
+ uint64_t start; /* resource start address */
+ uint64_t end; /* resource end address */
+ char name[64];
+ uint32_t flags;
+};
+
+struct archdev_data {}; /* arch specific additions */
+
+struct board_devices {
+ char name[64]; /* hold the device name */
+ uint32_t num_resources; /* number of device_resource */
+ /* for each device's resource */
+ struct resource_loongson resource[MAX_RESOURCE_NUMBER];
+ /* arch specific additions */
+ struct archdev_data archdata;
+};
+
+struct loongson_special_attribute {
+ uint16_t vers; /* version of this special */
+ char special_name[64]; /* special_atribute_name */
+ uint32_t loongson_special_type; /* type of special device */
+ /* for each device's resource */
+ struct resource_loongson resource[MAX_RESOURCE_NUMBER];
+};
+
+struct loongson_params {
+ uint64_t memory_offset; /* efi_memory_map_loongson struct offset */
+ uint64_t cpu_offset; /* efi_cpuinfo_loongson struct offset */
+ uint64_t system_offset; /* system_loongson struct offset */
+ uint64_t irq_offset; /* irq_source_routing_table struct offset */
+ uint64_t interface_offset; /* interface_info struct offset */
+ uint64_t special_offset; /* loongson_special_attribute struct offset */
+ uint64_t boarddev_table_offset; /* board_devices offset */
+};
+
+struct smbios_tables {
+ uint16_t vers; /* version of smbios */
+ uint64_t vga_bios; /* vga_bios address */
+ struct loongson_params lp;
+};
+
+struct efi_reset_system_t {
+ uint64_t ResetCold;
+ uint64_t ResetWarm;
+ uint64_t ResetType;
+ uint64_t Shutdown;
+ uint64_t DoSuspend; /* NULL if not support */
+};
+
+struct efi_loongson {
+ uint64_t mps; /* MPS table */
+ uint64_t acpi; /* ACPI table (IA64 ext 0.71) */
+ uint64_t acpi20; /* ACPI table (ACPI 2.0) */
+ struct smbios_tables smbios; /* SM BIOS table */
+ uint64_t sal_systab; /* SAL system table */
+ uint64_t boot_info; /* boot info table */
+};
+
+struct boot_params {
+ struct efi_loongson efi;
+ struct efi_reset_system_t reset_system;
+};
+
+/* Overall MMIO & Memory layout */
+enum {
+ VIRT_LOWMEM,
+ VIRT_PM,
+ VIRT_FW_CFG,
+ VIRT_RTC,
+ VIRT_PCIE_PIO,
+ VIRT_PCIE_ECAM,
+ VIRT_BIOS_ROM,
+ VIRT_UART,
+ VIRT_LIOINTC,
+ VIRT_PCIE_MMIO,
+ VIRT_HIGHMEM
+};
+
+/* Low MEM layout for QEMU kernel loader */
+enum {
+ LOADER_KERNEL,
+ LOADER_INITRD,
+ LOADER_CMDLINE
+};
+
+/* BIOS ROM layout for QEMU kernel loader */
+enum {
+ LOADER_BOOTROM,
+ LOADER_PARAM,
+};
+
+extern const MemMapEntry virt_memmap[];
+void init_loongson_params(struct loongson_params *lp, void *p,
+ uint64_t cpu_freq, uint64_t ram_size);
+void init_reset_system(struct efi_reset_system_t *reset);
+
+#endif
diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c
new file mode 100644
index 000000000..ae192db0c
--- /dev/null
+++ b/hw/mips/loongson3_virt.c
@@ -0,0 +1,634 @@
+/*
+ * Generic Loongson-3 Platform support
+ *
+ * Copyright (c) 2018-2020 Huacai Chen (chenhc@lemote.com)
+ * Copyright (c) 2018-2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Generic virtualized PC Platform based on Loongson-3 CPU (MIPS64R2 with
+ * extensions, 800~2000MHz)
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/units.h"
+#include "qemu/cutils.h"
+#include "qemu/datadir.h"
+#include "qapi/error.h"
+#include "elf.h"
+#include "kvm_mips.h"
+#include "hw/char/serial.h"
+#include "hw/intc/loongson_liointc.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/mips/fw_cfg.h"
+#include "hw/mips/loongson3_bootp.h"
+#include "hw/misc/unimp.h"
+#include "hw/intc/i8259.h"
+#include "hw/loader.h"
+#include "hw/isa/superio.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci-host/gpex.h"
+#include "hw/usb.h"
+#include "net/net.h"
+#include "sysemu/kvm.h"
+#include "sysemu/qtest.h"
+#include "sysemu/reset.h"
+#include "sysemu/runstate.h"
+#include "qemu/error-report.h"
+
+#define PM_CNTL_MODE 0x10
+
+#define LOONGSON_MAX_VCPUS 16
+
+/*
+ * Loongson-3's virtual machine BIOS can be obtained here:
+ * 1, https://github.com/loongson-community/firmware-nonfree
+ * 2, http://dev.lemote.com:8000/files/firmware/UEFI/KVM/bios_loongson3.bin
+ */
+#define LOONGSON3_BIOSNAME "bios_loongson3.bin"
+
+#define UART_IRQ 0
+#define RTC_IRQ 1
+#define PCIE_IRQ_BASE 2
+
+const MemMapEntry virt_memmap[] = {
+ [VIRT_LOWMEM] = { 0x00000000, 0x10000000 },
+ [VIRT_PM] = { 0x10080000, 0x100 },
+ [VIRT_FW_CFG] = { 0x10080100, 0x100 },
+ [VIRT_RTC] = { 0x10081000, 0x1000 },
+ [VIRT_PCIE_PIO] = { 0x18000000, 0x80000 },
+ [VIRT_PCIE_ECAM] = { 0x1a000000, 0x2000000 },
+ [VIRT_BIOS_ROM] = { 0x1fc00000, 0x200000 },
+ [VIRT_UART] = { 0x1fe001e0, 0x8 },
+ [VIRT_LIOINTC] = { 0x3ff01400, 0x64 },
+ [VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 },
+ [VIRT_HIGHMEM] = { 0x80000000, 0x0 }, /* Variable */
+};
+
+static const MemMapEntry loader_memmap[] = {
+ [LOADER_KERNEL] = { 0x00000000, 0x4000000 },
+ [LOADER_INITRD] = { 0x04000000, 0x0 }, /* Variable */
+ [LOADER_CMDLINE] = { 0x0ff00000, 0x100000 },
+};
+
+static const MemMapEntry loader_rommap[] = {
+ [LOADER_BOOTROM] = { 0x1fc00000, 0x1000 },
+ [LOADER_PARAM] = { 0x1fc01000, 0x10000 },
+};
+
+struct LoongsonMachineState {
+ MachineState parent_obj;
+ MemoryRegion *pio_alias;
+ MemoryRegion *mmio_alias;
+ MemoryRegion *ecam_alias;
+};
+typedef struct LoongsonMachineState LoongsonMachineState;
+
+#define TYPE_LOONGSON_MACHINE MACHINE_TYPE_NAME("loongson3-virt")
+DECLARE_INSTANCE_CHECKER(LoongsonMachineState, LOONGSON_MACHINE, TYPE_LOONGSON_MACHINE)
+
+static struct _loaderparams {
+ uint64_t cpu_freq;
+ uint64_t ram_size;
+ const char *kernel_cmdline;
+ const char *kernel_filename;
+ const char *initrd_filename;
+ uint64_t kernel_entry;
+ uint64_t a0, a1, a2;
+} loaderparams;
+
+static uint64_t loongson3_pm_read(void *opaque, hwaddr addr, unsigned size)
+{
+ return 0;
+}
+
+static void loongson3_pm_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ if (addr != PM_CNTL_MODE) {
+ return;
+ }
+
+ switch (val) {
+ case 0x00:
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ return;
+ case 0xff:
+ qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+ return;
+ default:
+ return;
+ }
+}
+
+static const MemoryRegionOps loongson3_pm_ops = {
+ .read = loongson3_pm_read,
+ .write = loongson3_pm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 1
+ }
+};
+
+#define DEF_LOONGSON3_FREQ (800 * 1000 * 1000)
+
+static uint64_t get_cpu_freq_hz(void)
+{
+#ifdef CONFIG_KVM
+ int ret;
+ uint64_t freq;
+ struct kvm_one_reg freq_reg = {
+ .id = KVM_REG_MIPS_COUNT_HZ,
+ .addr = (uintptr_t)(&freq)
+ };
+
+ if (kvm_enabled()) {
+ ret = kvm_vcpu_ioctl(first_cpu, KVM_GET_ONE_REG, &freq_reg);
+ if (ret >= 0) {
+ return freq * 2;
+ }
+ }
+#endif
+ return DEF_LOONGSON3_FREQ;
+}
+
+static void init_boot_param(void)
+{
+ static void *p;
+ struct boot_params *bp;
+
+ p = g_malloc0(loader_rommap[LOADER_PARAM].size);
+ bp = p;
+
+ bp->efi.smbios.vers = cpu_to_le16(1);
+ init_reset_system(&(bp->reset_system));
+ p += ROUND_UP(sizeof(struct boot_params), 64);
+ init_loongson_params(&(bp->efi.smbios.lp), p,
+ loaderparams.cpu_freq, loaderparams.ram_size);
+
+ rom_add_blob_fixed("params_rom", bp,
+ loader_rommap[LOADER_PARAM].size,
+ loader_rommap[LOADER_PARAM].base);
+
+ g_free(bp);
+
+ loaderparams.a2 = cpu_mips_phys_to_kseg0(NULL,
+ loader_rommap[LOADER_PARAM].base);
+}
+
+static void init_boot_rom(void)
+{
+ const unsigned int boot_code[] = {
+ 0x40086000, /* mfc0 t0, CP0_STATUS */
+ 0x240900E4, /* li t1, 0xe4 #set kx, sx, ux, erl */
+ 0x01094025, /* or t0, t0, t1 */
+ 0x3C090040, /* lui t1, 0x40 #set bev */
+ 0x01094025, /* or t0, t0, t1 */
+ 0x40886000, /* mtc0 t0, CP0_STATUS */
+ 0x00000000,
+ 0x40806800, /* mtc0 zero, CP0_CAUSE */
+ 0x00000000,
+ 0x400A7801, /* mfc0 t2, $15, 1 */
+ 0x314A00FF, /* andi t2, 0x0ff */
+ 0x3C089000, /* dli t0, 0x900000003ff01000 */
+ 0x00084438,
+ 0x35083FF0,
+ 0x00084438,
+ 0x35081000,
+ 0x314B0003, /* andi t3, t2, 0x3 #local cpuid */
+ 0x000B5A00, /* sll t3, 8 */
+ 0x010B4025, /* or t0, t0, t3 */
+ 0x314C000C, /* andi t4, t2, 0xc #node id */
+ 0x000C62BC, /* dsll t4, 42 */
+ 0x010C4025, /* or t0, t0, t4 */
+ /* WaitForInit: */
+ 0xDD020020, /* ld v0, FN_OFF(t0) #FN_OFF 0x020 */
+ 0x1040FFFE, /* beqz v0, WaitForInit */
+ 0x00000000, /* nop */
+ 0xDD1D0028, /* ld sp, SP_OFF(t0) #FN_OFF 0x028 */
+ 0xDD1C0030, /* ld gp, GP_OFF(t0) #FN_OFF 0x030 */
+ 0xDD050038, /* ld a1, A1_OFF(t0) #FN_OFF 0x038 */
+ 0x00400008, /* jr v0 #byebye */
+ 0x00000000, /* nop */
+ 0x1000FFFF, /* 1: b 1b */
+ 0x00000000, /* nop */
+
+ /* Reset */
+ 0x3C0C9000, /* dli t0, 0x9000000010080010 */
+ 0x358C0000,
+ 0x000C6438,
+ 0x358C1008,
+ 0x000C6438,
+ 0x358C0010,
+ 0x240D0000, /* li t1, 0x00 */
+ 0xA18D0000, /* sb t1, (t0) */
+ 0x1000FFFF, /* 1: b 1b */
+ 0x00000000, /* nop */
+
+ /* Shutdown */
+ 0x3C0C9000, /* dli t0, 0x9000000010080010 */
+ 0x358C0000,
+ 0x000C6438,
+ 0x358C1008,
+ 0x000C6438,
+ 0x358C0010,
+ 0x240D00FF, /* li t1, 0xff */
+ 0xA18D0000, /* sb t1, (t0) */
+ 0x1000FFFF, /* 1: b 1b */
+ 0x00000000 /* nop */
+ };
+
+ rom_add_blob_fixed("boot_rom", boot_code, sizeof(boot_code),
+ loader_rommap[LOADER_BOOTROM].base);
+}
+
+static void fw_cfg_boot_set(void *opaque, const char *boot_device,
+ Error **errp)
+{
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+}
+
+static void fw_conf_init(unsigned long ram_size)
+{
+ FWCfgState *fw_cfg;
+ hwaddr cfg_addr = virt_memmap[VIRT_FW_CFG].base;
+
+ fw_cfg = fw_cfg_init_mem_wide(cfg_addr, cfg_addr + 8, 8, 0, NULL);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)current_machine->smp.cpus);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)current_machine->smp.max_cpus);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_MACHINE_VERSION, 1);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_CPU_FREQ, get_cpu_freq_hz());
+ qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+}
+
+static int set_prom_cmdline(ram_addr_t initrd_offset, long initrd_size)
+{
+ int ret = 0;
+ void *cmdline_buf;
+ hwaddr cmdline_vaddr;
+ unsigned int *parg_env;
+
+ /* Allocate cmdline_buf for command line. */
+ cmdline_buf = g_malloc0(loader_memmap[LOADER_CMDLINE].size);
+ cmdline_vaddr = cpu_mips_phys_to_kseg0(NULL,
+ loader_memmap[LOADER_CMDLINE].base);
+
+ /*
+ * Layout of cmdline_buf looks like this:
+ * argv[0], argv[1], 0, env[0], env[1], ... env[i], 0,
+ * argv[0]'s data, argv[1]'s data, env[0]'data, ..., env[i]'s data, 0
+ */
+ parg_env = (void *)cmdline_buf;
+
+ ret = (3 + 1) * 4;
+ *parg_env++ = cmdline_vaddr + ret;
+ ret += (1 + snprintf(cmdline_buf + ret, 256 - ret, "g"));
+
+ /* argv1 */
+ *parg_env++ = cmdline_vaddr + ret;
+ if (initrd_size > 0)
+ ret += (1 + snprintf(cmdline_buf + ret, 256 - ret,
+ "rd_start=0x" TARGET_FMT_lx " rd_size=%li %s",
+ cpu_mips_phys_to_kseg0(NULL, initrd_offset),
+ initrd_size, loaderparams.kernel_cmdline));
+ else
+ ret += (1 + snprintf(cmdline_buf + ret, 256 - ret, "%s",
+ loaderparams.kernel_cmdline));
+
+ /* argv2 */
+ *parg_env++ = cmdline_vaddr + 4 * ret;
+
+ rom_add_blob_fixed("cmdline", cmdline_buf,
+ loader_memmap[LOADER_CMDLINE].size,
+ loader_memmap[LOADER_CMDLINE].base);
+
+ g_free(cmdline_buf);
+
+ loaderparams.a0 = 2;
+ loaderparams.a1 = cmdline_vaddr;
+
+ return 0;
+}
+
+static uint64_t load_kernel(CPUMIPSState *env)
+{
+ long kernel_size;
+ ram_addr_t initrd_offset;
+ uint64_t kernel_entry, kernel_low, kernel_high, initrd_size;
+
+ kernel_size = load_elf(loaderparams.kernel_filename, NULL,
+ cpu_mips_kseg0_to_phys, NULL,
+ (uint64_t *)&kernel_entry,
+ (uint64_t *)&kernel_low, (uint64_t *)&kernel_high,
+ NULL, 0, EM_MIPS, 1, 0);
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s': %s",
+ loaderparams.kernel_filename,
+ load_elf_strerror(kernel_size));
+ 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 = MAX(loader_memmap[LOADER_INITRD].base,
+ ROUND_UP(kernel_high, INITRD_PAGE_SIZE));
+
+ if (initrd_offset + initrd_size > loaderparams.ram_size) {
+ error_report("memory too small for initial ram disk '%s'",
+ 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) {
+ error_report("could not load initial ram disk '%s'",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* Setup prom cmdline. */
+ set_prom_cmdline(initrd_offset, initrd_size);
+
+ return kernel_entry;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ MIPSCPU *cpu = opaque;
+ CPUMIPSState *env = &cpu->env;
+
+ cpu_reset(CPU(cpu));
+
+ /* Loongson-3 reset stuff */
+ if (loaderparams.kernel_filename) {
+ if (cpu == MIPS_CPU(first_cpu)) {
+ env->active_tc.gpr[4] = loaderparams.a0;
+ env->active_tc.gpr[5] = loaderparams.a1;
+ env->active_tc.gpr[6] = loaderparams.a2;
+ env->active_tc.PC = loaderparams.kernel_entry;
+ }
+ env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL));
+ }
+}
+
+static inline void loongson3_virt_devices_init(MachineState *machine,
+ DeviceState *pic)
+{
+ int i;
+ qemu_irq irq;
+ PCIBus *pci_bus;
+ DeviceState *dev;
+ MemoryRegion *mmio_reg, *ecam_reg;
+ LoongsonMachineState *s = LOONGSON_MACHINE(machine);
+
+ dev = qdev_new(TYPE_GPEX_HOST);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ pci_bus = PCI_HOST_BRIDGE(dev)->bus;
+
+ s->ecam_alias = g_new0(MemoryRegion, 1);
+ ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+ memory_region_init_alias(s->ecam_alias, OBJECT(dev), "pcie-ecam",
+ ecam_reg, 0, virt_memmap[VIRT_PCIE_ECAM].size);
+ memory_region_add_subregion(get_system_memory(),
+ virt_memmap[VIRT_PCIE_ECAM].base,
+ s->ecam_alias);
+
+ s->mmio_alias = g_new0(MemoryRegion, 1);
+ mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
+ memory_region_init_alias(s->mmio_alias, OBJECT(dev), "pcie-mmio",
+ mmio_reg, virt_memmap[VIRT_PCIE_MMIO].base,
+ virt_memmap[VIRT_PCIE_MMIO].size);
+ memory_region_add_subregion(get_system_memory(),
+ virt_memmap[VIRT_PCIE_MMIO].base,
+ s->mmio_alias);
+
+ s->pio_alias = g_new0(MemoryRegion, 1);
+ memory_region_init_alias(s->pio_alias, OBJECT(dev), "pcie-pio",
+ get_system_io(), 0,
+ virt_memmap[VIRT_PCIE_PIO].size);
+ memory_region_add_subregion(get_system_memory(),
+ virt_memmap[VIRT_PCIE_PIO].base, s->pio_alias);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, virt_memmap[VIRT_PCIE_PIO].base);
+
+ for (i = 0; i < GPEX_NUM_IRQS; i++) {
+ irq = qdev_get_gpio_in(pic, PCIE_IRQ_BASE + i);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
+ gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ_BASE + i);
+ }
+ msi_nonbroken = true;
+
+ pci_vga_init(pci_bus);
+
+ if (defaults_enabled()) {
+ pci_create_simple(pci_bus, -1, "pci-ohci");
+ usb_create_simple(usb_bus_find(-1), "usb-kbd");
+ usb_create_simple(usb_bus_find(-1), "usb-tablet");
+ }
+
+ for (i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+
+ if (!nd->model) {
+ nd->model = g_strdup("virtio");
+ }
+
+ pci_nic_init_nofail(nd, pci_bus, nd->model, NULL);
+ }
+}
+
+static void mips_loongson3_virt_init(MachineState *machine)
+{
+ int i;
+ long bios_size;
+ MIPSCPU *cpu;
+ Clock *cpuclk;
+ CPUMIPSState *env;
+ DeviceState *liointc;
+ char *filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *initrd_filename = machine->initrd_filename;
+ ram_addr_t ram_size = machine->ram_size;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+ MemoryRegion *bios = g_new(MemoryRegion, 1);
+ MemoryRegion *iomem = g_new(MemoryRegion, 1);
+
+ /* TODO: TCG will support all CPU types */
+ if (!kvm_enabled()) {
+ if (!machine->cpu_type) {
+ machine->cpu_type = MIPS_CPU_TYPE_NAME("Loongson-3A1000");
+ }
+ if (!strstr(machine->cpu_type, "Loongson-3A1000")) {
+ error_report("Loongson-3/TCG needs cpu type Loongson-3A1000");
+ exit(1);
+ }
+ } else {
+ if (!machine->cpu_type) {
+ machine->cpu_type = MIPS_CPU_TYPE_NAME("Loongson-3A4000");
+ }
+ if (!strstr(machine->cpu_type, "Loongson-3A4000")) {
+ error_report("Loongson-3/KVM needs cpu type Loongson-3A4000");
+ exit(1);
+ }
+ }
+
+ if (ram_size < 512 * MiB) {
+ error_report("Loongson-3 machine needs at least 512MB memory");
+ exit(1);
+ }
+
+ /*
+ * The whole MMIO range among configure registers doesn't generate
+ * exception when accessing invalid memory. Create some unimplememted
+ * devices to emulate this feature.
+ */
+ create_unimplemented_device("mmio fallback 0", 0x10000000, 256 * MiB);
+ create_unimplemented_device("mmio fallback 1", 0x30000000, 256 * MiB);
+
+ liointc = qdev_new("loongson.liointc");
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(liointc), &error_fatal);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(liointc), 0, virt_memmap[VIRT_LIOINTC].base);
+
+ serial_mm_init(address_space_mem, virt_memmap[VIRT_UART].base, 0,
+ qdev_get_gpio_in(liointc, UART_IRQ), 115200, serial_hd(0),
+ DEVICE_NATIVE_ENDIAN);
+
+ sysbus_create_simple("goldfish_rtc", virt_memmap[VIRT_RTC].base,
+ qdev_get_gpio_in(liointc, RTC_IRQ));
+
+ cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
+ clock_set_hz(cpuclk, DEF_LOONGSON3_FREQ);
+
+ for (i = 0; i < machine->smp.cpus; i++) {
+ int ip;
+
+ /* init CPUs */
+ cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(cpu);
+ cpu_mips_clock_init(cpu);
+ qemu_register_reset(main_cpu_reset, cpu);
+
+ if (i >= 4) {
+ continue; /* Only node-0 can be connected to LIOINTC */
+ }
+
+ for (ip = 0; ip < 4 ; ip++) {
+ int pin = i * 4 + ip;
+ sysbus_connect_irq(SYS_BUS_DEVICE(liointc),
+ pin, cpu->env.irq[ip + 2]);
+ }
+ }
+ env = &MIPS_CPU(first_cpu)->env;
+
+ /* Allocate RAM/BIOS, 0x00000000~0x10000000 is alias of 0x80000000~0x90000000 */
+ memory_region_init_rom(bios, NULL, "loongson3.bios",
+ virt_memmap[VIRT_BIOS_ROM].size, &error_fatal);
+ memory_region_init_alias(ram, NULL, "loongson3.lowmem",
+ machine->ram, 0, virt_memmap[VIRT_LOWMEM].size);
+ memory_region_init_io(iomem, NULL, &loongson3_pm_ops,
+ NULL, "loongson3_pm", virt_memmap[VIRT_PM].size);
+
+ memory_region_add_subregion(address_space_mem,
+ virt_memmap[VIRT_LOWMEM].base, ram);
+ memory_region_add_subregion(address_space_mem,
+ virt_memmap[VIRT_BIOS_ROM].base, bios);
+ memory_region_add_subregion(address_space_mem,
+ virt_memmap[VIRT_HIGHMEM].base, machine->ram);
+ memory_region_add_subregion(address_space_mem,
+ virt_memmap[VIRT_PM].base, iomem);
+
+ /*
+ * We do not support flash operation, just loading bios.bin as raw BIOS.
+ * Please use -L to set the BIOS path and -bios to set bios name.
+ */
+
+ if (kernel_filename) {
+ loaderparams.cpu_freq = get_cpu_freq_hz();
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ loaderparams.kernel_entry = load_kernel(env);
+
+ init_boot_rom();
+ init_boot_param();
+ } else {
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
+ machine->firmware ?: LOONGSON3_BIOSNAME);
+ if (filename) {
+ bios_size = load_image_targphys(filename,
+ virt_memmap[VIRT_BIOS_ROM].base,
+ virt_memmap[VIRT_BIOS_ROM].size);
+ g_free(filename);
+ } else {
+ bios_size = -1;
+ }
+
+ if ((bios_size < 0 || bios_size > virt_memmap[VIRT_BIOS_ROM].size) &&
+ !kernel_filename && !qtest_enabled()) {
+ error_report("Could not load MIPS bios '%s'", machine->firmware);
+ exit(1);
+ }
+
+ fw_conf_init(ram_size);
+ }
+
+ loongson3_virt_devices_init(machine, liointc);
+}
+
+static void loongson3v_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Loongson-3 Virtualization Platform";
+ mc->init = mips_loongson3_virt_init;
+ mc->block_default_type = IF_IDE;
+ mc->max_cpus = LOONGSON_MAX_VCPUS;
+ mc->default_ram_id = "loongson3.highram";
+ mc->default_ram_size = 1600 * MiB;
+ mc->kvm_type = mips_kvm_type;
+ mc->minimum_page_bits = 14;
+}
+
+static const TypeInfo loongson3_machine_types[] = {
+ {
+ .name = TYPE_LOONGSON_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(LoongsonMachineState),
+ .class_init = loongson3v_machine_class_init,
+ }
+};
+
+DEFINE_TYPES(loongson3_machine_types)
diff --git a/hw/mips/malta.c b/hw/mips/malta.c
new file mode 100644
index 000000000..b770b8d36
--- /dev/null
+++ b/hw/mips/malta.c
@@ -0,0 +1,1464 @@
+/*
+ * 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 "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/bitops.h"
+#include "qemu-common.h"
+#include "qemu/datadir.h"
+#include "hw/clock.h"
+#include "hw/southbridge/piix.h"
+#include "hw/isa/superio.h"
+#include "hw/char/serial.h"
+#include "net/net.h"
+#include "hw/boards.h"
+#include "hw/i2c/smbus_eeprom.h"
+#include "hw/block/flash.h"
+#include "hw/mips/mips.h"
+#include "hw/mips/cpudevs.h"
+#include "hw/pci/pci.h"
+#include "qemu/log.h"
+#include "hw/mips/bios.h"
+#include "hw/ide.h"
+#include "hw/irq.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "qom/object.h"
+#include "hw/sysbus.h" /* SysBusDevice */
+#include "qemu/host-utils.h"
+#include "sysemu/qtest.h"
+#include "sysemu/reset.h"
+#include "sysemu/runstate.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "hw/misc/empty_slot.h"
+#include "sysemu/kvm.h"
+#include "semihosting/semihost.h"
+#include "hw/mips/cps.h"
+#include "hw/qdev-clock.h"
+
+#define ENVP_PADDR 0x2000
+#define ENVP_VADDR cpu_mips_phys_to_kseg0(NULL, ENVP_PADDR)
+#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;
+ CharBackend display;
+ char display_text[9];
+ SerialMM *uart;
+ bool display_inited;
+} MaltaFPGAState;
+
+#define TYPE_MIPS_MALTA "mips-malta"
+OBJECT_DECLARE_SIMPLE_TYPE(MaltaState, MIPS_MALTA)
+
+struct MaltaState {
+ SysBusDevice parent_obj;
+
+ Clock *cpuclk;
+ MIPSCPSState cps;
+ qemu_irq i8259[ISA_NUM_IRQS];
+};
+
+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.
+ */
+
+#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 /= MiB;
+
+ 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) {
+ warn_report("SPD cannot represent final " RAM_ADDR_FMT "MB"
+ " of SDRAM", 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;
+ 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:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "malta_fpga_read: Bad register addr 0x%"HWADDR_PRIX"\n",
+ addr);
+ 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(SHUTDOWN_CAUSE_GUEST_RESET);
+ }
+ 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:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "malta_fpga_write: Bad register addr 0x%"HWADDR_PRIX"\n",
+ addr);
+ 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_fgpa_display_event(void *opaque, QEMUChrEvent event)
+{
+ MaltaFPGAState *s = opaque;
+
+ if (event == CHR_EVENT_OPENED && !s->display_inited) {
+ qemu_chr_fe_printf(&s->display, "\e[HMalta LEDBAR\r\n");
+ qemu_chr_fe_printf(&s->display, "+--------+\r\n");
+ qemu_chr_fe_printf(&s->display, "+ +\r\n");
+ qemu_chr_fe_printf(&s->display, "+--------+\r\n");
+ qemu_chr_fe_printf(&s->display, "\n");
+ qemu_chr_fe_printf(&s->display, "Malta ASCII\r\n");
+ qemu_chr_fe_printf(&s->display, "+--------+\r\n");
+ qemu_chr_fe_printf(&s->display, "+ +\r\n");
+ qemu_chr_fe_printf(&s->display, "+--------+\r\n");
+ s->display_inited = true;
+ }
+}
+
+static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space,
+ hwaddr base, qemu_irq uart_irq, Chardev *uart_chr)
+{
+ MaltaFPGAState *s;
+ Chardev *chr;
+
+ s = g_new0(MaltaFPGAState, 1);
+
+ 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, 0x100000 - 0xa00);
+
+ memory_region_add_subregion(address_space, base, &s->iomem_lo);
+ memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi);
+
+ chr = qemu_chr_new("fpga", "vc:320x200", NULL);
+ qemu_chr_fe_init(&s->display, chr, NULL);
+ qemu_chr_fe_set_handlers(&s->display, NULL, NULL,
+ malta_fgpa_display_event, NULL, s, NULL, true);
+
+ 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);
+ }
+}
+
+static void write_bootloader_nanomips(uint8_t *base, uint64_t run_addr,
+ uint64_t kernel_entry)
+{
+ uint16_t *p;
+
+ /* Small bootloader */
+ p = (uint16_t *)base;
+
+#define NM_HI1(VAL) (((VAL) >> 16) & 0x1f)
+#define NM_HI2(VAL) \
+ (((VAL) & 0xf000) | (((VAL) >> 19) & 0xffc) | (((VAL) >> 31) & 0x1))
+#define NM_LO(VAL) ((VAL) & 0xfff)
+
+ stw_p(p++, 0x2800); stw_p(p++, 0x001c);
+ /* bc to_here */
+ stw_p(p++, 0x8000); stw_p(p++, 0xc000);
+ /* nop */
+ stw_p(p++, 0x8000); stw_p(p++, 0xc000);
+ /* nop */
+ stw_p(p++, 0x8000); stw_p(p++, 0xc000);
+ /* nop */
+ stw_p(p++, 0x8000); stw_p(p++, 0xc000);
+ /* nop */
+ stw_p(p++, 0x8000); stw_p(p++, 0xc000);
+ /* nop */
+ stw_p(p++, 0x8000); stw_p(p++, 0xc000);
+ /* nop */
+ stw_p(p++, 0x8000); stw_p(p++, 0xc000);
+ /* nop */
+
+ /* to_here: */
+ if (semihosting_get_argc()) {
+ /* Preserve a0 content as arguments have been passed */
+ stw_p(p++, 0x8000); stw_p(p++, 0xc000);
+ /* nop */
+ } else {
+ stw_p(p++, 0x0080); stw_p(p++, 0x0002);
+ /* li a0,2 */
+ }
+
+ stw_p(p++, 0xe3a0 | NM_HI1(ENVP_VADDR - 64));
+
+ stw_p(p++, NM_HI2(ENVP_VADDR - 64));
+ /* lui sp,%hi(ENVP_VADDR - 64) */
+
+ stw_p(p++, 0x83bd); stw_p(p++, NM_LO(ENVP_VADDR - 64));
+ /* ori sp,sp,%lo(ENVP_VADDR - 64) */
+
+ stw_p(p++, 0xe0a0 | NM_HI1(ENVP_VADDR));
+
+ stw_p(p++, NM_HI2(ENVP_VADDR));
+ /* lui a1,%hi(ENVP_VADDR) */
+
+ stw_p(p++, 0x80a5); stw_p(p++, NM_LO(ENVP_VADDR));
+ /* ori a1,a1,%lo(ENVP_VADDR) */
+
+ stw_p(p++, 0xe0c0 | NM_HI1(ENVP_VADDR + 8));
+
+ stw_p(p++, NM_HI2(ENVP_VADDR + 8));
+ /* lui a2,%hi(ENVP_VADDR + 8) */
+
+ stw_p(p++, 0x80c6); stw_p(p++, NM_LO(ENVP_VADDR + 8));
+ /* ori a2,a2,%lo(ENVP_VADDR + 8) */
+
+ stw_p(p++, 0xe0e0 | NM_HI1(loaderparams.ram_low_size));
+
+ stw_p(p++, NM_HI2(loaderparams.ram_low_size));
+ /* lui a3,%hi(loaderparams.ram_low_size) */
+
+ stw_p(p++, 0x80e7); stw_p(p++, NM_LO(loaderparams.ram_low_size));
+ /* ori a3,a3,%lo(loaderparams.ram_low_size) */
+
+ /*
+ * Load BAR registers as done by YAMON:
+ *
+ * - set up PCI0 I/O BARs from 0x18000000 to 0x181fffff
+ * - set up PCI0 MEM0 at 0x10000000, size 0x8000000
+ * - set up PCI0 MEM1 at 0x18200000, size 0xbe00000
+ *
+ */
+ stw_p(p++, 0xe040); stw_p(p++, 0x0681);
+ /* lui t1, %hi(0xb4000000) */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+
+ stw_p(p++, 0xe020); stw_p(p++, 0x0be1);
+ /* lui t0, %hi(0xdf000000) */
+
+ /* 0x68 corresponds to GT_ISD (from hw/mips/gt64xxx_pci.c) */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9068);
+ /* sw t0, 0x68(t1) */
+
+ stw_p(p++, 0xe040); stw_p(p++, 0x077d);
+ /* lui t1, %hi(0xbbe00000) */
+
+ stw_p(p++, 0xe020); stw_p(p++, 0x0801);
+ /* lui t0, %hi(0xc0000000) */
+
+ /* 0x48 corresponds to GT_PCI0IOLD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9048);
+ /* sw t0, 0x48(t1) */
+
+ stw_p(p++, 0xe020); stw_p(p++, 0x0800);
+ /* lui t0, %hi(0x40000000) */
+
+ /* 0x50 corresponds to GT_PCI0IOHD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9050);
+ /* sw t0, 0x50(t1) */
+
+ stw_p(p++, 0xe020); stw_p(p++, 0x0001);
+ /* lui t0, %hi(0x80000000) */
+
+ /* 0x58 corresponds to GT_PCI0M0LD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9058);
+ /* sw t0, 0x58(t1) */
+
+ stw_p(p++, 0xe020); stw_p(p++, 0x07e0);
+ /* lui t0, %hi(0x3f000000) */
+
+ /* 0x60 corresponds to GT_PCI0M0HD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9060);
+ /* sw t0, 0x60(t1) */
+
+ stw_p(p++, 0xe020); stw_p(p++, 0x0821);
+ /* lui t0, %hi(0xc1000000) */
+
+ /* 0x80 corresponds to GT_PCI0M1LD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9080);
+ /* sw t0, 0x80(t1) */
+
+ stw_p(p++, 0xe020); stw_p(p++, 0x0bc0);
+ /* lui t0, %hi(0x5e000000) */
+
+#else
+
+ stw_p(p++, 0x0020); stw_p(p++, 0x00df);
+ /* addiu[32] t0, $0, 0xdf */
+
+ /* 0x68 corresponds to GT_ISD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9068);
+ /* sw t0, 0x68(t1) */
+
+ /* Use kseg2 remapped address 0x1be00000 */
+ stw_p(p++, 0xe040); stw_p(p++, 0x077d);
+ /* lui t1, %hi(0xbbe00000) */
+
+ stw_p(p++, 0x0020); stw_p(p++, 0x00c0);
+ /* addiu[32] t0, $0, 0xc0 */
+
+ /* 0x48 corresponds to GT_PCI0IOLD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9048);
+ /* sw t0, 0x48(t1) */
+
+ stw_p(p++, 0x0020); stw_p(p++, 0x0040);
+ /* addiu[32] t0, $0, 0x40 */
+
+ /* 0x50 corresponds to GT_PCI0IOHD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9050);
+ /* sw t0, 0x50(t1) */
+
+ stw_p(p++, 0x0020); stw_p(p++, 0x0080);
+ /* addiu[32] t0, $0, 0x80 */
+
+ /* 0x58 corresponds to GT_PCI0M0LD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9058);
+ /* sw t0, 0x58(t1) */
+
+ stw_p(p++, 0x0020); stw_p(p++, 0x003f);
+ /* addiu[32] t0, $0, 0x3f */
+
+ /* 0x60 corresponds to GT_PCI0M0HD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9060);
+ /* sw t0, 0x60(t1) */
+
+ stw_p(p++, 0x0020); stw_p(p++, 0x00c1);
+ /* addiu[32] t0, $0, 0xc1 */
+
+ /* 0x80 corresponds to GT_PCI0M1LD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9080);
+ /* sw t0, 0x80(t1) */
+
+ stw_p(p++, 0x0020); stw_p(p++, 0x005e);
+ /* addiu[32] t0, $0, 0x5e */
+
+#endif
+
+ /* 0x88 corresponds to GT_PCI0M1HD */
+ stw_p(p++, 0x8422); stw_p(p++, 0x9088);
+ /* sw t0, 0x88(t1) */
+
+ stw_p(p++, 0xe320 | NM_HI1(kernel_entry));
+
+ stw_p(p++, NM_HI2(kernel_entry));
+ /* lui t9,%hi(kernel_entry) */
+
+ stw_p(p++, 0x8339); stw_p(p++, NM_LO(kernel_entry));
+ /* ori t9,t9,%lo(kernel_entry) */
+
+ stw_p(p++, 0x4bf9); stw_p(p++, 0x0000);
+ /* jalrc t8 */
+}
+
+/*
+ * 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(uint8_t *base, uint64_t run_addr,
+ uint64_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 */
+ }
+
+ /* lui sp, high(ENVP_VADDR) */
+ stl_p(p++, 0x3c1d0000 | (((ENVP_VADDR - 64) >> 16) & 0xffff));
+ /* ori sp, sp, low(ENVP_VADDR) */
+ stl_p(p++, 0x37bd0000 | ((ENVP_VADDR - 64) & 0xffff));
+ /* lui a1, high(ENVP_VADDR) */
+ stl_p(p++, 0x3c050000 | ((ENVP_VADDR >> 16) & 0xffff));
+ /* ori a1, a1, low(ENVP_VADDR) */
+ stl_p(p++, 0x34a50000 | (ENVP_VADDR & 0xffff));
+ /* lui a2, high(ENVP_VADDR + 8) */
+ stl_p(p++, 0x3c060000 | (((ENVP_VADDR + 8) >> 16) & 0xffff));
+ /* ori a2, a2, low(ENVP_VADDR + 8) */
+ stl_p(p++, 0x34c60000 | ((ENVP_VADDR + 8) & 0xffff));
+ /* lui a3, high(ram_low_size) */
+ stl_p(p++, 0x3c070000 | (loaderparams.ram_low_size >> 16));
+ /* ori a3, a3, low(ram_low_size) */
+ stl_p(p++, 0x34e70000 | (loaderparams.ram_low_size & 0xffff));
+
+ /* 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++, 0x1000fff9); /* b 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;
+ uint32_t table_addr;
+
+ if (index >= ENVP_NB_ENTRIES) {
+ return;
+ }
+
+ if (string == NULL) {
+ prom_buf[index] = 0;
+ return;
+ }
+
+ table_addr = sizeof(uint32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
+ prom_buf[index] = tswap32(ENVP_VADDR + table_addr);
+
+ va_start(ap, string);
+ vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
+ va_end(ap);
+}
+
+/* Kernel */
+static uint64_t load_kernel(void)
+{
+ uint64_t kernel_entry, kernel_high, initrd_size;
+ long kernel_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
+
+ kernel_size = load_elf(loaderparams.kernel_filename, NULL,
+ cpu_mips_kseg0_to_phys, NULL,
+ &kernel_entry, NULL,
+ &kernel_high, NULL, big_endian, EM_MIPS,
+ 1, 0);
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s': %s",
+ loaderparams.kernel_filename,
+ load_elf_strerror(kernel_size));
+ exit(1);
+ }
+
+ /* Check where the kernel has been linked */
+ if (kernel_entry & 0x80000000ll) {
+ if (kvm_enabled()) {
+ 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_phys_to_kseg0;
+ } else {
+ /* if kernel entry is in useg it is probably a KVM T&E kernel */
+ mips_um_ksegs_enable();
+
+ xlate_to_kseg0 = cpu_mips_kvm_um_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) {
+ /*
+ * The kernel allocates the bootmap memory in the low memory after
+ * the initrd. It takes at most 128kiB for 2GB RAM and 4kiB
+ * pages.
+ */
+ initrd_offset = ROUND_UP(loaderparams.ram_low_size
+ - (initrd_size + 128 * KiB),
+ INITRD_PAGE_SIZE);
+ if (kernel_high >= initrd_offset) {
+ error_report("memory too small for initial ram disk '%s'",
+ 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) {
+ error_report("could not load initial ram disk '%s'",
+ 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=%" PRId64 " %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, ENVP_PADDR);
+
+ g_free(prom_buf);
+ return kernel_entry;
+}
+
+static void malta_mips_config(MIPSCPU *cpu)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ unsigned int smp_cpus = ms->smp.cpus;
+ CPUMIPSState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
+
+ if (ase_mt_available(env)) {
+ env->mvp->CP0_MVPConf0 = deposit32(env->mvp->CP0_MVPConf0,
+ CP0MVPC0_PTC, 8,
+ smp_cpus * cs->nr_threads - 1);
+ env->mvp->CP0_MVPConf0 = deposit32(env->mvp->CP0_MVPConf0,
+ CP0MVPC0_PVPE, 4, smp_cpus - 1);
+ }
+}
+
+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_low_size;
+ }
+}
+
+static void create_cpu_without_cps(MachineState *ms, MaltaState *s,
+ qemu_irq *cbus_irq, qemu_irq *i8259_irq)
+{
+ CPUMIPSState *env;
+ MIPSCPU *cpu;
+ int i;
+
+ for (i = 0; i < ms->smp.cpus; i++) {
+ cpu = mips_cpu_create_with_clock(ms->cpu_type, s->cpuclk);
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(cpu);
+ cpu_mips_clock_init(cpu);
+ qemu_register_reset(main_cpu_reset, cpu);
+ }
+
+ cpu = MIPS_CPU(first_cpu);
+ env = &cpu->env;
+ *i8259_irq = env->irq[2];
+ *cbus_irq = env->irq[4];
+}
+
+static void create_cps(MachineState *ms, MaltaState *s,
+ qemu_irq *cbus_irq, qemu_irq *i8259_irq)
+{
+ object_initialize_child(OBJECT(s), "cps", &s->cps, TYPE_MIPS_CPS);
+ object_property_set_str(OBJECT(&s->cps), "cpu-type", ms->cpu_type,
+ &error_fatal);
+ object_property_set_int(OBJECT(&s->cps), "num-vp", ms->smp.cpus,
+ &error_fatal);
+ qdev_connect_clock_in(DEVICE(&s->cps), "clk-in", s->cpuclk);
+ sysbus_realize(SYS_BUS_DEVICE(&s->cps), &error_fatal);
+
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->cps), 0, 0, 1);
+
+ *i8259_irq = get_cps_irq(&s->cps, 3);
+ *cbus_irq = NULL;
+}
+
+static void mips_create_cpu(MachineState *ms, MaltaState *s,
+ qemu_irq *cbus_irq, qemu_irq *i8259_irq)
+{
+ if ((ms->smp.cpus > 1) && cpu_type_supports_cps_smp(ms->cpu_type)) {
+ create_cps(ms, s, cbus_irq, i8259_irq);
+ } else {
+ create_cpu_without_cps(ms, s, cbus_irq, i8259_irq);
+ }
+}
+
+static
+void mips_malta_init(MachineState *machine)
+{
+ ram_addr_t ram_size = machine->ram_size;
+ ram_addr_t ram_low_size;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *initrd_filename = machine->initrd_filename;
+ char *filename;
+ PFlashCFI01 *fl;
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *ram_low_preio = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_low_postio;
+ MemoryRegion *bios, *bios_copy = g_new(MemoryRegion, 1);
+ const size_t smbus_eeprom_size = 8 * 256;
+ uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size);
+ uint64_t kernel_entry, bootloader_run_addr;
+ PCIBus *pci_bus;
+ ISABus *isa_bus;
+ qemu_irq cbus_irq, i8259_irq;
+ I2CBus *smbus;
+ DriveInfo *dinfo;
+ int fl_idx = 0;
+ int be;
+ MaltaState *s;
+ DeviceState *dev;
+
+ s = MIPS_MALTA(qdev_new(TYPE_MIPS_MALTA));
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal);
+
+ /* create CPU */
+ mips_create_cpu(machine, s, &cbus_irq, &i8259_irq);
+
+ /* allocate RAM */
+ if (ram_size > 2 * GiB) {
+ error_report("Too much memory for this machine: %" PRId64 "MB,"
+ " maximum 2048MB", ram_size / MiB);
+ exit(1);
+ }
+
+ /* register RAM at high address where it is undisturbed by IO */
+ memory_region_add_subregion(system_memory, 0x80000000, machine->ram);
+
+ /* alias for pre IO hole access */
+ memory_region_init_alias(ram_low_preio, NULL, "mips_malta_low_preio.ram",
+ machine->ram, 0, MIN(ram_size, 256 * MiB));
+ 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 * MiB) {
+ ram_low_postio = g_new(MemoryRegion, 1);
+ memory_region_init_alias(ram_low_postio, NULL,
+ "mips_malta_low_postio.ram",
+ machine->ram, 512 * MiB,
+ ram_size - 512 * MiB);
+ memory_region_add_subregion(system_memory, 512 * MiB,
+ ram_low_postio);
+ }
+
+#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, cbus_irq, serial_hd(2));
+
+ /* Load firmware in flash / BIOS. */
+ dinfo = drive_get(IF_PFLASH, 0, fl_idx);
+ fl = pflash_cfi01_register(FLASH_ADDRESS, "mips_malta.bios",
+ FLASH_SIZE,
+ dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
+ 65536,
+ 4, 0x0000, 0x0000, 0x0000, 0x0000, be);
+ bios = pflash_cfi01_get_memory(fl);
+ fl_idx++;
+ if (kernel_filename) {
+ ram_low_size = MIN(ram_size, 256 * MiB);
+ /* For KVM we reserve 1MB of RAM for running bootloader */
+ if (kvm_enabled()) {
+ ram_low_size -= 0x100000;
+ bootloader_run_addr = cpu_mips_kvm_um_phys_to_kseg0(NULL, ram_low_size);
+ } else {
+ bootloader_run_addr = cpu_mips_phys_to_kseg0(NULL, RESET_ADDRESS);
+ }
+
+ /* 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();
+
+ if (!cpu_type_supports_isa(machine->cpu_type, ISA_NANOMIPS32)) {
+ write_bootloader(memory_region_get_ram_ptr(bios),
+ bootloader_run_addr, kernel_entry);
+ } else {
+ write_bootloader_nanomips(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(memory_region_get_ram_ptr(ram_low_preio) +
+ ram_low_size,
+ bootloader_run_addr, kernel_entry);
+ }
+ } else {
+ target_long bios_size = FLASH_SIZE;
+ /* 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. */
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
+ machine->firmware ?: BIOS_FILENAME);
+ 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) &&
+ machine->firmware && !qtest_enabled()) {
+ error_report("Could not load MIPS bios '%s'", machine->firmware);
+ 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;
+ const size_t swapsize = MIN(bios_size, 0x3e0000);
+ addr = rom_ptr(FLASH_ADDRESS, swapsize);
+ if (!addr) {
+ addr = memory_region_get_ram_ptr(bios);
+ }
+ end = (void *)addr + swapsize;
+ 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_fatal);
+ 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);
+
+ /* Northbridge */
+ pci_bus = gt64120_register(s->i8259);
+ /*
+ * 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("GT64120", 0, 0x20000000);
+
+ /* Southbridge */
+ dev = piix4_create(pci_bus, &isa_bus, &smbus);
+
+ /* Interrupt controller */
+ qdev_connect_gpio_out_named(dev, "intr", 0, i8259_irq);
+ for (int i = 0; i < ISA_NUM_IRQS; i++) {
+ s->i8259[i] = qdev_get_gpio_in_named(dev, "isa", i);
+ }
+
+ /* generate SPD EEPROM data */
+ generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size);
+ generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]);
+ smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size);
+ g_free(smbus_eeprom_buf);
+
+ /* Super I/O: SMS FDC37M817 */
+ isa_create_simple(isa_bus, TYPE_FDC37M81X_SUPERIO);
+
+ /* Network card */
+ network_init(pci_bus);
+
+ /* Optional PCI video card */
+ pci_vga_init(pci_bus);
+}
+
+static void mips_malta_instance_init(Object *obj)
+{
+ MaltaState *s = MIPS_MALTA(obj);
+
+ s->cpuclk = qdev_init_clock_out(DEVICE(obj), "cpu-refclk");
+ clock_set_hz(s->cpuclk, 320000000); /* 320 MHz */
+}
+
+static const TypeInfo mips_malta_device = {
+ .name = TYPE_MIPS_MALTA,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MaltaState),
+ .instance_init = mips_malta_instance_init,
+};
+
+static void mips_malta_machine_init(MachineClass *mc)
+{
+ mc->desc = "MIPS Malta Core LV";
+ mc->init = mips_malta_init;
+ mc->block_default_type = IF_IDE;
+ mc->max_cpus = 16;
+ mc->is_default = true;
+#ifdef TARGET_MIPS64
+ mc->default_cpu_type = MIPS_CPU_TYPE_NAME("20Kc");
+#else
+ mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf");
+#endif
+ mc->default_ram_id = "mips_malta.ram";
+}
+
+DEFINE_MACHINE("malta", mips_malta_machine_init)
+
+static void mips_malta_register_types(void)
+{
+ type_register_static(&mips_malta_device);
+}
+
+type_init(mips_malta_register_types)
diff --git a/hw/mips/meson.build b/hw/mips/meson.build
new file mode 100644
index 000000000..dd0101ad4
--- /dev/null
+++ b/hw/mips/meson.build
@@ -0,0 +1,15 @@
+mips_ss = ss.source_set()
+mips_ss.add(files('bootloader.c', 'mips_int.c'))
+mips_ss.add(when: 'CONFIG_FW_CFG_MIPS', if_true: files('fw_cfg.c'))
+mips_ss.add(when: 'CONFIG_LOONGSON3V', if_true: files('loongson3_bootp.c', 'loongson3_virt.c'))
+mips_ss.add(when: 'CONFIG_MALTA', if_true: files('gt64xxx_pci.c', 'malta.c'))
+mips_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('cps.c'))
+
+if 'CONFIG_TCG' in config_all
+mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c'))
+mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c'))
+mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c'))
+mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: [files('boston.c'), fdt])
+endif
+
+hw_arch += {'mips': mips_ss}
diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c
new file mode 100644
index 000000000..2db5e10fe
--- /dev/null
+++ b/hw/mips/mips_int.c
@@ -0,0 +1,88 @@
+/*
+ * 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 "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "hw/irq.h"
+#include "hw/mips/cpudevs.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);
+ bool locked = false;
+
+ if (irq < 0 || irq > 7) {
+ return;
+ }
+
+ /* Make sure locking works even if BQL is already held by the caller */
+ if (!qemu_mutex_iothread_locked()) {
+ locked = true;
+ qemu_mutex_lock_iothread();
+ }
+
+ if (level) {
+ env->CP0_Cause |= 1 << (irq + CP0Ca_IP);
+ } else {
+ env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP));
+ }
+
+ if (kvm_enabled() && (irq == 2 || irq == 3)) {
+ 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);
+ }
+
+ if (locked) {
+ qemu_mutex_unlock_iothread();
+ }
+}
+
+void cpu_mips_irq_init_cpu(MIPSCPU *cpu)
+{
+ CPUMIPSState *env = &cpu->env;
+ qemu_irq *qi;
+ int i;
+
+ qi = qemu_allocate_irqs(cpu_mips_irq_request, cpu, 8);
+ for (i = 0; i < 8; i++) {
+ env->irq[i] = qi[i];
+ }
+ g_free(qi);
+}
+
+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/hw/mips/mipssim.c b/hw/mips/mipssim.c
new file mode 100644
index 000000000..2325e7e05
--- /dev/null
+++ b/hw/mips/mipssim.c
@@ -0,0 +1,246 @@
+/*
+ * 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "qemu/datadir.h"
+#include "hw/clock.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 "hw/qdev-properties.h"
+#include "qemu/error-report.h"
+#include "sysemu/qtest.h"
+#include "sysemu/reset.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 uint64_t load_kernel(void)
+{
+ uint64_t entry, kernel_high, initrd_size;
+ long kernel_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, NULL,
+ cpu_mips_kseg0_to_phys, NULL,
+ &entry, NULL,
+ &kernel_high, NULL, big_endian,
+ EM_MIPS, 1, 0);
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s': %s",
+ loaderparams.kernel_filename,
+ load_elf_strerror(kernel_size));
+ 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 = ROUND_UP(kernel_high, INITRD_PAGE_SIZE);
+ if (initrd_offset + initrd_size > loaderparams.ram_size) {
+ error_report("memory too small for initial ram disk '%s'",
+ 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) {
+ error_report("could not load initial ram disk '%s'",
+ 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_new("mipsnet");
+ qdev_set_nic_properties(dev, nd);
+
+ s = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(s, &error_fatal);
+ 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)
+{
+ 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 *bios = g_new(MemoryRegion, 1);
+ Clock *cpuclk;
+ MIPSCPU *cpu;
+ CPUMIPSState *env;
+ ResetData *reset_info;
+ int bios_size;
+
+ cpuclk = clock_new(OBJECT(machine), "cpu-refclk");
+#ifdef TARGET_MIPS64
+ clock_set_hz(cpuclk, 6000000); /* 6 MHz */
+#else
+ clock_set_hz(cpuclk, 12000000); /* 12 MHz */
+#endif
+
+ /* Init CPUs. */
+ cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk);
+ 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_init_rom(bios, NULL, "mips_mipssim.bios", BIOS_SIZE,
+ &error_fatal);
+
+ memory_region_add_subregion(address_space_mem, 0, machine->ram);
+
+ /* Map the BIOS / boot exception handler. */
+ memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios);
+ /* Load a BIOS / boot exception handler image. */
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware ?: BIOS_FILENAME);
+ 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) &&
+ machine->firmware && !qtest_enabled()) {
+ /* Bail out if we have neither a kernel image nor boot vector code. */
+ error_report("Could not load MIPS bios '%s'", machine->firmware);
+ 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 = machine->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(cpu);
+ cpu_mips_clock_init(cpu);
+
+ /* 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_hd(0)) {
+ DeviceState *dev = qdev_new(TYPE_SERIAL_MM);
+
+ qdev_prop_set_chr(dev, "chardev", serial_hd(0));
+ qdev_prop_set_uint8(dev, "regshift", 0);
+ qdev_prop_set_uint8(dev, "endianness", DEVICE_LITTLE_ENDIAN);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, env->irq[4]);
+ sysbus_add_io(SYS_BUS_DEVICE(dev), 0x3f8,
+ sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
+ }
+
+ 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 void mips_mipssim_machine_init(MachineClass *mc)
+{
+ mc->desc = "MIPS MIPSsim platform";
+ mc->init = mips_mipssim_init;
+#ifdef TARGET_MIPS64
+ mc->default_cpu_type = MIPS_CPU_TYPE_NAME("5Kf");
+#else
+ mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf");
+#endif
+ mc->default_ram_id = "mips_mipssim.ram";
+}
+
+DEFINE_MACHINE("mipssim", mips_mipssim_machine_init)
diff --git a/hw/mips/trace-events b/hw/mips/trace-events
new file mode 100644
index 000000000..13ee731a4
--- /dev/null
+++ b/hw/mips/trace-events
@@ -0,0 +1,6 @@
+# gt64xxx_pci.c
+gt64120_read(uint64_t addr, uint64_t value) "gt64120 read 0x%03"PRIx64" value:0x%08" PRIx64
+gt64120_write(uint64_t addr, uint64_t value) "gt64120 write 0x%03"PRIx64" value:0x%08" PRIx64
+gt64120_read_intreg(const char *regname, unsigned size, uint64_t value) "gt64120 read %s size:%u value:0x%08" PRIx64
+gt64120_write_intreg(const char *regname, unsigned size, uint64_t value) "gt64120 write %s size:%u value:0x%08" PRIx64
+gt64120_isd_remap(uint64_t from_length, uint64_t from_addr, uint64_t to_length, uint64_t to_addr) "ISD: 0x%08" PRIx64 "@0x%08" PRIx64 " -> 0x%08" PRIx64 "@0x%08" PRIx64
diff --git a/hw/mips/trace.h b/hw/mips/trace.h
new file mode 100644
index 000000000..8d1fd7c9e
--- /dev/null
+++ b/hw/mips/trace.h
@@ -0,0 +1 @@
+#include "trace/trace-hw_mips.h"